Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
068f190
lane-start writes the Rule 3 stamp with one `write-handoff --stamp` t…
Oct 22, 2026
ce73c92
lane-start reads each restart-intent field to the end of the answer, …
Oct 22, 2026
b848bd8
lane-handoff writes the PAUSED top block with one `write-handoff --pr…
Oct 23, 2026
3ce57f6
The supervised-restart cases run /ctx through the lane's own checkout…
Oct 23, 2026
dbfbec6
lane-start launches with no first prompt where the lane's own copy of…
Oct 23, 2026
dc50eb5
lane-start and lane-handoff say, under LANES_NO_GIT=1, that the hando…
Oct 23, 2026
31990c4
lane-start says, where the lane's checkout refused a plain launch's s…
Oct 24, 2026
e97ad3f
lane-start says, under a supervised restart, to run a refused stamp's…
Nov 8, 2026
67e6da5
lane-handoff's --restart respawn carries the caller's LANES_WORKSPACE…
Nov 8, 2026
2bd3ea2
lane-start's duplicate-row refusal, under the opt-in, calls a duplica…
Nov 8, 2026
fb4a7b0
The t018 modules' docstrings name slice 2b's one exception to "byte f…
Nov 8, 2026
c1f2928
lane-handoff's register PAUSED line says what the handoff write did, …
Nov 8, 2026
2e4d7dd
lane-start's first prompt reads the file its stamp wrote, so a handof…
Nov 8, 2026
439699f
lane-handoff's register PAUSED line takes slice 3's record phrases wo…
Nov 8, 2026
e5c3326
The slice's tests pin fourteen of the mutant battery's survivors: the…
Nov 8, 2026
e12b369
Merge slice 2a's code-leg head 1320847 (old 7bc572c, A60) into slice …
Nov 8, 2026
58309ba
A row cell naming a sibling lane's handoff, in that lane's clone of t…
Nov 8, 2026
615cfdd
lane-start asks whether the restart's bound handoff changed before it…
Nov 8, 2026
5bf9bbb
An Amendment 8(d) handoff is stamped and takes the PAUSED block commi…
Nov 8, 2026
c12f078
The slice's restart cases pin 5c's lost-ownership die and a /ctx roun…
Nov 8, 2026
a44b9d2
Merge slice 2a' at 2b1a232 (2a's 1320847 with slice 1b at 34799b7, wh…
Nov 8, 2026
4031100
Merge slice 2a' at 56a9b87 (2b1a232 and A61, which makes 2a's other-o…
Nov 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
239 changes: 149 additions & 90 deletions lane-handoff

Large diffs are not rendered by default.

706 changes: 360 additions & 346 deletions lane-start

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions tests/fixtures/t018-handoff/single-repository-decoys.golden
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ lanes-edit: LANES_NO_FETCH=1 — not fetching; reading origin/main as the ref al
lane-handoff: lane=repoD-1 workstation=Eagle
lane-handoff: lifecycle: RUNNING -> SWAPPING (generation 2, operation <OPERATION>)
lane-handoff: writers polled: 1 under <HOME>/projects/repoD
lanes-edit: committed: handoff(repoD-1@Eagle): PAUSED, t018 handoff
lanes-edit: pushed origin/main (attempt 1)
lane-handoff: handoff refreshed: <HOME>/projects/wip/handoffs/repoD-1.md (+14 lines at the head)
lane-handoff: handoff commit: handoff(repoD-1@Eagle): PAUSED
lane-handoff: handoff pushed to origin/main
lane-handoff: handoff written through `write-handoff`, which exited 0: its words above say what it committed and pushed, or that nothing was committed
lanes-edit: 1 line appended to <HOME>/projects/wip/lanes/log/repoD-1.md
lanes-edit: committed: LOG(repoD-1@Eagle): PAUSED lane:repoD-1
lanes-edit: pushed origin/main (attempt 1)
Expand Down Expand Up @@ -105,9 +106,10 @@ lanes-edit: LANES_NO_FETCH=1 — not fetching; reading origin/main as the ref al
lane-handoff: lane=repoQ-1 workstation=Eagle
lane-handoff: lifecycle: RUNNING -> SWAPPING (generation 2, operation <OPERATION>)
lane-handoff: writers polled: 1 under <HOME>/projects/repoQ
lanes-edit: committed: handoff(repoQ-1@Eagle): PAUSED, t018 handoff
lanes-edit: pushed origin/main (attempt 1)
lane-handoff: handoff refreshed: <HOME>/projects/wip/handoffs/repoQ-1.md (+14 lines at the head)
lane-handoff: handoff commit: handoff(repoQ-1@Eagle): PAUSED
lane-handoff: handoff pushed to origin/main
lane-handoff: handoff written through `write-handoff`, which exited 0: its words above say what it committed and pushed, or that nothing was committed
lanes-edit: 1 line appended to <HOME>/projects/wip/lanes/log/repoQ-1.md
lanes-edit: committed: LOG(repoQ-1@Eagle): PAUSED lane:repoQ-1
lanes-edit: pushed origin/main (attempt 1)
Expand Down
5 changes: 3 additions & 2 deletions tests/fixtures/t018/single-repository.golden
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,10 @@ lanes-edit: LANES_NO_FETCH=1 — not fetching; reading origin/main as the ref al
lane-handoff: lane=repoS-1 workstation=Eagle
lane-handoff: lifecycle: RUNNING -> SWAPPING (generation 2, operation <OPERATION>)
lane-handoff: writers polled: 2 under <HOME>/projects/repoS
lanes-edit: committed: handoff(repoS-1@Eagle): PAUSED, t018 handoff
lanes-edit: pushed origin/main (attempt 1)
lane-handoff: handoff refreshed: <HOME>/projects/wip/handoffs/repoS-1.md (+15 lines at the head)
lane-handoff: handoff commit: handoff(repoS-1@Eagle): PAUSED
lane-handoff: handoff pushed to origin/main
lane-handoff: handoff written through `write-handoff`, which exited 0: its words above say what it committed and pushed, or that nothing was committed
lanes-edit: 1 line appended to <HOME>/projects/wip/lanes/log/repoS-1.md
lanes-edit: committed: LOG(repoS-1@Eagle): PAUSED lane:repoS-1
lanes-edit: pushed origin/main (attempt 1)
Expand Down
338 changes: 338 additions & 0 deletions tests/test_a23_lane_handoff.py

Large diffs are not rendered by default.

783 changes: 783 additions & 0 deletions tests/test_a23_lane_start.py

Large diffs are not rendered by default.

45 changes: 38 additions & 7 deletions tests/test_a23_lane_start_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
review of opensoft/openRepoTools#282, MUST 2); one case leaves it out. The
sandbox is `test_lane_start_row_stamp`'s, with its explicit seam taken back
out.

Since slice 2b `lane-start` reads the row from W again, but only after
`refresh-workspace` has fast-forwarded it, and makes the lane's checkout at
"5. the row" (`tests/test_a23_lane_start.py`). So W's working tree now moves,
by fast-forward and nothing else, and a checkout that cannot be made no longer
changes which register the start reads. `lane-end` keeps slice 1a's read.
"""

from __future__ import annotations
Expand Down Expand Up @@ -46,29 +52,53 @@ def rows(register: Path) -> list[str]:
if r.startswith(f"| `{LANE}`")]


def no_auto_gc(box: Sandbox, *where: str) -> None:
"""No automatic gc or maintenance in the repository `where` names
(`-C <path>` or `--git-dir <path>`), so a case's reflog and objects stay
what its own acts made."""
for key, value in (("gc.auto", "0"), ("maintenance.auto", "false")):
box.git(*where, "config", key, value)


def W_reflog(box: Sandbox) -> list[str]:
return box.git("-C", str(box.wip), "reflog", "--format=%gs", "HEAD").splitlines()


def assert_W_moved_only_by_fast_forward(box: Sandbox, before: list[str]) -> None:
"""W took no write: its HEAD moved only by `refresh-workspace`'s
fast-forward, and its working tree is clean (Amendment 23 S5)."""
after = W_reflog(box)
new = after[:len(after) - len(before)]
assert after[len(new):] == before, (before, after)
assert all(entry == "merge origin/main: Fast-forward" for entry in new), new
assert box.git("-C", str(box.wip), "status", "--porcelain") == "", "W is clean"


def started(tmp_path: Path, short_root: Path) -> tuple[Sandbox, Path]:
"""A NEW lane's first start: its row is added through its own checkout."""
box = Sandbox(tmp_path)
checkout = default_resolution(box)
lane_dir = _checkout_of_length(short_root, None)
box.checkout(lane_dir)
before = W_reflog(box)
first = box.start("--dir", str(lane_dir), window_name="claude")
assert first.returncode == 0, first.stderr
assert (checkout / ".git").is_dir(), "the first write made the lane's checkout"
assert len(rows(checkout / "lanes" / "LANES.md")) == 1
assert rows(box.wip / "lanes" / "LANES.md") == [], "W's working tree took no write"
assert_W_moved_only_by_fast_forward(box, before)
return box, checkout


def test_a_new_lanes_second_start_reads_its_row_in_its_own_checkout(tmp_path, short_root):
box, checkout = started(tmp_path, short_root)
before = W_reflog(box)
second = box.start(window_name=LANE)
assert second.returncode == 0, second.stderr
assert "ALREADY has a row" not in second.stderr, second.stderr
assert len(rows(checkout / "lanes" / "LANES.md")) == 1, "one row, and no second one added"
published = box.git("--git-dir", str(box.root / "origin.git"), "show", "main:lanes/LANES.md")
assert sum(1 for r in published.splitlines() if r.startswith(f"| `{LANE}`")) == 1
assert rows(box.wip / "lanes" / "LANES.md") == [], "and W's working tree still took none"
assert_W_moved_only_by_fast_forward(box, before)


def test_lane_end_finds_the_row_its_lane_start_wrote(tmp_path, short_root):
Expand All @@ -80,7 +110,7 @@ def test_lane_end_finds_the_row_its_lane_start_wrote(tmp_path, short_root):
capture_output=True, text=True, timeout=120, check=False)
assert "has no row for lane" not in end.stderr, end.stderr
assert end.returncode == 0, (end.stdout, end.stderr)
assert rows(box.wip / "lanes" / "LANES.md") == []
assert box.git("-C", str(box.wip), "status", "--porcelain") == "", "W took no write"


def test_a_dry_run_start_makes_no_checkout(tmp_path, short_root):
Expand Down Expand Up @@ -132,8 +162,9 @@ def test_without_the_opt_in_a_new_lane_starts_through_W(tmp_path, short_root):

def test_a_checkout_that_cannot_be_made_is_said_and_the_start_goes_on(tmp_path, short_root):
"""SHOULD 2: where the lane's checkout cannot be cloned, the start says so
and reads W, as it does for a dry run with none: a register act it can do
without never refuses a launch (Amendment 8(d))."""
and goes on: a register act it can do without never refuses a launch
(Amendment 8(d)). Since slice 2b the row is W's read either way, so only
the writes are touched."""
box = Sandbox(tmp_path)
checkout = default_resolution(box, route=False)
lane_dir = _checkout_of_length(short_root, None)
Expand All @@ -150,7 +181,7 @@ def test_a_checkout_that_cannot_be_made_is_said_and_the_start_goes_on(tmp_path,
box.env.update(LANES_ROUTE_WRITES="1", LANES_GIT=str(wrapper))
second = box.start(window_name=LANE)
assert "own register checkout could not be made" in second.stderr, second.stderr
assert "so this command reads W's register" in second.stderr
assert "ALREADY has a row" not in second.stderr, second.stderr
assert not checkout.exists()
assert second.returncode == 0, second.stderr

Expand Down Expand Up @@ -263,7 +294,7 @@ def test_a_held_creation_lock_is_said_and_the_start_goes_on(tmp_path, short_root
second = box.start(window_name=LANE, timeout=SLOW)
assert "own register checkout could not be made" in second.stderr, second.stderr
assert "exited 4" in second.stderr, second.stderr
assert "so this command reads W's register" in second.stderr
assert "ALREADY has a row" not in second.stderr, second.stderr
assert not checkout.exists()
assert second.returncode == 0, second.stderr

Expand Down
2 changes: 1 addition & 1 deletion tests/test_handoff_skill_80_85.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ def _lane_handoff_writes(box: Sandbox, why: str,
workstation and instant the skill is run with."""
script = ('why="$STUB_WHY"; handoff_kind="$STUB_KIND"\n' +
lane_handoff_why_text() +
f'lane={LANE}; uuid={UUID}; ws={WS}; UTC={NOW}\n'
f'lane={LANE}; uuid={UUID}; ws={WS}; UTC={NOW}; handoff_record="handoff refreshed"\n'
f'printf \'%s\\0%s\\0\' "$why_text" {lane_handoff_register_line()}\n')
proc = box.run("bash", script, _why_env(why, kind or "unknown"))
assert proc.returncode == 0, (proc.returncode, proc.stderr)
Expand Down
Loading
Loading