Repository navigation
The register's rewrites refuse a copy or write they cannot prove and put a cut-short register back, retire-rows refuses an unpushed log rename before it writes and calls a lane closed only from its published log, and add-row refuses an archive row it cannot read and takes back a row refused after its rebase (#120, #122) - #263
Merged
Merged
Conversation
…e-only rename, a closed lane's refusal, and add-row's archive scan New tests/test_register_hardening_120_122.py, in the shape of test_register_scan_hardening_138.py: a throwaway register in a local bare repository, the real lanes-edit.sh against it. All 19 cases fail at 5e88a2c, each on the assertion it targets: - #120 item 1: a register copy or rewrite cut short (a `cat` shim cuts one copy at half its lines) leaves a half register, committed with exit 0, or behind an exit 5 that calls it UNCHANGED; - #120 item 2: a case-only log rename whose commit is not pushed passes retire-rows' scan, and ensure_log refuses it inside the write loop after the lane before it has had its log created; - #120 item 3: the refusal for a lane whose log already closes it names no exit, and the one for a parked lane does not name lane-end <lane>; - #122: an archive row whose lane cell is mangled is skipped, and an awk scan that failed reads as no match, so add-row reissues a retired position. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd put the register back where a write cut it short (#120 item 1) replace_line and delete_lines made every proof (line count, numstat, the no-op) against a `cat` of the register into $pre whose status was never read, and wrote the result with `cat -- <out> > LANES.md`, whose status was read but not its result, while `>` truncates before cat writes. Measured at 5e88a2c with a `cat` that cuts one copy at half its lines: - a short COPY: the half register passed every proof and was written back, committed and pushed, exit 0 (set-row-state and archive-rows alike); - a WRITE that failed part-way: exit 5 saying "its line N is UNCHANGED", with the register at half its bytes; - a WRITE that came out short and said nothing: exit 0, half a register committed. register_copy now compares the copy with the register, and register_rewrite compares what landed with what was built; where it differs the copy's bytes are put back and proved, and the refusal (5) says which of the two the register holds, or, where even that failed, that it MAY NOW BE TRUNCATED and how to restore it before the next write commits it as a pre-existing edit. The status check on the write itself landed earlier (4c2ffef, #81, for replace_line; c1bac0d, #93, for delete_lines); this adds the proof the issue asked for, and the copy it rests on. The header's exit-code table and the README's row 5 say so. Tests: five cuts against each of the two acts in tests/test_register_hardening_120_122.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…anded, in ensure_log's own words, before any log is written (#120 item 2) The sweep's published-path preflight asked ensure_log's question only where this checkout had NO log (`rr_ln2 = 0`), beside a clause comparing a basename with a repo-relative path that could never be equal. So the state a case-only rename leaves when its commit is not pushed (the row's spelling here, committed; the old spelling on origin; a checkout merely ahead) passed the scan, and ensure_log refused it inside the write loop, after the lanes before it had had their logs created and appended: measured at 5e88a2c, exit 2 with lanes/log/repoX-2.md left behind for a sweep of repoX-2 and repoX-3. The preflight is now ensure_log's test (the published path is not the canonical one, and this checkout's file is not the one it names) and carries ensure_log's sentence, so the two refusals read the same. The README's sweep section names it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…D and names archive-rows, and sends a running or parked one to lane-end (#120 item 3) The rule stays clause (c)'s, as the issue declined to change it: a log that carries a lane-kind line refuses the sweep. The sentence now follows the LAST lane-kind line. ENDED or RETIRED: the lane is already CLOSED and hidden (19(b)), its log is append-only, and `lanes-edit.sh archive-rows <repo>` takes rows whose cell says RETIRED out of the register (19(d)); `lane-end` is not named, because it is not the exit for a lane that has ended. STARTED, PAUSED or RESUMED: "End it as itself when its work is done: lane-end <lane>". At 5e88a2c both read the same sentence and named neither exit. The shell suite's pinned fragments ("RETIRED line", "ENDED line", "carries a PAUSED line") are kept. The README's sweep section says so. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and an archive scan that failed, instead of reissuing a retired position (#122) retired_identity_hits skipped every `|` line of the archive with no backticked token, took the first backtick anywhere in a line as its lane, and returned the status of the `sort -u` behind its awk. So a retired row whose lane cell a hand edit had mangled, or an awk that failed, read as "no archived row matched", and add-row reissued the position (measured at 5e88a2c: exit 0, the row pushed). Every `|` line is now the rule, the header (first cell `lane`, as archive_rows writes it), or a row whose first backticked token lies wholly inside its first cell. Any other line is named by its line number and add-row refuses with 1, the code it uses for an archive it could not read; retired-identity and the after-rebase rescan fail closed the same way. The header is found by its first cell and not by a ` | ` count (it carries six, like a row) nor by the rule below it (the live register has rows between its header and its rule). Dry-run against the live register's 75 `|` lines: its header, its rule and all 73 rows pass, none refused. The scan's own status is now read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 2 days and 6 hours by commenting |
Reviewer's GuideThe PR hardens register and archive safety checks by proving file copies and rewrites, refusing unsafe log-rename and closed-lane retirements before writes, and making malformed or failed archive scans fail closed, with corresponding documentation and focused regression tests. Sequence diagram for fail-safe register rewritesequenceDiagram
participant Caller
participant replace_line_or_delete_lines
participant register_copy
participant register_rewrite
participant LANES_FILE
Caller->>replace_line_or_delete_lines: replace_line() / delete_lines()
replace_line_or_delete_lines->>register_copy: register_copy(pre, refusal)
register_copy->>LANES_FILE: cat copy
register_copy->>LANES_FILE: cmp copy
alt copy cannot be proved
register_copy-->>Caller: refuse exit 5
else copy proved
replace_line_or_delete_lines->>register_rewrite: register_rewrite(pre, out, refusal)
register_rewrite->>LANES_FILE: cat out
register_rewrite->>LANES_FILE: cmp landed with out
alt rewrite differs or is truncated
register_rewrite->>LANES_FILE: cat pre
register_rewrite->>LANES_FILE: cmp restored with pre
register_rewrite-->>Caller: refuse exit 5 with register state
else rewrite proved
register_rewrite-->>Caller: success
end
end
Flow diagram for archive validation before add-rowflowchart TD
A[add-row] --> B[retired_identity_hits]
B --> C[archive_text]
B --> D[archive_local_text]
C --> E[RETIRED_ROWS_AWK]
D --> E
E --> F{Every pipe line is readable?}
F -- No --> G[Refuse exit 1 and name invalid lines]
F -- Yes --> H{Lane is archived?}
H -- Yes --> I[Refuse: retired identity cannot be reused]
H -- No --> J[Continue add-row]
E --> K{Archive scan succeeded?}
K -- No --> G
Flow diagram for retire-rows preflight safetyflowchart TD
A[retire-rows] --> B[Scan each lane]
B --> C{Last log lane-kind line}
C -- ENDED or RETIRED --> D[Refuse: lane already CLOSED]
C -- STARTED, PAUSED, or RESUMED --> E[Refuse and direct to lane-end]
C -- No lane-kind line --> F[Check published log identity]
F --> G{Unpushed case-only rename?}
G -- Yes --> H[Refuse and request pull --rebase]
G -- No --> I[Continue preflight]
I --> J[Write logs and register]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…ter harness rather than a copy of it SonarCloud's quality gate on #263 failed on duplication alone: 72 of the 519 new lines (13.9%, gate 3%), every one of them in the Register class this file copied from its neighbour. It now subclasses that class, as test_lane_start_hardening_138 imports its Sandbox, and adds only what these cases need: thirty rows of another repository below repoX's, the published read, and the cat and awk seams. No case changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…k, register refusals name what is already on disk, and a lane is called CLOSED only from a published closing line The independent Opus review of #263 returned FIX: one MUST, two SHOULDs and seven NITs. M1. add_row_retirement_rescan only took the committed row back on a retired match. When the rescan after `pull --rebase` refused for any other reason (an archive it could not read, or a peer's archive line it could not take apart), it returned 1 and left the add-row commit at HEAD, and the next unrelated write from this checkout published the refused row. Every refusal there now runs the same compensation: the exact appended line is removed in a local commit. The new test is in repo19race's shape: a peer publishes a mangled archive row during add-row's pull. add-row exits 1, the row is gone from the local HEAD and from origin, and a later set-row-state publishes nothing of it. S2. register_copy's refusal said "Nothing was written" after retire-rows had appended a log and archive-rows had created the archive. It now says nothing has been committed, and lists what the act already wrote in this checkout: the sweeps name their writes with reg_wrote. retire-rows calls a lane CLOSED only when origin's log ends on the closing line. A closing line that only this checkout's log carries, such as an earlier sweep's that stopped part-way, is refused as exactly that. S1. One unparseable archive line makes `retired-identity`, and so every `lane-start`, refuse, and lane-start (lane 3's file, unchanged here) hides the reason. The README says so, and how to see the line. NITs: - 1: the README says CRLF archives and space-less tables are refused. - 3: add-row's own refusal is asserted. "As it was" has its own message, apart from the put-back and the truncation, with an unwritable-register case for both acts. archive-rows is matched exactly. - 4: each bad line names the copy it is in, published or this checkout's. - 5: an ENDED lane is not told it "is not retired a second time", and archive-rows is not named as its exit. - 6: where the register MAY NOW BE TRUNCATED, the exact copy from before the edit is moved out of the trap's reach and its path named. Lock lines are untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main moved past this branch's base 5e88a2c: #259, #260, #261 and #246. #246 touches lanes-edit.sh and docs/README-lanes.md too. The merge is clean, the lock lines are main's own, and lanes-edit.sh parses under bash 3.2.57 and 5.2. CI tests the moved main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ut adds, refusals say this act committed nothing, and the kept register copy lives in the git directory The delta review of #263 at 42e73e5 returned FIX: one MUST and three NITs. D1. add_row_retirement_rescan took the rejected row back by its exact text. Suppose a peer publishes the byte-identical row during add-row's `pull --rebase`. The rebase drops add-row's commit as already upstream, and the copy left is the peer's. The take-back deleted it, and the next write published the deletion, so the peer's row was gone from origin. The take-back now removes only the copy that this checkout's register adds over origin's. It reads that copy from their -U0 diff, by content, so the check holds under the sha the rebase gave the commit. - None added: nothing is taken back, and the refusal code is returned. - One added: exactly that line, and it is checked first. - More than one, or an unreadable diff: nothing is taken back, and the refusal says so. "origin already has the row" would be wrong the other way. A commit that survived beside a peer's identical row at another place would be kept, and the next write would publish a second copy. The retired-match branch shares the path. NITs: - A refusal now says "this act has committed nothing". handle_preexisting may already have captured a peer's edit as a commit of its own. Where add-row's take-back cannot rewrite the register, the refusal instead says that add-row HAS committed its row, and how to remove it. - "an ENDED line", not "a ENDED line". - The kept copy of the register goes to the checkout's own git directory, as lanes-edit-register-before-edit.<UTC>.<pid>. It is written with mode 0600 under noclobber and proved with cmp. Only where that cannot be done is it renamed beside $TMPD, and the refusal then says a restart would lose it. Lock lines are untouched; lane-start and lane-handoff are untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the take-back's message Both D1 cases now run the next unrelated write and count the published repoX-4 rows before they read add-row's stderr. A run against 42e73e5 is then red on what the review measured, the peer's row gone from origin or add-row's commit published as a second copy, rather than on a sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main moved from 55aae16 to 00d52ae with #252 (#113, #114, #115): lifecycle reads that fail are no longer answers, set-lane-tree's fence closes with its operation, and a lane sidecar is valid only when whole. The merge is textually clean. #252 changes the lifecycle sidecars, lane_reconcile, lane_tree_now, set-lane-tree and lane-state; this branch changes the register's rewrites, retire-rows, the archive scan and add-row's after-rebase take-back. Neither reads or writes the other's records, and no refusal code or README row both touch now contradicts the other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r context the diff carries, and its refusals speak add-row's own words Delta review 3 of #263 (d0256ff) returned FIX: one SHOULD and one NIT. S3. The take-back read its row's line from `git diff -U0` and counted only `+` lines. -U0 does not hold on its own. GIT_DIFF_OPTS=-u3 in the environment overrides it, and a user's diff.interHunkContext fuses a nearby change's hunk with the row's, with the lines between as context. Either way the row was put at the wrong line, the line check refused, nothing was taken back, and the next ordinary write published the refused row: M1 again. Both halves of the fix are in: - that one diff runs with GIT_DIFF_OPTS unset (inside the command substitution's own subshell) and -c diff.interHunkContext=0, which outranks every config file and GIT_CONFIG_* in the environment; - the parse advances its new-file line on every hunk line except a removed one (`-`) and git's "\ No newline" note, so context that still arrives is counted where it stands. The tests cover four cases: - GIT_DIFF_OPTS=-u3; - diff.interHunkContext=3 beside an unpushed edit one row above the end; - context forced in by a shim; - another lane's unpushed row just above. Each asserts the published register after the next write, and how the diff was called. NIT. When the take-back's own rewrite failed, the refusal ended in delete_lines' sentences, which are archive-rows': "the rows are in" the archive, and "the archive may have been appended". delete_lines now takes its caller's sentences, with archive-rows' as the default. add-row's say its row is still committed locally and unpushed, and that it must be removed before any other write publishes it. Lock lines are untouched; lane-start and lane-handoff are untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…roves its removal whatever the register's size, and every refusal or failed commit after it says add-row has committed its row Delta review 4 of #263 (5b460b7) closed S3 and returned FIX: one SHOULD and two NITs. S4. Where git classes the register as binary, the take-back's diff is the one line "Binary files ... differ", with no hunk to read. add-row then said this checkout added no copy of the row, kept its commit, and the next write published the row: M1 again. The review measured eight routes: a binary or -diff attribute, published or local, a diff driver with binary=true, core.attributesFile, GIT_ATTR_SOURCE, attr.tree, a NUL byte in the first 8000 bytes, and a core.bigFileThreshold below the register's size (the live register is 2,309,050 bytes). The fix: - (a) the take-back's diff is read with --text; - (b, the review's N2) delete_lines' line-count and numstat refusals end as register_copy's does: the register as it was, what this act has committed, the caller's sentence and what it has already written; - (c) delete_lines' numstat proof runs with -c core.bigFileThreshold=2g. Under a smaller threshold numstat printed "-" for both counts, and --text does not change that, so the take-back refused. A command-line -c outranks the global config and GIT_CONFIG_* in the environment. An attribute on every path, or a NUL byte, still makes numstat read the copies as binary at any threshold; the take-back then refuses (exit 5) in add-row's words. N1. The refusal of the pass that removes the line said "The register is as it was: no row was removed, so that row is still in it and in that commit", naming no lane and no commit. It takes (b)'s sentence. N3. When the compensating git add or git commit failed, add-row said nothing of its own, and the next write's commit carried the removal under that write's caption. A note now says whether the removal is staged, that add-row's commit still holds the row, and the command that commits the removal. Tests: three binary cases (-diff in .git/info/attributes, a published .gitattributes with binary, core.bigFileThreshold=200), each asserting the published register after the next write; three more ways the take-back cannot remove its row (the pass refused, a line count off, numstat reading binary), each asserting add-row's sentence; and the failed stage and commit, each running the command the note gives. Lock and sidecar lines are untouched; lane-start and lane-handoff are untouched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main moved from 677acc0 to 90eed74: the #228 release series (the openrepotools package, the package job in tests.yml, the release workflow and its docs) and #271 (#255: the handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search is unreadable, never absent). The merge is textually clean. Of this branch's files, #271 changes lanes-edit.sh only in lane_reconcile's unreadable-leg branch and docs/README-lanes.md only in the triad section's leg, handoff and reconcile rows; this branch changes the register's rewrites, retire-rows, the archive scan and add-row's after-rebase take-back, and the README's write-failure, sweep and archive rows. Neither reads or writes the other's state, and no refusal code or README row both touch now contradicts the other. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
This was referenced Oct 20, 2026
This was referenced Oct 21, 2026
brettheap
pushed a commit
that referenced
this pull request
Oct 21, 2026
…rea, and add the lines for #263, #289, #294 and #296, the changes after a42946e (#228) `docs/release-notes/v1.1.0.md` takes the fixes of lane 3's review of d8a349d (#293 comments 6314185715 and 6314529807, the second of which replaces three of the corrected texts of the first) and the lines for the changes after a42946e. The review's fixes: - Line 45 (#255, #271) no longer says that `lane-reconcile` and `lane-handoff` read a dependency nested in a leg: only the sweep and `lane-end` do. It says that a leg directory this account cannot search is unreadable and never `absent`, that a nested dependency is unreadable to the sweep and `lane-end` too, and that the sweep still reads a declared leg beneath such a directory as `absent` (#272, open). It also says NOT POLLED where the tool does. - Line 46 (#280, #283): an `awk` exits, it does not return, and the sentence on a status of 0 or 8 is gone. - Line 61 (#214): the fence never checks whose a local commit is, only that it adds or changes a register or handoff file. - The `<other-changes>` item counts 44 of the 53, because the openRepoShape pin's line (#233) is in the list, and the other 9, not 43 and 10. The milestone item names #247 and lists the six entries of 2026-10-20. - #209 is not tests-only: its `-e` fixes `lanes`' stderr filter under macOS's BSD grep, so it moves from the test suite to the lane area, and its one line there carries its test changes too. It is cited once. The lines for the changes after a42946e, each in its area and worded from its squash commit and description: #263 (d9065df) and #289 (fb2cf36) under the register and `lanes-edit.sh`, #294 (acc89ea, which closes #292) under `lane`, `lane-start`, `lane-end` and the name guard, and #296, which pins every Ubuntu runner to ubuntu-24.04, under the test suite and its checks. #296 has not landed yet: lane 3 lands it right before #293. The `<other-changes>` item says the lines were added on 2026-10-21 and are not in its counts, and the placeholder stays for #293 itself. Not touched: lane 3's optional sentence on #271 and its NIT on the tag. Lane: openRepoTools-2 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 21, 2026
… 1.1.0 and list, by area and from the squash titles of aaa7bc8..a42946e, what else changed on main since v1.0.0 (#228) (#293) Part of #228. It brings `docs/release-notes/v1.1.0.md` up to date on main: first on `a42946e`, as lane 3 asked on Brett Heap's word (#228 comment 6303608345, the section for lane openRepoTools-2, task 1), then with lane 3's review of `d8a349d` (comments 6314185715 and 6314529807 on this pull request), with the lines for the three changes that landed after `a42946e`, and with the line for #296, which landed right before this pull request. It is one file, changed in two commits (`d8a349d` and `bebb77d`), 59 insertions and 4 deletions against main. Lands last before rehearsal D's freeze; lane 3 reviews and merges it (#228 6303608345) **What changed** - **Lane 3's review is applied,** in the second commit: - **The line on #255 and #271** no longer says that `lane-reconcile` and `lane-handoff` read a dependency nested in a leg, since only the sweep and `lane-end` do. It says that a leg directory this account cannot search is unreadable and never `absent`; that a dependency nested in a leg whose directory, or one above it, it cannot search is unreadable to the sweep and `lane-end` too; and that one known gap remains, #272, where the sweep reads a declared leg beneath such a directory as `absent`, so `lane-end` does not hold on it. It also says NOT POLLED where the tool prints it, and it says that the item closes two gaps in #246, where it said "both gaps" with nothing before it to refer to. - **The line on #280 and #283:** an `awk` exits and does not "return", and the sentence on a status of 0 or 8 is gone, because a reader of the notes cannot act on it. - **The line on #214:** the fence passes a local commit only when it adds or changes a register or handoff file, and never checks whose it is, so the line no longer says "the lane tooling's own". - **The counts in the `<other-changes>` item:** the pin's line (#233) is in the list, so the list carries 44 of the 53, and the other 9 have their lines under "What 1.1.0 adds", not 43 and 10. The item says "the openRepoShape pin" where the runner pin of #296 could be taken for it. - **The milestone item** names #247, so it lists the six entries the milestone held on 2026-10-20. - **#209 is not tests-only.** Its `-e` fixes `lanes`' stderr filter under macOS's BSD `grep`, so it moves from the test suite to the lane area, after #208's, and its one line there also carries its test changes: the `repoG#8` assertion and the count of bash files in prose. It is cited once. - **Lines for the changes after `a42946e`:** #263 (`d9065df`), #289 (`fb2cf36`) and #294 (`acc89ea`, which closes #292) landed on 2026-10-21, from 11:28Z to 11:30Z. Each has a line in its area, worded from its squash commit and its description: #263 and #289 under "The register and `lanes-edit.sh`", and #294 under "`lane`, `lane-start`, `lane-end`, the name guard and their helpers". - **#296 has its line too,** last under "The test suite and its checks": CI and the release workflow run on `ubuntu-24.04`, because `ubuntu-latest` is now Ubuntu 26.04, and `macos-latest` is unchanged. It landed right before this pull request, as `5afba4a`. - **"What 1.1.0 adds" gains two lines,** after the annotated-tags item. They are for the three entries that closed in the v1.1.0 milestone after the draft was written: - **#255,** fixed by #271: the lane tools name what they could not read in a triad's legs. `lane-handoff` names every tree-inventory record that is not a whole schema-1 one, and a leg directory this account cannot search is unreadable and never `absent`. Only the sweep and `lane-end` also see a dependency nested in such a leg, since `lane-reconcile` and `lane-handoff` read declared legs only, and #272 stays open as the known gap. - **#280,** fixed by #283: `lane-handoff` reads the lane's lifecycle whole and parses it afterwards, so an `awk` that exits early can no longer turn `lane-state`'s SIGPIPE into a lifecycle that could not be read. - #283 is #280's pull request, so the three numbers make two lines. Each line cites the issue and its pull request, as the hook-pair line does for #237 and #247. - **"What else changed since v1.0.0" is drafted** from `git log aaa7bc8..a42946e`, which is 53 squash commits, one pull request each and no merge commit. It is grouped by area, one line each, and each line ends with its pull request. It lists 44 of the 53: the pin's line (#233) at its head and 43 by area, as 4 on the estate commands, 10 on the register and `lanes-edit.sh`, 11 on `lane`, `lane-start`, `lane-end` and the name guard, 6 on the handoff and its skill, 1 on the lane tools in a triad's legs (#246), 9 on the test suite and its checks, and 2 on the triad migration's records. - **The other 9 are accounted for elsewhere:** the package series of #228 (#262, #267, #268, #269, #270 and #284) is covered by #228's lines, and #247, #271 and #283 have the lines above. - **The lines added on 2026-10-21** are for the four changes after `a42946e`: #263 and #289 on the register and `lanes-edit.sh`, #294 on `lane`, `lane-start`, `lane-end` and the name guard, and #296 on the test suite and its checks. The areas now hold 47 lines, one for each of 47 pull requests, and #233's line at the head of the list makes 48. - **The list is redone at the tag.** It is drafted from the squash titles only. - **"Before the tag" is updated in three items:** - `<pr-count>` gives the count at `a42946e`, which is 53; - `<other-changes>` says what was drafted, which 9 pull requests have their lines under "What 1.1.0 adds", that the lines for #263, #289, #294 and #296 were added on 2026-10-21, and what is left to do at the tag; the placeholder's own line now stands for the changes after `a42946e` that have no line above; - the milestone item names the six entries the milestone held on 2026-10-20: #228 still open, #237 closed by its pull request #247, #255 closed, and #280 closed by its pull request #283. **Not added:** nothing that has landed. #296 landed right before this pull request and has its line. This pull request itself, and whatever lands after it, is what the `<other-changes>` placeholder stands for. None of #263, #289, #294, #296 and #292 is in the v1.1.0 milestone, which still holds the six entries of 2026-10-20. **How this keeps the draft's rules** - **One-line paragraphs:** every added line is its own list item, and none is wrapped. - **References:** every reference above "Before the tag" is spelled `opensoft/openRepoTools#N`, with no bare `#N`, since the notes are published from another repository after the migration. - **Placeholders:** `<other-changes>` stays as a named placeholder in code font, as the closing line of the list and as its checklist item. `<pr-count>` is unchanged apart from the drafting-time count. - **The file's closing lines are untouched:** the self-naming last line stays last. **Tests** - **T023's verify command** passes: the last line names `openRepoTools-spec` and `docs/release-notes/v1.1.0.md`, `## Before the tag` is there, and so is `pipx install --force openrepotools`. - **Coverage:** a script over `git log aaa7bc8..acc89ea`, 56 commits and no merge, finds each pull request cited above "Before the tag" except the six of #228's package series, which #228's lines cover. That is 46 by area, #233 at the head of the list, and #247, #271 and #283 under "What 1.1.0 adds". #296 is not in that range, and its line cites it. - **Hygiene:** by grep over `tests/`, no test names this file. The two that read its content scan every tracked file, and both are in `tests/test_repo_hygiene.py`. The only other test that names `release-notes` is in `tests/test_release_workflow.py`, and it reads `release.yml`, not the notes. All of `tests/test_repo_hygiene.py` was run on the file, through `tests/run.sh` with every `LANES_*` and `TMUX*` variable unset: ```text -k 'repo_hygiene' -v collected 2933 items / 2696 deselected / 237 selected 237 passed, 2696 deselected in 2.59s ``` - **Not run:** the whole suite. CI's `tests` job is the suite of record. This pull request's own runs on `bebb77d` went red on `ubuntu-latest`, which is Ubuntu 26.04, for the reasons #296 gives, because #296 was not yet on main. The record is lane 3's queue run over main, #296 and `bebb77d`, on `ubuntu-24.04` (below). **Where the draft's rules left room, and lane 3's answers (comment 6314185715)** - **Which section holds #255 and #280:** "What 1.1.0 adds", because the milestone is Brett Heap's scope for the release and #237 with #247 already sit there. - **`<other-changes>` stays** as a closing placeholder after the drafted list, since this pull request itself lands after `a42946e`. - **How much to list:** the tests-only and migration-record lines stay through the tag, because the check that every pull request has a line depends on them. If Brett Heap wants a shorter body, they fold into one line that still lists every pull request number. - **Issues and pull requests:** the milestone lines cite both, and the area lines cite the pull request only. **Lane 3's re-check of `bebb77d`** (#293 6315668503) is MERGE AFTER FIXES. - **What holds:** every line lane 2 listed, the recount behind line 140 (44 of the 53 pull requests in `aaa7bc8..a42946e`, and 56 in `aaa7bc8..acc89ea`), the file's rules, and T023's verify commands. - **One SHOULD and two NITs, all wording:** - line 45's sentence on a dependency nested in a leg, where "one above it" reads as a directory above the leg, which is #272's case. The text was lane 3's own; - "One known gap" in the same line; - line 74's last clause. - **Why they wait:** lane 3 merges the notes as tested and carries the three fixes into the notes' final pass at the tag. A push here would cost another CI round while lane 2 is out of usage until 15:30Z, and the notes are edited again at the tag in any case. Lane: openRepoTools-2 Landed by lane openRepoTools-3, as queue prefix `lane3-queue-u/p2-293` (tree `9b879ca`; tests.yml run 39595166877, green on `ubuntu-24.04`), right after #296. Before the landing, lane 3 merged main into the branch, so that its own checks ran over main with #296; the content is unchanged. Brett Heap's word, of 2026-10-20T22:5xZ: "merge all when green for all merges thru v1.1.0. use this for all lanes" (#228 6303614082). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
pushed a commit
that referenced
this pull request
Oct 21, 2026
…scripts, each tool pinned by version and sha256, in a workflow of its own (#277) This completes the WIP commit b1c59e6, which added lint.yml alone. It is a branch for v1.2.0, with no PR and no label, and the job has not yet run on a GitHub runner: every result below was taken on one machine with the pinned tools, so the CI record is still to come. No job ran either tool, so a warning on a workflow or a shell script showed nowhere. Lane openRepoTools-2's review of #228's release.yml found three mutants that actionlint's shellcheck rule flags and the suite lets through (I4, I13 and N3), and nothing in CI would have said so. What changed - .github/workflows/lint.yml, one job `lint`, in a file of its own. tests.yml is the release gate's file: release.yml requires exactly its four jobs, tests/test_tests_workflow.py holds that, and rehearsal D rewrites it for the code leg. The job runs on pull requests, on main and release/v*, and by dispatch, and it is no part of the gate. It runs on ubuntu-24.04, as every other ubuntu job does since #296 (ubuntu-latest became Ubuntu 26.04 on 2026-10-21). - actionlint 1.7.12 and shellcheck 0.11.0, each a release archive checked with sha256sum --strict -c before it is unpacked. Each digest was computed from the downloaded archive and equals GitHub's asset digest, and actionlint's also equals upstream's checksums.txt. Neither tool comes from the runner image or from a package index, and both are static binaries. - actionlint runs over every workflow with the pinned shellcheck given by path, with no exclusion, no severity floor and no config file, and the step fails if actionlint reports its shellcheck rule disabled, which it does silently when it cannot start the binary. - shellcheck runs, one file at a time, on every tracked file whose first line is a bash or sh shebang, at the baseline below. That is 16 files on main, and 15 of them run. - tests/test_lint_workflow.py (17 tests, 21 cases) pins the file and runs its three steps under fakes instead of only reading them: the install step over a genuine archive, over a tampered archive of each tool (nothing is unpacked, and after a bad actionlint archive the shellcheck one is never asked for) and over a tool that says another version; the actionlint step under a fake actionlint (a finding, a disabled shellcheck rule, a shellcheck that is not the pinned one); the shellcheck step in a throwaway git repository (selection by shebang, the baseline's flags, every file checked after one fails, an empty selection fails). The header's table of codes is held to the step's list, and the baselined scripts are held to be tracked bash files. What the tools found on main a42946e - release.yml, tests.yml and lint.yml: nothing, at any severity. - The 16 shell scripts: no error. park, resume and status: nothing at all. The other twelve: 319 findings, 190 warnings and 129 notes. Warnings: SC2034 157, SC2088 14, SC2209 4, SC1007 4, SC2154 3, SC2069 2, SC2178 2, SC2128 2, SC2141 1, SC2318 1. Notes: SC2016 55, SC2018 18, SC2019 18, SC2295 11, SC2329 10, SC2086 5, SC2015 5, SC2094 4, SC2020 1, SC2012 1, SC2006 1. lanes-edit.sh holds 142 of the 319. - tests/test_lane_helpers.sh, which the job does not run: 43 warnings and 218 notes, and 16.2 GB of memory to find them. - Two of the warnings are latent bugs and not style: SC2141 in lanes-edit.sh's session_start_block (IFS='\t' is a backslash and a t, so a live-fork row id<TAB>4242<TAB>interactive reads as id<TAB>4242<TAB>in, erac and ive) and SC2318 in openRepoTools's wip_fetch_template_file (bash expands $rel before the local that sets it takes effect, so path carries the caller's rel, which is right only because wip_init calls it from a loop over its own rel). Neither is fixed here. Main has moved since, and the baseline holds on it. On main 50d05d4 (after #263, #289, #294, #296 and #293) the job's three steps, run as written over that tree, exit 0: actionlint finds nothing in release.yml, tests.yml and lint.yml, and shellcheck finds the same 190 warnings, in the same ten codes and the same counts, no error, and 132 notes (lanes-edit.sh gained two SC2016 and one SC2015). lanes-edit.sh peaks at 10.9 GB and takes about 90 to 130 s, which a 16 GB runner holds. What the baseline leaves out - The workflows: nothing. - park, resume, status and every script added later: nothing. - The other twelve scripts are held to --severity=warning less ten codes, so the 129 notes and the 190 warnings of SC1007, SC2034, SC2069, SC2088, SC2128, SC2141, SC2154, SC2178, SC2209 and SC2318 are left out, and any error or warning of another code fails. Each code, its count and the reason it is left out are in lint.yml's header. - tests/test_lane_helpers.sh is not run: it is the lane helpers' own suite, nothing ships it, and shellcheck needs 16.2 GB for it (measured), more than a 16 GB runner has. lanes-edit.sh is run: 10.5 GB and about two minutes. The three mutants, applied to release.yml in scratch copies (lane 2's own diffs), and run through the job's own actionlint step, on a42946e's file and again on 50d05d4's - I4 (3.2.*) made *)): SC2221 and SC2222, warning. - I13 (the link fingerprint check made `if false`): SC2034, warning. - N3 (exit 0 as the clean step's first line): SC2317, info. Each makes the step exit 1. N3 is only info, which is why the workflows have no severity floor. Tests, run only through tests/run.sh -k - -k 'test_lint_yml or test_no_committed_file_names_a_host_absolute_path or test_adoption_act_zero_is_cited_by_the_sha_that_landed': 23 passed. The last two scan every tracked file, so they read lint.yml and the new test. - Before the workflow existed (lint.yml set aside), -k test_lint_yml: 19 errors and 1 failure of its 21 cases. The 21st reads only tracked files and passed. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This was referenced Oct 22, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Closes #120
Closes #122
Copilot rounds 5 and 6 on #93, filed rather than taken. Each finding was checked against main at
5e88a2cfirst, because landings since 09-16 had hardened nearby paths: #121 (replace_line's no-op), #221 and #230 (the sweeps' and the archive's reads). #227 changed CI only. Only what was still open is changed here.Lane openRepoTools-4 wrote this on lane 3's consent (NOTED 2026-10-18T13:34:54Z) and on its terms: one PR, an Opus writer and an Opus review, merged only on Brett Heap's word, and no changed line on act 2's sidecar-lock lines.
The independent Opus review of
0702547returned FIX: one MUST, two SHOULDs and seven NITs.2015d1ffixes them; see the review round.42e73e5merges main55aae16into the branch (no force), so CI tests the moved main. The delta review of42e73e5returned FIX again: one new MUST, which round 1's take-back brought in, and three NITs.ae0eb92and4ca5104fix them; see the delta round.d0256ffmerges main00d52ae(#252) into the branch (no force); see the merge of #252. Delta review 3 ofd0256fffound the merge clean to land and returned FIX on fix round 2: one new SHOULD (S3) and one NIT.5b460b7fixes them; see delta round 3. Delta review 4 of5b460b7closed S3 and returned FIX on one new SHOULD (S4) and two NITs (N1, N3).76b119dfixes them; see delta round 4. Delta review 5 of76b119dreturned LAND with no findings.32d75a9merges main90eed74into the branch (no force), on the coordinator's word; see the merge of main90eed74. Main has since moved toa42946e(#265, the shell suite and its wrapper only).refs/pull/263/mergemerges the two cleanly, so CI tests the PR on it; the branch is not re-merged without the coordinator's word. Line numbers below are in76b119d'slanes-edit.sh, except in the round tables, which name their own commit.Per finding
5e88a2c76b119d)cat > LANES.mdstatus is ignoredreplace_lineand since c1bac0d (#93's squash) indelete_lines. Thecmpproof the issue asked for was never added. A short write that exits 0, and one that fails part-way, both leave a truncated register; the second exits 5 saying "its line N is UNCHANGED". The copy$prethat every proof is made against had no check at all.register_copylanes-edit.sh:1654,register_rewrite:1658; used at:1703,:1752(replace_line) and:1778,:1834(delete_lines)rr_ln2 = 0.ensure_log's own test and sentence.lanes-edit.sh:12833rr_openhas kept the last lane-kind verb since c1bac0d, so the "one extra read" is settled. The sentence is one line for closed and running lanes alike, and names no exit.lanes-edit.sh:12749|line with no backtick and takes the first backtick anywhere as the lane.RETIRED_ROWS_AWKlanes-edit.sh:3713,retired_identity_hits:3731,add_row_retirement_rescan:3774, add-row:15281|| :halfarchive_textreturns 1 on a published or local copy that will not open. #230 (5e88a2c) extended that to an archive path that is not a regular file. The awk's status was still masked by the| sort -ubehind it.lanes-edit.sh:3731What each fix does
Three hardenings Copilot round 5 raised on #93, filed rather than taken: the register rewrite's unchecked status, the sweep's weakened published-path preflight, and retire-rows' wrong exit for an already-closed log #120 item 1. The register is rewritten in two places, and both now prove what they copied and what landed.
register_copycompares the copy with the register.register_rewritecompares what landed with what was built, and refuses with 5 in one of three sentences:lanes-edit-register-before-edit.<UTC>.<pid>. It is written with mode 0600 under noclobber and proved withcmp, so a restart that empties the temporary directory leaves it. Only where that cannot be done is it renamed beside$TMPD, and then the refusal says a restart would lose it. The refusal also says to restore before the next write, because that write would commit the truncated register as a pre-existing edit.handle_preexisting). Where add-row's take-back is the act that cannot rewrite, the refusal says add-row HAS committed its row, and how to remove it.5e88a2c, with acatthat cuts one copy at half its lines:Three hardenings Copilot round 5 raised on #93, filed rather than taken: the register rewrite's unchecked status, the sweep's weakened published-path preflight, and retire-rows' wrong exit for an already-closed log #120 item 2. The state a case-only rename leaves when its commit is not pushed used to pass the scan:
ensure_logthen refused it inside the write loop, after the lanes before it had their logs created. Measured:retire-rows repoX-2 repoX-3exited 2 withlanes/log/repoX-2.mdleft behind. It now refuses in the scan, inensure_log's words, and writes nothing.Three hardenings Copilot round 5 raised on #93, filed rather than taken: the register rewrite's unchecked status, the sweep's weakened published-path preflight, and retire-rows' wrong exit for an already-closed log #120 item 3. The rule stays as clause (c) has it. The message now follows the log's last lane-kind line:
RETIRED: the lane is already CLOSED and RETIRED, already hidden (19(b)), andlanes-edit.sh archive-rows <repo>takes its row out of the register (19(d)).ENDED: the lane is already CLOSED, and nothing more is owed. Its row stays as the record (Rule 9), andarchive-rowsis not its exit, since it moves only rows whose cell saysRETIRED.STARTED/PAUSED/RESUMED: "End it as itself when its work is done: lane-end<lane>".add-row's archive scan treats an unparseable archive row as a non-row, so a retired position can be reissued — and the obvious refusal would fire on the archive's own header (#93 round 6, filed rather than taken) #122. Every
|line of the archive must now be one of three:lane, asarchive_rowswrites it;Any other line is named by its line number and the copy it is in (
origin/main, this checkout, or the index). add-row refuses it with 1, the code it already uses for an archive it could not read. So doesretired-identity, and the scan's own status is now read.The after-rebase rescan fails closed and takes its row back, and only its own row. add-row commits its row before
pull --rebase, so a refusal there must undo that commit. Every refusal of the rescan now does: an archive it cannot read, a line it cannot take apart, or a peer's retirement. The copy taken back is the one this checkout's register adds over origin's. It is read from their-U0diff by content, under whatever sha the rebase gave the commit:That diff is asked for with
GIT_DIFF_OPTSunset anddiff.interHunkContext=0, either of which would otherwise add context to-U0. Its parse counts every line of a hunk but a removed one, so any context that still arrives is counted where it stands (delta review 3, S3). It is read as text whatever classes the register as binary, and the proof that the removal took exactly one line counts lines whatevercore.bigFileThresholdsays (delta review 4, S4). Where the take-back itself cannot remove the row, every refusal says so in add-row's own words: the row is still committed locally and unpushed, and must be removed before any other write publishes it. Where the removal is written but cannot be committed, a note says whether it is staged, that add-row's commit still holds the row, and the command that commits the removal.The issue suggested telling the header apart by its
|separator count. That cannot work: the header carries 6 separators, exactly like a data row (measured). "The line above the|---|rule" cannot be used either: the live register has two rows between its header and its rule (lines 76-79 on 2026-10-19). I also dry-ran the new rule, read-only, against the live register's 75|lines. Its header, its rule and all 73 rows pass, including the three spelled*(unnamed)* `<lane>`. None are refused. No archive is published on the live estate yet.Two shapes are now refused that look valid on screen: an archive with CRLF line endings, and a table written without the spaces around its
|.archive-rowswrites neither, and nothing else in this tool reads CRLF.docs/README-lanes.mdsays so.The header's exit-code table,
docs/README-lanes.md's exit-code row 5, its sweep section and its archive section all say so.One bad archive line refuses every
lane-start(review S1, for lane 3)retired_identity_hitsreturns 3 for a line it cannot take apart, whichever repository's row it is in, andlanes-edit.sh retired-identity <lane>exits 1 on it.lane-startasks that check about every lane before it launches, so while such a line is in the archive, everylane-starton the estate refuses, not only the bad line's lane. Measured by the review:retired-identity repoZ-1, against an archive whose bad line is a repoX row, exits 8 at5e88a2cand 1 here.This is fail-closed by design: an archive line nobody can read may hold the position being started. But
lane-starthides the reason:The line is seen with
lanes-edit.sh retired-identity <lane>. That prints each bad line with its number and its copy.docs/README-lanes.md's archive section now says both things.Suggested to lane 3 (
lane-startis lane 3's file, so it is not edited here): capture that call's stderr and print it in the*)refusal, or drop the2>/dev/nullthere. The refusal would then name the line to fix.The review round (
0702547→2015d1f)42e73e5)add_row_retirement_rescan:3714test_122_a_refusal_after_the_rebase_takes_its_row_back(repo19race's shape: a peer pushes a mangled archive row during add-row's pull; add-row exits 1; the row is gone from HEAD, fromorigin/main..HEADand from origin; a laterset-row-stateexits 0 and publishes no repoX-4)lane-start, and the reason is hiddenlane-startchange is suggested to lane 3; that file is not touched.docs/README-lanes.md, archive sectionreg_wrote/reg_written_here:1627,register_copy:1635; callers:11941,:11957,:11964,:12735,:12742,:12957,:12960test_120_item_1_a_sweep_refused_part_way_names_what_it_wrote_and_is_not_called_closed(first half); the copy cases of…cut_short…rr_pub_open:12587, the check:12603docs/README-lanes.md…reissues_no_position(6 cases)register_rewrite:1642test_120_item_1_a_write_that_never_reached_the_register_says_it_is_as_it_was(both acts, register made read-only)`lanes-edit.sh archive-rows repoX`test_120_item_3_…_closed[RETIRED]retired_identity_hits:3683-3687test_122_a_line_number_says_which_copy_it_is_in(line 10 on origin, line 12 here)archive-rowsarchive-rowsis not its exitretire_rows:12609test_120_item_3_…_closed[ENDED]$TMPD, out of the trap's reach (a rename, no free space needed), and its path and restore command are named.git checkoutis named only as the fallback that drops uncommitted edits.register_rewrite:1647…cut_short…[*-write and restore fail]: the named file equals the register as it wasThe delta round (
42e73e5→4ca5104)4ca5104)add_row_retirement_rescan:3743, the diff:3776, the take-back:3800test_122_a_refusal_after_the_rebase_leaves_a_peers_identical_row_alone(p15's case, for both refusals: a bad archive line, exit 1, and a retirement, exit 2);test_122_a_refusal_after_the_rebase_takes_back_its_own_row_beside_a_peers_copy(the other direction).test_122_a_refusal_after_the_rebase_takes_its_row_backstays green.handle_preexistingcommitted a peer's editreg_committed. Where add-row's take-back cannot rewrite the register, the refusal says add-row HAS committed its row, and how to remove it.reg_committed:1638;register_copy:1646;:1744,:1803; the take-back:3805test_120_item_1_a_refusal_after_a_captured_peer_edit_says_this_act_committed_nothing;test_122_a_take_back_that_cannot_rewrite_says_add_row_has_committed; the copy casesretire_rows:12673test_120_item_3_a_closing_line_only_this_checkout_has_is_not_called_closed[ENDED]$TMPDIR, mode 0644lanes-edit-register-before-edit.<UTC>.<pid>. It is written with mode 0600 under noclobber, after checking the name is free, and proved withcmp. The fallback beside$TMPDsays a restart would lose it.register_rewrite:1659…cut_short…[*-write and restore fail]: the named file is ingit rev-parse --absolute-git-dir, its name matches, its mode is 0600, and its bytes are the register as it wasRed at
42e73e5: 13 of 33.repoX-4row, andorigin/main..HEADheldcancel add-row ….The merge of #252 (
d0256ff)Main moved from
55aae16to00d52aewhen #252 landed (#113, #114, #115).git merge --no-ffof00d52aeinto4ca5104is textually clean: treee907343, the same tree the trial merge with #252's heade6a41b4gave.Semantic interplay: none found. Both PRs change
lanes-edit.shanddocs/README-lanes.md, but in different places.lane_sidecar_schema_ok,lane_sidecar_put),lane_state_read,lane_trees_list,lane_tree_now,lane_reconcile,worktree_rows,set-lane-treeandlane-state.register_copy,register_rewrite),retire_rows, the archive scan (RETIRED_ROWS_AWK,retired_identity_hits) and add-row's after-rebase take-back.git rev-parse --absolute-git-dir, not the control root, and its name cannot meetlane_sidecar_put's<file>.tmp.<pid>.On
d0256ff, both bash 3.2.57 and 5.2 passbash -n, and the lock lines are main's own (94be0fc3…, 65). The sidecar lines are main's own too (591ba21d…), with no changed line naming either.Delta round 3 (
d0256ff→5b460b7)Delta review 3 found the merge
d0256ffclean to land: no interplay with #252, and lane 3's terms held. Fix round 2 had one new SHOULD and one NIT.5b460b7)+lines of its-U0diff, and-U0does not hold on its own.GIT_DIFF_OPTS=-u3overrides it, and a user'sdiff.interHunkContextfuses a nearby change's hunk with the row's, with the lines between as context. The row was put at the wrong line, nothing was taken back, and the next ordinary write published the refused row: M1 again (the review's p18b and p18c).GIT_DIFF_OPTSunset and-c diff.interHunkContext=0. Theunsetis inside the command substitution's own subshell, so nothing after it is touched. A command-line-coutranks every config file andGIT_CONFIG_*in the environment (measured on git 2.43). And the parse advances its new-file line on every hunk line except a removed one and git's\ No newlinenote.:3805, the parse:3809test_122_a_take_back_finds_its_row_whatever_context_the_diff_carries, 4 cases (below)delete_lines' words, which are archive-rows': "the rows are in" the archive, and "the archive may have been appended".delete_linestakes its caller's sentences, with archive-rows' as the default. add-row's say its row is still committed locally and unpushed, and must be removed before any other write publishes it.delete_lines:1776, the take-back:3840test_122_a_take_back_that_cannot_rewrite_says_add_row_has_committed,[copy]and[write]The S3 test's four cases each assert the published register after the next ordinary write, then how the take-back's diff was called: no
GIT_DIFF_OPTS, and-c diff.interHunkContext=0.GIT_DIFF_OPTS=-u3in add-row's environment (p18c).diff.interHunkContext=3beside an unpushed edit one row above the end (p18b).+line in the row's hunk.Red at
d0256ff: 6 of 38.repoX-4.d0256ff's parse counts consecutive+lines right.The two halves cover for each other, so each needs its own kill.
+lines) is killed only by the shim case, whose context the suppression cannot turn off.Delta round 4 (
5b460b7→76b119d)Delta review 4 closed S3. Under 24 diff settings and routes, and under one hostile global config holding many of them at once, the take-back still found its row's line, except where a setting classes the register as binary: that is S4. A clean filter that changes the register's line count puts the row at the wrong line, and the line check then refuses loudly. Lane 3's terms held. It returned FIX on one new SHOULD and two NITs.
76b119d)binaryor-diffattribute, published or local; a diff driver withbinary=true;core.attributesFile;GIT_ATTR_SOURCE;attr.tree; a NUL byte in the first 8000 bytes; and acore.bigFileThresholdbelow the register's size. None is live on Eagle, but the live register is 2,309,050 bytes, so one global threshold under that would be.--text, which finds the row under all eight. (c) Its removal'snumstatproof runs with-c core.bigFileThreshold=2g. Under a smaller thresholdnumstatprinted-for both counts, which--textdoes not change, so the take-back refused. A command-line-coutranks the global config,GIT_CONFIG_COUNTandGIT_CONFIG_PARAMETERS(measured on git 2.43).:3832; the proof:1826test_122_a_take_back_reads_its_diff_as_text_whatever_classes_the_register_binary:-diffin.git/info/attributes, a published.gitattributeswithbinary, andcore.bigFileThreshold=200delete_lines' line-count andnumstatrefusals named no row and no commit: "the removal added or rewrote lines (numstat: …); refusing", and the next write published the row.delete_linesmakes after its copy ends asregister_copy's does: the register as it was, what this act has committed, the caller's sentence, and what the act has already written.dl_as_was:1787; the refusals:1817,:1828test_122_a_take_back_that_cannot_rewrite_says_add_row_has_committed,[line count]and[numstat]:1813…[awk pass]git addorgit commitfailed, add-row said nothing of its own. The removal was left written or staged, and the next write's commit carried it under that write's caption.:3878,:3882test_122_a_removal_that_cannot_be_committed_says_so,[staging]and[commit]. Each runs the command the note gives; the next write then publishes no repoX-4, and origin's history has the removal under its own caption.Red at
5b460b7: 8 of 46.repoX-4.[numstat]fails on add-row's exit: at5b460b7the diff read the register as binary, so add-row said there was nothing to take back.[awk pass]and[line count]fail on the sentence.Still a refusal, loud and true. An attribute on every path (
* -diffin acore.attributesFile) or a NUL byte in the register makesnumstatread the two copies as binary at any threshold. So the take-back refuses (exit 5) in add-row's words;[numstat]pins this. Nonumstatoption reads such copies as text:--textand the threshold change nothing there (measured).The merge of main
90eed74(32d75a9)The branch last merged main at
00d52ae(d0256ff). Since then main has taken #264, #266 and #283, the #228 release series (theopenrepotoolspackage, the package job intests.yml, the release workflow and its docs) and #271 (#255: the handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search is unreadable, never absent). Only #271 touches a file of this PR.git merge --no-ffof90eed74into76b119dis textually clean: tree44bfcae, the treegit merge-tree --write-treegave beforehand.Semantic interplay: none found. Of this PR's three files, main changed two, both in #271.
lanes-edit.sh: The handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search, or a dependency nested in one, is unreadable and never absent (fixes #255) #271 changes onlylane_reconcile's unreadable-leg branch: a leg directory that cannot be searched is aTREErowunreadable. This branch changes the register's rewrites (register_copy,register_rewrite,delete_lines),retire_rows, the archive scan and add-row's after-rebase take-back. Neither reads or writes the other's state, and The handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search, or a dependency nested in one, is unreadable and never absent (fixes #255) #271 spends no refusal code.docs/README-lanes.md: The handoff names every inventory record that is not a whole schema-1 one, and a leg directory this account cannot search, or a dependency nested in one, is unreadable and never absent (fixes #255) #271's rows are in the triad section: the sweep'slegrows, the handoff's inventory andlane-reconcile'sTREErows. This branch's are the write-failure paragraph, the sweep and archive sections and exit-code row 5. No row contradicts another.lane-handoff,lane-worktrees,openRepoTools,tests.yml, the package and the release workflow) is in no file of this PR, andtests/run.shandconftest.pyare unchanged since00d52ae.On
32d75a9, bash 3.2.57 and 5.2 both passbash -n. The lock lines are main's own (94be0fc3…, 65), and so are the sidecar lines (591ba21d…, 123). No line of the PR's diff against main names either, andlane-startandlane-handoffshow no diff against main.Tests: red at
5e88a2c, green at headThe new file is
tests/test_register_hardening_120_122.py, with 46 cases in the style oftest_register_scan_hardening_138.py. It subclasses that file'sRegisterharness, and its three race cases share one helper in the shell suite'srepo19raceshape.--collect-onlyof the five files below, at76b119d5e88a2c'slanes-edit.sh5b460b7'slanes-edit.sh(fix round 3)d0256ff'slanes-edit.sh(fix round 2, merged)4ca5104(33) against42e73e5'slanes-edit.sh(round 1, merged)0702547'slanes-edit.sh(round 1)2015d1f: new file +test_register_scan_hardening_138.py+test_repo_hygiene.py42e73e5(main merged): the same three4ca5104: the same threed0256ff(main00d52aemerged): both PRs' stems,-k 'register_hardening_120_122 or register_scan_hardening_138 or repo_hygiene or lifecycle_fail_open_113_114_115 or lanes_index'5b460b7: the same five files76b119d: the same five files32d75a9(main90eed74merged): the same five files and the nine other test files main changed since677acc0(test_install_pipx,test_openrepotools_command,test_package,test_package_stamp,test_release_workflow,test_test_roots,test_tests_workflow,test_triad_lane_tooling,test_triad_lane_tooling_handoff)test_release_workflow, 43 in the two triad files, 27 intest_tests_workflow, 5 intest_packageand 1 intest_test_rootsAll runs went through
tests/run.shonly, with everyLANES_*/TMUX*variable unset, started detached withsetsid -f, and never inside a flock.tests/run.sh <file>always passesteststoo, so every run narrowed with-k <module stem>; the collect-only run confirms the selection. CI runs the whole suite on this PR.bash 3.2.
bash -npasses on2015d1f's,42e73e5's,4ca5104's,d0256ff's,5b460b7's,76b119d's and32d75a9'slanes-edit.shunder GNU bash 3.2.57 (a musl build, run through its own loader), and under 5.2. That build refuses a planted syntax error, so its-nis real. No added line uses a bash-4 construct: a grep for${x,,},${x^^},declare -A,declare -n,local -n,mapfile,readarray,|&,&>>,coproc,;&,;;&and${x@Q}finds none.Mutants: 40 at
5b460b7and 19 at76b119d, all killedEach mutant is applied to the head's
lanes-edit.sh, the new file is run against it, andlanes-edit.shis restored from HEAD. The 24 below were run again atae0eb92, with F1b, F3 and F6 re-pointed where round 2 moved their code. All are still killed. D1a to D1c, F1 and F1b were run once more on4ca5104's reordered D1 tests. All 40 were run again at5b460b7, with F1b and D1b re-pointed where round 3 moved their code, and every one is killed: the last column is that run's count of failed cases.5b460b7cmpcmp5e88a2c's conditionarchive-rowsis given the lane, not the repository0702547's branch)$TMPD, not in the git directory+lines again (fix round 2's parse)GIT_DIFF_OPTSis keptGIT_DIFF_OPTS=-u3, on theGIT_DIFF_OPTSthe diff saw)diff.interHunkContextis kept-c diff.interHunkContext=0)[copy])[write])Round 4 (
76b119d)The 12 mutants whose lines round 4 changed, or that round 3 aimed at the take-back, were run again at
76b119d, with F1b re-pointed where round 4 moved its code. The 7 below them are round 4's own. All 19 are killed.76b119d+lines againGIT_DIFF_OPTSis keptdiff.interHunkContextis keptnumstatrefusal names no row and no commitnumstatruns under the user'score.bigFileThresholdgit addof the removal is silentLane 3's constraint: the lock lines are unchanged
The lock lines' content and order are identical at base and head (the md5 without
-n). The sidecar lines differ between5e88a2cand the head only because #246 and #252 changed them on main; the head has exactly main's.Trial merges (
git merge-tree --write-tree)2015d1f55aae16aa69fcf, and42e73e5is that merge42e73e5e6a41b4(both touchlanes-edit.shanddocs/README-lanes.md)3b859a7; itslanes-edit.shparses under bash 3.2.57 and 5.24ca5104e6a41b4e907343; itslanes-edit.shparses under bash 3.2.57 and 5.24ca510455aae16refs/pull/263/mergehas parents55aae16and4ca5104and the head's own tree96a671a5b460b721e0829(#264, #266 since00d52ae; neither touches this PR's files,run.shorconftest)a5486b2;refs/pull/263/mergeis that merge (60e6293), so CI tests it76b119d677acc0(#283 since21e0829:lane-handoffand its test only)9fc8b17;refs/pull/263/mergeis that merge (34f1fbb), so CI tests it76b119d90eed74(the #228 release series and #271 since677acc0; #271 toucheslanes-edit.shanddocs/README-lanes.md)44bfcae, and32d75a9is that merge32d75a9a42946e(#265 since90eed74: the shell suite and its wrapper only)ecaeccf;refs/pull/263/mergeis that merge (2df51c4), so CI tests itLeft open (outside these findings)
The archive reader and writer disagree about what a row is (review NIT 2).
archive_rowstakes the first backtick anywhere in a line as the lane, while the reader now takes it only from the first cell. Take aRETIREDregister row whose lane cell lost its backticks while a later cell keeps one.archive-rowsmoves it,laneslists it, and then every add-row andretired-identityrefuses. That row shape is contrived, since every writer of a row puts the lane cell in backticks. Givingarchive_rowsthe reader's rule is a candidate follow-up.next-free(lane_position_rows) still skips an archive or register|line with no backtick. add-row now refuses such a position, so a retired one is still never reissued, butnext-freemay offer one that add-row then refuses. Applying the rule there needs care: the register carries prose tables of its own. It is a candidate follow-up.The cure
ensure_lognames, now shared by the sweep, is "Pull first". That does not publish a rename commit that never left this checkout.lanes-edit.sh commit's publish fence refuses a rename ("a deletion (a lane rename's included)"), so that state has no one-command exit today.lane-start's refusal hidesretired-identity's reason (S1 above). That is lane 3's file.Four defensive checks have no test that reaches them, so no mutant either:
\ No newlinenote, since the register always ends in a newline;cmpproof.No input reaches them short of a corrupt diff, a register whose last line lost its newline, a reused pid in the same second, or a copy cut on its way into the git directory.
Other diff readers on main are blind to a binary register (delta review 4, not covered here):
identify_changed_lanes,warn_dirtyand the commit caption. Each only loses a warning or a caption; the take-back was the one reader whose blindness published a refused row.replace_line'snumstatproof refuses every row rewrite (exit 5) under acore.bigFileThresholdbelow the register's size, as it does on main. That is everyset-row-stateand the sweeps' rewrites: fail-closed, and outside these findings. Giving it round 4's-c core.bigFileThresholdis a candidate follow-up.One-true-awk and macOS were not run here. The new awk is POSIX; the review ran it under mawk and BusyBox awk with identical results.
Do not merge without Brett Heap's word.
🤖 Generated with Claude Code