Repository navigation
A register that could not be read no longer reads as a lane with no session ids, so the dead-lane --force check and duplicate-holder fail closed (#67) - #221
Merged
Conversation
…ession ids, so the dead-lane --force check and duplicate-holder fail closed (#67) register_text threw away the status of its `git show` (or its `cat`), and row_of_lane is `register_text | awk`, so session_ids_of_lane and session_ids_local_of_lane answered the same empty set for "this lane has no row" and "the register could not be read". holder_is_dead handed that empty set to live_holder, whose 8 then confirmed dead a lane nobody had checked, and duplicate_holder_pids returned 8 ("no duplicate holder is live") without asking the process table anything. A failed read was also cached as an empty register, so every later read in the process saw a register with no failure left to report. register_text_checked is now the read itself (0, or 1 where the read failed), and it caches only a read that succeeded. register_text is that read with the status dropped, unchanged for its pipeline callers. session_ids_checked is the published-union-local id set with a contract: 0 with the ids, 8 when both copies were read and give the lane no transcript uuid, 1 when either copy could not be read. Only the two fail-open callers use it. holder_is_dead stops on 1 and sets HOLDER_REGISTER_UNREADABLE beside HOLDER_TERMINAL_UNKNOWN, and claim --force refuses it in its own words ahead of the verb test. duplicate_holder_pids returns 1 and names the register, and the duplicate-holder arm's refusal now names the register among what could not be read. The other callers of the session-id pair are unchanged. The new case in tests/test_lane_helpers.sh runs in its own workspace. A peer publishes the holder's row after this clone was made, and a git shim fails the one `show` of the published register, so both halves of the union come back empty. Against f87fd5a it fails 8 of its 15 assertions: duplicate-holder answers 8, and claim --force grants a dead-lane TAKEOVER of a lane a live session still backs and pushes it before the rebase's rescan withdraws it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR separates “no session IDs” from “register could not be read” by introducing checked register and session-ID reads, then makes dead-lane --force and duplicate-holder fail closed with targeted errors instead of treating unreadable data as proof that no live holder exists. Sequence diagram for fail-closed dead-lane force checksequenceDiagram
participant Claim
participant Holder as holder_is_dead
participant IDs as session_ids_checked
participant Register as register_text_checked
participant Live as live_holder
Claim->>Holder: holder_is_dead(lane)
Holder->>IDs: session_ids_checked(lane)
IDs->>Register: register_text_checked()
Register-->>IDs: register + status
alt register unreadable
IDs-->>Holder: status 1
Holder-->>Claim: HOLDER_REGISTER_UNREADABLE
Claim-->>Claim: --force refused
else register readable
IDs-->>Holder: ids + status 0 or 8
Holder->>Live: live_holder(ids)
Live-->>Holder: liveness result
Holder-->>Claim: dead or not dead
end
Sequence diagram for duplicate-holder checked readsequenceDiagram
participant Command as duplicate-holder
participant Check as duplicate_holder_pids
participant IDs as session_ids_checked
participant Register as register_text_checked
participant Processes as ProcessTable
Command->>Check: duplicate_holder_pids(lane)
Check->>IDs: session_ids_checked(lane)
IDs->>Register: register_text_checked()
Register-->>IDs: register + status
alt register unreadable
IDs-->>Check: status 1
Check-->>Command: refusal naming register
else ids read successfully
IDs-->>Check: ids + status 0 or 8
Check->>Processes: scan holder processes(ids)
Processes-->>Check: matching processes or none
Check-->>Command: duplicate result
end
File-Level Changes
Assessment against linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
…CI tests this PR on today's main Main moved 15 commits past this branch's base f87fd5a, through the suite's machinery and pin (.github/workflows/tests.yml, contracts/openreposhape-pin.yaml, tests/conftest.py, tests/test_repo_hygiene.py, upstream/openRepoShape), after this PR's last green run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 18, 2026
…CI tests this PR on today's main Main moved 4 commits past this branch's base 2cb14a9, through the suite's machinery and pin (), after this PR's last green run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…ane holds the object (#74) (#204) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137725493) was green on head af5b7ce with main 586dea2 in its tree. Independent review (lane 5, Opus, of 4996156; the merges of main add nothing to the PR's diff): LAND. Closes #74 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…so a migrated PAUSED row stops showing NO LOG (#89) (#206) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137733527) was green on head cd3ced0 with main 586dea2 in its tree. Main 0a49697 merged with head cd3ced0 is tree f88c2f1, which queue run 39137981522 (workflow_dispatch of tests on 3f5f4b9) tested whole and green. Independent review (lane 5, Opus, of 00b75e6; the merges of main add nothing to the PR's diff): LAND. Brett Heap confirmed the unpickable (row) lanes as built (2026-10-18). Closes #89 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…or binding log instead of guessing a branch (#60) (#208) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137741203) was green on head 3edaf0a with main 586dea2 in its tree. Main be82c20 merged with head 3edaf0a is tree cfd2ee6, which queue run 39137988005 (workflow_dispatch of tests on 9db4863) tested whole and green. Independent review (lane 5, Opus, of c252a3c; the merges of main add nothing to the PR's diff): LAND. Closes #60 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…s why refuse it before writing, and the deferred lane-start command keeps --agent (#85, #72 items 5, 6) (#218) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137747959) was green on head bfc5ddd with main 586dea2 in its tree. Main fca60f5 merged with head bfc5ddd is tree b188309, which queue run 39137992777 (workflow_dispatch of tests on 586df3b) tested whole and green. Independent review (lane 5, Opus, of 99f3853; the merges of main add nothing to the PR's diff): LAND. Brett Heap confirmed refusing CR/LF at the door (2026-10-18). Part of #85. Part of #72 (items 5-6). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
added a commit
that referenced
this pull request
Oct 18, 2026
…ane-handoff renames the window only once the handoff can no longer be refused (#80 slices B and C) (#220) Landed on Brett Heap's word "land #204 #206 #208 #218 #220 #221 when green" (2026-10-18), by lane openRepoTools-5. Required Linux CI (guard-launch-mode, tests, tests-no-submodule; run 39137756635) was green on head 0b531e5 with main 586dea2 in its tree. Main 01eff2c merged with head 0b531e5 is tree a388312, which queue run 39137998354 (workflow_dispatch of tests on f335793) tested whole and green. Independent review (lane 5, Opus, of 9f6fd2d; the merges of main add nothing to the PR's diff): LAND. Brett Heap confirmed the rename trade as built (2026-10-18). Part of #80 (slices B and C). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
marked this pull request as ready for review
October 18, 2026 16:01
|
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 1 day and 23 hours by commenting |
brettheap
pushed a commit
that referenced
this pull request
Oct 18, 2026
…an-hardening-138 The one conflict is register_text, now #221's register_text_checked. It keeps #221's shape: a text and a status, a failed read printed and never cached, and register_text as the same read with the status dropped. The published-or-not question inside it is #230's register_published, the tree's answer (0 show, 8 this checkout's copy, 1 a refusal), not `cat-file -e`. The three #230 readers that relied on register_text's status now read register_text_checked, as #230's body planned: rows_named_ci (add-row's refusal), lanes_rows' one check (the withheld DORMANT class) and lane_position_rows (next-free). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
brettheap
pushed a commit
that referenced
this pull request
Oct 18, 2026
…86, so CI tests this PR on today's main One conflict, in docs/README-lanes.md at the end of the duplicate-holder paragraph: #221 (now on main) added the clause "and a register that cannot be read is never 'no duplicate': the row's ids are the whole search, so it exits 1 and names the read (#67)" in front of the lane-end --retire sentence, and this PR rewrote that sentence for #86 (TERM every child, not only the first; report each pid; say RETIRED only once all are gone). Both stay: #221's clause first, then this PR's sentences, which read on from it unchanged. Nothing else conflicted; lanes-edit.sh and tests/test_lane_helpers.sh merged cleanly. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
brettheap
pushed a commit
that referenced
this pull request
Oct 18, 2026
…second checkout by LANES_REPO and LANES_FILE, as the suite's other 24 cases do under Amendment 23's explicit seam (#198) Main's fe58808 merge brought a new helper, r67(), that pointed the commands at a second clone through LANES_WORKSPACE_ROOT alone. Since this slice, the suite exports LANES_REPO and LANES_FILE at $WIP for every case (ort186 Q9's seam). An explicit LANES_REPO is honoured in place and W no longer chooses the checkout a command runs in, so r67's reads and its refused `claim --force` would have run in $WIP, not the #67 clone. r67 now names the clone the same way the other 24 cases were converted (LANES_REPO=<clone> LANES_FILE=<clone>/lanes/LANES.md). Its assertions are unchanged. Lane: openreposhape-2 (openRepoShape-2) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 19, 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.



What was wrong
session_ids_of_laneandsession_ids_local_of_laneanswer the same empty set for two different facts: "this lane has no row" and "the register could not be read".register_text(lanes-edit.sh:3202at f87fd5a) runs itsgit show(orcat) inside$( )and always ends inprintf, so it answers 0 whatever the read did.row_of_laneisregister_text | awk, andawkanswers 0 over an empty render.The two callers that read an empty id set as a verdict were therefore fail-open:
holder_is_deadpassed the empty set tolive_holder, whose 8 then confirmed dead a lane nobody had checked.claim --force's dead-lane path (lanes-edit.sh: a dead lane's OPENED objects and its long-stale CLAIMED have no takeover path — claim --force takes a stale CLAIMED only, log TAKEOVER by hand is refused #30) then took over its holds.duplicate_holder_pidsreturned 8, meaning "no duplicate holder is live", without asking the process table anything.Evidence. The new test block runs against f87fd5a's
lanes-edit.shin an isolated sandbox. In that sandbox the published register is unreadable, and this checkout's copy has no row for the holder because a peer published it after the clone. Results:duplicate-holder repo67-1exits 8.claim --forcepronounces the holder dead even though a live session on this workstation carries its id. It commits the takeover, and the rescan after the rebase withdraws it only because the pull happened to bring the row into this checkout. Both lines are pushed into an append-only log:The TAKEOVER line itself reads
lane repo67-1 is dead (log ends RETIRED, no live session on this workstation, issue #30). If this checkout's copy were unreadable rather than behind, the rescan would not rescue it.What changed
register_text_checkedis now the read itself: 0 with the register, or 1 where the publishedgit showor the working tree'scatfailed. It caches only a read that succeeded.register_textis nowregister_text_checked || :. Its output is byte for byte what it was, success or failure, so its pipeline callers are unchanged.session_ids_checked(new) is the published-union-local id set with a contract:holder_is_deadasks the checked read. On 1 it sets a newHOLDER_REGISTER_UNREADABLEbeside the existingHOLDER_TERMINAL_UNKNOWN, so every existing fail-closed reader, includingclaim_rescan_hook, refuses it unchanged. On 8 it goes on tolive_holderexactly as before.claim --forcerefusesHOLDER_REGISTER_UNREADABLEin its own words, ahead of the verb test and next to the Amendment 18(b) refusal. Otherwise an OPENED hold would get the verb refusal, which blames a live session nobody looked for. Exit 2.duplicate_holder_pidsasks the checked read and returns 1, withSESSION_FILES_ERRnaming the register. Theduplicate-holderarm's refusal now reads "could not read the register, this workstation's process table or its session records …".lane-end --retirealready refuses on any code other than 0 or 8.docs/README-lanes.md: one sentence in the dead-lane--forceparagraph and one in theduplicate-holderparagraph state the new refusals.managed_projection_read. This change does not makerow_of_lanereturn nonzero on a failed read, so that function's expectation is still not met byrow_of_laneitself. Its only caller,lane_is_managed_owned, already refuses an emptyregister_textbefore calling it, andmanaged_projection_checkhas no caller. Changingrow_of_lane's status would reach every other caller, so it is left alone.A side effect, and why I kept it. A failed read is no longer cached, so a later read in the same process asks git or
catagain instead of being served an empty register. For a mode-000 working-tree register with nothing published,cat's error now repeats on each read instead of printing once. The exempt subcommands are unaffected: measured with a missing register, before and after,guardprinted the same 9 stderr lines with nocat:line, andsession-startprinted nothing.Functions and regions edited
lanes-edit.sh: register_text→register_text_checked+ a one-lineregister_text(f87fd5a 3200-3222; head 3201-3245)lanes-edit.sh: holder_is_dead— preamble comment and theHOLDER_*globals (f87fd5a 3751-3757), the reset (3765) and the id read (3813-3814); head 3776-3801 and 3849-3857lanes-edit.sh: session_ids_checked— new, directly aftersession_ids_local_of_lane(head 5035-5059)lanes-edit.sh: duplicate_holder_pids— only its header comment's last line (f87fd5a 6734) and the id read and empty-set check (6741-6743); head 6802 and 6809-6820. The--resumeparsing and the child scan are untouched.lanes-edit.sh: dispatcher, claim)— one newif … diein the--forcedead-lane else-branch, after the Amendment 18(b) refusal (head 14799-14807)lanes-edit.sh: dispatcher, duplicate-holder)— its exit-code comment and its*)die message (head 15928-15929, 15941)docs/README-lanes.md— the dead-lane--forceparagraph (f87fd5a 948) and theduplicate-holderparagraph (1743)tests/test_lane_helpers.sh— one new block after the issue lanes-edit.sh: a dead lane's OPENED objects and its long-stale CLAIMED have no takeover path — claim --force takes a stale CLAIMED only, log TAKEOVER by hand is refused #30 section, beforeLANDING, LANDED, who --landing(head 1669-1759)Tests
One block in
tests/test_lane_helpers.sh, 15 assertions, in its own workspace: its own bare origin, a peer clone and this clone, the same shape as the Amendment 19 sandbox.The fixture.
repo67-1after this clone was made. repo67-1 has repoK-3's shape: its log ends RETIRED with an OPENED PR left behind, and a live session record carries its id.gitshim fails the oneshow origin/main:lanes/LANES.md, the archive case's idiom.[ -f "$LANES_FILE" ]guard refuses first. The git shim fails the read this bug is about.The cases.
duplicate-holder, readable register and no--fork-sessionprocess: 8.duplicate-holder, unreadable register with a stray in the table: 1, never 8, naming the register.claim --force, readable register: refused 2 as a live holder.claim --force, unreadable register: refused 2, naming the register, with no TAKEOVER line and no push.The duplicate-holder assertions return before
pgrepruns, or rest onpgrep's own "no match", so they don't depend on the--resumeparsing or child scan that the parallel #76/#86 work changes.Fail before, pass after. Proved in isolation, not by running the suite. A scratch runner, run under
env -i, builds a temp$HOME, the suite's ownpgrep/psfakes, a livesleep, andwrite_record. It then sources this block verbatim, extracted withawkbetween its header and the next section (block sha2563095ee7f…in both runs):bash -npasses on both changed bash files. I also checked the three bash-3.2 text rules fromtests/test_repo_hygiene.py(an unbraced$namebefore a non-ASCII character,caseinside$( ), a quoted replacement half) against both files by regex: no hits. The full suite was not run locally, per the lane's testing rule, so CI is the record.Closes #67
🤖 Generated with Claude Code
Summary by Sourcery
Fail closed when lane registers cannot be read so dead-lane takeovers and duplicate-holder checks require confirmed session data.
Bug Fixes:
claim --forceandduplicate-holderwhen the register cannot be read, with explicit refusal and error reporting.Enhancements:
Documentation:
Tests:
claim --forceandduplicate-holder.