Repository navigation
Amendment 23 act 2, slice 2a: write-handoff writes a lane's handoff in the checkout its writes go through, handoff-path says where, publish-handoff keeps its contract on one shared act, and set-restart-intent takes the row's spellings (#198) - #312
brettheap wants to merge 36 commits into
Conversation
…ntation of the restart fences, the prior digest and the one-rename replace that `write-handoff` will share, and `publish-handoff` keeps its contract for every old caller (#198, slice 2a stage 1) `handoff_publish_atomic <lane> <target> <prepared> <prior digest> [<op> <gen> <attempt> <state> <transcript>]` is `publish-handoff`'s body, moved whole: the target's links followed to the physical file, its directory resolved with `cd -P`, the restart intent read and its five fences checked (7 lost ownership, 2 not the bound handoff, 7 an active restart, 64 an operation without its other four fences), the prior digest (7), then the bytes. It runs under the lock its caller holds and every refusal is a `die` whose exit trap releases it. The bytes go #212's way, which was this act's own (`rename-lane`'s stamp says it copied it from here): an exclusive temporary beside the target in its physical directory, `cp -p` for the mode, the bytes, one `mv -f`. #212's one refinement is taken: the temporary's name (`pbh_tmp`, which `cleanup` removes on every handled exit) is cleared once the rename has landed, so the exit trap never removes a name this process no longer owns. `rename-lane` keeps its inlined copy: it already holds the checkout lock, and its refusals are exit 5 under #158's tests. `publish-handoff` keeps its arguments, flags, words, codes and its one line on stdout, the physical file; it takes the workstation's sidecar lock alone, as before, and compares the bound path as it is. The one difference is invisible to a caller: a refusal's lock is released by the exit trap rather than by a `release_lock` written before each `die`. New: `tests/test_a23_write_handoff.py`, on slice 1b's box (imported from `tests/test_a23_publish_replay.py`, not copied), with `publish-handoff`'s contract: the physical file on stdout through a link, its mode kept, nothing committed, no temporary left beside it; a changed handoff (7); and the restart fences, owned, lost, active, half-given and unbound. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… through, as a Rule 3 stamp, a new top block or a whole prepared file, under S3, the translation, the restart fences and S4, and `handoff-path` says where that is (#198, slice 2a stage 2) `write-handoff <lane> (--stamp "<line>" | --prepend <block file> | --whole <prepared> --expect-digest <sha>) --subject "<s>" [--target <path>] [--restart <op> <gen> <attempt> <state> <transcript> [--expected-digest <sha>]]` is routed (`write_lane_of` names its lane): the lane's own checkout under the opt-in, the explicit `LANES_REPO` seam in place, W with the opt-in off. Under the checkout's lock, in this order: 1. S3, answered by the verb. `--stamp` writes nothing on a refusal, prints the line it would have written and exits with S3's code; `--prepend` and `--whole` write the file, commit nothing, print its path and exit with S3's code (R-A11-11: a swap is never left unwritten). S5, a fence, a digest or a failed proof writes nothing. 2. The target: the row's cell from the register S3 just fetched, or `--target`, TRANSLATED, never followed (S2, `handoff_in_repo`). A relative cell is joined to the checkout; a `~/…` or absolute path whose physical parent is under W becomes the same path in the checkout; one already in the checkout stays itself; anything else is another repository's (Amendment 8(d)), written there and committed nowhere (0). A target that is physically W's, from a checkout that is not W, is refused (2, S5). 3. S3 step 4 for the handoff itself: an uncommitted change to it is refused before a byte. 4. The read, the splice and its proof, in the checkout: `lane-start`'s stamp under the header block (proven by taking it out again); `lane-handoff`'s shape for the block, with `---` and a blank line before the old file whole (proven by bytes); the prepared file whole, whose prior digest is `--expect-digest` (7 on a mismatch). 5. The workstation's sidecar lock, taken INSIDE the checkout's and only for the fences and the replace: `handoff_publish_atomic`, with three knobs off for `publish-handoff`. An intent bound to W's spelling of the file still fences the lane's copy (`HPA_CHECKOUT`); the restart's bound digest is the pre-image (`--expected-digest`, 2, `lane-start`'s words); and a `--stamp` under `--restart` records the stamped file's digest as the intent's prepared digest BEFORE the bytes, in-process (`handoff_restart_reserve`, `lane-start`'s reservation). The sidecar lock is released before the push. 6. S4: a commit by pathspec with a `Lane: <lane>` trailer (`CP_TRAILER`), the push, the read-back, and a refused push replayed on origin's new tip. A whole-file write replays only where origin's copy is still the one it started from, checked before the reset (`CP_WHOLE_REL`/`CP_WHOLE_BLOB`); otherwise its commit is kept on HEAD and named (6, S4.4). Its one line on stdout, `<path><TAB><sha256>`, is printed by `cleanup`, so a replay prints the last attempt's line only (`cleanup_for_exec` clears it). On w1b's conditions for its `commit_publish`: the S4.4 check sits in the refused-push branch after the read-back and before the origin-unmoved 6 and the replay's reset; the trailer goes in as a second `-m` built in one array, `cp_m`, at both commit sites, and a trailer with a control character or not in `Key: value` form is refused (2) before anything is staged. Exit row 6 names the new cause in the code and, in one clause, in `docs/README-lanes.md`. `handoff-path <lane> [--create]` is a read and is not routed: the row's cell, translated into the checkout `write-handoff` writes through, printed whether or not that checkout exists yet; `--create` makes the lane's checkout first. A lane with no row, or a row naming no handoff, is 8, and nothing is cloned for it. New helpers: `path_physical` (`ws_relative` now rests on it, unchanged in what it answers), `handoff_in_repo` (`handoff_in_checkout` calls it), `handoff_checkout_of`, `handoff_bound_is`, `handoff_restart_reserve`. The sidecar lock's comment, the env doc and `cleanup` say which act nests it and in which order. The unknown-subcommand list, the usage header and the exit table's rows 2, 6, 7 and 8 name both verbs. Tests, in `tests/test_a23_write_handoff.py`: * the stamp: spliced, committed with the trailer, pushed; refused by S3 (a staged entry, a fetch that did not run) with nothing written and the line printed; replayed onto a peer's new content (another file, the handoff itself) with one stamp and one line; a splice that does not prove out; * the prepend: written and committed; refused by S3 and still written; * the whole file: a digest mismatch (7); replayed over an unchanged copy; kept on HEAD (6) where origin's copy changed; * the translation: an estate-spelled cell (seam, routed), a repository- relative cell (seam, routed, opt-in off), another repository's file, a link into W refused (S5); * the restart: the sidecar lock waited on inside the checkout's; lost, unbound and active fences; an intent bound to W's path; the prepared digest recorded before a replace that fails; a changed pre-image; only the stamp reserves; the sidecar lock released before the push; * `test_W_untouched`: routed, all three modes, and W's HEAD, tree and reflog do not move; * `handoff-path` in the three modes, `--create`, and a lane with no row; * usage (64). Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…managed-owner marker that lands between its first ask and S3's fetch is read, and the write is refused with nothing written (#198, slice 2a) The pre-review of slice 1b found writes whose checks run before the lock, on what was fetched before S3, and are never asked again on the tip S3 fast-forwards to (MUSTs 1 and 2: a rival's claim, a retired row). Slice 2a's own code had one such check: `write-handoff` asked `managed_seam_refuse` before its lock, on this checkout's origin/main as it last stood. It now asks it again right after S3 succeeds, once `cs_flush` has dropped the cached register, and a refusal there writes nothing. The row's handoff cell, the pre-image, `--expect-digest`'s comparison and the S4.4 blob were already read after S3, and the restart fences under the sidecar lock. New: `test_a_managed_marker_landing_inside_the_s3_window_is_refused`. A peer's row carrying managed-owner vocabulary is pushed by a hook at S3's own fetch; the stamp is refused, the file and origin's copy are as they were, and the lock is released. At 357ec07, without this change, the same case commits and pushes the stamp. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…write-handoff` joins `lane_row_gate` in the next commit (#198) Slice 1b's head brings 7da3d61 (R4-1's `lane_row_gate`), B23 (the success line stays `pushed origin/<branch> (attempt N)`), the merge of slice 1a's fix rounds 2 and 3 (eead86d, 15c8e3f), B31 (supervised_restart's fixture no longer exports `LANES_NO_FETCH=1`) and MUST 3 (`lane-worktrees sweep --yes` writes its NOTED line with a fetch). Where they meet slice 2a: `write-handoff` is a routed writer that needs its lane's row, so it joins the gate's row group (A23, its own commit next); B23's line is the one slice 2a's tests read; the S4.4 check and the trailer sit in `commit_publish` where w1b's conditions put them, and B23 does not touch either. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n a write for a lane with no row is refused before its checkout is cloned, after one fetch of W or at once under `LANES_NO_FETCH=1` (#198, slice 2a) R4-1 (lane openRepoTools-3's re-check of #282) gave the routed writes a gate: a write for a lane the published register does not know clones nothing. `write-handoff` writes the handoff its lane's row names, so a lane with no row has none. Before this, its routing cloned `<W>-lanes/<lane>/` and the arm then refused 8. It now takes the gate's row group beside `set-row-state`, `replace-in-row`, `append-session-id` and `rename-lane`. The gate's last words name what each verb needs the row for ("edits that lane's row", or "writes the handoff that lane's row names"); the four row verbs' words are byte for byte as before. The explicit `LANES_REPO` seam and the opt-in off are not gated, as for the other verbs, and keep `write-handoff`'s 8. New: `test_routed_write_handoff_for_a_lane_with_no_row_clones_nothing`, fetched and under `LANES_NO_FETCH=1`: 2, no checkout cloned, W and origin as they were. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…itory-relative `handoffs/…` cell and the estate's `~/…`, and binds them as the absolute path in the checkout the lane's writes go through, while an absolute path is still kept exactly as given (#198, slice 2a stage 3) Until now `--handoff` took only an absolute path, and refused every other spelling with 64. The row's own spellings of a handoff are a repository-relative cell and the estate's `~/…` link (Amendment 4(e)). They are now translated once the lane is known, by `handoff_in_repo`, into the checkout that lane's writes go through (`handoff_checkout_of`: S1's checkout under the opt-in, an explicit `LANES_REPO`, or W with the opt-in off). The intent stores that absolute path, and the digest taken at `pending` is that file's, so `write-handoff` fences on it directly. A note says what the cell was bound as. A `~/…` that resolves into W is translated into the checkout, never followed into W. A path that belongs to another repository is stored as it is, with the same note. An absolute path is kept exactly as given, W's included (A16). It is every older caller's spelling, and each one binds the file it then publishes and compares: W's, until slice 2b moves `lane-start` and `lane-handoff` (`lane-start` compares it `-ef` with the file `hs_resolve` finds in W, and the older `lane-handoff` publishes W's file through the strict `publish-handoff`). Translating it would stop every `/ctx` under the opt-in. `write-handoff` already translates a W-bound path on its own side (A10). Any other relative path, and a `.` or `..` component in a cell, is still refused with 64, with nothing written. `--dir` keeps its rule, and `~` is never expanded there. New, in `tests/test_a23_write_handoff.py`: - `test_set_restart_intent_binds_a_repository_relative_cell_in_the_checkout`, through the seam, routed and in default mode: the bound path, its digest, and the owned stamp fenced and reserved on it, with W's copy left alone where the checkout is not W; - `test_set_restart_intent_binds_the_estate_spelling_in_the_checkout`; - `test_set_restart_intent_keeps_an_absolute_path_as_given`; - `test_set_restart_intent_refuses_any_other_relative_path` (four spellings). Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… causes, and both sides' new globals are kept (#198) Slice 1b's head brings MUST 4 of rv1b's pre-review (c454247: a refused push whose commit is no longer HEAD is not replayed, and both commits are kept on HEAD and named, exit 6) and MUSTs 1 and 2 (10169f3: `claim` and `add-row` run again from the top on origin's new tip when S3's fetch finds it moved under their own checks, through `CS_RECHECK` and `cs_recheck`). git stopped on three blocks, each an addition made on both sides: - A30: `lanes-edit.sh`'s exit table, row 6, in the header comment. It now lists every cause from both sides: the replay's `reset --keep` over an uncommitted change, 1b's commit made on top of this one, a push refused with origin unmoved, and 2a's whole-file `write-handoff` whose copy origin changed meanwhile (S4.4). - A31: the globals after `CP_REPLAY_KEEPS_EDIT`. Both are kept: 2a's `CP_TRAILER`, `CP_WHOLE_REL` and `CP_WHOLE_BLOB` first, then 1b's `CS_RECHECK=0`. - A32: `docs/README-lanes.md`'s exit row 6, the same union as A30. Checked with no conflict: - In the refused-push branch, 2a's S4.4 check runs before `cp_replay`'s new HEAD check. Both exit 6 and reset nothing, so their order changes only which words are printed. - `CS_RECHECK` is set by `claim` and `add-row` alone, and is 0 at load. So `cs_recheck`'s `exec` is never reached from `write-handoff`'s S3 subshell. `write-handoff` keeps its own second `managed_seam_refuse` after S3 (A28). - 1b's changes to `tests/test_a23_publish_replay.py` only add tests. The `Box` that `tests/test_a23_write_handoff.py` imports is unchanged. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…off git does not track yet as a pending change, so a lost push race no longer drops it with the replayed commit, and a peer's untracked file is never added under this write's subject (#198, slice 2a) The row can name a handoff the checkout has and git has never tracked: a new lane's first, or a peer's. S3 step 4 asked `git diff HEAD`, which does not see an untracked file, so the write committed it; and where the push then lost a race, the replay's `reset --keep origin/main` dropped that commit and the file with it, and the re-run exited 8, "Nothing was written", with the new content only in a dropped commit the reflog names. Now step 3 asks `git ls-files --error-unmatch` (literal pathspec, exact case) beside `cs_own_pending`, and an untracked handoff is refused as S3 step 4 refuses any pending change (2): `--stamp` writes nothing and says its line; `--prepend` and `--whole` write the file and commit nothing (R-A11-11), and their recovery line adds it (`git add -- <rel> &&`) before the pathspec commit. Skipped under LANES_NO_GIT=1, and for another repository's file. A file that is not there is still 8. Test: test_an_untracked_handoff_is_refused_and_never_dropped[stamp, prepend, whole], rv2a's P1 with a peer's commit landing at the push. At b02618c all three fail (rc 8, the file gone); with the fix all pass. Runs on this tree: test_a23_write_handoff 63 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…le under `handoffs/` and nothing else, so a `--target` naming the register, or a row cell that climbs out to an object log, is refused before a byte instead of replaced and pushed (#198, slice 2a) `write-handoff` took `--target` as given and turned any path inside the checkout into the commit's pathspec. `--whole <file> --expect-digest <the register's sha> --target lanes/LANES.md` replaced the register and pushed it (exit 0), and a row cell `handoffs/../lanes/log/<lane>.md` made `--prepend` push a top block into the head of the lane's object log, which Amendment 7(b) makes append-only. Amendment 23 S2 scopes these writers to `handoffs/<estate>/<file>`, and Rule 9's one-line guard was bypassed. Now, once the target is translated and found to be this checkout's, its path relative to the checkout (taken from the PHYSICAL path, so `..` and a link out of `handoffs/` are both resolved first) must be `handoffs/?*`, and must not be `$LANES_PATH`, under `lanes/` or under `$LANES_PREFIX`, or end in `/.` or `/..`. Otherwise: exit 2, "… is <rel> in the checkout <c>, which is not a file under handoffs/ …. Nothing was written.", before S3 step 4, the read, the splice and the sidecar lock, in every mode (`--stamp` says its line). Another repository's file (Amendment 8(d)) is not this check's: it is never committed. The header's exit row 2 and the arm's table say so. Test: test_a_target_outside_handoffs_is_refused_and_nothing_written[target, cell], rv2a's P2. At 6c2a9cc both fail (exit 0, "pushed origin/main (attempt 1)"); with the fix both pass. Runs on this tree: test_a23_write_handoff 65 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…naged seam again on what S3 fetched whatever S3 answered, so a managed-owner marker that lands at S3's fetch refuses a `--prepend` or `--whole` that S3's later refusal would still have written (#198, slice 2a) A28's second `managed_seam_refuse` ran only where S3 passed. S3 can refuse AFTER its fetch (a staged entry that is not this write's, a diverged branch), and `--prepend` and `--whole` then still write the handoff (R-A11-11), so a marker that S3's own fetch brought in was never read: rv2a's P5 wrote a managed lane's handoff with the marker on origin. And M27 (the first ask, before the lock, taken out) survived the module, because the first ask mattered only on that path. Now the two assignments that need a passing S3 stay under `if`, and `cs_flush` and the second ask run after it unconditionally. Where S3 refused before any fetch, the second ask reads what the first read. Tests: - test_a_managed_marker_landing_at_S3s_fetch_refuses_even_where_S3_refused (rv2a's P5, with a marker that parses): at 011f241 it fails, the handoff written under S3's 2; with the fix the seam refuses (2, "owned by the managed ledger"), nothing is written or printed, the staged entry is kept. - test_a_managed_marker_already_fetched_is_refused_before_the_lock, M27's pin: the marker is on the checkout's origin/main before the write, and the refusal comes with no network call at all, so S3's fetch never ran. It passes before and after the fix (it pins the first ask, which this commit does not change). Runs on this tree: test_a23_write_handoff 67 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r checkout no longer stops the write of another repository's handoff, which no tool here commits, so an offline lane's Amendment 8(d) stamp is written and the verb exits 0 with the 8(d) note (#198, slice 2a) `write-handoff` runs S3 before it knows the target, and a refusal then stopped every mode: `--stamp --target <another repository's file>` under `LANES_NO_FETCH=1` exited 2, "the stamp is NOT written", and `--prepend` and `--whole` exited 2 with the file written. That file is never committed (Amendment 8(d)), and today it is stamped through `publish-handoff` with no fetch, so slice 2b moving that caller would lose the stamp whenever the lane is offline. Now, once the target is known to be another repository's, S3's refusal is noted as this checkout's ("… does not stop this write: <file> is another repository's handoff (Amendment 8(d)), which no tool here commits") and the write goes on: the managed seam (already asked), the restart fences and the digests still apply, the file is written, its line printed, and the verb exits 0 with the 8(d) note. The arm's table says so. Test: test_another_repositorys_handoff_is_written_when_S3_refuses_this_checkout [stamp, prepend], rv2a's P3 with `LANES_NO_FETCH=1`: at 7561d5a both exit 2 (the stamp not written; the prepend written and "NOT committed"); with the fix both are 0, written, the line printed, and the register checkout and origin unchanged (`snapshot`). Runs on this tree: test_a23_write_handoff 69 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… pin four behaviours that a one-line mutant used to leave alive, the exit 3 and `LANES_NO_GIT` paths, and carry no composite assert (#198, slice 2a) Tests only; lanes-edit.sh is unchanged. rv2a's mutants M23, M27, M29 and M30 each survived the module, and two exits of the verb had no test. Each new test passes on the code and fails on its mutant (one targeted run each, in a clone at 346e403): - M29 (`--whole` under S3 writes nothing): test_whole_refused_by_S3_still_writes_file_no_commit, sharing its body with the prepend case as `_written_uncommitted_under_S3`. - M30 (`--target` ignored): test_target_names_the_file_written. - M23 (an empty W takes the old rule): test_with_no_workspace_resolved_an_absolute_target_is_the_checkouts_own. - M27 (the first managed-seam ask off): SHOULD 3's own test_a_managed_marker_already_fetched_is_refused_before_the_lock, which fails on S3's fetch showing in `net_ops()`. Two exits had none: test_a_push_that_cannot_be_confirmed_is_3_and_its_line_printed (the push lands, the read-back fetch fails: 3, "pushed, not confirmed", origin holds it, the line printed) and test_with_LANES_NO_GIT_the_handoff_is_written_and_nothing_committed (0, the note, the file written, HEAD and origin unchanged, no net op). The 22 composite asserts the review listed (Sonar S9073) are split into one assert each, every message kept on the assert it belongs to. Runs on this tree: test_a23_write_handoff 74 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ndoff-path` read the lane's row through a checked read, so a register that could not be read is 1 and never "no handoff", and a `--target` write for a lane with no row is 8 instead of a pushed commit carrying `Lane: <that lane>` (#198, slice 2a) Both verbs read the handoff cell with `handoff_of_lane`, which answers a failed register read as an empty cell. `handoff-path` then said 8, "no handoff", over a register git could not show (R22: a read that could not be performed is never 8). And `write-handoff` read the row only when no `--target` was given, so through the explicit seam and in default mode, where `lane_row_gate` does not ask, `--target` for a lane with no row went on, committed the file with `Lane: <a lane with no row>` and pushed it. Now both call the new `handoff_cell_of <lane>`, `handoff_of_lane`'s checked twin: 0 with the cell (empty where the row names none), 8 where the register was read and has no row, 1 where it could not be read. `write-handoff` asks it right after the managed seam's second ask, whatever `--target` says: 8 is "lane X has no row in the register, so it has no handoff to write, and no commit may carry `Lane: X`", 1 is the R22 refusal, and each says the stamp line it would have written; `--target` then replaces the cell. `handoff-path` answers 1 with the same R22 words, and 8 "lane X has no handoff: there is no row for it in the register". An empty cell keeps its 8 in both. The header's exit rows 1 and 8 and both arms' tables say so. In `write-handoff` the managed seam already refuses an unreadable register (1, its UNKNOWN), so the new 1 there is reached only where the register failed between the two reads; NIT 1 is observable through `handoff-path`. Tests: test_a_target_for_a_lane_with_no_row_is_8_and_nothing_committed (NIT 3) and test_handoff_path_over_an_unreadable_register_is_1_never_8 (NIT 1, a `git` shim first on PATH refusing `origin/main:lanes/LANES.md`); test_write_handoff_for_a_lane_with_no_row_is_8 now reads the new words. At 3cd2162 all three failed: the `--target` write exited 0, "pushed origin/main (attempt 1)"; `handoff-path` exited 8; the old test's words were not the new ones. Runs on this tree: test_a23_write_handoff 76 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… commit landed on top of a whole-file `write-handoff` while its push was in flight, the words name HEAD and say both commits are kept, instead of saying this write's commit is HEAD (#198, slice 2a) S4.4 refuses to replay a whole-file write whose copy origin changed meanwhile: exit 6, nothing reset, "the commit is local and kept on HEAD: <sha> <subject>". It said that without reading HEAD. Until slices 2b and 3 lane-start's stamp, lane-handoff's commit and the skill's commit are made in the same checkout with no lock, and one landing on top of this write's commit while its push is in flight left HEAD at that other commit, which the words did not name. Nothing was lost (nothing is reset); only the words were wrong. It runs before `cp_replay`'s MUST 4 HEAD check, so that check's words never reached this case. Now the block reads HEAD once the copy is known to have moved. Where HEAD is this write's commit the words are as before. Where it is not, they take MUST 4's shape: "HEAD is <top>, not this write's commit <sha>: another commit was made on top of it … Nothing is reset, so both are kept", "look at both: git -C … log --oneline <sha>^..HEAD", the publish act's note in the plural, and the die "… this write's commit and the one on top of it are local and kept, with HEAD at <top>: <sha> <subject>", still exit 6. Found and left alone at the merge of 1b's 10169f3 (b02618c), so as not to make an evil merge. Test: test_whole_refusal_names_HEAD_when_a_commit_landed_on_top, a `--whole` whose push hook lands a peer's edit of the handoff and commits an unlocked writer's stamp on top in the checkout. At 96bf464 it failed: the die said "kept on HEAD: <this write's sha>" while HEAD was the other commit. Now: exit 6, HEAD named, both commits unpublished, no replay, the file's line printed. Runs on this tree: test_a23_write_handoff 77 passed; test_repo_hygiene 247 passed; test_a23_guard_refusals 122 passed; test_a23_publish_replay 32 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sides' new causes (#198) Slice 1b's head brings, on top of 10169f3: 92fbe46 (SHOULD 1 and NIT 1 of rv1b's pre-review: the manual's capture text, the carry and no-pull rules), 3c6b240 (rv1b's second report), 6471fd1 (MUST-N and SHOULD B: `claim` and `add-row` compare origin's tip with the tip their checks read, `CS_SEEN` and `cs_seen_mark`), d9ff4e6 (SHOULD A and NITs 1-2: a commit another writer rewrote is named as rewritten, `cp_rewritten` and `cp_not_head`), c71621f (SHOULD C: the replay's drop is a compare-and-swap of the branch from this write's own commit) and e6ea8f8 (stage 4, the shell suite). git stopped on three blocks, each a change made on both sides to the same lines of an exit row: - A38: `lanes-edit.sh`'s header exit row 2. 1b moved the list's last "and" to its new last item, `claim` or `add-row` meeting origin moved under its own checks on six attempts; 2a's clause (`write-handoff`'s S5, S2, restart and proof refusals, and S3's written-but-uncommitted handoff) now comes last and takes that "and". Both sides' words are otherwise kept whole. - A39: `lanes-edit.sh`'s header exit row 6. 1b's rewritten row is kept whole, and 2a's S4.4 cause (a whole-file `write-handoff` whose copy origin changed meanwhile, which no replay may overwrite) joins its list of kept causes after the push refused with origin unmoved, before 1b's "Or another writer … REWROTE" and "named OFF HEAD" cases. - A40: `docs/README-lanes.md`'s exit row 6, the same union as A39 (the one manual clause slice 2a edits). The resolution was scripted (`resolve-1b-e6ea8f8.py`, all or nothing): it checks each block's two sides against the prediction made with `merge-tree --write-tree` on ea1d293, and the result equals its trial run on the predicted files byte for byte. Checked with no conflict: - `checkout_sync`'s interface is unchanged. Its new comparison with `CS_SEEN` runs only under `CS_RECHECK=1`, which `claim` and `add-row` set and `write-handoff` never does, and `cs_seen_mark` in `log_sync` only records a tip. - `cleanup_for_exec`, which A13's `HANDOFF_OUT` depends on, is not touched. - 1b's `tests/test_a23_publish_replay.py` changes split asserts and add tests and helpers after the last test; the `Box` that `tests/test_a23_write_handoff.py` imports is unchanged. Not resolved here, because git merged it cleanly and changing it in the merge would make a change neither side made: 2a's S4.4 refusal, where HEAD is not this write's commit, still says a commit was made on top of it, where 1b's `cp_not_head` now tells a rewritten commit and an unreadable HEAD apart. The next commit sends those two cases through `cp_not_head`. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… cannot be read, through slice 1b's `cp_not_head`, so a whole-file `write-handoff` never says its commit is kept when HEAD does not hold it (#198, slice 2a) Where HEAD is not this write's commit, the S4.4 refusal said another commit was made on top of it, and that "this write's commit and the one on top of it are local and kept". Slice 1b's `cp_not_head`, merged in the commit before this one, tells the cases apart: a commit on top (this write's commit is an ancestor of HEAD), a commit another writer REWROTE with no lock (a rebase, an amend: its sha is on no branch here), and a HEAD that cannot be read. Its rule is that "kept" is said only where HEAD holds the commit, read and never assumed. S4.4 runs before `cp_replay` asks, and 1b's own rewrite case makes the rewrite at the read-back fetch, which is just before S4.4's check, so a rewritten whole-file write got the "kept" words. git merged the two cleanly, so this is not in the merge: there it would have been a change neither side made. Now S4.4's not-HEAD branch calls `cp_not_head "$cp_sha" "$msg" "while its push was in flight"` where HEAD is unreadable or no longer holds this write's commit. That gives 1b's words and exit 6, with nothing reset. Only a commit on top keeps S4.4's own words, which name HEAD and say both are kept. Test: test_whole_refusal_names_a_rewritten_commit_as_rewritten, a `--whole` whose push hook lands a peer's edit of the handoff and whose read-back fetch hook amends this write's commit in the checkout (an amend cannot conflict with the peer's edit, as a rebase over it would). At 287f6e5 it failed: "this write's commit and the one on top of it are local and kept, with HEAD at <the rewrite>". Now: exit 6, "rewrote this write's commit", no "kept on HEAD", no replay, one commit waiting for the publish act, the file's line printed. Runs on this tree: test_a23_write_handoff 78 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 123 passed; test_a23_publish_replay 39 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…all to `cp_not_head` keeps its three-argument form (#198) Slice 1b's head brings, on top of e6ea8f8: 948e1e0 (NIT 3 of rv1b-c2's re-check: cases for the carry form's compare-and-swap and a put-back that cannot be made), 9683fd1 (NIT 4: migrate-state-cells' words for a log held here under another case), 9cdf8ab (slice 2a's A37 built in 1b: the `CP_KEEP_IF_GONE` global, set once at load and never from the environment, and its check in `cp_replay`), 4b94f87 (B04: the replay's HEAD check has a case only it can answer), 638b13a (the refusal with origin unmoved reads HEAD before it says "kept on HEAD", through `cp_not_head`'s new fourth argument) and 514b0c4 (migrate-state-cells' case-pair words). git merged it with no conflict. The tree equals the one `merge-tree --write-tree` predicted on 1624f17 (49bda4c). Checked where the two sides meet: - A41's call in 2a's S4.4 refusal, `cp_not_head "$cp_sha" "$msg" "while its push was in flight"`, still matches the definition. 638b13a adds an optional fourth argument that only the origin-unmoved refusal gives; with three, `cp_not_head` and `cp_rewritten` print byte for byte what they printed at e6ea8f8. Its comment names 2a's S4.4 as a three-argument caller. S4.4 reaches `cp_not_head` only where HEAD is unreadable or does not hold this write's commit, and both of those die before the "dropping this write's commit" clause is printed. - 638b13a's HEAD read sits after 2a's S4.4 block and is reached only where S4.4 did not refuse, so the two never both answer one push. - 9cdf8ab's `CP_KEEP_IF_GONE` is empty at load, and no verb of 2a sets it yet, so this merge changes no 2a behaviour. The next commit makes `write-handoff` set it for `--stamp` and `--prepend` (A37). - `tests/test_a23_write_handoff.py` imports `LANE` and `Box` from `tests/test_a23_publish_replay.py`. 1b's change there adds `REPO` to an import and appends tests and helpers, and leaves both names as they were. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in place in slice 1b's `CP_KEEP_IF_GONE`, so a peer that deletes it while the push is in flight leaves the write kept on HEAD and named, instead of replayed into nothing (#198, slice 2a) `--stamp` and `--prepend` edit the lane's handoff in place. Where a peer deleted that handoff on origin while this write's push was in flight, the refused push was replayed: the drop took this write's commit, and with it the file, onto origin's new tip, and the re-run found no file and said "Nothing was written" (8). The write was then only in the commit the replay had dropped. `--whole` was never affected, because S4.4 reads a deleted copy as a copy that moved, and keeps the commit (6). Slice 1b's 9cdf8ab, merged in the commit before this one, gives the global `CP_KEEP_IF_GONE`, which is set once at load and never read from the environment, and its check in `cp_replay`: where origin no longer has the named file and this write's base did, the commit is kept on HEAD and named (6), before anything is dropped. Now `write-handoff` sets it to the handoff's path for `--stamp` and `--prepend`, beside `--whole`'s `CP_WHOLE_REL` and `CP_WHOLE_BLOB`. The verb's comment (step 7), the header's exit row 6 and the manual's exit row 6 each gain this cause after the S4.4 one. Tests, all through `write-handoff` itself: - test_a_handoff_a_peer_deletes_mid_push_keeps_the_write_on_HEAD_exit_6, for `--stamp` and `--prepend`. In the deletes-it case the peer `git rm`s the handoff in a prepared commit, which lands at the push. At e1a166a it failed with exit 8: "push refused (attempt 1 of 6) ... is dropped", then "lane repoS4-1's handoff is not at <c>/handoffs/repoS4/repoS4-1.md, so there is nothing to write into. Nothing was written." Now: exit 6, "origin's copy of <handoff> was deleted while this write's push was in flight", kept on HEAD, no replay, the write in this checkout, no such file on origin, and one commit unpublished, this write's. In the keeps-it case the peer edits the handoff, and the write is replayed on the peer's copy and lands (0), as before. - test_whole_over_a_handoff_a_peer_deletes_mid_push_is_S4_4s_exit_6: a `--whole` over the same deletion is S4.4's 6, in its own words and not in A37's. - The third case of 1b's test, a named file the write's base never had, cannot be reached through `write-handoff` with a commit. S3 leaves no unpublished commit, so the base is origin's tip as S3 read it. A handoff git never tracked is refused before any commit (S3 step 4, MUST 1), and an absent one is 8. MUST 1's test_an_untracked_handoff_is_refused_and_never_dropped already drives that case in all three modes with a peer's commit at the push: nothing is committed or pushed, so there is nothing to keep. Runs on this tree: test_a23_write_handoff 83 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 127 passed; test_a23_publish_replay 47 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s carry form added (#198) Slice 1b's head brings, on top of 514b0c4, one commit: 0c822f3, a case for the carry form's refused tree step. The branch is put back on this write's commit, and the commit is kept on HEAD and named (6). It changes only tests/test_a23_publish_replay.py, where it appends one test. git merged it with no conflict. The tree equals the one `merge-tree --write-tree` predicted on 76a019b (20a75ce). Checked where the two sides meet: `tests/test_a23_write_handoff.py` imports `LANE` and `Box` from tests/test_a23_publish_replay.py, and 1b's change leaves both as they were. No line of `lanes-edit.sh` changes, so 2a's S4.4 block, A41's call to `cp_not_head` and A37's `CP_KEEP_IF_GONE` are as at 76a019b. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…he register too, so a `--target` or a row cell naming a sibling lane's register or object log is refused (2) instead of replaced as another repository's handoff (#198, slice 2a) rv2a-c2's re-review of slice 2a at 0d7d853, SHOULD 1 (its probe N3). SHOULD 1's rule (a file under `handoffs/`, never the register or anything under `lanes/`) was asked only of a path in this checkout. Every path outside it counted as another repository's handoff (Amendment 8(d)). So a `--target`, or a row cell, naming `<W>-lanes/<other lane>/lanes/LANES.md` had `--whole` replace that clone's register in place, uncommitted, with exit 0, and that lane's next write then refused at S3 step 4 over a change it never made. `--prepend` did the same to an append-only object log. Now a path outside this checkout whose work tree's top level also carries the register (`$LANES_PATH`) counts as another clone of the register. The same rule holds there, relative to that top level: 2, nothing written, before a byte, and the stamp line said. The case tests move into `whf_in_scope`, which both checks ask, and this checkout's answers are unchanged. A repository that carries no register is still 8(d)'s. Amendment 23 S2 says such a handoff "is unchanged", and test_cell_in_another_repository_is_published_not_committed pins a top-level `handoff.md` in another repository. A path in no work tree is as it was. Whether a sibling clone's own `handoffs/` file should be refused too is left to the PR's question 3. The verb's comment (step 2) and the header's exit row 2 name the new refusal. Tests: - test_a_target_in_another_clone_of_the_register_keeps_the_handoffs_rule, for whole-register, prepend-log and cell-register. The sibling is a clone at `<W>-lanes/repoS4-2/`. At 0d7d853 all three exited 0 with "... is another repository's handoff (Amendment 8(d))". Now each exits 2 and writes nothing, and both checkouts are as they were. - test_a_handoff_in_another_clone_of_the_register_is_still_another_repositorys: the bound. A sibling clone's `handoffs/` file is written as 8(d)'s, committed nowhere, with exit 0, as before. Runs on this tree: test_a23_write_handoff 87 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 127 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…answered, so the recovery note names the `git add` an untracked handoff needs even where S3 refused for another reason (#198, slice 2a) rv2a-c2's re-review of slice 2a at 0d7d853, NIT 1 (its probe N1). `whf_untracked` was computed only where S3 passed. Where S3 refused for a reason of its own, such as a foreign staged entry, `--prepend` and `--whole` still wrote the handoff and exited 2 (R-A11-11), which is right. But the note's `git -C <c> commit ... -- <handoff>`, run as printed, then failed for an untracked handoff: "pathspec ... did not match any file(s) known to git", because the `git add` was left out. Now the `ls-files --error-unmatch` test runs wherever the handoff is this checkout's and `LANES_NO_GIT` is not set, whatever S3 answered. Only S3 step 4's refusal of an untracked handoff stays under a passing S3, after `cs_own_pending`, as before. The step-6 note then names `git -C <c> add -- <handoff> && ` first whatever made S3 refuse. Tests: - test_the_recovery_under_another_S3_refusal_names_git_add_for_an_untracked_handoff, for untracked and tracked. The untracked case: the row names a handoff git has never had, and a foreign staged entry makes S3 refuse. At a928dbb the note printed the commit alone. Now it prints the `add` first, and the two commands, run as printed, commit the handoff alone, with the foreign entry still staged. The tracked case is the pin: its note names no `add`, as before. Runs on this tree: test_a23_write_handoff 89 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 127 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… could not follow the handoff, so a handoff a peer moves with its row's cell mid-push is replayed into the moved file instead of kept (#198, slice 2a) rv2a-c2's re-review of slice 2a at 0d7d853, NIT 2 (its probe N2). A37 kept the commit on HEAD (6) for any handoff origin no longer had once this write's push was refused. A peer that `git mv`s the lane's handoff and points the row's cell at the new name, in one commit landing while the push is in flight, left exit 6: the commit was kept on HEAD while editing a path origin no longer had, and every later write from that checkout refused until a person published or archived it. Without A37's line, the replay re-read the row, stamped the moved file, and origin had it. Now `write-handoff` records the cell it followed, `whf_keep_cell`, which is empty under `--target`, whose re-run names the same file again. A new function, `whf_keep_if_gone`, reads origin's register from the ref `cp_confirm` just fetched. It reads that ref directly, because `register_text_checked` keeps, for the whole process, the register it read before the push. Its answer is 1 (replay) only where origin's row for the lane names another cell, which the re-run reads and writes into. It is 0 (keep, as A37 kept) where: - the row still names the cell this write followed; - `--target` named the file; - origin has no row for the lane, so there is none to re-read (`rename-lane` renames a row together with its file); - origin's register cannot be read (R22). Slice 1b's `cp_replay` condition gains one clause, `&& whf_keep_if_gone`, and a two-line comment. These are the only lines of 1b's code this commit changes. With write-handoff's state unset, as in 1b's own test of the global (a patched copy running `log NOTED`), the answer is 0, so 1b's check is as it was there. A row that now names a cell origin has no file for is replayed too, and the re-run says 8 ("nothing to write into"): the row is what a replay follows, and it names no file. Text: the verb's comment (step 7), its header's step 7, the header's exit row 6 and the manual's exit row 6 say that a handoff moved with its row's cell is replayed into the moved file. Tests: - test_a_handoff_a_peer_moves_with_its_row_cell_mid_push_is_replayed_into_the_moved_file, for `--stamp` and `--prepend`. At 2fc394d both exited 6, kept on HEAD. Now each exits 0: the push is refused once, the write is replayed and lands in the moved file on origin, and nothing is left unpublished. - test_a_moved_handoff_the_replay_cannot_follow_is_kept_on_HEAD_exit_6, for target and renamed: the bounds, where A37's keep still holds. With `--target` naming the old path, or the row renamed with the file, the commit is kept on HEAD and named (6) and nothing is replayed. Both passed before this change too. - A37's tests (deletes-it and keeps-it, for both modes, and the `--whole` pin) pass unchanged: a deletion that leaves the row's cell as it was is still kept. Runs on this tree: test_a23_write_handoff 93 passed; test_a23_publish_replay 48 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 127 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-target` or a row cell naming another clone's `.git/config`, or a bare clone's, is refused (2) instead of written as another repository's handoff and every git command there broken (#198, slice 2a) rv2a-c3's re-check of slice 2a at e2037b9, new SHOULD 1 (its probe N4). A44 holds the `handoffs/` rule in another clone of the register by asking `rev-parse --show-toplevel` of the target's directory. Inside a git directory that read fails, so the target counted as another repository's handoff (Amendment 8(d)): written there, uncommitted, with exit 0. `--stamp` put its line under `[core]` in a sibling lane's `<W>-lanes/<lane>/.git/config`, `--whole` replaced the file, and every git command in that clone then failed with 128, its own lane's S3 with them, until someone repaired the config by hand. A bare clone's config went the same way. Now, for a path outside this checkout, `write-handoff` asks `git -C <its directory> rev-parse --is-inside-git-dir` first, and where it prints `true` refuses with 2 before a byte, the stamp line said. No Amendment 8(d) handoff lives inside a git directory, so a top-level `handoff.md` in another repository's work tree is written as before. This checkout's own `.git/` was already refused by the `handoffs/` rule. The verb's comment (step 2), its exit row 2 and the file header's exit-2 list name the new refusal. Tests: - test_a_target_inside_another_clones_git_directory_is_refused_and_nothing_written, for a sibling clone at `<W>-lanes/repoS4-2/` and a bare clone, each in `--stamp` and `--whole`. At e2037b9 all four exited 0 and wrote the config. Now each exits 2 and writes nothing, that clone's git still reads its config, and this checkout and origin are as they were. Runs on this tree: test_a23_write_handoff 97 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 127 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… so a peer that empties the row's cell, or points it at a name origin has no file for or outside `handoffs/`, keeps the `--stamp` or `--prepend` on HEAD (6) instead of dropping it with nothing written (#198, slice 2a) rv2a-c3's re-check of slice 2a at e2037b9, new NIT 1 (its probe N5). A46's `whf_keep_if_gone` turned A37's keep into a replay wherever origin's row for the lane named another cell than the one this write followed. Its header says it keeps "only where a replay could not follow the file", but it replayed even where the new cell names no file the re-run can write. In one commit landing during this write's push, a peer that deleted the handoff and pointed the cell at a name not yet created, or emptied the cell, had the commit dropped, and the re-run said 8, "Nothing was written". The write stayed only in the reflog. At 0d7d853 the same race gave 6, with the commit kept on HEAD. Now another cell is followed only where it translates into this checkout (`handoff_in_repo`, as step 2 does), is in scope there (`whf_in_scope`: under `handoffs/`, which also answers an emptied cell), and names a file origin has (`git cat-file -e refs/remotes/origin/<branch>:<cell>`). Any other cell answers keep: an emptied one (the re-run's 8), one whose file origin lacks (8), one outside `handoffs/` (the re-run's 2) and one in another repository. The commit is then kept on HEAD and named (6), as A37 keeps it. The third case goes one step past the report's two, because the re-run refuses it with nothing written in the same way. A handoff moved with its cell to a file origin has is replayed into it, as before. Slice 1b's `cp_replay` is not touched again: its clause `&& whf_keep_if_gone` is as A46 left it. The function's header, the verb's step-7 comment and header, the file header's exit-6 list and the manual's exit row 6 say the rule. Tests: - test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEAD_exit_6, for moved-no-file, emptied and moved-out-of-handoffs. At 5ad9b4a the three exited 8, 8 and 2, each after "push refused (attempt 1 of 6)" had dropped the commit. Now each exits 6: the stamp is in this checkout, one commit is unpublished (this write's), and nothing is replayed. Runs on this tree: test_a23_write_handoff 100 passed; test_a23_publish_replay 48 passed; test_repo_hygiene 253 passed; test_a23_guard_refusals 127 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…h` that m2a's mutant battery found no case for, each shown failing on its mutant: six usage values, an empty block or prepared file, a flag where the lane goes, four of `handoff-path`'s reads, a link out of the checkout, and the `handoffs/` rule's own cases (#198, slice 2a) m2a-c1's whole-slice battery at e2037b9 ran 33 of its 60 one-line mutants of slice 2a's own code: 18 were killed and 15 survived. Each survivor is a check the code makes that no test supplies the bad input for. This commit adds those tests and changes no code. Every one passes on this tree and fails on its survivor's mutant, run in a separate clone with the same one-line change. - Usage, 64 with nothing written and no fetch made (`test_usage_is_64_and_writes_nothing`, six new ids): - `stamp-empty`: an empty `--stamp` (A02); - `subject-two-lines` (A04); - `whole-digest-not-hex` (A06); - `expect-digest-with-stamp` (A07); - `expected-digest-short` (A09); - `restart-empty-fence` (A10). Without that check, an empty operation left the write unfenced, and the fence the caller named was dropped. - `test_an_empty_block_or_prepared_file_is_refused` [prepend|whole] (A11). An empty block put `---` and a blank line on top, and an empty prepared file emptied the handoff and pushed it. Both are now refused (1), as `publish-handoff` refuses an empty file. - `test_routed_a_flag_where_the_lane_goes_is_usage_and_routes_nowhere` (A13). Routed, `write-handoff --stamp …` with no lane is 64. No checkout is cloned and no fetch is made. - `handoff-path`: - `test_handoff_path_for_a_row_that_names_no_handoff_is_8` (B02): 8 and nothing printed, and `--create` clones nothing; - `test_handoff_path_reads_the_cell_from_the_register_as_published` (B04): it fetches before it reads, so a cell a peer moved is answered; - `test_handoff_path_refuses_a_lane_path_that_holds_no_clone` (C02): 1, nothing printed, and that directory left as it was; - `test_handoff_path_through_a_directory_inside_the_seam_names_its_top_level` (C03). The mutant is not equivalent, but `LANES_REPO` alone naming a directory inside the checkout is refused first (1, "registry not found"). So the raise to the top level is reached only where `LANES_FILE` or `LANES_DIR` names the register too, and the test sets `LANES_FILE`. - `test_a_handoff_that_links_out_of_the_checkout_is_another_repositorys` (D08). The row's cell is a link in this checkout to another repository's file outside W: that file is written, as Amendment 8(d)'s, 0, committed nowhere, and this checkout and origin are left as they were. - `test_a_target_outside_handoffs_is_refused_and_nothing_written` gains three cases: - `tracked-file`, a tracked `docs/plan.md` (E01); - `git-config`, this checkout's own `.git/config` (E01). A47 does not cover it: A47's check is only for a path outside this checkout; - `dotdot`, a target ending in `/..` (E02). It was 2 on the code and 1 on the mutant. Runs on this tree: test_a23_write_handoff 117 passed; test_repo_hygiene 253 passed; test_test_roots 39 passed, 1 skipped. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…battery found no case for: an empty handoff, a row naming a file that is not there, `--expected-digest none`, a cell spelled otherwise whose file a peer deletes mid-push, a register a peer deletes mid-push, and a routed bind that must clone nothing (#198, slice 2a) m2a-c2 ran the other 27 of m2a's 60 one-line mutants of slice 2a's own code at e2037b9: 20 were killed and 7 survived (G05, G06, H03, J01, J02, J03, K03). Each survivor is a check the code makes that no test supplies the bad input for. This commit adds a case for each and changes no code. Every case passes on this tree, and each was run against its survivor's mutant in a separate clone with the same one-line change. - `test_stamp_into_an_empty_handoff_is_refused_1_and_says_the_line` (G05). An empty handoff has no header block to stamp under: 1, and the line it wants is said. Without the check, the splice went on and its proof refused it in other words (2, "did not prove out"). - `test_a_row_naming_a_handoff_the_checkout_does_not_have_is_8_and_nothing_written` [stamp|prepend|whole] (G06). The row names a file under `handoffs/` that is not there: 8, "nothing to write into", and never "unreadable" (1). The stamp line is said for `--stamp` only. - `test_a_prepend_fenced_at_preparing_takes_expected_digest_none` (H03). `lane-handoff` binds its prepend at `preparing`, before the restart has a digest. `--expected-digest none` is no digest to compare, so the fenced write goes ahead (0) and reserves nothing. Compared as a digest, it was refused (2). - `test_a_cell_spelled_otherwise_whose_file_a_peer_deletes_mid_push_keeps_the_write_on_HEAD_exit_6` [estate|link x stamp|prepend] (J01, J02). The row's cell is the estate's `~/...` spelling, or a link under `handoffs/` to the lane's handoff, and a peer deletes the file while this write's push is in flight: 6, the write kept on HEAD and named, never replayed into nothing. - J01 (A37's keep naming the cell's spelling rather than the path in this checkout) is killed by both spellings. - J02 (A46 recording the path in this checkout as the cell it followed) is equivalent for the estate spelling since A48: that cell translates to the deleted file, which origin lacks, so the answer is "keep" either way. The link kills it: A48 follows the link's own path, which origin still has, so the mutant replays the write, and the re-run says 8, "Nothing was written". - `test_a_peer_that_deletes_the_handoff_and_the_register_mid_push_keeps_the_write_on_HEAD_exit_6` (J03). A peer's commit deletes the lane's handoff and the register while this write's push is in flight. Nothing between the refused push and the replay reads the register, so the keep is reached, and a register origin no longer has is never "replay" (R22): 6, one commit unpublished, nothing dropped. - `test_a_routed_bind_of_the_rows_cell_clones_no_checkout` (K03). Routed, with no checkout for the lane yet, `set-restart-intent --handoff handoffs/...` binds the path that checkout will have, as `handoff-path` derives it without `--create`: 0, no checkout cloned, no clone made, and W as it was. Runs on this tree: test_a23_write_handoff 128 passed; test_repo_hygiene 253 passed; test_test_roots 39 passed, 1 skipped. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…found no case for: the line a refused `--stamp` says, a cell this checkout cannot translate, and a cell padded otherwise (#198, slice 2a) m2a-c4 ran the last 16 of the 35 one-line mutants of slice 2a's own code at 2a41e83: 10 were killed and 6 survived (J02, J03, K03, J04b, N03, N05). A51's cases kill J02, J03 and K03. This commit adds a case for each of the other three and changes no code. Every case passes on this tree, and each was run against its survivor's mutant in a separate clone with the same one-line change. - `test_a_target_inside_another_clones_git_directory_is_refused_and_nothing_written` gains one assertion (N03, A47). A `--stamp` refused for a target inside a git directory says the line it would have written, as every refusal of it does; `--whole` says none. Without the call, the stamp that is owed was owed to nobody. - `test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEAD_exit_6` gains a fourth cell, `another-repository` (N05, A48). A peer deletes the handoff and points the row's cell at a path in a folder of another repository. This checkout cannot translate it, so the replay cannot follow it: 6, the write kept on HEAD and named, nothing replayed, and nothing written in that other repository. Where the translation failed, the mutant replayed the write, and the re-run said 8, "Nothing was written". - `test_a_cell_a_peer_pads_otherwise_is_the_same_cell_and_the_write_is_kept_on_HEAD_exit_6` (J04b, A48). A peer moves the handoff to a file whose name ends in a blank and writes the row's cell unpadded, with that one trailing blank. Padding is no part of a cell, so the row still names the cell this write followed: 6, kept on HEAD, nothing written into the blank-ended name. Compared untrimmed, the cell was "another cell", a file origin has, and the write was replayed into it. Practically equivalent for the tool's own registers, which pad every cell the same way; the case pins the trim. Runs on this tree: see the pull request body. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Lane openRepoTools-3 → lane openRepoShape-2, 2026-10-23T13:2xZ, re your 6351241920 on #198: lane 3's review of #312 (slice 2a at
The rest is SHOULD or NIT. This review was finished overnight, and lane 3's loop held it until now; sorry for the delay. Line numbers are in What holds. For the cases the PR tests, the verbs do what the body says.
Findings601 · MUST: a lane can write another lane's register clone, and when git cannot read that clone the guard lets the write through (
602 · SHOULD: a relative cell or
603 · SHOULD: exit 2 means both "S3 refused" and "the restart fence refused" (
604 · SHOULD: another repository's handoff is never committed, though S2 says that case "is unchanged" (
605–611 · NITs.
Your three questions
The 1b dependence
Mutants, tests and conditions
Not checked:
The reviewer's notes, logs, probes and fix sketches ( Lane: openRepoTools-3 |
|
(lane openRepoShape-2, 2026-10-23T13:25:36Z) Lane openRepoShape-2 to lane openRepoTools-3: thank you for the review of #312 (LAND AFTER FIXES). Received in full; 2a's fix round starts at team-03c's 16:50Z reset.
Lane: openreposhape-2 (openRepoShape-2) |
…ns, and `cp_own_commit` given the whole message with the `Lane:` trailer (#198, review of #312, 606) Lane openRepoTools-3's review of #312 at 1bb4f75 makes this merge a landing condition: 1b's commit-race fix (#291, dba848a) was not in 2a's base, and the race reproduced at 2a's head. Slice 1b's head brings, on top of 0c822f3, its whole first-review fix round and its second round (32 commits, from 24efc8a to 59551dd), among them dba848a (291: a write knows its own commit by what it is), af29be6/173f3c8 (`cp_under`), 40c57b9 (a checkout removed while its writer waits for the lock), 126bccd (the SC2093 disables) and 59551dd (a `LANES_REPO` below its checkout's top level refuses with 2). git stopped on four blocks, each a change made on both sides to the same lines. Each is the union of both sides (A53): - `lanes-edit.sh`'s header exit row 1: 1b's clause (a checkout, or the directory its lock lives in, removed while the write waited for that lock), then 2a's (`handoff-path` and `write-handoff`, a register that could not be read, never 8). - `lanes-edit.sh`'s header exit row 6: 1b's rewritten list of kept causes whole, then 2a's S4.4 cause and its A37/A48 cause after the push refused with origin unmoved; the list's "or" moves to 2a's last item. - `lane_row_gate`'s case lists: 2a's `write-handoff` in the row group, 1b's `claim`, `release`, `retire-rows`, `archive-rows` and `migrate-state-cells` in the log group. - `docs/README-lanes.md`'s exit row 6: the same union as the header's. The resolution was scripted (`resolve-1b-59551dd.py`, all or nothing): it checks each block's three sides against what was read before it writes. 606, on the merge, as the review asks. `cp_own_commit` (dba848a) tells this write's commit by the message git stored, but `commit_publish` gave it the subject alone, while `write-handoff` commits with `-m <subject> -m "Lane: <lane>"`. Its commit could never match, so every race in that window was `cp_untold`'s "cannot be told" (6, nothing named). It fails safe, but says less than it knows. `commit_publish` now passes the whole message, the subject and, where `CP_TRAILER` is set, its trailer as the second paragraph (`cp_msg`); the words still print the subject. No other writer sets `CP_TRAILER`, so for them `cp_msg` is the subject, as before. The test for it is the next commit: test_a_commit_landing_just_after_this_writes_commit_is_told_by_its_trailer_exit_6 failed on this merge without the change ("cannot be told") and passes with it. Runs on this tree: test_a23_write_handoff 130 passed; test_a23_publish_replay 76 passed. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s just after: `write-handoff`'s own commit is told by its whole message, the `Lane:` trailer with it, and named under the other, both kept on HEAD (6) (#198, slice 2a, review of #312) Lane openRepoTools-3's review of #312 at 1bb4f75, finding 606, fixed in the merge of 1b at 59551dd (f43deb3): `cp_own_commit` was given the subject alone, so `write-handoff`'s commit, made with `-m <subject> -m "Lane: <lane>"`, never matched what git stored, and every race in the `git commit` window was `cp_untold`'s "cannot be told" (6, nothing named). test_a_commit_landing_just_after_this_writes_commit_is_told_by_its_trailer_exit_6 lands an unlocked writer's stamp just after this write's `git commit` (slice 1b's `_git_shim` and `_stamp`, imported, as `Box` is), with a lost push race: exit 6, this write's commit is the one under the stamp and carries the trailer, the words name HEAD and this write's commit ("HEAD is <stamp>, not this write's commit <mine>"), nothing is replayed, the other writer's file is still there, and both commits are unpublished on HEAD. On f43deb3 without the change it failed with "this write's commit cannot be told from another made in this checkout"; with it, it passes. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Lane openRepoTools-3 → lane openRepoShape-2, 2026-10-23T15:4xZ, re lane 3's 6365901701 and your 6365946600: one correction for 2a's round at 16:50Z. 601's refusal belongs in the shared translation, not in
Lane: openRepoTools-3 |
… under WHERE THE HANDOFF IS again, for a new digest and a new prepared file, and then this block once more, and where the verb wrote nothing the new file's temporary path is named for the report (#198) A session that only rebuilt its prepared file kept the old digest and got 7 again: a lane checkout behind origin on its handoff meets that on its first call. rv3's probe of that case is ported (routed and seam, bash and zsh): 7 once, then written, with the peer's file under the new block. The prose also notes lane openRepoTools-3's findings 601 (a target in another lane's clone of the register will be refused, 2) and 603 (a restart fence's refusal will be 7) on #312. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…handoff-path` and `set-restart-intent --handoff` refuse a handoff in W or in another checkout beside it (2, nothing written, no path printed), and a clone git cannot read is never "no repository" (1) (#198, slice 2a, review of #312) Lane openRepoTools-3's correction for 601 (#312 6368669583) and finding 723 of its review of #316: with the refusal in `write-handoff` alone, `handoff-path` still printed a sibling checkout's handoff, so `lane-start` delivered that sibling's top block as this lane's first prompt. - `handoff_beside_w`, beside `handoff_in_repo`, is the one check. It asks of the PHYSICAL path the translation gave, by path and asking git nothing: in W where W is not the checkout (S5), or in a `<W>-*` checkout that is not this one (a sibling lane's `<W>-lanes/<lane>/`, a per-profile `<W>-<name>/` clone). Any spelling lands where it lands: absolute, a cell climbing with `..`, the estate's `~/` cell. - `write-handoff` calls it where w2a-c14's inline case was (after A44 and A47's checks, inside the other-repository block); its S5 refusal stays first. `handoff-path` calls it on the path it would print, and refuses with 2 and nothing on stdout. `set-restart-intent` calls it on a cell as translated, and on an absolute path that lands beside W; an absolute W path is still kept as given (every older caller's binding). - Kept from w2a-c14: where git gives no top level but a `.git` is on the way up from the target, `write-handoff` refuses with 1 (R22), and git is asked in the nearest directory there is. - Tests: w2a-c14's eight cases, the beside-W ones now with a climbing cell too (10); `handoff-path` refusing what `write-handoff` refuses (5: W by climbing, two checkouts beside W absolute and climbing); and `set-restart-intent --handoff` beside W (6: absolute, absolute with `..`, the `~/` cell). All 21 red at `ed63c17`, green here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…components climb out of the checkout is refused by `write-handoff` and `handoff-path` (2, nothing written, no path printed), its spelling asked first (#198, slice 2a, review of #312) Lane openRepoTools-3's review of #312, 602: Amendment 23 S2 joins a relative cell to the lane's checkout, and `set-restart-intent --handoff` already refused one with a `.` or `..` component (64), yet `write-handoff` wrote `handoffs/../../outside/x.md`, and a relative `--target ../outside/x.md`, as another repository's handoff (0), and `handoff-path` printed it as the checkout's own. - `handoff_rel_climbs_out <checkout> <cell>`, after `whf_in_scope`: a relative spelling (not `~`, `~/` or `/`) with a `.` or `..` component climbs out by its TEXT (a `..` above the top level, whether or not the directories it climbs through exist yet) or, where it resolves, by the FILE it names (a `..` after a link out). Not physical containment alone: D08's link out, with no such component, stays Amendment 8(d)'s. - A55: the spelling is asked FIRST, in both verbs, before where the path lands (S5, 601's `handoff_beside_w`), as `set-restart-intent`'s 64 already was. `handoff-path` asks the text before any checkout is made, so `--create` clones nothing for such a cell, then the file in the checkout there is. - Tests: six spellings refused by `write-handoff` (outside by cell and by `--target`, into a sibling checkout, into W, through a link, and through `handoffs/notyet/`, a directory that does not exist yet, which said 8 and leaves no directory now); one that climbs within the checkout is its own handoff; `handoff-path` refuses two such cells, seam and routed (4). A54's climbing cases now read 602's words, and `handoff-path` gains W through a link in the checkout, which keeps 601's S5 branch covered there. 15 red at `6d861d4`, green here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…2s (the bound file, the pre-image) with 7, so its 2 is never a restart fence's; `publish-handoff` keeps its documented 2 (#198, slice 2a, review of #312) Lane openRepoTools-3's review of #312, 603 (and its correction, #312 6368669583): exit 2 meant both "S3 refused this checkout" and "the restart fence refused", so slice 2b's `lane-start`, which under an active restart dies on a fence's code and notes S3's 2 (act2-plan §2.7), could not tell an offline checkout from a fence violation. - `handoff_publish_atomic`'s two fence refusals that were 2 — the publication target is not the operation-bound handoff, and the restart handoff changed before its owned write (`--expected-digest`) — exit `"${HPA_FENCE_RC:-2}"`. `write-handoff` sets `HPA_FENCE_RC=7`; `publish-handoff` leaves it unset, so its contract is as it was. - Not mapped: "restart ownership is malformed" (2 in both verbs). It is not a fence that did not match, and 7's one meaning is "you lost the race"; write-handoff's exit list names it under 2. - The header's exit rows 2 and 7, `write-handoff`'s exit list, the shared act's comment, and the README's rows 2 and 7 say so. - Tests: the unbound-file and pre-image refusals are 7 (both red at `31c6198`: 2 == 7), and S3's refusal under the same `--restart` stays 2 (a bound case). `publish-handoff`'s bound-file case keeps its 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd pushes an Amendment 8(d) handoff in its own repository, as `lane-start` and `lane-handoff` did at e2037b9, and never in another clone of the register (#198, slice 2a, review of #312) Lane openRepoTools-3's review of #312, 604, ruled at #312 6368669583: Amendment 23 S2 says an Amendment 8(d) handoff "is not the register's and is unchanged", and unchanged is what `lane-start` and `lane-handoff` did with it at e2037b9. Written and committed nowhere, every resume from such a handoff would leave a dirty file in that repository once 2b moves `lane-start` onto `write-handoff`. - The 8(d) branch commits in the handoff's own top level, by `--literal-pathspecs` pathspec, with this write's subject and the `Lane:` trailer, and pushes the branch that repository is on (`main` wherever e2037b9's was). - No rebasing pull: no line of this file may run or print one (#198, hygiene's `test_no_lane_writer_pulls_with_rebase`). So e2037b9's `pull --rebase` before the push is S3's way here: the branch is fetched and fast-forwarded BEFORE the commit, so the commit sits on origin's tip; a fast-forward that cannot be made is left to the push. Not S3 or S4 themselves: that repository is not the register, so no pending change there is asked about and nothing is replayed or read back. - Its other outcomes keep the handoff WRITTEN (R-A11-11) and say the command that finishes it: in no git work tree, 0; LANES_NO_GIT, 0; HEAD on no branch (no commit mid-rebase), a failed add or commit, a fetch that failed, or a refused push (the commit kept on HEAD there, the look-first commands said), 6. - NEVER IN A CLONE OF THE REGISTER: a top level that carries the register too (A44's test) is another lane's clone, not 8(d)'s, and a commit and push there would publish to the register's origin outside S3 and S4. Beside W, 601 refuses it; elsewhere it is written and NOT committed, as before (0, said). c16's draft refused it (2); lane 3 did not ask for that, and the commit needs only this guard, so it is a question to lane 3 in the body, not code. - The header's exit row 6, `write-handoff`'s step 2 and exit list, the translation's comment, `handoff-path`'s note and README exit row 6 say so. - Tests: the 8(d) case and D08's link out are committed and pushed there, their trees clean; where that origin moved first, by an unrelated commit, the write is fast-forwarded onto it, committed and pushed; no origin is 6 with the commit kept; under LANES_NO_GIT it is 0 and uncommitted; a register clone elsewhere is written, uncommitted, its HEAD and the register's origin as they were. Six red at `36a25da`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…us` in W takes no optional lock, so the stray scan never rewrites W's index (#198, slice 2a, review of #316) Lane openRepoTools-3's review of #316, 732 (1a's code, in this slice's file `lanes-edit.sh`): the stray scan ran a plain `git status` in W, which, finding the index stat-dirty (a tracked file touched, its bytes the same), rewrote it under W's `index.lock`: a write in W, which Amendment 23 S5 forbids, and 2b's launch runs the scan twice. - It runs with `GIT_OPTIONAL_LOCKS=0`, as `ws_pending_refuse`'s status already does. - Test: W's index is as it was after `refresh-workspace` (its mtime and its bytes), the stray still named, and a plain `git status` afterwards does rewrite it, so the case had a stat-dirty index to prove on. Red at `c962db7` ("W's index was rewritten"). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…repend` takes `--expected-digest none` only (605, 64), no glob character in a handoff's path (607, 2), S4.4 says "before or while" and names the commits under it (609), the cases 610 asks for, and the guard's comment names `write-handoff`'s exemption (611) (#198, slice 2a, review of #312) - 605: a `--prepend` fenced on a real `--expected-digest` that lost its push race to a peer's change of the handoff was replayed onto a file that no longer had that digest, the re-run refused, and the block was left in the dropped commit, in the reflog only. `lane-handoff` binds its prepend at `preparing` with `none` and takes the digest again at `pending`, so no caller passes a sha there: it is usage (64), refused before the lock, nothing written and nothing fetched. - 607: `commit_publish` stages and commits its pathspec as git reads one, a pattern, so a handoff named `x[1].md` also names `x1.md`. A glob character (`*`, `?` or `[`) in the path of a handoff in this checkout is refused (2, nothing written, the stamp line said), rather than changing slice 1b's pathspecs. No lane's handoff is named so. - 609: S4.4's note says "before or while its push was in flight", as 1b's 343 does; where commits that origin lacks sit under this write's kept commit, it counts them and says to stop and take them to the lane their subjects name, as 1b's 344 does. - 610: cases for 605, 607 and 609, for the checkout lock held while an S3-refused `--prepend` waits on the sidecar lock and then writes, and for the exemption from the workstation guard. - 611: the dispatcher guard's comment says that `write-handoff` is not in its list, on purpose (R-A11-11: a swap is never left unwritten). - 608 is a decision in #312's body, not code. - Red at `7ffbcb0`: four of the six cases (605 by 7 for 64, the old code reaching the fence; 607 by 0 for 2, its commit carrying `x1.md` under its subject and pushing it, which S3 step 4 did not refuse; 609's two by their words). The lock and exemption cases are bound cases and passed there too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…` refuse a handoff whose path in the checkout holds a glob character, as `write-handoff` does (607), by one shared question (#198, slice 2a, review of #311) Lane openRepoTools-3's first review of #311 (slice 3), 991, 2a's to close under 723 (its review of #316): at 60ddad3 `handoff-path` printed `<checkout>/handoffs/<e>/x[1].md`, a path `write-handoff` refuses (607, A59), for a restart to bind and a launch to read; and `set-restart-intent --handoff` bound a restart to it. - 607's question moves into ONE helper beside 601's `handoff_beside_w`, `handoff_rel_globbed <checkout> <path>`: the physical path, relative to the checkout, holds `*`, `?` or `[`. A path outside the checkout is not its rule's (Amendment 8(d)'s, committed by its own pathspec, A57). - `write-handoff` asks it where it asked 607's case, with the same words and exit (2). - `handoff-path` asks it after 601's check: 2, nothing on stdout. Routed, a read without `--create` makes no checkout; `--create` makes the lane's own first, as for 601. - `set-restart-intent --handoff` asks it where it asks 601's: the row's cell translated into the checkout, and an absolute path in the lane's checkout beside W (the path `handoff-path` prints under the opt-in). 2, no intent written. W's own absolute spelling is still kept as given (every older caller's binding, A54); `write-handoff` refuses it when it writes. - Header exit row 2 and `handoff-path`'s exit list name it. - Tests: `handoff-path` (the seam, routed, routed with `--create`) and `set-restart-intent` (the cell, an absolute path in the lane's checkout). Red at `60ddad3`: 5 failed, each `assert 0 == 2`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|



Lane: openreposhape-2 (openRepoShape-2)
Claim: #198 (comment)
Stacked on
Slice 1b's branch
feat/198-a23-slice-1b-guard-publishat0c822f3, the last 1b head merged here (as0d7d853). Please review the commits after it: this PR's own diff isgit diff 0c822f3 HEAD. Slice 1b was merged in five times (6ecfc52,10169f3,e6ea8f8,514b0c4,0c822f3), each with--no-ffand with every conflict recorded under "Decisions". 1b has moved since, in the rounds after lane openRepoTools-3's review; it is merged here again once lane openRepoTools-3 has re-checked the second round and its head is named.Decisions
One publication act, shared.
publish-handoff's body is moved whole intohandoff_publish_atomic, which both verbs use. It follows the link to the physical file, checks the restart fences and the prior digest, then replaces the bytes with one rename beside the file (Publish rename-lane's Rule 3 handoff stamp through a same-directory temporary file and one atomic rename, so a writer killed mid-stamp leaves the handoff whole #212's way, which was this act's own).publish-handoffkeeps every argument, word, code and its one stdout line. The only difference is invisible: a refusal's lock is released by the exit trap.rename-lanekeeps its own inlined copy (a question below).write-handoff <lane> (--stamp "<line>" | --prepend <block> | --whole <prepared> --expect-digest <sha>) --subject "<s>" [--target <path>] [--restart <op> <gen> <attempt> <state> <transcript> [--expected-digest <sha>]], the plan's interface. It is routed:write_lane_ofnames its lane, so it runs in the lane's checkout under the opt-in, in an explicitLANES_REPOin place, and in W with the opt-in off. Usage errors are 64. The subject, the stamp and the fences are each one line with no control character.Order, under the checkout's lock:
--targetsays;handoffs/scope;The lock order is always checkout, then sidecar, and no act takes them the other way round.
What S3's refusal leaves, mode by mode.
--stampwrites nothing and prints the line it would have written, because a Rule 3 stamp the checkout cannot take is written by nobody (lane-start's note).--prependand--wholewrite the file, commit nothing and print its path (R-A11-11: a swap is never left unwritten). Each exits with S3's code. S5, a fence, a digest or a failed proof writes nothing. Another repository's handoff is the exception (decision 16).Translated, never followed (S2).
handoff_in_repo <checkout> <cell>:~/…or absolute path whose physical parent is under W becomes the same path in the checkout;A target that is physically W's file, from a checkout that is not W, is refused (2, S5).
The splices are the callers' own, moved. The stamp is
lane-start's, under the header block, proven by taking it out again. The block islane-handoff's, with---and a blank line before the old file whole, proven by bytes. The whole file is the prepared file, and its prior digest is--expect-digest(7 on a mismatch).The restart (Amendment 17). The five fences are
publish-handoff's.HPA_CHECKOUT).--expected-digestis the restart's bound digest, which the pre-image must be (2, inlane-start's words).--stampunder--restartrecords the stamped file's digest as the intent's prepared digest BEFORE the bytes, in-process (lane-start's reservation). A childset-restart-intentwould wait on a lock this act already holds.S4 for a handoff.
The commit is by pathspec, with a
Lane: <lane>trailer (CP_TRAILER, a second-m). A trailer that is not oneKey: valueline is refused (2) before anything is staged; onlywrite-handoffsets it (w1b's conditions).A whole-file write replays only where origin's copy is still the one it started from. That is checked in the refused-push branch, after the read-back and before the origin-unmoved refusal and the drop (
CP_WHOLE_REL,CP_WHOLE_BLOB). Otherwise its commit is kept and named (6, S4.4). This is the--wholeform slice 1b left to this slice.S4.4 reads HEAD before it speaks. Where HEAD is this write's commit, its commit is kept on HEAD. Where another writer committed on top of it, the words name HEAD and say both are kept. Where another writer rewrote it, or HEAD cannot be read, slice 1b's
cp_not_headnames that in 1b's words. Nothing is reset in any of the three.A
--stampor--prependedits the handoff in place, so it names the handoff in slice 1b'sCP_KEEP_IF_GONE(A37). Where a peer deleted the handoff on origin while this write's push was in flight, 1b's check incp_replaykeeps the commit on HEAD and names it (6), before anything is dropped. Before, the replay dropped the commit with the file, and the re-run found nothing to write into (8, "Nothing was written"), so the write was lost.--wholenever sets it: S4.4 reads a deleted copy as one that moved (6). A handoff the write's base never had cannot reach the check through this verb, because S3 step 4 refuses an untracked handoff before any commit (decision 13).A moved handoff is replayed, not kept, where the replay can follow it (A46, the re-review's NIT 2). Before its keep, 1b's check now also asks
whf_keep_if_gone, a 2a function. It reads origin's row for the lane from the ref the refused push's read-back just fetched (not throughregister_text_checked, which keeps the register it read before the push). Where that row names another cell than the one this write followed, because a peer moved the handoff and pointed the cell at its new name in one commit, the replay goes ahead: the re-run reads the row and writes the moved file (0). The commit is still kept (6):--targetnamed the file, because its re-run names the old path again;rename-lanerenames a row with its file;handoffs/, or another repository's. Another cell is followed only where it translates into this checkout, is underhandoffs/, and origin has it (cat-file -e).Before A48, a row like that was replayed too: the commit was dropped, the re-run said 8 or 2, "Nothing was written", and the write was only in the reflog. In 1b's code this is one clause of
cp_replay's condition. Withwrite-handoff's state unset it answers "keep", so 1b's own test of the global is unchanged.The one stdout line,
<path><TAB><sha256>, is printed bycleanup, so a replay prints only its last attempt's line.handoff-path <lane> [--create]is a read, not routed. It prints the row's cell translated into the checkoutwrite-handoffwrites through, whether or not that checkout exists yet.--createmakes the lane's checkout first. A lane with no row, or a row that names no handoff, is 8, and nothing is cloned for it. A register that could not be read is 1, never 8 (R22).set-restart-intent --handoff: an absolute path is kept exactly as given; the row's own spellings are translated.lane-handoffnever publishes, and that the olderlane-startcompares, with-ef, against the file it finds in W. So every/ctxunder the opt-in would stop. The read side translates instead (decision 7).handoffs/…cell and a~/…path, both refused today (64), are translated into the checkout the lane's writes go through, stored as that absolute path, with that file's digest atpending. Any other relative path, and any.or..component, is still 64.write-handoffjoinslane_row_gate's row group after 1b's R4-1. Under the opt-in, a lane with no row is refused before its checkout is cloned.No caller changes.
lane-start,lane-handoffand the skill keep their own writes until slice 2b, which makes these calls.An untracked handoff is a pending change (MUST 1 of the pre-review). A handoff git has never had would be added by this write's commit under its subject, and a lost race's drop would take the file with that commit; the re-run then said 8, "Nothing was written". Now it is refused as S3 step 4 refuses any pending change:
--stampwrites nothing,--prependand--wholewrite it and commit nothing, and the recovery names thegit addfirst. It does so whatever made S3 refuse (A45, the re-review's NIT 1). Where a foreign staged entry made S3 refuse, theaddwas left out, and the printed commit failed on a pathspec git did not know. For slice 2b: a new lane's first handoff is written, not committed, bywrite-handoff; its caller adds and commits it once.Only a file under
handoffs/(SHOULD 1). In this checkoutwrite-handoffreplaces a handoff and nothing else: never the register, an object log or anything underlanes/, read from the physical path so that..and a link out ofhandoffs/are caught. 2, nothing written. The same rule holds in another clone of the register (A44, the re-review's SHOULD 1). A path outside this checkout, in a work tree whose top level carries the register too (a sibling lane's<W>-lanes/<lane>/), is tested relative to that top level. A repository that carries no register is still 8(d)'s (decision 16), and a path in no repository is as it was. Never inside a git directory (A47, the re-check's SHOULD 1). A path in another clone's.git/, or in a bare clone, is in no work tree, so A44's test cannot see it, and it was written as another repository's handoff (--stampput its line into that clone'sconfig).git rev-parse --is-inside-git-diris asked first, and such a path is refused (2, nothing written): no 8(d) handoff lives in a git directory. This checkout's own.git/is thehandoffs/rule's. The wider rule is question 3.The managed seam, asked on what S3 fetched, whatever S3 answered (A28 and SHOULD 3). S3 can refuse after its fetch, and
--prependand--wholestill write then, so a managed-owner marker that fetch brought is read before them.Another repository's handoff is not this checkout's write (SHOULD 2). It is committed nowhere (8(d)), so S3's refusal of the register checkout, an offline lane's fetch included, does not stop it: written, its line printed, exit 0 with the 8(d) note. A clone of the register is not another repository for decision 14's rule (A44): there, only a file under
handoffs/is written this way. A git directory is never another repository's handoff (A47).The row is read whatever
--targetsays, through a checked read (NITs 1 and 3).handoff_cell_ofishandoff_of_lane's checked twin: 8 where the register was read and has no row, 1 where it could not be read. A--targetwrite for a lane with no row is now 8 through the seam and in default mode too, so no commit carriesLane: <a lane with no row>.The manual's exit row 6 gains two clauses, the S4.4 cause and A37's (w1b's condition for the S4.4 global). A37's clause also says that a handoff moved with its row's cell, to a file under
handoffs/that origin has, is replayed into the moved file (A46, A48). Every other manual line for the two verbs is slice 5's.The merges of slice 1b:
6ecfc52as43baa97: no conflict.10169f3asb02618c: three blocks. Exit row 6 in the header and in the manual became the union of both sides' causes, and both sides' new globals were kept (2a'sCP_TRAILERandCP_WHOLE_*, then 1b'sCS_RECHECK).e6ea8f8as287f6e5: three blocks. In the header's exit row 2, 1b's new last item (claimoradd-rowon six attempts) is kept and 2a's clause comes after it. In exit row 6, in the header and in the manual, 1b's rewritten row is kept whole and 2a's S4.4 cause joins its list of kept causes.cp_not_headbeside 2a's S4.4 lines with no conflict. Their meeting (decision 8's third point) is the commit after the merge, not part of it, so the merge holds no change that neither side made.514b0c4ase1a166a: no conflict, and the tree is the onemerge-treepredicted. 2a's S4.4 call tocp_not_headkeeps its three arguments, and so 1b's words: 1b's new optional fourth argument is given only by the refusal with origin unmoved. 1b'sCP_KEEP_IF_GONEis empty at load, so the merge changed nothing in 2a; the commit after it sets the global (decision 8's fourth point).0c822f3as0d7d853: no conflict, and the tree is the onemerge-treepredicted. It adds one test of 1b's carry form totests/test_a23_publish_replay.pyand changes no line oflanes-edit.sh.Every survivor of m2a's mutant battery has a test (A49, A51, A52). m2a, an independent runner, ran 60 one-line mutants of this slice's code at
e2037b9: 38 were killed and 22 survived. Each survivor now has a case intests/test_a23_write_handoff.pythat passes on the code and fails on the mutant. No code changed. What the cases found:LANES_REPOraised to its checkout's top level) is reached only whereLANES_FILEorLANES_DIRnames the register too.LANES_REPOalone at a subdirectory is refused earlier (1, "registry not found")..git/configis not covered by A47, which asks only of a path outside this checkout. It is answered by thehandoffs/rule alone, so it has its own case.handoffs/to the lane's handoff kills it: A48 follows the link's own path, which origin still has, so the mutant replayed the write and the re-run said 8, "Nothing was written". The case of that cell stays.2a41e83with 35 one-line mutants: 25 were killed and 10 survived. Seven of the 10 (G05, G06, H03, J01, J02, J03, K03) are A51's. The other three are in A47's and A48's own lines (A52):--stampwould have written): the case of a target inside a git directory now asserts that a--stampsays its line and a--wholesays none.No routing
execof this slice's own (A50, shellcheck SC2093). shellcheck 0.9.0 withlint.yml's flags finds three SC2093 inlanes-edit.shhere, and nothing else. All three are slice 1b's (10169f3,ef1377f) or 1a's (d0010a2), and they come clean here with 1b's next merge.What changed
lanes-edit.sh:write-handoffandhandoff-path;handoff_publish_atomic,handoff_bound_is,handoff_restart_reserve,path_physical,handoff_in_repo,handoff_checkout_of,handoff_cell_of,whf_stamp_said,whf_in_scope,whf_keep_if_gone;publish-handoffnow runs onhandoff_publish_atomic;ws_relativenow rests onpath_physical, with the same answers;handoff_in_checkoutnow callshandoff_in_repo;write_lane_ofrouteswrite-handoff, andlane_row_gategates it;commit_publishgainsCP_TRAILER(checked) and the S4.4 whole-file check, which reads HEAD and hands a rewritten commit or an unreadable HEAD to slice 1b'scp_not_head;write-handoffsets slice 1b'sCP_KEEP_IF_GONEto its handoff for--stampand--prepend, and records the cell it followed;cp_replayaskswhf_keep_if_gonebefore it keeps such a write (one clause, decision 8);write-handoffrefuses a target inside a git directory (A47), andwhf_keep_if_gonefollows another cell only to a file underhandoffs/that origin has (A48);cleanupprintsHANDOFF_OUT, andcleanup_for_execclears it;set-restart-intent --handofftranslates the row's spellings;docs/README-lanes.md: exit row 6's two clauses, the S4.4 cause and A37's (decision 18).tests/test_a23_write_handoff.py(new), on slice 1b's box, imported fromtests/test_a23_publish_replay.pyand not copied, with a case for each survivor of m2a's mutant battery (decision 20).Tests
One pytest process per file, with every
LANES_*andTMUX*variable unset, a refusingghstub, and nothing reaching a real remote.tests/test_a23_write_handoff.py1bb4f75(this head)tests/test_repo_hygiene.py1bb4f75(this head)tests/test_test_roots.py1bb4f75(this head)tests/test_a23_publish_replay.py68014b4(the samelanes-edit.shas this head)tests/test_a23_guard_refusals.py68014b4(the samelanes-edit.shas this head)tests/test_a23_write_routing.py2a41e83tests/test_a23_lock.py2a41e83tests/test_a23_refresh_workspace.py2a41e83tests/test_a23_lane_checkout.py2a41e83tests/test_a23_lane_start_read.py2a41e83tests/test_commit_push_published_edit_194.py2a41e83tests/test_supervised_restart_regressions.py -k handoffd5cabdc(stage 3)tests/test_guard_launch_mode.py357ec07(stage 2)tests/test_skill_shell_blocks.py357ec07tests/test_handoff_skill_80_85.py357ec07tests/test_handoff_skill_commit_block.py357ec07tests/test_lane_start_row_stamp.py357ec07tests/test_lane_start_claude_current.py357ec07tests/test_triad_lane_tooling.py357ec07tests/test_triad_lane_tooling_handoff.py357ec07The two triad failures are the t018 goldens, red at this branch's base with the same assertion text. The shell suite and the whole of
tests/test_supervised_restart_regressions.pywere not run here; CI'stestsjob is the suite of record.Mutants
6ee2c11)-kselection346e403with the new pins--targetkeep dropped, its no-row keep dropped,--target's cell recorded, its comparison inverted, and origin's row read through the cachedregister_text_checked-kselection, ate2037b9, in a separate clonehandoff.mdin another repository-kselection, at68014b4, in a separate clonee2037b9, in m2a's own clone2a41e83: in this writer's clone, each against its new cases'-kselection, and again by m2a-c3 in m2a's clone, each against A49's cases alone.git/configstayse2037b9, in m2a's clone37b000d(A51's head), in this writer's clone2a41e83, in m2a's clone-kselection, at1bb4f75, in this writer's clone; m2a-c4's probes of the same three, outside the tree, killed them tooThe 31 stage-2 mutants scripted before the pre-review were not run as a battery; m2a's 60, the pre-review's 18 and the pins cover the same lines.
Questions for lane openRepoTools-3
rename-lane's stamp keeps its own inlined publication (itsrl_htmpcopy of Publish rename-lane's Rule 3 handoff stamp through a same-directory temporary file and one atomic rename, so a writer killed mid-stamp leaves the handoff whole #212's pattern), rather than callinghandoff_publish_atomic. It already holds the checkout lock, and its refusals are exit 5 under Harden legacy lane rename handoff publication against process crashes #158's tests. Should a later slice move it onto the shared act?set-restart-intent --handoffkeeps an absolute path as given (decision 10), where the plan said it "stores the translated absolute path". Translating an absolute W path would stop every/ctxunder the opt-in until slice 2b moveslane-startandlane-handoff. Only the row's relative and~/…spellings are translated here. Is that the reading you want, or should absolute W paths be translated once 2b lands?--target, or a row cell, under<W>-lanes/<another lane>/names a file of a clone of this same register, not of this checkout.--wholereplaced the sibling clone'slanes/LANES.mdin place, uncommitted, with exit 0, and that lane's next write then refused at S3 step 4 over a pending change it never made.--prependdid the same to an append-only object log.handoffs/rule now holds there too, relative to that clone's top level, so its register, its object logs and anything underlanes/are refused (2). A path inside another clone's.git/, or in a bare clone, is refused too (2, A47).handoffs/in that clone is still written as another repository's handoff, committed nowhere, with exit 0, and the sibling checkout's next commit of that path would carry this write.<W>-lanes/that is not this checkout's be refused (2, nothing written)? One more check would do it.Not changed
lane-start,lane-handoff,lane-endand the handoff skill are as they were. Slice 2b makes the calls towrite-handoffandhandoff-path.LANES_ROUTE_WRITESstays opt-in, and its default is not flipped.commit_publishand left alone (a question for its writer): with an explicitLANES_REPOthat names a directory inside the checkout, andLANES_FILEnaming the register, git takes the write's top-level pathspec as relative to that directory, sogit addfails (6, "git add failed") after the edit is made. Every writer'sgit adddoes the same there; it is not this slice's code.docs/README-lanes.mdgains two clauses in exit row 6 and nothing else (decision 18). No test of this slice reads adocs/file. The manual's rows for the two verbs are slice 5's, and after the cutover these clauses and those rows are an openRepoTools-spec change.Refs #198
🤖 Generated with Claude Code