Skip to content

Amendment 23 act 2, slice 2b: lane-start and lane-handoff write the Rule 3 stamp and the PAUSED block with one write-handoff through the lane's checkout, W moves only by fast-forward, and a launch refuses only at a restart's fences - #20

Draft
brettheap wants to merge 22 commits into
feat/198-a23-slice-2a-handoff-verbsfrom
feat/198-a23-slice-2b-start-handoff

Conversation

@brettheap

@brettheap brettheap commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Lane: openreposhape-2 (openRepoShape-2)
Claim: opensoft/openRepoTools#198 (comment)

Re-homed from opensoft/openRepoTools#316

Receipt: the 7 commits, old opensoft/openRepoTools sha to code-leg sha

Each subject ends with the slice's reference to the act 2 issue, left out here.

# old (opensoft/openRepoTools) new (opensoft/openRepoTools-code) subject
1 f1b19e9 068f190 lane-start writes the Rule 3 stamp with one write-handoff --stamp through the lane's checkout, fast-forwards W before and after its writes, and is refused after its first write only by a restart's own fences
2 84633df ce73c92 lane-start reads each restart-intent field to the end of the answer, so a launch no longer dies with 141 when an early-exiting awk closes the pipe under printf
3 a1f658e b848bd8 lane-handoff writes the PAUSED top block with one write-handoff --prepend through the file handoff-path names, so its handoff is committed and pushed under the checkout's lock and never by its own pull and push in W
4 37b8806 3ce57f6 The supervised-restart cases run /ctx through the lane's own checkout, and a stamp that could not be published, or that the checkout refused, launches with the bound file at its pre-image digest
5 c8ac6ca dbfbec6 lane-start launches with no first prompt where the lane's own copy of its handoff is not there, naming that file and the command that makes the checkout, and never reads W's copy in its place
6 127ac75 dc50eb5 lane-start and lane-handoff say, under LANES_NO_GIT=1, that the handoff is written and nothing is committed, where their exit-0 words claimed a commit and a push
7 77e94b9 31990c4 lane-start says, where the lane's checkout refused a plain launch's stamp, that the first prompt is the checkout's copy and origin's differs, naming both copies and the command that brings the checkout up to date

Stacked on

Slice 2a' on the code leg, feat/198-a23-slice-2a-handoff-verbs at 56a9b87: 2a's round head 1320847 (old 7bc572c, A60) with slice 1b at 34799b7, which brings slice 1a's merge of the code leg's main (577110e), merged as 2b1a232, and A61 on top of it (2a's decision 32: the fix for the two cases CI failed at 2b1a232). This branch merged 1320847 at e12b369, 2a' at 2b1a232 as a44b9d2, and 2a' at 56a9b87 as 4031100, all clean. This PR's own diff is git diff 56a9b87 HEAD: lane-start, lane-handoff and their tests.

Why one PR for both scripts (the plan's §3, slice 2b): /ctx's round trip compares paths with -ef and by digest. lane-handoff binds the restart intent, and lane-start checks it. If either script moved alone, every /ctx under the opt-in would refuse as "different handoffs".

This round: lane openRepoTools-3's review of opensoft/openRepoTools#316 at 37b8806, and its review of opensoft/openRepoTools#311

Lane openRepoTools-3's review of opensoft/openRepoTools#311 (comment 6376170218) touched this slice in three places (982, question 6, 990), and its review of opensoft/openRepoTools#316 said LAND AFTER FIXES.

Each fix has a case that was red before it, or, where the case pins what already held, a mutant the case kills; each has its own commit. The docstring change (question 3) needed neither. Commits before 31990c4 were made on the old root and are named by their old shas.

item what it found answered in decision
727, question 2 5d fell back to W's copy, against S2's "never through W", and kept 723 open old c8ac6ca (dbfbec6) 24
721, 728, question 4, rv2b SHOULD 1 the exit-0 words claimed a commit under LANES_NO_GIT=1 old 127ac75 (dc50eb5) 25
725 a refused plain stamp delivered the checkout's older block and did not say so old 77e94b9 (31990c4) 26
726 the printed re-run of a refused stamp carries none of the restart's fences e97ad3f 27
729 the --restart respawn dropped LANES_WORKSPACE_ROOT, LANES_REPO and LANES_FILE 67e6da5 28
730, 731 the duplicate-row refusal sent a merge to W under the opt-in; retired_word's pipe was missing from "Not changed" 2bd3ea2 29
question 3 the t018 docstrings still said "byte for byte" fb4a7b0 30
opensoft/openRepoTools#303's fit (c); 982, question 6 and 990 of the review of opensoft/openRepoTools#311 the register's PAUSED line said handoff refreshed whatever happened; the 6 arm said "kept on HEAD" c1f2928, 439699f 31
rv2b's NITs 1, 2, 3, 5 5d read a path a peer had moved; composite asserts; two copies of each helper; WORKBENCHES_ not scrubbed 2e4d7dd 32
733 (in part), the battery's survivors 26 of m2b's 59 mutants survived the bench e5c3326 33
merge 2a's round head, which 723, 722 and 724 rest on e12b369
MUST 723 a sibling lane's cell was stamped, written and delivered 58309ba 34
724 after 2a's 603, a changed pre-image would read as lost ownership 615cfdd 35
722, and 721's last case an 8(d) handoff at both call sites 5bf9bbb 36
733 (M1, M2, M4) 5c's two restart dies and a why with a tab had no case c12f078 37
merge 2a' (2a with slices 1b and 1a and the code leg's main) a44b9d2
2a's A61 CI's git 2.56.0 read the clone that 2a's other-owner case makes unreadable, so two cases of tests/test_a23_write_handoff.py failed on #18 at 2b1a232 (2a's decision 32), and this slice carries the same file merge 4031100

