fix(s7b): BudgetExceeded refuses instead of tracebacking, approaches land in exploration.json
Fiks-ordre 20260904T070930Z-8335015575-from-.claude. Two scoped defects the
S7b syretest observed and left unfixed (docs/2026-09-04-syretest-s7b-k2.md
§ 3.6):
1. `BudgetExceeded` (a RuntimeError, econ 56) could leave `--explore` as a
raw Python traceback from TWO raise sites: the exploration loop's own
round/token cap (`explore()`/`resume_exploration()`, uncaught in
`main()`'s exploration `try`), and `generate_via_llm`'s retry loop when a
mandate's own-proposal evaluation hits an unparseable reply (the full-run
dispatch's `except` tuple only knew `ValueError`/`FileNotFoundError`/
`ValidationError`). Both now end as `run refused: {exc}` on stderr, rc 1,
same shape as every other loader refusal in run.py.
2. `{run_id}-exploration.json` carried rounds/tool_calls/plan_reviews/
quick_validations but not the approaches the loop actually shaped — those
stood only in the stdout mandate announcement. `explore.trace_payload`
now takes a required `mandate` keyword and renders `mandate.approaches`
under an "approaches" key, so an operator reading the artefact days later
(the whole point of the async U12 door) can recover what the run decided
to evaluate without the terminal.
Fifteen mutations across three detach points, all red against the full
suite: the exploration-loop except clause (2 red), the full-run except
tuple (1 red), and the approaches rendering (1 red) — plus a control per
fix proving the happy path is unaffected. Green control 1295 passed / 5
skipped (supersett of S7b's 1290/5, 0 removed), golden
demo-transcript.stdout byte-unchanged (shasum -a 1 = ea8c534…), ruff +
mypy clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
6f8330cc61
commit
5d8844fef5
7 changed files with 479 additions and 26 deletions
|
|
@ -375,7 +375,9 @@ class ExplorationTrace:
|
|||
tokens_spent: int = 0
|
||||
|
||||
|
||||
def trace_payload(trace: ExplorationTrace, *, stop: str | None, completed: bool) -> dict[str, Any]:
|
||||
def trace_payload(
|
||||
trace: ExplorationTrace, *, stop: str | None, completed: bool, mandate: Mandate | None
|
||||
) -> dict[str, Any]:
|
||||
"""The ONE rendering of a trace into plain data for ``outbox.write_exploration``.
|
||||
|
||||
Plain mappings only, so the RAW output layer stays MAF-free (the ``write_parse_failures``
|
||||
|
|
@ -384,11 +386,32 @@ def trace_payload(trace: ExplorationTrace, *, stop: str | None, completed: bool)
|
|||
``completed`` is a required field rather than an inference from ``stop``. With no result there
|
||||
is no stop, and a ``stop: null`` meaning BOTH "concluded normally" and "we never found out"
|
||||
is exactly the silence ``ProvenanceStamp.cost_baseline_anchored`` was made required to close.
|
||||
|
||||
``mandate`` is required for the same reason, applied one field over: before this the
|
||||
approaches a loop SHAPED reached only the stdout announcement (``mandate.announce``), so a
|
||||
caller who kept the artefact but not the terminal had no way to learn what the run had
|
||||
decided to evaluate (measured on K2, S7b's own uttalte grense). ``None`` is not "zero
|
||||
approaches" — ``ExplorationResult.mandate`` always carries at least the seeded ones, so the
|
||||
only way to reach here with no mandate is a run that never produced one (a cap that fired, or
|
||||
a park), which is exactly what ``completed=False`` already says. Collapsing that into an
|
||||
empty list would make "the loop formed no approaches" and "the loop never got that far"
|
||||
unreadable from each other.
|
||||
"""
|
||||
return {
|
||||
"completed": completed,
|
||||
"stop": stop,
|
||||
"tokens_spent": trace.tokens_spent,
|
||||
"approaches": [
|
||||
{
|
||||
"id": approach.id,
|
||||
"label": approach.label,
|
||||
"description": approach.description,
|
||||
"affected_codes": list(approach.affected_codes),
|
||||
"claimed_saving_nok": approach.claimed_saving_nok,
|
||||
"bundle_id": approach.bundle_id,
|
||||
}
|
||||
for approach in (mandate.approaches if mandate is not None else ())
|
||||
],
|
||||
"rounds": [
|
||||
{
|
||||
"round_index": entry.round_index,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue