fix(major2): --proposal-review komponerer med --resume, og armen som beviser det

To funn, ETT commit, fordi de deler vitnet ved konstruksjon: 02159d21
(run.py:2738, PLAN_EXECUTE_DRIFT) er vakten, da485928
(tests/...:1344, MISSING_TEST) er armen som skulle sett den. AA dele dem ville
krevd en kastbar duplikattest.

VAKTEN: `if args.checkpoint_dir is not None` nektet --proposal-review for HVER
argv med en checkpoint-katalog - men --resume KREVER --checkpoint-dir
(run.py:2776), saa komposisjonen nekten selv anbefaler («pass --proposal-review
at --resume instead») var unaabar, og run.py:2218-helpen, README.md:546 og
MAJOR-2-raden beskrev en sti ingen argv kunne ta. Vakten nekter naa en PARK
(en etappe som returnerer foer noen kandidat finnes), aldri et LOEFT.

ARMEN: `test_the_door_composes_with_resume` sendte hverken --resume,
--checkpoint-dir eller --review-inbox - den var en vanlig enkeltkjoering T13
allerede dekket, altsaa groenn mot nettopp den defekten den var navngitt for.
Den driver naa en EKTE resume: dag 1 parkerer en ekte plan-review gjennom
run.main, ekspertens svar legges i en ekte innboks, og dag N sender
--resume ... --checkpoint-dir ... --review-inbox ... --proposal-review med
run_project innspilt og _refuse_model som kontroll paa null modellkall.

MAALT, i denne rekkefoelgen (Iron Law): armen skrevet FOERST og kjoert mot
uendret vakt -> ROED med nettopp nektlinja i stderr («pass --proposal-review at
--resume instead»), calls == []. Etter vakt-fiksen: 49 passed i fila, og
park-nekten (test_the_door_and_a_parked_exploration_contradict, M39) staar
groenn - den sender --explore uten --resume.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-05 22:01:49 +02:00
commit c81a90c88a
2 changed files with 130 additions and 11 deletions

View file