Decisions

  1. Routing stays opt-in. 2b makes the calls, and the verbs route themselves.
  2. The refreshes run only under the opt-in with the default resolution, and never in a dry run (which prints a PLAN line for each).
    • Without the opt-in, W is the write checkout, and every write's S3 fast-forwards it under its lock. A refresh outside that lock could race a writer there.
    • An explicit LANES_REPO or LANES_FILE is the caller's register (the seam), so W is not what is read.
    • Every exit of refresh-workspace is a note.
  3. rows_for_lane reads W after the first refresh (the plan's §2.7).
    • It reads W's working tree where the refresh exited 0. Otherwise it reads origin/main's register as W's tracking ref has it, and falls back to the working tree, with a note, where W has no such ref. No temporary file is used, since exec skips the EXIT trap.
    • This replaces slice 1a's lane_register_follow in lane-start. lane-end keeps its own follow (slice 4's).
  4. The lane's checkout is made at "5. the row" by lane-checkout <lane> --create, under the opt-in with the default resolution, and never in a dry run. A refusal is a note, and the launch goes on (1a's SHOULD 2). After the row, the writable handoff path that handoff-path names is printed beside the row's spelling.
  5. 5c is one LANES_LANE=<lane> write-handoff <lane> --stamp <line> --subject <subject>, with no LANES_NO_FETCH. Under an active restart it adds --restart <op> <gen> <attempt> starting <transcript> --expected-digest <digest>.
    • Exits 0, 3 and 6 mean the file was written, and ri_expected_digest becomes the digest on stdout.
    • Exit-code notes: 3 says "look before writing it again". 6 said "kept on HEAD" (superseded by 31), and names the publish act. Any other exit says "written and was NOT published". None promises a carry (S3).
  6. hs_git_net and hs_carry_note are deleted, as the plan says. That settles lane openRepoTools-3's request (282) that hs_carry_note's middle branch name the publish act: there is no carry note left to reword, and no commit of lane-start's outside a lock.
  7. The reads follow the write. (Its fallback to W's copy is superseded by 24.) The restart binding and 5d read the file that handoff-path names, which is the one write-handoff writes.
    • Under an active restart this is strict.
    • For any other launch, a path that is not a file falls back to hs_resolve (W's copy), with a note. A checkout that could not be made then does not cost the first prompt.
  8. "Never refuses the launch" is made true: after the first write, only the restart fences die.
  9. The stale-checkout recoveries name no pull and no bare push.
    • Where the register read is W, they name lanes-edit.sh refresh-workspace. Where it is a caller's checkout, they name git -C <checkout> fetch origin main && git -C <checkout> merge --ff-only origin/main.
    • "Push the register from here" becomes "publish the register from here, then re-run: LANES_LANE=<lane> lanes-edit.sh commit "<what it is>"", the publish act, which also carries a pending hand edit.
  10. request-handoff gains LANES_LANE in its env. The printed step line is unchanged.
  11. lane-handoff's file is handoff-path <lane> --create, run with its stderr shown and without LANES_NO_FETCH, so the cell is read from the freshest origin/main.
    • 8 is "no handoff named": the old note, with the block printed.
    • Any other exit is a note naming it, with the block printed.
    • It does not check first that the file exists: write-handoff's S3 fast-forwards a stale checkout, and its 8 means there is nothing to write into.
  12. One LANES_LANE=<lane> write-handoff <lane> --prepend <block> --subject "handoff(<lane>@<ws>): PAUSED, <why>", with [--restart <op> <gen> 0 preparing none] under a restart, no LANES_NO_FETCH and no --expected-digest, replaces publish-handoff and the commit block.
    • The block file is top_block's output alone. write-handoff adds --- and a blank line, the same bytes as before.
    • handoff_written=1 exactly where stdout carries the path<TAB>digest line: exits 0, 3, 6, and S3's 2, which writes and does not commit. handoff_file becomes that path, so the pending intent and its read-back bind the file that was written.
    • Each exit gets a note in place of the commit block's two step lines. The helper's own words and git's are printed, no longer sent to /dev/null.
  13. append-line gains LANES_LANE. The skill test's append-line ("PAUSED — lane …") regex still finds the one call.
  14. A --restart respawn line carries LANES_ROUTE_WRITES when it is set, so the supervisor and the lane-start it runs resolve the checkout the intent bound. Without it, an opted-in /ctx binds the lane's checkout, and its respawn reads W and refuses with "different handoffs". The new round-trip case shows that refusal.
  15. hs_resolve's case (c) is no longer a stamp target. The stamp goes only where write-handoff resolves the row's cell, in the checkout writes go through (S5: never W). The shell suite's CN-2 becomes "not stamped, the line printed".
  16. handoff-path after the row also runs in a dry run, with LANES_NO_FETCH=1 there. An active restart's --no-launch is a dry run, and 5d's strict branch needs the bound file in it. Elsewhere it fetches, so a new lane's first start still finds the row it pushed from its checkout before W's ref moved.
  17. The restart binding's handoff-path runs with LANES_NO_FETCH=1, like every other restart read there. It keeps lane-start's two refusals word for word: "the restart handoff the row names is missing" and "the restart intent and register name different handoffs".
  18. Under a restart, the preparing bind names the file handoff-path gave, at handoff_digest of it now (none where it is absent), before the write.
    • A file that is not there is then write-handoff's 8: the block is printed, and the gate refuses with "the HANDOFF WAS NOT REFRESHED", as before (the shell suite's repoHF-12).
  19. The PAUSED subject spells every control character of the why as a space (subject_text, LC_ALL=C). write-handoff refuses a subject that carries one (64, nothing written). The door already refuses a newline and a carriage return, so this only keeps a why with a tab refreshing the handoff, as the old git commit -m did.
  20. lane-start reads the restart intent's answer to its end (intent_field), for eri_state, eri_op and the twelve ri_* fields.
  21. The atomic-stamp case is rewritten, not deleted. test_restart_atomic_stamp_failure_preserves_original_for_retry asserted exit 2. Now a stamp that could not be published atomically is a note (decision 8): the launch exits 0, the bound file is unchanged, and the intent is starting at the pre-image digest. The retry stamps it once.
  22. The t018 single-repository goldens move exactly where lane-handoff's commit act printed (t018-handoff/single-repository-decoys.golden:34-36,108-110 and t018/single-repository.golden:142-144).
    • handoff commit: <subject> and handoff pushed to origin/main become write-handoff's own lanes-edit: committed: <subject> and lanes-edit: pushed origin/main (attempt 1), before handoff refreshed: …. After it comes lane-handoff's line naming the exit it read.
    • The subject carries the why, as decision 12 sends it. The old printed subject left it out.
    • Nothing else in either golden moves. Both were re-captured from this branch's tools by each module's own --capture, normalised as before.
  23. Hygiene covers both scripts. _NO_PULL_FILES gains lane-start and lane-handoff. The new test_no_lane_script_promises_the_carry[lane-start|lane-handoff] reads both scripts. At the base, lane-handoff had a carry promise ("captured by the next lane's commit") and two pull --rebase lines.
  24. No first prompt where the lane's own copy is not there (727, question 2; supersedes 7's fallback). 5d reads only the file handoff-path names, for every launch. Where it is missing, the launch goes on with no first prompt, and one note names the missing file and, under the opt-in, LANES_ROUTE_WRITES=1 lanes-edit.sh lane-checkout <lane> --create. W's copy is never read in its place. hs_resolve and hs_workspace_root lost their last caller and are deleted, which retires 7's fallback and 15's case (c).
  25. The exit-0 words say what was done (721, 728, question 4, rv2b SHOULD 1). Under LANES_NO_GIT=1, lane-start says "Rule 3's stamp is written in and NOT committed: LANES_NO_GIT=1 commits and pushes nothing", and lane-handoff says "the block is written in and nothing is committed or pushed". 36 words every other 0.
  26. A refused plain stamp names a checkout copy that is behind origin (725). Where 5c wrote nothing on a plain launch, 5d compares the checkout's file with its own origin/<branch>, which S3 has just fetched (blob ids). Where they differ, one note names both copies and git -C <checkout> merge --ff-only origin/<branch>. The launch goes on with the checkout's copy, the file this lane's writes go to.
  27. The printed re-run waits for ready under a restart (726). Where the stamp could not be written, lane-start prints the exact write-handoff --stamp re-run. Under an active restart a second line says to run it only once the restart is confirmed ready, since that write carries none of the restart's fences and exits 7 while the intent is starting. A plain launch's words are unchanged.
  28. The respawn carries the register's seams (729). Beside LANES_ROUTE_WRITES, the --restart respawn line carries LANES_WORKSPACE_ROOT, LANES_REPO and LANES_FILE where each is set, quoted as the others are. lane-handoff never sets one of its own. Without them the new pane resolved the default W, and a /ctx with a register of its own failed closed. This predates 2b.
  29. Under the opt-in a duplicate row is never merged in W (730). The rows are read from W, and the refusal said to merge them "by hand in" W's register and commit with lanes-edit.sh commit, which under the opt-in commits in the lane's own checkout. Now, under the opt-in:
    • where origin/<branch>, as W last fetched it, has fewer than two such rows, the duplicate is only in W's working tree, and the refusal names it a stray in W, which a person takes out;
    • where origin carries the duplicate, the merge is a hand edit of the lane's own checkout's register, published from there with LANES_LANE=<lane> LANES_ROUTE_WRITES=1 lanes-edit.sh commit.
    • The second arm is not reached with this helper, as lane openRepoTools-3's review says ("reachable only through an uncommitted duplicate in W"): canon-lane reads the published register after W is fast-forwarded and refuses a published duplicate first (Amendment 15(d)), and lane-dir and the managed-ledger fence refuse it too. A case pins that, and the code's comment says so. Its mutant (I6c) is not pinned.
  30. The t018 docstrings name slice 2b's one exception (question 3). Both module docstrings and both capture() docstrings say which lines moved (the commit act's three hunks, decision 22), why, and that every other line is c45a452's. The case names are kept.
  31. The register's PAUSED line says what the write did, in the skill's words (Most reads of a lane's log cannot tell a failed read from a lane with no log: lane-dir, lane-profile, lane-agent, lane-transcript, history, lane-last, binding and lane-trees answer 8 ("none") over a log git could not read (from #290) openRepoTools#303's fit (c); question 6, 982 and 990 of the review of Amendment 23 act 2, slice 3: the handoff skill locates its file with handoff-path and writes it with ONE write-handoff --whole call from the lane's own checkout, so it spells, commits in, pulls and pushes no checkout of its own (#198) openRepoTools#311; slice 3's F23 and F24). lane-handoff's 4(b) line keys on what write-handoff did:
    • handoff refreshed on a 0, LANES_NO_GIT=1 included: it claims no commit, and the step line says what was done (25, 36);
    • handoff written, NOT published (write-handoff exited <n>) for a 2 or a 6 that wrote;
    • handoff written, its publication NOT confirmed (write-handoff exited <n>) for a 3, and for any other exit after a write, which claims nothing either way;
    • handoff NOT refreshed (<why>) where nothing was written: no handoff found, handoff-path failed, write-handoff not called, or it exited n and wrote nothing.
    • Exit 6 is not one state (982, which names lane-handoff:2689). git add or commit failed and nothing is committed, or the commit is kept on HEAD and named; and since 2a's 604, an 8(d) handoff's commit or push in its own repository failed. Both scripts' 6 words now say the file is written and NOT published, point at the helper's words for whether and where a commit was made, and name the publish act only for a commit kept in the lane's register checkout. This supersedes 5's "kept on HEAD".
    • The shell suite's RV-B2 (a) assertion moves with lane-start's words (CI only). No reader parses the suffix. Most reads of a lane's log cannot tell a failed read from a lane with no log: lane-dir, lane-profile, lane-agent, lane-transcript, history, lane-last, binding and lane-trees answer 8 ("none") over a log git could not read (from #290) openRepoTools#303's fits (a) and (b) wait for its R7, which is not pushed.
  32. rv2b's NITs.
    • NIT 1: where a peer moves the lane's handoff, and the row's cell with it, while this launch stamps it, the write's replay follows the cell, and 5d now reads the file the write printed, not the path handoff-path named before it.
    • NIT 2: fifteen composite asserts (S9073) are split.
    • NIT 3: W_reflog, assert_W_moved_only_by_fast_forward and a new no_auto_gc in test_a23_lane_start_read replace their copies. _published and published stay two, because the row-stamp sandbox has no origin and returns text, and the claude-current one returns bytes.
    • NIT 4 needs no change here: slice 3 spells the PAUSED subject as lane-handoff does.
    • NIT 5: WORKBENCHES_ joins both SCRUBBED_PREFIXES.
  33. The battery's survivors are pinned where a case can tell them apart. m2b's 59 one-line mutants of this slice's code left 26 alive on the bench. Fourteen are now killed by new cases or assertions (LS02, LS03, LS06, LS07, LS10, LS12, LS17, LS19, LS29, LS41, LH03, LH13, LH14, LH16), and five by this round's other cases (I1a, I2a, I2b, LS45, LS49). The rest are under "Not changed".
  34. A sibling lane's cell is neither written nor delivered (723, on 2a's 601). With 601 in the translation that write-handoff, handoff-path and set-restart-intent share (A54), and 5d reading only the file handoff-path names (24), a row cell naming another lane's handoff in that lane's clone of the register beside W is refused at every call site. lane-start writes nothing there, delivers none of its block and launches with no first prompt, naming why. lane-handoff writes nothing there, prints its block, and records handoff NOT refreshed (handoff-path exited 2). No 2b code changed for it; one case per call site pins it.
  35. The bound file is asked before a 7 (724). Since 2a's 603 the restart fences' 2s are 7, so a pre-image a peer changed after binding comes back as 7 too. 5c now asks hs_bound_unchanged first and dies "the restart handoff changed before its owned resume stamp"; a 7 with the bound file unchanged is still "lost ownership". Both exit 2, and S3's 2 stays a note.
  36. An 8(d) handoff at both call sites (722, and 721's last case). Lane openRepoTools-3 read Amendment 23 S2 as governing, and 2a's 604 commits and pushes an 8(d) handoff in its own repository, as e2037b9 did. Pinned at both call sites: the stamp and the PAUSED block are committed there by pathspec and pushed to that repository's origin, its tree clean, and the register takes no commit for them. A 0 may still commit nothing (an 8(d) handoff in no work tree, a clone of the register not beside W), so both scripts' exit-0 words now point at the helper's note "or that nothing was committed", as the skill's 0 arm does (F24). lane-start's 0 says "handoff stamped through write-handoff, which exited 0: : (its words above say what it committed and pushed, or that nothing was committed)". The three golden lines of lane-handoff's exit-0 step and the shell suite's :3822 move with them. An 8(d) stamp whose push fails there is a 6 with the helper's words (982's 8(d) case).
  37. 5c's two restart dies and a why with a tab have cases (733's M1, M2 and M4). A 7 with no written line and the bound file unchanged dies "lost ownership" (M1, the battery's LS46); 35's case is M2's (LS47); a why with a tab refreshes the handoff with a space in the subject (M4, LH04).

What changed

  • lane-start:
    • two ls_refresh calls (decision 2) and the rows source (3);
    • section 5's checkout and printed handoff path (4, 16);
    • 5c as one write-handoff --stamp (5, 8);
    • 5d and the restart binding read the written file (7, 17);
    • the restart-intent reads (20);
    • the recoveries' text (9) and request-handoff's env (10);
    • the header's "never refuses" paragraph, the 5c comment and the env doc (LANES_ROUTE_WRITES).
  • lane-handoff:
    • section 2's file is handoff-path --create (11);
    • the write is one write-handoff --prepend, and the commit block is deleted (12, 18, 19);
    • append-line's env (13);
    • the --restart respawn line (14);
    • the header's env doc.
  • Tests:
    • new tests/test_a23_lane_start.py (8 cases at first, 24 now) and tests/test_a23_lane_handoff.py (3 cases at first, 13 now), on test_lane_start_row_stamp's sandbox with the default resolution and the opt-in;
    • slice 1a's lane-start cases in tests/test_a23_lane_start_read.py follow decisions 3 and 4: W moves only by fast-forward, and the checkout-refusal cases keep "could not be made" and "exited 4";
    • tests/test_supervised_restart_regressions.py: the pipe-sized restart-intent answer case (20); the default-resolution variant (routed_restart_box, prepare_restart_routed, sharing bind_pending with prepare_restart); test_ctx_round_trip_through_lane_checkout; test_refused_stamp_with_restart_intent_still_launches; and the atomic-stamp case's rewrite (21);
    • the shell suite tests/test_lane_helpers.sh, run in CI only:
      • :3189's recovery text (9);
      • RV-B2 (a), a refused push: exit 0, the stamp committed and kept on HEAD, no carry sentence;
      • RV-B2 (b), LANES_WORKSPACE_ROOT=$WS2: not stamped, the line printed (15);
      • RV-B3 (c): no pull, the stamp pushed;
    • tests/test_repo_hygiene.py (23);
    • the two t018 goldens (22).

Functions this PR changes in lane-start and lane-handoff

This list is for lane openRepoTools-4's opensoft/openRepoTools#303 ("every read of a lane's log reports whether it happened"), which may need these two scripts. It was compiled from git diff e2037b9 HEAD by function body, and checked again against git diff 1320847 HEAD for this round.

  • lane-start (56 → 56 functions):
    • added: ls_refresh, ls_register_text, ls_rows_source, hs_bound_unchanged, intent_field;
    • deleted: hs_git_net, hs_carry_note, lane_register_follow, and, with 24, hs_resolve and hs_workspace_root;
    • body changed: rows_for_lane, row_names_for_lane;
    • top-level code changed: the refreshes, the restart-intent reads (eri_*, ri_*, the binding), the stale-checkout recoveries, request-handoff's env, section 5, 5c and 5d.
    • No log read changed: rows_for_lane reads the register, not a log, and the log and lane-reconcile calls are as they were.
  • lane-handoff (47 → 48 functions):
    • added: subject_text;
    • no other function's body changed: top_block, sanitize, cut_to_line, the supervisor and every log, row and lifecycle function are as they were;
    • top-level code changed: section 2's file resolution, the write (section 3), append-line's env in section 4(b), the --restart respawn line and the header.

Tests

One pytest process at a time, with every LANES_*, LANE_START_*, TMUX* and WORKBENCHES_* variable unset, a refusing gh stub first on PATH, GH_TOKEN=bogus and GIT_SSH_COMMAND=false. The bench was loaded by other lanes' runs throughout.

The module set (run at a44b9d2, the merge of 2a'; the four rows marked 4031100 run again at this head, the merge of 2a' with A61, which changes tests/test_a23_write_handoff.py and nothing else):

module result
tests/test_a23_lane_start.py 24 passed (4031100)
tests/test_a23_lane_handoff.py 13 passed (4031100)
tests/test_supervised_restart_regressions.py -k "routed or refused_stamp or atomic_stamp or restart_owned or respawn or ctx or second_ctx or plain_handoff or handoff_publishers or bound_handoff or lost_ownership or W_behind or larger_than_a_pipe or missing_checksum or interrupted_stamp or opening_prompt or explicitly_clears_previous" 24 passed, 157 deselected
tests/test_a23_lane_start_read.py 10 passed
tests/test_lane_start_row_stamp.py 3 passed
tests/test_lane_start_claude_current.py 34 passed
tests/test_handoff_skill_80_85.py 41 passed
tests/test_triad_lane_tooling.py 5 passed, 22 skipped
tests/test_triad_lane_tooling_handoff.py 1 passed, 21 skipped
tests/test_repo_hygiene.py 239 passed, 18 skipped (4031100)
tests/test_a23_write_handoff.py (slice 2a's) 179 passed (4031100)

The triad skips are NEEDS_UPSTREAM, as before. Before the merge of 2a', hygiene's test_the_root_carries_the_line_ending_rule failed on every commit of this round for want of .gitattributes, which came with the code leg's front door (6314fc8) through 2a'. Not run on the bench, and asked of CI: the shell suite (tests/test_lane_helpers.sh, whose :3755 and :3822 move in this round), the whole of tests/test_supervised_restart_regressions.py, and the full suite.

Red first, then green, then a killed mutant, for each item of this round:

item red green mutants killed
726 (e97ad3f) at 31990c4: no "only once this restart is confirmed ready" 1 case; supervised -k "refused_stamp or atomic_stamp or restart_owned" 3 passed I4a, LS49
729 (67e6da5) at e97ad3f: no LANES_WORKSPACE_ROOT= in the respawn supervised respawn/ctx list 10, test_a23_lane_handoff 4, 80_85 41 I5a, I5b
730 (2bd3ea2) at 67e6da5: the stray case got W's words; the published pin passed 2 cases; -k "duplicate or launch_creates or fast_forwards" 4, _read 10 I6a, I6b
question 3 (fb4a7b0) docstrings only t018: 5 passed 22 skipped, 1 passed 21 skipped none needed
fit (c), 982, 990 (c1f2928) at fb4a7b0: 6 failed (the four record rows, the LANES_NO_GIT record, the stamp's exit 6) 50 passed; -k "exit_6 or no_git" 2; t018 as above I8a, I8b, I8c, I8d, LS45
F23/F24 (439699f) at c1f2928: the LANES_NO_GIT record and an exit-5 row 50 passed; t018 as above I8e, I8f, I8g, I8h
rv2b's NITs (2e4d7dd) at c1f2928: a moved handoff launched with no first prompt test_a23_lane_start 15, _read 10, test_a23_lane_handoff 8, row_stamp 3, claude_current 34, supervised list 13 I9a
the battery (e5c3326) pins of today's code: each shown by its mutant test_a23_lane_start 21, test_a23_lane_handoff 10 LS02, LS03, LS06, LS07, LS10, LS12, LS17, LS19, LS29, LS41, LH03, LH13, LH14, LH16
723 (58309ba) at e5c3326, before the merge: the sibling's file written, at both call sites on the merge, before any 2b code the merge's own 601; red before it
724 (615cfdd) on the merge: "lost ownership" for a peer's change to the bound file 11 part-2 cases P724a (also LS47, M2)
722 (5bf9bbb) at e5c3326: the 8(d) file left uncommitted; on the merge: the old exit-0 words 11 part-2 cases P722a, P722b
733 (c12f078) pins: each passed before its case was added 11 part-2 cases LS46 (M1), LH04 (M4)

Part 2's four commits were tested as one tree (11 cases passed at 16:12Z) and then made by re-running each item's edit script in order on the merge; the four commits' tree is that tested tree byte for byte.

Before this round, on the old root: run 39893128849 at 37b8806 had tests at 3183 passed, 75 skipped, 1 failed and tests-no-submodule at 2386 passed, 872 skipped, 1 failed, the failure in both being the runner image's known test_a_command_that_ignores_term_is_killed_after_the_grace[host].

Lane openRepoTools-3's answers to this PR's four questions

  1. The refreshes' scope: kept as it is until the default flips (decision 2). Lane 3 also asks that, before the flip, refresh_workspace's fetch and merge take W's mutex (try once, a note if held), and that its stray scan be lock-free. The lock-free scan is 2a's 732 (A58), merged here; the mutex is for the flip.
  2. The fallback to W's copy: dropped (decision 24).
  3. The t018 goldens: the exception is named in the docstrings, and the case names kept (decision 30).
  4. LANES_NO_GIT=1: named (decision 25), with one case per script.

Not changed

Refs opensoft/openRepoTools#198

🤖 Generated with Claude Code

Refs opensoft/openRepoTools#198

🤖 Generated with Claude Code

Brett Heap and others added 21 commits October 22, 2026 15:33
…hrough the lane's checkout, fast-forwards W before and after its writes, and is refused after its first write only by a restart's own fences (#198, slice 2b)

Amendment 23, act 2's slice 2b, its `lane-start` half (lane openRepoTools-3's
finding 282 on #307, its first writer):

- 5c is one `LANES_LANE=<lane> lanes-edit.sh write-handoff <lane> --stamp
  <line> --subject <subject> [--restart ... --expected-digest <sha>]`, with no
  LANES_NO_FETCH. `hs_git_net` and `hs_carry_note` are deleted, and with them
  the publish under W's lock and the add/commit/pull/push in W outside any
  lock. Its exit is a note; under a supervised restart a 7, or a bound file
  no longer at the bound digest, still dies.
- `refresh-workspace` runs before the first read and again before section
  6, under the opt-in with the default resolution and never in a dry run;
  every exit is a note. The row is read from W after the first refresh, or
  from W's origin/main where it did not fast-forward; slice 1a's
  `lane_register_follow` is gone from lane-start.
- The lane's checkout is made at "5. the row" (`lane-checkout --create`, a
  note on refusal), and the writable handoff path `handoff-path` names is
  printed beside the row's spelling.
- The restart binding and 5d read the file `handoff-path` names; an ordinary
  launch whose own copy is missing falls back to `hs_resolve`, said.
- The stale-checkout recoveries name `refresh-workspace` (or fetch and
  `merge --ff-only` for a caller's checkout) and the publish act, never a
  pull or a bare push. `request-handoff` gains LANES_LANE.

Tests: new tests/test_a23_lane_start.py (eight cases, red at e2037b9);
tests/test_a23_lane_start_read.py's lane-start cases follow the new read;
the shell suite's RV-B2/RV-B3 (CN-1..3) and the :3189 recovery text;
hygiene's no-pull rule covers lane-start.

Refs #198

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit f1b19e96637eb3f67f76d645ac6c8fb5b40869a6)
…so a launch no longer dies with 141 when an early-exiting awk closes the pipe under printf (#198, slice 2b)

CI run 39806108007 at f1b19e9: test_restart_missing_checksum_is_not_launch_authority
exited 141 (SIGPIPE) in `tests` and passed in `tests-no-submodule`. Its
stderr ends at "its record refs are", just before the restart-intent read
`eri_state`/`eri_op`, read by `printf ... | awk '... { print $2; exit }'`
under `set -o pipefail`. bash's printf writes that answer a line at a time,
so an awk that has found its line and exited can close the pipe under a
writer that is not done; the writer's SIGPIPE is the pipeline's status and
`set -e` ends the launch with no word said (openRepoTools#281's class).

`intent_field <answer> <field>` reads to the end and keeps the FIRST value,
as the early exit did. It now reads `eri_state`, `eri_op` and the twelve
`ri_*` fields of the restart reads. The other early-stopping pipes of #281's
class (`lane-start` :426, :1336, :2254, :3783, :3789 and lane-handoff's
restart reads) are left for #281.

New test_restart_intent_answer_larger_than_a_pipe_is_read_to_its_end
[missing-checksum|owned-launch]: a lanes-edit.sh wrapper pads restart-intent's
answer to about 4.6 MB, so an early-stopping reader is certain to close the
pipe before its writer is done. It is red at f1b19e9 (both rc 141, with
stderr ending where CI's did), red again with only the `ri_*` reads
reverted (141 after "binding: unchanged"), and green with this change.

Refs #198

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 84633dfa60e8318ffa2c06627b85db2ad7b809b0)
…epend` through the file `handoff-path` names, so its handoff is committed and pushed under the checkout's lock and never by its own pull and push in W (#198, slice 2b)

Amendment 23, act 2's slice 2b, its `lane-handoff` half (lane
openRepoTools-3's finding 282 on #307, its second writer):

- The handoff file is `lanes-edit.sh handoff-path <lane> --create`: the
  row's cell translated into the checkout this lane's writes go through
  (its own `<W>-lanes/<lane>/` under the opt-in, made here where it is not
  there yet; an explicit LANES_REPO; W where the opt-in is off). 8 is "no
  handoff named" as before; any other exit is a note in the helper's words.
- The publish and the commit block are one `LANES_LANE=<lane> write-handoff
  <lane> --prepend <block> --subject "handoff(<lane>@<ws>): PAUSED, <why>"
  [--restart <op> <gen> 0 preparing none]`, with no LANES_NO_FETCH. The
  add/commit/`pull --rebase`/push in whatever repository held the file,
  outside any lock and with git's words sent to /dev/null, is deleted; the
  helper's own words are printed, with a note for each of its exits (2: S3
  refused and the file is written, not committed; 3; 6: kept on HEAD, the
  publish act named). `handoff_written=1` exactly where the helper's stdout
  line names the file it wrote, and the pending intent binds that path.
- Under `--restart` the preparing bind names that file at its digest now
  (`none` where it is absent), before the write.
- A why's control characters are spelled as spaces in the subject
  (`subject_text`), which `write-handoff` requires.
- `append-line` gains LANES_LANE; a `--restart` respawn line carries
  LANES_ROUTE_WRITES, so the supervisor's lane-start resolves the checkout
  the intent bound.

The t018 single-repository goldens move exactly where the old commit act
printed (`t018-handoff/single-repository-decoys.golden` :34-36 and :108-110,
`t018/single-repository.golden` :142-144): `handoff commit: ...` and
`handoff pushed to origin/main` become write-handoff's own `committed:` and
`pushed origin/main (attempt 1)` lines, its subject carries the why, and
lane-handoff says which exit it read. Nothing else in either golden moves;
both are re-captured by each module's own `--capture`.

Tests: new tests/test_a23_lane_handoff.py (three cases, red at 84633df);
hygiene's no-pull rule covers lane-handoff, and the carry rule now reads
lane-start and lane-handoff.

Refs #198

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit a1f658ef2f2a3d531636d83227dcecc98993a6a8)
…, and a stamp that could not be published, or that the checkout refused, launches with the bound file at its pre-image digest (#198, slice 2b)

Amendment 23, act 2's slice 2b, its tests in
tests/test_supervised_restart_regressions.py:

- `routed_restart_box` and `prepare_restart_routed`: `restart_box` and
  `prepare_restart` with the default resolution (no LANES_REPO, no
  LANES_FILE, `workspace.yaml` naming W, LANES_ROUTE_WRITES=1). The first
  start makes the lane's checkout and adds the row there; the handoff is
  committed and pushed from it; the intent binds the file `handoff-path`
  names. `bind_pending` is the one pending-intent call both share.
- `test_ctx_round_trip_through_lane_checkout`: `lane-handoff --restart`
  writes and pushes the PAUSED block from the checkout and binds that file;
  its respawn line carries LANES_ROUTE_WRITES=1; a `lane-start` without the
  opt-in refuses (2) with nothing changed; with it, it launches with the
  PAUSED block as its first prompt, stamps the checkout's file once and
  pushes it, and the intent is `starting` at the stamped digest. W moves only
  by fast-forward.
- `test_refused_stamp_with_restart_intent_still_launches` (#198 A2): an
  entry staged in the checkout makes S3 refuse the stamp; the launch exits 0
  with nothing written or published, and the intent goes `pending` ->
  `starting` at the pre-image digest.
- `test_restart_atomic_stamp_failure_preserves_original_for_retry` is
  rewritten for 5c's `write-handoff`: a stamp that could not be published
  atomically no longer refuses the launch (exit 0, the bound file unchanged,
  the intent at its pre-image digest), and the retry stamps it once.

All three are red at the base: with `lane-handoff` from 84633df the
`--restart` refuses "HANDOFF WAS NOT REFRESHED" (it resolved W's copy); with
`lane-start` from e2037b9 the atomic case dies 2, the refused stamp dies 2
"the restart handoff the row names is missing", and the round trip never
reaches its launch.

Refs #198

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 37b880617ac5d1791b08841e5095a0e2af368993)
… its handoff is not there, naming that file and the command that makes the checkout, and never reads W's copy in its place (#198, slice 2b)

Lane openRepoTools-3's review of #316, finding 727 and its question 2.
5d used to fall back to `hs_resolve`, which joined the row's cell to
`LANES_REPO` and then to W's `workspace-root`, wherever the file that
`handoff-path` names was missing. That read went through W, against
Amendment 23 S2 ("never through W"). The copy it found never carried this
resume's stamp, and its cell was not fenced the way the writes' cell is,
which also kept 723 open.

Now the first prompt is read only from the file `handoff-path` names, for
every launch. Where that file is not there, the launch goes on with no
first prompt, and one note names the missing file and, under the opt-in,
`LANES_ROUTE_WRITES=1 lanes-edit.sh lane-checkout <lane> --create`.
`hs_resolve` and `hs_workspace_root` had no other caller and are deleted.

New case: test_no_lane_copy_launches_with_no_first_prompt_never_W_copy
(red at 37b8806: the first prompt was W's copy).

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit c8ac6ca71a97024ea7b4ba094b4a348c8fed2d4f)
…ff is written and nothing is committed, where their exit-0 words claimed a commit and a push (#198, slice 2b)

Lane openRepoTools-3's review of #316, findings 721 and 728 and its
question 4, and rv2b's SHOULD 1. Under LANES_NO_GIT=1, `write-handoff`
writes the file, commits nothing and exits 0. lane-start then printed
"handoff stamped, committed and pushed", which was false. lane-handoff
printed "its words above say what it committed and pushed".

Now both scripts say the file is written and NOT committed under
LANES_NO_GIT=1. Every other exit 0 keeps its words byte for byte, so the
t018 goldens do not move. The 8(d) and sibling cases wait for slice 2a's
604 and 601 (part 2).

New cases, both red at c8ac6ca:
test_no_git_stamp_says_written_and_not_committed and
test_no_git_handoff_says_written_and_nothing_committed.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 127ac759a7ea1d62998dbed177a3c93d9f96ae4e)
…tamp, that the first prompt is the checkout's copy and origin's differs, naming both copies and the command that brings the checkout up to date (#198, slice 2b)

Lane openRepoTools-3's review of #316, finding 725. Routed, a staged
stray in the lane's checkout makes S3 refuse the stamp, and the checkout
is not fast-forwarded. Where origin holds a newer PAUSED block from
another workstation, 5d delivered the checkout's older block and did not
say so. At e2037b9 the newer block was delivered.

Now, where 5c wrote nothing on a plain launch, 5d compares the
checkout's file with the checkout's own origin/<branch>, which the
refused write has just fetched (blob ids). Where they differ, one note
names both copies, `git -C <checkout> merge --ff-only origin/<branch>`
once the refusal is answered, and how to read origin's copy. The launch
still goes on with the checkout's copy, the file this lane's writes go
to. Under a restart the bound file stays the prompt, by 5d's fences.

New case, red at 127ac75:
test_refused_stamp_names_a_checkout_copy_behind_origin.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 77e94b921fac0d100eddea6aeac4fe5bc757f5c2)
… printed re-run only once the restart is confirmed ready, since that write carries none of the restart's fences and exits 7 while the intent is starting (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, NIT 726. Where the stamp could not
be written, lane-start prints the exact `write-handoff --stamp` re-run.
That line carries none of a restart's fences, so run while the intent is
`starting` the helper refuses it with 7.

Now, under an active restart, a second line says to run it only once the
restart is confirmed ready. A plain launch's words are unchanged.

`test_refused_stamp_with_restart_intent_still_launches` now asserts the
re-run line (`with: LANES_LANE=repoZ-1 `, which pins the battery's
survivor LS49) and the new line; red at the parent for the new line.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_ROOT, LANES_REPO and LANES_FILE beside LANES_ROUTE_WRITES, so a /ctx with a register of its own resolves it again in the new pane (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, NIT 729. The respawn line carried
LANES_ROUTE_WRITES but not the register's own seams, so the supervisor
and the lane-start it runs resolved the default W, and a /ctx with a W
or a register of its own failed closed. That predates slice 2b.

Now each of the three is carried where it is set, quoted as the others
are; lane-handoff never sets one of its own. Its environment doc names
them.

New case, red at the parent:
test_ctx_respawn_carries_the_register_seams.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te only W's working tree shows a stray in W, never a hand edit and commit in W, and sends a published one to the lane's own checkout's register and the publish act from there (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, NIT 730.
Under LANES_ROUTE_WRITES=1 with the default resolution, the rows are read
from W, and the refusal said to merge the rows "by hand in" W's register
and commit with `lanes-edit.sh commit`, which under the opt-in commits in
the lane's own checkout, not in W. W is the readers' copy, where no tool
writes (Amendment 23 S5).

Now, under the opt-in:
- where origin/<branch>, as W last fetched it, has fewer than two such
  rows, the duplicate is only in W's working tree, and the refusal names
  it a stray in W, which a person takes out;
- where origin carries the duplicate, the merge is a hand edit of the
  lane's own checkout's register (`lane-checkout <lane>` names it, or
  `--create` makes it), published from there with
  `LANES_LANE=<lane> LANES_ROUTE_WRITES=1 lanes-edit.sh commit`.
Without the opt-in the words are unchanged.

The second arm is not reached with this helper: `canon-lane` reads the
published register after W is fast-forwarded and refuses a published
duplicate first (Amendment 15(d)), and where it cannot answer, `lane-dir`
and the managed-ledger fence refuse it too. So under the opt-in this
refusal meets the stray in W, and the second arm is the right words for
a path that reaches it anyway; the comment says so.

New case, red at the parent:
test_duplicate_row_only_in_W_is_named_a_stray_there. And a pin, green at
the parent too: test_published_duplicate_row_is_refused_before_W_is_named
(canon-lane's refusal, nothing written, never the words that send a merge
to W).

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or byte what c45a452 printed": lane-handoff's commit act, re-captured from slice 2b's tools (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, question 3. Slice 2b replaced
lane-handoff's own commit and push with one `write-handoff --prepend`,
and moved the two single-repository goldens at exactly those lines
(three hunks in all). "Byte for byte what c45a452 printed" was then
false for them.

Both module docstrings and both `capture()` docstrings now name the
exception: which lines, why, and that every other line is c45a452's.
The case names are kept. No behaviour changes, so there is no red case
and no mutant.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…in the skill's phrases, and both scripts' exit-6 words point at the helper's account instead of saying a commit is kept on HEAD of the lane's checkout (opensoft/openRepoTools#198, slice 2b)

opensoft/openRepoTools#303's fit (c) in lane openRepoTools-3's review of opensoft/openRepoTools#316, and from its
review of opensoft/openRepoTools#311 at 1913db9: question 6, SHOULD 982 (which names
lane-handoff:2689) and INFO 990.

The register's file-level PAUSED line ended "; handoff refreshed" whatever
happened. Now it keys on what `write-handoff` did, with the skill's
phrases, so the two writers of the one act (Amendment 17(a)) say the
same thing:
- "handoff refreshed": it wrote the block and exited 0, git allowed;
- "handoff written, NOT published (write-handoff exited n)": 2, 6, and
  any other code that wrote; "(LANES_NO_GIT=1)" for 990's harness case;
- "handoff written, its publication NOT confirmed (write-handoff
  exited 3)": 3 only;
- "handoff NOT refreshed (...)": no handoff found, handoff-path failed,
  write-handoff not called, or it exited n and wrote nothing.
No reader parses the suffix.

Exit 6 is not one state: `git add` or `commit` failed and nothing is
committed, or the commit is kept on HEAD and named; and with slice 2a's
604, an 8(d) handoff's commit or push in its own repository failed. Both
scripts' 6 words now say the file is written and NOT published, point
at the helper's words for whether and where a commit was made, and name
the publish act only for a commit kept in the lane's register checkout.
The shell suite's RV-B2 (a) assertion moves with lane-start's words.
test_handoff_skill_80_85's reading of lane-handoff's register line sets
the record of a write that exited 0.

New cases, red at the parent:
test_register_paused_line_says_what_the_write_did[2, 3, 6, 1] and
test_stamp_exit_6_says_what_the_helper_said_not_kept_on_HEAD (which also
pins the battery's survivor LS45); test_no_git_handoff_says_written_and_
nothing_committed asserts the LANES_NO_GIT=1 record.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f a peer moved with its row's cell during the stamp is still delivered; the slice's tests split their composite asserts, share one copy of each W helper, and scrub a caller's WORKBENCHES_ window (opensoft/openRepoTools#198, slice 2b)

rv2b's NITs on opensoft/openRepoTools#316.

NIT 1. Where a peer moves the lane's handoff, and the row's cell with it,
while this launch stamps it, the write's replay follows the cell and
stamps the moved file. 5d then read the path `handoff-path` named before
the write, which is gone from the checkout, and launched with no first
prompt. Now 5d reads the file this call wrote (`HS_PATH` from the
helper's written-file line, where that file is there). New case, red at
the parent: test_first_prompt_follows_a_handoff_a_peer_moved_during_the_
stamp, whose wrapper's peer `git mv`s the handoff and edits the row's
cell during `write-handoff`.

NIT 2. Fifteen composite asserts (S9073) are split into one condition
each: six in test_a23_lane_start, four in test_a23_lane_handoff and five
in the supervised-restart cases.

NIT 3. One copy of each helper: test_a23_lane_start's `_reflog` and
`_assert_W_only_fast_forwarded` give way to slice 1a's `W_reflog` and
`assert_W_moved_only_by_fast_forward` in test_a23_lane_start_read, which
check the same three things; a new `no_auto_gc` there replaces `_quiet`
and both gc loops; `FF` is dropped. `_published` and `published` stay
two: the row-stamp sandbox has no `origin` and returns text, the
claude-current one returns bytes.

NIT 5. `WORKBENCHES_` joins both SCRUBBED_PREFIXES, so a caller's own
lane window never reaches a sandbox.

NIT 4 needs no change here: slice 3 already spells the PAUSED subject as
lane-handoff does (`${ws:-unknown}`, control characters made spaces).

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rd for word: a 0 is "handoff refreshed", LANES_NO_GIT=1 included, a 2 or 6 that wrote is "NOT published", and a 3 or any other exit after a write is "its publication NOT confirmed" (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's answer 6 on opensoft/openRepoTools#311 asked for
one set of record phrases for the act's two writers, "refined on both
sides". Slice 3 took it on the skill's side (its F23 and F24, at 2a420e0
on the code leg), and this commit makes lane-handoff's 4(b) line read the
same:
- a 0 records "handoff refreshed", which claims no commit. Under
  LANES_NO_GIT=1 that is now the record too, where item 8 wrote "handoff
  written, NOT published (LANES_NO_GIT=1)": the step line already says
  that nothing was committed (728), and the skill keeps the 0 record for
  every 0 (F24, which answers INFO 990 for both writers);
- a 2 or a 6 that wrote: "handoff written, NOT published (write-handoff
  exited <n>)";
- a 3, or any other exit after a write: "handoff written, its publication
  NOT confirmed (write-handoff exited <n>)", since such an exit claims
  nothing either way (item 8 said "NOT published" for the others);
- "handoff NOT refreshed (<why>)" where nothing was written, unchanged.

The comment over the record names the set. Item 8's explicit 3 arm
assignment goes, since 3 now takes the default.

Cases: test_register_paused_line_says_what_the_write_did gains a row for
an exit 5 after a write, and test_no_git_handoff_says_written_and_
nothing_committed asserts "; handoff refreshed"; both red at the parent.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… refreshes' scope, a refresh that could not fast-forward W, the stamp's LANES_LANE, both stale-row recoveries, the checkout made at the row, and lane-handoff's exit words, its printed block and a handoff-path that failed (opensoft/openRepoTools#198, slice 2b)

m2b's battery of 59 one-line mutants of 2b's own code left 26 alive on
10-23. Earlier items pinned I1a, I2a and I2b (items 1 and 2), LS49
(item 4) and LS45 (item 8); LS46, LS47 and LH04 are part 2's 733 cases. This commit adds a
case, or an assertion in one, for each of these, and each is shown
killing its mutant:
- LS03 and LS02: a recording LANES_EDIT shows no refresh-workspace
  without the opt-in, and none for a caller's LANES_REPO under it;
- LS06: a normal routed start says nothing of a refresh that failed and
  reads the row from W's working tree;
- LS07, LS10 and LS12: where W holds a commit of its own, the refresh
  cannot fast-forward it; the launch says so, reads the row a peer
  pushed from origin/main as W last fetched it, and offers no second row;
- LS29: a first start says nothing of a checkout that could not be made;
- LS41: the stamp's write-handoff carries LANES_LANE;
- LS17: without the opt-in, a start behind a peer's row names
  refresh-workspace, never a fetch and merge typed in W;
- LS19: under the opt-in, a row only W's working tree carries is named a
  stray in W;
- LH13: lane-handoff's exit-0 step line; LH14: exit 2's own note ("Never
  re-run this handoff"); LH16: the block printed where nothing was
  written; LH03: a handoff-path that failed is said, the block printed,
  and the register records "handoff NOT refreshed (handoff-path exited
  1)".
LS12 and LH13 are rewritten against today's code (LS12's recorded text
did not match, LH13's arm changed in item 2). Not pinned, and named in
the PR body: LS08, LS21, LH07 and LH11 (equivalent today, or needing
restart plumbing), LS52 (its fallback is gone since item 1) and LS20
(the shell suite's :3189, CI only).

No behaviour changes.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2b, so 2b's cases run on the verbs it calls: 601 in the shared translation, 602, 603's 7, 604's 8(d) commit, 605, 732 and 991 (opensoft/openRepoTools#198)

Main named 2a's head at 14:07Z. This slice stood on f00def1 (A46); 2a's
round since then, A47 to A60 with slice 1b merged at 59551dd, changes
lanes-edit.sh and its tests only:
- A54, 601 (and the precondition of 723): write-handoff, handoff-path and
  set-restart-intent --handoff refuse a handoff in W or in another
  checkout of the register, in the translation the three share;
- A55, 602: a cell or --target whose . or .. components climb out of the
  checkout is refused;
- A56, 603: write-handoff answers the shared act's two restart-fence 2s
  (the bound file, the pre-image) with 7;
- A57, 604: an Amendment 8(d) handoff is committed and pushed in its own
  repository, as lane-start and lane-handoff did at e2037b9;
- A58, 732: refresh_workspace_strays' git status in W takes no optional
  lock;
- A59 and A60, 605 and 991: a fenced --prepend takes --expected-digest
  none only, and no glob character in a handoff's path.

The merge is clean: no hunk was resolved by hand. git merge-tree of
1320847 into this branch's e5c3326 gave tree 660ad87 before the merge
was made, and the merge commit's tree is that tree.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he register beside W, is neither stamped nor delivered by lane-start and takes no PAUSED block from lane-handoff, now that slice 2a refuses it in the translation all three verbs share (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, MUST 723.
With the lane's cell naming repoZ-2's handoff in repoZ-2's own clone of
the register beside W, lane-start stamped repoZ-1's RESUMED line into
that clone, uncommitted, and launched with repoZ-2's top block as its
first prompt; lane-handoff wrote the PAUSED block there the same way.

Slice 2a's 601 (A54), merged at e12b369, puts the refusal in the
translation write-handoff, handoff-path and set-restart-intent share,
and item 1 (727) made 5d read only the file handoff-path names. So no
2b code changes; one case per call site pins it:
- lane-start: nothing is written in the sibling's file or checkout, none
  of its block is delivered, and the launch goes on with no first
  prompt, write-handoff's words naming another checkout of the register;
- lane-handoff: nothing is written there, the block is printed, and the
  register's PAUSED line records "handoff NOT refreshed (handoff-path
  exited 2)".

Both cases were red at e5c3326, before the merge (the sibling's file
written), and are green on it.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… reads a refused stamp's 7 as lost ownership, since slice 2a now answers both restart fences with 7 (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, SHOULD 724.
Since slice 2a's 603 (A56), merged at e12b369, write-handoff answers the
shared act's two restart-fence 2s (the bound file, the pre-image) with
7. So a bound handoff a peer changed after binding came back from 5c as
7, and lane-start, which asked the 7 first, said "this restart attempt
lost ownership of its handoff".

5c now asks hs_bound_unchanged first and dies "the restart handoff
changed before its owned resume stamp"; a 7 with the bound file
unchanged is still lost ownership. Both exit 2, and S3's 2 stays a note
(test_refused_stamp_with_restart_intent_still_launches).

New case, red on the merge before this commit:
test_a_bound_handoff_changed_before_the_stamp_is_named_so, whose wrapper
changes the bound file just before the real write-handoff. It also pins
the battery's LS47 (733's M2).

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tted and pushed in its own repository at both call sites, and both scripts' exit-0 words point at the helper's account, which may say nothing was committed (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, SHOULD 722
and 721's last case, with 982's 8(d) case from its review of
opensoft/openRepoTools#311. Lane 3 read Amendment 23 S2 as governing,
and slice 2a's 604 (A57), merged at e12b369, commits and pushes an 8(d)
handoff in its own repository, as e2037b9 did.

Pinned at both call sites: the stamp and the PAUSED block are committed
there by pathspec and pushed to that repository's origin, its tree
clean, the register taking no commit for them; an 8(d) stamp whose push
fails there is a 6 whose note points at the helper's words, never at
HEAD of the lane's checkout.

A 0 is still not always a commit: an 8(d) handoff in no work tree, or a
clone of the register not beside W, is committed nowhere, and the
helper's note says so. So both scripts' exit-0 words now end as slice
3's 0 arm does (its F24), "or that nothing was committed":
- lane-start: "handoff stamped through `write-handoff`, which exited 0:
  <file>: <stamp> (its words above say what it committed and pushed, or
  that nothing was committed)", where it said "handoff stamped,
  committed and pushed";
- lane-handoff: "... its words above say what it committed and pushed,
  or that nothing was committed".
The three golden lines of lane-handoff's exit-0 step (decision 22's
hunks) and the shell suite's :3822 (CI only) move with them.

New cases, red at e5c3326 before the merge (the 8(d) file left
uncommitted), and the two exit-0 cases red on the merge before this
commit's words: test_an_8d_cell_is_stamped_committed_and_pushed_in_its_
own_repository, test_an_8d_stamp_whose_push_fails_there_says_what_the_
helper_said, test_an_8d_cell_takes_the_block_committed_and_pushed_in_
its_own_repository.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d trip while W is behind and cannot be fast-forwarded, and a why with a tab in lane-handoff (opensoft/openRepoTools#198, slice 2b)

Lane openRepoTools-3's review of opensoft/openRepoTools#316, SHOULD 733,
its remaining cases, and slice 2a's Q2 caveat:
- M1 (the battery's LS46): under a restart, a stamp write-handoff
  refuses with 7 and no written line, the bound file unchanged, dies 2
  "lost ownership of its handoff before the resume stamp", nothing
  launched (with slice 2a's 603 this is the fence's code, and S3's 2
  stays a note);
- 733's first case and 2a's Q2 caveat: with W holding a commit of its
  own and a peer's push since, no refresh can fast-forward W; the /ctx
  round trip still writes and binds the lane's checkout's file, path and
  digest through handoff-path, the launch says W was not fast-forwarded,
  delivers and stamps that file, and W's HEAD does not move;
- M4 (the battery's LH04): a why with a tab refreshes the handoff, the
  subject carrying a space (decision 19).
M2 is the case of the previous commit (724). M3 has no case of its own:
handoff_written on a written but uncommitted /ctx is set from the
written-file line, which S3's 2 prints, and the battery's LH12 (the same
flag held at 0) is killed by the round trip.

No behaviour changes; each case passed before this commit's tests were
added, so each is shown by its mutant.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ich brings slice 1a's merge of openRepoTools-code main 577110e) into slice 2b, so the slice stands on the code leg's main and its front door (opensoft/openRepoTools#198)

Main named 2a' at 16:2xZ. This slice merged 2a's 1320847 at e12b369, and
2a' contains it; it adds slice 1b's round as rebuilt on the code leg and,
through slice 1a's 903d751, the code leg's main 577110e: the cutover's own
commits (among them 6314fc8, the code leg's front door with
.gitattributes, the LF rule) and code PRs 3 to 7, T017's five
lanes-edit.sh fixes. 31 files differ between 1320847 and 2b1a232; of the
files this slice changes, only tests/test_lane_helpers.sh and
tests/test_repo_hygiene.py, in hunks this slice does not touch.

1b' and main are not merged here on their own: 2a' carries them, as main
asked.

The merge is clean: no hunk was resolved by hand. git merge-tree of
2b1a232 into this branch's c12f078 gave tree 4b6b9ea before the
merge was made, and the merge commit's tree is that tree.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Nov 8, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Implementation

Route lane-start’s Rule 3 stamp and lane-handoff’s PAUSED block through the lane’s writable checkout with write-handoff, while keeping W fast-forward-only and preserving restart-specific refusal fences.

  • lane-handoff L2525-2547, L2619-2665, L2672-2732 (+4 small edits)
  • lane-start L1209-1240, L4416-4484, L4517-4549 (+22 small edits)

Tests

Add coverage for routed stamp and handoff writes, failure and recovery messaging, restart behavior, missing or moved handoffs, alternate repositories, and W’s fast-forward-only movement.

  • tests/test_a23_lane_handoff.py L1-338
  • tests/test_a23_lane_start.py L1-783
  • tests/test_supervised_restart_regressions.py L181-233, L484-679, L1010-1068 (+5 small edits)

Update launch-read regressions and environment scrubbing to verify W moves only by fast-forward and sandbox runs do not inherit caller window variables.

  • tests/test_a23_lane_start_read.py L19-24, L55-76 (+8 small edits)
  • tests/test_lane_start_claude_current.py L52
  • tests/test_lane_start_row_stamp.py L60

Align shell regressions for stale-row recovery and refused or successful stamp publication with the new write-handoff behavior.

  • tests/test_lane_helpers.sh L3727-3740, L3776-3792, L3811-3828 (+3 small edits)

Extend hygiene checks to prohibit pulls and carry promises in both lane scripts.

  • tests/test_repo_hygiene.py L3292-3296, L3401-3410

Refresh the tooling goldens and document their lane-handoff output exception, while retaining the surrounding baseline output.

  • tests/fixtures/t018-handoff/single-repository-decoys.golden L34-37, L109-112
  • tests/fixtures/t018/single-repository.golden L142-145
  • tests/test_triad_lane_tooling.py L28-38, L1234-1237
  • tests/test_triad_lane_tooling_handoff.py L32-45, L1294-1297

Update the skill regression to expect the PAUSED record phrase produced by a successful handoff write.

  • tests/test_handoff_skill_80_85.py L227

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

brettheap pushed a commit that referenced this pull request Nov 8, 2026
… brings lane-start and lane-handoff on write-handoff and the three-name _NO_PULL_FILES that slice 3 left to it (opensoft/openRepoTools#198)

Slice 2b's head on the code leg, feat/198-a23-slice-2b-start-handoff at
a44b9d2 (2b', which carries 2a', 1b', 1a' and the code leg's main 577110e),
named by main for the one sitting that lane openRepoTools-3 ruled
(opensoft/openRepoTools#198): slice 3 lands last, right after 2b, and its
merge tree must equal this head's tree. The tree is
`git merge-tree --write-tree 95c52cc a44b9d2` = bebe083.
The merge base is 2b1a232, the 2a' head that slice 3 merged at 95c52cc; from
it 2b' changes 15 files (2123 insertions, 537 deletions). Of slice 3's own
files it touches one, tests/test_handoff_skill_80_85.py, by one line in
_lane_handoff_writes (it binds handoff_record in the stand-in script), in a
hunk slice 3 does not touch, and it leaves skills/handoff/SKILL.md and
tests/test_handoff_skill_commit_block.py as they are. It also changes
tests/test_repo_hygiene.py's _NO_PULL_FILES to ("lanes-edit.sh",
"lane-start", "lane-handoff"), which slice 3 left to it (lane 3's answer 3).

The merge is clean: no hunk was resolved by hand.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…wner case hold on CI's git 2.56.0) into slice 2b (opensoft/openRepoTools#198)

CI on #18 failed 2 of 3654 at 2b1a232, the
two `other-owner` cases of
`test_a_clone_git_cannot_read_is_never_taken_for_no_repository` in
tests/test_a23_write_handoff.py. 56a9b87 is 2a's fix for them: the case
sets `GIT_CONFIG_NOSYSTEM=1` beside git's test hook, and skips, saying
why, where git still reads the clone. It changes that one test file by 32
lines added and 2 removed, and nothing else.

This slice carried the same test file at a44b9d2, with 2b1a232's
version of it, so it would have failed the same two cases on CI's git.

The merge is clean: no hunk was resolved by hand. git merge-tree of
56a9b87 into this branch's a44b9d2 gave tree c17ffb5 before the
merge was made, and the merge commit's tree is that tree.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Nov 8, 2026

Copy link
Copy Markdown

brettheap pushed a commit that referenced this pull request Nov 8, 2026
… brings A61, which makes 2a's other-owner case hold on CI's git 2.56.0, and it changes no file of this slice (opensoft/openRepoTools#198)

Slice 2b's new head on the code leg, feat/198-a23-slice-2b-start-handoff at
4031100 (2b''), named by main. It is a --no-ff merge of 2a'' (56a9b87) into
a44b9d2 (2b'), the head that slice 3 merged at 6b81f19. 56a9b87 is A61,
a test-only fix to 2a: CI's git 2.56.0 failed
test_a_clone_git_cannot_read_is_never_taken_for_no_repository and its two
other-owner cases, which slice 3 carried through 2a', and the case now sets
GIT_CONFIG_NOSYSTEM=1 and skips, saying why, where git still reads the
clone. CI at 95c52cc failed only those two (3734 passed).

The tree is `git merge-tree --write-tree 7690a1f 4031100` =
fdf667e. The merge base is a44b9d2; from
it 2b'' changes one file, tests/test_a23_write_handoff.py (32 insertions,
2 deletions), and relative to this branch's 7690a1f the merge changes that
file and no other. None of this slice's files is touched.

The merge is clean: no hunk was resolved by hand.

Lane: openreposhape-2 (openRepoShape-2)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@brettheap brettheap changed the title Amendment 23 act 2, slice 2b: lane-start and lane-handoff write the Rule 3 stamp and the PAUSED block with one write-handoff through the lane's checkout, W moves only by fast-forward, and a launch refuses only at a restart's fences (opensoft/openRepoTools#198) Amendment 23 act 2, slice 2b: lane-start and lane-handoff write the Rule 3 stamp and the PAUSED block with one write-handoff through the lane's checkout, W moves only by fast-forward, and a launch refuses only at a restart's fences Nov 8, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant