From 35c04ff2d9cc29da1ce1e03d960110e422709e16 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Tue, 20 Oct 2026 21:25:17 +0000 Subject: [PATCH 01/34] `publish-handoff`'s act becomes `handoff_publish_atomic`, one implementation 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 [ ]` 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 (cherry picked from commit 9f4f4f7db51bd260d0ebed6dfc87b91eab75923a) --- lanes-edit.sh | 113 +++++++++++++++------ tests/test_a23_write_handoff.py | 168 ++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+), 31 deletions(-) create mode 100644 tests/test_a23_write_handoff.py diff --git a/lanes-edit.sh b/lanes-edit.sh index 3655750..7410d04 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -12671,6 +12671,81 @@ lane_restart_put() { # +# [ ]` +# +# `publish-handoff`'s act, FACTORED OUT WHOLE, so the two acts that replace a +# lane's handoff — `publish-handoff`, which commits nothing, and +# `write-handoff`, which publishes under S3 and S4 — fence it and write its +# bytes with one implementation. In order, and every refusal is a `die` that +# says nothing was published: +# 1. the target's links are followed to the physical file (40 at most), its +# directory resolved with `cd -P`, and the file must be readable (1); +# 2. the lane's restart intent is read (1 where it cannot be, 2 where it is +# malformed). With an operation, the five fences must be the intent's own +# — operation, generation, attempt, state, transcript — or this writer +# lost ownership (7), and the intent's bound handoff must be this file +# (2). With none, an active restart owns the file (7); +# 3. the file's digest must still be the prior digest its caller read (7); +# 4. the bytes. THEY GO #212's WAY, which is this act's own (`rename-lane`'s +# stamp says it copied it from here): an exclusive temporary BESIDE the +# target, in its physical directory, so the one `mv -f` is a rename and +# never a copy across a mount; `cp -p` first, so the handoff keeps its +# mode; then the bytes; then the rename. A reader sees the handoff whole +# before or whole after, a full disk leaves it as it was, and a crash +# leaves `.resume.XXXXXX`, which no reader of `*.md` takes for a +# handoff. `pbh_tmp` is the name `cleanup` removes on every handled exit, +# and, #212's one refinement, it is CLEARED once the rename has landed: +# the name is no longer this process's to remove. +# +# RUN UNDER A LOCK THE CALLER HOLDS — the workstation's sidecar lock, or the +# checkout's where the opt-in is off (`sidecar_lock_dir`) — which the exit trap +# releases on every refusal. It sets HPA_TARGET, the file it published. +HPA_TARGET="" +handoff_publish_atomic() { # [ ] + hpa_lane="$1"; hpa_target="$2"; hpa_source="$3"; hpa_digest="$4" + hpa_op="${5-}"; hpa_gen="${6-}"; hpa_attempt="${7-}"; hpa_state="${8-}"; hpa_sid="${9-}" + HPA_TARGET="" + hpa_links=0 + while [ -L "$hpa_target" ]; do + hpa_links=$((hpa_links+1)); [ "$hpa_links" -lt 40 ] || die "the handoff link chain cannot be resolved" 2 + hpa_link="$(readlink "$hpa_target")" || die "the handoff link could not be read" 1 + case "$hpa_link" in /*) hpa_target="$hpa_link" ;; *) hpa_target="$(dirname -- "$hpa_target")/$hpa_link" ;; esac + done + hpa_parent="$( CDPATH=''; cd -P -- "$(dirname -- "$hpa_target")" && pwd -P )" || die "the handoff parent is missing" 1 + hpa_target="$hpa_parent/$(basename -- "$hpa_target")" + [ -f "$hpa_target" ] && [ -r "$hpa_target" ] || die "the handoff target is unreadable" 1 + hpa_irc=0; hpa_intent="$(lane_restart_read "$hpa_lane")" || hpa_irc=$? + case "$hpa_irc" in 0|8) : ;; *) die "restart ownership could not be read; no handoff was published" 1 ;; esac + hpa_current="$(printf '%s\n' "$hpa_intent" | awk -F'\t' '$1=="state" {print $2; exit}')" + case "$hpa_current" in UNKNOWN-*|INCOMPLETE) die "restart ownership is malformed; no handoff was published" 2 ;; esac + if [ -n "$hpa_op" ]; then + [ -n "$hpa_gen" ] && [ -n "$hpa_attempt" ] && [ -n "$hpa_state" ] && [ -n "$hpa_sid" ] || + die "operation publication needs generation, attempt, state and transcript fences" 64 + hpa_actual="$(printf '%s\n' "$hpa_intent" | awk -F'\t' ' + $1=="operation" {o=$2} $1=="generation" {g=$2} $1=="attempt" {a=$2} $1=="state" {s=$2} $1=="new_transcript" {t=$2} + END {printf "%s|%s|%s|%s|%s",o,g,a,s,t}')" + [ "$hpa_actual" = "$hpa_op|$hpa_gen|$hpa_attempt|$hpa_state|$hpa_sid" ] || + die "this handoff publisher lost restart ownership; nothing was published" 7 + hpa_bound="$(printf '%s\n' "$hpa_intent" | awk -F'\t' '$1=="handoff" {print $2; exit}')" + [ -n "$hpa_bound" ] && [ "$hpa_bound" != none ] && [ "$hpa_bound" -ef "$hpa_target" ] || + die "the publication target is not the operation-bound handoff; nothing was published" 2 + else + case "$hpa_current" in preparing|pending|starting) die "an active restart owns this handoff; nothing was published" 7 ;; esac + fi + [ "$(lane_handoff_digest "$hpa_target")" = "$hpa_digest" ] || die "the handoff changed before publication; nothing was published" 7 + pbh_tmp="$(mktemp "$hpa_target.resume.XXXXXX")" || { pbh_tmp=""; die "the publication temporary file could not be made" 1; } + if ! cp -p -- "$hpa_target" "$pbh_tmp" || ! cat -- "$hpa_source" > "$pbh_tmp" || ! mv -f -- "$pbh_tmp" "$hpa_target"; then + rm -f -- "$pbh_tmp"; pbh_tmp="" + die "the handoff could not be published atomically" 1 + fi + pbh_tmp="" + HPA_TARGET="$hpa_target" + return 0 +} + # ------------------- AMENDMENT 19(c): THE SWEEP, ONE COMMIT OR NONE --------- # # `lane-end --retire-dormant ` is the word a person types, the dry run @@ -18720,38 +18795,14 @@ EOF [ -f "$pbh_source" ] && [ -r "$pbh_source" ] && [ -s "$pbh_source" ] || die "the prepared handoff is unreadable or empty" 1 sidecar_lock_use; acquire_lock # the workstation's sidecar lock, and no other (Amendment 23 S2) managed_seam_refuse "$lane" "publish-handoff" - pbh_links=0 - while [ -L "$pbh_target" ]; do - pbh_links=$((pbh_links+1)); [ "$pbh_links" -lt 40 ] || die "the handoff link chain cannot be resolved" 2 - pbh_link="$(readlink "$pbh_target")" || die "the handoff link could not be read" 1 - case "$pbh_link" in /*) pbh_target="$pbh_link" ;; *) pbh_target="$(dirname -- "$pbh_target")/$pbh_link" ;; esac - done - pbh_parent="$( CDPATH=''; cd -P -- "$(dirname -- "$pbh_target")" && pwd -P )" || die "the handoff parent is missing" 1 - pbh_target="$pbh_parent/$(basename -- "$pbh_target")" - [ -f "$pbh_target" ] && [ -r "$pbh_target" ] || die "the handoff target is unreadable" 1 - pbh_irc=0; pbh_intent="$(lane_restart_read "$lane")" || pbh_irc=$? - case "$pbh_irc" in 0|8) : ;; *) release_lock; die "restart ownership could not be read; no handoff was published" 1 ;; esac - pbh_current="$(printf '%s\n' "$pbh_intent" | awk -F'\t' '$1=="state" {print $2; exit}')" - case "$pbh_current" in UNKNOWN-*|INCOMPLETE) release_lock; die "restart ownership is malformed; no handoff was published" 2 ;; esac - if [ -n "$pbh_op" ]; then - [ -n "$pbh_gen" ] && [ -n "$pbh_attempt" ] && [ -n "$pbh_state" ] && [ -n "$pbh_sid" ] || { release_lock; die "operation publication needs generation, attempt, state and transcript fences" 64; } - pbh_actual="$(printf '%s\n' "$pbh_intent" | awk -F'\t' ' - $1=="operation" {o=$2} $1=="generation" {g=$2} $1=="attempt" {a=$2} $1=="state" {s=$2} $1=="new_transcript" {t=$2} - END {printf "%s|%s|%s|%s|%s",o,g,a,s,t}')" - [ "$pbh_actual" = "$pbh_op|$pbh_gen|$pbh_attempt|$pbh_state|$pbh_sid" ] || { release_lock; die "this handoff publisher lost restart ownership; nothing was published" 7; } - pbh_bound="$(printf '%s\n' "$pbh_intent" | awk -F'\t' '$1=="handoff" {print $2; exit}')" - [ -n "$pbh_bound" ] && [ "$pbh_bound" != none ] && [ "$pbh_bound" -ef "$pbh_target" ] || - { release_lock; die "the publication target is not the operation-bound handoff; nothing was published" 2; } - else - case "$pbh_current" in preparing|pending|starting) release_lock; die "an active restart owns this handoff; nothing was published" 7 ;; esac - fi - [ "$(lane_handoff_digest "$pbh_target")" = "$pbh_digest" ] || { release_lock; die "the handoff changed before publication; nothing was published" 7; } - pbh_tmp="$(mktemp "$pbh_target.resume.XXXXXX")" || { release_lock; die "the publication temporary file could not be made" 1; } - if ! cp -p -- "$pbh_target" "$pbh_tmp" || ! cat -- "$pbh_source" > "$pbh_tmp" || ! mv -f -- "$pbh_tmp" "$pbh_target"; then - rm -f -- "$pbh_tmp"; release_lock; die "the handoff could not be published atomically" 1 - fi + # THE ACT ITSELF IS `handoff_publish_atomic`'s (Amendment 23 slice 2a), which + # `write-handoff` shares: the same fences, the same words and codes, the same + # bytes. Its contract here is the one every old caller has: the physical + # file on stdout, nothing committed, and the bound path compared as it is. + handoff_publish_atomic "$lane" "$pbh_target" "$pbh_source" "$pbh_digest" \ + "$pbh_op" "$pbh_gen" "$pbh_attempt" "$pbh_state" "$pbh_sid" release_lock - printf '%s\n' "$pbh_target" + printf '%s\n' "$HPA_TARGET" ;; legacy-restart-check) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py new file mode 100644 index 0000000..887a9fb --- /dev/null +++ b/tests/test_a23_write_handoff.py @@ -0,0 +1,168 @@ +# SPDX-License-Identifier: Apache-2.0 +"""Amendment 23, act 2, slice 2a: the handoff verbs. + +`publish-handoff` keeps its contract for every old caller, and its act — the +link followed to the physical file, the restart fences, the prior digest, and +the bytes replaced by one rename beside the file — is `handoff_publish_atomic`, +which `write-handoff` shares. + +The sandbox is the real `lanes-edit.sh`; nothing reaches a real remote. The box +is slice 1b's (`tests/test_a23_publish_replay.py`: two rows, a peer clone, and +a push-time `race` hook), imported rather than copied, with the lane's handoff +published on origin and a restart state root of its own. +""" + +from __future__ import annotations + +import hashlib +import os +import shutil +import stat +from pathlib import Path + +import pytest + +from conftest import WINDOWS_SKIP +from test_a23_publish_replay import LANE, Box as ReplayBox + +pytestmark = [pytest.mark.skipif(shutil.which("bash") is None, + reason="lanes-edit.sh is a bash script"), + pytest.mark.skipif(shutil.which("git") is None, + reason="the register is a git repository"), + WINDOWS_SKIP] + +#: The handoff cell `_row` gives the lane, and the file origin carries there: +#: a header line, the blank that ends the header block, then the block. +HANDOFF_REL = f"handoffs/repoS4/{LANE}.md" +HANDOFF = (f"Lane: {LANE} (profile p, session s) — single-use resume prompt\n" + "\n" + "## RESUME PROMPT — the block the next session reads\n" + "\n" + "Preserved task.\n") + + +def sha(path: Path) -> str: + return hashlib.sha256(path.read_bytes()).hexdigest() + + +class Box(ReplayBox): + """Slice 1b's box, with the lane's handoff on origin and its restart + intents under a state root of the box's own.""" + + def __init__(self, root: Path, **kw) -> None: + root = Path(os.path.realpath(root)) + state = root / "state" + env = dict(kw.pop("env", None) or {}, LANES_LANE_STATE_ROOT=str(state)) + super().__init__(root, env=env, **kw) + self.state = state + self.peer_pushes(HANDOFF_REL, HANDOFF) + + def intent(self, state: str, *extra: str) -> None: + made = self.helper("set-restart-intent", LANE, state, *extra) + assert made.returncode == 0, (made.stdout, made.stderr) + + def intent_field(self, key: str) -> str: + text = (self.state / LANE / "restart-intent.yaml").read_text(encoding="utf-8") + return next(line.split(": ", 1)[1] for line in text.splitlines() + if line.startswith(f"{key}: ")) + + +@pytest.fixture +def box(tmp_path: Path) -> Box: + return Box(tmp_path) + + +def _no_temporary_beside(path: Path) -> None: + assert [p.name for p in path.parent.iterdir() if ".resume." in p.name] == [] + + +# ------------------------------------------------- publish-handoff's contract + +def test_publish_handoff_prints_the_physical_file_and_keeps_its_mode(box: Box): + """Through a link, the physical file is replaced by one rename beside it: + it is the one line on stdout, it keeps its mode, the link stays a link, + nothing is committed, and no temporary is left beside it.""" + c = box.seam() + real = c / HANDOFF_REL + real.chmod(0o640) + link = box.root / "estate" / "handoff.md" + link.parent.mkdir() + link.symlink_to(real) + prepared = box.root / "prepared.md" + prepared.write_text("# published whole\n", encoding="utf-8") + head, tip = box.head(c), box.origin_head() + result = box.helper("publish-handoff", LANE, str(link), str(prepared), + "--expect-digest", sha(real)) + assert result.returncode == 0, result.stderr + assert result.stdout == f"{real}\n" + assert real.read_text(encoding="utf-8") == "# published whole\n" + assert stat.S_IMODE(real.stat().st_mode) == 0o640 + assert link.is_symlink() + _no_temporary_beside(real) + assert box.head(c) == head and box.origin_head() == tip, "publish-handoff commits nothing" + + +def test_publish_handoff_refuses_a_handoff_that_changed_exit_7(box: Box): + c = box.seam() + real = c / HANDOFF_REL + prepared = box.root / "prepared.md" + prepared.write_text("# published whole\n", encoding="utf-8") + result = box.helper("publish-handoff", LANE, str(real), str(prepared), + "--expect-digest", "0" * 64) + assert result.returncode == 7, result.stderr + assert "the handoff changed before publication; nothing was published" in result.stderr + assert real.read_text(encoding="utf-8") == HANDOFF + assert result.stdout == "" + _no_temporary_beside(real) + + +FENCES = { + # name: (the intent's state, the publish's fence flags, exit, words) + "owned": ("preparing", ("--operation", "op-a23", "--generation", "3", "--attempt", "0", + "--state", "preparing", "--transcript", "none"), 0, ""), + "lost": ("preparing", ("--operation", "op-a23", "--generation", "4", "--attempt", "0", + "--state", "preparing", "--transcript", "none"), 7, + "this handoff publisher lost restart ownership; nothing was published"), + "active": ("pending", (), 7, "an active restart owns this handoff; nothing was published"), + "half": ("preparing", ("--operation", "op-a23"), 64, + "operation publication needs generation, attempt, state and transcript fences"), +} + + +@pytest.mark.parametrize("case", FENCES) +def test_publish_handoff_restart_fences(box: Box, case: str): + """The five fences are the intent's own or nothing is published: a lost + operation is 7, an active restart with no fences is 7, and an operation + without its other four fences is 64.""" + c = box.seam() + real = c / HANDOFF_REL + state, flags, code, words = FENCES[case] + box.intent(state, "--expect", "none", "--operation", "op-a23", "--generation", "3", + "--handoff", str(real)) + prepared = box.root / "prepared.md" + prepared.write_text("# fenced\n", encoding="utf-8") + result = box.helper("publish-handoff", LANE, str(real), str(prepared), + "--expect-digest", sha(real), *flags) + assert result.returncode == code, result.stderr + assert words in result.stderr + assert real.read_text(encoding="utf-8") == ("# fenced\n" if code == 0 else HANDOFF) + _no_temporary_beside(real) + + +def test_publish_handoff_refuses_a_file_the_restart_did_not_bind(box: Box): + """Under an operation, the file published must be the one the intent + binds — compared as the same file, never by spelling (2).""" + c = box.seam() + real = c / HANDOFF_REL + other = c / "handoffs" / "README.md" + box.intent("preparing", "--expect", "none", "--operation", "op-a23", "--generation", "3", + "--handoff", str(other)) + prepared = box.root / "prepared.md" + prepared.write_text("# fenced\n", encoding="utf-8") + result = box.helper("publish-handoff", LANE, str(real), str(prepared), + "--expect-digest", sha(real), "--operation", "op-a23", + "--generation", "3", "--attempt", "0", "--state", "preparing", + "--transcript", "none") + assert result.returncode == 2, result.stderr + assert "the publication target is not the operation-bound handoff" in result.stderr + assert real.read_text(encoding="utf-8") == HANDOFF From 32f2f6a1f167298798bce3acc81ca9d0d773f676 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 12:45:35 +0000 Subject: [PATCH 02/34] `write-handoff` writes a lane's handoff in the checkout its writes go 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) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `write-handoff (--stamp "" | --prepend | --whole --expect-digest ) --subject "" [--target ] [--restart [--expected-digest ]]` 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: ` 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, ``, 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 [--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 (cherry picked from commit 357ec0765cd459e923c96ee4a46c86555499726e) --- lanes-edit.sh | 520 +++++++++++++++++++++++--- tests/test_a23_write_handoff.py | 626 +++++++++++++++++++++++++++++++- 2 files changed, 1102 insertions(+), 44 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 7410d04..146ba53 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -64,6 +64,13 @@ # lanes-edit.sh migrate-state-cells [--yes] # Amendment 13(e), once # lanes-edit.sh retire-rows … [--reason ""] [--writer ] # lanes-edit.sh archive-rows [--yes] # Amendment 19(d) +# lanes-edit.sh write-handoff (--stamp "" | --prepend +# | --whole --expect-digest ) --subject "" +# [--target ] [--restart +# [--expected-digest ]] +# (Amendment 23: the lane's handoff written in its own checkout, +# under S3-S4; `` on stdout) +# lanes-edit.sh handoff-path [--create] # where write-handoff writes it # # AMENDMENT 19 — A CLOSED OR DORMANT LANE LEAVES THE LISTING # lanes-edit.sh lanes [--closed] # CLOSED and DORMANT rows too @@ -471,7 +478,12 @@ # branch, a pending change to a path the write commits (the register's, # for every writer but `commit`), or a fast-forward git refused; the # withdrawn `--sweep`; and the publish act over a stack origin has moved -# past (S4, Open question 2). EIGHT MEANINGS ON ONE NUMBER, and they +# past (S4, Open question 2); and — slice 2a — `write-handoff`'s handoff +# that resolves into W through a link (S5), a restart's bound file or +# pre-image that is not this one, or a splice that does not prove out, +# each with nothing written; while S3's refusal of a `--prepend` or a +# `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed +# its path. EIGHT MEANINGS ON ONE NUMBER, and they # stay readable only because of where each can occur: bad arguments to a # subcommand that predates Amendment 11; `register-row`'s "not lane-shaped"; # AN UNKNOWN SUBCOMMAND (the `*)` arm below), which is how a caller detects @@ -527,9 +539,10 @@ # 6 git add or commit failed — or, AMENDMENT 23 S4, the commit is KEPT ON # HEAD AND NAMED (sha, subject, `git show`): origin moved under six # attempts (the bound, ratified choices 4 and 5), a replay's -# `reset --keep` refused over an uncommitted change, or a push (a +# `reset --keep` refused over an uncommitted change, a push (a # write's, or the publish act's) refused with origin unmoved, which -# no replay can mend. Every later write from that +# no replay can mend, or a whole-file `write-handoff` whose copy origin +# changed meanwhile, which no replay may overwrite (S4.4). Every later write from that # checkout refuses under S3 until the lane publishes or archives it # 7 ANOTHER ACT GOT THERE FIRST, and this one wrote nothing. `claim`: # CLAIM-LOST, another lane's claim landed on main first. @@ -539,14 +552,18 @@ # `set-lane-tree`: the `--generation`/`--operation` this observation was # taken under is no longer the lane's — or the lane is `SWAPPED` and the # operation it names is finished (#114) — so a superseded poll cannot be -# filed over the current inventory. ONE MEANING ON THE NUMBER, in three -# places: you lost the race. +# filed over the current inventory. `publish-handoff` and `write-handoff`: +# the restart fence did not match, an active restart owns the handoff, or +# the handoff changed under the write, and nothing was published. ONE +# MEANING ON THE NUMBER, in five places: you lost the race. # 8 no record — `who` found nothing; `lane-objects` has no log file for the # lane; `live-holder` READ this workstation's session records and none of # them holds it; `swapped` found no lane swapped on the workstation; # `history` READ the lane's log and it carries no `NOTED` or `RULED` line; # `window-session` found no live session in the window; `lane-checkout` -# found no checkout for the lane (Amendment 23). A read that could +# found no checkout for the lane (Amendment 23); `handoff-path` and +# `write-handoff` found no handoff for the lane — its row names none, or +# (`write-handoff`) the file is not there — and wrote nothing. A read that could # not be performed is never 8 (R22). `session-start` never exits 8, or # anything but 0: it is a hook. `guard` never exits 8 either — it is a hook # too, and a BLOCKING one, so its two codes are 0 and 2 (see 2 above). @@ -658,8 +675,9 @@ # WORKSTATION's, taken by the lifecycle, restart and tree # sidecars, by `publish-handoff` and by `lane_state_follow`, # each ALONE: the follow-up runs after its write released -# the checkout lock, and no act holds both. No lock is taken -# in W then. Without the opt-in those acts take the +# the checkout lock. `write-handoff` alone holds both, the +# checkout's first and this one inside it, for its restart +# fence and replace only. No lock is taken in W then. Without the opt-in those acts take the # checkout's lock, as before. Each lock holds a `pid` file, # ` `: a live holder in this # container is never aged out; one in another container is @@ -1257,6 +1275,14 @@ CP_REPLAY_ENV=() # tree, which no argv can make again, so a replay gives it back as the hand # edit it was instead of dropping it with the commit (`cp_replay`). CP_REPLAY_KEEPS_EDIT=0 +# Set by `write-handoff` alone (slice 2a), never from the environment: +# CP_TRAILER the commit's second paragraph, `Lane: ` (Rule 5's +# trailer, as `lane-start` and `lane-handoff` commit a handoff); +# CP_WHOLE_REL a WHOLE-FILE write's path, and CP_WHOLE_BLOB the blob it +# CP_WHOLE_BLOB started from (empty for a file HEAD did not have): a refused +# push replays only where origin's copy is still that one +# (Amendment 23 S4.4), checked before any reset. +CP_TRAILER=""; CP_WHOLE_REL=""; CP_WHOLE_BLOB="" # die "" [] — the MESSAGE is $1 alone. It used to be "$*", # which joined the exit code on to the end of every refusal that passed one: @@ -1389,10 +1415,13 @@ release_lock() { # snapshot, the restart intent, the tree records and `publish-handoff`'s fence # were serialised by W's mutex; a routed write takes no lock in W, and a lane's # checkout lock serialises one lane only, so they share this one beside the -# lane checkouts, on the mount every container sees. EACH TAKER TAKES IT ALONE -# (the review of #282, NIT 2): a sidecar verb, and `lane_state_follow`, which -# runs after its write released the checkout lock. No act holds both, so no -# order between them can deadlock. +# lane checkouts, on the mount every container sees. EVERY SIDECAR ACT TAKES IT +# ALONE (the review of #282, NIT 2): a sidecar verb, and `lane_state_follow`, +# which runs after its write released the checkout lock. ONE ACT NESTS IT: +# `write-handoff` (slice 2a) takes it INSIDE its checkout lock, for its restart +# fence and the replace alone, and releases it before it pushes. The order is +# always checkout lock, then sidecar lock, and no act takes them the other way +# round, so no order between them can deadlock (ort186 Q3, condition 1). # # WITHOUT THE OPT-IN it is the checkout's own lock, `$LOCK`, as it was before # Amendment 23: the sidecar verbs keep their `acquire_lock`, and the follow-up @@ -1517,6 +1546,13 @@ rename_undo() { # remove is set above, before the trap: `RL_*`, `LANES_IDX_DIR`, # `LANES_WT_IDX_DIR`, `LOCK` and `LOCK_HELD`. TMPD=""; pbh_tmp=""; rl_htmp=""; SE_CACHE_FILE="" +# `write-handoff`'s ONE LINE ON STDOUT, ``, printed by +# `cleanup` once this process has written the handoff — committed or not, so a +# refusal after the bytes (S3's for `--prepend` and `--whole`, a push not +# confirmed, the bound) still says what the file now is — and never by a run +# that `exec`s into its replay, which runs no EXIT trap: a replayed write +# prints one line, its last attempt's. +HANDOFF_OUT="" # The creation lock's heartbeat (`lane_checkout_ensure`), stopped by pid. create_heartbeat_stop() { [ -n "${CREATE_HB:-}" ] || return 0 @@ -1526,11 +1562,12 @@ create_heartbeat_stop() { return 0 } cleanup() { + if [ -n "${HANDOFF_OUT:-}" ]; then printf '%s\n' "$HANDOFF_OUT"; HANDOFF_OUT=""; fi [ -z "${pbh_tmp:-}" ] || rm -f -- "$pbh_tmp" [ -z "${rl_htmp:-}" ] || rm -f -- "$rl_htmp" # rename-lane's stamp before its replace (#158) rename_undo - sidecar_release # Amendment 23: never held with the - create_heartbeat_stop # checkout lock, so the order is free + sidecar_release # Amendment 23: released before the + create_heartbeat_stop # checkout lock, the reverse of its order [ -z "$CREATE_LOCK" ] || mutex_release "$CREATE_LOCK" if [ "$LOCK_HELD" = 1 ]; then release_lock; fi [ -n "${RL_SNAP:-}" ] && [ -d "${RL_SNAP:-}" ] && rm -rf -- "$RL_SNAP" @@ -1560,7 +1597,7 @@ cleanup() { cleanup_for_exec() { [ -z "${pbh_tmp:-}" ] || rm -f -- "$pbh_tmp" [ -z "${rl_htmp:-}" ] || rm -f -- "$rl_htmp" - pbh_tmp=""; rl_htmp=""; RL_ACTIVE=0 + pbh_tmp=""; rl_htmp=""; RL_ACTIVE=0; HANDOFF_OUT="" sidecar_release create_heartbeat_stop [ -z "$CREATE_LOCK" ] || mutex_release "$CREATE_LOCK" @@ -2696,6 +2733,20 @@ commit_publish() { msg="$1"; shift || : if [ "$#" -gt 0 ]; then CP_PATHS=("$@"); else CP_PATHS=("$LANES_PATH"); fi [ "$NO_GIT" = 1 ] && { note "LANES_NO_GIT=1 — not committing"; return 0; } + # RULE 5'S TRAILER, `write-handoff`'s alone (slice 2a, on w1b's conditions): + # a second paragraph of ONE `Key: value` line, or nothing is staged or + # committed (2). The arguments are one array that always has the subject, so + # it expands under `set -u` in bash 3.2. The publish act commits nothing and + # never reads it, and no verb sets it with CP_PUBLISH_AHEAD. + cp_m=(-m "$msg") + if [ -n "$CP_TRAILER" ]; then + cp_tk="${CP_TRAILER%%: *}" + case "$cp_tk" in ''|*[!A-Za-z0-9-]*) cp_tk="" ;; esac + if [ -z "$cp_tk" ] || [ "$cp_tk" = "$CP_TRAILER" ] || [ -z "${CP_TRAILER#*: }" ] || ! lane_restart_value_ok "$CP_TRAILER"; then + die "this write's commit trailer is not one \`Key: value\` line with no control character, so nothing was staged or committed: $(printf '%q' "$CP_TRAILER")" 2 + fi + cp_m+=(-m "$CP_TRAILER") + fi # THE OLD PATH IS A PATHSPEC FOR THE DIFFS AND NEVER FOR THE `add`: after the # index entry is force-removed it matches neither the index nor a directory # read that compares exactly, and `git add` FAILS on a pathspec that matches @@ -2746,13 +2797,13 @@ EOF if [ -n "$cp_extra" ]; then die "this commit carries a case-only rename of the lane's log, so it is made from the INDEX rather than from its pathspecs — and the index also holds$cp_extra, which is not this write's to commit. Stage-reset it — \`git -C $LANES_REPO restore --staged --\`$cp_extra — and re-run. Nothing was committed and the log is already renamed on disk." 6 fi - git -C "$LANES_REPO" -c "$CP_EXACT" commit -q -m "$msg" || die "git commit failed" 6 + git -C "$LANES_REPO" -c "$CP_EXACT" commit -q "${cp_m[@]}" || die "git commit failed" 6 # THE RENAME IS DONE AND IN HEAD: the old path is no longer a pathspec of # anything below, and the next `commit_publish` of this run is not a rename. CP_PATHS=(${cp_add[@]+"${cp_add[@]}"}) LOG_RENAMED_FROM="" else - git -C "$LANES_REPO" -c "$CP_EXACT" commit -q -m "$msg" -- "${CP_PATHS[@]}" || die "git commit failed" 6 + git -C "$LANES_REPO" -c "$CP_EXACT" commit -q "${cp_m[@]}" -- "${CP_PATHS[@]}" || die "git commit failed" 6 fi cp_sha="$(git -C "$LANES_REPO" rev-parse HEAD 2>/dev/null)" note "committed: $msg" @@ -2778,6 +2829,21 @@ EOF 0) cp_pushed "$cp_sha"; return 0 ;; 2) cp_not_confirmed "$cp_sha" "$msg" "the push was refused, and $(cp_why_unconfirmed 2)" ;; esac + # S4.4 — A WHOLE-FILE WRITE REPLAYS ONLY OVER THE COPY IT STARTED FROM, and + # that is asked here, with origin just fetched, BEFORE the origin-unmoved + # refusal below and BEFORE the reset (w1b's placement): a replay + # would write the whole file again over whatever a peer put there. So where + # origin's copy has moved, the handoff stays written, its commit is kept on + # HEAD and named, as at the bound, and nobody's change is undone (R-A11-11). + if [ -n "$CP_WHOLE_REL" ]; then + cpw_now="$(git -C "$LANES_REPO" rev-parse -q --verify "refs/remotes/origin/$LANES_BRANCH:$CP_WHOLE_REL" 2>/dev/null || :)" + if [ "$cpw_now" != "$CP_WHOLE_BLOB" ]; then + note "origin/$LANES_BRANCH's $CP_WHOLE_REL is ${cpw_now:-gone} now, and this whole-file write started from ${CP_WHOLE_BLOB:-no such file}." + note "look at it: git -C $LANES_REPO show $cp_sha; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" + note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." + die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); the commit is local and kept on HEAD: $cp_sha $msg" 6 + fi + fi # A REFUSAL WITH ORIGIN UNMOVED IS NOT A LOST RACE: the replay would send the # same commit onto the same tip and meet the same refusal six times (a hook, # a permission, a protected branch). git's own words are above; the commit @@ -12703,7 +12769,53 @@ lane_restart_put() { # + [ -n "$1" ] && [ "$1" != none ] || return 1 + [ "$1" -ef "$2" ] && return 0 + [ -n "$HPA_CHECKOUT" ] || return 1 + hbi_t="$(handoff_in_repo "$HPA_CHECKOUT" "$1")" || return 1 + [ "$hbi_t" -ef "$2" ] +} +# THE STAMP'S RESERVATION, `lane-start`'s (`:4436-4449`), MADE IN-PROCESS: the +# intent records the stamped file's digest as its PREPARED digest before the +# stamp's bytes are published, so an interruption on either side of the rename +# stays retryable — a retry finds the file at the bound digest or at the +# prepared one, and both are this operation's. In-process because it runs +# under the lock `handoff_publish_atomic` runs under, with the fence already +# checked there: a child `set-restart-intent` would wait for that lock, which +# is the checkout's own where the opt-in is off. Every other field is kept, as +# `set-restart-intent` keeps them for a transition of the same operation. +handoff_restart_reserve() { # + hrr_f="$(printf '%s\n' "$2" | awk -F'\t' '$1=="file" {print $2; exit}')" + [ -n "$hrr_f" ] && [ -f "$hrr_f" ] || + die "the restart intent this write is fenced on could not be found again to reserve its stamp; nothing was published" 1 + hrr_dig="$HPA_EXPECTED"; [ -n "$hrr_dig" ] || hrr_dig="$(lane_restart_sidecar_field "$hrr_f" digest)" + lane_restart_put "${hrr_f%/*}" "$1" \ + "$(lane_restart_sidecar_field "$hrr_f" state)" "$(lane_restart_sidecar_field "$hrr_f" generation)" \ + "$(lane_restart_sidecar_field "$hrr_f" operation)" "$(lane_restart_sidecar_field "$hrr_f" mode)" \ + "$(lane_restart_sidecar_field "$hrr_f" agent)" "$(lane_restart_sidecar_field "$hrr_f" profile)" \ + "$(lane_restart_sidecar_field "$hrr_f" dir)" "$(lane_restart_sidecar_field "$hrr_f" pane)" \ + "$(lane_restart_sidecar_field "$hrr_f" window)" "$(lane_restart_sidecar_field "$hrr_f" handoff)" \ + "$hrr_dig" "$(lane_restart_sidecar_field "$hrr_f" old_transcript)" \ + "$(lane_restart_sidecar_field "$hrr_f" new_transcript)" "$(lane_restart_sidecar_field "$hrr_f" attempt)" \ + "owned handoff stamp prepared" "$(lane_restart_sidecar_field "$hrr_f" created)" "$3" || + die "this write could not reserve its handoff stamp in $hrr_f (the shell's own error, if any, is above); nothing was published" 1 +} handoff_publish_atomic() { # [ ] hpa_lane="$1"; hpa_target="$2"; hpa_source="$3"; hpa_digest="$4" hpa_op="${5-}"; hpa_gen="${6-}"; hpa_attempt="${7-}"; hpa_state="${8-}"; hpa_sid="${9-}" @@ -12730,12 +12842,16 @@ handoff_publish_atomic() { # [ < [ "$hpa_actual" = "$hpa_op|$hpa_gen|$hpa_attempt|$hpa_state|$hpa_sid" ] || die "this handoff publisher lost restart ownership; nothing was published" 7 hpa_bound="$(printf '%s\n' "$hpa_intent" | awk -F'\t' '$1=="handoff" {print $2; exit}')" - [ -n "$hpa_bound" ] && [ "$hpa_bound" != none ] && [ "$hpa_bound" -ef "$hpa_target" ] || + handoff_bound_is "$hpa_bound" "$hpa_target" || die "the publication target is not the operation-bound handoff; nothing was published" 2 else case "$hpa_current" in preparing|pending|starting) die "an active restart owns this handoff; nothing was published" 7 ;; esac fi [ "$(lane_handoff_digest "$hpa_target")" = "$hpa_digest" ] || die "the handoff changed before publication; nothing was published" 7 + if [ -n "$HPA_EXPECTED" ] && [ "$HPA_EXPECTED" != none ] && [ "$hpa_digest" != "$HPA_EXPECTED" ]; then + die "the restart handoff changed before ${HPA_EXPECTED_FOR:-its owned write}: $hpa_target digests $hpa_digest, and the restart bound $HPA_EXPECTED. Nothing was published." 2 + fi + [ -z "$HPA_RESERVE" ] || handoff_restart_reserve "$hpa_lane" "$hpa_intent" "$HPA_RESERVE" pbh_tmp="$(mktemp "$hpa_target.resume.XXXXXX")" || { pbh_tmp=""; die "the publication temporary file could not be made" 1; } if ! cp -p -- "$hpa_target" "$pbh_tmp" || ! cat -- "$hpa_source" > "$pbh_tmp" || ! mv -f -- "$pbh_tmp" "$hpa_target"; then rm -f -- "$pbh_tmp"; pbh_tmp="" @@ -15405,6 +15521,11 @@ write_lane_of() { # set-row-state|replace-in-row|append-session-id) [ -n "${1-}" ] || return 1 printf '%s\n' "$1" ;; + # A LANE'S HANDOFF, WRITTEN AND COMMITTED (slice 2a): the lane is `$1`, and a + # flag where the lane should be is the arm's own usage refusal (64). + write-handoff) + case "${1-}" in ''|-*) return 1 ;; esac + printf '%s\n' "$1" ;; request-handoff) # THE ARM'S OWN PARSE (the review of #282, NIT 1): `--` ends the options, # and every word after it is the `--force` why — so a why that reads @@ -15600,36 +15721,91 @@ lane_checkout_enter() { # # the lane's checkout; anything else is another repository's (Amendment 8(d)), # published and never committed by these tools. The row's cell keeps its # spelling: this answers where the file is WRITTEN. -ws_relative() { # — 0 and its path relative to W; 1 and the real path - wsr_in="$1" - case "$wsr_in" in - '~') wsr_in="$HOME" ;; - '~/'*) wsr_in="$HOME/${wsr_in#'~/'}" ;; - /*) : ;; - *) printf '%s\n' "$wsr_in"; return 0 ;; - esac - wsr_n=0 - while [ -L "$wsr_in" ] && [ "$wsr_n" -lt 40 ]; do - wsr_l="$(readlink "$wsr_in")" || break - case "$wsr_l" in /*) wsr_in="$wsr_l" ;; *) wsr_in="$(dirname -- "$wsr_in")/$wsr_l" ;; esac - wsr_n=$((wsr_n + 1)) +# A path's physical spelling: `~` expanded, the leaf's links followed (40 at +# most), its directory resolved with `cd -P`. 1, and the path as far as it was +# followed, where that directory cannot be entered. +path_physical() { # + pp_in="$1" + case "$pp_in" in '~') pp_in="$HOME" ;; '~/'*) pp_in="$HOME/${pp_in#'~/'}" ;; esac + pp_n=0 + while [ -L "$pp_in" ] && [ "$pp_n" -lt 40 ]; do + pp_l="$(readlink "$pp_in")" || break + case "$pp_l" in /*) pp_in="$pp_l" ;; *) pp_in="$(dirname -- "$pp_in")/$pp_l" ;; esac + pp_n=$((pp_n + 1)) done - wsr_par="$(cd -P -- "$(dirname -- "$wsr_in")" 2>/dev/null && pwd -P)" || { printf '%s\n' "$wsr_in"; return 1; } - wsr_real="$wsr_par/$(basename -- "$wsr_in")" + pp_par="$(cd -P -- "$(dirname -- "$pp_in")" 2>/dev/null && pwd -P)" || { printf '%s\n' "$pp_in"; return 1; } + printf '%s/%s\n' "$pp_par" "$(basename -- "$pp_in")" +} +ws_relative() { # — 0 and its path relative to W; 1 and the real path + case "$1" in '~'|'~/'*|/*) : ;; *) printf '%s\n' "$1"; return 0 ;; esac + wsr_real="$(path_physical "$1")" || { printf '%s\n' "$wsr_real"; return 1; } case "$wsr_real" in "$LANES_WS_REAL"/*) printf '%s\n' "${wsr_real#"$LANES_WS_REAL"/}"; return 0 ;; esac printf '%s\n' "$wsr_real" return 1 } +# THE SAME TRANSLATION INTO WHICHEVER CHECKOUT THE WRITE GOES THROUGH (slice +# 2a): the lane's own under the opt-in, an explicit `LANES_REPO` (the seam), or +# W where the opt-in is off — and `write-handoff`, `handoff-path` and +# `set-restart-intent` translate into it. W's rule needs W: where none was +# resolved, an absolute path is only ever the checkout's own or another +# repository's (an empty W would read every absolute path as inside it). +handoff_in_repo() { # — 0 its path in that checkout; 3 another repository's + hir_c="$1"; hir_rel=""; hir_rc=0 + case "$2" in + '~'|'~/'*|/*) + if [ -n "$LANES_WS_REAL" ]; then + hir_rel="$(ws_relative "$2")" || hir_rc=$? + else + hir_rel="$(path_physical "$2")" || : + hir_rc=1 + fi ;; + *) hir_rel="$2" ;; + esac + if [ "$hir_rc" = 0 ]; then printf '%s/%s\n' "$hir_c" "$hir_rel"; return 0; fi + if path_under "$hir_rel" "$hir_c"; then printf '%s\n' "$hir_rel"; return 0; fi + printf '%s\n' "$hir_rel" + return 3 +} handoff_in_checkout() { # — 0 its path in the lane's checkout; 3 another repository's hic_p="$(lane_checkout_path "$1")" || return 1 - hic_rel=""; hic_rc=0 - hic_rel="$(ws_relative "$2")" || hic_rc=$? - if [ "$hic_rc" = 0 ]; then printf '%s/%s\n' "$hic_p" "$hic_rel"; return 0; fi - if path_under "$hic_rel" "$hic_p"; then printf '%s\n' "$hic_rel"; return 0; fi - printf '%s\n' "$hic_rel" - return 3 + handoff_in_repo "$hic_p" "$2" +} +# THE CHECKOUT A LANE'S HANDOFF IS WRITTEN THROUGH, for the two acts that answer +# it without writing (`handoff-path`, `set-restart-intent`): S1's path under the +# opt-in with no explicit `LANES_REPO` — made first with `create` 1, or checked +# where it already exists — and otherwise `LANES_REPO`'s top level, as every +# write goes through it there. Sets HCO_PATH; dies as `lane-checkout` does. +HCO_PATH="" +handoff_checkout_of() { # + HCO_PATH="" + if [ "$LANES_ROUTE_ON" = 1 ] && [ "$LANES_REPO_GIVEN" != 1 ] && [ -z "$LANES_ROUTED" ]; then + lanes_home_check + if [ "${2:-0}" = 1 ]; then + lane_checkout_ensure "$1" + HCO_PATH="$LCE_PATH" + return 0 + fi + HCO_PATH="$(lane_checkout_path "$1")" || die "$(lanes_workspace_why)" 1 + if { [ -e "$HCO_PATH" ] || [ -L "$HCO_PATH" ]; } && + ! { [ -d "$HCO_PATH" ] && [ ! -L "$HCO_PATH" ] && [ -z "$(ls -A -- "$HCO_PATH" 2>/dev/null)" ]; }; then + lane_checkout_verify "$HCO_PATH" || lane_checkout_refuse "$HCO_PATH" + fi + return 0 + fi + HCO_PATH="$(cd -P -- "$LANES_REPO" 2>/dev/null && pwd -P)" || + die "the checkout $LANES_REPO cannot be entered, so where this lane's handoff is written cannot be said." 1 + hco_top="$(git -C "$HCO_PATH" rev-parse --show-toplevel 2>/dev/null || :)" + hco_top="$( [ -n "$hco_top" ] && cd -P -- "$hco_top" 2>/dev/null && pwd -P || :)" + [ -z "$hco_top" ] || HCO_PATH="$hco_top" + return 0 +} +# A `--stamp` THAT WRITES NOTHING STILL SAYS ITS LINE (S3's `lane-start` note): +# a stamp that is owed is a stamp somebody writes by hand. +whf_stamp_said() { + [ "${whf_mode:-}" != stamp ] || note "the stamp line it would have written under the handoff's header block: ${whf_arg:-}" + return 0 } # S5 — W IS ONLY EVER FAST-FORWARDED, and `refresh-workspace` is the one act @@ -18805,6 +18981,268 @@ EOF printf '%s\n' "$HPA_TARGET" ;; + # ---------- AMENDMENT 23, SLICE 2A: A LANE'S HANDOFF, WRITTEN UNDER S3-S4 + # + # `write-handoff (--stamp "" | --prepend | + # --whole --expect-digest ) --subject "" + # [--target ] + # [--restart + # [--expected-digest ]]` + # + # The one act that `lane-start`'s Rule 3 stamp, `lane-handoff`'s top block + # and the skill's whole-file handoff call from slices 2b and 3 on, so that no + # caller holds a lock or commits a handoff by hand (plan §2.7; Open question + # 1's reading: the lock's holder is this process). In order: + # 1. THE CHECKOUT'S LOCK, then S3 (`checkout_sync`), run in a subshell so + # that its refusal is this arm's to answer (step 6). + # 2. THE TARGET: the row's handoff cell in the published register, read + # after S3's fetch — or `--target`, the old callers' spelling — and + # TRANSLATED into the checkout this write goes through, never followed + # (`handoff_in_repo`, S2). One that still resolves into W, through a + # link, refuses (S5, 2). One in ANOTHER REPOSITORY (Amendment 8(d)) is + # written there and never committed. + # 3. S3 step 4 for the handoff's own path (`cs_own_pending`). + # 4. THE READ FOLLOWS THE WRITE: the file in that checkout, and `--stamp`'s + # splice (`lane-start`'s, unchanged: the line after the first blank line + # after line 1), `--prepend`'s (`lane-handoff`'s: the block, `---` and a + # blank line, then the old file) or `--whole`'s prepared file, each + # proven before it lands. + # 5. THE WORKSTATION'S SIDECAR LOCK, inside the checkout's, for this step + # only: `handoff_publish_atomic` — the restart fences (`--restart`, the + # bound path translated), the digests, `--stamp`'s reservation of its + # prepared digest, and the one-rename replace. + # 6. WHERE S3 REFUSED: `--stamp` wrote nothing and says its line; `--prepend` + # and `--whole` have written the handoff, commit nothing (R-A11-11: a + # swap is never left unwritten), and S3's own words are above. Exit 2. + # 7. `commit_publish`, by pathspec, with `Lane: ` as its trailer: the + # push, the read-back, and a refused push replays the whole verb on + # origin's new tip — a `--whole` only where origin's copy is still the + # one it read (S4.4). + # STDOUT: `` once the file is written (`HANDOFF_OUT`). + # 0 pushed, or nothing to commit (another repository's file; LANES_NO_GIT) + # 1 environment: a checkout that cannot be entered, an unreadable file + # 2 refused: S3 (step 6), S5, a restart's bound file or pre-image, a proof + # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named + # 7 a restart fence, or the handoff changed under the write + # 8 no handoff to write: the row names none, or its file is not there + # 64 usage + write-handoff) + whf_use="usage: write-handoff (--stamp \"\" | --prepend | --whole --expect-digest ) --subject \"\" [--target ] [--restart [--expected-digest ]]" + whf_lane="${1-}" + case "$whf_lane" in ''|-*) die "$whf_use" 64 ;; esac + shift + whf_mode=""; whf_arg=""; whf_expect=""; whf_subject=""; whf_target="" + whf_restart=0; whf_rop=""; whf_rgen=""; whf_ratt=""; whf_rstate=""; whf_rsid=""; whf_rexp="" + while [ "$#" -gt 0 ]; do + case "$1" in + --stamp|--prepend|--whole) + [ -z "$whf_mode" ] || die "write-handoff takes ONE of --stamp, --prepend and --whole. $whf_use" 64 + [ "$#" -ge 2 ] && [ -n "${2-}" ] || die "$1 needs a value. $whf_use" 64 + whf_mode="${1#--}"; whf_arg="$2"; shift 2 ;; + --expect-digest|--subject|--target|--expected-digest) + [ "$#" -ge 2 ] && [ -n "${2-}" ] || die "$1 needs a value. $whf_use" 64 + case "$1" in + --expect-digest) whf_expect="$2" ;; + --subject) whf_subject="$2" ;; + --target) whf_target="$2" ;; + --expected-digest) whf_rexp="$2" ;; + esac + shift 2 ;; + --restart) + [ "$#" -ge 6 ] || die "--restart takes five values: . $whf_use" 64 + whf_restart=1; whf_rop="$2"; whf_rgen="$3"; whf_ratt="$4"; whf_rstate="$5"; whf_rsid="$6"; shift 6 ;; + *) die "unknown write-handoff flag $1. $whf_use" 64 ;; + esac + done + [ -n "$whf_mode" ] || die "write-handoff needs one of --stamp, --prepend and --whole. $whf_use" 64 + [ -n "$whf_subject" ] || die "write-handoff needs --subject, the commit's subject line. $whf_use" 64 + for whf_v in "$whf_subject" "$whf_target" "$whf_rop" "$whf_rgen" "$whf_ratt" "$whf_rstate" "$whf_rsid"; do + lane_restart_value_ok "$whf_v" || + die "write-handoff's subject, target and restart fences are each one line with no control character. Nothing was written." 64 + done + if [ "$whf_mode" = stamp ] && ! lane_restart_value_ok "$whf_arg"; then + die "--stamp is ONE line with no control character: the line it puts under the handoff's header block. Nothing was written." 64 + fi + if [ "$whf_mode" = whole ]; then + [ "${#whf_expect}" = 64 ] || die "--whole requires --expect-digest, the complete SHA-256 of the handoff its prepared file was made from. $whf_use" 64 + case "$whf_expect" in *[!0-9a-fA-F]*) die "--expect-digest takes a SHA-256" 64 ;; esac + elif [ -n "$whf_expect" ]; then + die "--expect-digest goes with --whole: --stamp and --prepend read the handoff themselves, under the lock. $whf_use" 64 + fi + if [ "$whf_restart" = 1 ]; then + [ -n "$whf_rop" ] && [ -n "$whf_rgen" ] && [ -n "$whf_ratt" ] && [ -n "$whf_rstate" ] && [ -n "$whf_rsid" ] || + die "--restart's five fences each need a value: . $whf_use" 64 + elif [ -n "$whf_rexp" ]; then + die "--expected-digest goes with --restart: it is the digest that restart bound. $whf_use" 64 + fi + if [ -n "$whf_rexp" ] && [ "$whf_rexp" != none ]; then + [ "${#whf_rexp}" = 64 ] || die "--expected-digest takes a SHA-256, or none" 64 + case "$whf_rexp" in *[!0-9a-fA-F]*) die "--expected-digest takes a SHA-256, or none" 64 ;; esac + fi + if [ "$whf_mode" != stamp ]; then + [ -f "$whf_arg" ] && [ -r "$whf_arg" ] && [ -s "$whf_arg" ] || + die "the $whf_mode file $whf_arg is unreadable or empty. Nothing was written." 1 + fi + if [ "$whf_mode" = prepend ] && [ -n "$(tail -c 1 -- "$whf_arg")" ]; then + die "the block $whf_arg does not end with a newline, so the \`---\` under it would join its last line, and a caller's bytes are never altered. Nothing was written." 64 + fi + check_lane_name "$whf_lane" + lane="$(canon_lane "$whf_lane")" || exit 2 # Amendment 15 + managed_seam_refuse "$lane" "write-handoff" + acquire_lock + handoff_checkout_of "$lane" 0; whf_c="$HCO_PATH" + # ---- 1. S3, answered here. + whf_s3=0; whf_cs="" + whf_cs="$(checkout_sync write-handoff "$lane" write && printf 'ok%s' "$CS_NO_ORIGIN_BRANCH")" || whf_s3=$? + if [ "$whf_s3" = 0 ]; then + CS_NO_ORIGIN_BRANCH="${whf_cs##*ok}"; cs_flush; LOG_SYNC_FETCH=yes + fi + # ---- 2. the target, translated. + if [ -n "$whf_target" ]; then whf_cell="$whf_target"; else whf_cell="$(handoff_of_lane "$lane" 2>/dev/null || :)"; fi + if [ -z "$whf_cell" ]; then + whf_stamp_said + die "lane $lane's row names no handoff, so there is none to write. Nothing was written." 8 + fi + whf_other=0; whf_path="$(handoff_in_repo "$whf_c" "$whf_cell")" || whf_other=1 + whf_phys="$(path_physical "$whf_path")" || whf_phys="$whf_path" + if [ -n "$LANES_WS_REAL" ] && [ "$whf_c" != "$LANES_WS_REAL" ] && path_under "$whf_phys" "$LANES_WS_REAL"; then + whf_stamp_said + die "lane $lane's handoff $whf_path resolves to $whf_phys, inside the workspace checkout $LANES_WS_REAL, the readers' copy, where no tool writes (Amendment 23 S5). Nothing was written. The lane's handoff is a file of its own checkout, $whf_c: make $whf_path one there, then run LANES_LANE=$lane $SELF write-handoff $lane … again." 2 + fi + [ "$whf_other" = 1 ] || path_under "$whf_phys" "$whf_c" || whf_other=1 + whf_rel=""; [ "$whf_other" = 1 ] || whf_rel="${whf_phys#"$whf_c"/}" + # ---- 3. S3 step 4 for the handoff itself. + if [ "$whf_s3" = 0 ] && [ "$whf_other" = 0 ]; then + ( cs_own_pending write-handoff "$lane" "$whf_rel" ) || whf_s3=$? + fi + if [ "$whf_s3" != 0 ] && [ "$whf_mode" = stamp ]; then + note "the stamp is NOT written: the checkout refused above (Amendment 23 S3), and a Rule 3 stamp the checkout cannot take is written by nobody, and never blocks a launch." + whf_stamp_said + exit "$whf_s3" + fi + # ---- 4. the read, the splice and its proof. + if [ ! -e "$whf_phys" ] && [ ! -L "$whf_phys" ]; then + whf_stamp_said + die "lane $lane's handoff is not at $whf_phys, so there is nothing to write into. Nothing was written." 8 + fi + [ -f "$whf_phys" ] && [ -r "$whf_phys" ] || die "the handoff target is unreadable" 1 + TMPD="$(mktemp -d)" || die "no temporary directory could be made under ${TMPDIR:-/tmp} for the handoff's write. Nothing was written." 1 + whf_pre="$TMPD/pre"; whf_out="$TMPD/out" + cat -- "$whf_phys" > "$whf_pre" 2>/dev/null || die "the handoff at $whf_phys could not be read. Nothing was written." 1 + whf_prior="$(lane_handoff_digest "$whf_pre")" + case "$whf_mode" in + stamp) + [ -s "$whf_pre" ] || + die "the handoff at $whf_phys is empty, so it has no header block to stamp under, and Rule 3's stamp is not written. The line it wants: $whf_arg" 1 + whf_at="$(awk 'NR > 1 && /^[[:space:]]*$/ { print NR; exit }' "$whf_pre" || :)" + if [ -z "$whf_at" ]; then + whf_at=1 + note "$whf_phys has no blank line after line 1, so it has no header block to stamp under — the stamp goes directly under line 1" + fi + { head -n "$whf_at" -- "$whf_pre" + printf '%s\n' "$whf_arg" + tail -n "+$((whf_at + 1))" -- "$whf_pre" + } > "$whf_out" || die "the stamped copy of the handoff could not be made in $TMPD. Nothing was written." 1 + # THE PROOF, `lane-start`'s: the added line is the stamp, at the ruled + # place, and taking it out again gives the file back byte for byte. + if [ "$(sed -n -e "$((whf_at + 1))p" "$whf_out")" != "$whf_arg" ] || + ! { head -n "$whf_at" -- "$whf_out"; tail -n "+$((whf_at + 2))" -- "$whf_out"; } | cmp -s - "$whf_pre"; then + die "the splice of the stamp into $whf_phys did not prove out (one line added, none changed), so NOTHING was written to it. Add this line under its header block yourself: $whf_arg" 2 + fi ;; + prepend) + printf -- '---\n\n' > "$TMPD/sep" && + { cat -- "$whf_arg"; cat -- "$TMPD/sep"; cat -- "$whf_pre"; } > "$whf_out" || + die "the new top block could not be put in front of the handoff in $TMPD. Nothing was written." 1 + # THE PROOF, by bytes: the block, then `---` and a blank line, then the + # old file whole — which is also what a last line with no newline needs. + whf_bn="$(wc -c < "$whf_arg" | tr -d ' ')" + if ! head -c "$whf_bn" -- "$whf_out" | cmp -s - "$whf_arg" || + ! head -c "$((whf_bn + 5))" -- "$whf_out" | tail -c 5 | cmp -s - "$TMPD/sep" || + ! tail -c "+$((whf_bn + 6))" -- "$whf_out" | cmp -s - "$whf_pre"; then + die "the new top block for $whf_phys did not prove out (the old file must survive it byte for byte), so NOTHING was written to it." 2 + fi ;; + whole) + cat -- "$whf_arg" > "$whf_out" || die "the prepared handoff could not be read. Nothing was written." 1 ;; + esac + whf_blob="" + [ "$whf_other" = 1 ] || whf_blob="$(git -C "$whf_c" rev-parse -q --verify "HEAD:$whf_rel" 2>/dev/null || :)" + # ---- 5. under the workstation's sidecar lock, inside the checkout's. + sidecar_try 60 || + die "could not acquire the workstation's sidecar lock $(sidecar_lock_dir 2>/dev/null) after 60s — a restart, lifecycle or tree act holds it (holder pid $(cat -- "$(sidecar_lock_dir 2>/dev/null)/pid" 2>/dev/null || printf 'unrecorded')). Nothing was written." 4 + HPA_CHECKOUT="$whf_c" + whf_fence=() + if [ "$whf_restart" = 1 ]; then + whf_fence=("$whf_rop" "$whf_rgen" "$whf_ratt" "$whf_rstate" "$whf_rsid") + HPA_EXPECTED="$whf_rexp"; HPA_EXPECTED_FOR="this owned write" + if [ "$whf_mode" = stamp ]; then + HPA_EXPECTED_FOR="its owned resume stamp" + HPA_RESERVE="$(lane_handoff_digest "$whf_out")" + fi + fi + whf_prior_is="$whf_prior"; [ "$whf_mode" != whole ] || whf_prior_is="$whf_expect" + handoff_publish_atomic "$lane" "$whf_phys" "$whf_out" "$whf_prior_is" ${whf_fence[@]+"${whf_fence[@]}"} + sidecar_release + HANDOFF_OUT="$HPA_TARGET $(lane_handoff_digest "$HPA_TARGET")" + # ---- 6. a refused checkout: written, and not committed. + if [ "$whf_s3" != 0 ]; then + note "the handoff is WRITTEN and NOT committed: the checkout refused above (Amendment 23 S3; R-A11-11 — a swap is never left unwritten, so the handoff is written, no commit is made, and git's own words are above). It is $HPA_TARGET." + if [ "$whf_other" = 0 ]; then + note "once the refusal is answered, commit it by pathspec and publish it: git -C $whf_c commit -m \"$whf_subject\" -m \"Lane: $lane\" -- $whf_rel, then LANES_LANE=$lane $SELF commit \"\" (the publish act). Never write it again with --prepend: that puts a second block on top." + fi + exit "$whf_s3" + fi + if [ "$whf_other" = 1 ]; then + note "$HPA_TARGET is another repository's handoff (Amendment 8(d)): it is written there, and these tools never commit it." + exit 0 + fi + # ---- 7. the commit, the push, the read-back. + CP_TRAILER="Lane: $lane" + if [ "$whf_mode" = whole ]; then CP_WHOLE_REL="$whf_rel"; CP_WHOLE_BLOB="$whf_blob"; fi + commit_publish "$whf_subject" "$whf_rel" + ;; + + # ---------- AMENDMENT 23, SLICE 2A: WHERE A LANE'S HANDOFF IS WRITTEN + # + # `handoff-path [--create]`, a read: the row's handoff cell, out of the + # published register, TRANSLATED into the checkout `write-handoff` writes it + # through — S1's `-lanes//` under the opt-in with no explicit + # `LANES_REPO`, otherwise that checkout — for a caller that binds a restart + # intent to it before it writes (plan §2.7). The path is derived whether or + # not the checkout is there yet; `--create` makes the lane's checkout first. + # Not routed: it writes nothing. + # 0 the path (another repository's too, said on stderr) · 8 no row, or a + # row that names no handoff, and nothing cloned · 1 a checkout there that + # is not a clone · 2 a name refused, + # or the clone failed · 4 the creation lock · 64 usage + handoff-path) + hp_lane=""; hp_create=0 + while [ $# -gt 0 ]; do + case "$1" in + --create) hp_create=1; shift ;; + --) shift ;; + -*) die "unknown option '$1' for handoff-path (usage: handoff-path [--create])" 64 ;; + *) [ -z "$hp_lane" ] || die "handoff-path takes ONE lane (usage: handoff-path [--create])" 64 + hp_lane="$1"; shift ;; + esac + done + [ -n "$hp_lane" ] || die "usage: handoff-path [--create]" 64 + check_lane_name "$hp_lane" + log_sync + hp_lane="$(canon_lane "$hp_lane")" || exit $? + check_lane_name "$hp_lane" + # THE CELL FIRST: a lane with no row, or a row with no handoff, has nothing + # to write, and `--create` clones nothing for it (R4-1's rule for writes). + hp_cell="$(handoff_of_lane "$hp_lane" 2>/dev/null || :)" + if [ -z "$hp_cell" ]; then + note "lane $hp_lane's row names no handoff, or there is no row for it in the register" + exit 8 + fi + handoff_checkout_of "$hp_lane" "$hp_create" + hp_rc=0; hp_path="$(handoff_in_repo "$HCO_PATH" "$hp_cell")" || hp_rc=$? + [ "$hp_rc" = 0 ] || note "$hp_path is another repository's handoff (Amendment 8(d)): \`write-handoff\` writes it there and commits it nowhere" + printf '%s\n' "$hp_path" + ;; + legacy-restart-check) lane="${1-}"; check_lane_name "$lane" lane="$(canon_lane "$lane")" || exit 2 @@ -19682,6 +20120,6 @@ EOF ;; *) - die "unknown subcommand '$cmd' (verify-row|set-row-state|append-row-status|replace-in-row|append-session-id|append-line|add-row|retire-rows|archive-rows|migrate-state-cells|commit|rename-lane|log|claim|release|who|history|swapped|session-start|guard|idle-holders|live-holder|window-session|transcript-holders|binding|request-handoff|session-lane|window-lane|lane-dir|lane-profile|lane-agent|lane-transcript|lane-last|workspace-root|last-session|forks|duplicate-holder|workstation|fetch-age|lanes|lane-groups|next-free|sibling-filter|resolve-repo|lane-objects|register-row|register-row-local|retired-identity|canon-lane|resolve-home|managed-projection|lane-state|set-lane-state|lane-trees|set-lane-tree|lane-tree-now|project-legs|lane-reconcile|lane-holders|legacy-restart-check|publish-handoff|restart-intent|set-restart-intent|worktrees|pathspec-check|lane-checkout|refresh-workspace)" 2 + die "unknown subcommand '$cmd' (verify-row|set-row-state|append-row-status|replace-in-row|append-session-id|append-line|add-row|retire-rows|archive-rows|migrate-state-cells|commit|rename-lane|log|claim|release|who|history|swapped|session-start|guard|idle-holders|live-holder|window-session|transcript-holders|binding|request-handoff|session-lane|window-lane|lane-dir|lane-profile|lane-agent|lane-transcript|lane-last|workspace-root|last-session|forks|duplicate-holder|workstation|fetch-age|lanes|lane-groups|next-free|sibling-filter|resolve-repo|lane-objects|register-row|register-row-local|retired-identity|canon-lane|resolve-home|managed-projection|lane-state|set-lane-state|lane-trees|set-lane-tree|lane-tree-now|project-legs|lane-reconcile|lane-holders|legacy-restart-check|publish-handoff|restart-intent|set-restart-intent|worktrees|pathspec-check|lane-checkout|refresh-workspace|write-handoff|handoff-path)" 2 ;; esac diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 887a9fb..708d08f 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -6,22 +6,37 @@ the bytes replaced by one rename beside the file — is `handoff_publish_atomic`, which `write-handoff` shares. +`write-handoff (--stamp | --prepend | --whole) --subject …` writes the +lane's handoff in the checkout its writes go through (the lane's own under the +opt-in, the explicit `LANES_REPO` seam, W with the opt-in off), under the +checkout's lock: S3 first, the row's cell translated (S2), the read in that +checkout, the splice proven, the restart fences and the one-rename replace under +the workstation's sidecar lock nested inside, then S4 — commit by pathspec with +the `Lane:` trailer, push, read back, and a refused push replayed on origin's +new tip (a whole-file write only over the copy it started from). S3's refusal +leaves `--stamp` unwritten and `--prepend`/`--whole` written and uncommitted +(R-A11-11). `handoff-path` answers where the handoff is written. + The sandbox is the real `lanes-edit.sh`; nothing reaches a real remote. The box is slice 1b's (`tests/test_a23_publish_replay.py`: two rows, a peer clone, and a push-time `race` hook), imported rather than copied, with the lane's handoff -published on origin and a restart state root of its own. +published on origin, W brought to it, and a restart state root of its own. """ from __future__ import annotations import hashlib import os +import shlex import shutil import stat +import subprocess +import time from pathlib import Path import pytest +from a23_box import ME, REGISTER from conftest import WINDOWS_SKIP from test_a23_publish_replay import LANE, Box as ReplayBox @@ -41,13 +56,41 @@ "Preserved task.\n") +SUBJECT = f"handoff({LANE}@Eagle): RESUMED by a23-tester" +STAMP = f"RESUMED by a23-tester (lane {LANE}) at 2026-10-20T21:00:00Z — lane-start on Eagle" +BLOCK = (f"Lane: {LANE} (profile p, session s) — single-use resume prompt\n" + "\n" + "## RESUME PROMPT — PAUSED, the newer block\n" + "\n") +#: The fences `--restart` names, and the intent `restart()` makes to match. +FENCE = ("op-a23", "3", "1", "starting", "sid-a23") +#: How long a waiter is watched to prove it is still waiting (it polls at 1 s). +WATCH = 4 + + def sha(path: Path) -> str: return hashlib.sha256(path.read_bytes()).hexdigest() +def sha_of(text: str) -> str: + return hashlib.sha256(text.encode("utf-8")).hexdigest() + + +def stamped(text: str, line: str = STAMP) -> str: + """`lane-start`'s splice: the line after the first blank line after line + 1, else directly under line 1.""" + lines = text.splitlines(keepends=True) + at = next((i + 1 for i, l in enumerate(lines) if i > 0 and not l.strip()), 1) + return "".join(lines[:at]) + line + "\n" + "".join(lines[at:]) + + +def prepended(text: str, block: str = BLOCK) -> str: + return block + "---\n\n" + text + + class Box(ReplayBox): - """Slice 1b's box, with the lane's handoff on origin and its restart - intents under a state root of the box's own.""" + """Slice 1b's box, with the lane's handoff on origin, W brought to it (the + S6.5 state), and the restart intents under a state root of the box's own.""" def __init__(self, root: Path, **kw) -> None: root = Path(os.path.realpath(root)) @@ -56,6 +99,34 @@ def __init__(self, root: Path, **kw) -> None: super().__init__(root, env=env, **kw) self.state = state self.peer_pushes(HANDOFF_REL, HANDOFF) + self.w_current() + + def w_current(self) -> None: + """W fast-forwarded by the fixture, as `refresh-workspace` would.""" + self.git("-C", str(self.w), "fetch", "-q", "origin") + self.git("-C", str(self.w), "merge", "-q", "--ff-only", "refs/remotes/origin/main") + + def write_handoff(self, *args: str, **extra: str) -> subprocess.CompletedProcess: + return self.helper("write-handoff", LANE, *args, **extra) + + def restart(self, handoff: Path) -> None: + """The operation FENCE names, as a `/ctx` and its supervisor leave it: + `pending` bound to `handoff` (its digest taken there), then `starting` + with the attempt and the transcript the launch prepares.""" + self.intent("pending", "--expect", "none", "--operation", FENCE[0], "--generation", FENCE[1], + "--handoff", str(handoff)) + self.intent("starting", "--expect", "pending", "--expect-operation", FENCE[0], + "--attempt", FENCE[2], "--new-transcript", FENCE[4]) + + def cell_is(self, cell: str) -> None: + """The peer publishes the lane's row with another handoff cell.""" + self.peer_edits(REGISTER, f"| {HANDOFF_REL} |", f"| {cell} |", "a peer respells the handoff cell") + + def last_message(self) -> list: + return self.git("--git-dir", str(self.origin), "log", "-1", "--format=%B", "main").splitlines() + + def w_reflog(self) -> str: + return self.git("-C", str(self.w), "reflog", "--format=%H %gs", "HEAD") def intent(self, state: str, *extra: str) -> None: made = self.helper("set-restart-intent", LANE, state, *extra) @@ -166,3 +237,552 @@ def test_publish_handoff_refuses_a_file_the_restart_did_not_bind(box: Box): assert result.returncode == 2, result.stderr assert "the publication target is not the operation-bound handoff" in result.stderr assert real.read_text(encoding="utf-8") == HANDOFF + + +# ------------------------------------------------------------ write-handoff + +def _released(box: Box, c: Path) -> None: + assert not box.lock_of(c).exists(), "the checkout's lock is released" + assert not (box.lanes_root / ".lane-state.lock").exists(), "the sidecar lock is released" + + +def _line(path: Path) -> str: + """write-handoff's one line on stdout for the file it wrote.""" + return f"{path}\t{sha(path)}\n" + + +def _staged_foreign(box: Box, c: Path) -> None: + box.write(c, "handoffs/README.md", "# handoffs\na peer's staged line\n") + box.git("-C", str(c), "add", "--", "handoffs/README.md") + + +# ------------------------------------------------- --stamp + +def test_stamp_spliced_committed_pushed_with_lane_trailer(box: Box): + """`lane-start`'s splice, moved: the stamp under the header block, proven, + committed by pathspec with the `Lane:` trailer, pushed and read back.""" + c = box.seam() + f = c / HANDOFF_REL + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) + msg = box.last_message() + assert msg[0] == SUBJECT and f"Lane: {LANE}" in msg, msg + assert box.git("--git-dir", str(box.origin), "show", "--name-only", "--format=", "main").split() == [HANDOFF_REL] + assert result.stdout == _line(f) + assert "pushed origin/main" in result.stderr + assert box.head(c) == box.origin_head() and box.unpublished(c) == [] + _released(box, c) + + +@pytest.mark.parametrize("row", ["staged", "no-fetch"]) +def test_stamp_refused_by_S3_writes_nothing_prints_line(box: Box, row: str): + """S3 refuses (a foreign staged entry; a fetch that did not run): the + stamp is written by nobody, the line it would have written is printed, and + the checkout and origin are as they were (2).""" + c = box.seam() + extra = {} + if row == "staged": + _staged_foreign(box, c) + words = "staged entries this write did not make: handoffs/README.md" + else: + extra = {"LANES_NO_FETCH": "1"} + words = "LANES_NO_FETCH=1 is set" + before = box.snapshot(c) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, **extra) + assert result.returncode == 2, result.stderr + assert words in result.stderr and "Nothing was written." in result.stderr + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert result.stdout == "" + assert box.snapshot(c) == before + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + _released(box, c) + + +@pytest.mark.parametrize("peer", ["another-file", "the-handoff"]) +def test_stamp_replays_on_new_content(box: Box, peer: str): + """A peer's commit lands at the moment of the push: the stamp's commit is + dropped and the verb runs again on origin's new tip, splicing the stamp + into the new content. One stamp reaches origin, and one line is printed.""" + c = box.seam() + base = HANDOFF + if peer == "another-file": + box.peer_prepares("handoffs/README.md", None, "# handoffs, by a peer\n", "a peer writes another file") + else: + base = HANDOFF.replace("Preserved task.\n", "Preserved task, and a peer's line.\n") + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + box.race(prepared=True) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert "push refused (attempt 1 of 6)" in result.stderr + published = box.origin_file(HANDOFF_REL) + assert published == stamped(base) and published.count(STAMP) == 1 + f = c / HANDOFF_REL + assert result.stdout == _line(f), "one line: the last attempt's" + assert box.subjects()[0] == SUBJECT + assert box.head(c) == box.origin_head() and box.unpublished(c) == [] + _released(box, c) + + +def test_stamp_that_does_not_prove_out_writes_nothing(box: Box): + """A one-line handoff with no newline would have the stamp glued to it: + the proof fails, and nothing is written (2).""" + c = box.seam() + box.write(c, HANDOFF_REL, "one line and no newline") + box.commit(c, "a handoff of one unterminated line", HANDOFF_REL) + box.git("-C", str(c), "push", "-q", "origin", "main") + tip = box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 2, result.stderr + assert "did not prove out" in result.stderr and STAMP in result.stderr + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == "one line and no newline" + assert box.origin_head() == tip and result.stdout == "" + _released(box, c) + + +def test_stamp_over_an_uncommitted_change_to_the_handoff_is_refused(box: Box): + """S3 step 4 for the handoff: a change nobody committed would ride the + stamp's commit under its subject. Refused (2), and the change is left as + it was, unstamped.""" + c = box.seam() + f = c / HANDOFF_REL + edited = HANDOFF + "A line somebody wrote and did not commit.\n" + f.write_text(edited, encoding="utf-8") + tip = box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 2, result.stderr + assert f"these paths this write commits hold an uncommitted change: {HANDOFF_REL}" in result.stderr + assert f.read_text(encoding="utf-8") == edited + assert box.origin_head() == tip and result.stdout == "" + _released(box, c) + + +# ------------------------------------------------- --prepend + +def _block(box: Box) -> Path: + block = box.root / "block.md" + block.write_text(BLOCK, encoding="utf-8") + return block + + +def test_prepend_written_committed(box: Box): + """`lane-handoff`'s shape, moved: the block, `---` and a blank line, then + the old file whole; committed with the trailer, pushed, read back.""" + c = box.seam() + f = c / HANDOFF_REL + subject = f"handoff({LANE}@Eagle): PAUSED, a23" + result = box.write_handoff("--prepend", str(_block(box)), "--subject", subject) + assert result.returncode == 0, result.stderr + assert f.read_text(encoding="utf-8") == prepended(HANDOFF) + assert box.origin_file(HANDOFF_REL) == prepended(HANDOFF) + msg = box.last_message() + assert msg[0] == subject and f"Lane: {LANE}" in msg + assert result.stdout == _line(f) + assert box.unpublished(c) == [] + _released(box, c) + + +def test_prepend_refused_by_S3_still_writes_file_no_commit(box: Box): + """R-A11-11: a swap is never left unwritten. S3 refuses the commit (a + foreign staged entry), the handoff is written all the same, nothing is + committed or pushed, S3's words are printed, and so is the path (2).""" + c = box.seam() + f = c / HANDOFF_REL + _staged_foreign(box, c) + head, tip = box.head(c), box.origin_head() + result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED") + assert result.returncode == 2, result.stderr + assert "staged entries this write did not make: handoffs/README.md" in result.stderr + assert "the handoff is WRITTEN and NOT committed" in result.stderr + assert f.read_text(encoding="utf-8") == prepended(HANDOFF) + assert result.stdout == _line(f) + assert box.head(c) == head and box.origin_head() == tip + assert box.git("-C", str(c), "diff", "--cached", "--name-only").split() == ["handoffs/README.md"] + _released(box, c) + + +# ------------------------------------------------- --whole + +def _prepared(box: Box, text: str = "# the handoff, written whole\n") -> Path: + prepared = box.root / "prepared.md" + prepared.write_text(text, encoding="utf-8") + return prepared + + +def test_whole_expect_digest_mismatch_exit_7_nothing_published(box: Box): + c = box.seam() + tip = box.origin_head() + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", "0" * 64, + "--subject", "handoff: PAUSED") + assert result.returncode == 7, result.stderr + assert "the handoff changed before publication; nothing was published" in result.stderr + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + assert result.stdout == "" and box.origin_head() == tip + _released(box, c) + + +def test_whole_replays_only_when_origin_copy_unchanged(box: Box): + """Origin moved under the push, and its copy of the handoff is still the + one this write started from: the whole-file write replays and lands.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares("handoffs/README.md", None, "# handoffs, by a peer\n", "a peer writes another file") + box.race(prepared=True) + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", "handoff: PAUSED") + assert result.returncode == 0, result.stderr + assert "push refused (attempt 1 of 6)" in result.stderr + assert box.origin_file(HANDOFF_REL) == "# the handoff, written whole\n" + assert box.origin_file("handoffs/README.md") == "# handoffs, by a peer\n" + assert result.stdout == _line(f) + _released(box, c) + + +def test_whole_refuses_with_commit_kept_exit_6_when_origin_changed_it(box: Box): + """S4.4: origin's copy moved under the push, so the whole-file write does + not replay over that change. The handoff stays written, its commit is kept + on HEAD and named (6), and origin keeps the peer's version.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + box.race(prepared=True) + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", "handoff: PAUSED") + assert result.returncode == 6, result.stderr + kept = box.head(c) + assert "is no longer the one this whole-file write started from" in result.stderr + assert f"kept on HEAD: {kept}" in result.stderr + assert "push refused (attempt" not in result.stderr, "no replay" + assert f.read_text(encoding="utf-8") == "# the handoff, written whole\n" + assert "and a peer's line" in box.origin_file(HANDOFF_REL) + assert [line.split()[1] for line in box.unpublished(c)] == [kept] + assert result.stdout == _line(f) + _released(box, c) + + +# ------------------------------------------------- the translation (S2) + +def _estate_link(box: Box) -> str: + """`~/projects/repoS4/handoffs`, the estate link into W, and the cell + spelled through it (Amendment 4(e)).""" + estate = box.home / "projects" / "repoS4" + estate.mkdir(parents=True) + (estate / "handoffs").symlink_to(box.w / "handoffs" / "repoS4") + return f"~/projects/repoS4/handoffs/{LANE}.md" + + +@pytest.mark.parametrize("how", ["seam", "routed"]) +def test_estate_spelled_cell_is_written_in_checkout(box: Box, how: str): + """A cell spelled through the estate link resolves into W, and is + TRANSLATED, never followed: the lane's checkout's copy is written and + committed, and W's is left alone.""" + box.cell_is(_estate_link(box)) + c = box.seam() if how == "seam" else box.checkout(LANE) + w_file = box.w / HANDOFF_REL + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) + assert w_file.read_text(encoding="utf-8") == HANDOFF, "W's copy is never written" + assert result.stdout == _line(c / HANDOFF_REL) + _released(box, c) + + +@pytest.mark.parametrize("how", ["seam", "routed", "default"]) +def test_repo_relative_cell(tmp_path: Path, how: str): + """The repository-relative cell is joined to the checkout the write goes + through: the lane's own routed, the seam's, and W's with the opt-in off — + where every writer goes through W, as before Amendment 23.""" + box = Box(tmp_path, route=(how != "default")) + c = {"seam": box.seam, "routed": lambda: box.checkout(LANE), "default": lambda: box.w}[how]() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert result.stdout == _line(c / HANDOFF_REL) + assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) + if how != "default": + assert (box.w / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + _released(box, c) + + +def test_cell_in_another_repository_is_published_not_committed(box: Box): + """Amendment 8(d): a handoff in another repository is not the register's. + It is written there, and committed nowhere.""" + other = box.root / "other" + box.git("init", "-q", "-b", "main", str(other)) + box.write(other, "handoff.md", HANDOFF) + box.commit(other, "another repository's handoff", "handoff.md") + box.cell_is(str(other / "handoff.md")) + c = box.seam() + other_head = box.git("-C", str(other), "rev-parse", "HEAD").strip() + head, tip = box.head(c), box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert "another repository's handoff (Amendment 8(d))" in result.stderr + assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.git("-C", str(other), "rev-parse", "HEAD").strip() == other_head + assert box.git("-C", str(other), "status", "--porcelain").split() == ["M", "handoff.md"] + assert box.head(c) == head and box.origin_head() == tip + assert result.stdout == _line(other / "handoff.md") + _released(box, c) + + +def test_a_cell_through_a_link_into_W_is_refused_S5(box: Box): + """S5: a handoff in the lane's checkout that is a link into W would write + W's file. Refused before a byte (2), naming the checkout and the lane.""" + c = box.seam() + f = c / HANDOFF_REL + f.unlink() + f.symlink_to(box.w / HANDOFF_REL) + result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED") + assert result.returncode == 2, result.stderr + assert "the readers' copy, where no tool writes (Amendment 23 S5)" in result.stderr + assert f"LANES_LANE={LANE}" in result.stderr + assert (box.w / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + assert result.stdout == "" + _released(box, c) + + +# ------------------------------------------------- the restart (Amendment 17) + +def test_restart_fences_under_sidecar_lock(box: Box): + """The restart's fence and the replace run under the workstation's sidecar + lock, taken INSIDE the checkout's: while another act holds it, the write + waits holding its checkout lock and writes nothing; then it lands.""" + c = box.seam() + f = c / HANDOFF_REL + box.restart(f) + sidecar = box.lanes_root / ".lane-state.lock" + holder = subprocess.Popen(["sleep", "300"]) + try: + sidecar.mkdir(parents=True) + (sidecar / "pid").write_text(f"{holder.pid} {ME}\n", encoding="utf-8") + waiter = box.popen("write-handoff", LANE, "--stamp", STAMP, "--subject", SUBJECT, + "--restart", *FENCE) + time.sleep(WATCH) + assert waiter.poll() is None, "it waits on the workstation's sidecar lock" + assert box.lock_of(c).is_dir(), "…holding its checkout lock, taken first" + assert f.read_text(encoding="utf-8") == HANDOFF + finally: + holder.kill() + holder.wait() + out, err = waiter.communicate(timeout=120) + assert waiter.returncode == 0, err + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert out == _line(f) + _released(box, c) + + +REFUSED_FENCES = { + "lost": (("--restart", "op-a23", "4", "1", "starting", "sid-a23"), 7, + "this handoff publisher lost restart ownership"), + "unbound": (("--restart", *FENCE), 2, "the publication target is not the operation-bound handoff"), + "active": ((), 7, "an active restart owns this handoff"), +} + + +@pytest.mark.parametrize("case", REFUSED_FENCES) +def test_restart_fence_refusals_write_nothing(box: Box, case: str): + c = box.seam() + f = c / HANDOFF_REL + box.restart(c / "handoffs" / "README.md" if case == "unbound" else f) + flags, code, words = REFUSED_FENCES[case] + tip = box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, *flags) + assert result.returncode == code, result.stderr + assert words in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF + assert result.stdout == "" and box.origin_head() == tip + assert box.intent_field("prepared_digest") == "none", "nothing was reserved" + _released(box, c) + + +def test_intent_bound_to_W_path_is_translated(box: Box): + """An intent an older `lane-handoff` bound to W's spelling of the file + still fences the lane's own copy: the bound path is TRANSLATED into the + checkout before it is compared. `publish-handoff` keeps the strict test.""" + w_file = box.w / HANDOFF_REL + box.restart(w_file) + made = box.helper("set-row-state", LANE, "LIVE · the checkout is made") + assert made.returncode == 0, made.stderr + c = box.checkout(LANE) + strict = box.helper("publish-handoff", LANE, str(c / HANDOFF_REL), str(_prepared(box)), + "--expect-digest", sha(c / HANDOFF_REL), "--operation", FENCE[0], + "--generation", FENCE[1], "--attempt", FENCE[2], "--state", FENCE[3], + "--transcript", FENCE[4]) + assert strict.returncode == 2, strict.stderr + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE) + assert result.returncode == 0, result.stderr + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == stamped(HANDOFF) + assert w_file.read_text(encoding="utf-8") == HANDOFF + assert box.intent_field("handoff") == str(w_file), "the intent itself is not rewritten" + _released(box, c) + + +def test_prepared_digest_recorded_before_publication(box: Box): + """`lane-start`'s reservation, in-process: the stamped file's digest is the + intent's prepared digest BEFORE the bytes are replaced — here the replace + fails (no temporary can be made beside the file), and the reservation is + already there, with the bound digest and the operation kept.""" + if os.geteuid() == 0: + pytest.skip("a directory this process cannot write needs an unprivileged runner") + c = box.seam() + f = c / HANDOFF_REL + box.restart(f) + bound = box.intent_field("digest") + assert bound == sha(f) + f.parent.chmod(0o555) + try: + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, + "--expected-digest", bound) + finally: + f.parent.chmod(0o755) + assert result.returncode == 1, result.stderr + assert "the publication temporary file could not be made" in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF + assert box.intent_field("prepared_digest") == sha_of(stamped(HANDOFF)) + assert box.intent_field("digest") == bound + assert box.intent_field("reason") == "owned handoff stamp prepared" + assert (box.intent_field("operation"), box.intent_field("state")) == (FENCE[0], FENCE[3]) + again = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, + "--expected-digest", bound) + assert again.returncode == 0, again.stderr + assert sha(f) == box.intent_field("prepared_digest") + _released(box, c) + + +def test_restart_pre_image_must_be_the_bound_digest(box: Box): + """The handoff changed under the restart: `lane-start`'s refusal (2), and + nothing written or reserved.""" + c = box.seam() + f = c / HANDOFF_REL + box.restart(f) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, + "--expected-digest", "1" * 64) + assert result.returncode == 2, result.stderr + assert "the restart handoff changed before its owned resume stamp" in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF + assert box.intent_field("prepared_digest") == "none" + _released(box, c) + + +def test_only_the_stamp_reserves(box: Box): + """`lane-handoff`'s prepend binds at `preparing` and the digest is taken + again at `pending`: a fenced prepend writes, and reserves nothing.""" + c = box.seam() + f = c / HANDOFF_REL + box.intent("preparing", "--expect", "none", "--operation", FENCE[0], "--generation", FENCE[1], + "--handoff", str(f)) + result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED", + "--restart", FENCE[0], FENCE[1], "0", "preparing", "none") + assert result.returncode == 0, result.stderr + assert f.read_text(encoding="utf-8") == prepended(HANDOFF) + assert box.intent_field("prepared_digest") == "none" + _released(box, c) + + +def test_the_sidecar_lock_is_released_before_the_push(box: Box): + """The workstation's lock is held for the fence and the replace only; the + push and the read-back run under the checkout's lock alone.""" + c = box.seam() + sidecar = shlex.quote(str(box.lanes_root / ".lane-state.lock")) + checkout_lock = shlex.quote(str(box.lock_of(c))) + seen = box.root / "at-push" + box.use_net('[ "$1" = push ] || exit 0\n' + f"{{ [ -d {sidecar} ] && echo sidecar; [ -d {checkout_lock} ] && echo checkout; }} " + f">> {shlex.quote(str(seen))}\nexit 0\n") + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert seen.read_text(encoding="utf-8").split() == ["checkout"] + _released(box, c) + + +# ------------------------------------------------- always: W + +def test_W_untouched(box: Box): + """Routed, every mode writes the lane's own checkout and origin. W is the + readers' copy: its HEAD, its working tree and its reflog do not move.""" + w_before, reflog = box.w_state(), box.w_reflog() + stamp = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert stamp.returncode == 0, stamp.stderr + c = box.checkout(LANE) + prepend = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED") + assert prepend.returncode == 0, prepend.stderr + whole = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(c / HANDOFF_REL), + "--subject", "handoff: whole") + assert whole.returncode == 0, whole.stderr + assert box.origin_file(HANDOFF_REL) == "# the handoff, written whole\n" + assert box.subjects()[:3] == ["handoff: whole", "handoff: PAUSED", SUBJECT] + assert box.w_state() == w_before and box.w_reflog() == reflog + assert (box.w / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + assert box.head(c) == box.origin_head() and box.unpublished(c) == [] + _released(box, c) + + +# ------------------------------------------------- handoff-path + +@pytest.mark.parametrize("how", ["routed", "seam", "default"]) +def test_handoff_path_names_where_write_handoff_writes(tmp_path: Path, how: str): + """A read: the row's cell translated into the checkout `write-handoff` + writes through. Routed, the path is derived before the checkout exists, + and `--create` makes it.""" + box = Box(tmp_path, route=(how != "default")) + c = {"seam": box.seam, "routed": lambda: box.checkout(LANE), "default": lambda: box.w}[how]() + result = box.helper("handoff-path", LANE) + assert result.returncode == 0, result.stderr + assert result.stdout == f"{c / HANDOFF_REL}\n" + if how == "routed": + assert not c.exists(), "a read makes no checkout" + made = box.helper("handoff-path", LANE, "--create") + assert made.returncode == 0, made.stderr + assert made.stdout == f"{c / HANDOFF_REL}\n" and (c / HANDOFF_REL).is_file() + for argv in (("repoS4-9",), ("repoS4-9", "--create")): + none = box.helper("handoff-path", *argv) + assert none.returncode == 8, none.stderr + assert "no row for it in the register" in none.stderr and none.stdout == "" + assert not box.checkout("repoS4-9").exists(), "a lane with no row gets no checkout" + + +# ------------------------------------------------- usage + +@pytest.mark.parametrize("argv", [ + ("--subject", SUBJECT), + ("--stamp", STAMP), + ("--stamp", STAMP, "--prepend", "x", "--subject", SUBJECT), + ("--whole", "x", "--subject", SUBJECT), + ("--stamp", STAMP, "--subject", SUBJECT, "--expected-digest", "0" * 64), + ("--stamp", "two\nlines", "--subject", SUBJECT), + ("--stamp", STAMP, "--subject", SUBJECT, "--restart", "op"), +], ids=["no-mode", "no-subject", "two-modes", "whole-no-digest", "expected-no-restart", + "stamp-two-lines", "restart-short"]) +def test_usage_is_64_and_writes_nothing(box: Box, argv): + c = box.seam() + box.use_net() + before = box.snapshot(c) + result = box.write_handoff(*argv) + assert result.returncode == 64, result.stderr + assert box.snapshot(c) == before and box.net_ops() == [] + + +def test_write_handoff_for_a_lane_with_no_row_is_8(box: Box): + """No row, no handoff cell: nothing to write (8), and the stamp it would + have written is said.""" + c = box.seam() + before = box.snapshot(c) + result = box.helper("write-handoff", "repoS4-9", "--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 8, result.stderr + assert "row names no handoff, so there is none to write" in result.stderr + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert result.stdout == "" and box.snapshot(c) == before + _released(box, c) + + +def test_a_block_with_no_last_newline_is_refused(box: Box): + c = box.seam() + block = box.root / "block.md" + block.write_text("a block whose last line has no newline", encoding="utf-8") + result = box.write_handoff("--prepend", str(block), "--subject", "handoff: PAUSED") + assert result.returncode == 64, result.stderr + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF From bbb8f48026639859cc7486617688273ebb9a2f63 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 13:11:50 +0000 Subject: [PATCH 03/34] `write-handoff` asks the managed seam again on what S3 fetched, so a 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 (cherry picked from commit 548095e95134d98ce84c0d9b0aa757db45048c57) --- lanes-edit.sh | 5 +++++ tests/test_a23_write_handoff.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/lanes-edit.sh b/lanes-edit.sh index 146ba53..66df1f9 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -19096,6 +19096,11 @@ EOF whf_cs="$(checkout_sync write-handoff "$lane" write && printf 'ok%s' "$CS_NO_ORIGIN_BRANCH")" || whf_s3=$? if [ "$whf_s3" = 0 ]; then CS_NO_ORIGIN_BRANCH="${whf_cs##*ok}"; cs_flush; LOG_SYNC_FETCH=yes + # THE SEAM ASKED AGAIN, ON WHAT S3 JUST FETCHED (the pre-review of 1b, + # MUST 1's shape): the ask before the lock read this checkout's + # origin/$LANES_BRANCH as it last stood, and a managed-owner marker that + # landed since would otherwise go unread. A refusal here writes nothing. + managed_seam_refuse "$lane" "write-handoff" fi # ---- 2. the target, translated. if [ -n "$whf_target" ]; then whf_cell="$whf_target"; else whf_cell="$(handoff_of_lane "$lane" 2>/dev/null || :)"; fi diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 708d08f..6dc8cd5 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -786,3 +786,32 @@ def test_a_block_with_no_last_newline_is_refused(box: Box): result = box.write_handoff("--prepend", str(block), "--subject", "handoff: PAUSED") assert result.returncode == 64, result.stderr assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + + +# ------------------------------------------------- the seam, asked on S3's tip (A28) + +def test_a_managed_marker_landing_inside_the_s3_window_is_refused(box: Box): + """The managed seam is asked before the lock on this checkout's + origin/main as it last stood, and AGAIN on what S3 fetched: a peer's row + carrying managed-owner vocabulary, landed at the moment of S3's fetch, is + read, and the stamp is refused with nothing written.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(REGISTER, f"| handoffs/repoS4/{LANE}.md | LIVE · seeded |", + f"| handoffs/repoS4/{LANE}.md | LIVE · managed-owner: daemon-a23 |", + "a peer's row names a managed owner") + peer = shlex.quote(str(box.peer)) + count = shlex.quote(str(box.count)) + box.use_net('[ "$1" = fetch ] || exit 0\n' + f'[ -e {count} ] && exit 0\n' + f"echo 1 > {count}\n" + f"git -C {peer} push -q origin main\n" + "exit 0\n") + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode in (1, 2), result.stderr + assert "managed ledger" in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF, "nothing written" + assert "managed-owner: daemon-a23" in box.origin_register(), "the peer's row landed" + assert box.origin_file(HANDOFF_REL) == HANDOFF and result.stdout == "" + assert box.net_ops()[0] == "fetch", "the marker landed at S3's fetch, after the first ask" + _released(box, c) From 8129efa4266decdc79695135ca4e01788761c605 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 14:00:21 +0000 Subject: [PATCH 04/34] `write-handoff` joins `lane_row_gate`'s row group, so under the opt-in 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 `-lanes//` 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 (cherry picked from commit 6ee2c117c10a5bbed2f551c5cdab3d4c2246fc2c) --- lanes-edit.sh | 11 +++++++---- tests/test_a23_write_handoff.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 2650f2a..9087842 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -15703,8 +15703,9 @@ ws_pending_refuse() { # # refused, "expected exactly 1 row", leaving the clone. So where the lane has # no checkout yet and the verb's arm needs exactly one row of that lane (the # four routed `row_line` callers; `rename-lane` goes through its OLD name, -# whose row it needs), a lane with no row in the published register refuses -# first, and nothing is cloned. +# whose row it needs; `write-handoff`, slice 2a, writes the handoff the row +# names, so a lane with no row has none), a lane with no row in the +# published register refuses first, and nothing is cloned. # * AND THE WRITES THAT NAME A LANE WITH NO ROW (lane openRepoTools-3's # re-check of #282, R4-1, for slice 1b): `log`, `commit`, `request-handoff` # and `append-line` take a lane with no row — an archived lane keeps its @@ -15737,7 +15738,7 @@ lrg_known() { # <1 = its published log answers too> — 0 kno } lane_row_gate() { # case "$cmd" in - set-row-state|replace-in-row|append-session-id|rename-lane) lrg_log=0 ;; + set-row-state|replace-in-row|append-session-id|rename-lane|write-handoff) lrg_log=0 ;; log|commit|request-handoff|append-line) lrg_log=1 ;; *) return 0 ;; esac @@ -15755,7 +15756,9 @@ lane_row_gate() { # if [ "$lrg_log" = 1 ]; then die "lane $1 has no row and no object log on origin/$LANES_BRANCH (fetched just now into W), so no lane of that name was ever started or archived, and \`$cmd\` would write for it: its checkout $2/ was not cloned (Amendment 23 S1, S2). Nothing was written. Check the name — \`$SELF lanes\` lists every lane, and \`$SELF lanes --closed\` the closed ones — or start the lane first (lane-start; its \`add-row\` makes the row)." 2 fi - die "lane $1 has no row in the register on origin/$LANES_BRANCH (fetched just now into W), and \`$cmd\` edits that lane's row: there is nothing for it to edit, so its checkout $2/ was not cloned (Amendment 23 S1, S2). Nothing was written. Check the name — \`$SELF lanes\` lists every lane — or add the row first (\`add-row\`)." 2 + lrg_needs="edits that lane's row: there is nothing for it to edit" + [ "$cmd" != write-handoff ] || lrg_needs="writes the handoff that lane's row names: there is none to write" + die "lane $1 has no row in the register on origin/$LANES_BRANCH (fetched just now into W), and \`$cmd\` $lrg_needs, so its checkout $2/ was not cloned (Amendment 23 S1, S2). Nothing was written. Check the name — \`$SELF lanes\` lists every lane — or add the row first (\`add-row\`)." 2 } # S2, IN ITS ORDER, for a write: (1) an explicit `LANES_REPO` keeps its meaning diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 6dc8cd5..71cee07 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -815,3 +815,23 @@ def test_a_managed_marker_landing_inside_the_s3_window_is_refused(box: Box): assert box.origin_file(HANDOFF_REL) == HANDOFF and result.stdout == "" assert box.net_ops()[0] == "fetch", "the marker landed at S3's fetch, after the first ask" _released(box, c) + + +# ------------------------------------------------- a lane with no row, routed (A23) + +@pytest.mark.parametrize("fetch", ["fetched", "no-fetch"]) +def test_routed_write_handoff_for_a_lane_with_no_row_clones_nothing(box: Box, fetch: str): + """R4-1's rule for this verb: routed, a lane with no row has no handoff, + and it is refused (2) before its checkout is cloned — after one fetch of + W, or at once under `LANES_NO_FETCH=1`, which a writer cannot run on.""" + extra = {"LANES_NO_FETCH": "1"} if fetch == "no-fetch" else {} + w_before, tip = box.w_state(), box.origin_head() + result = box.helper("write-handoff", "repoS4-9", "--stamp", STAMP, "--subject", SUBJECT, **extra) + assert result.returncode == 2, result.stderr + if fetch == "fetched": + assert "writes the handoff that lane's row names: there is none to write" in result.stderr + else: + assert "LANES_NO_FETCH=1 is set" in result.stderr + assert "was not cloned" in result.stderr + assert not box.checkout("repoS4-9").exists(), "nothing is cloned for a lane with no row" + assert box.w_state() == w_before and box.origin_head() == tip and result.stdout == "" From c9fd35d464e551fd8c2ef86fbca15a2c25455975 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 14:50:10 +0000 Subject: [PATCH 05/34] =?UTF-8?q?`set-restart-intent=20--handoff`=20takes?= =?UTF-8?q?=20the=20row's=20own=20spellings,=20a=20repository-relative=20`?= =?UTF-8?q?handoffs/=E2=80=A6`=20cell=20and=20the=20estate's=20`~/?= =?UTF-8?q?=E2=80=A6`,=20and=20binds=20them=20as=20the=20absolute=20path?= =?UTF-8?q?=20in=20the=20checkout=20the=20lane's=20writes=20go=20through,?= =?UTF-8?q?=20while=20an=20absolute=20path=20is=20still=20kept=20exactly?= =?UTF-8?q?=20as=20given=20(#198,=20slice=202a=20stage=203)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (cherry picked from commit d5cabdcd943067f94888d739cf7da8b225097c36) --- lanes-edit.sh | 33 +++++++++++++++- tests/test_a23_write_handoff.py | 69 +++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 9087842..578f089 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -19492,15 +19492,44 @@ EOF # the handoff: the reader of this file is a DIFFERENT PROCESS IN A # DIFFERENT DIRECTORY — the supervisor in a respawned pane — and a relative # path resolved against its cwd is a path to somewhere else. `~` is the - # writing shell's and is never expanded here. + # writing shell's and is never expanded in `--dir`. + # + # …EXCEPT THE ROW'S OWN SPELLINGS OF A HANDOFF (Amendment 23 S2, slice + # 2a): a repository-relative `handoffs/…` cell, and the estate's `~/…`, + # are TRANSLATED once the lane is known — into the checkout that lane's + # writes go through (`handoff_checkout_of`: its own under the opt-in, an + # explicit `LANES_REPO`, W with the opt-in off) — and stored as that + # absolute path, so the digest below is that file's and `write-handoff` + # fences on it directly. An ABSOLUTE path is kept exactly as given: it + # is every older caller's, and each binds the file it then publishes and + # compares — W's, until slice 2b moves `lane-start` and `lane-handoff` + # (`write-handoff` translates a W-bound path on its own side). Any other + # relative path, and a `.` or `..` component, is still refused. case "$sri_dir" in ''|/*) : ;; *) die "--dir takes an ABSOLUTE path: this record is read by the supervisor in a pane whose working directory is not this one, and a relative path there names a different place. Got '$sri_dir'." 64 ;; esac - case "$sri_hf" in ''|/*) : ;; *) die "--handoff takes an ABSOLUTE path, for the same reason --dir does: the launch that reads it back stands somewhere else. Got '$sri_hf'." 64 ;; esac + sri_hf_cell="" + case "$sri_hf" in + ''|/*) : ;; + handoffs/*|'~/'*) + case "/$sri_hf/" in + */../*|*/./*) die "--handoff's repository-relative cell is joined to a checkout, so it names no '.' or '..' component. Got '$sri_hf'." 64 ;; + esac + sri_hf_cell="$sri_hf" ;; + *) die "--handoff takes an ABSOLUTE path, for the same reason --dir does: the launch that reads it back stands somewhere else — or the row's own spelling, a repository-relative handoffs/… cell or ~/…, which is translated into the lane's checkout. Got '$sri_hf'." 64 ;; + esac for sri_exact in "$sri_dir" "$sri_pane" "$sri_win" "$sri_hf" "$sri_old" "$sri_new" "$sri_dig" "$sri_prepared"; do lane_restart_value_ok "$sri_exact" || die "restart launch facts cannot contain control characters. Nothing was written." 64 done check_lane_name "$lane" log_sync lane="$(canon_lane "$lane")" || exit 2 # Amendment 15 + if [ -n "$sri_hf_cell" ]; then + handoff_checkout_of "$lane" 0 + sri_hf="$(handoff_in_repo "$HCO_PATH" "$sri_hf_cell")" || : + case "$sri_hf" in + /*) note "--handoff $sri_hf_cell is bound as $sri_hf: the row's spelling, translated into the checkout lane $lane's writes go through, or another repository's file as it is (Amendment 23 S2)" ;; + *) die "--handoff $sri_hf_cell could not be made an absolute path in the checkout lane $lane's writes go through (got '$sri_hf'). Nothing was written." 1 ;; + esac + fi # Select the physical intent under the CAS mutex: a waiting writer must # see a reservation another writer established before releasing the lock. sidecar_lock_use; acquire_lock # the workstation's sidecar lock, and no other (Amendment 23 S2) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 71cee07..8f4fdd0 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -835,3 +835,72 @@ def test_routed_write_handoff_for_a_lane_with_no_row_clones_nothing(box: Box, fe assert "was not cloned" in result.stderr assert not box.checkout("repoS4-9").exists(), "nothing is cloned for a lane with no row" assert box.w_state() == w_before and box.origin_head() == tip and result.stdout == "" + + +# ------------------------------------------------- set-restart-intent --handoff + +def _bound(box: Box, cell: str) -> subprocess.CompletedProcess: + """A `/ctx`'s `pending`, bound to `cell` as the caller spells it.""" + return box.helper("set-restart-intent", LANE, "pending", "--expect", "none", "--operation", FENCE[0], + "--generation", FENCE[1], "--handoff", cell) + + +@pytest.mark.parametrize("how", ["seam", "routed", "default"]) +def test_set_restart_intent_binds_a_repository_relative_cell_in_the_checkout(tmp_path: Path, how: str): + """The row's repository-relative cell is translated into the checkout the + lane's writes go through and stored as that absolute path, with that + file's digest; the owned stamp then lands on it, fenced and reserved.""" + box = Box(tmp_path, route=(how != "default")) + if how == "routed": + made = box.helper("handoff-path", LANE, "--create") + assert made.returncode == 0, made.stderr + c = {"seam": box.seam, "routed": lambda: box.checkout(LANE), "default": lambda: box.w}[how]() + f = c / HANDOFF_REL + made = _bound(box, HANDOFF_REL) + assert made.returncode == 0, made.stderr + assert f"--handoff {HANDOFF_REL} is bound as {f}" in made.stderr + assert box.intent_field("handoff") == str(f) + assert box.intent_field("digest") == sha(f) + box.intent("starting", "--expect", "pending", "--expect-operation", FENCE[0], + "--attempt", FENCE[2], "--new-transcript", FENCE[4]) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, + "--expected-digest", box.intent_field("digest")) + assert result.returncode == 0, result.stderr + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.intent_field("prepared_digest") == sha(f) + if how != "default": + assert (box.w / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + + +def test_set_restart_intent_binds_the_estate_spelling_in_the_checkout(box: Box): + """`~/…` through the estate link is W's file, and is bound as the + checkout's copy of it, never followed into W.""" + cell = _estate_link(box) + c = box.seam() + made = _bound(box, cell) + assert made.returncode == 0, made.stderr + assert box.intent_field("handoff") == str(c / HANDOFF_REL) + assert box.intent_field("digest") == sha(c / HANDOFF_REL) + + +def test_set_restart_intent_keeps_an_absolute_path_as_given(box: Box): + """Every older caller's spelling: an absolute path is stored exactly as + given, W's included — `lane-start` compares it with the file it resolves + in W until slice 2b, and the older `lane-handoff` publishes that file.""" + w_file = box.w / HANDOFF_REL + made = _bound(box, str(w_file)) + assert made.returncode == 0, made.stderr + assert box.intent_field("handoff") == str(w_file) + assert box.intent_field("digest") == sha(w_file) + assert "is bound as" not in made.stderr + + +@pytest.mark.parametrize("cell", ["notes/handoff.md", "handoffs/../lanes/LANES.md", + "handoffs/./repoS4/repoS4-1.md", "~user/handoff.md"]) +def test_set_restart_intent_refuses_any_other_relative_path(box: Box, cell: str): + """Only the row's own spellings are translated; any other relative path + is still refused (64) and nothing is written.""" + made = _bound(box, cell) + assert made.returncode == 64, made.stderr + assert "--handoff" in made.stderr + assert not (box.state / LANE / "restart-intent.yaml").exists() From 2e56bb408e5f37db2c6547f5814db402c917891c Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 15:31:36 +0000 Subject: [PATCH 06/34] MUST 1 (rv2a's pre-review of slice 2a): `write-handoff` counts a handoff 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 -- &&`) 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 (cherry picked from commit 6c2a9cc65b920a22fea3b4ca034af5290f1cb71f) --- lanes-edit.sh | 25 ++++++++++++++++--- tests/test_a23_write_handoff.py | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 86c05a4..571bddc 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -19176,7 +19176,9 @@ EOF # (`handoff_in_repo`, S2). One that still resolves into W, through a # link, refuses (S5, 2). One in ANOTHER REPOSITORY (Amendment 8(d)) is # written there and never committed. - # 3. S3 step 4 for the handoff's own path (`cs_own_pending`). + # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a + # handoff git does not track yet, which a commit would add and a lost + # race's replay would drop. # 4. THE READ FOLLOWS THE WRITE: the file in that checkout, and `--stamp`'s # splice (`lane-start`'s, unchanged: the line after the first blank line # after line 1), `--prepend`'s (`lane-handoff`'s: the block, `---` and a @@ -19291,9 +19293,25 @@ EOF fi [ "$whf_other" = 1 ] || path_under "$whf_phys" "$whf_c" || whf_other=1 whf_rel=""; [ "$whf_other" = 1 ] || whf_rel="${whf_phys#"$whf_c"/}" - # ---- 3. S3 step 4 for the handoff itself. + # ---- 3. S3 step 4 for the handoff itself: a change to it nobody committed, + # and a handoff git does not track yet (rv2a's pre-review of slice 2a, + # MUST 1). An untracked file — a new lane's first handoff, or a peer's — + # would be ADDED by this write's commit, under its subject; and where the + # push then lost a race, the replay's `reset --keep` dropped that commit + # and the file with it, and the re-run found nothing to write into (8). So + # it is a pending change like any other, and whoever made it adds and + # commits it as its own write. + whf_untracked=0 if [ "$whf_s3" = 0 ] && [ "$whf_other" = 0 ]; then ( cs_own_pending write-handoff "$lane" "$whf_rel" ) || whf_s3=$? + if [ "$whf_s3" = 0 ] && [ "$NO_GIT" != 1 ] && { [ -e "$whf_phys" ] || [ -L "$whf_phys" ]; } && + ! git --literal-pathspecs -C "$whf_c" -c "$CP_EXACT" ls-files --error-unmatch -- "$whf_rel" >/dev/null 2>&1; then + whf_untracked=1 + ( cs_refuse write-handoff "$whf_rel, this write's handoff, is not tracked yet: git has never had it (a new lane's first handoff, or a peer's), so this write's commit would add it under its own subject, and a replay after a lost race would drop that commit and the file with it (Amendment 23 S3 step 4)" \ + "read it: git -C $LANES_REPO status --short -- $whf_rel" \ + "whoever made it adds and commits it by pathspec, as its own write: git -C $LANES_REPO add -- $whf_rel && git -C $LANES_REPO commit -m \"()\" -- $whf_rel, then LANES_LANE= $SELF commit \"\" (the publish act)" \ + "never commit a file you did not make; then re-run this write" ) || whf_s3=$? + fi fi if [ "$whf_s3" != 0 ] && [ "$whf_mode" = stamp ]; then note "the stamp is NOT written: the checkout refused above (Amendment 23 S3), and a Rule 3 stamp the checkout cannot take is written by nobody, and never blocks a launch." @@ -19367,7 +19385,8 @@ EOF if [ "$whf_s3" != 0 ]; then note "the handoff is WRITTEN and NOT committed: the checkout refused above (Amendment 23 S3; R-A11-11 — a swap is never left unwritten, so the handoff is written, no commit is made, and git's own words are above). It is $HPA_TARGET." if [ "$whf_other" = 0 ]; then - note "once the refusal is answered, commit it by pathspec and publish it: git -C $whf_c commit -m \"$whf_subject\" -m \"Lane: $lane\" -- $whf_rel, then LANES_LANE=$lane $SELF commit \"\" (the publish act). Never write it again with --prepend: that puts a second block on top." + whf_add=""; [ "$whf_untracked" = 0 ] || whf_add="git -C $whf_c add -- $whf_rel && " + note "once the refusal is answered, commit it by pathspec and publish it: ${whf_add}git -C $whf_c commit -m \"$whf_subject\" -m \"Lane: $lane\" -- $whf_rel, then LANES_LANE=$lane $SELF commit \"\" (the publish act). Never write it again with --prepend: that puts a second block on top." fi exit "$whf_s3" fi diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 8f4fdd0..b3341e1 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -904,3 +904,47 @@ def test_set_restart_intent_refuses_any_other_relative_path(box: Box, cell: str) assert made.returncode == 64, made.stderr assert "--handoff" in made.stderr assert not (box.state / LANE / "restart-intent.yaml").exists() + + +# ------------------------------------------------- rv2a's pre-review of slice 2a + +#: A handoff the checkout has and git has never tracked: a new lane's first, or +#: a peer's. +NEW_REL = "handoffs/repoS4/new-untracked.md" + + +@pytest.mark.parametrize("mode", ["stamp", "prepend", "whole"]) +def test_an_untracked_handoff_is_refused_and_never_dropped(box: Box, mode: str): + """MUST 1: the row names a handoff the checkout has and git has never + tracked, and a peer's commit lands at the push. This write's commit would + add the file under its own subject, and the lost race's replay dropped that + commit and the file with it (8, "Nothing was written"). S3 step 4 counts it + as a pending change: refused (2) before any commit, `--stamp` writing + nothing and `--prepend`/`--whole` writing it, uncommitted (R-A11-11). The + file is still there in every mode, still untracked, and nothing is pushed.""" + c = box.seam() + box.cell_is(NEW_REL) + f = c / NEW_REL + f.write_text(HANDOFF, encoding="utf-8") + box.race() + if mode == "stamp": + args, after = ("--stamp", STAMP), HANDOFF + elif mode == "prepend": + args, after = ("--prepend", str(_block(box))), prepended(HANDOFF) + else: + args, after = ("--whole", str(_prepared(box)), "--expect-digest", sha(f)), "# the handoff, written whole\n" + result = box.write_handoff(*args, "--subject", "handoff: PAUSED") + assert result.returncode == 2, result.stderr + assert f"{NEW_REL}, this write's handoff, is not tracked yet" in result.stderr + assert f.read_text(encoding="utf-8") == after + assert box.git("-C", str(c), "status", "--porcelain", "--", NEW_REL).split() == ["??", NEW_REL] + assert box.head(c) == box.origin_head(), "S3's fast-forward, and no commit of this write" + assert "push" not in box.net_ops() + assert NEW_REL not in box.git("--git-dir", str(box.origin), "ls-tree", "-r", "--name-only", "main").split() + if mode == "stamp": + assert result.stdout == "" + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + else: + assert result.stdout == _line(f) + assert f"git -C {c} add -- {NEW_REL} && git -C {c} commit" in result.stderr + _released(box, c) From 9fb46cdf21a447765ca6eef4b5081cdceef5417a Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 16:17:42 +0000 Subject: [PATCH 07/34] SHOULD 1 (rv2a's pre-review of slice 2a): `write-handoff` writes a file 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) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `write-handoff` took `--target` as given and turned any path inside the checkout into the commit's pathspec. `--whole --expect-digest --target lanes/LANES.md` replaced the register and pushed it (exit 0), and a row cell `handoffs/../lanes/log/.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//`, 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 in the checkout , 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 (cherry picked from commit 011f241b4e83de8660b2e2abbfbdaa1ee7718eab) --- lanes-edit.sh | 30 ++++++++++++++++++++++++++---- tests/test_a23_write_handoff.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 571bddc..b0812ef 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -479,7 +479,8 @@ # for every writer but `commit`), or a fast-forward git refused; the # withdrawn `--sweep`; and the publish act over a stack origin has moved # past (S4, Open question 2); and — slice 2a — `write-handoff`'s handoff -# that resolves into W through a link (S5), a restart's bound file or +# that resolves into W through a link (S5), a target in the checkout that +# is not a file under `handoffs/` (S2), a restart's bound file or # pre-image that is not this one, or a splice that does not prove out, # each with nothing written; while S3's refusal of a `--prepend` or a # `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed @@ -19174,8 +19175,10 @@ EOF # after S3's fetch — or `--target`, the old callers' spelling — and # TRANSLATED into the checkout this write goes through, never followed # (`handoff_in_repo`, S2). One that still resolves into W, through a - # link, refuses (S5, 2). One in ANOTHER REPOSITORY (Amendment 8(d)) is - # written there and never committed. + # link, refuses (S5, 2), and so does one in the checkout that is not a + # file under `handoffs/` (S2: never the register or `lanes/`). One in + # ANOTHER REPOSITORY (Amendment 8(d)) is written there and never + # committed. # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -19198,7 +19201,8 @@ EOF # STDOUT: `` once the file is written (`HANDOFF_OUT`). # 0 pushed, or nothing to commit (another repository's file; LANES_NO_GIT) # 1 environment: a checkout that cannot be entered, an unreadable file - # 2 refused: S3 (step 6), S5, a restart's bound file or pre-image, a proof + # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2), a + # restart's bound file or pre-image, a proof # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named # 7 a restart fence, or the handoff changed under the write # 8 no handoff to write: the row names none, or its file is not there @@ -19293,6 +19297,24 @@ EOF fi [ "$whf_other" = 1 ] || path_under "$whf_phys" "$whf_c" || whf_other=1 whf_rel=""; [ "$whf_other" = 1 ] || whf_rel="${whf_phys#"$whf_c"/}" + # ONLY A HANDOFF (rv2a's pre-review of slice 2a, SHOULD 1). This verb + # replaces a file whole and pushes it, so in this checkout it writes a file + # under `handoffs/` and nothing else: never the register, an object log or + # anything else under `lanes/`, which Rule 9's one-line guard and the logs' + # append-only rule (Amendment 7(b)) protect and this write would bypass + # (Amendment 23 S2 scopes it to `handoffs//`). `whf_rel` is + # the PHYSICAL path's, so a `--target`, a cell that climbs out through + # `..`, and a link out of `handoffs/` are all caught here, before a byte. + if [ "$whf_other" = 0 ]; then + whf_scope=1 + case "$whf_rel" in handoffs/?*) whf_scope=0 ;; esac + case "$whf_rel" in "$LANES_PATH"|lanes/*|*/.|*/..) whf_scope=1 ;; esac + if [ -n "$LANES_PREFIX" ]; then case "$whf_rel" in "$LANES_PREFIX"*) whf_scope=1 ;; esac; fi + if [ "$whf_scope" = 1 ]; then + whf_stamp_said + die "lane $lane's handoff target $whf_path is $whf_rel in the checkout $whf_c, which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never the register ($LANES_PATH) or anything under lanes/ (Amendment 23 S2). Nothing was written." 2 + fi + fi # ---- 3. S3 step 4 for the handoff itself: a change to it nobody committed, # and a handoff git does not track yet (rv2a's pre-review of slice 2a, # MUST 1). An untracked file — a new lane's first handoff, or a peer's — diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index b3341e1..f39a197 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -948,3 +948,31 @@ def test_an_untracked_handoff_is_refused_and_never_dropped(box: Box, mode: str): assert result.stdout == _line(f) assert f"git -C {c} add -- {NEW_REL} && git -C {c} commit" in result.stderr _released(box, c) + + +@pytest.mark.parametrize("how", ["target", "cell"]) +def test_a_target_outside_handoffs_is_refused_and_nothing_written(box: Box, how: str): + """SHOULD 1: this verb replaces a file and pushes it, so in the checkout + it writes a file under `handoffs/` and nothing else. A `--target` naming + the register, or a row cell that climbs out of `handoffs/` to the lane's + append-only object log, is refused (2) before a byte: the checkout's copy + and origin's are as they were, and nothing is printed on stdout.""" + c = box.seam() + if how == "target": + rel = REGISTER + args = ("--whole", str(_prepared(box, "# not a register any more\n")), + "--expect-digest", sha(c / REGISTER), "--target", REGISTER) + else: + rel = f"lanes/log/{LANE}.md" + box.cell_is(f"handoffs/../{rel}") + args = ("--prepend", str(_block(box))) + before, published, tip = (c / rel).read_bytes(), box.origin_file(rel), box.origin_head() + result = box.write_handoff(*args, "--subject", "handoff: PAUSED") + assert result.returncode == 2, result.stderr + assert f"is {rel} in the checkout {c}, which is not a file under handoffs/" in result.stderr + assert "Nothing was written." in result.stderr + assert (c / rel).read_bytes() == before + assert box.origin_file(rel) == published + assert box.origin_head() == tip + assert result.stdout == "" + _released(box, c) From 2ae7336a88f8e0248a482730120a6ebcd7b69a35 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 16:30:49 +0000 Subject: [PATCH 08/34] SHOULD 3 (rv2a's pre-review of slice 2a): `write-handoff` asks the managed 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 (cherry picked from commit 7561d5a08bcc8d96150955d27012fbc5e085683f) --- lanes-edit.sh | 23 ++++++++------ tests/test_a23_write_handoff.py | 53 +++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index b0812ef..40f3152 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -19170,7 +19170,8 @@ EOF # caller holds a lock or commits a handoff by hand (plan §2.7; Open question # 1's reading: the lock's holder is this process). In order: # 1. THE CHECKOUT'S LOCK, then S3 (`checkout_sync`), run in a subshell so - # that its refusal is this arm's to answer (step 6). + # that its refusal is this arm's to answer (step 6); then the managed + # seam asked again on what S3 fetched, whatever S3 answered. # 2. THE TARGET: the row's handoff cell in the published register, read # after S3's fetch — or `--target`, the old callers' spelling — and # TRANSLATED into the checkout this write goes through, never followed @@ -19275,14 +19276,18 @@ EOF # ---- 1. S3, answered here. whf_s3=0; whf_cs="" whf_cs="$(checkout_sync write-handoff "$lane" write && printf 'ok%s' "$CS_NO_ORIGIN_BRANCH")" || whf_s3=$? - if [ "$whf_s3" = 0 ]; then - CS_NO_ORIGIN_BRANCH="${whf_cs##*ok}"; cs_flush; LOG_SYNC_FETCH=yes - # THE SEAM ASKED AGAIN, ON WHAT S3 JUST FETCHED (the pre-review of 1b, - # MUST 1's shape): the ask before the lock read this checkout's - # origin/$LANES_BRANCH as it last stood, and a managed-owner marker that - # landed since would otherwise go unread. A refusal here writes nothing. - managed_seam_refuse "$lane" "write-handoff" - fi + if [ "$whf_s3" = 0 ]; then CS_NO_ORIGIN_BRANCH="${whf_cs##*ok}"; LOG_SYNC_FETCH=yes; fi + # THE SEAM ASKED AGAIN, ON WHAT S3 JUST FETCHED (the pre-review of 1b, + # MUST 1's shape): the ask before the lock read this checkout's + # origin/$LANES_BRANCH as it last stood, and a managed-owner marker that + # landed since would otherwise go unread. WHATEVER S3 ANSWERED (rv2a's + # pre-review of slice 2a, SHOULD 3): S3 can refuse AFTER its fetch — a + # staged entry, a diverged branch — and `--prepend` and `--whole` still + # write there (step 6), so the marker that fetch brought is read before + # them too. Where S3 refused before any fetch, this reads what the first + # ask read. A refusal here writes nothing. + cs_flush + managed_seam_refuse "$lane" "write-handoff" # ---- 2. the target, translated. if [ -n "$whf_target" ]; then whf_cell="$whf_target"; else whf_cell="$(handoff_of_lane "$lane" 2>/dev/null || :)"; fi if [ -z "$whf_cell" ]; then diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index f39a197..b7199a8 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -976,3 +976,56 @@ def test_a_target_outside_handoffs_is_refused_and_nothing_written(box: Box, how: assert box.origin_head() == tip assert result.stdout == "" _released(box, c) + + +def _managed_row(box: Box) -> tuple: + """The lane's row as a peer republishes it, with a managed-owner marker + that parses, so the seam's answer is "owned", not "unknown".""" + return (f"| handoffs/repoS4/{LANE}.md | LIVE · seeded |", + f"| handoffs/repoS4/{LANE}.md | LIVE · 2026-10-21T00:00:00Z · managed-owner mode=managed " + f"daemon=ledger-a23 generation=3 bound-lane={LANE} |") + + +def test_a_managed_marker_landing_at_S3s_fetch_refuses_even_where_S3_refused(box: Box): + """SHOULD 3: the seam is asked again on what S3 fetched WHATEVER S3 + answered. A peer's managed-owner marker lands at S3's fetch, and S3 then + refuses (a foreign staged entry): a `--prepend`, which S3's refusal leaves + written (R-A11-11), is refused by the managed ledger instead, with nothing + written, nothing printed, and the staged entry left as it was.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(REGISTER, *_managed_row(box), "a peer's row names a managed owner") + peer = shlex.quote(str(box.peer)) + count = shlex.quote(str(box.count)) + box.use_net('[ "$1" = fetch ] || exit 0\n' + f'[ -e {count} ] && exit 0\n' + f"echo 1 > {count}\n" + f"git -C {peer} push -q origin main\n" + "exit 0\n") + _staged_foreign(box, c) + result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED") + assert result.returncode == 2, result.stderr + assert "is owned by the managed ledger (owner ledger-a23" in result.stderr + assert "the handoff is WRITTEN" not in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF, "a managed lane's handoff is never written" + assert "daemon=ledger-a23" in box.origin_register(), "the peer's row landed at S3's fetch" + assert result.stdout == "" + assert box.git("-C", str(c), "diff", "--cached", "--name-only").split() == ["handoffs/README.md"] + _released(box, c) + + +def test_a_managed_marker_already_fetched_is_refused_before_the_lock(box: Box): + """The FIRST ask, before the lock: a marker this checkout's origin/main + already carries refuses the write before S3 runs, so no fetch is made and + nothing is written.""" + box.peer_edits(REGISTER, *_managed_row(box), "a peer's row names a managed owner") + c = box.seam() + f = c / HANDOFF_REL + box.use_net() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 2, result.stderr + assert "is owned by the managed ledger (owner ledger-a23" in result.stderr + assert box.net_ops() == [], "refused before the lock: S3's fetch never ran" + assert f.read_text(encoding="utf-8") == HANDOFF + assert result.stdout == "" + _released(box, c) From b643ae0b17462a091025078dd0b877663ec98f4d Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 16:39:32 +0000 Subject: [PATCH 09/34] SHOULD 2 (rv2a's pre-review of slice 2a): S3's refusal of the register 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) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `write-handoff` runs S3 before it knows the target, and a refusal then stopped every mode: `--stamp --target ` 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: 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 (cherry picked from commit 346e403fb28b481201c2812b67f06f687c902cae) --- lanes-edit.sh | 15 +++++++++++++-- tests/test_a23_write_handoff.py | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 40f3152..7cae85b 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -19179,7 +19179,7 @@ EOF # link, refuses (S5, 2), and so does one in the checkout that is not a # file under `handoffs/` (S2: never the register or `lanes/`). One in # ANOTHER REPOSITORY (Amendment 8(d)) is written there and never - # committed. + # committed, and S3's refusal of this checkout does not stop it. # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -19200,7 +19200,8 @@ EOF # origin's new tip — a `--whole` only where origin's copy is still the # one it read (S4.4). # STDOUT: `` once the file is written (`HANDOFF_OUT`). - # 0 pushed, or nothing to commit (another repository's file; LANES_NO_GIT) + # 0 pushed, or nothing to commit (another repository's file, S3's refusal + # of this checkout or not; LANES_NO_GIT) # 1 environment: a checkout that cannot be entered, an unreadable file # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2), a # restart's bound file or pre-image, a proof @@ -19320,6 +19321,16 @@ EOF die "lane $lane's handoff target $whf_path is $whf_rel in the checkout $whf_c, which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never the register ($LANES_PATH) or anything under lanes/ (Amendment 23 S2). Nothing was written." 2 fi fi + # ANOTHER REPOSITORY'S HANDOFF IS NOT THIS CHECKOUT'S WRITE (rv2a's + # pre-review of slice 2a, SHOULD 2). It is written there and committed + # nowhere (Amendment 8(d)), so S3's refusal of the register checkout — an + # offline lane's fetch, a staged entry — does not stop it, as the old + # `publish-handoff` never fetched for it. The managed seam (asked above), + # the restart fences and the digests still apply. + if [ "$whf_other" = 1 ] && [ "$whf_s3" != 0 ]; then + note "the refusal above is this checkout's (Amendment 23 S3) and does not stop this write: $whf_phys is another repository's handoff (Amendment 8(d)), which no tool here commits" + whf_s3=0 + fi # ---- 3. S3 step 4 for the handoff itself: a change to it nobody committed, # and a handoff git does not track yet (rv2a's pre-review of slice 2a, # MUST 1). An untracked file — a new lane's first handoff, or a peer's — diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index b7199a8..0374248 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1029,3 +1029,30 @@ def test_a_managed_marker_already_fetched_is_refused_before_the_lock(box: Box): assert f.read_text(encoding="utf-8") == HANDOFF assert result.stdout == "" _released(box, c) + + +@pytest.mark.parametrize("mode", ["stamp", "prepend"]) +def test_another_repositorys_handoff_is_written_when_S3_refuses_this_checkout(box: Box, mode: str): + """SHOULD 2: a handoff in another repository is never committed + (Amendment 8(d)), so S3's refusal of the REGISTER checkout — here no + fetch can run, `LANES_NO_FETCH=1` — does not stop its write, as the old + `publish-handoff` never fetched for it. Written, said, its line printed, + exit 0; the register checkout and origin are as they were.""" + c = box.seam() + other = box.root / "otherrepo" / "h.md" + other.parent.mkdir(parents=True) + other.write_text(HANDOFF, encoding="utf-8") + args, after = (("--stamp", STAMP), stamped(HANDOFF)) if mode == "stamp" else \ + (("--prepend", str(_block(box))), prepended(HANDOFF)) + before = box.snapshot(c) + result = box.write_handoff(*args, "--subject", SUBJECT, "--target", str(other), LANES_NO_FETCH="1") + assert result.returncode == 0, result.stderr + assert "LANES_NO_FETCH=1 is set" in result.stderr + assert f"does not stop this write: {other} is another repository's handoff (Amendment 8(d))" in result.stderr + assert "another repository's handoff (Amendment 8(d)): it is written there" in result.stderr + assert "the stamp is NOT written" not in result.stderr + assert "the handoff is WRITTEN and NOT committed" not in result.stderr + assert other.read_text(encoding="utf-8") == after + assert result.stdout == _line(other) + assert box.snapshot(c) == before + _released(box, c) From 2978d69daa50d04cdc4a41230927be11b2b652d8 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 18:19:49 +0000 Subject: [PATCH 10/34] SHOULD 4 (rv2a's pre-review of slice 2a): `write-handoff`'s tests now 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 (cherry picked from commit 3cd2162f7553410a3f7b8f750d07f7e05d52caf7) --- tests/test_a23_write_handoff.py | 170 ++++++++++++++++++++++++++------ 1 file changed, 142 insertions(+), 28 deletions(-) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 0374248..1ab82b4 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -170,7 +170,8 @@ def test_publish_handoff_prints_the_physical_file_and_keeps_its_mode(box: Box): assert stat.S_IMODE(real.stat().st_mode) == 0o640 assert link.is_symlink() _no_temporary_beside(real) - assert box.head(c) == head and box.origin_head() == tip, "publish-handoff commits nothing" + assert box.head(c) == head, "publish-handoff commits nothing" + assert box.origin_head() == tip, "publish-handoff pushes nothing" def test_publish_handoff_refuses_a_handoff_that_changed_exit_7(box: Box): @@ -268,11 +269,13 @@ def test_stamp_spliced_committed_pushed_with_lane_trailer(box: Box): assert f.read_text(encoding="utf-8") == stamped(HANDOFF) assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) msg = box.last_message() - assert msg[0] == SUBJECT and f"Lane: {LANE}" in msg, msg + assert msg[0] == SUBJECT, msg + assert f"Lane: {LANE}" in msg, msg assert box.git("--git-dir", str(box.origin), "show", "--name-only", "--format=", "main").split() == [HANDOFF_REL] assert result.stdout == _line(f) assert "pushed origin/main" in result.stderr - assert box.head(c) == box.origin_head() and box.unpublished(c) == [] + assert box.head(c) == box.origin_head() + assert box.unpublished(c) == [] _released(box, c) @@ -292,7 +295,8 @@ def test_stamp_refused_by_S3_writes_nothing_prints_line(box: Box, row: str): before = box.snapshot(c) result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, **extra) assert result.returncode == 2, result.stderr - assert words in result.stderr and "Nothing was written." in result.stderr + assert words in result.stderr + assert "Nothing was written." in result.stderr assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr assert result.stdout == "" assert box.snapshot(c) == before @@ -318,11 +322,13 @@ def test_stamp_replays_on_new_content(box: Box, peer: str): assert result.returncode == 0, result.stderr assert "push refused (attempt 1 of 6)" in result.stderr published = box.origin_file(HANDOFF_REL) - assert published == stamped(base) and published.count(STAMP) == 1 + assert published == stamped(base) + assert published.count(STAMP) == 1 f = c / HANDOFF_REL assert result.stdout == _line(f), "one line: the last attempt's" assert box.subjects()[0] == SUBJECT - assert box.head(c) == box.origin_head() and box.unpublished(c) == [] + assert box.head(c) == box.origin_head() + assert box.unpublished(c) == [] _released(box, c) @@ -336,9 +342,11 @@ def test_stamp_that_does_not_prove_out_writes_nothing(box: Box): tip = box.origin_head() result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) assert result.returncode == 2, result.stderr - assert "did not prove out" in result.stderr and STAMP in result.stderr + assert "did not prove out" in result.stderr + assert STAMP in result.stderr assert (c / HANDOFF_REL).read_text(encoding="utf-8") == "one line and no newline" - assert box.origin_head() == tip and result.stdout == "" + assert box.origin_head() == tip + assert result.stdout == "" _released(box, c) @@ -355,7 +363,8 @@ def test_stamp_over_an_uncommitted_change_to_the_handoff_is_refused(box: Box): assert result.returncode == 2, result.stderr assert f"these paths this write commits hold an uncommitted change: {HANDOFF_REL}" in result.stderr assert f.read_text(encoding="utf-8") == edited - assert box.origin_head() == tip and result.stdout == "" + assert box.origin_head() == tip + assert result.stdout == "" _released(box, c) @@ -378,31 +387,49 @@ def test_prepend_written_committed(box: Box): assert f.read_text(encoding="utf-8") == prepended(HANDOFF) assert box.origin_file(HANDOFF_REL) == prepended(HANDOFF) msg = box.last_message() - assert msg[0] == subject and f"Lane: {LANE}" in msg + assert msg[0] == subject + assert f"Lane: {LANE}" in msg assert result.stdout == _line(f) assert box.unpublished(c) == [] _released(box, c) -def test_prepend_refused_by_S3_still_writes_file_no_commit(box: Box): - """R-A11-11: a swap is never left unwritten. S3 refuses the commit (a - foreign staged entry), the handoff is written all the same, nothing is - committed or pushed, S3's words are printed, and so is the path (2).""" +def _written_uncommitted_under_S3(box: Box, mode: str) -> None: + """S3 refuses the commit (a foreign staged entry); the handoff is written + all the same, nothing is committed or pushed, S3's words are printed, and + so is the path (2).""" c = box.seam() f = c / HANDOFF_REL + if mode == "prepend": + args, after = ("--prepend", str(_block(box))), prepended(HANDOFF) + else: + args, after = ("--whole", str(_prepared(box)), "--expect-digest", sha(f)), "# the handoff, written whole\n" _staged_foreign(box, c) head, tip = box.head(c), box.origin_head() - result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED") + result = box.write_handoff(*args, "--subject", "handoff: PAUSED") assert result.returncode == 2, result.stderr assert "staged entries this write did not make: handoffs/README.md" in result.stderr assert "the handoff is WRITTEN and NOT committed" in result.stderr - assert f.read_text(encoding="utf-8") == prepended(HANDOFF) + assert f.read_text(encoding="utf-8") == after assert result.stdout == _line(f) - assert box.head(c) == head and box.origin_head() == tip + assert box.head(c) == head + assert box.origin_head() == tip assert box.git("-C", str(c), "diff", "--cached", "--name-only").split() == ["handoffs/README.md"] _released(box, c) +def test_prepend_refused_by_S3_still_writes_file_no_commit(box: Box): + """R-A11-11: a swap is never left unwritten, so S3's refusal leaves the + new top block written and uncommitted.""" + _written_uncommitted_under_S3(box, "prepend") + + +def test_whole_refused_by_S3_still_writes_file_no_commit(box: Box): + """The same for the skill's whole-file write (R-A11-11's path): the + prepared handoff is written, uncommitted, with S3's words and the path.""" + _written_uncommitted_under_S3(box, "whole") + + # ------------------------------------------------- --whole def _prepared(box: Box, text: str = "# the handoff, written whole\n") -> Path: @@ -419,7 +446,8 @@ def test_whole_expect_digest_mismatch_exit_7_nothing_published(box: Box): assert result.returncode == 7, result.stderr assert "the handoff changed before publication; nothing was published" in result.stderr assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF - assert result.stdout == "" and box.origin_head() == tip + assert result.stdout == "" + assert box.origin_head() == tip _released(box, c) @@ -524,7 +552,8 @@ def test_cell_in_another_repository_is_published_not_committed(box: Box): assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) assert box.git("-C", str(other), "rev-parse", "HEAD").strip() == other_head assert box.git("-C", str(other), "status", "--porcelain").split() == ["M", "handoff.md"] - assert box.head(c) == head and box.origin_head() == tip + assert box.head(c) == head + assert box.origin_head() == tip assert result.stdout == _line(other / "handoff.md") _released(box, c) @@ -594,7 +623,8 @@ def test_restart_fence_refusals_write_nothing(box: Box, case: str): assert result.returncode == code, result.stderr assert words in result.stderr assert f.read_text(encoding="utf-8") == HANDOFF - assert result.stdout == "" and box.origin_head() == tip + assert result.stdout == "" + assert box.origin_head() == tip assert box.intent_field("prepared_digest") == "none", "nothing was reserved" _released(box, c) @@ -715,9 +745,11 @@ def test_W_untouched(box: Box): assert whole.returncode == 0, whole.stderr assert box.origin_file(HANDOFF_REL) == "# the handoff, written whole\n" assert box.subjects()[:3] == ["handoff: whole", "handoff: PAUSED", SUBJECT] - assert box.w_state() == w_before and box.w_reflog() == reflog + assert box.w_state() == w_before + assert box.w_reflog() == reflog assert (box.w / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF - assert box.head(c) == box.origin_head() and box.unpublished(c) == [] + assert box.head(c) == box.origin_head() + assert box.unpublished(c) == [] _released(box, c) @@ -737,11 +769,13 @@ def test_handoff_path_names_where_write_handoff_writes(tmp_path: Path, how: str) assert not c.exists(), "a read makes no checkout" made = box.helper("handoff-path", LANE, "--create") assert made.returncode == 0, made.stderr - assert made.stdout == f"{c / HANDOFF_REL}\n" and (c / HANDOFF_REL).is_file() + assert made.stdout == f"{c / HANDOFF_REL}\n" + assert (c / HANDOFF_REL).is_file() for argv in (("repoS4-9",), ("repoS4-9", "--create")): none = box.helper("handoff-path", *argv) assert none.returncode == 8, none.stderr - assert "no row for it in the register" in none.stderr and none.stdout == "" + assert "no row for it in the register" in none.stderr + assert none.stdout == "" assert not box.checkout("repoS4-9").exists(), "a lane with no row gets no checkout" @@ -763,7 +797,8 @@ def test_usage_is_64_and_writes_nothing(box: Box, argv): before = box.snapshot(c) result = box.write_handoff(*argv) assert result.returncode == 64, result.stderr - assert box.snapshot(c) == before and box.net_ops() == [] + assert box.snapshot(c) == before + assert box.net_ops() == [] def test_write_handoff_for_a_lane_with_no_row_is_8(box: Box): @@ -775,7 +810,8 @@ def test_write_handoff_for_a_lane_with_no_row_is_8(box: Box): assert result.returncode == 8, result.stderr assert "row names no handoff, so there is none to write" in result.stderr assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr - assert result.stdout == "" and box.snapshot(c) == before + assert result.stdout == "" + assert box.snapshot(c) == before _released(box, c) @@ -812,7 +848,8 @@ def test_a_managed_marker_landing_inside_the_s3_window_is_refused(box: Box): assert "managed ledger" in result.stderr assert f.read_text(encoding="utf-8") == HANDOFF, "nothing written" assert "managed-owner: daemon-a23" in box.origin_register(), "the peer's row landed" - assert box.origin_file(HANDOFF_REL) == HANDOFF and result.stdout == "" + assert box.origin_file(HANDOFF_REL) == HANDOFF + assert result.stdout == "" assert box.net_ops()[0] == "fetch", "the marker landed at S3's fetch, after the first ask" _released(box, c) @@ -834,7 +871,9 @@ def test_routed_write_handoff_for_a_lane_with_no_row_clones_nothing(box: Box, fe assert "LANES_NO_FETCH=1 is set" in result.stderr assert "was not cloned" in result.stderr assert not box.checkout("repoS4-9").exists(), "nothing is cloned for a lane with no row" - assert box.w_state() == w_before and box.origin_head() == tip and result.stdout == "" + assert box.w_state() == w_before + assert box.origin_head() == tip + assert result.stdout == "" # ------------------------------------------------- set-restart-intent --handoff @@ -1056,3 +1095,78 @@ def test_another_repositorys_handoff_is_written_when_S3_refuses_this_checkout(bo assert result.stdout == _line(other) assert box.snapshot(c) == before _released(box, c) + + +def test_target_names_the_file_written(box: Box): + """`--target`, the old callers' spelling, is the file written: a tracked + handoff that is not the row's is stamped, committed and pushed, and the + row's own handoff is left as it was, in the checkout and on origin.""" + rel = "handoffs/repoS4/old-callers.md" + box.peer_pushes(rel, HANDOFF) + c = box.seam() + f = c / rel + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--target", str(f)) + assert result.returncode == 0, result.stderr + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.origin_file(rel) == stamped(HANDOFF) + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + assert box.origin_file(HANDOFF_REL) == HANDOFF + assert result.stdout == _line(f) + _released(box, c) + + +def test_with_no_workspace_resolved_an_absolute_target_is_the_checkouts_own(tmp_path: Path): + """A5's rule for an unresolved W: with no workspace checkout, an absolute + path is only ever the checkout's own or another repository's — an empty W + would read every absolute path as inside it and strip its leading `/`. + Through the seam, a `--target` spelled absolute is written there.""" + box = Box(tmp_path, route=False) + (box.home / ".agents" / "workspace.yaml").write_text( + f"repository: {box.origin}\npath: {box.root / 'no-such-workspace'}\n", encoding="utf-8") + c = box.seam() + f = c / HANDOFF_REL + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--target", str(f)) + assert result.returncode == 0, result.stderr + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) + assert result.stdout == _line(f) + _released(box, c) + + +def test_a_push_that_cannot_be_confirmed_is_3_and_its_line_printed(box: Box): + """S4's read-back: the push lands and the fetch that would confirm it + fails. 3, `pushed, not confirmed: `, never the success line; the + handoff is written, its line printed (A13), and origin does hold it.""" + c = box.seam() + f = c / HANDOFF_REL + mark = shlex.quote(str(box.root / "pushed")) + box.use_net(f'[ "$1" = push ] && {{ touch {mark}; exit 0; }}\n' + f'[ "$1" = fetch ] && [ -e {mark} ] && {{ echo "fatal: no read-back" >&2; exit 128; }}\n' + "exit 0\n") + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 3, result.stderr + sent = box.head(c) + assert f"pushed, not confirmed: {sent}" in result.stderr + assert "pushed origin/main (attempt" not in result.stderr, "never the success line" + assert box.origin_head() == sent, "it did land: 3 never says it did not" + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert result.stdout == _line(f) + _released(box, c) + + +def test_with_LANES_NO_GIT_the_handoff_is_written_and_nothing_committed(box: Box): + """`LANES_NO_GIT=1`: the handoff is written and proven as ever, and no git + act follows — no fetch, no commit, no push. 0, said, its line printed.""" + c = box.seam() + f = c / HANDOFF_REL + box.use_net() + head, tip = box.head(c), box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, LANES_NO_GIT="1") + assert result.returncode == 0, result.stderr + assert "LANES_NO_GIT=1 — not committing" in result.stderr + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert result.stdout == _line(f) + assert box.head(c) == head + assert box.origin_head() == tip + assert box.net_ops() == [] + _released(box, c) From 0d7e64da25fadbd0af26c1c5b1cf88320d53790a Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 21:32:33 +0000 Subject: [PATCH 11/34] NITs 1 and 3 (rv2a's pre-review of slice 2a): `write-handoff` and `handoff-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: ` (#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: ` and pushed it. Now both call the new `handoff_cell_of `, `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 (cherry picked from commit 96bf46471009a0cc00a58912e15ade4142f68be6) --- lanes-edit.sh | 57 +++++++++++++++++++++++++++------ tests/test_a23_write_handoff.py | 36 ++++++++++++++++++++- 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 7cae85b..1f2b5c2 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -453,7 +453,9 @@ # 1 environment (no register, no writer) — and, AMENDMENT 23 S1, a lane # checkout's path that holds something other than a clone of the register # (every routed write, and `lane-checkout`), which is refused and never -# deleted, moved or repaired; `refresh-workspace` with no W +# deleted, moved or repaired; `refresh-workspace` with no W; +# `handoff-path` and `write-handoff`, a register that could not be read +# (R22: never 8) # 2 refusal: bad arguments, the object is held, an unknown alias, — # Amendment 15 — a register holding # two rows whose lane names differ only by case, which every writer and @@ -565,7 +567,8 @@ # `history` READ the lane's log and it carries no `NOTED` or `RULED` line; # `window-session` found no live session in the window; `lane-checkout` # found no checkout for the lane (Amendment 23); `handoff-path` and -# `write-handoff` found no handoff for the lane — its row names none, or +# `write-handoff` found no handoff for the lane — there is no row for it +# (`write-handoff` with `--target` too), its row names none, or # (`write-handoff`) the file is not there — and wrote nothing. A read that could # not be performed is never 8 (R22). `session-start` never exits 8, or # anything but 0: it is a hook. `guard` never exits 8 either — it is a hook @@ -6011,6 +6014,19 @@ handoff_of_lane() { row_cell "$hl_row" 7 | sed 's/^ *//; s/ *$//' } +# THE SAME CELL, AND WHETHER IT WAS READ (rv2a's pre-review of slice 2a, NITs +# 1 and 3), for `write-handoff` and `handoff-path`: 0 with the row's handoff +# cell on stdout (empty where the row names none); 8 where the register WAS +# read and has no row for the lane; 1 where it could not be read. The read +# above answers a failed read as an empty cell, which its callers take for "no +# handoff"; a read that failed is never that (R22). +handoff_cell_of() { # + hcl_reg="$(register_text_checked 2>/dev/null)" || return 1 + hcl_row="$(printf '%s\n' "$hcl_reg" | awk -v lane="$1" "$ROW_AWK")" || return 1 + [ -n "$hcl_row" ] || return 8 + row_cell "$hcl_row" 7 | sed 's/^ *//; s/ *$//' +} + short_ws() { printf '%s' "${1%%.*}" | tr 'A-Z' 'a-z'; } # Column 3 of the row is ` / / `. A lane recorded on @@ -19202,12 +19218,14 @@ EOF # STDOUT: `` once the file is written (`HANDOFF_OUT`). # 0 pushed, or nothing to commit (another repository's file, S3's refusal # of this checkout or not; LANES_NO_GIT) - # 1 environment: a checkout that cannot be entered, an unreadable file + # 1 environment: a checkout that cannot be entered, an unreadable file, + # a register that could not be read (R22: never 8) # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2), a # restart's bound file or pre-image, a proof # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named # 7 a restart fence, or the handoff changed under the write - # 8 no handoff to write: the row names none, or its file is not there + # 8 no handoff to write: no row for the lane (with `--target` too), the + # row names none, or its file is not there # 64 usage write-handoff) whf_use="usage: write-handoff (--stamp \"\" | --prepend | --whole --expect-digest ) --subject \"\" [--target ] [--restart [--expected-digest ]]" @@ -19289,8 +19307,22 @@ EOF # ask read. A refusal here writes nothing. cs_flush managed_seam_refuse "$lane" "write-handoff" - # ---- 2. the target, translated. - if [ -n "$whf_target" ]; then whf_cell="$whf_target"; else whf_cell="$(handoff_of_lane "$lane" 2>/dev/null || :)"; fi + # ---- 2. the target, translated. THE ROW FIRST, WHATEVER `--target` + # SAYS (rv2a's pre-review of slice 2a, NITs 1 and 3): a lane with no row + # has no handoff to write, and no commit may carry `Lane: ` — through + # the seam and in default mode too, where no `lane_row_gate` asked before + # the lock — and a register that could not be read is 1, never "no + # handoff" (R22). The seam above refuses an unreadable register first (1), + # so that 1 is this read's own only where the register failed between them. + whf_hrc=0; whf_cell="$(handoff_cell_of "$lane")" || whf_hrc=$? + case "$whf_hrc" in + 0) : ;; + 8) whf_stamp_said + die "lane $lane has no row in the register, so it has no handoff to write, and no commit may carry \`Lane: $lane\`. Nothing was written." 8 ;; + *) whf_stamp_said + die "the register could not be read, so whether lane $lane's row names a handoff cannot be said — a read that failed is never 'no handoff' (R22). Nothing was written." 1 ;; + esac + if [ -n "$whf_target" ]; then whf_cell="$whf_target"; fi if [ -z "$whf_cell" ]; then whf_stamp_said die "lane $lane's row names no handoff, so there is none to write. Nothing was written." 8 @@ -19449,7 +19481,8 @@ EOF # Not routed: it writes nothing. # 0 the path (another repository's too, said on stderr) · 8 no row, or a # row that names no handoff, and nothing cloned · 1 a checkout there that - # is not a clone · 2 a name refused, + # is not a clone, or a register that could not be read (R22: never 8) · + # 2 a name refused, # or the clone failed · 4 the creation lock · 64 usage handoff-path) hp_lane=""; hp_create=0 @@ -19469,9 +19502,15 @@ EOF check_lane_name "$hp_lane" # THE CELL FIRST: a lane with no row, or a row with no handoff, has nothing # to write, and `--create` clones nothing for it (R4-1's rule for writes). - hp_cell="$(handoff_of_lane "$hp_lane" 2>/dev/null || :)" + hp_hrc=0; hp_cell="$(handoff_cell_of "$hp_lane")" || hp_hrc=$? + case "$hp_hrc" in + 0) : ;; + 8) note "lane $hp_lane has no handoff: there is no row for it in the register" + exit 8 ;; + *) die "the register could not be read, so whether lane $hp_lane's row names a handoff cannot be said — a read that failed is never 'no handoff' (R22). Nothing was cloned." 1 ;; + esac if [ -z "$hp_cell" ]; then - note "lane $hp_lane's row names no handoff, or there is no row for it in the register" + note "lane $hp_lane's row names no handoff" exit 8 fi handoff_checkout_of "$hp_lane" "$hp_create" diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 1ab82b4..65ce3d7 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -808,7 +808,7 @@ def test_write_handoff_for_a_lane_with_no_row_is_8(box: Box): before = box.snapshot(c) result = box.helper("write-handoff", "repoS4-9", "--stamp", STAMP, "--subject", SUBJECT) assert result.returncode == 8, result.stderr - assert "row names no handoff, so there is none to write" in result.stderr + assert "lane repoS4-9 has no row in the register" in result.stderr assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr assert result.stdout == "" assert box.snapshot(c) == before @@ -1170,3 +1170,37 @@ def test_with_LANES_NO_GIT_the_handoff_is_written_and_nothing_committed(box: Box assert box.origin_head() == tip assert box.net_ops() == [] _released(box, c) + + +def test_a_target_for_a_lane_with_no_row_is_8_and_nothing_committed(box: Box): + """NIT 3: the row is needed whatever `--target` says. Through the seam + (and in default mode) a `--target` write for a lane with no row went on + and committed `Lane: `; now it is 8 with nothing + written, as the opt-in's gate refuses it before the clone.""" + c = box.seam() + before = box.snapshot(c) + result = box.helper("write-handoff", "repoS4-9", "--stamp", STAMP, "--subject", SUBJECT, + "--target", str(c / HANDOFF_REL)) + assert result.returncode == 8, result.stderr + assert "lane repoS4-9 has no row in the register" in result.stderr + assert result.stdout == "" + assert box.snapshot(c) == before + assert (c / HANDOFF_REL).read_text(encoding="utf-8") == HANDOFF + _released(box, c) + + +def test_handoff_path_over_an_unreadable_register_is_1_never_8(box: Box): + """NIT 1 (R22): a register read that fails is never "no handoff". With + the published register unreadable, `handoff-path` is 1 and prints + nothing, where it answered 8.""" + box.seam() + real = shutil.which("git", path=os.environ.get("PATH")) + shim = box.fakebin / "git" + shim.write_text('#!/usr/bin/env bash\ncase " $* " in\n' + f' *"origin/main:{REGISTER}"*) echo "test shim: git $* refused" >&2; exit 128 ;;\n' + f'esac\nexec "{real}" "$@"\n', encoding="utf-8") + shim.chmod(0o755) + result = box.helper("handoff-path", LANE) + assert result.returncode == 1, result.stderr + assert "a read that failed is never 'no handoff' (R22)" in result.stderr + assert result.stdout == "" From e7a4960395cc0fd4fa7712845795f64a7ab20c2b Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 21:42:58 +0000 Subject: [PATCH 12/34] `commit_publish`'s S4.4 refusal reads HEAD, so where another writer's 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) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: ". 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 , not this write's commit : another commit was made on top of it … Nothing is reset, so both are kept", "look at both: git -C … log --oneline ^..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 : ", 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: " 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 (cherry picked from commit ea1d29300ddb4e41b9186797b59e16bbb0515a34) --- lanes-edit.sh | 22 +++++++++++++++----- tests/test_a23_write_handoff.py | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 1f2b5c2..c1149db 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -2895,15 +2895,27 @@ EOF # that is asked here, with origin just fetched, BEFORE the origin-unmoved # refusal below and BEFORE the reset (w1b's placement): a replay # would write the whole file again over whatever a peer put there. So where - # origin's copy has moved, the handoff stays written, its commit is kept on - # HEAD and named, as at the bound, and nobody's change is undone (R-A11-11). + # origin's copy has moved, the handoff stays written, its commit is kept and + # named, as at the bound, and nobody's change is undone (R-A11-11). + # HEAD IS READ, NOT ASSUMED (MUST 4's shape, which `cp_replay` asks after + # this): until slices 2b and 3 another writer can commit on top of this one in + # the same checkout while its push is in flight. Nothing is reset here either + # way, so both are kept; the words then name HEAD, and say both are kept, + # never that this write's commit is HEAD. if [ -n "$CP_WHOLE_REL" ]; then cpw_now="$(git -C "$LANES_REPO" rev-parse -q --verify "refs/remotes/origin/$LANES_BRANCH:$CP_WHOLE_REL" 2>/dev/null || :)" if [ "$cpw_now" != "$CP_WHOLE_BLOB" ]; then note "origin/$LANES_BRANCH's $CP_WHOLE_REL is ${cpw_now:-gone} now, and this whole-file write started from ${CP_WHOLE_BLOB:-no such file}." - note "look at it: git -C $LANES_REPO show $cp_sha; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" - note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." - die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); the commit is local and kept on HEAD: $cp_sha $msg" 6 + cpw_head="$(git -C "$LANES_REPO" rev-parse -q --verify HEAD 2>/dev/null || :)" + if [ "$cpw_head" = "$cp_sha" ]; then + note "look at it: git -C $LANES_REPO show $cp_sha; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" + note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." + die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); the commit is local and kept on HEAD: $cp_sha $msg" 6 + fi + note "HEAD is ${cpw_head:-unreadable}, not this write's commit $cp_sha: another commit was made on top of it in this checkout while its push was in flight. Nothing is reset, so both are kept." + note "look at both: git -C $LANES_REPO log --oneline $cp_sha^..HEAD; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" + note "every later write from this checkout refuses (S3's unpushed row) until they are published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3)." + die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); this write's commit and the one on top of it are local and kept, with HEAD at ${cpw_head:-unreadable}: $cp_sha $msg" 6 fi fi # A REFUSAL WITH ORIGIN UNMOVED IS NOT A LOST RACE: the replay would send the diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 65ce3d7..b9a844c 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1204,3 +1204,39 @@ def test_handoff_path_over_an_unreadable_register_is_1_never_8(box: Box): assert result.returncode == 1, result.stderr assert "a read that failed is never 'no handoff' (R22)" in result.stderr assert result.stdout == "" + + +def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): + """S4.4's refusal where another writer's commit landed on top of this + write's while its push was in flight: HEAD is that commit, so the words + name it and say both are kept — never "kept on HEAD" for this write's. + Nothing is reset (6), and both commits wait for the publish act.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + peer = shlex.quote(str(box.peer)) + cq = shlex.quote(str(c)) + count = shlex.quote(str(box.count)) + box.use_net('[ "$1" = push ] || exit 0\n' + f"[ -e {count} ] && exit 0\n" + f"echo 1 > {count}\n" + f"git -C {peer} push -q origin main\n" + f"printf 'stamp' > {cq}/handoffs/stamp.md\n" + f"git -C {cq} add -- handoffs/stamp.md\n" + f"git -C {cq} commit -q -m 'a stamp from an unlocked writer' -- handoffs/stamp.md\n" + "exit 0\n") + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", "handoff: PAUSED") + assert result.returncode == 6, result.stderr + top = box.head(c) + mine = box.git("-C", str(c), "rev-parse", "HEAD~1").strip() + assert box.subject(c) == "a stamp from an unlocked writer" + assert box.subject(c, "HEAD~1") == "handoff: PAUSED" + assert f"HEAD is {top}, not this write's commit {mine}" in result.stderr + assert f"kept, with HEAD at {top}: {mine} handoff: PAUSED" in result.stderr + assert f"kept on HEAD: {mine}" not in result.stderr + assert "push refused (attempt" not in result.stderr, "no reset, no replay" + assert len(box.unpublished(c)) == 2, "both kept, for the publish act" + assert result.stdout == _line(f) + _released(box, c) From 8fd9bcbd0e5ce187300cb38c0aa283e00dbd34e4 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Wed, 21 Oct 2026 22:13:52 +0000 Subject: [PATCH 13/34] S4.4's refusal sends a commit another writer rewrote, and a HEAD that 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 ". 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 (cherry picked from commit 1624f175d89a08db2fc06d169bf67937756ad5a9) --- lanes-edit.sh | 13 +++++++++--- tests/test_a23_write_handoff.py | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index cb0948c..6674daf 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -2922,7 +2922,11 @@ EOF # this): until slices 2b and 3 another writer can commit on top of this one in # the same checkout while its push is in flight. Nothing is reset here either # way, so both are kept; the words then name HEAD, and say both are kept, - # never that this write's commit is HEAD. + # never that this write's commit is HEAD. A COMMIT ON TOP IS ONE CASE; A + # REWRITE IS ANOTHER (slice 1b's `cp_not_head`, rv1b-c's SHOULD A): where + # HEAD cannot be read, or no longer holds this write's commit at all (another + # writer rebased or amended it with no lock), nothing of it is kept here, and + # `cp_not_head` names that in 1b's words. if [ -n "$CP_WHOLE_REL" ]; then cpw_now="$(git -C "$LANES_REPO" rev-parse -q --verify "refs/remotes/origin/$LANES_BRANCH:$CP_WHOLE_REL" 2>/dev/null || :)" if [ "$cpw_now" != "$CP_WHOLE_BLOB" ]; then @@ -2933,10 +2937,13 @@ EOF note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); the commit is local and kept on HEAD: $cp_sha $msg" 6 fi - note "HEAD is ${cpw_head:-unreadable}, not this write's commit $cp_sha: another commit was made on top of it in this checkout while its push was in flight. Nothing is reset, so both are kept." + if [ -z "$cpw_head" ] || ! git -C "$LANES_REPO" merge-base --is-ancestor "$cp_sha" "$cpw_head" 2>/dev/null; then + cp_not_head "$cp_sha" "$msg" "while its push was in flight" + fi + note "HEAD is $cpw_head, not this write's commit $cp_sha: another commit was made on top of it in this checkout while its push was in flight. Nothing is reset, so both are kept." note "look at both: git -C $LANES_REPO log --oneline $cp_sha^..HEAD; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" note "every later write from this checkout refuses (S3's unpushed row) until they are published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3)." - die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); this write's commit and the one on top of it are local and kept, with HEAD at ${cpw_head:-unreadable}: $cp_sha $msg" 6 + die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); this write's commit and the one on top of it are local and kept, with HEAD at $cpw_head: $cp_sha $msg" 6 fi fi # A REFUSAL WITH ORIGIN UNMOVED IS NOT A LOST RACE: the replay would send the diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index b9a844c..4dc12d4 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1240,3 +1240,39 @@ def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): assert len(box.unpublished(c)) == 2, "both kept, for the publish act" assert result.stdout == _line(f) _released(box, c) + + +def test_whole_refusal_names_a_rewritten_commit_as_rewritten(box: Box): + """S4.4's refusal where another writer REWROTE this write's commit while + its push was in flight (slice 1b's SHOULD A case, which S4.4 meets before + `cp_replay` does): HEAD does not hold this write's sha at all, so nothing + is said to be kept, on HEAD or under it, and slice 1b's `cp_not_head` + names the rewrite. The rewrite here is an amend at the read-back fetch, + which cannot conflict with the peer's edit as a rebase over it would.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + peer = shlex.quote(str(box.peer)) + cq = shlex.quote(str(c)) + pushed = shlex.quote(str(box.root / "pushed")) + done = shlex.quote(str(box.root / "rewritten")) + box.use_net(f'if [ "$1" = push ] && [ ! -e {pushed} ]; then echo 1 > {pushed}\n' + f" git -C {peer} push -q origin main\n" + " exit 0\n" + "fi\n" + f'if [ "$1" = fetch ] && [ -e {pushed} ] && [ ! -e {done} ]; then echo 1 > {done}\n' + f" git -C {cq} commit -q --amend -m 'handoff: PAUSED, reworded by an unlocked writer'\n" + "fi\n" + "exit 0\n") + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", "handoff: PAUSED") + assert result.returncode == 6, result.stderr + assert "rewrote this write's commit" in result.stderr + assert "kept on HEAD" not in result.stderr, "this write's sha is on no branch here" + assert "local and kept, with HEAD at" not in result.stderr, "no commit was made on top" + assert "push refused (attempt" not in result.stderr, "no reset, no replay" + assert box.subject(c) == "handoff: PAUSED, reworded by an unlocked writer" + assert len(box.unpublished(c)) == 1, "the rewritten copy waits for the publish act" + assert result.stdout == _line(f) + _released(box, c) From 844a88d343b094ac92375d4e79819543003e550f Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 02:56:18 +0000 Subject: [PATCH 14/34] A37: `write-handoff` names the handoff `--stamp` and `--prepend` edit 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 /handoffs/repoS4/repoS4-1.md, so there is nothing to write into. Nothing was written." Now: exit 6, "origin's copy of 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 (cherry picked from commit 76a019bbb329738729b9d631b0851f0a87099d42) --- lanes-edit.sh | 20 ++++++-- tests/test_a23_write_handoff.py | 91 +++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index a8d0d53..a18d9c1 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -551,7 +551,9 @@ # commit, so it never takes another), a push (a write's, or the publish act's) # refused with origin unmoved, which no replay can mend, or a # whole-file `write-handoff` whose copy origin changed meanwhile, which -# no replay may overwrite (S4.4). Or another +# no replay may overwrite (S4.4), or a `--stamp` or `--prepend` one +# whose handoff origin deleted meanwhile, which a replay would find +# nothing to write into. Or another # writer in the checkout REWROTE this write's commit (a `pull --rebase` # with no lock): it is not on HEAD at all, and what reached origin is # read by content. Or, where the drop's tree step was refused and @@ -19429,7 +19431,9 @@ EOF # 7. `commit_publish`, by pathspec, with `Lane: ` as its trailer: the # push, the read-back, and a refused push replays the whole verb on # origin's new tip — a `--whole` only where origin's copy is still the - # one it read (S4.4). + # one it read (S4.4), and a `--stamp` or `--prepend` only where origin + # still has the handoff it edits in place (A37, `CP_KEEP_IF_GONE`); + # otherwise the commit is kept on HEAD and named (6). # STDOUT: `` once the file is written (`HANDOFF_OUT`). # 0 pushed, or nothing to commit (another repository's file, S3's refusal # of this checkout or not; LANES_NO_GIT) @@ -19681,7 +19685,17 @@ EOF fi # ---- 7. the commit, the push, the read-back. CP_TRAILER="Lane: $lane" - if [ "$whf_mode" = whole ]; then CP_WHOLE_REL="$whf_rel"; CP_WHOLE_BLOB="$whf_blob"; fi + # A WRITE IN PLACE IS NEVER REPLAYED INTO NOTHING (A37, through slice 1b's + # `CP_KEEP_IF_GONE`): `--stamp` and `--prepend` edit the handoff in place, + # so where another writer deleted it while this write's push was in + # flight, the replay's re-run would find no file (8, "Nothing was + # written") and the write would be only in the commit it dropped; the + # commit is kept on HEAD and named instead (6). `--whole` is S4.4's: a + # copy origin deleted is a copy that moved. + case "$whf_mode" in + whole) CP_WHOLE_REL="$whf_rel"; CP_WHOLE_BLOB="$whf_blob" ;; + *) CP_KEEP_IF_GONE="$whf_rel" ;; + esac commit_publish "$whf_subject" "$whf_rel" ;; diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 4dc12d4..c9cff3e 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1276,3 +1276,94 @@ def test_whole_refusal_names_a_rewritten_commit_as_rewritten(box: Box): assert len(box.unpublished(c)) == 1, "the rewritten copy waits for the publish act" assert result.stdout == _line(f) _released(box, c) + + +# ------------------------------------------------- a handoff a peer deletes mid-push (A37) + +def _peer_deletes_the_handoff(box: Box) -> None: + """The peer clone commits the deletion of the lane's handoff WITHOUT + pushing it: the race hook lands it at the moment this write pushes. The + row still names the file.""" + box.git("-C", str(box.peer), "fetch", "-q", "origin") + box.git("-C", str(box.peer), "merge", "-q", "--ff-only", "refs/remotes/origin/main") + box.git("-C", str(box.peer), "rm", "-q", "--", HANDOFF_REL) + box.git("-C", str(box.peer), "commit", "-q", "-m", f"a peer deletes {HANDOFF_REL}") + + +def _origin_has(box: Box, rel: str) -> bool: + return rel in box.git("--git-dir", str(box.origin), "ls-tree", "-r", "--name-only", "main").split() + + +@pytest.mark.parametrize("mode", ["stamp", "prepend"]) +@pytest.mark.parametrize("peer", ["deletes-it", "keeps-it"]) +def test_a_handoff_a_peer_deletes_mid_push_keeps_the_write_on_HEAD_exit_6(box: Box, peer: str, mode: str): + """A37: `--stamp` and `--prepend` edit the handoff IN PLACE. A peer that + deleted it while this write's push was in flight left the replay nothing + to write into: the re-run said "Nothing was written" (8), and the write + was only in the commit the replay had dropped. `write-handoff` now names + the handoff in slice 1b's `CP_KEEP_IF_GONE`, so the commit is kept on + HEAD and named (6) before anything is dropped: the write is in this + checkout, origin has no such file, and one commit is unpublished, this + write's. A peer's commit that KEEPS the handoff, and changes it, is an + ordinary lost race: the write is replayed on the peer's copy and lands + (0).""" + c = box.seam() + f = c / HANDOFF_REL + if mode == "stamp": + args, written = ("--stamp", STAMP), stamped + else: + args, written = ("--prepend", str(_block(box))), prepended + if peer == "deletes-it": + _peer_deletes_the_handoff(box) + else: + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + box.race(prepared=True) + result = box.write_handoff(*args, "--subject", SUBJECT) + err = result.stderr + if peer == "deletes-it": + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin's copy of {HANDOFF_REL} was deleted while this write's push was in flight" in err, err + assert f"kept on HEAD: {kept}" in err + assert "push refused (attempt" not in err, "nothing is dropped or replayed" + assert box.subject(c) == SUBJECT + assert f.read_text(encoding="utf-8") == written(HANDOFF), "the write is in this checkout" + assert not _origin_has(box, HANDOFF_REL), "origin has no such file" + assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" + else: + base = HANDOFF.replace("Preserved task.\n", "Preserved task, and a peer's line.\n") + assert result.returncode == 0, err + assert "push refused (attempt 1 of 6)" in err + assert "was deleted while this write's push was in flight" not in err + assert box.origin_file(HANDOFF_REL) == written(base) + assert f.read_text(encoding="utf-8") == written(base) + assert box.unpublished(c) == [] + assert result.stdout == _line(f) + _released(box, c) + + +def test_whole_over_a_handoff_a_peer_deletes_mid_push_is_S4_4s_exit_6(box: Box): + """`--whole` never names its file in `CP_KEEP_IF_GONE` (A37 is + `--stamp`'s and `--prepend`'s): a handoff origin deleted is a copy that + is no longer the one it started from, so S4.4 answers, before the + replay, and keeps the commit on HEAD (6) in its own words.""" + c = box.seam() + f = c / HANDOFF_REL + _peer_deletes_the_handoff(box) + box.race(prepared=True) + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", "handoff: PAUSED") + err = result.stderr + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin/main's {HANDOFF_REL} is gone now" in err, err + assert "is no longer the one this whole-file write started from" in err + assert f"kept on HEAD: {kept}" in err + assert "was deleted while this write's push was in flight" not in err + assert "push refused (attempt" not in err, "no replay" + assert f.read_text(encoding="utf-8") == "# the handoff, written whole\n" + assert not _origin_has(box, HANDOFF_REL) + assert [line.split()[1] for line in box.unpublished(c)] == [kept] + assert result.stdout == _line(f) + _released(box, c) From 8b81216e6d164af98e699f4cd5ae8d5d0b1bb0be Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 07:51:37 +0000 Subject: [PATCH 15/34] A44: `write-handoff` holds its `handoffs/` rule in another clone of the 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 `-lanes//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 `-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 (cherry picked from commit a928dbb3e50ecd911a4d1a4e235fba4ff1984b3c) --- lanes-edit.sh | 49 ++++++++++++++++++++-------- tests/test_a23_write_handoff.py | 57 ++++++++++++++++++++++++++++++++- 2 files changed, 92 insertions(+), 14 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index a18d9c1..7c87809 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -483,8 +483,9 @@ # past (S4, Open question 2); `claim` or `add-row` meeting origin # moved under its own checks on each of six attempts (S3, S4), before any # edit; and — slice 2a — `write-handoff`'s handoff -# that resolves into W through a link (S5), a target in the checkout that -# is not a file under `handoffs/` (S2), a restart's bound file or +# that resolves into W through a link (S5), a target in the checkout, or +# in another clone of the register, that is not a file under `handoffs/` +# (S2), a restart's bound file or # pre-image that is not this one, or a splice that does not prove out, # each with nothing written; while S3's refusal of a `--prepend` or a # `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed @@ -16209,6 +16210,15 @@ whf_stamp_said() { [ "${whf_mode:-}" != stamp ] || note "the stamp line it would have written under the handoff's header block: ${whf_arg:-}" return 0 } +# A FILE `write-handoff` MAY REPLACE, relative to a clone of the register's top +# level (rv2a's pre-review of slice 2a, SHOULD 1): one under `handoffs/`, and +# never the register, an object log or anything else under `lanes/`. +whf_in_scope() { # — 0 in scope; 1 not + case "$1" in handoffs/?*) : ;; *) return 1 ;; esac + case "$1" in "$LANES_PATH"|lanes/*|*/.|*/..) return 1 ;; esac + if [ -n "$LANES_PREFIX" ]; then case "$1" in "$LANES_PREFIX"*) return 1 ;; esac; fi + return 0 +} # S5 — W IS ONLY EVER FAST-FORWARDED, and `refresh-workspace` is the one act # that does it: `git fetch origin main` and `git merge --ff-only origin/main`, @@ -19409,10 +19419,11 @@ EOF # after S3's fetch — or `--target`, the old callers' spelling — and # TRANSLATED into the checkout this write goes through, never followed # (`handoff_in_repo`, S2). One that still resolves into W, through a - # link, refuses (S5, 2), and so does one in the checkout that is not a - # file under `handoffs/` (S2: never the register or `lanes/`). One in - # ANOTHER REPOSITORY (Amendment 8(d)) is written there and never - # committed, and S3's refusal of this checkout does not stop it. + # link, refuses (S5, 2), and so does one in the checkout, or in another + # clone of the register, that is not a file under `handoffs/` (S2: + # never the register or `lanes/`). One in ANOTHER REPOSITORY (Amendment + # 8(d)) is written there and never committed, and S3's refusal of this + # checkout does not stop it. # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -19562,14 +19573,26 @@ EOF # (Amendment 23 S2 scopes it to `handoffs//`). `whf_rel` is # the PHYSICAL path's, so a `--target`, a cell that climbs out through # `..`, and a link out of `handoffs/` are all caught here, before a byte. - if [ "$whf_other" = 0 ]; then - whf_scope=1 - case "$whf_rel" in handoffs/?*) whf_scope=0 ;; esac - case "$whf_rel" in "$LANES_PATH"|lanes/*|*/.|*/..) whf_scope=1 ;; esac - if [ -n "$LANES_PREFIX" ]; then case "$whf_rel" in "$LANES_PREFIX"*) whf_scope=1 ;; esac; fi - if [ "$whf_scope" = 1 ]; then + if [ "$whf_other" = 0 ] && ! whf_in_scope "$whf_rel"; then + whf_stamp_said + die "lane $lane's handoff target $whf_path is $whf_rel in the checkout $whf_c, which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never the register ($LANES_PATH) or anything under lanes/ (Amendment 23 S2). Nothing was written." 2 + fi + # AND IN ANOTHER CLONE OF THE REGISTER (rv2a-c2's re-review of slice 2a, + # SHOULD 1). A path outside this checkout, in a git work tree whose top + # level carries the register too — a sibling lane's `-lanes//`, + # an older clone — is that clone's file, not another repository's: the + # same rule holds there, relative to that top level. Otherwise `--whole` + # replaced that clone's register in place, uncommitted, and its lane's + # next write refused at S3 step 4 over a change it never made. A + # repository that carries no register is Amendment 8(d)'s, and a path in + # no work tree is as it was (A44). + if [ "$whf_other" = 1 ]; then + whf_otop="$(git -C "$(dirname -- "$whf_phys")" rev-parse --show-toplevel 2>/dev/null || :)" + whf_otop="$( [ -n "$whf_otop" ] && cd -P -- "$whf_otop" 2>/dev/null && pwd -P || :)" + if [ -n "$whf_otop" ] && { [ -e "$whf_otop/$LANES_PATH" ] || [ -L "$whf_otop/$LANES_PATH" ]; } && + path_under "$whf_phys" "$whf_otop" && ! whf_in_scope "${whf_phys#"$whf_otop"/}"; then whf_stamp_said - die "lane $lane's handoff target $whf_path is $whf_rel in the checkout $whf_c, which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never the register ($LANES_PATH) or anything under lanes/ (Amendment 23 S2). Nothing was written." 2 + die "lane $lane's handoff target $whf_path is ${whf_phys#"$whf_otop"/} in $whf_otop, another checkout of the register (its top level carries $LANES_PATH too), which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never a register or anything under lanes/, in this checkout or in another clone of it (Amendment 23 S2). Nothing was written." 2 fi fi # ANOTHER REPOSITORY'S HANDOFF IS NOT THIS CHECKOUT'S WRITE (rv2a's diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index c9cff3e..3dd92e9 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -38,7 +38,7 @@ from a23_box import ME, REGISTER from conftest import WINDOWS_SKIP -from test_a23_publish_replay import LANE, Box as ReplayBox +from test_a23_publish_replay import LANE, NEIGHBOUR, Box as ReplayBox pytestmark = [pytest.mark.skipif(shutil.which("bash") is None, reason="lanes-edit.sh is a bash script"), @@ -1017,6 +1017,61 @@ def test_a_target_outside_handoffs_is_refused_and_nothing_written(box: Box, how: _released(box, c) +@pytest.mark.parametrize("how", ["whole-register", "prepend-log", "cell-register"]) +def test_a_target_in_another_clone_of_the_register_keeps_the_handoffs_rule(box: Box, how: str): + """rv2a-c2's SHOULD 1 (its probe N3): the `handoffs/` rule held only in + this checkout, so a `--target`, or a row cell, naming a file of ANOTHER + clone of the register — a sibling lane's checkout, `-lanes//` — + was taken for another repository's handoff (8(d)): `--whole` replaced + that clone's register in place and `--prepend` put a block on its + append-only object log, uncommitted, exit 0, and that lane's next write + then refused at S3 step 4 over a change it never made. The rule holds + relative to that clone's own top level (A44): 2, nothing written there or + here, nothing on stdout.""" + d = box.lanes_root / NEIGHBOUR + rel = f"lanes/log/{NEIGHBOUR}.md" if how == "prepend-log" else REGISTER + if how == "cell-register": + box.cell_is(str(d / REGISTER)) + c = box.seam() + box.clone(d) + if how == "whole-register": + args = ("--whole", str(_prepared(box, "# not a register any more\n")), + "--expect-digest", sha(d / REGISTER), "--target", str(d / REGISTER)) + elif how == "prepend-log": + args = ("--prepend", str(_block(box)), "--target", str(d / rel)) + else: + args = ("--stamp", STAMP) + text, theirs, mine = (d / rel).read_bytes(), box.snapshot(d), box.snapshot(c) + result = box.write_handoff(*args, "--subject", "handoff: PAUSED") + assert result.returncode == 2, result.stderr + assert f"is {rel} in {d}, another checkout of the register" in result.stderr + assert "Nothing was written." in result.stderr + assert "another repository's handoff" not in result.stderr + assert (d / rel).read_bytes() == text + assert box.snapshot(d) == theirs + assert box.snapshot(c) == mine + assert result.stdout == "" + _released(box, c) + + +def test_a_handoff_in_another_clone_of_the_register_is_still_another_repositorys(box: Box): + """A44's bound: in another clone of the register, a file under + `handoffs/` passes the rule and is written as another repository's + handoff (8(d)), committed nowhere, exit 0, as before. Whether such a file + is refused too is question 3's, for lane openRepoTools-3.""" + c = box.seam() + d = box.clone(box.lanes_root / NEIGHBOUR) + f = d / HANDOFF_REL + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--target", str(f)) + assert result.returncode == 0, result.stderr + assert f"{f} is another repository's handoff (Amendment 8(d))" in result.stderr + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.git("-C", str(d), "status", "--porcelain").split() == ["M", HANDOFF_REL] + assert box.head(c) == box.origin_head() + assert result.stdout == _line(f) + _released(box, c) + + def _managed_row(box: Box) -> tuple: """The lane's row as a peer republishes it, with a managed-owner marker that parses, so the seam's answer is "owned", not "unknown".""" From 27e76d744785e63f7b5255dab8bfa09939e9d041 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 07:57:40 +0000 Subject: [PATCH 16/34] A45: `write-handoff` asks whether its handoff is tracked whatever S3 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 commit ... -- `, 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 add -- && ` 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 (cherry picked from commit 2fc394d9fbbc8c658ce4680037f84e4bc4eccdba) --- lanes-edit.sh | 13 ++++++++---- tests/test_a23_write_handoff.py | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 7c87809..bd7d5eb 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -19612,13 +19612,18 @@ EOF # push then lost a race, the replay's `reset --keep` dropped that commit # and the file with it, and the re-run found nothing to write into (8). So # it is a pending change like any other, and whoever made it adds and - # commits it as its own write. + # commits it as its own write. WHETHER IT IS TRACKED IS ASKED WHATEVER S3 + # ANSWERED (rv2a-c2's re-review of slice 2a, NIT 1; A45): where S3 refused + # for a reason of its own, `--prepend` and `--whole` still write it (step + # 6), and the commit that note prints needs the `git add` first. whf_untracked=0 + if [ "$whf_other" = 0 ] && [ "$NO_GIT" != 1 ] && { [ -e "$whf_phys" ] || [ -L "$whf_phys" ]; } && + ! git --literal-pathspecs -C "$whf_c" -c "$CP_EXACT" ls-files --error-unmatch -- "$whf_rel" >/dev/null 2>&1; then + whf_untracked=1 + fi if [ "$whf_s3" = 0 ] && [ "$whf_other" = 0 ]; then ( cs_own_pending write-handoff "$lane" "$whf_rel" ) || whf_s3=$? - if [ "$whf_s3" = 0 ] && [ "$NO_GIT" != 1 ] && { [ -e "$whf_phys" ] || [ -L "$whf_phys" ]; } && - ! git --literal-pathspecs -C "$whf_c" -c "$CP_EXACT" ls-files --error-unmatch -- "$whf_rel" >/dev/null 2>&1; then - whf_untracked=1 + if [ "$whf_s3" = 0 ] && [ "$whf_untracked" = 1 ]; then ( cs_refuse write-handoff "$whf_rel, this write's handoff, is not tracked yet: git has never had it (a new lane's first handoff, or a peer's), so this write's commit would add it under its own subject, and a replay after a lost race would drop that commit and the file with it (Amendment 23 S3 step 4)" \ "read it: git -C $LANES_REPO status --short -- $whf_rel" \ "whoever made it adds and commits it by pathspec, as its own write: git -C $LANES_REPO add -- $whf_rel && git -C $LANES_REPO commit -m \"()\" -- $whf_rel, then LANES_LANE= $SELF commit \"\" (the publish act)" \ diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 3dd92e9..7790179 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -989,6 +989,43 @@ def test_an_untracked_handoff_is_refused_and_never_dropped(box: Box, mode: str): _released(box, c) +@pytest.mark.parametrize("handoff", ["untracked", "tracked"]) +def test_the_recovery_under_another_S3_refusal_names_git_add_for_an_untracked_handoff(box: Box, handoff: str): + """rv2a-c2's NIT 1 (its probe N1): S3 refuses for a foreign staged entry, + and the row names a handoff git does not track. `--prepend` writes it, + uncommitted (2), and the recovery's commit by pathspec needs the `git + add` first: git refuses a pathspec it does not know. The note left the + `add` out wherever S3 had refused for a reason of its own. It names it + now (A45), and the two commands, run as printed, commit the handoff + alone, the foreign entry still staged. A tracked handoff's note names no + `add`, as before.""" + c = box.seam() + rel = NEW_REL if handoff == "untracked" else HANDOFF_REL + if handoff == "untracked": + box.cell_is(NEW_REL) + (c / NEW_REL).write_text(HANDOFF, encoding="utf-8") + _staged_foreign(box, c) + result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED") + err = result.stderr + assert result.returncode == 2, err + assert "staged entries this write did not make: handoffs/README.md" in err + assert "the handoff is WRITTEN and NOT committed" in err + add = f"git -C {c} add -- {rel}" + commit = f'git -C {c} commit -m "handoff: PAUSED" -m "Lane: {LANE}" -- {rel}' + if handoff == "tracked": + assert f"publish it: {commit}, then" in err, err + assert f"add -- {rel}" not in err + _released(box, c) + return + assert f"publish it: {add} && {commit}, then" in err, err + for printed in (add, commit): + box.git(*shlex.split(printed)[1:]) + assert box.git("-C", str(c), "show", "--name-only", "--format=", "HEAD").split() == [NEW_REL] + assert (c / NEW_REL).read_text(encoding="utf-8") == prepended(HANDOFF) + assert box.git("-C", str(c), "diff", "--cached", "--name-only").split() == ["handoffs/README.md"] + _released(box, c) + + @pytest.mark.parametrize("how", ["target", "cell"]) def test_a_target_outside_handoffs_is_refused_and_nothing_written(box: Box, how: str): """SHOULD 1: this verb replaces a file and pushes it, so in the checkout From f00def18958608ebee6e931d83e704273302c26f Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 08:07:00 +0000 Subject: [PATCH 17/34] A46: A37 keeps a `--stamp` or `--prepend` on HEAD only where a replay 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 (cherry picked from commit e2037b912e853c948739b6bd1d36b98b806cc12e) --- lanes-edit.sh | 39 ++++++++++++--- tests/test_a23_write_handoff.py | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index bd7d5eb..75dee55 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -554,7 +554,8 @@ # whole-file `write-handoff` whose copy origin changed meanwhile, which # no replay may overwrite (S4.4), or a `--stamp` or `--prepend` one # whose handoff origin deleted meanwhile, which a replay would find -# nothing to write into. Or another +# nothing to write into (one moved with its row's cell is replayed into +# the moved file). Or another # writer in the checkout REWROTE this write's commit (a `pull --rebase` # with no lock): it is not on HEAD at all, and what reached origin is # read by content. Or, where the drop's tree step was refused and @@ -3086,9 +3087,12 @@ cp_replay() { # # the HEAD check, so "kept on HEAD" is said only of a commit HEAD is. Origin # was just fetched (`cp_confirm`), and a file its base had and origin lacks # means origin moved, so the origin-unmoved refusal can never be this one. + # Kept only where a replay could not follow the file (slice 2a's A46, + # `whf_keep_if_gone`): a handoff moved with its row's cell is replayed. if [ -n "$CP_KEEP_IF_GONE" ] && git -C "$LANES_REPO" cat-file -e "$1^:$CP_KEEP_IF_GONE" 2>/dev/null && - ! git -C "$LANES_REPO" cat-file -e "refs/remotes/origin/$LANES_BRANCH:$CP_KEEP_IF_GONE" 2>/dev/null; then + ! git -C "$LANES_REPO" cat-file -e "refs/remotes/origin/$LANES_BRANCH:$CP_KEEP_IF_GONE" 2>/dev/null && + whf_keep_if_gone; then note "origin/$LANES_BRANCH no longer has $CP_KEEP_IF_GONE, which this write edits in place and its base had: another writer deleted or moved it while this write's push was in flight." note "look at it: git -C $LANES_REPO show $1; and at origin's: git -C $LANES_REPO log -1 --stat refs/remotes/origin/$LANES_BRANCH -- $CP_KEEP_IF_GONE" note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." @@ -16219,6 +16223,24 @@ whf_in_scope() { # — 0 in scope; 1 if [ -n "$LANES_PREFIX" ]; then case "$1" in "$LANES_PREFIX"*) return 1 ;; esac; fi return 0 } +# A37'S KEEP, ONLY WHERE A REPLAY COULD NOT FOLLOW THE FILE (rv2a-c2's +# re-review of slice 2a, NIT 2; A46). Slice 1b's `cp_replay` asks it before it +# keeps a write whose in-place file origin no longer has, of origin as +# `cp_confirm` just fetched it. 1, replay, only where origin's row for the lane +# names ANOTHER cell than the one this write followed: a peer moved the handoff +# and pointed the cell at its new name, and the re-run reads that row and +# writes the moved file. 0, keep: the row still names that cell; `--target` +# named the file (the re-run names it again); origin has no row for the lane +# (none to re-read: `rename-lane` renames a row with its file); or origin's +# register cannot be read (R22). Read from the fetched ref itself, because +# `register_text_checked` keeps the register it read before the push. +whf_keep_if_gone() { + [ -n "${whf_keep_cell:-}" ] || return 0 + wkg_reg="$(git -C "$LANES_REPO" show "refs/remotes/origin/$LANES_BRANCH:$LANES_PATH" 2>/dev/null)" || return 0 + wkg_row="$(printf '%s\n' "$wkg_reg" | awk -v lane="${lane:-}" "$ROW_AWK")" || return 0 + [ -n "$wkg_row" ] || return 0 + [ "$(row_cell "$wkg_row" 7 | sed 's/^ *//; s/ *$//')" = "$whf_keep_cell" ] +} # S5 — W IS ONLY EVER FAST-FORWARDED, and `refresh-workspace` is the one act # that does it: `git fetch origin main` and `git merge --ff-only origin/main`, @@ -19443,8 +19465,9 @@ EOF # push, the read-back, and a refused push replays the whole verb on # origin's new tip — a `--whole` only where origin's copy is still the # one it read (S4.4), and a `--stamp` or `--prepend` only where origin - # still has the handoff it edits in place (A37, `CP_KEEP_IF_GONE`); - # otherwise the commit is kept on HEAD and named (6). + # still has the handoff it edits in place (A37, `CP_KEEP_IF_GONE`), or + # where origin's row now names another cell, which the re-run writes + # (A46); otherwise the commit is kept on HEAD and named (6). # STDOUT: `` once the file is written (`HANDOFF_OUT`). # 0 pushed, or nothing to commit (another repository's file, S3's refusal # of this checkout or not; LANES_NO_GIT) @@ -19719,10 +19742,14 @@ EOF # flight, the replay's re-run would find no file (8, "Nothing was # written") and the write would be only in the commit it dropped; the # commit is kept on HEAD and named instead (6). `--whole` is S4.4's: a - # copy origin deleted is a copy that moved. + # copy origin deleted is a copy that moved. A HANDOFF MOVED WITH ITS + # ROW'S CELL IS REPLAYED (A46): `whf_keep_cell` is the cell this write + # followed (none under `--target`), and where origin's row names another + # one, the re-run reads it and writes the moved file. case "$whf_mode" in whole) CP_WHOLE_REL="$whf_rel"; CP_WHOLE_BLOB="$whf_blob" ;; - *) CP_KEEP_IF_GONE="$whf_rel" ;; + *) CP_KEEP_IF_GONE="$whf_rel" + whf_keep_cell=""; [ -n "$whf_target" ] || whf_keep_cell="$whf_cell" ;; esac commit_publish "$whf_subject" "$whf_rel" ;; diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 7790179..8f0c3e5 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1459,3 +1459,87 @@ def test_whole_over_a_handoff_a_peer_deletes_mid_push_is_S4_4s_exit_6(box: Box): assert [line.split()[1] for line in box.unpublished(c)] == [kept] assert result.stdout == _line(f) _released(box, c) + + +#: Where a peer moves the lane's handoff to (rv2a-c2's probe N2). +MOVED_REL = "handoffs/repoS4/moved-handoff.md" + + +def _peer_moves_the_handoff(box: Box, row: str) -> None: + """The peer clone `git mv`s the lane's handoff to MOVED_REL in a commit + it does NOT push: the race hook lands it at this write's push. In the + same commit the register's row for the lane either follows the file + ("follows": the cell names MOVED_REL) or is renamed with it ("renamed": + the lane is `repoS4-9` now, as `rename-lane` renames a row and moves its + handoff), so origin has no row for this write's lane.""" + p = box.peer + box.git("-C", str(p), "fetch", "-q", "origin") + box.git("-C", str(p), "merge", "-q", "--ff-only", "refs/remotes/origin/main") + box.git("-C", str(p), "mv", "--", HANDOFF_REL, MOVED_REL) + reg = p / REGISTER + text = reg.read_text(encoding="utf-8") + assert text.count(f"| {HANDOFF_REL} |") == 1 + new = text.replace(f"| {HANDOFF_REL} |", f"| {MOVED_REL} |") + if row == "renamed": + assert new.count(f"| `{LANE}` |") == 1 + new = new.replace(f"| `{LANE}` |", "| `repoS4-9` |") + reg.write_text(new, encoding="utf-8") + box.git("-C", str(p), "add", "--", REGISTER) + box.git("-C", str(p), "commit", "-q", "-m", f"a peer moves {HANDOFF_REL} and its row ({row})") + + +@pytest.mark.parametrize("mode", ["stamp", "prepend"]) +def test_a_handoff_a_peer_moves_with_its_row_cell_mid_push_is_replayed_into_the_moved_file(box: Box, mode: str): + """rv2a-c2's NIT 2 (its probe N2): a peer moves the lane's handoff and + points the row's cell at its new name, in one commit that lands while + this write's push is in flight. A37 kept the commit on HEAD (6) for any + handoff origin no longer has, though the replay's re-run reads the row + again and writes the moved file. It is kept now only where origin's row + still names the cell this write followed (A46): here the replay goes + ahead, the write lands in the moved file on origin (0), and nothing is + left unpublished.""" + c = box.seam() + if mode == "stamp": + args, written = ("--stamp", STAMP), stamped + else: + args, written = ("--prepend", str(_block(box))), prepended + _peer_moves_the_handoff(box, "follows") + box.race(prepared=True) + result = box.write_handoff(*args, "--subject", SUBJECT) + err = result.stderr + assert result.returncode == 0, err + assert "push refused (attempt 1 of 6)" in err + assert "was deleted while this write's push was in flight" not in err + assert box.origin_file(MOVED_REL) == written(HANDOFF) + assert not _origin_has(box, HANDOFF_REL) + assert (c / MOVED_REL).read_text(encoding="utf-8") == written(HANDOFF) + assert box.unpublished(c) == [] + assert result.stdout == _line(c / MOVED_REL) + _released(box, c) + + +@pytest.mark.parametrize("how", ["target", "renamed"]) +def test_a_moved_handoff_the_replay_cannot_follow_is_kept_on_HEAD_exit_6(box: Box, how: str): + """A46's bounds, where A37's keep still holds (6): the replay's re-run + could not follow the move. A `--target` names the old path again, so + the re-run would find nothing there; and where the row was renamed with + the file, origin has no row for this lane to re-read, so the re-run + would be 8 ("no row"). The write is in this checkout and kept on HEAD, + one commit unpublished, nothing replayed.""" + c = box.seam() + f = c / HANDOFF_REL + _peer_moves_the_handoff(box, "follows" if how == "target" else "renamed") + box.race(prepared=True) + extra = ("--target", str(f)) if how == "target" else () + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, *extra) + err = result.stderr + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin's copy of {HANDOFF_REL} was deleted while this write's push was in flight" in err, err + assert f"kept on HEAD: {kept}" in err + assert "push refused (attempt" not in err, "nothing is dropped or replayed" + assert f.read_text(encoding="utf-8") == stamped(HANDOFF) + assert _origin_has(box, MOVED_REL) and not _origin_has(box, HANDOFF_REL) + assert [line.split()[1] for line in box.unpublished(c)] == [kept] + assert result.stdout == _line(f) + _released(box, c) From 9e9281fbca44f21aeea0fb2975b7d661df2797e5 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 12:16:51 +0000 Subject: [PATCH 18/34] A47: `write-handoff` refuses a target inside a git directory, so a `--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 `-lanes//.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 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 `-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 (cherry picked from commit 5ad9b4a600794e32379b41aa3e690a9f7b112176) --- lanes-edit.sh | 27 +++++++++++++++------ tests/test_a23_write_handoff.py | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 7 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 75dee55..b3ddaf7 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -485,7 +485,7 @@ # edit; and — slice 2a — `write-handoff`'s handoff # that resolves into W through a link (S5), a target in the checkout, or # in another clone of the register, that is not a file under `handoffs/` -# (S2), a restart's bound file or +# (S2), a target inside a git directory, a restart's bound file or # pre-image that is not this one, or a splice that does not prove out, # each with nothing written; while S3's refusal of a `--prepend` or a # `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed @@ -19443,9 +19443,10 @@ EOF # (`handoff_in_repo`, S2). One that still resolves into W, through a # link, refuses (S5, 2), and so does one in the checkout, or in another # clone of the register, that is not a file under `handoffs/` (S2: - # never the register or `lanes/`). One in ANOTHER REPOSITORY (Amendment - # 8(d)) is written there and never committed, and S3's refusal of this - # checkout does not stop it. + # never the register or `lanes/`), and one inside a git directory — + # another clone's `.git/`, a bare clone. One in ANOTHER REPOSITORY + # (Amendment 8(d)) is written there and never committed, and S3's + # refusal of this checkout does not stop it. # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -19473,8 +19474,8 @@ EOF # of this checkout or not; LANES_NO_GIT) # 1 environment: a checkout that cannot be entered, an unreadable file, # a register that could not be read (R22: never 8) - # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2), a - # restart's bound file or pre-image, a proof + # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2) or + # is inside a git directory, a restart's bound file or pre-image, a proof # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named # 7 a restart fence, or the handoff changed under the write # 8 no handoff to write: no row for the lane (with `--target` too), the @@ -19608,8 +19609,20 @@ EOF # replaced that clone's register in place, uncommitted, and its lane's # next write refused at S3 step 4 over a change it never made. A # repository that carries no register is Amendment 8(d)'s, and a path in - # no work tree is as it was (A44). + # no repository at all is as it was (A44). + # NEVER INSIDE A GIT DIRECTORY (rv2a-c3's re-check of slice 2a, SHOULD 1; + # A47). A path in another clone's `.git/`, or in a bare clone, is in no + # work tree, so the test below cannot see it, and it was written as + # another repository's handoff: `--stamp` put its line under `[core]` in + # that clone's `config`, `--whole` replaced it, and every git command + # there then failed (128), its own lane's S3 with them. No handoff of + # Amendment 8(d)'s lives in a git directory, so it is refused first. if [ "$whf_other" = 1 ]; then + whf_gd="$(dirname -- "$whf_phys")" + if [ "$(git -C "$whf_gd" rev-parse --is-inside-git-dir 2>/dev/null || :)" = true ]; then + whf_stamp_said + die "lane $lane's handoff target $whf_path is $whf_phys, inside a git directory ($whf_gd), where git keeps a repository's own files (another clone's .git/, or a bare clone): write-handoff writes a lane's handoff and nothing else, and no handoff lives in a git directory (Amendment 23 S2; Amendment 8(d)'s is a file of a work tree). Nothing was written." 2 + fi whf_otop="$(git -C "$(dirname -- "$whf_phys")" rev-parse --show-toplevel 2>/dev/null || :)" whf_otop="$( [ -n "$whf_otop" ] && cd -P -- "$whf_otop" 2>/dev/null && pwd -P || :)" if [ -n "$whf_otop" ] && { [ -e "$whf_otop/$LANES_PATH" ] || [ -L "$whf_otop/$LANES_PATH" ]; } && diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 8f0c3e5..bd3deb4 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1109,6 +1109,49 @@ def test_a_handoff_in_another_clone_of_the_register_is_still_another_repositorys _released(box, c) +@pytest.mark.parametrize("mode", ["stamp", "whole"]) +@pytest.mark.parametrize("where", ["sibling-clone", "bare-clone"]) +def test_a_target_inside_another_clones_git_directory_is_refused_and_nothing_written( + box: Box, where: str, mode: str): + """rv2a-c3's SHOULD 1 (its probe N4): a path inside ANOTHER clone's git + directory — a sibling lane's `-lanes//.git/config` — or inside a + bare clone is in no work tree, so A44's test could not see it, and it was + written as another repository's handoff (8(d)), exit 0: `--stamp` put its + line under `[core]`, `--whole` replaced the file, and every git command in + that clone then failed (128), its own lane's S3 with them. No handoff + lives in a git directory (A47): 2 before a byte, nothing written there or + here, that clone's git still reads its config, and nothing on stdout.""" + c = box.seam() + if where == "sibling-clone": + d = box.clone(box.lanes_root / NEIGHBOUR) + gd = d / ".git" + else: + d = None + gd = box.root / "bare.git" + box.git("clone", "-q", "--bare", str(box.origin), str(gd)) + box.quiet(gd, bare=True) + cfg = gd / "config" + if mode == "stamp": + args = ("--stamp", STAMP) + else: + args = ("--whole", str(_prepared(box, "[core\n")), "--expect-digest", sha(cfg)) + before, mine = cfg.read_bytes(), box.snapshot(c) + theirs = box.snapshot(d) if d is not None else None + result = box.write_handoff(*args, "--subject", "handoff: PAUSED", "--target", str(cfg)) + assert result.returncode == 2, result.stderr + assert f"is {cfg}, inside a git directory ({gd})" in result.stderr + assert "Nothing was written." in result.stderr + assert "another repository's handoff" not in result.stderr + assert cfg.read_bytes() == before + assert box.git("--git-dir", str(gd), "config", "core.bare").strip() == ( + "true" if d is None else "false"), "that clone's git still reads its config" + if d is not None: + assert box.snapshot(d) == theirs + assert box.snapshot(c) == mine + assert result.stdout == "" + _released(box, c) + + def _managed_row(box: Box) -> tuple: """The lane's row as a peer republishes it, with a managed-owner marker that parses, so the seam's answer is "owned", not "unknown".""" From ad1a44dd69e9e83d55603115be36c067a2064051 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 12:30:32 +0000 Subject: [PATCH 19/34] A48: A46 follows a moved handoff only to a file the replay can write, 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/:`). 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 (cherry picked from commit 68014b4154545b819c1281f1f4e0d28e8ea6e1d1) --- lanes-edit.sh | 27 +++++++++++---- tests/test_a23_write_handoff.py | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index b3ddaf7..3d6b604 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -554,8 +554,8 @@ # whole-file `write-handoff` whose copy origin changed meanwhile, which # no replay may overwrite (S4.4), or a `--stamp` or `--prepend` one # whose handoff origin deleted meanwhile, which a replay would find -# nothing to write into (one moved with its row's cell is replayed into -# the moved file). Or another +# nothing to write into (one moved with its row's cell, to a file under +# `handoffs/` that origin has, is replayed into the moved file). Or another # writer in the checkout REWROTE this write's commit (a `pull --rebase` # with no lock): it is not on HEAD at all, and what reached origin is # read by content. Or, where the drop's tree step was refused and @@ -16227,19 +16227,30 @@ whf_in_scope() { # — 0 in scope; 1 # re-review of slice 2a, NIT 2; A46). Slice 1b's `cp_replay` asks it before it # keeps a write whose in-place file origin no longer has, of origin as # `cp_confirm` just fetched it. 1, replay, only where origin's row for the lane -# names ANOTHER cell than the one this write followed: a peer moved the handoff +# names ANOTHER cell than the one this write followed, and that cell is a file +# under `handoffs/` in this checkout that origin has: a peer moved the handoff # and pointed the cell at its new name, and the re-run reads that row and # writes the moved file. 0, keep: the row still names that cell; `--target` # named the file (the re-run names it again); origin has no row for the lane # (none to re-read: `rename-lane` renames a row with its file); or origin's # register cannot be read (R22). Read from the fetched ref itself, because # `register_text_checked` keeps the register it read before the push. +# ANOTHER CELL IS FOLLOWED ONLY TO A FILE THE RE-RUN CAN WRITE (rv2a-c3's +# re-check of slice 2a, NIT 1; A48): an emptied cell (the re-run's 8, "names +# no handoff"), one whose file origin lacks (8, "nothing to write into"), one +# outside `handoffs/` (2) or in another repository is 0 too, so the write is +# kept on HEAD (6) and never dropped with nothing written. whf_keep_if_gone() { [ -n "${whf_keep_cell:-}" ] || return 0 wkg_reg="$(git -C "$LANES_REPO" show "refs/remotes/origin/$LANES_BRANCH:$LANES_PATH" 2>/dev/null)" || return 0 wkg_row="$(printf '%s\n' "$wkg_reg" | awk -v lane="${lane:-}" "$ROW_AWK")" || return 0 [ -n "$wkg_row" ] || return 0 - [ "$(row_cell "$wkg_row" 7 | sed 's/^ *//; s/ *$//')" = "$whf_keep_cell" ] + wkg_cell="$(row_cell "$wkg_row" 7 | sed 's/^ *//; s/ *$//')" + [ "$wkg_cell" != "$whf_keep_cell" ] || return 0 + wkg_path="$(handoff_in_repo "$whf_c" "$wkg_cell")" || return 0 + wkg_rel="${wkg_path#"$whf_c"/}" + whf_in_scope "$wkg_rel" || return 0 + ! git -C "$LANES_REPO" cat-file -e "refs/remotes/origin/$LANES_BRANCH:$wkg_rel" 2>/dev/null } # S5 — W IS ONLY EVER FAST-FORWARDED, and `refresh-workspace` is the one act @@ -19467,8 +19478,9 @@ EOF # origin's new tip — a `--whole` only where origin's copy is still the # one it read (S4.4), and a `--stamp` or `--prepend` only where origin # still has the handoff it edits in place (A37, `CP_KEEP_IF_GONE`), or - # where origin's row now names another cell, which the re-run writes - # (A46); otherwise the commit is kept on HEAD and named (6). + # where origin's row now names another cell, a file under `handoffs/` + # that origin has, which the re-run writes (A46, A48); otherwise the + # commit is kept on HEAD and named (6). # STDOUT: `` once the file is written (`HANDOFF_OUT`). # 0 pushed, or nothing to commit (another repository's file, S3's refusal # of this checkout or not; LANES_NO_GIT) @@ -19758,7 +19770,8 @@ EOF # copy origin deleted is a copy that moved. A HANDOFF MOVED WITH ITS # ROW'S CELL IS REPLAYED (A46): `whf_keep_cell` is the cell this write # followed (none under `--target`), and where origin's row names another - # one, the re-run reads it and writes the moved file. + # one, a file under `handoffs/` that origin has, the re-run reads it and + # writes the moved file. Any other cell keeps the commit (6, A48). case "$whf_mode" in whole) CP_WHOLE_REL="$whf_rel"; CP_WHOLE_BLOB="$whf_blob" ;; *) CP_KEEP_IF_GONE="$whf_rel" diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index bd3deb4..719019b 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1586,3 +1586,62 @@ def test_a_moved_handoff_the_replay_cannot_follow_is_kept_on_HEAD_exit_6(box: Bo assert [line.split()[1] for line in box.unpublished(c)] == [kept] assert result.stdout == _line(f) _released(box, c) + + +#: Where a peer moves the lane's handoff OUT of `handoffs/` (A48's third case). +OUTSIDE_REL = "notes/moved-handoff.md" + + +def _peer_repoints_the_cell(box: Box, cell: str) -> None: + """In one commit the peer clone does NOT push (the race hook lands it at + this write's push), the lane's handoff leaves origin and the row's cell + stops naming it (rv2a-c3's probe N5): "moved-no-file" deletes it and + points the cell at MOVED_REL, which origin has no file for; "emptied" + deletes it and empties the cell; "moved-out-of-handoffs" `git mv`s it to + OUTSIDE_REL and points the cell there.""" + p = box.peer + box.git("-C", str(p), "fetch", "-q", "origin") + box.git("-C", str(p), "merge", "-q", "--ff-only", "refs/remotes/origin/main") + if cell == "moved-out-of-handoffs": + (p / OUTSIDE_REL).parent.mkdir(parents=True, exist_ok=True) + box.git("-C", str(p), "mv", "--", HANDOFF_REL, OUTSIDE_REL) + else: + box.git("-C", str(p), "rm", "-q", "--", HANDOFF_REL) + new = {"moved-no-file": MOVED_REL, "emptied": "", "moved-out-of-handoffs": OUTSIDE_REL}[cell] + reg = p / REGISTER + text = reg.read_text(encoding="utf-8") + assert text.count(f"| {HANDOFF_REL} |") == 1 + reg.write_text(text.replace(f"| {HANDOFF_REL} |", f"| {new} |"), encoding="utf-8") + box.git("-C", str(p), "add", "--", REGISTER) + box.git("-C", str(p), "commit", "-q", "-m", f"a peer repoints the lane's handoff cell ({cell})") + + +@pytest.mark.parametrize("cell", ["moved-no-file", "emptied", "moved-out-of-handoffs"]) +def test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEAD_exit_6(box: Box, cell: str): + """rv2a-c3's NIT 1 (its probe N5): A46 replayed wherever origin's row + named another cell, though the re-run writes only a handoff that is + there. A peer that deleted the handoff and pointed the cell at a name + origin has no file for, or emptied it, while this write's push was in + flight had the commit dropped and the re-run say "Nothing was written" + (8): the write was only in the reflog. Another cell is followed now only + to a file under `handoffs/` that origin has (A48); otherwise — no file, + no cell, or a file the re-run refuses (2, outside `handoffs/`) — A37's + keep holds: 6, the write in this checkout, one commit unpublished, + nothing replayed.""" + c = box.seam() + f = c / HANDOFF_REL + _peer_repoints_the_cell(box, cell) + box.race(prepared=True) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + err = result.stderr + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin's copy of {HANDOFF_REL} was deleted while this write's push was in flight" in err, err + assert f"kept on HEAD: {kept}" in err + assert "push refused (attempt" not in err, "nothing is dropped or replayed" + assert box.subject(c) == SUBJECT + assert f.read_text(encoding="utf-8") == stamped(HANDOFF), "the write is in this checkout" + assert not _origin_has(box, HANDOFF_REL) + assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" + assert result.stdout == _line(f) + _released(box, c) From d7522f6337f2aeeeb05cf6679fa78c2bc19a325c Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 13:38:41 +0000 Subject: [PATCH 20/34] A49: tests for the fifteen checks of `write-handoff` and `handoff-path` 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) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (cherry picked from commit 2a41e8336cc6f45764cb67ef3b51737c61527f34) --- tests/test_a23_write_handoff.py | 178 ++++++++++++++++++++++++++++++-- 1 file changed, 171 insertions(+), 7 deletions(-) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 719019b..2d5162c 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -789,9 +789,23 @@ def test_handoff_path_names_where_write_handoff_writes(tmp_path: Path, how: str) ("--stamp", STAMP, "--subject", SUBJECT, "--expected-digest", "0" * 64), ("--stamp", "two\nlines", "--subject", SUBJECT), ("--stamp", STAMP, "--subject", SUBJECT, "--restart", "op"), + ("--stamp", "", "--subject", SUBJECT), + ("--stamp", STAMP, "--subject", "two\nlines"), + ("--whole", "x", "--expect-digest", "g" * 64, "--subject", SUBJECT), + ("--stamp", STAMP, "--subject", SUBJECT, "--expect-digest", "0" * 64), + ("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, "--expected-digest", "abc"), + ("--stamp", STAMP, "--subject", SUBJECT, "--restart", "", *FENCE[1:]), ], ids=["no-mode", "no-subject", "two-modes", "whole-no-digest", "expected-no-restart", - "stamp-two-lines", "restart-short"]) + "stamp-two-lines", "restart-short", "stamp-empty", "subject-two-lines", + "whole-digest-not-hex", "expect-digest-with-stamp", "expected-digest-short", + "restart-empty-fence"]) def test_usage_is_64_and_writes_nothing(box: Box, argv): + """Every argument is checked before the lock, and a bad one is 64 with + nothing written and no fetch made. m2a's battery found six checks no case + supplied the bad value for (its A02-A10): an empty stamp, a subject of two + lines, an `--expect-digest` that is not hex, one beside `--stamp`, an + `--expected-digest` that is not 64 long, and an empty fence, which left + the operation empty, so the write went out unfenced.""" c = box.seam() box.use_net() before = box.snapshot(c) @@ -1026,30 +1040,50 @@ def test_the_recovery_under_another_S3_refusal_names_git_add_for_an_untracked_ha _released(box, c) -@pytest.mark.parametrize("how", ["target", "cell"]) +@pytest.mark.parametrize("how", ["target", "cell", "tracked-file", "git-config", "dotdot"]) def test_a_target_outside_handoffs_is_refused_and_nothing_written(box: Box, how: str): """SHOULD 1: this verb replaces a file and pushes it, so in the checkout it writes a file under `handoffs/` and nothing else. A `--target` naming the register, or a row cell that climbs out of `handoffs/` to the lane's append-only object log, is refused (2) before a byte: the checkout's copy - and origin's are as they were, and nothing is printed on stdout.""" + and origin's are as they were, and nothing is printed on stdout. The rule + is `handoffs/` itself, not only the register's and `lanes/`'s exclusions + (m2a's E01 and E02): a tracked file elsewhere in the tree, this + checkout's own `.git/config`, and a target whose last part is `..` are + refused the same way.""" + if how == "tracked-file": + box.peer_pushes("docs/plan.md", "# a plan, tracked outside handoffs/\n") c = box.seam() if how == "target": rel = REGISTER args = ("--whole", str(_prepared(box, "# not a register any more\n")), "--expect-digest", sha(c / REGISTER), "--target", REGISTER) - else: + elif how == "cell": rel = f"lanes/log/{LANE}.md" box.cell_is(f"handoffs/../{rel}") args = ("--prepend", str(_block(box))) - before, published, tip = (c / rel).read_bytes(), box.origin_file(rel), box.origin_head() + elif how == "tracked-file": + rel = "docs/plan.md" + args = ("--stamp", STAMP, "--target", str(c / rel)) + elif how == "git-config": + rel = ".git/config" + args = ("--whole", str(_prepared(box, "[core\n")), "--expect-digest", sha(c / rel), + "--target", str(c / rel)) + else: + rel = "handoffs/repoS4/.." + args = ("--stamp", STAMP, "--target", str(c / rel)) + watched = c / (HANDOFF_REL if how == "dotdot" else rel) + before, tip = watched.read_bytes(), box.origin_head() + published = None if how in ("git-config", "dotdot") else box.origin_file(rel) result = box.write_handoff(*args, "--subject", "handoff: PAUSED") assert result.returncode == 2, result.stderr assert f"is {rel} in the checkout {c}, which is not a file under handoffs/" in result.stderr assert "Nothing was written." in result.stderr - assert (c / rel).read_bytes() == before - assert box.origin_file(rel) == published + assert watched.read_bytes() == before + if published is not None: + assert box.origin_file(rel) == published assert box.origin_head() == tip + assert box.git("-C", str(c), "config", "gc.auto").strip() == "0", "git still reads the checkout's config" assert result.stdout == "" _released(box, c) @@ -1645,3 +1679,133 @@ def test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEA assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" assert result.stdout == _line(f) _released(box, c) + + +# ------------------------------------------------- m2a's battery: what no case asked + +@pytest.mark.parametrize("mode", ["prepend", "whole"]) +def test_an_empty_block_or_prepared_file_is_refused(box: Box, mode: str): + """m2a's A11: an empty `--prepend` block would put `---` and a blank + line on top of the handoff, and an empty `--whole` file would empty the + handoff and push that. Each is refused (1) before the lock, as + `publish-handoff` refuses an empty prepared file: nothing written, + committed or pushed, no fetch made, nothing on stdout.""" + c = box.seam() + f = c / HANDOFF_REL + empty = box.root / "empty.md" + empty.write_text("", encoding="utf-8") + args = (("--prepend", str(empty)) if mode == "prepend" else + ("--whole", str(empty), "--expect-digest", sha(f))) + box.use_net() + before = box.snapshot(c) + result = box.write_handoff(*args, "--subject", "handoff: PAUSED") + assert result.returncode == 1, result.stderr + assert f"the {mode} file {empty} is unreadable or empty. Nothing was written." in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF + assert box.snapshot(c) == before + assert box.net_ops() == [] + assert result.stdout == "" + _released(box, c) + + +def test_routed_a_flag_where_the_lane_goes_is_usage_and_routes_nowhere(box: Box): + """m2a's A13: routed, the lane is `write-handoff`'s first word, and a + flag there is the arm's own usage refusal (64), never a lane to route + to: no checkout is cloned for it, no fetch is made, and W and origin are + as they were.""" + box.use_net() + w_before, tip = box.w_state(), box.origin_head() + result = box.helper("write-handoff", "--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 64, result.stderr + assert "usage: write-handoff " in result.stderr + assert box.net_ops() == [] + assert not box.checkout("--stamp").exists() + assert box.w_state() == w_before + assert box.origin_head() == tip + assert result.stdout == "" + + +def test_handoff_path_for_a_row_that_names_no_handoff_is_8(box: Box): + """m2a's B02: a row whose handoff cell is empty names no file, so there + is no path to answer: 8, said, nothing on stdout (never the checkout's + own directory), and routed, `--create` clones nothing for it.""" + box.cell_is("") + for argv in ((LANE,), (LANE, "--create")): + result = box.helper("handoff-path", *argv) + assert result.returncode == 8, result.stderr + assert f"lane {LANE}'s row names no handoff" in result.stderr + assert result.stdout == "" + assert not box.checkout(LANE).exists(), "a row that names no handoff gets no checkout" + + +def test_handoff_path_reads_the_cell_from_the_register_as_published(box: Box): + """m2a's B04: `handoff-path` fetches before it reads the row, as + `write-handoff` does, so a peer that points the lane's cell at another + file after this checkout last fetched is answered with that file.""" + c = box.seam() + box.cell_is(MOVED_REL) + result = box.helper("handoff-path", LANE) + assert result.returncode == 0, result.stderr + assert result.stdout == f"{c / MOVED_REL}\n" + + +def test_handoff_path_refuses_a_lane_path_that_holds_no_clone(box: Box): + """m2a's C02: routed, the lane's checkout path is a directory that is not + a clone of the register. The path it would print is not one a write can + go through: refused (1), nothing on stdout, and nothing there deleted, + moved or written.""" + d = box.checkout(LANE) + d.mkdir(parents=True) + (d / "notes.md").write_text("a person's notes\n", encoding="utf-8") + result = box.helper("handoff-path", LANE) + assert result.returncode == 1, result.stderr + assert f"{d} is not a clone of the register: it is a directory that is not a git checkout" in result.stderr + assert result.stdout == "" + assert [p.name for p in d.iterdir()] == ["notes.md"] + assert (d / "notes.md").read_text(encoding="utf-8") == "a person's notes\n" + + +def test_handoff_path_through_a_directory_inside_the_seam_names_its_top_level(box: Box): + """m2a's C03: an explicit `LANES_REPO` that names a directory inside the + checkout is raised to that checkout's top level, so the row's + repository-relative cell is joined to the top level and never to the + directory as given. `LANES_REPO` alone would name no register there (1, + "registry not found"), so `LANES_FILE` names the checkout's.""" + c = box.seam() + box.env["LANES_REPO"] = str(c / "handoffs") + box.env["LANES_FILE"] = str(c / REGISTER) + result = box.helper("handoff-path", LANE) + assert result.returncode == 0, result.stderr + assert result.stdout == f"{c / HANDOFF_REL}\n" + + +def test_a_handoff_that_links_out_of_the_checkout_is_another_repositorys(box: Box): + """m2a's D08: the row's cell names a file of this checkout, and that file + is a link to another repository's file outside W. The path is followed + to the physical file, and that file is another repository's (Amendment + 8(d)): written there, said, its line printed, 0, committed nowhere. This + checkout and origin are as they were, the link still a link.""" + other = box.root / "other" + box.git("init", "-q", "-b", "main", str(other)) + box.write(other, "handoff.md", HANDOFF) + box.commit(other, "another repository's handoff", "handoff.md") + other_head = box.git("-C", str(other), "rev-parse", "HEAD").strip() + seed = box.seed + box.git("-C", str(seed), "pull", "-q", "--ff-only", "origin", "main") + (seed / HANDOFF_REL).unlink() + (seed / HANDOFF_REL).symlink_to(other / "handoff.md") + box.commit(seed, "the lane's handoff is a link into another repository", HANDOFF_REL) + box.git("-C", str(seed), "push", "-q", "origin", "main") + c = box.seam() + assert (c / HANDOFF_REL).is_symlink() + before = box.snapshot(c) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert "another repository's handoff (Amendment 8(d))" in result.stderr + assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) + assert box.git("-C", str(other), "rev-parse", "HEAD").strip() == other_head + assert box.git("-C", str(other), "status", "--porcelain").split() == ["M", "handoff.md"] + assert (c / HANDOFF_REL).is_symlink() + assert box.snapshot(c) == before + assert result.stdout == _line(other / "handoff.md") + _released(box, c) From bec6a4efb2e8cc5d8a5f22a3e5624f569bc1e6f9 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 17:36:31 +0000 Subject: [PATCH 21/34] A51: tests for the seven checks that the second half of m2a's mutant 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 (cherry picked from commit 37b000d5491a1660d59ed5e474e2ad2526217337) --- tests/test_a23_write_handoff.py | 178 ++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 2d5162c..1941bfb 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1809,3 +1809,181 @@ def test_a_handoff_that_links_out_of_the_checkout_is_another_repositorys(box: Bo assert box.snapshot(c) == before assert result.stdout == _line(other / "handoff.md") _released(box, c) + + +# ------------------------------------------------- m2a's battery, its second half + +def test_stamp_into_an_empty_handoff_is_refused_1_and_says_the_line(box: Box): + """m2a's G05: an empty handoff has no header block to stamp under, so the + stamp is refused (1) and the line it wants is said, as `lane-start` said + it. Without that check the splice went on, and its proof refused it in + other words (2, "did not prove out"). Nothing written, committed or + pushed.""" + c = box.seam() + f = c / HANDOFF_REL + box.write(c, HANDOFF_REL, "") + box.commit(c, "an empty handoff", HANDOFF_REL) + box.git("-C", str(c), "push", "-q", "origin", "main") + tip = box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 1, result.stderr + assert f"the handoff at {f} is empty, so it has no header block to stamp under" in result.stderr + assert f"The line it wants: {STAMP}" in result.stderr + assert "did not prove out" not in result.stderr + assert f.read_text(encoding="utf-8") == "" + assert box.origin_head() == tip + assert result.stdout == "" + _released(box, c) + + +#: A handoff the row names under `handoffs/` and nobody ever wrote. +GONE_REL = "handoffs/repoS4/no-such-handoff.md" + + +@pytest.mark.parametrize("mode", ["stamp", "prepend", "whole"]) +def test_a_row_naming_a_handoff_the_checkout_does_not_have_is_8_and_nothing_written(box: Box, mode: str): + """m2a's G06: the row names a file under `handoffs/` that is not there. + There is nothing to write into: 8, the path said (and, for `--stamp`, the + line it would have written), never "unreadable" (1). Nothing is made, + committed or pushed.""" + box.cell_is(GONE_REL) + c = box.seam() + gone = c / GONE_REL + args = {"stamp": ("--stamp", STAMP), + "prepend": ("--prepend", str(_block(box))), + "whole": ("--whole", str(_prepared(box)), "--expect-digest", "0" * 64)}[mode] + before = box.snapshot(c) + result = box.write_handoff(*args, "--subject", SUBJECT) + err = result.stderr + assert result.returncode == 8, err + assert f"lane {LANE}'s handoff is not at {gone}, so there is nothing to write into. Nothing was written." in err + assert "unreadable" not in err + said = f"the stamp line it would have written under the handoff's header block: {STAMP}" in err + assert said == (mode == "stamp"), err + assert not gone.exists() + assert not gone.is_symlink() + assert box.snapshot(c) == before + assert result.stdout == "" + _released(box, c) + + +def test_a_prepend_fenced_at_preparing_takes_expected_digest_none(box: Box): + """m2a's H03: `lane-handoff` binds its prepend at `preparing`, before the + restart has a digest, and `--expected-digest none` says so: `none` is no + digest to compare, so the fenced write goes ahead (0) and reserves + nothing. Compared as a digest, it was refused (2, "the restart handoff + changed before this owned write").""" + c = box.seam() + f = c / HANDOFF_REL + box.intent("preparing", "--expect", "none", "--operation", FENCE[0], "--generation", FENCE[1], + "--handoff", str(f)) + result = box.write_handoff("--prepend", str(_block(box)), "--subject", "handoff: PAUSED", + "--restart", FENCE[0], FENCE[1], "0", "preparing", "none", + "--expected-digest", "none") + assert result.returncode == 0, result.stderr + assert "the restart handoff changed before" not in result.stderr + assert f.read_text(encoding="utf-8") == prepended(HANDOFF) + assert box.origin_file(HANDOFF_REL) == prepended(HANDOFF) + assert box.intent_field("prepared_digest") == "none" + assert result.stdout == _line(f) + _released(box, c) + + +#: A link under `handoffs/` to the lane's handoff, which a row's cell may name. +LINK_REL = "handoffs/repoS4/link-to-handoff.md" + + +def _cell_spelled(box: Box, spelling: str) -> None: + """The peer points the lane's cell at its handoff in another spelling than + the checkout's own path: the estate's `~/...` through the estate link, or + a link under `handoffs/` to the lane's handoff, published first.""" + if spelling == "estate": + box.cell_is(_estate_link(box)) + return + seed = box.seed + box.git("-C", str(seed), "pull", "-q", "--ff-only", "origin", "main") + (seed / LINK_REL).symlink_to(Path(HANDOFF_REL).name) + box.commit(seed, "a link to the lane's handoff", LINK_REL) + box.git("-C", str(seed), "push", "-q", "origin", "main") + box.cell_is(LINK_REL) + + +@pytest.mark.parametrize("mode", ["stamp", "prepend"]) +@pytest.mark.parametrize("spelling", ["estate", "link"]) +def test_a_cell_spelled_otherwise_whose_file_a_peer_deletes_mid_push_keeps_the_write_on_HEAD_exit_6( + box: Box, spelling: str, mode: str): + """m2a's J01 and J02: A37's keep names the file by its path in this + checkout, and A46 compares origin's cell with the cell this write + followed, each in its own spelling. Where the cell is not that path (the + estate's `~/...`, or a link under `handoffs/` to the lane's handoff) and a + peer deletes the file while this write's push is in flight, the row left + as it was, the write is kept on HEAD (6) and never replayed into nothing + (8, the write only in the reflog).""" + _cell_spelled(box, spelling) + c = box.seam() + f = c / HANDOFF_REL + if mode == "stamp": + args, written = ("--stamp", STAMP), stamped + else: + args, written = ("--prepend", str(_block(box))), prepended + _peer_deletes_the_handoff(box) + box.race(prepared=True) + result = box.write_handoff(*args, "--subject", SUBJECT) + err = result.stderr + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin's copy of {HANDOFF_REL} was deleted while this write's push was in flight" in err, err + assert f"kept on HEAD: {kept}" in err + assert "push refused (attempt" not in err, "nothing is dropped or replayed" + assert box.subject(c) == SUBJECT + assert f.read_text(encoding="utf-8") == written(HANDOFF), "the write is in this checkout" + assert not _origin_has(box, HANDOFF_REL) + assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" + assert result.stdout == _line(f) + _released(box, c) + + +def test_a_peer_that_deletes_the_handoff_and_the_register_mid_push_keeps_the_write_on_HEAD_exit_6(box: Box): + """m2a's J03: where origin's register cannot be read at the replay, A46 + cannot say whether the row names another cell, and a read that failed is + never "replay" (R22). A peer's commit that deletes the lane's handoff and + the register lands while this write's push is in flight: the write is + kept on HEAD (6), one commit unpublished, nothing dropped.""" + c = box.seam() + f = c / HANDOFF_REL + p = box.peer + box.git("-C", str(p), "fetch", "-q", "origin") + box.git("-C", str(p), "merge", "-q", "--ff-only", "refs/remotes/origin/main") + box.git("-C", str(p), "rm", "-q", "--", HANDOFF_REL, REGISTER) + box.git("-C", str(p), "commit", "-q", "-m", "a peer deletes the handoff and the register") + box.race(prepared=True) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + err = result.stderr + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin's copy of {HANDOFF_REL} was deleted while this write's push was in flight" in err, err + assert f"kept on HEAD: {kept}" in err + assert "push refused (attempt" not in err, "nothing is dropped or replayed" + assert f.read_text(encoding="utf-8") == stamped(HANDOFF), "the write is in this checkout" + assert not _origin_has(box, REGISTER) + assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" + assert result.stdout == _line(f) + _released(box, c) + + +def test_a_routed_bind_of_the_rows_cell_clones_no_checkout(box: Box): + """m2a's K03: routed, the lane has no checkout yet, and + `set-restart-intent --handoff ` binds the path that + checkout will have, as `handoff-path` derives it without `--create`. A + bind writes the sidecar and nothing else: no checkout is cloned for it, + and W is as it was.""" + box.use_net() + c = box.checkout(LANE) + w_before = box.w_state() + made = _bound(box, HANDOFF_REL) + assert made.returncode == 0, made.stderr + assert f"--handoff {HANDOFF_REL} is bound as {c / HANDOFF_REL}" in made.stderr + assert box.intent_field("handoff") == str(c / HANDOFF_REL) + assert not c.exists(), "a bind makes no checkout" + assert "clone" not in box.net_ops() + assert box.w_state() == w_before From 392e7227479eaf86d05c4eb526d402722a897137 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Thu, 22 Oct 2026 20:10:50 +0000 Subject: [PATCH 22/34] A52: tests for the three checks that m2a's last re-check of slice 2a 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 (cherry picked from commit 1bb4f7566e0ed55e7a7a96d7dc1645587b7287f1) --- tests/test_a23_write_handoff.py | 70 +++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 1941bfb..f56d53f 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1154,7 +1154,10 @@ def test_a_target_inside_another_clones_git_directory_is_refused_and_nothing_wri line under `[core]`, `--whole` replaced the file, and every git command in that clone then failed (128), its own lane's S3 with them. No handoff lives in a git directory (A47): 2 before a byte, nothing written there or - here, that clone's git still reads its config, and nothing on stdout.""" + here, that clone's git still reads its config, and nothing on stdout. + A `--stamp` refused here says the line it would have written, as every + refusal of it does (m2a's N03): a stamp that is owed is a stamp somebody + writes by hand.""" c = box.seam() if where == "sibling-clone": d = box.clone(box.lanes_root / NEIGHBOUR) @@ -1176,6 +1179,8 @@ def test_a_target_inside_another_clones_git_directory_is_refused_and_nothing_wri assert f"is {cfg}, inside a git directory ({gd})" in result.stderr assert "Nothing was written." in result.stderr assert "another repository's handoff" not in result.stderr + said = f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert said == (mode == "stamp"), result.stderr assert cfg.read_bytes() == before assert box.git("--git-dir", str(gd), "config", "core.bare").strip() == ( "true" if d is None else "false"), "that clone's git still reads its config" @@ -1626,13 +1631,20 @@ def test_a_moved_handoff_the_replay_cannot_follow_is_kept_on_HEAD_exit_6(box: Bo OUTSIDE_REL = "notes/moved-handoff.md" +def _elsewhere(box: Box) -> Path: + """A file in another repository's folder, which nobody wrote.""" + return box.root / "elsewhere" / "handoff.md" + + def _peer_repoints_the_cell(box: Box, cell: str) -> None: """In one commit the peer clone does NOT push (the race hook lands it at this write's push), the lane's handoff leaves origin and the row's cell stops naming it (rv2a-c3's probe N5): "moved-no-file" deletes it and points the cell at MOVED_REL, which origin has no file for; "emptied" deletes it and empties the cell; "moved-out-of-handoffs" `git mv`s it to - OUTSIDE_REL and points the cell there.""" + OUTSIDE_REL and points the cell there; "another-repository" deletes it + and points the cell at a path in a folder of another repository, which + this checkout cannot translate (m2a's N05).""" p = box.peer box.git("-C", str(p), "fetch", "-q", "origin") box.git("-C", str(p), "merge", "-q", "--ff-only", "refs/remotes/origin/main") @@ -1641,7 +1653,10 @@ def _peer_repoints_the_cell(box: Box, cell: str) -> None: box.git("-C", str(p), "mv", "--", HANDOFF_REL, OUTSIDE_REL) else: box.git("-C", str(p), "rm", "-q", "--", HANDOFF_REL) - new = {"moved-no-file": MOVED_REL, "emptied": "", "moved-out-of-handoffs": OUTSIDE_REL}[cell] + if cell == "another-repository": + _elsewhere(box).parent.mkdir() # the folder is there, the file is not + new = {"moved-no-file": MOVED_REL, "emptied": "", "moved-out-of-handoffs": OUTSIDE_REL, + "another-repository": str(_elsewhere(box))}[cell] reg = p / REGISTER text = reg.read_text(encoding="utf-8") assert text.count(f"| {HANDOFF_REL} |") == 1 @@ -1650,7 +1665,7 @@ def _peer_repoints_the_cell(box: Box, cell: str) -> None: box.git("-C", str(p), "commit", "-q", "-m", f"a peer repoints the lane's handoff cell ({cell})") -@pytest.mark.parametrize("cell", ["moved-no-file", "emptied", "moved-out-of-handoffs"]) +@pytest.mark.parametrize("cell", ["moved-no-file", "emptied", "moved-out-of-handoffs", "another-repository"]) def test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEAD_exit_6(box: Box, cell: str): """rv2a-c3's NIT 1 (its probe N5): A46 replayed wherever origin's row named another cell, though the re-run writes only a handoff that is @@ -1659,9 +1674,10 @@ def test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEA flight had the commit dropped and the re-run say "Nothing was written" (8): the write was only in the reflog. Another cell is followed now only to a file under `handoffs/` that origin has (A48); otherwise — no file, - no cell, or a file the re-run refuses (2, outside `handoffs/`) — A37's - keep holds: 6, the write in this checkout, one commit unpublished, - nothing replayed.""" + no cell, a file the re-run refuses (2, outside `handoffs/`), or a path + this checkout cannot translate, in another repository (m2a's N05) — + A37's keep holds: 6, the write in this checkout, one commit unpublished, + nothing replayed, and nothing written in that other repository either.""" c = box.seam() f = c / HANDOFF_REL _peer_repoints_the_cell(box, cell) @@ -1677,6 +1693,7 @@ def test_a_cell_a_peer_repoints_where_no_replay_can_write_keeps_the_write_on_HEA assert f.read_text(encoding="utf-8") == stamped(HANDOFF), "the write is in this checkout" assert not _origin_has(box, HANDOFF_REL) assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" + assert not _elsewhere(box).exists(), "nothing was written in another repository either" assert result.stdout == _line(f) _released(box, c) @@ -1971,6 +1988,45 @@ def test_a_peer_that_deletes_the_handoff_and_the_register_mid_push_keeps_the_wri _released(box, c) +def test_a_cell_a_peer_pads_otherwise_is_the_same_cell_and_the_write_is_kept_on_HEAD_exit_6(box: Box): + """m2a's J04b: A46 compares origin's cell with the cell this write + followed once the blanks round it are trimmed, as the register's readers + take it: padding is no part of a cell. A peer that moves the handoff to a + file whose name ends in a blank and writes the row's cell unpadded, with + that one trailing blank, has not pointed the cell anywhere else, so the + write is kept on HEAD (6) and is not replayed into the file whose name + ends in a blank (compared untrimmed, it was "another cell", a file origin + has: the commit dropped and the re-run writing there).""" + c = box.seam() + f = c / HANDOFF_REL + p = box.peer + odd = HANDOFF_REL + " " + box.git("-C", str(p), "fetch", "-q", "origin") + box.git("-C", str(p), "merge", "-q", "--ff-only", "refs/remotes/origin/main") + box.git("-C", str(p), "mv", "--", HANDOFF_REL, odd) + reg = p / REGISTER + text = reg.read_text(encoding="utf-8") + assert text.count(f"| {HANDOFF_REL} |") == 1 + reg.write_text(text.replace(f"| {HANDOFF_REL} |", f"|{HANDOFF_REL} |"), encoding="utf-8") + box.git("-C", str(p), "add", "--", REGISTER) + box.git("-C", str(p), "commit", "-q", "-m", "a peer renames the file to a blank-ended name, cell unpadded") + box.race(prepared=True) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + err = result.stderr + assert result.returncode == 6, err + kept = box.head(c) + assert f"origin's copy of {HANDOFF_REL} was deleted while this write's push was in flight" in err, err + assert f"kept on HEAD: {kept}" in err + assert "push refused (attempt" not in err, "nothing is dropped or replayed" + assert f.read_text(encoding="utf-8") == stamped(HANDOFF), "the write is in this checkout" + names = box.git("--git-dir", str(box.origin), "ls-tree", "-r", "--name-only", "main").splitlines() + assert HANDOFF_REL not in names and odd in names + assert not (c / odd).exists(), "nothing was written into the blank-ended name" + assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" + assert result.stdout == _line(f) + _released(box, c) + + def test_a_routed_bind_of_the_rows_cell_clones_no_checkout(box: Box): """m2a's K03: routed, the lane has no checkout yet, and `set-restart-intent --handoff ` binds the path that From cee03460a3428013234bedff6c30584bc0e327a4 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 15:37:06 +0000 Subject: [PATCH 23/34] A53: a test for 606, the write whose commit an unlocked writer's lands 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 -m "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 , not this write's commit "), 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 (cherry picked from commit ed63c171e78ac88a76158200826edfebb49ca734) --- tests/test_a23_write_handoff.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index f56d53f..e98ebf0 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -38,7 +38,7 @@ from a23_box import ME, REGISTER from conftest import WINDOWS_SKIP -from test_a23_publish_replay import LANE, NEIGHBOUR, Box as ReplayBox +from test_a23_publish_replay import LANE, NEIGHBOUR, Box as ReplayBox, _git_shim, _stamp pytestmark = [pytest.mark.skipif(shutil.which("bash") is None, reason="lanes-edit.sh is a bash script"), @@ -2043,3 +2043,30 @@ def test_a_routed_bind_of_the_rows_cell_clones_no_checkout(box: Box): assert not c.exists(), "a bind makes no checkout" assert "clone" not in box.net_ops() assert box.w_state() == w_before + + +# ------------------------------------------- lane openRepoTools-3's review of #312 + +def test_a_commit_landing_just_after_this_writes_commit_is_told_by_its_trailer_exit_6(box: Box): + """606: slice 1b's 291 tells this write's commit by its message where an + unlocked writer's lands just after `git commit`. This write's message has + the `Lane:` trailer as its second paragraph, and the subject alone never + matched what git stored, so every such race was "cannot be told" (6, + nothing named). Given the whole message, this write's commit is found + under the stamp and named, nothing is replayed, and both are kept on HEAD + (6), as `set-row-state`'s are.""" + c = box.seam() + box.race() + env = _git_shim(box, '*" commit -q -m handoff("*', after=_stamp(c)) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, **env) + assert (box.root / "fired").exists(), "the stamp landed just after this write's commit" + assert result.returncode == 6, result.stderr + err = result.stderr + stamp, mine = box.head(c), box.git("-C", str(c), "rev-parse", "HEAD~1").strip() + assert box.subject(c, "HEAD~1") == SUBJECT, "this write's commit is under the stamp" + assert box.git("-C", str(c), "log", "-1", "--format=%B", mine).strip().splitlines()[1:] == ["", f"Lane: {LANE}"] + assert "cannot be told from another made in this checkout" not in err, err + assert f"HEAD is {stamp}, not this write's commit {mine}" in err, err + assert "push refused (attempt 1 of 6)" not in err, "nothing is replayed" + assert (c / "handoffs" / "stamp.md").is_file(), "the other writer's file is still there" + assert len(box.unpublished(c)) == 2, "both kept on HEAD" From 54fa27b4b214432f360fed9b15de431c3e015a6d Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 18:20:29 +0000 Subject: [PATCH 24/34] A54: 601 in the translation the three verbs share: `write-handoff`, `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 `-*` checkout that is not this one (a sibling lane's `-lanes//`, a per-profile `-/` 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 (cherry picked from commit 6d861d413750ed4833f808bfc8ea8550c59081a2) --- lanes-edit.sh | 117 ++++++++++++++++++-- tests/test_a23_write_handoff.py | 187 +++++++++++++++++++++++++++++--- 2 files changed, 282 insertions(+), 22 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 6123e5c..db2f5c4 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -457,7 +457,8 @@ # or the directory its lock lives in, removed while the write waited for # that lock (360: lane-end's sweep of a CLOSED lane), said at once; # `handoff-path` and `write-handoff`, a register that could not be read -# (R22: never 8) +# (R22: never 8), and `write-handoff`'s target under a repository that +# git could not read (R22: never "no repository") # 2 refusal: bad arguments, the object is held, an unknown alias, — # Amendment 15 — a register holding # two rows whose lane names differ only by case, which every writer and @@ -489,7 +490,11 @@ # edit; and — slice 2a — `write-handoff`'s handoff # that resolves into W through a link (S5), a target in the checkout, or # in another clone of the register, that is not a file under `handoffs/` -# (S2), a target inside a git directory, a restart's bound file or +# (S2), a target inside a git directory, a target in W or in another +# checkout of the register beside it (`-lanes//`, `-/`; +# a lane's handoff is written in its own checkout only, and +# `handoff-path` and `set-restart-intent --handoff` refuse the same +# handoff by the same check, 601), a restart's bound file or # pre-image that is not this one, or a splice that does not prove out, # each with nothing written; while S3's refusal of a `--prepend` or a # `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed @@ -16560,6 +16565,41 @@ handoff_in_checkout() { # — 0 its path in th hic_p="$(lane_checkout_path "$1")" || return 1 handoff_in_repo "$hic_p" "$2" } +# A LANE'S HANDOFF IS A FILE OF ITS OWN CHECKOUT, NEVER OF W OR OF ANOTHER +# CHECKOUT BESIDE IT (lane openRepoTools-3's review of #312, 601; its +# correction, #312 6368669583; finding 723 of its review of #316). The three +# verbs that translate a handoff — `write-handoff`, `handoff-path` and +# `set-restart-intent --handoff` — ask this ONE question of the path +# `handoff_in_repo` gave them, so whatever one refuses the others refuse: a +# path that is physically in W where W is not the checkout (S5, the readers' +# copy), or in a checkout beside W that is not this one — a sibling lane's +# `-lanes//` (S1), a per-profile `-/` clone. The PHYSICAL +# path is asked about, so any spelling — absolute, or climbing with `..` — +# lands where it lands; and it is asked BY PATH, asking git nothing, so a +# clone git cannot read is refused the same. In `write-handoff` alone, a +# sibling's `handoffs/` file was written there as another repository's, +# uncommitted, and that lane's next `--stamp` refused at S3 step 4 over a +# change it never made; while `lane-start` still delivered the sibling's top +# block as this lane's first prompt, because `handoff-path` printed its path. +# Sets HBW_WHY: the words that follow "is" in each verb's refusal. +HBW_WHY="" +handoff_beside_w() { # — 0 refused, HBW_WHY set; 1 not + HBW_WHY="" + [ -n "$LANES_WS_REAL" ] || return 1 + hbw_c="$(cd -P -- "$1" 2>/dev/null && pwd -P)" || hbw_c="$1" + hbw_p="$(path_physical "$2")" || hbw_p="$2" + if [ "$hbw_c" != "$LANES_WS_REAL" ] && path_under "$hbw_p" "$LANES_WS_REAL"; then + HBW_WHY="$hbw_p, inside the workspace checkout $LANES_WS_REAL, the readers' copy, where no tool writes (Amendment 23 S5), not in the checkout $hbw_c this lane's writes go through" + return 0 + fi + path_under "$hbw_p" "$hbw_c" && return 1 + case "$hbw_p" in + "$LANES_WS_REAL"-*/*) + HBW_WHY="$hbw_p, in another checkout of the register beside W ($LANES_WS_REAL), not in the checkout $hbw_c this lane's writes go through: a lane's handoff is written in its own checkout only (Amendment 23 S1, S2)" + return 0 ;; + esac + return 1 +} # THE CHECKOUT A LANE'S HANDOFF IS WRITTEN THROUGH, for the two acts that answer # it without writing (`handoff-path`, `set-restart-intent`): S1's path under the # opt-in with no explicit `LANES_REPO` — made first with `create` 1, or checked @@ -19872,9 +19912,12 @@ EOF # link, refuses (S5, 2), and so does one in the checkout, or in another # clone of the register, that is not a file under `handoffs/` (S2: # never the register or `lanes/`), and one inside a git directory — - # another clone's `.git/`, a bare clone. One in ANOTHER REPOSITORY - # (Amendment 8(d)) is written there and never committed, and S3's - # refusal of this checkout does not stop it. + # another clone's `.git/`, a bare clone — and any path in W or in a + # checkout beside it (`-lanes//`, `-/`): a lane's + # handoff is written in its own checkout only (601). Where git cannot + # read a repository on the way up from the target, it refuses (1, R22). + # One in ANOTHER REPOSITORY (Amendment 8(d)) is written there and never + # committed, and S3's refusal of this checkout does not stop it. # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -19902,9 +19945,11 @@ EOF # 0 pushed, or nothing to commit (another repository's file, S3's refusal # of this checkout or not; LANES_NO_GIT) # 1 environment: a checkout that cannot be entered, an unreadable file, - # a register that could not be read (R22: never 8) + # a register that could not be read (R22: never 8), a repository on the + # way up from the target that git could not read (R22) # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2) or - # is inside a git directory, a restart's bound file or pre-image, a proof + # is inside a git directory, one in a checkout beside W that is not this + # one, a restart's bound file or pre-image, a proof # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named # 7 a restart fence, or the handoff changed under the write # 8 no handoff to write: no row for the lane (with `--target` too), the @@ -20047,18 +20092,48 @@ EOF # there then failed (128), its own lane's S3 with them. No handoff of # Amendment 8(d)'s lives in a git directory, so it is refused first. if [ "$whf_other" = 1 ]; then + # git is asked in the NEAREST DIRECTORY THERE IS on the way up, so a + # target whose own directory is missing is still read where it would be. whf_gd="$(dirname -- "$whf_phys")" + while [ ! -d "$whf_gd" ] && [ "$whf_gd" != / ] && [ "$whf_gd" != . ]; do whf_gd="$(dirname -- "$whf_gd")"; done if [ "$(git -C "$whf_gd" rev-parse --is-inside-git-dir 2>/dev/null || :)" = true ]; then whf_stamp_said die "lane $lane's handoff target $whf_path is $whf_phys, inside a git directory ($whf_gd), where git keeps a repository's own files (another clone's .git/, or a bare clone): write-handoff writes a lane's handoff and nothing else, and no handoff lives in a git directory (Amendment 23 S2; Amendment 8(d)'s is a file of a work tree). Nothing was written." 2 fi - whf_otop="$(git -C "$(dirname -- "$whf_phys")" rev-parse --show-toplevel 2>/dev/null || :)" + whf_otop="$(git -C "$whf_gd" rev-parse --show-toplevel 2>/dev/null || :)" whf_otop="$( [ -n "$whf_otop" ] && cd -P -- "$whf_otop" 2>/dev/null && pwd -P || :)" if [ -n "$whf_otop" ] && { [ -e "$whf_otop/$LANES_PATH" ] || [ -L "$whf_otop/$LANES_PATH" ]; } && path_under "$whf_phys" "$whf_otop" && ! whf_in_scope "${whf_phys#"$whf_otop"/}"; then whf_stamp_said die "lane $lane's handoff target $whf_path is ${whf_phys#"$whf_otop"/} in $whf_otop, another checkout of the register (its top level carries $LANES_PATH too), which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never a register or anything under lanes/, in this checkout or in another clone of it (Amendment 23 S2). Nothing was written." 2 fi + # A LANE'S HANDOFF IS WRITTEN IN ITS OWN CHECKOUT ONLY (601; the check + # `handoff-path` and `set-restart-intent` share, `handoff_beside_w`): any + # path in a checkout beside W — a sibling lane's `-lanes//`, a + # per-profile `-/` clone — is refused by path, so a clone git + # cannot read is never A44's and A47's "no repository" there. W itself + # is S5's, above, wherever this checkout is not W. + if handoff_beside_w "$whf_c" "$whf_path"; then + whf_stamp_said + die "lane $lane's handoff target $whf_path is $HBW_WHY. Nothing was written." 2 + fi + # A READ THAT FAILED IS NEVER "NO REPOSITORY" (601's second half, R22). + # Where git gave no top level, the target is in no work tree only if no + # `.git` is on the way up from it; one that is there is a repository git + # could not read here (a broken config, another owner), whose register + # or git directory A44 and A47 could not see. Refused, nothing written. + if [ -z "$whf_otop" ]; then + whf_up="$whf_gd" + while :; do + if [ -e "$whf_up/.git" ] || [ -L "$whf_up/.git" ]; then + whf_gerr="$(git -C "$whf_gd" rev-parse --show-toplevel 2>&1 >/dev/null | head -n 1)" + whf_stamp_said + die "lane $lane's handoff target $whf_path is $whf_phys, under $whf_up, which holds a git repository that git could not read here (${whf_gerr:-no top level}), so whether it is another clone of the register, or its git directory, cannot be said: a read that failed is never 'no repository' (R22). Nothing was written." 1 + fi + [ "$whf_up" != / ] && [ "$whf_up" != . ] || break + whf_up="$(dirname -- "$whf_up")" + done + fi fi # ANOTHER REPOSITORY'S HANDOFF IS NOT THIS CHECKOUT'S WRITE (rv2a's # pre-review of slice 2a, SHOULD 2). It is written there and committed @@ -20209,7 +20284,8 @@ EOF # 0 the path (another repository's too, said on stderr) · 8 no row, or a # row that names no handoff, and nothing cloned · 1 a checkout there that # is not a clone, or a register that could not be read (R22: never 8) · - # 2 a name refused, + # 2 a name refused, a handoff in W or in another checkout beside it + # (601, as `write-handoff` refuses it; nothing on stdout), # or the clone failed · 4 the creation lock · 64 usage handoff-path) hp_lane=""; hp_create=0 @@ -20242,6 +20318,13 @@ EOF fi handoff_checkout_of "$hp_lane" "$hp_create" hp_rc=0; hp_path="$(handoff_in_repo "$HCO_PATH" "$hp_cell")" || hp_rc=$? + # WHAT `write-handoff` REFUSES, NO PATH IS PRINTED FOR (finding 723 of + # lane openRepoTools-3's review of #316): a caller binds a restart to this + # path and reads its top block as the lane's first prompt, so a handoff in + # W or in another checkout beside it is refused here by the same check. + if handoff_beside_w "$HCO_PATH" "$hp_path"; then + die "lane $hp_lane's handoff $hp_cell is $HBW_WHY: \`write-handoff\` refuses it, so no path is printed for a restart to bind or a launch to read (601). Nothing was written." 2 + fi [ "$hp_rc" = 0 ] || note "$hp_path is another repository's handoff (Amendment 8(d)): \`write-handoff\` writes it there and commits it nowhere" printf '%s\n' "$hp_path" ;; @@ -20413,6 +20496,22 @@ EOF /*) note "--handoff $sri_hf_cell is bound as $sri_hf: the row's spelling, translated into the checkout lane $lane's writes go through, or another repository's file as it is (Amendment 23 S2)" ;; *) die "--handoff $sri_hf_cell could not be made an absolute path in the checkout lane $lane's writes go through (got '$sri_hf'). Nothing was written." 1 ;; esac + if handoff_beside_w "$HCO_PATH" "$sri_hf"; then + die "--handoff $sri_hf_cell is $HBW_WHY: \`write-handoff\` refuses it, so no restart is bound to it (601). Nothing was written." 2 + fi + elif [ -n "$sri_hf" ] && [ -n "$LANES_WS_REAL" ]; then + # AN ABSOLUTE PATH IS KEPT AS GIVEN (W's spelling is every older + # caller's, and `write-handoff` translates it on its side), but one that + # lands in a checkout beside W is asked the same question as a cell + # (601): only the lane's own checkout there is its handoff's. + sri_hf_phys="$(path_physical "$sri_hf")" || sri_hf_phys="$sri_hf" + case "$sri_hf_phys" in + "$LANES_WS_REAL"-*/*) + handoff_checkout_of "$lane" 0 + if handoff_beside_w "$HCO_PATH" "$sri_hf"; then + die "--handoff $sri_hf is $HBW_WHY: \`write-handoff\` refuses it, so no restart is bound to it (601). Nothing was written." 2 + fi ;; + esac fi # Select the physical intent under the CAS mutex: a waiting writer must # see a reservation another writer established before releasing the lock. diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index e98ebf0..491176d 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1125,24 +1125,185 @@ def test_a_target_in_another_clone_of_the_register_keeps_the_handoffs_rule(box: _released(box, c) -def test_a_handoff_in_another_clone_of_the_register_is_still_another_repositorys(box: Box): - """A44's bound: in another clone of the register, a file under - `handoffs/` passes the rule and is written as another repository's - handoff (8(d)), committed nowhere, exit 0, as before. Whether such a file - is refused too is question 3's, for lane openRepoTools-3.""" +def _beside_w_dir(box: Box, where: str) -> Path: + """Where another checkout of the register BESIDE W is: a sibling lane's + `-lanes//` (S1), or a per-profile `-/` clone.""" + if where == "lanes-sibling": + return box.lanes_root / NEIGHBOUR + return box.w.parent / f"{box.w.name}-p2" + + +def _beside_w(box: Box, where: str) -> Path: + """Another checkout of the register beside W, cloned.""" + return box.clone(_beside_w_dir(box, where)) + + +def _climb(box: Box, f: Path) -> str: + """`f` spelled as a repository-relative cell of the seam `/c` that + climbs out of it with `..` (lane 3's 723 scenario's second spelling).""" + return "handoffs/../" + os.path.relpath(f, box.root / "c") + + +@pytest.mark.parametrize("how", ["target", "abs-cell", "climb-cell"]) +@pytest.mark.parametrize("where", ["lanes-sibling", "profile-clone"]) +def test_a_handoff_in_another_checkout_of_the_register_beside_W_is_refused(box: Box, where: str, how: str): + """601 (MUST) and Q3 of lane openRepoTools-3's review of #312. A file + under `handoffs/` in another checkout of the register beside W was taken + for another repository's handoff (8(d)): written there, uncommitted, + exit 0, and that lane's own next `--stamp` refused at S3 step 4 over a + change it never made, its next `--prepend` printed a recovery that would + commit this lane's line under its own trailer. This test pinned that as + `test_a_handoff_in_another_clone_of_the_register_is_still_another_repositorys` + until lane 3's ruling. A lane's handoff is written in its own checkout + only, so the path refuses it (2), by `--target` or by an absolute cell: + nothing written there or here, the stamp line said, nothing on stdout.""" + if how == "abs-cell": + box.cell_is(str(_beside_w_dir(box, where) / HANDOFF_REL)) + if how == "climb-cell": + box.cell_is(_climb(box, _beside_w_dir(box, where) / HANDOFF_REL)) c = box.seam() - d = box.clone(box.lanes_root / NEIGHBOUR) + d = _beside_w(box, where) f = d / HANDOFF_REL - result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--target", str(f)) - assert result.returncode == 0, result.stderr - assert f"{f} is another repository's handoff (Amendment 8(d))" in result.stderr - assert f.read_text(encoding="utf-8") == stamped(HANDOFF) - assert box.git("-C", str(d), "status", "--porcelain").split() == ["M", HANDOFF_REL] - assert box.head(c) == box.origin_head() - assert result.stdout == _line(f) + args = ("--target", str(f)) if how == "target" else () + text, theirs, mine = f.read_bytes(), box.snapshot(d), box.snapshot(c) + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, *args) + assert result.returncode == 2, result.stderr + assert f"is {f}, in another checkout of the register beside W ({box.w})" in result.stderr + assert "Nothing was written." in result.stderr + assert "another repository's handoff" not in result.stderr + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert f.read_bytes() == text + assert box.snapshot(d) == theirs, "that lane's checkout is as it was: nothing pending for its S3" + assert box.snapshot(c) == mine + assert result.stdout == "" + _released(box, c) + + +def _unreadable(box: Box, d: Path, how: str, c: Path) -> dict: + """Make the clone at `d` one git cannot read: a broken line in its + config, or another owner (git's `dubious ownership`, with this box's own + repositories named safe). Returns the environment the write runs with.""" + extra = {} + if how == "bad-config": + with open(d / ".git" / "config", "a", encoding="utf-8") as fh: + fh.write("[broken\n") + else: + safe = (c, box.w, box.origin, box.seed, box.peer) + (box.home / ".gitconfig").write_text( + "[safe]\n" + "".join(f"\tdirectory = {x}\n" for x in safe), encoding="utf-8") + extra = {"GIT_TEST_ASSUME_DIFFERENT_OWNER": "1"} + probe = subprocess.run(["git", "-C", str(d), "rev-parse", "--show-toplevel"], + env=dict(box.env, **extra), capture_output=True, text=True, check=False) + assert probe.returncode != 0, ("git can still read the clone", probe.stdout) + mine = subprocess.run(["git", "-C", str(c), "rev-parse", "--show-toplevel"], + env=dict(box.env, **extra), capture_output=True, text=True, check=False) + assert mine.returncode == 0, ("git cannot read this checkout either", mine.stderr) + return extra + + +@pytest.mark.parametrize("how", ["bad-config", "other-owner"]) +@pytest.mark.parametrize("where", ["beside-W", "elsewhere"]) +def test_a_clone_git_cannot_read_is_never_taken_for_no_repository(box: Box, where: str, how: str): + """601's second half: A44 and A47 asked git about the other clone and + read a failed answer as "no repository" (against R22), so where git + could not read that clone — a broken config line, or another owner — + `--whole` replaced its register in place, exit 0, which A44 exists to + refuse. Beside W the path refuses it (2); anywhere else, a `.git` on the + way up from the target that git gave no answer for refuses (1, R22). + Nothing written there or here, nothing on stdout.""" + c = box.seam() + d = box.clone(box.lanes_root / NEIGHBOUR if where == "beside-W" else box.root / "elsewhere-clone") + reg = d / REGISTER + before, mine = reg.read_bytes(), box.snapshot(c) + extra = _unreadable(box, d, how, c) + result = box.write_handoff("--whole", str(_prepared(box, "# not a register any more\n")), + "--expect-digest", sha(reg), "--subject", "handoff: PAUSED", + "--target", str(reg), **extra) + if where == "beside-W": + assert result.returncode == 2, result.stderr + assert f"is {reg}, in another checkout of the register beside W ({box.w})" in result.stderr + else: + assert result.returncode == 1, result.stderr + assert f"is {reg}, under {d}, which holds a git repository that git could not read here" in result.stderr + assert "a read that failed is never 'no repository' (R22)" in result.stderr + assert "Nothing was written." in result.stderr + assert "another repository's handoff" not in result.stderr + assert reg.read_bytes() == before + assert box.snapshot(c) == mine + assert result.stdout == "" _released(box, c) +HANDOFF_PATH_REFUSED = [("W", "climb-cell"), ("lanes-sibling", "abs-cell"), ("lanes-sibling", "climb-cell"), + ("profile-clone", "abs-cell"), ("profile-clone", "climb-cell")] + + +@pytest.mark.parametrize("where,how", HANDOFF_PATH_REFUSED) +def test_handoff_path_refuses_what_write_handoff_refuses(box: Box, where: str, how: str): + """Finding 723 of lane openRepoTools-3's review of #316, and its + correction for 601 (#312 6368669583): with the refusal in `write-handoff` + alone, `handoff-path` still printed a sibling checkout's handoff, and + `lane-start` delivered that sibling's top block as this lane's first + prompt. The three verbs now share one check, so `handoff-path` refuses + what `write-handoff` refuses (2): a cell that lands in W (S5), or in + another checkout of the register beside W, absolute or climbing with + `..`. No path on stdout, the refusal named, nothing written there or + here. W's own absolute spelling is still translated into the checkout + (S2), so W is reached here only by climbing.""" + if where == "W": + box.w_current() + d = box.w + else: + d = _beside_w(box, where) + f = d / HANDOFF_REL + assert f.is_file() + box.cell_is(str(f) if how == "abs-cell" else _climb(box, f)) + c = box.seam() + theirs, mine = box.snapshot(d), box.snapshot(c) + result = box.helper("handoff-path", LANE) + assert result.returncode == 2, result.stderr + if where == "W": + assert f"is {f}, inside the workspace checkout {box.w}, the readers' copy" in result.stderr + else: + assert f"is {f}, in another checkout of the register beside W ({box.w}), not in the checkout {c}" in result.stderr + assert "`write-handoff` refuses it, so no path is printed" in result.stderr + assert "another repository's handoff" not in result.stderr + assert result.stdout == "" + assert box.snapshot(d) == theirs + assert box.snapshot(c) == mine + + +@pytest.mark.parametrize("how", ["abs", "abs-climb", "estate-cell"]) +@pytest.mark.parametrize("where", ["lanes-sibling", "profile-clone"]) +def test_set_restart_intent_refuses_a_handoff_beside_W_that_is_not_the_lanes(box: Box, where: str, how: str): + """601 in the third verb that translates a handoff: `set-restart-intent + --handoff` binds a restart to a file `write-handoff` would then refuse, + so it refuses the same file by the same check (2), whether the path is + absolute, absolute and climbing with `..`, or the estate's `~/` cell: + no intent written, the refusal named, nothing on stdout. W's absolute + path is still kept as given (every older caller's binding).""" + c = box.seam() + d = _beside_w(box, where) + f = d / HANDOFF_REL + assert f.is_file() + if how == "abs": + spelled = str(f) + elif how == "abs-climb": + spelled = str(c / "handoffs" / ".." / os.path.relpath(f, c)) + else: + spelled = "~/" + os.path.relpath(f, box.home) + theirs, mine = box.snapshot(d), box.snapshot(c) + made = _bound(box, spelled) + assert made.returncode == 2, made.stderr + assert (f"--handoff {spelled} is {f}, in another checkout of the register beside W ({box.w}), " + f"not in the checkout {c}") in made.stderr + assert "Nothing was written." in made.stderr + assert made.stdout == "" + assert not (box.state / LANE / "restart-intent.yaml").exists() + assert box.snapshot(d) == theirs + assert box.snapshot(c) == mine + + @pytest.mark.parametrize("mode", ["stamp", "whole"]) @pytest.mark.parametrize("where", ["sibling-clone", "bare-clone"]) def test_a_target_inside_another_clones_git_directory_is_refused_and_nothing_written( From 6eb236e4d1f00ef432817030e776921f255dd198 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 20:43:36 +0000 Subject: [PATCH 25/34] A55: 602, a repository-relative cell or `--target` whose `.` or `..` 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 `, 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 (cherry picked from commit 31c61986dda9169bc6b95c7ae79186df9bde9740) --- lanes-edit.sh | 63 ++++++++++++-- tests/test_a23_write_handoff.py | 146 ++++++++++++++++++++++++++++++-- 2 files changed, 196 insertions(+), 13 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index db2f5c4..a3c509a 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -494,7 +494,9 @@ # checkout of the register beside it (`-lanes//`, `-/`; # a lane's handoff is written in its own checkout only, and # `handoff-path` and `set-restart-intent --handoff` refuse the same -# handoff by the same check, 601), a restart's bound file or +# handoff by the same check, 601), a relative cell or `--target` whose +# `.` or `..` components climb out of the checkout (S2, 602; `handoff-path` +# refuses that cell too), a restart's bound file or # pre-image that is not this one, or a splice that does not prove out, # each with nothing written; while S3's refusal of a `--prepend` or a # `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed @@ -16644,6 +16646,41 @@ whf_in_scope() { # — 0 in scope; 1 if [ -n "$LANES_PREFIX" ]; then case "$1" in "$LANES_PREFIX"*) return 1 ;; esac; fi return 0 } +# A REPOSITORY-RELATIVE SPELLING NAMES A FILE OF THE CHECKOUT IT IS JOINED TO +# (Amendment 23 S2; lane openRepoTools-3's review of #312, 602), so one whose +# `.` or `..` components climb out of that checkout names no other +# repository's handoff: `write-handoff` and `handoff-path` refuse it (2), as +# `set-restart-intent --handoff` refuses the same spelling (64). It climbs out +# by its TEXT (a `..` above the checkout's top level, whether or not the +# directories it climbs through exist yet), or, where the checkout is given +# and the spelling resolves, by the FILE it names (a `..` after a link out). +# A spelling with no such component is not this rule's: a link that carries +# it out stays Amendment 8(d)'s (m2a's D08), and one that climbs within the +# checkout is the checkout's own file. THE SPELLING IS ASKED FIRST (A55), in +# both verbs, before where the path lands (S5, 601's `handoff_beside_w`): a +# relative spelling that climbs out is refused for what it is, wherever it +# lands, as `set-restart-intent`'s 64 already was, and `handoff-path` +# refuses it before `--create` clones anything. +handoff_rel_climbs_out() { # — 0 it climbs out; 1 not + case "$2" in '~'|'~/'*|/*) return 1 ;; esac + case "/$2/" in */../*|*/./*) : ;; *) return 1 ;; esac + hrco_d=0; hrco_rest="$2" + while [ -n "$hrco_rest" ]; do + case "$hrco_rest" in + */*) hrco_part="${hrco_rest%%/*}"; hrco_rest="${hrco_rest#*/}" ;; + *) hrco_part="$hrco_rest"; hrco_rest="" ;; + esac + case "$hrco_part" in + ''|.) : ;; + ..) hrco_d=$((hrco_d - 1)); [ "$hrco_d" -ge 0 ] || return 0 ;; + *) hrco_d=$((hrco_d + 1)) ;; + esac + done + [ -n "$1" ] || return 1 + hrco_p="$(path_physical "$1/$2")" || return 1 + path_under "$hrco_p" "$1" && return 1 + return 0 +} # A37'S KEEP, ONLY WHERE A REPLAY COULD NOT FOLLOW THE FILE (rv2a-c2's # re-review of slice 2a, NIT 2; A46). Slice 1b's `cp_replay` asks it before it # keeps a write whose in-place file origin no longer has, of origin as @@ -19914,8 +19951,11 @@ EOF # never the register or `lanes/`), and one inside a git directory — # another clone's `.git/`, a bare clone — and any path in W or in a # checkout beside it (`-lanes//`, `-/`): a lane's - # handoff is written in its own checkout only (601). Where git cannot - # read a repository on the way up from the target, it refuses (1, R22). + # handoff is written in its own checkout only (601). A relative cell + # or `--target` whose `.` or `..` components climb out of the checkout + # is refused first, for its spelling, wherever it lands (602, A55). + # Where git cannot read a repository on the way up from the target, it + # refuses (1, R22). # One in ANOTHER REPOSITORY (Amendment 8(d)) is written there and never # committed, and S3's refusal of this checkout does not stop it. # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a @@ -19949,7 +19989,8 @@ EOF # way up from the target that git could not read (R22) # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2) or # is inside a git directory, one in a checkout beside W that is not this - # one, a restart's bound file or pre-image, a proof + # one, a relative spelling that climbs out of the checkout (S2), a + # restart's bound file or pre-image, a proof # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named # 7 a restart fence, or the handoff changed under the write # 8 no handoff to write: no row for the lane (with `--target` too), the @@ -20057,6 +20098,10 @@ EOF fi whf_other=0; whf_path="$(handoff_in_repo "$whf_c" "$whf_cell")" || whf_other=1 whf_phys="$(path_physical "$whf_path")" || whf_phys="$whf_path" + if handoff_rel_climbs_out "$whf_c" "$whf_cell"; then + whf_stamp_said + die "lane $lane's handoff $whf_cell is a repository-relative spelling, joined to this write's checkout $whf_c, and its '.' or '..' components climb out of it (to $whf_phys): a relative cell or --target names a file of its checkout, never another repository's (Amendment 23 S2), as set-restart-intent --handoff refuses the same spelling. Nothing was written." 2 + fi if [ -n "$LANES_WS_REAL" ] && [ "$whf_c" != "$LANES_WS_REAL" ] && path_under "$whf_phys" "$LANES_WS_REAL"; then whf_stamp_said die "lane $lane's handoff $whf_path resolves to $whf_phys, inside the workspace checkout $LANES_WS_REAL, the readers' copy, where no tool writes (Amendment 23 S5). Nothing was written. The lane's handoff is a file of its own checkout, $whf_c: make $whf_path one there, then run LANES_LANE=$lane $SELF write-handoff $lane … again." 2 @@ -20285,7 +20330,8 @@ EOF # row that names no handoff, and nothing cloned · 1 a checkout there that # is not a clone, or a register that could not be read (R22: never 8) · # 2 a name refused, a handoff in W or in another checkout beside it - # (601, as `write-handoff` refuses it; nothing on stdout), + # (601, as `write-handoff` refuses it; nothing on stdout), a relative + # cell whose `.` or `..` components climb out of the checkout (602, S2), # or the clone failed · 4 the creation lock · 64 usage handoff-path) hp_lane=""; hp_create=0 @@ -20316,7 +20362,14 @@ EOF note "lane $hp_lane's row names no handoff" exit 8 fi + # A RELATIVE CELL THAT CLIMBS OUT OF THE CHECKOUT (602): refused as + # `write-handoff` refuses it, and first, as there (A55) — by its text + # before any checkout is made, then by the file it names in the checkout + # there is. + hp_climbs="lane $hp_lane's handoff cell $hp_cell is a repository-relative spelling, joined to the checkout its writes go through, and its '.' or '..' components climb out of it: a relative cell names a file of that checkout, never another repository's (Amendment 23 S2), so write-handoff refuses it too. Nothing was printed." + ! handoff_rel_climbs_out "" "$hp_cell" || die "$hp_climbs" 2 handoff_checkout_of "$hp_lane" "$hp_create" + ! handoff_rel_climbs_out "$HCO_PATH" "$hp_cell" || die "$hp_climbs" 2 hp_rc=0; hp_path="$(handoff_in_repo "$HCO_PATH" "$hp_cell")" || hp_rc=$? # WHAT `write-handoff` REFUSES, NO PATH IS PRINTED FOR (finding 723 of # lane openRepoTools-3's review of #316): a caller binds a restart to this diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 491176d..f1d276b 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1156,7 +1156,9 @@ def test_a_handoff_in_another_checkout_of_the_register_beside_W_is_refused(box: `test_a_handoff_in_another_clone_of_the_register_is_still_another_repositorys` until lane 3's ruling. A lane's handoff is written in its own checkout only, so the path refuses it (2), by `--target` or by an absolute cell: - nothing written there or here, the stamp line said, nothing on stdout.""" + nothing written there or here, the stamp line said, nothing on stdout. + A relative cell that climbs there with `..` is refused the same (2), by + 602's rule, which asks the spelling first (A55).""" if how == "abs-cell": box.cell_is(str(_beside_w_dir(box, where) / HANDOFF_REL)) if how == "climb-cell": @@ -1168,7 +1170,12 @@ def test_a_handoff_in_another_checkout_of_the_register_beside_W_is_refused(box: text, theirs, mine = f.read_bytes(), box.snapshot(d), box.snapshot(c) result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, *args) assert result.returncode == 2, result.stderr - assert f"is {f}, in another checkout of the register beside W ({box.w})" in result.stderr + if how == "climb-cell": + assert (f"lane {LANE}'s handoff {_climb(box, f)} is a repository-relative spelling, joined to " + f"this write's checkout {c}, and its '.' or '..' components climb out of it (to {f})" + ) in result.stderr + else: + assert f"is {f}, in another checkout of the register beside W ({box.w})" in result.stderr assert "Nothing was written." in result.stderr assert "another repository's handoff" not in result.stderr assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr @@ -1234,8 +1241,9 @@ def test_a_clone_git_cannot_read_is_never_taken_for_no_repository(box: Box, wher _released(box, c) -HANDOFF_PATH_REFUSED = [("W", "climb-cell"), ("lanes-sibling", "abs-cell"), ("lanes-sibling", "climb-cell"), - ("profile-clone", "abs-cell"), ("profile-clone", "climb-cell")] +HANDOFF_PATH_REFUSED = [("W", "climb-cell"), ("W", "link-cell"), ("lanes-sibling", "abs-cell"), + ("lanes-sibling", "climb-cell"), ("profile-clone", "abs-cell"), + ("profile-clone", "climb-cell")] @pytest.mark.parametrize("where,how", HANDOFF_PATH_REFUSED) @@ -1249,7 +1257,9 @@ def test_handoff_path_refuses_what_write_handoff_refuses(box: Box, where: str, h another checkout of the register beside W, absolute or climbing with `..`. No path on stdout, the refusal named, nothing written there or here. W's own absolute spelling is still translated into the checkout - (S2), so W is reached here only by climbing.""" + (S2), so W is reached here only by climbing, or through a link in the + checkout. A relative cell that climbs out is refused by 602's rule, + which asks the spelling first (A55), as `write-handoff` does.""" if where == "W": box.w_current() d = box.w @@ -1257,16 +1267,26 @@ def test_handoff_path_refuses_what_write_handoff_refuses(box: Box, where: str, h d = _beside_w(box, where) f = d / HANDOFF_REL assert f.is_file() - box.cell_is(str(f) if how == "abs-cell" else _climb(box, f)) + if how != "link-cell": + box.cell_is(str(f) if how == "abs-cell" else _climb(box, f)) c = box.seam() + if how == "link-cell": + (c / HANDOFF_REL).unlink() + (c / HANDOFF_REL).symlink_to(f) theirs, mine = box.snapshot(d), box.snapshot(c) result = box.helper("handoff-path", LANE) assert result.returncode == 2, result.stderr - if where == "W": + if how == "climb-cell": + assert (f"lane {LANE}'s handoff cell {_climb(box, f)} is a repository-relative spelling, joined to " + "the checkout its writes go through, and its '.' or '..' components climb out of it" + ) in result.stderr + assert "so write-handoff refuses it too. Nothing was printed." in result.stderr + elif where == "W": assert f"is {f}, inside the workspace checkout {box.w}, the readers' copy" in result.stderr else: assert f"is {f}, in another checkout of the register beside W ({box.w}), not in the checkout {c}" in result.stderr - assert "`write-handoff` refuses it, so no path is printed" in result.stderr + if how != "climb-cell": + assert "`write-handoff` refuses it, so no path is printed" in result.stderr assert "another repository's handoff" not in result.stderr assert result.stdout == "" assert box.snapshot(d) == theirs @@ -1989,6 +2009,116 @@ def test_a_handoff_that_links_out_of_the_checkout_is_another_repositorys(box: Bo _released(box, c) +# ------------------------------------------------- 602: a relative spelling that climbs out + +CLIMBS = { + # name: (the spelling, where it is given, the file it reaches from the seam `/c`); + # None: the box's own file, spelled by `_climb` (a sibling checkout's handoff, W's) + "cell-outside": ("handoffs/../../outside/x.md", "cell", "outside/x.md"), + "target-outside": ("../outside/x.md", "target", "outside/x.md"), + "cell-sibling": (None, "cell", None), + "cell-W": (None, "cell", None), + "cell-through-a-link": ("handoffs/lnk/../x.md", "cell", "elsewhere/x.md"), + "cell-not-there-yet": ("handoffs/notyet/../../../outside/x.md", "cell", "outside/x.md"), +} + + +@pytest.mark.parametrize("case", CLIMBS) +def test_a_relative_spelling_that_climbs_out_of_the_checkout_is_refused(box: Box, case: str): + """602 of lane openRepoTools-3's review of #312: a repository-relative + cell, or a relative `--target`, is joined to the checkout (S2), and one + whose `.` or `..` components climb out of it was written as another + repository's handoff, exit 0, where `set-restart-intent --handoff` + refuses the same cell (64). It names no other repository's handoff: + refused (2), nothing written anywhere, the stamp line said. Climbing out + is read from the text, and where the spelling resolves, from the file it + names: `handoffs/lnk/../x.md`, with `lnk` a link out, stays in the + checkout by its text and leaves it by its file. A link with no such + component is still Amendment 8(d)'s (D08's test). The spelling is asked + first (A55), so a cell that climbs into W or a checkout beside it is + refused for its spelling too. And one that climbs out THROUGH A + DIRECTORY NOT THERE YET (`handoffs/notyet/..`), whose file neither + `path_physical` nor the kernel can resolve, so no check by path can say + where it lands, is refused by its text (it said 8, "not there"), and no + directory is made for it.""" + spelling, given, rel = CLIMBS[case] + if case == "cell-sibling": + f = box.lanes_root / NEIGHBOUR / HANDOFF_REL + elif case == "cell-W": + f = box.w / HANDOFF_REL + else: + f = box.root / rel + if spelling is None: + spelling = _climb(box, f) + if given == "cell": + box.cell_is(spelling) + c = box.seam() + if case == "cell-sibling": + box.clone(box.lanes_root / NEIGHBOUR) + elif case != "cell-W": + f.parent.mkdir(parents=True, exist_ok=True) + f.write_text(HANDOFF, encoding="utf-8") + if case == "cell-through-a-link": + (box.root / "elsewhere" / "a").mkdir() + (c / "handoffs" / "lnk").symlink_to(box.root / "elsewhere" / "a") + assert f.is_file() + args = ("--target", spelling) if given == "target" else () + text, mine, w = f.read_bytes(), box.snapshot(c), box.w_state() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, *args) + assert result.returncode == 2, result.stderr + assert (f"lane {LANE}'s handoff {spelling} is a repository-relative spelling, joined to this " + f"write's checkout {c}, and its '.' or '..' components climb out of it") in result.stderr + assert "Nothing was written." in result.stderr + assert "another repository's handoff" not in result.stderr + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert f.read_bytes() == text + assert box.snapshot(c) == mine + assert box.w_state() == w + assert not (c / "handoffs" / "notyet").exists() + assert result.stdout == "" + _released(box, c) + + +def test_a_relative_spelling_that_stays_in_the_checkout_is_its_own_handoff(box: Box): + """602's bound: the rule is a spelling that LEAVES the checkout, not any + `..`. A cell that climbs within it names the lane's own handoff, which + is written, committed and pushed as the plain cell's is.""" + box.cell_is(f"handoffs/../{HANDOFF_REL}") + c = box.seam() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) + assert result.stdout == _line(c / HANDOFF_REL) + _released(box, c) + + +@pytest.mark.parametrize("cell", ["handoffs/../../outside/x.md", "handoffs/notyet/../../../outside/x.md"]) +@pytest.mark.parametrize("how", ["seam", "routed"]) +def test_handoff_path_refuses_a_relative_cell_that_climbs_out_of_the_checkout(box: Box, how: str, cell: str): + """602 in `handoff-path`: it printed `/handoffs/../../outside/x.md` + as this checkout's own file, with no note, for a cell `write-handoff` + now refuses, and the same for a cell that climbs out through a directory + not there yet. It answers where that write goes, so it refuses the cell + too (2), prints nothing, and routed, `--create` clones nothing for it: + the text is read before any checkout is made.""" + f = box.root / "outside" / "x.md" + f.parent.mkdir(parents=True, exist_ok=True) + f.write_text(HANDOFF, encoding="utf-8") + box.cell_is(cell) + c = box.seam() if how == "seam" else box.checkout(LANE) + for argv in ((LANE,), (LANE, "--create")): + result = box.helper("handoff-path", *argv) + assert result.returncode == 2, result.stderr + assert (f"lane {LANE}'s handoff cell {cell} is a repository-relative " + "spelling, joined to the checkout its writes go through, and its '.' or '..' " + "components climb out of it") in result.stderr + assert result.stdout == "" + if how == "routed": + assert not c.exists(), "a cell that climbs out gets no checkout" + else: + assert not (c / "handoffs" / "notyet").exists() + + # ------------------------------------------------- m2a's battery, its second half def test_stamp_into_an_empty_handoff_is_refused_1_and_says_the_line(box: Box): From 351bc2216306105a3ed5553d2fcd001a2a3da396 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 20:55:29 +0000 Subject: [PATCH 26/34] A56: 603, `write-handoff` answers the shared act's two restart-fence 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) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (cherry picked from commit 36a25da84c0a986ce665a6f05e641badfdd10259) --- lanes-edit.sh | 45 ++++++++++++++++++++++----------- tests/test_a23_write_handoff.py | 38 +++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 19 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index a3c509a..8ded097 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -496,9 +496,10 @@ # `handoff-path` and `set-restart-intent --handoff` refuse the same # handoff by the same check, 601), a relative cell or `--target` whose # `.` or `..` components climb out of the checkout (S2, 602; `handoff-path` -# refuses that cell too), a restart's bound file or -# pre-image that is not this one, or a splice that does not prove out, -# each with nothing written; while S3's refusal of a `--prepend` or a +# refuses that cell too), or a splice that does not prove out, each with +# nothing written — a restart's bound file or pre-image that is not this +# one is `publish-handoff`'s 2 and `write-handoff`'s 7 (603: its 2 is +# never a restart fence's); while S3's refusal of a `--prepend` or a # `--whole` has WRITTEN the handoff, uncommitted (R-A11-11), and printed # its path. EIGHT MEANINGS ON ONE NUMBER, and they # stay readable only because of where each can occur: bad arguments to a @@ -593,7 +594,9 @@ # operation it names is finished (#114) — so a superseded poll cannot be # filed over the current inventory. `publish-handoff` and `write-handoff`: # the restart fence did not match, an active restart owns the handoff, or -# the handoff changed under the write, and nothing was published. ONE +# the handoff changed under the write — and in `write-handoff` alone, the +# restart's bound file or pre-image is not this one, which +# `publish-handoff` answers 2 (603) — and nothing was published. ONE # MEANING ON THE NUMBER, in five places: you lost the race. # 8 no record — `who` found nothing; `lane-objects` has no log file for the # lane; `live-holder` READ this workstation's session records and none of @@ -13439,7 +13442,8 @@ lane_restart_put() { # [ -n "$1" ] && [ "$1" != none ] || return 1 @@ -13529,13 +13541,13 @@ handoff_publish_atomic() { # [ < die "this handoff publisher lost restart ownership; nothing was published" 7 hpa_bound="$(printf '%s\n' "$hpa_intent" | awk -F'\t' '$1=="handoff" {print $2; exit}')" handoff_bound_is "$hpa_bound" "$hpa_target" || - die "the publication target is not the operation-bound handoff; nothing was published" 2 + die "the publication target is not the operation-bound handoff; nothing was published" "${HPA_FENCE_RC:-2}" else case "$hpa_current" in preparing|pending|starting) die "an active restart owns this handoff; nothing was published" 7 ;; esac fi [ "$(lane_handoff_digest "$hpa_target")" = "$hpa_digest" ] || die "the handoff changed before publication; nothing was published" 7 if [ -n "$HPA_EXPECTED" ] && [ "$HPA_EXPECTED" != none ] && [ "$hpa_digest" != "$HPA_EXPECTED" ]; then - die "the restart handoff changed before ${HPA_EXPECTED_FOR:-its owned write}: $hpa_target digests $hpa_digest, and the restart bound $HPA_EXPECTED. Nothing was published." 2 + die "the restart handoff changed before ${HPA_EXPECTED_FOR:-its owned write}: $hpa_target digests $hpa_digest, and the restart bound $HPA_EXPECTED. Nothing was published." "${HPA_FENCE_RC:-2}" fi [ -z "$HPA_RESERVE" ] || handoff_restart_reserve "$hpa_lane" "$hpa_intent" "$HPA_RESERVE" pbh_tmp="$(mktemp "$hpa_target.resume.XXXXXX")" || { pbh_tmp=""; die "the publication temporary file could not be made" 1; } @@ -19990,9 +20002,11 @@ EOF # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2) or # is inside a git directory, one in a checkout beside W that is not this # one, a relative spelling that climbs out of the checkout (S2), a - # restart's bound file or pre-image, a proof + # proof, a restart intent that is malformed — never a restart fence (603) # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named - # 7 a restart fence, or the handoff changed under the write + # 7 a restart fence — the five fences, an active restart, and the + # restart's bound file and pre-image, which `publish-handoff` answers 2 + # (603) — or the handoff changed under the write # 8 no handoff to write: no row for the lane (with `--target` too), the # row names none, or its file is not there # 64 usage @@ -20270,6 +20284,7 @@ EOF sidecar_try 60 || die "could not acquire the workstation's sidecar lock $(sidecar_lock_dir 2>/dev/null) after 60s — a restart, lifecycle or tree act holds it (holder pid $(cat -- "$(sidecar_lock_dir 2>/dev/null)/pid" 2>/dev/null || printf 'unrecorded')). Nothing was written." 4 HPA_CHECKOUT="$whf_c" + HPA_FENCE_RC=7 # 603: this verb's 2 is S3's, so a restart fence's 2 is 7 here whf_fence=() if [ "$whf_restart" = 1 ]; then whf_fence=("$whf_rop" "$whf_rgen" "$whf_ratt" "$whf_rstate" "$whf_rsid") diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index f1d276b..30471ac 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -607,7 +607,7 @@ def test_restart_fences_under_sidecar_lock(box: Box): REFUSED_FENCES = { "lost": (("--restart", "op-a23", "4", "1", "starting", "sid-a23"), 7, "this handoff publisher lost restart ownership"), - "unbound": (("--restart", *FENCE), 2, "the publication target is not the operation-bound handoff"), + "unbound": (("--restart", *FENCE), 7, "the publication target is not the operation-bound handoff"), "active": ((), 7, "an active restart owns this handoff"), } @@ -684,20 +684,50 @@ def test_prepared_digest_recorded_before_publication(box: Box): def test_restart_pre_image_must_be_the_bound_digest(box: Box): - """The handoff changed under the restart: `lane-start`'s refusal (2), and - nothing written or reserved.""" + """The handoff changed under the restart: `lane-start`'s refusal, and + nothing written or reserved. A restart fence, so 7 (603): this verb's 2 + is S3's refusal.""" c = box.seam() f = c / HANDOFF_REL box.restart(f) result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, "--expected-digest", "1" * 64) - assert result.returncode == 2, result.stderr + assert result.returncode == 7, result.stderr assert "the restart handoff changed before its owned resume stamp" in result.stderr assert f.read_text(encoding="utf-8") == HANDOFF assert box.intent_field("prepared_digest") == "none" _released(box, c) +def test_under_a_restart_2_is_the_checkouts_refusal_and_a_fence_is_7(box: Box): + """603 of lane openRepoTools-3's review of #312: `write-handoff`'s 2 meant + both "S3 refused this checkout" and "the restart fence refused" (its bound + file, its pre-image), so slice 2b's `lane-start`, which notes the first + and dies on the second under an active restart (act2-plan §2.7), could + not tell them apart. This verb answers the shared act's two fence 2s with + 7 (the cases above), and S3's refusal under the same `--restart` is 2 + still: the stamp not written, its line said, nothing reserved. + `publish-handoff` keeps its documented 2 for the bound file + (`test_publish_handoff_refuses_a_file_the_restart_did_not_bind`).""" + c = box.seam() + f = c / HANDOFF_REL + box.restart(f) + bound = box.intent_field("digest") + _staged_foreign(box, c) + tip = box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--restart", *FENCE, + "--expected-digest", bound) + assert result.returncode == 2, result.stderr + assert "the stamp is NOT written" in result.stderr + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert "nothing was published" not in result.stderr.lower() + assert f.read_text(encoding="utf-8") == HANDOFF + assert box.intent_field("prepared_digest") == "none" + assert box.origin_head() == tip + assert result.stdout == "" + _released(box, c) + + def test_only_the_stamp_reserves(box: Box): """`lane-handoff`'s prepend binds at `preparing` and the digest is taken again at `pending`: a fenced prepend writes, and reserves nothing.""" From 60f943ea5508bb00e37307c456479df3d31d32a8 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 21:41:08 +0000 Subject: [PATCH 27/34] A57: 604, as lane openRepoTools-3 ruled it, `write-handoff` commits and 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 (cherry picked from commit c962db78e22c1e8fa9f513ec66a49041369c064e) --- lanes-edit.sh | 99 ++++++++++++++++++--- tests/test_a23_write_handoff.py | 148 +++++++++++++++++++++++++++++--- 2 files changed, 222 insertions(+), 25 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 8ded097..939df07 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -577,7 +577,9 @@ # no replay may overwrite (S4.4), or a `--stamp` or `--prepend` one # whose handoff origin deleted meanwhile, which a replay would find # nothing to write into (one moved with its row's cell, to a file under -# `handoffs/` that origin has, is replayed into the moved file). Or another +# `handoffs/` that origin has, is replayed into the moved file), or an +# Amendment 8(d) handoff `write-handoff` wrote whose commit or push in its +# own repository failed (604; the command that finishes it is said). Or another # writer in the checkout REWROTE this write's commit (a `pull --rebase` # with no lock): it is not on HEAD at all, and what reached origin is # read by content. Or, where the drop's tree step was refused and @@ -16526,7 +16528,8 @@ lane_checkout_enter() { # # (`handoffs//`) is joined to the lane's checkout; a `~/…` or # absolute path whose physical parent is under W becomes the same path under # the lane's checkout; anything else is another repository's (Amendment 8(d)), -# published and never committed by these tools. The row's cell keeps its +# which `write-handoff` commits and pushes in its own repository (604) and +# `publish-handoff` never commits. The row's cell keeps its # spelling: this answers where the file is WRITTEN. # A path's physical spelling: `~` expanded, the leaf's links followed (40 at # most), its directory resolved with `cd -P`. 1, and the path as far as it was @@ -19968,8 +19971,10 @@ EOF # is refused first, for its spelling, wherever it lands (602, A55). # Where git cannot read a repository on the way up from the target, it # refuses (1, R22). - # One in ANOTHER REPOSITORY (Amendment 8(d)) is written there and never - # committed, and S3's refusal of this checkout does not stop it. + # One in ANOTHER REPOSITORY (Amendment 8(d)) is written there, then + # committed and pushed in its own top level (604), and S3's refusal of + # this checkout does not stop it; one in another clone of the + # register not beside W is written and never committed (A57). # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -19994,7 +19999,8 @@ EOF # that origin has, which the re-run writes (A46, A48); otherwise the # commit is kept on HEAD and named (6). # STDOUT: `` once the file is written (`HANDOFF_OUT`). - # 0 pushed, or nothing to commit (another repository's file, S3's refusal + # 0 pushed — another repository's handoff in its own repository (8(d), + # 604) — or nothing to commit (a file in no git work tree, S3's refusal # of this checkout or not; LANES_NO_GIT) # 1 environment: a checkout that cannot be entered, an unreadable file, # a register that could not be read (R22: never 8), a repository on the @@ -20003,7 +20009,9 @@ EOF # is inside a git directory, one in a checkout beside W that is not this # one, a relative spelling that climbs out of the checkout (S2), a # proof, a restart intent that is malformed — never a restart fence (603) - # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named + # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named — and an + # 8(d) handoff, written, whose commit or push in its own repository + # failed (the command that finishes it said) # 7 a restart fence — the five fences, an active restart, and the # restart's bound file and pre-image, which `publish-handoff` answers 2 # (603) — or the handoff changed under the write @@ -20195,13 +20203,14 @@ EOF fi fi # ANOTHER REPOSITORY'S HANDOFF IS NOT THIS CHECKOUT'S WRITE (rv2a's - # pre-review of slice 2a, SHOULD 2). It is written there and committed - # nowhere (Amendment 8(d)), so S3's refusal of the register checkout — an + # pre-review of slice 2a, SHOULD 2). It is written, committed and pushed + # in its own repository (Amendment 8(d); 604), so S3's refusal of the + # register checkout — an # offline lane's fetch, a staged entry — does not stop it, as the old # `publish-handoff` never fetched for it. The managed seam (asked above), # the restart fences and the digests still apply. if [ "$whf_other" = 1 ] && [ "$whf_s3" != 0 ]; then - note "the refusal above is this checkout's (Amendment 23 S3) and does not stop this write: $whf_phys is another repository's handoff (Amendment 8(d)), which no tool here commits" + note "the refusal above is this checkout's (Amendment 23 S3) and does not stop this write: $whf_phys is another repository's handoff (Amendment 8(d)), which is never committed through this checkout" whf_s3=0 fi # ---- 3. S3 step 4 for the handoff itself: a change to it nobody committed, @@ -20307,8 +20316,76 @@ EOF fi exit "$whf_s3" fi + # ANOTHER REPOSITORY'S HANDOFF IS COMMITTED AND PUSHED THERE (lane + # openRepoTools-3's review of #312, 604, ruled at #312 6368669583; A57). + # 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: one commit in the handoff's OWN top level, by + # pathspec, with this write's subject and the `Lane:` trailer, then a + # `push` of the branch that repository is on, which is `main` wherever + # theirs was. Their `pull --rebase` before the push is S3's way here, as + # no line of this file pulls (#198): the branch is fetched and + # fast-forwarded BEFORE the commit, so the commit sits on origin's tip, + # and a fast-forward that cannot be made (a local commit, or origin's + # change to this very file) is left to the push, which is refused, and the + # commit kept (6). Not S3 or S4 themselves: that repository is not the + # register, so no pending change there is asked about, nothing is + # replayed and nothing is read back. NEVER IN + # A CLONE OF THE REGISTER (A57): a top level that carries the register + # too (A44's test) is another lane's clone, not 8(d)'s "not the + # register's", and a commit and push there would publish to the + # register's origin outside S3 and S4, from a clone this lane does not + # hold; beside W, 601 refused it above, and elsewhere it is written and + # NOT committed, as before 604 (0, said; its lane commits it). A step + # that fails there leaves the handoff WRITTEN, says what is left with the + # command that finishes it, and is 6 (`git add` or `commit` failed, or the + # commit is kept on HEAD and named); a file in no git work tree is written + # and commits nowhere (0), as `lane-start` said; LANES_NO_GIT commits + # nothing (0). if [ "$whf_other" = 1 ]; then - note "$HPA_TARGET is another repository's handoff (Amendment 8(d)): it is written there, and these tools never commit it." + whf_8d="$HPA_TARGET is another repository's handoff (Amendment 8(d)): it is written there" + whf_8top="$(git -C "$(dirname -- "$HPA_TARGET")" rev-parse --show-toplevel 2>/dev/null || :)" + whf_8top="$( [ -n "$whf_8top" ] && cd -P -- "$whf_8top" 2>/dev/null && pwd -P || :)" + if [ -z "$whf_8top" ] || ! path_under "$HPA_TARGET" "$whf_8top"; then + note "$whf_8d, and it is in no git work tree, so nothing commits it from here." + exit 0 + fi + if [ -e "$whf_8top/$LANES_PATH" ] || [ -L "$whf_8top/$LANES_PATH" ]; then + note "$HPA_TARGET is in $whf_8top, another clone of the register (its top level carries $LANES_PATH too), not this write's checkout $whf_c, so it is not Amendment 8(d)'s: it is written there and NOT committed, since a commit and push there would publish to the register outside Amendment 23 S3 and S4. That clone's lane commits it." + exit 0 + fi + whf_8rel="${HPA_TARGET#"$whf_8top"/}" + whf_8cmd="git --literal-pathspecs -C $whf_8top add -- $whf_8rel && git --literal-pathspecs -C $whf_8top commit -m \"$whf_subject\" -m \"Lane: $lane\" -- $whf_8rel" + if [ "$NO_GIT" = 1 ]; then + note "$whf_8d, and NOT committed: LANES_NO_GIT=1. Commit it there by pathspec, then push it: $whf_8cmd" + exit 0 + fi + whf_8br="$(git -C "$whf_8top" symbolic-ref -q --short HEAD 2>/dev/null || :)" + if [ -z "$whf_8br" ]; then + note "$whf_8d, and NOT committed: $whf_8top's HEAD is on no branch (a rebase in progress, or a detached checkout), and no commit is made there in the middle of one. Once it is on its branch: $whf_8cmd, then push it." + exit 6 + fi + whf_8look="look first: git -C $whf_8top fetch origin $whf_8br && git -C $whf_8top log --oneline origin/$whf_8br -3, since a push that timed out may have landed; only if it is not there, put it on origin's tip (git -C $whf_8top rebase origin/$whf_8br) and push it: git -C $whf_8top push origin $whf_8br. Never force." + whf_8ff=0 + if git_net -C "$whf_8top" fetch -q origin "$whf_8br" >&2 && + git -C "$whf_8top" rev-parse -q --verify "refs/remotes/origin/$whf_8br" >/dev/null 2>&1; then + whf_8ff=1 + git -C "$whf_8top" -c gc.auto=0 -c maintenance.auto=false merge --ff-only -q "refs/remotes/origin/$whf_8br" >/dev/null 2>&1 || : + fi + if ! git --literal-pathspecs -C "$whf_8top" add -- "$whf_8rel" >&2 || + ! git --literal-pathspecs -C "$whf_8top" commit -q -m "$whf_subject" -m "Lane: $lane" -- "$whf_8rel" >&2; then + note "$whf_8d, and NOT committed: git's own words are above. Commit it there by pathspec, as this write's own, then push it: $whf_8cmd" + exit 6 + fi + if [ "$whf_8ff" = 0 ]; then + note "$whf_8d and committed there, on $whf_8br, \"$whf_subject\", and NOT pushed: origin/$whf_8br could not be fetched there first (no origin, no reachable origin, or a timeout). The commit is kept on HEAD there — $whf_8look" + exit 6 + fi + if ! git_net -C "$whf_8top" push -q origin "$whf_8br" >&2; then + note "$whf_8d and committed there, on $whf_8br, \"$whf_subject\", and the push was refused or timed out (a hung push often lands). The commit is kept on HEAD there — $whf_8look" + exit 6 + fi + note "$whf_8d, committed there and pushed to origin/$whf_8br: \"$whf_subject\" (Lane: $lane)." exit 0 fi # ---- 7. the commit, the push, the read-back. @@ -20393,7 +20470,7 @@ EOF if handoff_beside_w "$HCO_PATH" "$hp_path"; then die "lane $hp_lane's handoff $hp_cell is $HBW_WHY: \`write-handoff\` refuses it, so no path is printed for a restart to bind or a launch to read (601). Nothing was written." 2 fi - [ "$hp_rc" = 0 ] || note "$hp_path is another repository's handoff (Amendment 8(d)): \`write-handoff\` writes it there and commits it nowhere" + [ "$hp_rc" = 0 ] || note "$hp_path is another repository's handoff (Amendment 8(d)): \`write-handoff\` writes it there, and commits and pushes it in that repository (604), never in another clone of the register" printf '%s\n' "$hp_path" ;; diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 30471ac..364dec0 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -535,29 +535,152 @@ def test_repo_relative_cell(tmp_path: Path, how: str): _released(box, c) -def test_cell_in_another_repository_is_published_not_committed(box: Box): - """Amendment 8(d): a handoff in another repository is not the register's. - It is written there, and committed nowhere.""" +def _other_repository(box: Box, *, origin: bool = True) -> Path: + """Another repository, not the register: `/other`, its handoff + committed on `main`, and (with `origin`) a bare origin `main` tracks.""" other = box.root / "other" box.git("init", "-q", "-b", "main", str(other)) box.write(other, "handoff.md", HANDOFF) box.commit(other, "another repository's handoff", "handoff.md") + if origin: + bare = box.root / "other.git" + box.git("init", "-q", "--bare", "-b", "main", str(bare)) + box.git("-C", str(other), "remote", "add", "origin", str(bare)) + box.git("-C", str(other), "push", "-q", "-u", "origin", "main") + return other + + +def _committed_and_pushed_there(box: Box, other: Path, before: str, rel: str = "handoff.md") -> None: + """604's outcome: one commit on `main` in the handoff's own repository, + by pathspec, with this write's subject and `Lane:` trailer; its tree + clean; its origin holding it.""" + o = ["-C", str(other)] + assert box.git(*o, "status", "--porcelain") == "" + assert box.git(*o, "rev-parse", "HEAD~1").strip() == before + assert box.git(*o, "log", "-1", "--format=%B").strip().splitlines() == [SUBJECT, "", f"Lane: {LANE}"] + assert box.git(*o, "diff", "--name-only", "HEAD~1", "HEAD").split() == [rel] + assert (box.git("--git-dir", str(box.root / "other.git"), "rev-parse", "main").strip() + == box.git(*o, "rev-parse", "HEAD").strip()) + + +def test_a_cell_in_another_repository_is_committed_and_pushed_there(box: Box): + """Amendment 8(d), as lane openRepoTools-3 ruled 604 (#312 6368669583): + Amendment 23 S2 says another repository's handoff "is not the register's + and is unchanged", so it is committed and pushed in its own top level, + as `lane-start` and `lane-handoff` did at e2037b9 — by pathspec, under + this write's subject and `Lane:` trailer — and its tree is left clean. + It was written there and committed nowhere, ` M` for good. This checkout + and the register's origin are as they were.""" + other = _other_repository(box) box.cell_is(str(other / "handoff.md")) c = box.seam() other_head = box.git("-C", str(other), "rev-parse", "HEAD").strip() head, tip = box.head(c), box.origin_head() result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) assert result.returncode == 0, result.stderr - assert "another repository's handoff (Amendment 8(d))" in result.stderr + assert (f"{other / 'handoff.md'} is another repository's handoff (Amendment 8(d)): it is written " + "there, committed there and pushed to origin/main") in result.stderr + assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) + _committed_and_pushed_there(box, other, other_head) + assert box.head(c) == head + assert box.origin_head() == tip + assert result.stdout == _line(other / "handoff.md") + _released(box, c) + + +@pytest.mark.parametrize("how", ["no-origin", "no-git"]) +def test_an_8d_handoff_that_cannot_be_committed_or_pushed_there_is_written_and_said(box: Box, how: str): + """604's other outcomes, each with the handoff WRITTEN (R-A11-11): where + its repository has no origin to push to, the commit is made and kept on + HEAD there, and the look-first commands said (6); under LANES_NO_GIT + nothing is committed, and the command that commits it is said (0).""" + other = _other_repository(box, origin=False) + box.cell_is(str(other / "handoff.md")) + c = box.seam() + other_head = box.git("-C", str(other), "rev-parse", "HEAD").strip() + head, tip = box.head(c), box.origin_head() + extra = {"LANES_NO_GIT": "1"} if how == "no-git" else {} + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, **extra) assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) - assert box.git("-C", str(other), "rev-parse", "HEAD").strip() == other_head - assert box.git("-C", str(other), "status", "--porcelain").split() == ["M", "handoff.md"] + o = ["-C", str(other)] + if how == "no-origin": + assert result.returncode == 6, result.stderr + assert "and committed there, on main" in result.stderr + assert f"git -C {other} fetch origin main && git -C {other} log --oneline origin/main -3" in result.stderr + assert box.git(*o, "status", "--porcelain") == "" + assert box.git(*o, "rev-parse", "HEAD~1").strip() == other_head + assert box.git(*o, "log", "-1", "--format=%B").strip().splitlines() == [SUBJECT, "", f"Lane: {LANE}"] + else: + assert result.returncode == 0, result.stderr + assert "and NOT committed: LANES_NO_GIT=1" in result.stderr + assert f"git --literal-pathspecs -C {other} add -- handoff.md" in result.stderr + assert box.git(*o, "rev-parse", "HEAD").strip() == other_head + assert box.git(*o, "status", "--porcelain").split() == ["M", "handoff.md"] assert box.head(c) == head assert box.origin_head() == tip assert result.stdout == _line(other / "handoff.md") _released(box, c) +def test_an_8d_handoff_whose_origin_moved_is_fast_forwarded_first_then_committed_and_pushed(box: Box): + """604 without a rebasing pull (no line of `lanes-edit.sh` pulls, #198): + where the handoff's repository's origin moved first, by a commit that + does not touch the handoff, the branch is fetched and fast-forwarded + BEFORE the commit, as S3 does, so this write's commit sits on that + commit and the push goes through (0), its tree clean.""" + other = _other_repository(box) + peer = box.root / "other-peer" + box.git("clone", "-q", str(box.root / "other.git"), str(peer)) + box.write(peer, "notes.md", "a peer's note\n") + box.commit(peer, "a peer's unrelated commit", "notes.md") + box.git("-C", str(peer), "push", "-q", "origin", "main") + moved = box.git("-C", str(peer), "rev-parse", "HEAD").strip() + box.cell_is(str(other / "handoff.md")) + c = box.seam() + head, tip = box.head(c), box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 0, result.stderr + assert "committed there and pushed to origin/main" in result.stderr + assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) + _committed_and_pushed_there(box, other, moved) + assert (other / "notes.md").read_text(encoding="utf-8") == "a peer's note\n" + assert box.head(c) == head + assert box.origin_head() == tip + _released(box, c) + + +def test_a_handoff_in_another_clone_of_the_register_elsewhere_is_written_never_committed(box: Box): + """A57, because of 604: the 8(d) branch now commits and pushes in the + handoff's own top level, but never in one that carries the register + (A44's test). A file under `handoffs/` of another clone of the register + that is NOT beside W (601 refuses one beside W) is that clone's lane's, + not Amendment 8(d)'s "not the register's", and a commit and push there + would publish to the register's origin outside S3 and S4, from a clone + this lane does not hold. So it is written there and NOT committed, as + before 604 (0), and said: that clone's HEAD, this checkout and the + register's origin are as they were. Whether it should be refused + instead is put to lane openRepoTools-3 (the body's Q4).""" + c = box.seam() + d = box.clone(box.root / "elsewhere-clone") + f = d / HANDOFF_REL + text = f.read_text(encoding="utf-8") + d_head = box.git("-C", str(d), "rev-parse", "HEAD").strip() + mine, tip = box.snapshot(c), box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--target", str(f)) + assert result.returncode == 0, result.stderr + assert (f"{f} is in {d}, another clone of the register (its top level carries {REGISTER} too), " + f"not this write's checkout {c}, so it is not Amendment 8(d)'s: it is written there and " + "NOT committed") in result.stderr + assert "committed there" not in result.stderr + assert f.read_text(encoding="utf-8") == stamped(text) + assert box.git("-C", str(d), "rev-parse", "HEAD").strip() == d_head + assert box.git("-C", str(d), "status", "--porcelain").split() == ["M", HANDOFF_REL] + assert box.origin_head() == tip + assert box.snapshot(c) == mine + assert result.stdout == _line(f) + _released(box, c) + + def test_a_cell_through_a_link_into_W_is_refused_S5(box: Box): """S5: a handoff in the lane's checkout that is a link into W would write W's file. Refused before a byte (2), naming the checkout and the lane.""" @@ -2011,12 +2134,10 @@ def test_a_handoff_that_links_out_of_the_checkout_is_another_repositorys(box: Bo """m2a's D08: the row's cell names a file of this checkout, and that file is a link to another repository's file outside W. The path is followed to the physical file, and that file is another repository's (Amendment - 8(d)): written there, said, its line printed, 0, committed nowhere. This - checkout and origin are as they were, the link still a link.""" - other = box.root / "other" - box.git("init", "-q", "-b", "main", str(other)) - box.write(other, "handoff.md", HANDOFF) - box.commit(other, "another repository's handoff", "handoff.md") + 8(d)): written there, said, its line printed, 0, and committed and + pushed in that repository (604), its tree clean. This checkout and + origin are as they were, the link still a link.""" + other = _other_repository(box) other_head = box.git("-C", str(other), "rev-parse", "HEAD").strip() seed = box.seed box.git("-C", str(seed), "pull", "-q", "--ff-only", "origin", "main") @@ -2031,8 +2152,7 @@ def test_a_handoff_that_links_out_of_the_checkout_is_another_repositorys(box: Bo assert result.returncode == 0, result.stderr assert "another repository's handoff (Amendment 8(d))" in result.stderr assert (other / "handoff.md").read_text(encoding="utf-8") == stamped(HANDOFF) - assert box.git("-C", str(other), "rev-parse", "HEAD").strip() == other_head - assert box.git("-C", str(other), "status", "--porcelain").split() == ["M", "handoff.md"] + _committed_and_pushed_there(box, other, other_head) assert (c / HANDOFF_REL).is_symlink() assert box.snapshot(c) == before assert result.stdout == _line(other / "handoff.md") From a79d0d917322b0e3f2a1499738725118a2b5417a Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 21:51:47 +0000 Subject: [PATCH 28/34] A58: 732, carried in 2a's file, `refresh_workspace_strays`' `git status` 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 (cherry picked from commit 7ffbcb04903468f6bf5d65d94b2b526351b315e6) --- lanes-edit.sh | 6 +++++- tests/test_a23_write_handoff.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 939df07..af3daf2 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -16815,8 +16815,12 @@ refresh_workspace() { # uncommitted, where nothing will commit it. Each one under `handoffs/` or # `lanes/` is named by path, and where a row's handoff cell names it, by the # lane whose handoff it is and the place in that lane's checkout it belongs. +# THE SCAN WRITES NOTHING IN W (lane openRepoTools-3's review of #316, 732): a +# plain `git status` that finds the index stat-dirty rewrites it under W's +# `index.lock`, a write S5 forbids, which 2b's launch would make twice. So it +# runs with `GIT_OPTIONAL_LOCKS=0`, as `ws_pending_refuse`'s status does. refresh_workspace_strays() { # - rws_list="$(git -C "$1" status --porcelain --untracked-files=all -- handoffs lanes 2>/dev/null || :)" + rws_list="$(GIT_OPTIONAL_LOCKS=0 git -C "$1" status --porcelain --untracked-files=all -- handoffs lanes 2>/dev/null || :)" [ -n "$rws_list" ] || return 0 rws_map="$(awk -F'|' 'substr($0,1,1) == "|" { p1 = index($0, "`"); if (p1 == 0) next diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 364dec0..0187d98 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -2269,6 +2269,36 @@ def test_handoff_path_refuses_a_relative_cell_that_climbs_out_of_the_checkout(bo assert not (c / "handoffs" / "notyet").exists() +# ------------------------------------------------- 732: 1a's stray scan, carried in 2a's file + +def test_refresh_workspaces_stray_scan_never_rewrites_Ws_index(box: Box): + """732 of lane openRepoTools-3's review of #316 (1a's code, in this + slice's file): `refresh_workspace_strays` 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 S5 + forbids, and 2b's launch runs the scan twice. It runs with + `GIT_OPTIONAL_LOCKS=0`, as `ws_pending_refuse`'s status does: W's index + is as it was, its mtime and its bytes, and the stray is still named. A + plain `git status` afterwards does rewrite it, so the index was + stat-dirty.""" + w = box.w + index = w / ".git" / "index" + reg = w / REGISTER + st = reg.stat() + os.utime(reg, ns=(st.st_atime_ns, st.st_mtime_ns + 5_000_000_000)) + box.write(w, "handoffs/repoS4/stray.md", "# a stray\n") + time.sleep(1.1) + before = (index.stat().st_mtime_ns, index.read_bytes()) + result = box.helper("refresh-workspace", timeout=120) + assert result.returncode == 0, result.stderr + assert "a stray in W: handoffs/repoS4/stray.md (??)" in result.stderr + assert (index.stat().st_mtime_ns, index.read_bytes()) == before, "W's index was rewritten" + plain = subprocess.run(["git", "-C", str(w), "status", "--porcelain"], env=box.env, + capture_output=True, text=True, check=False) + assert plain.returncode == 0, plain.stderr + assert index.stat().st_mtime_ns != before[0], "the index was not stat-dirty: the case proves nothing" + + # ------------------------------------------------- m2a's battery, its second half def test_stamp_into_an_empty_handoff_is_refused_1_and_says_the_line(box: Box): From 2bb3e2a7461bd71019cc06de43d7e0a454812df8 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Fri, 23 Oct 2026 21:59:27 +0000 Subject: [PATCH 29/34] A59: the NITs of lane openRepoTools-3's review of #312, a fenced `--prepend` 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 (cherry picked from commit 60ddad353b34f00061e013dbe6386cfd43d181b4) --- lanes-edit.sh | 49 +++++++++++- tests/test_a23_write_handoff.py | 136 ++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 4 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index af3daf2..5543269 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -496,7 +496,8 @@ # `handoff-path` and `set-restart-intent --handoff` refuse the same # handoff by the same check, 601), a relative cell or `--target` whose # `.` or `..` components climb out of the checkout (S2, 602; `handoff-path` -# refuses that cell too), or a splice that does not prove out, each with +# refuses that cell too), a handoff whose path holds a glob character +# (607), or a splice that does not prove out, each with # nothing written — a restart's bound file or pre-image that is not this # one is `publish-handoff`'s 2 and `write-handoff`'s 7 (603: its 2 is # never a restart fence's); while S3's refusal of a `--prepend` or a @@ -3105,14 +3106,23 @@ EOF note "origin/$LANES_BRANCH's $CP_WHOLE_REL is ${cpw_now:-gone} now, and this whole-file write started from ${CP_WHOLE_BLOB:-no such file}." cpw_head="$(git -C "$LANES_REPO" rev-parse -q --verify HEAD 2>/dev/null || :)" if [ "$cpw_head" = "$cp_sha" ]; then + # THE COMMITS UNDER IT ARE NAMED TOO (lane openRepoTools-3's review of + # #312, 609, as 1b's 344 names them below): an unlocked writer's + # commit landing after S3 and before this write's `git commit` sits + # under it, and the `reset --keep` the next note offers would take it. + if git -C "$LANES_REPO" rev-parse -q --verify "$cp_sha^" >/dev/null 2>&1 && + ! git -C "$LANES_REPO" merge-base --is-ancestor "$cp_sha^" "refs/remotes/origin/$LANES_BRANCH" 2>/dev/null; then + cpw_un="$(git -C "$LANES_REPO" rev-list --count "refs/remotes/origin/$LANES_BRANCH..$cp_sha^" 2>/dev/null || printf 'some')" + note "under it are $cpw_un commit(s) that origin/$LANES_BRANCH lacks and this write did not make; list them: git -C $LANES_REPO log --oneline refs/remotes/origin/$LANES_BRANCH..HEAD; if any is not your own write, STOP and take it to the lane its subject names — never reset, stash or overwrite a peer's work" + fi note "look at it: git -C $LANES_REPO show $cp_sha; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); the commit is local and kept on HEAD: $cp_sha $msg" 6 fi if [ -z "$cpw_head" ] || ! git -C "$LANES_REPO" merge-base --is-ancestor "$cp_sha" "$cpw_head" 2>/dev/null; then - cp_not_head "$cp_sha" "$msg" "while its push was in flight" + cp_not_head "$cp_sha" "$msg" "before or while its push was in flight" fi - note "HEAD is $cpw_head, not this write's commit $cp_sha: another commit was made on top of it in this checkout while its push was in flight. Nothing is reset, so both are kept." + note "HEAD is $cpw_head, not this write's commit $cp_sha: another commit was made on top of it in this checkout before or while its push was in flight (609, 1b's 343). Nothing is reset, so both are kept." note "look at both: git -C $LANES_REPO log --oneline $cp_sha^..HEAD; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" note "every later write from this checkout refuses (S3's unpushed row) until they are published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3)." die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); this write's commit and the one on top of it are local and kept, with HEAD at $cpw_head: $cp_sha $msg" 6 @@ -16886,6 +16896,12 @@ fi # of them would come to disagree. The READS are untouched — a read in # front of every launch may not refuse, and one that answers nothing for a # workstation nobody configured is telling the truth. +# `write-handoff` (Amendment 23, slice 2a) IS NOT IN THE LIST, ON PURPOSE +# (lane openRepoTools-3's review of #312, 611; R-A11-11): a swap is never left +# unwritten, so a container with no workstation still gets its handoff written +# and published, and the commit's subject is the caller's, `lane-start`'s and +# `lane-handoff`'s, which spell the workstation themselves. So not EVERY writer +# refuses here: every writer in this list does. case "$cmd" in # `request-handoff` JOINS THE LIST BECAUSE IT WRITES (Amendment 18(c)/(e)). It # reaches `write_event` directly rather than through `log`, so the dispatcher @@ -20012,7 +20028,8 @@ EOF # 2 refused: S3 (step 6), S5, a target that is not under handoffs/ (S2) or # is inside a git directory, one in a checkout beside W that is not this # one, a relative spelling that climbs out of the checkout (S2), a - # proof, a restart intent that is malformed — never a restart fence (603) + # glob character in the handoff's path (607), a proof, a restart intent + # that is malformed — never a restart fence (603) # 3 pushed, not confirmed · 4 a lock · 6 kept on HEAD, named — and an # 8(d) handoff, written, whose commit or push in its own repository # failed (the command that finishes it said) @@ -20075,6 +20092,17 @@ EOF [ "${#whf_rexp}" = 64 ] || die "--expected-digest takes a SHA-256, or none" 64 case "$whf_rexp" in *[!0-9a-fA-F]*) die "--expected-digest takes a SHA-256, or none" 64 ;; esac fi + # A FENCED `--prepend` TAKES `--expected-digest none` ONLY (lane + # openRepoTools-3's review of #312, 605; A58). Fenced on a digest, a push + # that lost its race to a peer's change of the handoff was replayed onto a + # pre-image that was no longer the bound one, 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 here: one that does is usage, + # refused before the lock, and a swap's block is never lost to a replay. + if [ "$whf_mode" = prepend ] && [ -n "$whf_rexp" ] && [ "$whf_rexp" != none ]; then + die "--prepend takes --expected-digest none only: fenced on a digest, a replay after a lost push race would find the handoff changed, refuse, and leave the block in the reflog only (605; lane-handoff binds its prepend at preparing with none). Nothing was written." 64 + fi if [ "$whf_mode" != stamp ]; then [ -f "$whf_arg" ] && [ -r "$whf_arg" ] && [ -s "$whf_arg" ] || die "the $whf_mode file $whf_arg is unreadable or empty. Nothing was written." 1 @@ -20146,6 +20174,19 @@ EOF whf_stamp_said die "lane $lane's handoff target $whf_path is $whf_rel in the checkout $whf_c, which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never the register ($LANES_PATH) or anything under lanes/ (Amendment 23 S2). Nothing was written." 2 fi + # NO GLOB CHARACTER IN THE HANDOFF'S PATH (lane openRepoTools-3's review + # of #312, 607; A58). `commit_publish` stages and commits its pathspec as + # git reads one, a pattern, so `handoffs//x[1].md` names + # `handoffs//x1.md` too, a file this write never wrote, which its + # commit could carry under its subject. No lane's handoff is named so + # (a lane name has none of `*?[`), so it is refused, nothing written. + if [ "$whf_other" = 0 ]; then + case "$whf_rel" in + *'*'*|*'?'*|*'['*) + whf_stamp_said + die "lane $lane's handoff $whf_rel in the checkout $whf_c has a glob character ('*', '?' or '[') in its path, and git reads a pathspec as a pattern, so this write's commit could stage a file it never wrote (607). Nothing was written. Name the handoff without one." 2 ;; + esac + fi # AND IN ANOTHER CLONE OF THE REGISTER (rv2a-c2's re-review of slice 2a, # SHOULD 1). A path outside this checkout, in a git work tree whose top # level carries the register too — a sibling lane's `-lanes//`, diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 0187d98..9dd6ef9 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1742,6 +1742,8 @@ def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): assert box.subject(c) == "a stamp from an unlocked writer" assert box.subject(c, "HEAD~1") == "handoff: PAUSED" assert f"HEAD is {top}, not this write's commit {mine}" in result.stderr + assert ("another commit was made on top of it in this checkout before or while its push was " + "in flight") in result.stderr, "609: 1b's 343 words" assert f"kept, with HEAD at {top}: {mine} handoff: PAUSED" in result.stderr assert f"kept on HEAD: {mine}" not in result.stderr assert "push refused (attempt" not in result.stderr, "no reset, no replay" @@ -2299,6 +2301,140 @@ def test_refresh_workspaces_stray_scan_never_rewrites_Ws_index(box: Box): assert index.stat().st_mtime_ns != before[0], "the index was not stat-dirty: the case proves nothing" +# ------------------------------------------------- the NITs of lane 3's review of #312 + +def test_a_fenced_prepend_with_a_bound_digest_is_usage_64(box: Box): + """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, refused, and its block was left in the + reflog only. `lane-handoff` binds its prepend at `preparing` with `none` + and takes the digest again at `pending`, so a sha there has no caller: + usage (64), before the lock, nothing written and no fetch made. `none` + is still taken (`test_a_prepend_fenced_at_preparing_takes_expected_digest_none`).""" + c = box.seam() + f = c / HANDOFF_REL + block = _block(box) + box.use_net() + before = box.snapshot(c) + result = box.write_handoff("--prepend", str(block), "--subject", "handoff: PAUSED", + "--restart", FENCE[0], FENCE[1], "0", "preparing", "none", + "--expected-digest", sha(f)) + assert result.returncode == 64, result.stderr + assert "--prepend takes --expected-digest none only" in result.stderr + assert "Nothing was written." in result.stderr + assert f.read_text(encoding="utf-8") == HANDOFF + assert box.snapshot(c) == before + assert box.net_ops() == [] + assert result.stdout == "" + + +def test_a_handoff_whose_path_holds_a_glob_character_is_refused(box: Box): + """607: `commit_publish` stages and commits its pathspec as git reads + one, a pattern, so a handoff named `x[1].md` names `x1.md` too, here a + file nobody committed. At 7ffbcb0 S3 step 4 did not refuse it (it is + not the handoff), and the write's commit carried `x1.md` under its + subject and pushed it (0). A lane's handoff is never named so: refused + (2), nothing written, staged or pushed, the stamp line said.""" + glob_rel = "handoffs/repoS4/x[1].md" + box.peer_pushes(glob_rel, HANDOFF) + box.cell_is(glob_rel) + c = box.seam() + box.write(c, "handoffs/repoS4/x1.md", "# a file nobody committed\n") + mine, tip = box.snapshot(c), box.origin_head() + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT) + assert result.returncode == 2, result.stderr + assert (f"lane {LANE}'s handoff {glob_rel} in the checkout {c} has a glob character ('*', '?' " + "or '[') in its path") in result.stderr + assert "Nothing was written." in result.stderr + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert (c / glob_rel).read_text(encoding="utf-8") == HANDOFF + assert box.snapshot(c) == mine + assert box.origin_head() == tip + assert result.stdout == "" + _released(box, c) + + +def test_whole_kept_by_S4_4_names_the_commits_under_it(box: Box): + """609: S4.4's refusal did not list the commits under this write's, as + 1b's 344 does. An unlocked writer's stamp landing just before this + whole-file write's commit sits under it, and the `reset --keep` S4.4's + note offers would take it, so they are counted and the STOP line said. + Origin's copy moved, so nothing is replayed: kept on HEAD (6), both + commits there for the publish act.""" + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + box.race(prepared=True) + env = _git_shim(box, '*" commit -q -m handoff("*', before=_stamp(c)) + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", SUBJECT, **env) + assert (box.root / "fired").exists(), "the stamp landed just before this write's commit" + assert result.returncode == 6, result.stderr + kept = box.head(c) + assert box.subject(c) == SUBJECT + assert box.subject(c, "HEAD~1") == "handoff(repoS4-2@Eagle): a stamp from an unlocked writer" + assert "is no longer the one this whole-file write started from" in result.stderr + assert f"kept on HEAD: {kept}" in result.stderr + assert ("under it are 1 commit(s) that origin/main lacks and this write did not make" + in result.stderr), result.stderr + assert "STOP and take it to the lane its subject names" in result.stderr + assert "push refused (attempt" not in result.stderr, "no replay" + assert len(box.unpublished(c)) == 2, "both kept on HEAD" + assert result.stdout == _line(f) + _released(box, c) + + +def test_a_prepend_S3_refused_writes_under_the_checkouts_lock(box: Box): + """610: no case showed that a `--prepend` S3 refused, which still writes + the handoff (R-A11-11), writes it under the checkout's lock. While another + act holds the workstation's sidecar lock, the write waits HOLDING the + checkout's lock, the handoff unwritten; then it writes it, uncommitted + (2), and releases both.""" + c = box.seam() + f = c / HANDOFF_REL + block = _block(box) + _staged_foreign(box, c) + sidecar = box.lanes_root / ".lane-state.lock" + holder = subprocess.Popen(["sleep", "300"]) + try: + sidecar.mkdir(parents=True) + (sidecar / "pid").write_text(f"{holder.pid} {ME}\n", encoding="utf-8") + waiter = box.popen("write-handoff", LANE, "--prepend", str(block), "--subject", "handoff: PAUSED") + time.sleep(WATCH) + assert waiter.poll() is None, "it waits on the workstation's sidecar lock" + assert box.lock_of(c).is_dir(), "...holding its checkout lock, though S3 refused" + assert f.read_text(encoding="utf-8") == HANDOFF + finally: + holder.kill() + holder.wait() + out, err = waiter.communicate(timeout=120) + assert waiter.returncode == 2, err + assert "the handoff is WRITTEN and NOT committed" in err + assert f.read_text(encoding="utf-8") == prepended(HANDOFF) + assert out == _line(f) + _released(box, c) + + +def test_write_handoff_is_exempt_from_the_workstation_guard(box: Box): + """610 and 611: in a container with no `LANES_WORKSTATION`, every writer + in the dispatcher's guard refuses (2, R-A11-14), and `write-handoff` is + not in that guard, on purpose (R-A11-11: a swap is never left unwritten, + and the commit's subject is its caller's). It writes, commits and + pushes the handoff; `set-row-state` beside it is refused.""" + c = box.seam() + unset = {"LANES_WORKSTATION": "", "LANES_IN_CONTAINER": "1"} + guarded = box.helper("set-row-state", LANE, "LIVE · in a container", **unset) + assert guarded.returncode == 2, guarded.stderr + assert "this is a container and $LANES_WORKSTATION is not set" in guarded.stderr + result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, **unset) + assert result.returncode == 0, result.stderr + assert "$LANES_WORKSTATION is not set" not in result.stderr + assert box.origin_file(HANDOFF_REL) == stamped(HANDOFF) + assert result.stdout == _line(c / HANDOFF_REL) + _released(box, c) + + # ------------------------------------------------- m2a's battery, its second half def test_stamp_into_an_empty_handoff_is_refused_1_and_says_the_line(box: Box): From 132084746115ca9e769e614c0dfa604c14e5812a Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Sat, 24 Oct 2026 14:47:02 +0000 Subject: [PATCH 30/34] A60: 991, under 723, `handoff-path` and `set-restart-intent --handoff` 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 `/handoffs//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 `: 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 (cherry picked from commit 7bc572cdbf27afeb692a7f98aeeb095d52c5f38f) --- lanes-edit.sh | 61 ++++++++++++++++++++++++-------- tests/test_a23_write_handoff.py | 62 +++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 15 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 5543269..8eda912 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -497,7 +497,8 @@ # handoff by the same check, 601), a relative cell or `--target` whose # `.` or `..` components climb out of the checkout (S2, 602; `handoff-path` # refuses that cell too), a handoff whose path holds a glob character -# (607), or a splice that does not prove out, each with +# (607; `handoff-path` and `set-restart-intent --handoff` refuse it +# too, 991), or a splice that does not prove out, each with # nothing written — a restart's bound file or pre-image that is not this # one is `publish-handoff`'s 2 and `write-handoff`'s 7 (603: its 2 is # never a restart fence's); while S3's refusal of a `--prepend` or a @@ -16627,6 +16628,29 @@ handoff_beside_w() { # — esac return 1 } +# NO GLOB CHARACTER IN A HANDOFF'S PATH IN ITS CHECKOUT (lane +# openRepoTools-3's review of #312, 607; A59), ASKED BY ALL THREE VERBS (991 +# of its first review of #311, under 723; A60). `commit_publish` stages and +# commits its pathspec as git reads one, a pattern, so `handoffs//x[1].md` +# names `handoffs//x1.md` too, a file the write never wrote, which its +# commit could carry under its subject. No lane's handoff is named so (a lane +# name has none of `*?[`), so `write-handoff` refuses it; and where +# `handoff-path` printed that path, a restart was bound to a file the write +# then refused. So `write-handoff`, `handoff-path` and `set-restart-intent +# --handoff` ask this ONE question, as they ask 601's: of the PHYSICAL path +# (`path_physical`, as `write-handoff`'s scope check reads it), relative to +# the checkout. A path outside the checkout is not this rule's: another +# repository's handoff is Amendment 8(d)'s, committed by its own top level's +# pathspec (A57). Sets HRG_REL: the path in the checkout. +HRG_REL="" +handoff_rel_globbed() { # — 0 refused, HRG_REL set; 1 not + HRG_REL="" + hrg_p="$(path_physical "$2")" || hrg_p="$2" + path_under "$hrg_p" "$1" || return 1 + HRG_REL="${hrg_p#"$1"/}" + case "$HRG_REL" in *'*'*|*'?'*|*'['*) return 0 ;; esac + return 1 +} # THE CHECKOUT A LANE'S HANDOFF IS WRITTEN THROUGH, for the two acts that answer # it without writing (`handoff-path`, `set-restart-intent`): S1's path under the # opt-in with no explicit `LANES_REPO` — made first with `create` 1, or checked @@ -20175,17 +20199,11 @@ EOF die "lane $lane's handoff target $whf_path is $whf_rel in the checkout $whf_c, which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never the register ($LANES_PATH) or anything under lanes/ (Amendment 23 S2). Nothing was written." 2 fi # NO GLOB CHARACTER IN THE HANDOFF'S PATH (lane openRepoTools-3's review - # of #312, 607; A58). `commit_publish` stages and commits its pathspec as - # git reads one, a pattern, so `handoffs//x[1].md` names - # `handoffs//x1.md` too, a file this write never wrote, which its - # commit could carry under its subject. No lane's handoff is named so - # (a lane name has none of `*?[`), so it is refused, nothing written. - if [ "$whf_other" = 0 ]; then - case "$whf_rel" in - *'*'*|*'?'*|*'['*) - whf_stamp_said - die "lane $lane's handoff $whf_rel in the checkout $whf_c has a glob character ('*', '?' or '[') in its path, and git reads a pathspec as a pattern, so this write's commit could stage a file it never wrote (607). Nothing was written. Name the handoff without one." 2 ;; - esac + # of #312, 607; A59): the question `handoff-path` and `set-restart-intent + # --handoff` ask too (`handoff_rel_globbed`, 991; A60), nothing written. + if [ "$whf_other" = 0 ] && handoff_rel_globbed "$whf_c" "$whf_path"; then + whf_stamp_said + die "lane $lane's handoff $HRG_REL in the checkout $whf_c has a glob character ('*', '?' or '[') in its path, and git reads a pathspec as a pattern, so this write's commit could stage a file it never wrote (607). Nothing was written. Name the handoff without one." 2 fi # AND IN ANOTHER CLONE OF THE REGISTER (rv2a-c2's re-review of slice 2a, # SHOULD 1). A path outside this checkout, in a git work tree whose top @@ -20469,7 +20487,9 @@ EOF # 2 a name refused, a handoff in W or in another checkout beside it # (601, as `write-handoff` refuses it; nothing on stdout), a relative # cell whose `.` or `..` components climb out of the checkout (602, S2), - # or the clone failed · 4 the creation lock · 64 usage + # a path in the checkout that holds a glob character (607, as + # `write-handoff` refuses it, 991), or the clone failed · 4 the creation + # lock · 64 usage handoff-path) hp_lane=""; hp_create=0 while [ $# -gt 0 ]; do @@ -20515,6 +20535,9 @@ EOF if handoff_beside_w "$HCO_PATH" "$hp_path"; then die "lane $hp_lane's handoff $hp_cell is $HBW_WHY: \`write-handoff\` refuses it, so no path is printed for a restart to bind or a launch to read (601). Nothing was written." 2 fi + if handoff_rel_globbed "$HCO_PATH" "$hp_path"; then + die "lane $hp_lane's handoff $hp_cell is $HRG_REL in the checkout $HCO_PATH, with a glob character ('*', '?' or '[') in its path: \`write-handoff\` refuses it (607), so no path is printed for a restart to bind or a launch to read (991). Nothing was written. Name the handoff without one." 2 + fi [ "$hp_rc" = 0 ] || note "$hp_path is another repository's handoff (Amendment 8(d)): \`write-handoff\` writes it there, and commits and pushes it in that repository (604), never in another clone of the register" printf '%s\n' "$hp_path" ;; @@ -20689,17 +20712,25 @@ EOF if handoff_beside_w "$HCO_PATH" "$sri_hf"; then die "--handoff $sri_hf_cell is $HBW_WHY: \`write-handoff\` refuses it, so no restart is bound to it (601). Nothing was written." 2 fi + if handoff_rel_globbed "$HCO_PATH" "$sri_hf"; then + die "--handoff $sri_hf_cell is $HRG_REL in the checkout $HCO_PATH, with a glob character ('*', '?' or '[') in its path: \`write-handoff\` refuses it (607), so no restart is bound to it (991). Nothing was written." 2 + fi elif [ -n "$sri_hf" ] && [ -n "$LANES_WS_REAL" ]; then # AN ABSOLUTE PATH IS KEPT AS GIVEN (W's spelling is every older # caller's, and `write-handoff` translates it on its side), but one that - # lands in a checkout beside W is asked the same question as a cell - # (601): only the lane's own checkout there is its handoff's. + # lands in a checkout beside W is asked the same questions as a cell + # (601): only the lane's own checkout there is its handoff's, and no + # glob character in its path there (607, 991) — the path `handoff-path` + # prints under the opt-in. W's own spelling is not asked (A60). sri_hf_phys="$(path_physical "$sri_hf")" || sri_hf_phys="$sri_hf" case "$sri_hf_phys" in "$LANES_WS_REAL"-*/*) handoff_checkout_of "$lane" 0 if handoff_beside_w "$HCO_PATH" "$sri_hf"; then die "--handoff $sri_hf is $HBW_WHY: \`write-handoff\` refuses it, so no restart is bound to it (601). Nothing was written." 2 + fi + if handoff_rel_globbed "$HCO_PATH" "$sri_hf"; then + die "--handoff $sri_hf is $HRG_REL in the checkout $HCO_PATH, with a glob character ('*', '?' or '[') in its path: \`write-handoff\` refuses it (607), so no restart is bound to it (991). Nothing was written." 2 fi ;; esac fi diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 9dd6ef9..640cc1f 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -2354,6 +2354,68 @@ def test_a_handoff_whose_path_holds_a_glob_character_is_refused(box: Box): _released(box, c) +#: 607's handoff: a path whose part in the checkout holds a glob character. +GLOB_REL = "handoffs/repoS4/x[1].md" + + +@pytest.mark.parametrize("how,argv", [("seam", ()), ("routed", ()), ("routed", ("--create",))]) +def test_handoff_path_refuses_a_glob_character_as_write_handoff_does(tmp_path: Path, how: str, argv: tuple): + """991 of lane openRepoTools-3's first review of #311, 2a's to close + under 723: at 60ddad3 `handoff-path` printed `/handoffs/repoS4/x[1].md`, + a path `write-handoff` refuses (607), for a restart to bind and a launch + to read. The verbs now ask one question of the physical path in the + checkout (`handoff_rel_globbed`, A60), so `handoff-path` refuses it too + (2): no path on stdout, the refusal named, nothing written. Routed, a + read without `--create` makes no checkout, and `--create` makes the + lane's own (S1's) before the question is asked, as 601's refusal does.""" + box = Box(tmp_path, route=(how == "routed")) + box.peer_pushes(GLOB_REL, HANDOFF) + box.cell_is(GLOB_REL) + c = box.seam() if how == "seam" else box.checkout(LANE) + mine = box.snapshot(c) if how == "seam" else None + result = box.helper("handoff-path", LANE, *argv) + assert result.returncode == 2, result.stderr + assert (f"lane {LANE}'s handoff {GLOB_REL} is {GLOB_REL} in the checkout {c}, with a glob character " + "('*', '?' or '[') in its path: `write-handoff` refuses it (607), so no path is printed for " + "a restart to bind or a launch to read (991)") in result.stderr + assert "Nothing was written." in result.stderr + assert "another repository's handoff" not in result.stderr + assert result.stdout == "" + if how == "seam": + assert box.snapshot(c) == mine + elif argv: + assert (c / ".git").exists(), "--create made the lane's own checkout first" + else: + assert not c.exists(), "a read makes no checkout" + + +@pytest.mark.parametrize("how", ["cell", "abs-in-lane-checkout"]) +def test_set_restart_intent_refuses_a_glob_character_as_write_handoff_does(tmp_path: Path, how: str): + """991 in the third verb that translates a handoff: `set-restart-intent + --handoff` bound a restart to `x[1].md`, a file `write-handoff` then + refuses (607). It asks the same question where it asks 601's (A60): of + the row's cell translated into the checkout, and of an absolute path in + the lane's own checkout beside W, the path `handoff-path` prints under + the opt-in. Refused (2): no intent written, nothing on stdout. W's own + absolute spelling is still kept as given (every older caller's binding, + A54), and `write-handoff` refuses it when it writes.""" + box = Box(tmp_path, route=True) + if how == "cell": + c = box.seam() + spelled = GLOB_REL + else: + c = box.checkout(LANE) + spelled = str(c / GLOB_REL) + made = _bound(box, spelled) + assert made.returncode == 2, made.stderr + assert (f"--handoff {spelled} is {GLOB_REL} in the checkout {c}, with a glob character ('*', '?' or " + "'[') in its path: `write-handoff` refuses it (607), so no restart is bound to it (991)" + ) in made.stderr + assert "Nothing was written." in made.stderr + assert made.stdout == "" + assert not (box.state / LANE / "restart-intent.yaml").exists() + + def test_whole_kept_by_S4_4_names_the_commits_under_it(box: Box): """609: S4.4's refusal did not list the commits under this write's, as 1b's 344 does. An unlocked writer's stamp landing just before this From 56a9b877cf1b26dd1a333aa3c4239be0de146c98 Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Sun, 8 Nov 2026 18:16:18 +0000 Subject: [PATCH 31/34] A61: CI's git 2.56.0 read the clone that `other-owner` makes unreadable, so the case now sets `GIT_CONFIG_NOSYSTEM=1`, and skips, saying why, where git still reads it (opensoft/openRepoTools#198, slice 2a, opensoft/openRepoTools-code#18) CI on opensoft/openRepoTools-code#18 at 2b1a232 ran git 2.56.0 and failed `tests` and `tests-no-submodule`: 2 failed, 3652 passed. Both were `test_a_clone_git_cannot_read_is_never_taken_for_no_repository`, `[beside-W-other-owner]` and `[elsewhere-other-owner]`, at `_unreadable`'s own probe, `git -C rev-parse --show-toplevel`, which exited 0: "git can still read the clone". The `bad-config` variants passed there. This workstation has git 2.43.0, where `GIT_TEST_ASSUME_DIFFERENT_OWNER=1` refuses the clone (128, "dubious ownership"), so the failure could not be seen here. Two causes fit: (a) that git ignores the test hook, or (b) the runner's system config (`/etc/gitconfig`) names `safe.directory = *`, which the box's own `.gitconfig` does not override. - `other-owner` sets `GIT_CONFIG_NOSYSTEM=1` beside the hook, in the environment `_unreadable` returns, so the probe, the check of the lane's own checkout and the write under test all run in it. The box scrubs `GIT_*` and sets HOME to its own, so `$HOME/.gitconfig` stays the global config git reads and names the box's repositories safe. That answers (b). - Where the probe still reads the clone under `other-owner`, the case is skipped (`pytest.skip`), never passed: the reason has `git --version` and every `safe.directory` git reports in that environment with its file (`git config --show-origin --get-all safe.directory`), and names the cause, a `*` in the global config or a git that ignores the hook (a). CI's output then says which it was. - `bad-config` is as it was. - Shown on this workstation, git 2.43.0: both `other-owner` cases run and pass, not skipped; a scratch system config with `safe.directory = *` (`GIT_CONFIG_SYSTEM`) lets the probe read the clone without `GIT_CONFIG_NOSYSTEM` (exit 0, the CI failure) and refuses it with it (128); a `*` put into the box's own `.gitconfig`, and the hook given as 0 to stand for a git that ignores it, each skip the case with their reason. Co-Authored-By: Claude Sonnet 5.5 --- tests/test_a23_write_handoff.py | 34 +++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 640cc1f..50ff7cb 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -1339,10 +1339,38 @@ def test_a_handoff_in_another_checkout_of_the_register_beside_W_is_refused(box: _released(box, c) +def _git_says(env: dict, *args: str, cwd: Path) -> str: + got = subprocess.run(["git", *args], env=env, cwd=str(cwd), capture_output=True, text=True, check=False) + return (got.stdout + got.stderr).strip() or "nothing" + + +def _skip_other_owner(box: Box, env: dict) -> None: + """Skip, saying why: git read a clone it was told to take for another + owner's. Either this git ignores `GIT_TEST_ASSUME_DIFFERENT_OWNER`, or a + `safe.directory = *` reaches it through the global config; the version + and every `safe.directory` it reports in this environment (each value + with the file it comes from) name which.""" + version = _git_says(env, "--version", cwd=box.root) + safe = _git_says(env, "config", "--show-origin", "--get-all", "safe.directory", cwd=box.root) + star = any(line.split("\t")[-1].strip() == "*" for line in safe.splitlines()) + cause = ("a `safe.directory = *` in the global config" if star + else "a git that ignores GIT_TEST_ASSUME_DIFFERENT_OWNER") + pytest.skip(f"{version}: with GIT_TEST_ASSUME_DIFFERENT_OWNER=1 and GIT_CONFIG_NOSYSTEM=1, git still reads " + f"a clone of another owner, which fits {cause}; safe.directory as git reports it in this " + f"environment: {safe.replace(chr(10), '; ')}") + + def _unreadable(box: Box, d: Path, how: str, c: Path) -> dict: """Make the clone at `d` one git cannot read: a broken line in its config, or another owner (git's `dubious ownership`, with this box's own - repositories named safe). Returns the environment the write runs with.""" + repositories named safe). Returns the environment the write runs with. + Another owner is git's test hook with `GIT_CONFIG_NOSYSTEM=1`, so that a + `safe.directory = *` in the machine's system config cannot name the clone + safe (on CI's git 2.56.0 the probe read the clone, and this case failed + there, with the box's `.gitconfig` as the global config): the box's + `.gitconfig` is the only config that names a directory safe, and the + write runs in the environment the probe ran in. Where git still reads the + clone, the case is skipped with the cause, never passed.""" extra = {} if how == "bad-config": with open(d / ".git" / "config", "a", encoding="utf-8") as fh: @@ -1351,9 +1379,11 @@ def _unreadable(box: Box, d: Path, how: str, c: Path) -> dict: safe = (c, box.w, box.origin, box.seed, box.peer) (box.home / ".gitconfig").write_text( "[safe]\n" + "".join(f"\tdirectory = {x}\n" for x in safe), encoding="utf-8") - extra = {"GIT_TEST_ASSUME_DIFFERENT_OWNER": "1"} + extra = {"GIT_TEST_ASSUME_DIFFERENT_OWNER": "1", "GIT_CONFIG_NOSYSTEM": "1"} probe = subprocess.run(["git", "-C", str(d), "rev-parse", "--show-toplevel"], env=dict(box.env, **extra), capture_output=True, text=True, check=False) + if how == "other-owner" and probe.returncode == 0: + _skip_other_owner(box, dict(box.env, **extra)) assert probe.returncode != 0, ("git can still read the clone", probe.stdout) mine = subprocess.run(["git", "-C", str(c), "rev-parse", "--show-toplevel"], env=dict(box.env, **extra), capture_output=True, text=True, check=False) From 05474082722f7991d9ce69c2d00f647af27fb7ac Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Sun, 8 Nov 2026 19:39:39 +0000 Subject: [PATCH 32/34] A62: S4.4's two notes after a push that origin's move refused no longer offer the publish act, which refuses there (2), and give S6.3's archive as 1b's 705 does (opensoft/openRepoTools#198, slice 2a, review of opensoft/openRepoTools-code#18, SHOULD 2) Lane openRepoTools-3's review of opensoft/openRepoTools-code#18 at 2b1a232 (#198 6726042267), SHOULD 2. `commit_publish`'s S4.4 refusal of a whole-file `write-handoff` runs only after a push was refused because origin moved. Its two notes, where this write's commit is HEAD and where another commit sits on top of it, still said "until it is published - LANES_LANE= lanes-edit.sh commit ..., the publish act - or archived", and the first also offered a short `reset --keep origin/main`. The publish act refuses there: after the refusal it exits 2, "refusing to publish: origin/main has moved past the local commits ... Nothing was pushed." 1b's round after d8859c8 (705, NIT 294) moved every other origin-moved note to `cp_recovery_steps`, and `cp_not_head`'s comment names 2a's S4.4 as such a caller; the merge of 1b (2b1a232) brought the rule in without applying it to these two lines. - The two notes are now `cp_recovery_steps "it is"` and `cp_recovery_steps "they are"`: what is kept must be archived and dropped, then S6.3 in order, the list, the STOP line, the archive on origin and the drop, with the tracking ref named in full (`reset --keep refs/remotes/origin/main`). - The case runs both notes (`how`: this write's commit is HEAD, or an unlocked writer's stamp is on top of it), checks S6.3's order with test_a23_publish_replay's `_recovery_is_s63s_archive`, that no short `reset --keep origin/main` is printed, then runs the publish act exactly as the old note offered it: it exits 2, "moved past the local commits", "Nothing was pushed.", and origin and HEAD are as they were. The setup of the stamp-on-top race is one helper, shared with the older test. - Red first, on the parent's lanes-edit.sh (56a9b87): both cases failed, `_recovery_is_s63s_archive`'s `assert 0 <= lead` (the "until ... archived and dropped here" line was not there); with the two lines, both pass. - The docstrings and messages of four older S4.4 assertions said the commits wait "for the publish act"; they say "for the archive". Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 --- lanes-edit.sh | 8 +++- tests/test_a23_write_handoff.py | 65 ++++++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 11 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 1d1655c..046a777 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -3289,7 +3289,11 @@ EOF note "under it are $cpw_un commit(s) that origin/$LANES_BRANCH lacks and this write did not make; list them: git -C $LANES_REPO log --oneline refs/remotes/origin/$LANES_BRANCH..HEAD; if any is not your own write, STOP and take it to the lane its subject names — never reset, stash or overwrite a peer's work" fi note "look at it: git -C $LANES_REPO show $cp_sha; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" - note "every later write from this checkout refuses (S3's unpushed row) until it is published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3) and \`git -C $LANES_REPO reset --keep origin/$LANES_BRANCH\` run." + # THE PUBLISH ACT REFUSES HERE ("moved past", 2): origin moved, so the + # recovery is S6.3's archive, as 1b's 705 made every other + # origin-moved note say (lane openRepoTools-3's review of #18 at + # 2b1a232, SHOULD 2; A62). + cp_recovery_steps "it is" die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); the commit is local and kept on HEAD: $cp_sha $msg" 6 fi if [ -z "$cpw_head" ] || ! git -C "$LANES_REPO" merge-base --is-ancestor "$cp_sha" "$cpw_head" 2>/dev/null; then @@ -3297,7 +3301,7 @@ EOF fi note "HEAD is $cpw_head, not this write's commit $cp_sha: another commit was made on top of it in this checkout before or while its push was in flight (609, 1b's 343). Nothing is reset, so both are kept." note "look at both: git -C $LANES_REPO log --oneline $cp_sha^..HEAD; and at origin's: git -C $LANES_REPO log -1 -p refs/remotes/origin/$LANES_BRANCH -- $CP_WHOLE_REL" - note "every later write from this checkout refuses (S3's unpushed row) until they are published — LANES_LANE= $SELF commit \"\", the publish act — or archived (S6.3)." + cp_recovery_steps "they are" die "the push was refused because origin/$LANES_BRANCH moved, and origin's copy of $CP_WHOLE_REL is no longer the one this whole-file write started from, so it does not replay over that change (Amendment 23 S4.4); this write's commit and the one on top of it are local and kept, with HEAD at $cpw_head: $cp_sha $msg" 6 fi fi diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 50ff7cb..48897a8 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -38,7 +38,8 @@ from a23_box import ME, REGISTER from conftest import WINDOWS_SKIP -from test_a23_publish_replay import LANE, NEIGHBOUR, Box as ReplayBox, _git_shim, _stamp +from test_a23_publish_replay import (LANE, NEIGHBOUR, Box as ReplayBox, _git_shim, + _recovery_is_s63s_archive, _stamp) pytestmark = [pytest.mark.skipif(shutil.which("bash") is None, reason="lanes-edit.sh is a bash script"), @@ -1744,11 +1745,11 @@ def test_handoff_path_over_an_unreadable_register_is_1_never_8(box: Box): assert result.stdout == "" -def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): - """S4.4's refusal where another writer's commit landed on top of this - write's while its push was in flight: HEAD is that commit, so the words - name it and say both are kept — never "kept on HEAD" for this write's. - Nothing is reset (6), and both commits wait for the publish act.""" +def _whole_write_a_stamp_lands_on_top_of(box: Box) -> tuple: + """The setup S4.4's on-top cases share: a peer edits the handoff and lands + it at this write's push, and an unlocked writer's stamp lands on top of + this write's commit in the same moment. Returns the checkout and the + handoff in it.""" c = box.seam() f = c / HANDOFF_REL box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", @@ -1764,6 +1765,15 @@ def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): f"git -C {cq} add -- handoffs/stamp.md\n" f"git -C {cq} commit -q -m 'a stamp from an unlocked writer' -- handoffs/stamp.md\n" "exit 0\n") + return c, f + + +def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): + """S4.4's refusal where another writer's commit landed on top of this + write's while its push was in flight: HEAD is that commit, so the words + name it and say both are kept — never "kept on HEAD" for this write's. + Nothing is reset (6), and both commits wait for S6.3's archive.""" + c, f = _whole_write_a_stamp_lands_on_top_of(box) result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), "--subject", "handoff: PAUSED") assert result.returncode == 6, result.stderr @@ -1777,7 +1787,44 @@ def test_whole_refusal_names_HEAD_when_a_commit_landed_on_top(box: Box): assert f"kept, with HEAD at {top}: {mine} handoff: PAUSED" in result.stderr assert f"kept on HEAD: {mine}" not in result.stderr assert "push refused (attempt" not in result.stderr, "no reset, no replay" - assert len(box.unpublished(c)) == 2, "both kept, for the publish act" + assert len(box.unpublished(c)) == 2, "both kept, for the archive" + assert result.stdout == _line(f) + _released(box, c) + + +@pytest.mark.parametrize("how", ["head-is-this-write", "stamp-on-top"]) +def test_whole_kept_by_S4_4_offers_the_archive_not_the_publish_act_that_refuses(box: Box, how: str): + """lane openRepoTools-3's review of #18 at 2b1a232, SHOULD 2 (A62): S4.4's + two notes (this write's commit is HEAD, or another commit is on top of it) + run only after a push origin's move refused, and they offered the publish + act, with a short `reset --keep origin/main`. The publish act refuses + there ("moved past", 2), as 1b's 705 made every other origin-moved note + say. Now each says what is kept must be archived and dropped, then gives + S6.3's recovery: the list, the STOP line, the archive and the drop with + the tracking ref in full, never the publish act. The act is run after the + refusal, as the old note offered it: it refuses (2) and pushes nothing.""" + if how == "stamp-on-top": + c, f = _whole_write_a_stamp_lands_on_top_of(box) + else: + c = box.seam() + f = c / HANDOFF_REL + box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", + "a peer edits the handoff") + box.race(prepared=True) + result = box.write_handoff("--whole", str(_prepared(box)), "--expect-digest", sha(f), + "--subject", "handoff: PAUSED") + assert result.returncode == 6, result.stderr + err = result.stderr + assert "is no longer the one this whole-file write started from" in err + _recovery_is_s63s_archive(err, c, "it is" if how == "head-is-this-write" else "they are") + assert "reset --keep origin/main" not in err, "the tracking ref is named in full" + tip, mine = box.origin_head(), box.head(c) + offered = box.helper("commit", "what it is", LANES_LANE=LANE) + assert offered.returncode == 2, offered.stderr + assert "origin/main has moved past the local commits" in offered.stderr + assert "Nothing was pushed." in offered.stderr + assert box.origin_head() == tip + assert box.head(c) == mine assert result.stdout == _line(f) _released(box, c) @@ -1813,7 +1860,7 @@ def test_whole_refusal_names_a_rewritten_commit_as_rewritten(box: Box): assert "local and kept, with HEAD at" not in result.stderr, "no commit was made on top" assert "push refused (attempt" not in result.stderr, "no reset, no replay" assert box.subject(c) == "handoff: PAUSED, reworded by an unlocked writer" - assert len(box.unpublished(c)) == 1, "the rewritten copy waits for the publish act" + assert len(box.unpublished(c)) == 1, "the rewritten copy waits for the archive" assert result.stdout == _line(f) _released(box, c) @@ -2452,7 +2499,7 @@ def test_whole_kept_by_S4_4_names_the_commits_under_it(box: Box): whole-file write's commit sits under it, and the `reset --keep` S4.4's note offers would take it, so they are counted and the STOP line said. Origin's copy moved, so nothing is replayed: kept on HEAD (6), both - commits there for the publish act.""" + commits there for the archive.""" c = box.seam() f = c / HANDOFF_REL box.peer_prepares(HANDOFF_REL, "Preserved task.\n", "Preserved task, and a peer's line.\n", From 053b3829ff7aa82d5eca86acf55c199489c15d0f Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Sun, 8 Nov 2026 19:42:46 +0000 Subject: [PATCH 33/34] A63: question 4, as lane openRepoTools-3 answered it (refuse): `write-handoff` refuses (2, nothing written) a file under handoffs/ of another clone of the register that is not beside W, instead of writing it there uncommitted (opensoft/openRepoTools#198, slice 2a, opensoft/openRepoTools-code#18) Question 4 of opensoft/openRepoTools-code#18, answered by lane openRepoTools-3 in its review at 2b1a232 (#198 6726042267): refuse it with 2. Clones of the register outside W's siblings exist in this estate (per-lane `LANES_REPO` clones), and an uncommitted write there wedges that lane's next write at S3 step 4, which is 601's harm. A57 kept 604's commit and push out of such a clone and wrote the file there, exit 0, saying it was NOT committed; this round's first draft had refused it. - A44's test of the top level (`whf_oreg`, now asked once) already finds the clone: a top level that carries the register, with the target under it. A file there that is not under `handoffs/` was refused already (A44). A file under `handoffs/` is refused now, after 601's check, so a clone beside W keeps 601's words and A44's keep theirs: 2, before a byte, the stamp line said, nothing on stdout, that clone, this checkout and origin as they were. By `--target` or by an absolute cell, in `--stamp`, `--prepend` and `--whole`. - The branch of step 7 that wrote it there and did not commit it stays as a backstop for a top level that came to carry the register after the checks above, and its comment says so. - The header's exit row 2 and the step-2 comment name the refusal. The manual's exit row 2 (`docs/README-lanes.md`, the spec leg's) names none of `write-handoff`'s S2 refusals (A44's, 601's, 602's, 607's, A47's), so it needs no change for this one. - The case that pinned the old behaviour, `test_a_handoff_in_another_clone_of_the_register_elsewhere_is_written_never_committed`, is now `..._elsewhere_is_refused`, in four forms (`--stamp` and `--prepend` and `--whole` by `--target`, and `--stamp` by a cell). Red first, on the parent's lanes-edit.sh (0547408): all four failed, `assert 0 == 2` (the old code wrote the file and exited 0); with the refusal they pass, with the 601, A44, A47 and 8(d) cases. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 --- lanes-edit.sh | 40 ++++++++++++++++++---- tests/test_a23_write_handoff.py | 59 +++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 31 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 046a777..7c8fa89 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -497,7 +497,10 @@ # checkout of the register beside it (`-lanes//`, `-/`; # a lane's handoff is written in its own checkout only, and # `handoff-path` and `set-restart-intent --handoff` refuse the same -# handoff by the same check, 601), a relative cell or `--target` whose +# handoff by the same check, 601), a file under `handoffs/` of any other +# clone of the register (A63: lane openRepoTools-3's answer to question +# 4 of #18; an uncommitted write there wedged that lane's next write at +# S3 step 4), a relative cell or `--target` whose # `.` or `..` components climb out of the checkout (S2, 602; `handoff-path` # refuses that cell too), a handoff whose path holds a glob character # (607; `handoff-path` and `set-restart-intent --handoff` refuse it @@ -20971,15 +20974,19 @@ EOF # never the register or `lanes/`), and one inside a git directory — # another clone's `.git/`, a bare clone — and any path in W or in a # checkout beside it (`-lanes//`, `-/`): a lane's - # handoff is written in its own checkout only (601). A relative cell + # handoff is written in its own checkout only (601). So is a file + # under `handoffs/` of any other clone of the register, not beside W + # too (A63: lane openRepoTools-3's answer to question 4 of #18; an + # uncommitted write there wedged that lane's next write at S3 step 4). + # A relative cell # or `--target` whose `.` or `..` components climb out of the checkout # is refused first, for its spelling, wherever it lands (602, A55). # Where git cannot read a repository on the way up from the target, it # refuses (1, R22). # One in ANOTHER REPOSITORY (Amendment 8(d)) is written there, then # committed and pushed in its own top level (604), and S3's refusal of - # this checkout does not stop it; one in another clone of the - # register not beside W is written and never committed (A57). + # this checkout does not stop it; no clone of the register is one (A57, + # A63). # 3. S3 step 4 for the handoff's own path (`cs_own_pending`), and for a # handoff git does not track yet, which a commit would add and a lost # race's replay would drop. @@ -21193,8 +21200,12 @@ EOF fi whf_otop="$(git -C "$whf_gd" rev-parse --show-toplevel 2>/dev/null || :)" whf_otop="$( [ -n "$whf_otop" ] && cd -P -- "$whf_otop" 2>/dev/null && pwd -P || :)" + whf_oreg=0 if [ -n "$whf_otop" ] && { [ -e "$whf_otop/$LANES_PATH" ] || [ -L "$whf_otop/$LANES_PATH" ]; } && - path_under "$whf_phys" "$whf_otop" && ! whf_in_scope "${whf_phys#"$whf_otop"/}"; then + path_under "$whf_phys" "$whf_otop"; then + whf_oreg=1 + fi + if [ "$whf_oreg" = 1 ] && ! whf_in_scope "${whf_phys#"$whf_otop"/}"; then whf_stamp_said die "lane $lane's handoff target $whf_path is ${whf_phys#"$whf_otop"/} in $whf_otop, another checkout of the register (its top level carries $LANES_PATH too), which is not a file under handoffs/: write-handoff writes a lane's handoff and nothing else, never a register or anything under lanes/, in this checkout or in another clone of it (Amendment 23 S2). Nothing was written." 2 fi @@ -21208,6 +21219,18 @@ EOF whf_stamp_said die "lane $lane's handoff target $whf_path is $HBW_WHY. Nothing was written." 2 fi + # AND A FILE UNDER handoffs/ OF ANOTHER CLONE OF THE REGISTER THAT IS NOT + # BESIDE W IS REFUSED TOO (lane openRepoTools-3's answer to question 4 of + # #18, REFUSE; A63). It is that clone's lane's file, not Amendment 8(d)'s + # "not the register's": 604's commit and push stay out of it (A57), and + # written there, uncommitted, it left a change its lane's next write + # refused over at S3 step 4, 601's harm. Per-lane `LANES_REPO` clones + # exist outside W's siblings, and A44's test of the top level is there + # already. After 601's check, so a clone beside W keeps 601's words. + if [ "$whf_oreg" = 1 ]; then + whf_stamp_said + die "lane $lane's handoff target $whf_path is ${whf_phys#"$whf_otop"/} in $whf_otop, another clone of the register (its top level carries $LANES_PATH too), not this write's checkout $whf_c, so it is not Amendment 8(d)'s \"not the register's\": a lane's handoff is written in its own checkout only. A write there would leave a change that clone's lane refuses over at its next write (Amendment 23 S3 step 4), and a commit and push there would publish to the register outside S3 and S4. Nothing was written." 2 + fi # A READ THAT FAILED IS NEVER "NO REPOSITORY" (601's second half, R22). # Where git gave no top level, the target is in no work tree only if no # `.git` is on the way up from it; one that is there is a repository git @@ -21359,8 +21382,11 @@ EOF # too (A44's test) is another lane's clone, not 8(d)'s "not the # register's", and a commit and push there would publish to the # register's origin outside S3 and S4, from a clone this lane does not - # hold; beside W, 601 refused it above, and elsewhere it is written and - # NOT committed, as before 604 (0, said; its lane commits it). A step + # hold. Beside W, 601 refused it above, and elsewhere so did A63's + # (lane openRepoTools-3's answer to question 4 of #18), before a byte, so + # the branch below is a BACKSTOP for a top level that came to carry the + # register after those checks: the handoff is then written and NOT + # committed (0, said; its lane commits it). A step # that fails there leaves the handoff WRITTEN, says what is left with the # command that finishes it, and is 6 (`git add` or `commit` failed, or the # commit is kept on HEAD and named); a file in no git work tree is written diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 48897a8..90ecd95 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -650,35 +650,46 @@ def test_an_8d_handoff_whose_origin_moved_is_fast_forwarded_first_then_committed _released(box, c) -def test_a_handoff_in_another_clone_of_the_register_elsewhere_is_written_never_committed(box: Box): - """A57, because of 604: the 8(d) branch now commits and pushes in the - handoff's own top level, but never in one that carries the register - (A44's test). A file under `handoffs/` of another clone of the register - that is NOT beside W (601 refuses one beside W) is that clone's lane's, - not Amendment 8(d)'s "not the register's", and a commit and push there - would publish to the register's origin outside S3 and S4, from a clone - this lane does not hold. So it is written there and NOT committed, as - before 604 (0), and said: that clone's HEAD, this checkout and the - register's origin are as they were. Whether it should be refused - instead is put to lane openRepoTools-3 (the body's Q4).""" - c = box.seam() - d = box.clone(box.root / "elsewhere-clone") +@pytest.mark.parametrize("how", ["stamp-target", "stamp-cell", "prepend-target", "whole-target"]) +def test_a_handoff_in_another_clone_of_the_register_elsewhere_is_refused(box: Box, how: str): + """Question 4 of #18, answered by lane openRepoTools-3 (REFUSE, 2; A63). + A file under `handoffs/` of another clone of the register that is NOT + beside W (601 refuses one beside W) is that clone's lane's, not Amendment + 8(d)'s "not the register's". A57 kept 604's commit and push out of it and + wrote it there, uncommitted, exit 0: the change then wedged that lane's + next write at S3 step 4, which is 601's harm. A44's test of the top level + is there already, so the file is refused (2) before a byte, by `--target` + or by an absolute cell, in each of the three modes: that clone, this + checkout and the register's origin are as they were, the stamp line is + said, nothing is on stdout.""" + d = box.root / "elsewhere-clone" + if how == "stamp-cell": + box.cell_is(str(d / HANDOFF_REL)) + c = box.seam() + box.clone(d) f = d / HANDOFF_REL - text = f.read_text(encoding="utf-8") + if how == "prepend-target": + args = ("--prepend", str(_block(box)), "--target", str(f)) + elif how == "whole-target": + args = ("--whole", str(_prepared(box)), "--expect-digest", sha(f), "--target", str(f)) + else: + args = ("--stamp", STAMP) + (("--target", str(f)) if how == "stamp-target" else ()) + text, theirs, mine = f.read_bytes(), box.snapshot(d), box.snapshot(c) d_head = box.git("-C", str(d), "rev-parse", "HEAD").strip() - mine, tip = box.snapshot(c), box.origin_head() - result = box.write_handoff("--stamp", STAMP, "--subject", SUBJECT, "--target", str(f)) - assert result.returncode == 0, result.stderr - assert (f"{f} is in {d}, another clone of the register (its top level carries {REGISTER} too), " - f"not this write's checkout {c}, so it is not Amendment 8(d)'s: it is written there and " - "NOT committed") in result.stderr + result = box.write_handoff(*args, "--subject", SUBJECT) + assert result.returncode == 2, result.stderr + assert (f"is {HANDOFF_REL} in {d}, another clone of the register (its top level carries {REGISTER} too), " + f"not this write's checkout {c}, so it is not Amendment 8(d)'s") in result.stderr + assert "Nothing was written." in result.stderr assert "committed there" not in result.stderr - assert f.read_text(encoding="utf-8") == stamped(text) + if how.startswith("stamp"): + assert f"the stamp line it would have written under the handoff's header block: {STAMP}" in result.stderr + assert f.read_bytes() == text assert box.git("-C", str(d), "rev-parse", "HEAD").strip() == d_head - assert box.git("-C", str(d), "status", "--porcelain").split() == ["M", HANDOFF_REL] - assert box.origin_head() == tip + assert box.git("-C", str(d), "status", "--porcelain") == "", "nothing pending in that clone for its S3" + assert box.snapshot(d) == theirs assert box.snapshot(c) == mine - assert result.stdout == _line(f) + assert result.stdout == "" _released(box, c) From 2ff2c3489e582687395d76bb8e33b1148e63864f Mon Sep 17 00:00:00 2001 From: Brett Heap Date: Sun, 8 Nov 2026 19:44:45 +0000 Subject: [PATCH 34/34] A64: the NITs of lane openRepoTools-3's review of opensoft/openRepoTools-code#18 at 2b1a232: the 8(d) clause of exit row 6 says its commit is in the handoff's own repository, the comment on 605 says A59, and two composite asserts are split (opensoft/openRepoTools#198, slice 2a, NITs 5, 6 and 7) - NIT 5: the header's exit row 6 listed 604's Amendment 8(d) cause among the kept-on-HEAD causes, and the row ends "Every later write from that checkout refuses under S3 until the lane publishes or archives it". That is false for 8(d): the commit is in the handoff's own repository, and no write in the lane checkout refuses over it. The clause now ends "(that commit is in that repository; no write here refuses over it)", the words opensoft/openRepoTools-spec#16's exit row 6 takes (its NIT 5 commit), on one line so the two can be found together. No test compares the code's header with the manual's row 6: the hygiene test that does (the exit-3 row) reads row 3, and a read of every test that opens the manual found none that quotes row 6 or 8(d). - NIT 6: the comment on the fenced `--prepend` said "605; A58". The change is A59 (decision 27); A58 is 732. - NIT 7: SonarCloud's S9073, composite asserts, at the test file's A46 and A52 cases: `assert _origin_has(box, MOVED_REL) and not _origin_has(box, HANDOFF_REL)` and `assert HANDOFF_REL not in names and odd in names` are each two asserts with a message. An AST scan of both test files for an `assert` whose test is an `and` or `or` found those two in tests/test_a23_write_handoff.py before, and none after. tests/test_handoff_skill_commit_block.py:911 (984) is slice 3's: it is not in this slice's file at all (483 lines here, 1207 at slice 3's f2340ad, where it is line 901, `assert block_file and block_file != "", first.stdout`, in `test_block_1_block_2_and_step_4_each_in_its_own_shell_ carry_what_step_2_did`), so this slice does not touch that file and slice 3's next merge of this head has nothing to conflict on there. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Sonnet 5.5 --- lanes-edit.sh | 15 ++++++++------- tests/test_a23_write_handoff.py | 6 ++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lanes-edit.sh b/lanes-edit.sh index 7c8fa89..68b7485 100755 --- a/lanes-edit.sh +++ b/lanes-edit.sh @@ -591,12 +591,13 @@ # nothing to write into (one moved with its row's cell, to a file under # `handoffs/` that origin has, is replayed into the moved file), or an # Amendment 8(d) handoff `write-handoff` wrote whose commit or push in its -# own repository failed (604; the command that finishes it is said). Or another -# writer in the checkout REWROTE this write's commit (a `pull --rebase` -# with no lock): it is not on HEAD at all, and what reached origin is -# read by content. Or, where the drop's tree step was refused and -# another writer committed on the branch in between, the commit is -# named OFF HEAD, with `reflog -5`. Every later write from that +# own repository failed (604; the command that finishes it is said) +# (that commit is in that repository; no write here refuses over it). Or +# another writer in the checkout REWROTE this write's commit (a +# `pull --rebase` with no lock): it is not on HEAD at all, and what +# reached origin is read by content. Or, where the drop's tree step was +# refused and another writer committed on the branch in between, the +# commit is named OFF HEAD, with `reflog -5`. Every later write from that # checkout refuses under S3 until the lane publishes or archives it # 7 ANOTHER ACT GOT THERE FIRST, and this one wrote nothing. `claim`: # CLAIM-LOST, another lane's claim landed on main first. @@ -21085,7 +21086,7 @@ EOF case "$whf_rexp" in *[!0-9a-fA-F]*) die "--expected-digest takes a SHA-256, or none" 64 ;; esac fi # A FENCED `--prepend` TAKES `--expected-digest none` ONLY (lane - # openRepoTools-3's review of #312, 605; A58). Fenced on a digest, a push + # openRepoTools-3's review of #312, 605; A59). Fenced on a digest, a push # that lost its race to a peer's change of the handoff was replayed onto a # pre-image that was no longer the bound one, the re-run refused, and the # block was left in the dropped commit, in the reflog only. `lane-handoff` diff --git a/tests/test_a23_write_handoff.py b/tests/test_a23_write_handoff.py index 90ecd95..826a2e8 100644 --- a/tests/test_a23_write_handoff.py +++ b/tests/test_a23_write_handoff.py @@ -2045,7 +2045,8 @@ def test_a_moved_handoff_the_replay_cannot_follow_is_kept_on_HEAD_exit_6(box: Bo assert f"kept on HEAD: {kept}" in err assert "push refused (attempt" not in err, "nothing is dropped or replayed" assert f.read_text(encoding="utf-8") == stamped(HANDOFF) - assert _origin_has(box, MOVED_REL) and not _origin_has(box, HANDOFF_REL) + assert _origin_has(box, MOVED_REL), "origin has the moved file" + assert not _origin_has(box, HANDOFF_REL), "origin no longer has the old name" assert [line.split()[1] for line in box.unpublished(c)] == [kept] assert result.stdout == _line(f) _released(box, c) @@ -2777,7 +2778,8 @@ def test_a_cell_a_peer_pads_otherwise_is_the_same_cell_and_the_write_is_kept_on_ assert "push refused (attempt" not in err, "nothing is dropped or replayed" assert f.read_text(encoding="utf-8") == stamped(HANDOFF), "the write is in this checkout" names = box.git("--git-dir", str(box.origin), "ls-tree", "-r", "--name-only", "main").splitlines() - assert HANDOFF_REL not in names and odd in names + assert HANDOFF_REL not in names, "origin no longer has the old name" + assert odd in names, "origin has the blank-ended name" assert not (c / odd).exists(), "nothing was written into the blank-ended name" assert [line.split()[1] for line in box.unpublished(c)] == [kept], "one commit unpublished, this write's" assert result.stdout == _line(f)