fix: budget gate and loop-breaker refusals exit without a traceback (Gitea #147)
CI / verify (push) Successful in 1m47s
Release / release (push) Successful in 39s

Files changed:
- CHANGES.md
- VERSION
- tools/chemenu/cli.py
- tools/chemenu/commands/run_budget.py
- tools/chemenu/tests/test_run_budget.py
This commit is contained in:
torben committed 2026-09-26 20:27:05 +02:00
1 parent 8f61209b6b
commit bc314e5c8c
5 files changed
+133 -17

No files matched your search

+19 -1
View File
@@ -59,7 +59,7 @@ concern - readable here, never shipped as something to parse.
---
## 7.1.0-beta.26 - 2026-09-26 - docs contract: the merged-stream test pins its own ON FAILURE line
## 7.1.0-beta.27 - 2026-09-26 - Budget gate and loop-breaker refusals exit without a traceback
**Author:** Torben Nehmer
@@ -71,6 +71,7 @@ concern - readable here, never shipped as something to parse.
- CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
- dist export no longer cuts the dist export record out of the shipped tools/CONTRACT.md
- fail() prints the command's ON FAILURE lines on stderr
- Budget gate and loop-breaker refusals exit without a traceback
**Low impact**
- version bump no longer points at version release in its output
@@ -97,6 +98,23 @@ concern - readable here, never shipped as something to parse.
- docs contract: the merged-stream test pins its own ON FAILURE line
<!-- /wikitool:bumps -->
### Budget gate and loop-breaker refusals exit without a traceback
`cli.main()` calls `run_budget.record_and_check()` before Typer ever dispatches to a subcommand,
so a refusal leaves through `_util.fail()`'s `typer.Exit` outside any Click context - nothing
caught it there, so the process exited 1 correctly but printed a Python traceback right after the
`ERROR` line (Gitea #147). That traceback sat exactly in the output AGENTS.md § Gates asks a
session to show a human and stop on; it reads as a crash rather than a gate and invites the retry
the message forbids (Gitea #52). `main()` now catches `typer.Exit` around that one call and exits
with its code directly, and two new subprocess tests - through the real `python -m chemenu.cli`
entry point, not the direct `record_and_check()` call the existing cross-process tests use -
pin that a tripped gate leaves neither stream carrying a traceback, still emits `gate.refused`,
and neither records a `wikitool.call` nor advances the session's counter for the refused call
itself. Both refusal messages, the `budget` command group's own help text, and one code comment
also pointed at "the tooling contract's 'Iteration and Cost Limits'" or "...Tool Error
Contracts" - neither section exists by that name - and now name AGENTS.md § Tool error contract
and `instructions/gates.md` § Iteration Budget Gate and loop-breaker, the sections that do.
### CalDAV task-tracker provider (Nextcloud Tasks, iOS Reminders); review reports unknown-value findings instead of skipping them
A second task-tracker adapter, `caldav` (RFC 4791/5545), so an instance is not bound to Super