Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
fdf34f6
Amendment 23 S3: every writer fetches, checks and fast-forwards its c…
Oct 20, 2026
8652321
Amendment 23 S4: every write commits by pathspec, pushes with no pull…
Oct 20, 2026
87e94da
Amendment 23 S3 for the sweeps and `rename-lane`: each takes S3 under…
Oct 20, 2026
c14bd3b
Merge slice 1a's fix round 2 (eead86d) into slice 1b: git found no co…
Oct 20, 2026
7e07e62
R4-1: under the opt-in, `log`, `commit`, `request-handoff` and `appen…
Oct 20, 2026
2ed9e62
B23: a confirmed push still says `pushed origin/<branch> (attempt N)`…
Oct 20, 2026
2e18850
Merge slice 1a's fix round 3 (15c8e3f, R4-2) into slice 1b: git found…
Oct 21, 2026
0562d07
B31: supervised_restart's fixture no longer exports `LANES_NO_FETCH=1…
Oct 21, 2026
9714694
MUST 3 (rv1b's pre-review): `lane-worktrees sweep --yes` writes its N…
Oct 21, 2026
db98ee6
MUST 4 (rv1b's pre-review): a refused push whose commit is no longer …
Oct 21, 2026
caeca9c
MUSTs 1 and 2 (rv1b's pre-review): `claim` and `add-row` run again fr…
Oct 21, 2026
d878c40
SHOULD 1 and NIT 1 (rv1b's pre-review): the manual no longer describe…
Oct 21, 2026
6197298
rv1b's second report: one tracked and one untracked log is named as t…
Oct 21, 2026
af4d0e1
MUST-N and SHOULD B (rv1b-c's re-check at 10169f3): `claim` and `add-…
Oct 21, 2026
c4393e8
SHOULD A and NITs 1-2 (rv1b-c's re-check at 10169f3): a commit anothe…
Oct 21, 2026
59c238d
SHOULD C (rv1b-c2's re-check at d9ff4e6): the replay's drop is a comp…
Oct 21, 2026
e594547
Amendment 23 S3-S4 in the shell suite: every case that pinned the pul…
Oct 21, 2026
4b80898
NIT 3 (rv1b-c2's re-check at d9ff4e6, re-aimed by SHOULD C): the carr…
Oct 21, 2026
bf77bf2
NIT 4 (rv1b-c2's re-check at d9ff4e6): migrate-state-cells' skip line…
Oct 21, 2026
8089633
A37, slice 2a's, built in 1b: `CP_KEEP_IF_GONE` names the one file a …
Oct 21, 2026
37b0f51
B04 (m1b-c2's mutant run): the replay's HEAD check has a case that on…
Oct 21, 2026
0f26c5e
The refusal with origin unmoved reads HEAD before it says "kept on HE…
Oct 22, 2026
c14738a
migrate-state-cells' line for two logs whose names differ only by cas…
Oct 22, 2026
b324e00
The carry form's tree step refused has a case: the branch is put back…
Oct 22, 2026
0edcfb4
The last two places that said "kept on HEAD" without reading HEAD rea…
Oct 22, 2026
e3e8124
A drop's compare-and-swap refused for a reason that is not a moved br…
Oct 22, 2026
99ea7d3
`migrate-state-cells` calls a case-only rename "committed here" only …
Oct 22, 2026
40fa53c
A file a write itself creates has a case in the A37 check: the `^` of…
Oct 22, 2026
02f2d08
The manual says what `add-row`'s refusal of a row a peer pushed says:…
Oct 22, 2026
82977c1
S3 fast-forwards to the sha its own fetch read, not to `refs/remotes/…
Oct 22, 2026
5c5e9d3
A write knows its own commit by what it is, not by reading HEAD after…
Oct 22, 2026
0308f0c
`commit`'s carry retries a hand edit with `git apply -C0` only where …
Oct 22, 2026
4bcc6a8
The read-back that stops a doubled log line has a case: a push git ca…
Oct 22, 2026
38fff00
The managed seam is asked again right after S3, at the five legacy si…
Oct 22, 2026
b2638a7
Under the opt-in, `claim`, `release` and the sweeps' runner lane clon…
Oct 22, 2026
1d59ce5
A claim that runs again from the top with the lock held makes no netw…
Oct 22, 2026
5be93b3
The manual names the re-run decisions 19 and 22 made: where S3's fetc…
Oct 22, 2026
bd9d6ab
S3's diverged refusal tells a checkout holding a hand merge to archiv…
Oct 22, 2026
d6064de
The NOT TRACKED recoveries of the migration, `retire-rows` and `archi…
Oct 22, 2026
21ad618
The publish act's refusal over a stack origin has moved past fills th…
Oct 22, 2026
a01ea3a
Exit row 3, in both tables and in the manual's step 5, says the uncon…
Oct 22, 2026
b98d3a7
`commit`'s carry tries `git apply -C0` only where every line the hand…
Oct 22, 2026
5fb952d
Under the opt-in, the NOT TRACKED recovery a dry run reads in W gives…
Oct 22, 2026
2e6f8f1
A claim's re-run is said to make no GitHub read, not no network read:…
Oct 22, 2026
941163c
S3's list and reset recoveries and the publish act's list line name `…
Oct 22, 2026
ca94468
Merge slice 1a's SC2093 disable (6ca3158) into slice 1b: git found no…
Oct 22, 2026
0fb5416
A hand edit that is also staged is carried against HEAD's version, ne…
Oct 22, 2026
60d81d6
The comment over `cs_removed_unique` says `.git/lanes-edit/` holds fi…
Oct 22, 2026
479d413
A claim's re-run carries attempt 1's reads capped at 32,768 bytes, so…
Oct 22, 2026
89e98f2
Once the read-back finds a carried hand edit on origin, each copy of …
Oct 22, 2026
27fede5
`cp_under` names the commits under this write's as ones origin lacks …
Oct 22, 2026
4c6da04
The refusal with origin unmoved lists the commits under this write's …
Oct 22, 2026
c248df2
A writer whose checkout, or the directory its lock lives in, is remov…
Oct 22, 2026
ee16b26
The two routing `exec` lines slice 1b added, `cs_recheck`'s re-run an…
Oct 22, 2026
d8859c8
A write whose `LANES_REPO` names a directory below its checkout's top…
Oct 23, 2026
70edb34
A sidecar act whose `<W>-lanes/` folder is moved aside while it waits…
Oct 23, 2026
88a3692
S6.3's archive, as `cp_under` and the publish act print it, names the…
Oct 23, 2026
8a88f50
A claim's re-run posts Rule 1's reads whole, from a file attempt 1 ke…
Oct 23, 2026
7fe295d
The recovery commands S4's refusals print name origin's tracking ref …
Oct 23, 2026
97fb75c
Where origin moved, the bound's, the keep-if-gone and `cp_not_head`'s…
Oct 23, 2026
9fcbf30
After a lost race, a `commit` whose re-run carry no longer applies na…
Oct 23, 2026
07bf43c
The two-copies test asserts one removal note per copy the run recorde…
Oct 24, 2026
195fca9
Merge slice 1a's merge of main (903d751, which brings openRepoTools-c…
Nov 8, 2026
34799b7
opensoft/openRepoTools#263's register-hardening cases race the writer…
Nov 8, 2026
8e42208
lanes-edit.sh: retire-rows' published-log refusal, which Amendment 23…
Nov 8, 2026
cc8f6ab
tests: the identical-row case asserts that the replay ran and the cap…
Nov 8, 2026
458cb38
Merge slice 1a's merge of openRepoTools-code main e393958 (f0d363a: c…
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
11 changes: 8 additions & 3 deletions lane-worktrees
Original file line number Diff line number Diff line change
Expand Up @@ -490,13 +490,18 @@ class Helper:
self._fetched = False

def call(self, *args: str, env_extra: dict | None = None, fetch: bool = False,
timeout: int = 300):
write: bool = False, timeout: int = 300):
"""`fetch` asks for one fetch per session; every later read is made
with `LANES_NO_FETCH=1`. A `write` is never given it: for a writer, a
fetch that did not run is a fetch that failed, and lanes-edit.sh's
Amendment 23 S3 refuses it (2, ratified choice 2), so the sweep's
NOTED line was refused after its first removal (#198, slice 1b)."""
if not self.path:
return subprocess.CompletedProcess(args, 127, "", "no lanes-edit.sh beside this "
"command or on PATH")
env = dict(os.environ)
env["LANES_INDEX"] = "off"
if not fetch or self._fetched:
if not write and (not fetch or self._fetched):
env["LANES_NO_FETCH"] = "1"
if env_extra:
env.update(env_extra)
Expand Down Expand Up @@ -669,7 +674,7 @@ class Helper:
if session:
extra["LANES_SESSION"] = session
return self.call("log", "NOTED", f"lane:{lane}", register_text(text),
env_extra=extra, fetch=False)
env_extra=extra, write=True)


LANE_LINE_RE = re.compile(r"^(?:STARTED|PAUSED|RESUMED|ENDED|RETIRED) — lane [^,]+, "
Expand Down
2,419 changes: 1,638 additions & 781 deletions lanes-edit.sh

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions tests/a23_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@
exit 90
"""

#: `LANES_GIT`: every NETWORK call the helper makes, logged one per line, then
#: an optional hook (`FAKE_NET_HOOK`, run as `<hook> <op> <git args…>`), then the
#: real git. The hook decides: exit 0 runs the real git; exit 97 means the hook
#: ran git itself and the call succeeded; any other exit is the call's own
#: (a failure). `<op>` is the git subcommand: fetch, push, ls-remote or clone.
NET_GIT = """#!/usr/bin/env bash
op=""
for a in "$@"; do case "$a" in fetch|push|pull|ls-remote|clone) op="$a"; break ;; esac; done
printf '%s %s\\n' "$op" "$*" >> "$FAKE_NET_LOG"
if [ -n "${FAKE_NET_HOOK:-}" ]; then
rc=0; "$FAKE_NET_HOOK" "$op" "$@" || rc=$?
case "$rc" in 0) : ;; 97) exit 0 ;; *) exit "$rc" ;; esac
fi
exec git "$@"
"""


def register_text(rows) -> str:
return HEADER + "".join(f"{r}\n" for r in rows)
Expand Down Expand Up @@ -150,6 +166,69 @@ def peer_pushes(self, rel: str, text: str) -> None:
self.commit(self.seed, f"a peer writes {rel}", rel)
self.git("-C", str(self.seed), "push", "-q", "origin", "main")

def peer_edits(self, rel: str, old: str, new: str, message: str = "") -> None:
"""The seed clone, brought up to date, replaces `old` with `new` in one
file and publishes it: another writer's commit landing first."""
self.git("-C", str(self.seed), "pull", "-q", "--ff-only", "origin", "main")
path = self.seed / rel
text = path.read_text(encoding="utf-8")
assert text.count(old) == 1, (rel, old)
path.write_text(text.replace(old, new), encoding="utf-8")
self.commit(self.seed, message or f"a peer edits {rel}", rel)
self.git("-C", str(self.seed), "push", "-q", "origin", "main")

# ------------------------------------------------------------- the seam
def seam(self, name: str = "c") -> Path:
"""A clone of origin named by the explicit `LANES_REPO` (S2.1, the
test seam): every write of this box then goes through it, in place."""
checkout = self.clone(self.root / name)
self.env["LANES_REPO"] = str(checkout)
return checkout

def use_net(self, hook: str = "") -> Path:
"""Route the helper's network calls through `NET_GIT`, logged to
`self.net_log`; `hook` is the body of `FAKE_NET_HOOK`, if any."""
shim = self.fakebin / "net-git"
shim.write_text(NET_GIT, encoding="utf-8")
shim.chmod(0o755)
self.net_log = self.root / "net.log"
self.net_log.write_text("", encoding="utf-8")
self.env["LANES_GIT"] = str(shim)
self.env["FAKE_NET_LOG"] = str(self.net_log)
if hook:
body = self.fakebin / "net-hook"
body.write_text("#!/usr/bin/env bash\n" + hook, encoding="utf-8")
body.chmod(0o755)
self.env["FAKE_NET_HOOK"] = str(body)
return shim

def net_ops(self) -> list:
"""The network operations the helper made, in order."""
return [line.split(" ", 1)[0] for line in
self.net_log.read_text(encoding="utf-8").splitlines()]

def snapshot(self, checkout: Path) -> tuple:
"""Everything a refusal must leave as it was: HEAD and the branch it
is on, the index, the working tree, and origin's tip."""
c = ["-C", str(checkout)]
return (self.git(*c, "rev-parse", "HEAD").strip(),
self.git(*c, "symbolic-ref", "-q", "HEAD", check=False).strip(),
self.git(*c, "status", "--porcelain", "--untracked-files=all"),
self.git(*c, "diff"),
self.git(*c, "diff", "--cached"),
self.origin_head())

@staticmethod
def lock_of(checkout: Path) -> Path:
return checkout / "lanes" / ".lanes-edit.lock"

def unpublished(self, checkout: Path) -> list:
"""`git cherry -v origin/main HEAD`'s `+` lines: what origin lacks."""
self.git("-C", str(checkout), "fetch", "-q", "origin")
return [line for line in self.git("-C", str(checkout), "cherry", "-v",
"refs/remotes/origin/main", "HEAD").splitlines()
if line.startswith("+")]

# ------------------------------------------------------------- the helper
def cmd(self, *args: str) -> list:
return ["bash", str(REPO / "lanes-edit.sh"), *args]
Expand Down
Loading
Loading