@ -2735,7 +2735,12 @@ def main(argv: list[str] | None = None) -> int:
file=sys.stderr,
)
return 1
if args.checkpoint_dir is not None:
# ``and args.resume is None`` is load-bearing, not defensive: --resume REQUIRES
# --checkpoint-dir (:2776 below), so a bare --checkpoint-dir test refused the very
# composition this message recommends, and the help text, README and the invariant row all
# described a path no argv could take. What is being refused is a PARK (a leg that returns
# before any candidate exists), never a LIFT (a leg that runs the pipeline to a candidate).
if args.checkpoint_dir is not None and args.resume is None:
print(
"run refused: --proposal-review and --checkpoint-dir: a parked exploration "
"returns before any candidate exists, so the review would never be asked — pass "

View file

@ -1325,14 +1325,124 @@ def test_the_door_and_a_parked_exploration_contradict(
assert "--resume" in err
def test_the_door_composes_with_resume(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
"""A3, VERIFIED rather than deferred: the resume block yields the parked exploration's mandate
and falls through to the SAME full-run dispatch, so the reviewer built at the CLI is reached.
Detach point: refusing ``--resume`` together with the door, or forgetting to pass the reviewer
on that path.
#: Day 1's parked exploration, driven exactly as an operator drives it: a real ``--explore`` leg
#: with ``enable_plan_review`` armed and NO reviewer at the terminal, so the loop parks. The
#: proposal-review door is deliberately absent here — that argv is the refusal this arm's sibling
#: (``…and_a_parked_exploration_contradict``) keeps green.
_RESUME_RUN_ID = "proposal-review-resume"
_RESUME_MANAGER_REPLY = json.dumps(
{
"is_request_satisfied": {"reason": "r", "answer": True},
"is_in_loop": {"reason": "r", "answer": False},
"is_progress_being_made": {"reason": "r", "answer": True},
"next_speaker": {"reason": "r", "answer": "hypothesiser"},
"instruction_or_question": {"reason": "r", "answer": "go"},
}
)
def _resume_replies_file(tmp_path: Path) -> str:
"""Both time-scales' roles in one file: the exploration's three and the debate's two. A
missing ``navigator`` key is the ``KeyError`` M40 of økt 64 the resume leg builds an
exploration too, so it needs the same roster the park leg did."""
path = tmp_path / "resume-replies.json"
path.write_text(
json.dumps(
{
"proposer": _run_reply(_CLI_MEASURE, 30_000),
"checker": "VERDICT: APPROVE",
"manager": _RESUME_MANAGER_REPLY,
"navigator": "NAVIGATOR: read the index.",
"hypothesiser": "HYPOTHESIS: "
+ json.dumps({"label": "Night setback", "rationale": "y"}),
}
),
encoding="utf-8",
)
return str(path)
def _resume_config_file(tmp_path: Path) -> str:
path = tmp_path / "resume-exploration.json"
path.write_text(
json.dumps(
{
"max_rounds": 4,
"max_tokens": 200_000,
"max_stall_count": 2,
"max_reset_count": 1,
"max_plan_revisions": 2,
"enable_plan_review": True,
}
),
encoding="utf-8",
)
return str(path)
def _park_then_answer(tmp_path: Path) -> None:
"""Day 1 and the days between: park a real plan review, then drop the expert's answer in the
inbox. Both halves go through the shipped surfaces a hand-written question file would test
the fixture, not the door."""
outbox = tmp_path / "outbox"
assert (
run.main(
[
_RUN_PID,
"--docs-dir",
str(_BUNDLE_DIR),
"--bundle-dir",
str(_BUNDLE_DIR),
"--explore",
"Find the cheapest saving.",
"--explore-config",
_resume_config_file(tmp_path),
"--scripted-replies",
_resume_replies_file(tmp_path),
"--outbox-dir",
str(outbox),
"--checkpoint-dir",
str(tmp_path / "checkpoints"),
"--run-id",
_RESUME_RUN_ID,
]
)
== 0
)
question = json.loads(
(outbox / f"{_RESUME_RUN_ID}-plan-review.json").read_text(encoding="utf-8")
)
inbox = tmp_path / "review-inbox"
inbox.mkdir(parents=True, exist_ok=True)
(inbox / f"{_RESUME_RUN_ID}-plan-review-answer.json").write_text(
json.dumps(
{
"run_id": _RESUME_RUN_ID,
"request_id": question["request_id"],
"decision": "approve",
}
),
encoding="utf-8",
)
def test_the_door_composes_with_resume(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
"""A3, and the WITNESS the review found missing: the ``--checkpoint-dir`` refusal points the
operator at ``--resume``, and ``--resume`` itself REQUIRES ``--checkpoint-dir`` so before
this arm the recommended composition was refused by the very message that recommended it, and
nothing in the suite could see it. Detach point: reverting the guard to a bare
``if args.checkpoint_dir is not None``.
The argv is a REAL resume: day 1 parks a real plan review through ``run.main``, the expert's
answer is dropped in a real inbox, and day N passes ``--resume --checkpoint-dir
--review-inbox --proposal-review``. The arm it replaced sent none of those three flags and
was a plain single-project run T13 already covered green against the defect it was named
for. ``run_project`` is a recorder, so what is measured is the WIRING at the fall-through, and
``_refuse_model`` proves the recorded call is the ONLY place a model would have been reached
from."""
_park_then_answer(tmp_path)
``run_project`` is replaced by a recorder, so the arm measures the WIRING rather than a whole
resumed exploration."""
calls: list[dict[str, Any]] = []
async def _recorder(*args: Any, **kwargs: Any) -> Any:
@ -1349,15 +1459,19 @@ def test_the_door_composes_with_resume(tmp_path: Path, monkeypatch: pytest.Monke
"--bundle-dir",
str(_BUNDLE_DIR),
"--scripted-replies",
_cli_replies_file(tmp_path),
_resume_replies_file(tmp_path),
"--outbox-dir",
str(tmp_path / "outbox"),
"--run-id",
_RUN_ID,
"--checkpoint-dir",
str(tmp_path / "checkpoints"),
"--review-inbox",
str(tmp_path / "review-inbox"),
"--resume",
_RESUME_RUN_ID,
"--proposal-review",
]
)
assert len(calls) == 1
assert len(calls) == 1, "the resume must fall through to the full-run dispatch, not be refused"
assert calls[0]["proposal_reviewer"] is not None