Repository navigation
T017: A lane control root that cannot be searched is read as not read, not as no trees: lane-state and lane-trees exit 10, the listing says UNREAD, and a sync keeps the lane's rows (opensoft/openRepoTools#291) (part of opensoft/openRepoTools#186) - #5
Conversation
… is read as not read instead of as no trees: lane-state and lane-trees exit 10 instead of 8, the listing says UNREAD, and an inventory sync keeps the lane's rows T017 continuation, in opensoft/openRepoTools-code, of the opensoft/openRepoTools branch fix/control-root-unsearchable-291 at 3e97ae51445291fc3efdd36da5709edee3103549 (issue opensoft/openRepoTools#291). It carries that branch's own change against 50d05d4, with its merge of main 50d05d4 (457b73b) left out, for the code leg's paths: lanes-edit.sh, lanes, lanes-index and tests/test_unsearchable_control_root_291.py. Its one other path, docs/README-lanes.md (+25 -8), belongs to the spec leg and is not in this commit. lookup_blocked, new, tells a lookup that a directory this account cannot search refused from an established absence; lane_control_root keeps a blocked rung's own root; lane-state and lane-trees exit 10 instead of 8 there; worktree_rows names such a lane UNREAD so that an inventory sync keeps its rows; and lane-reconcile is indeterminate. The subjects below are quoted from the originals; a bare #N in them is opensoft/openRepoTools#N. Old-to-new receipt (the original's own non-merge commits; 7db059f458a7 and 3e97ae514452 change docs/README-lanes.md alone, so their part is the spec leg's): a431854c42c4 Test that a lane control root nobody could search is never read as a lane with no trees, and that an inventory sync keeps that lane's rows (#291) 02571b4ade4f A lane control root that cannot be searched is read as not read, never as no trees, so an inventory sync keeps the lane's rows (#291) af4d132af4c7 A lane whose inventory could not be read is no longer said to have one that is there, because a blocked lookup never saw whether it was (#291) 124b509eb6f0 Test that a write under a lane control root nobody could look for fails and is filed nowhere else, and that a .lane-state link whose target is gone is unread, never none (#291) b0a631baa46c lane_generation's docstring no longer says that a snapshot it could not read is there, because a blocked lookup never saw whether it was (#291) 7db059f458a7 The README's rung section says that a rung is passed over only where its directory is established absent, and that a rung nobody could look for keeps its root, so its reads are 10 and its writes fail (#291) 468b20e6c411 lane-reconcile no longer says that a trees directory nobody could look for is there, and says instead that whether it is there could not be read, and why (#291) 3af5006f565f The lanes-index marker comment, the second exit-10 header in lanes-edit.sh and the README's unreadable verdict row no longer say that a record nobody could look for is there (#291) a3ea2af1946a Only the #291 cases that set a mode skip as root, so the links that cannot be followed, which set none, are proved there too (#291) 82ac6b742392 lookup_blocked's comment names its limit: a denial that the mode bits do not show still reads as absent (#291) 3e97ae514452 The README's rung paragraph now names what each surface says where a rung could not be looked for, instead of calling every read 10 (#291) Resolutions: none. The patch applied without conflict, at offsets only: lanes +29 lines and lanes-edit.sh +97 to +158 lines, from the two commits below it in this chain. Lane: openRepoTools-4 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideAPIRead the lane-state and lane-trees exit-code and diagnostic changes together with the listing’s UNREAD behavior, since these public results distinguish an unreadable lookup from an established absence.
ImplementationReview the lookup classification and control-root rung selection together: blocked ancestry must remain unknown rather than be treated as an absent rung.
Follow the unreadable snapshot and inventory results through reconciliation and row construction, including the wording that distinguishes a blocked lookup from an unreadable object that was found.
Check the sync hold reason and generation-reader documentation alongside the new behavior so blocked reads retain indexed rows without claiming the object was found.
Check the user-facing footer wording associated with the revised unreadability distinction.
TestsReview the regression suite’s blocked-root, blocked-link, write-routing, inventory-sync, and neighboring unreadable-versus-absent cases against the intended exit codes and listing behavior.
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
|
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 6 days and 4 hours by commenting |



What this is
This is the T017 continuation, in this leg (opensoft/openRepoTools-code), of the
opensoft/openRepoToolsbranchfix/control-root-unsearchable-291at3e97ae514452, for opensoft/openRepoTools#291 (part of opensoft/openRepoTools#186). The original tree and branch stay as the archived original (lane 4's decision, opensoft/openRepoTools#186 comment 6548343086). One commit here,ca9b847f678e, carries it.It carries that branch's own change against
50d05d4, with its merge of main50d05d4(457b73b) left out, for the code leg's paths:lanes-edit.sh(+157 −23),lanes-index(+5 −5, comments and one hold reason),lanes(+1 −1, one footer line) and the newtests/test_unsearchable_control_root_291.py(+442). Its one other path,docs/README-lanes.md(+25 −8), is the spec leg's, in the paired PR.lane-startandlane-handoffhave no diff.What was wrong and what changed
What was wrong
A lane's trees and its lifecycle snapshot sit under its control root. Where this account may not search a folder on the way to that root, the shell's
-d,-e,-rand-Ltests answer false exactly as they do for a name that is gone, so the lane read as one with no trees and no snapshot:worktrees --allleft it out, an inventory sync deleted its rows,lane-treesandlane-statesaid 8, andlane-reconcilesaidno-state.Found by the independent review of opensoft/openRepoTools#289 as its SHOULD-4. It is in the family of opensoft/openRepoTools#173's finding, a read that failed taken for an empty one (R22, Amendment 7(d)), and it is at main before opensoft/openRepoTools#289 too.
A lane's trees and its lifecycle snapshot are under its control root,
<parent of its dir>/.lane-state/<lane>, or$PROJECTS_ROOT/.lane-state/<lane>for a lane whosedirhas no parent here.[ -d ],[ -e ],[ -r ]and[ -L ]are all false for a path under a directory this account may not search: stat fails there with EACCES exactly as it fails with ENOENT for a name that is gone, and none of the shell's tests tells the two apart. So with a lane's.lane-stateat mode 000 (the review's probe P5):worktrees --allleft the lane out, andlanes-index sync --source inventorywrote0 upserted, 1 removed: the lane's indexed rows were deleted. Unlike A lane listing that is not whole no longer removes indexed worktree rows: the inventory read says PARTIAL, lanes --worktrees prints it NOT WHOLE, and lanes-index writes nothing over it; an older helper's tree with no path stays a tree (#173 item c1, #252 delta review 2) openRepoTools#289's SHOULD-1 windows, this failure persists;lane-treesandlane-stateanswered 8, "this lane recorded no tree" and "this lane has no snapshot", which a launcher answers by going on; andlane-reconcilepronouncedno-state.The rungs that place the root did the same one level up. A recorded
dirwhose parent could not be looked for fell to$PROJECTS_ROOT's root, a different place where nothing of the lane's is, and a$PROJECTS_ROOTthat could not be looked for left the lane no root at all (8).What changed
All in
lanes-edit.shunless said.lookup_blocked, new, is called on a path the caller found not there. It walks up to the deepest name that is there. A directory this process can search has answered for the name below it, and a name that is not a directory has no name below it: the absence is established (1). A directory that cannot be searched has not answered (0), and nor has a link that cannot be followed (0), whose target is gone or sits under a directory that cannot be searched, which stat cannot tell apart. It tests-x, not-r, because a lookup needs search permission alone.LOOKUP_BLOCKED_WHYsays where it stopped.lane_control_root): a rung is passed over only where its directory is established absent. A recordeddirwhose parent could not be looked for keeps its own root, and so does a$PROJECTS_ROOTthat could not be looked for.lane_state_readandlane_trees_listanswer 10, not 8, where the snapshot ortreescould not be looked for.lane-stateandlane-treesexit 10 and say that whether the record is there could not be read, and why ("could not be looked for, because the directory X cannot be searched", or "X is a symbolic link that cannot be followed"). They never say that it is there.worktree_rows). A lane the listing named whosetreescould not be looked for is its ownUNREADline, solanes-index syncholds that lane's rows and removes none. It isUNREADand notPARTIALbecause in every one of these cases the listing named the lane and only its inventory is unread, which is whatUNREADhas meant since Derived index: a worktrees table mirroring every lane's #97 inventory under inventory:<Workstation>, nudged by every inventory write, and lanes --worktrees to read it with or without --index openRepoTools#171.PARTIALis for a lane the listing could not name, and the indexer refuses the whole read over it, so one lane's permissions would freeze every lane's rows. A lane with a tree record that could not be read isUNREADbeside that record's own row. A sync used to take the record's row, keyed by its id where its path would be, for a new tree, and remove the row the record had last given.lane-treeskeeps exit 0 and the<unreadable>row, which the handoff reads per record (The handoff's tree inventory names a record it could not read whatever its schema, and a leg directory it cannot search is never read as absent (#246 follow-up, before the cutover) openRepoTools#255).lane-reconcilepronouncesindeterminate, with a reason that says that whether the snapshot, or thetreesdirectory, is there could not be read, and why. It no longer says that atreesnobody could look for is there. Atreesdirectory that is there and cannot be listed keeps its own wording (The lifecycle's remaining fail-open reads: an unreadable register becomes 'no holder', an unreadable sidecar becomes no tree, and two git reads become fabricated values openRepoTools#113, Findings deferred from #97: fetch-first reads, the live-swap takeover, a hand-written RESUMED, and the five of the 8c5958f round openRepoTools#157).set-lane-stateandset-lane-treecannot make their directory. Where the recordeddir's parent could not be looked for, they used to write under$PROJECTS_ROOT's root, where nothing of the lane's is: the lane's real records were hidden while the block lasted, and what was written stayed stranded when it lifted.lanesfooter (+1 −1),lanes-index's hold reason (+5 −5, with its comments and thelane_generationdocstring) and the second exit-10 header inlanes-edit.sh. The manual's matching text is the paired spec PR's.Unchanged: an absence that was actually looked for is still 8, and a lane with no trees still holds nothing in a sync.
Brett Heap's word of 2026-10-21, logged at 15:07:36Z ("yes to all as recommended"), on the writer's four questions:
lane-treeskeeps exit 0 for an unreadable record (the row is the signal, opensoft/openRepoTools#255 and a test pin exit 0); the wording "is there and could not be read" is fixed on this branch, inlanes-indexand thelanesfooter (at 15:41:21Z he addedlane_generation's docstring); a malformed record with nopath:line goes to a separate v1.2.0 issue, opensoft/openRepoTools#302; and two deliberate behaviour changes stay, with two small tests: a write under a blocked rung fails and files nothing under$PROJECTS_ROOT's root, and a.lane-statelink whose target is gone is exit 10.Not in this PR
lane_state_renameis a write that does not: it moves nothing and says nothing. That, andset-lane-state's fence reading a snapshot it cannot look for as none (so--expect RUNNINGexits 7 when no act got there first), are the older problems filed as A control root nobody can search reads as an empty one in two writers: rename-lane leaves the lifecycle records behind without a word, and set-lane-state's fence says 7 for a race that never happened (from #291) openRepoTools#309 on Brett Heap's word of 2026-10-22.path:line still swaps the tree's row in the inventory index for one keyed by the record's id: A tree record with no path: line still swaps the tree's row in the inventory index for a row keyed by the record's id: the loss #291's part F fixes for an unreadable record is open for a malformed one openRepoTools#302, the next PR of this chain after this one.lookup_blockedreads mode bits, so a denial they do not show (macOS TCC under~/Documents, a Linux security module, a FUSE mount withoutallow_other) still reads as absent. Its comment names the limit.Old to new: the receipt
The original's own non-merge commits in
opensoft/openRepoTools(50d05d4..3e97ae5), 11 in all, are carried by one commit in this leg,ca9b847f678e. The list is the commit's own body, and it equals the original's non-merge commits read from the chain'srefs/root/ref (checked).The subjects are the original's, byte for byte; a bare
(#N)in them names the root issue.Resolutions
None. The patch applied without conflict, at offsets only:
lanes+29 lines andlanes-edit.sh+97 to +158 lines, from the two commits below it in this chain.The pairing
The manual's part of this change (
docs/README-lanes.md, +25 −8) is the commit0b6f2a0be00aonfix/control-root-unsearchable-291in opensoft/openRepoTools-spec, its own PR, opensoft/openRepoTools-spec#8 (basefix/subfield-misreads-301). The code leg's checks read the manual throughOPENREPOTOOLS_SPEC_ROOT:tests/test_repo_hygiene.pyreadsdocs/README-lanes.md(call sites at lines 1373, 1404, 1423, 3139 and 3572 of the code leg'smain; 1404 is the tuple of surfaces that a loop reads throughdoc()) andtests/test_test_roots.pypins which manual a run reads. The code leg's own CI (OPENREPOTOOLS_COMPOSED: '0', no spec leg beside the checkout) skips those checks, so they bind only in a composed run or with the variable set to a spec clone. A search of the manual lines this change touches for the strings those checks pin (the rung-count heading, the two settlement quotes,every lane on this workstation, the exit-code section's two markers andlane-end <lane> --retire) finds none: read, not run. So the two PRs are one change in two repositories, and they land together, as a pair. The code PR goes first, so that the manual never describes behaviour the code lacks: this PR first, then its spec PR, opensoft/openRepoTools-spec#8. The state that order passes through, the code chain's tip beside the spec leg'smain, passed-k test_repo_hygienein the independent review (run (e) under Tests: 223 passed, 14 skipped; that run read the chains' tips, not this commit alone). The other order's state, the spec tip beside the code leg'smain, was not run. I found no check that turns either order red; that is a reading, not a run.Tests
Run by the CODE writer, in a sandbox clone, with
env -i(HOME, XDG_* and TMPDIR inside the sandbox),OPENREPOTOOLS_CODE_ROOTset andOPENREPOTOOLS_COMPOSED=0, as the leg's CI sets it. Each targeted run read this branch's own commit in its own worktree; that commit was6ebbac8ff929before the message amend, and the amend kept its tree, so the results stand forca9b847f678e.bash -nonlanes-edit.sh,laneandlanes, at every commit of the chainpy_compileon every changed.pyfile and onlanes-indexrun.sh -k test_unsearchable_control_root_291shellcheck, over the whole chain (mainto the tip): errors 0 to 0 and warnings 108 to 108 (lane14 to 14,lanes8 to 8); notes up by 3, 2 and 2, all SC1003 on the literal'\'patterns of opensoft/openRepoTools#301's commit.The pair runs below are of the two chains' tips, so they cover this commit with every later commit of its chain; apart from the targeted run above, no run was made at this commit alone. They are lane 4's independent paired review, 2026-10-31 (the letters are the review report's own), with
OPENREPOTOOLS_SPEC_ROOTset to the spec clone shown,env -i,TMPDIR=/tmpand a 54-characterXDG_STATE_HOME.-k test_repo_hygiene5f2995d43893+ spec tip8104f2cf965c-k test_repo_hygiene, baselinemain2a057a6+ specmain9f8b21a-k lane_helpers-k test_repo_hygiene5f2995d43893+ specmain9f8b21aThe pair runs read the chains' tips as they were before the message amend (code
5f2995d43893, spec8104f2cf965c). The amend kept every tree (git difffrom each old commit to its new one is empty), so the runs stand for the amended tips, code2c15aa6613c3and spec430ea20784ac. No failure is new at the tips. Run (d), the manual-reading tests at each branch, was skipped under the review's brief, because none of the five branches' test files reads the manual.The CODE writer's first run of
-k lane_helpersat the chain's tip, with a 212-character run root, gave 5 FAILs in the shell suite (4870 ok, 5 FAIL, 3 skipped), becauserc_tree_idintests/test_lane_helpers.shlacks the 180-character cap thattree_id_forinlanes-edit.shhas, so at that length the two names differ and the planted records are never read; the chain changes neither, and run (c) above, with the 54-character state root, gave 0 FAIL.Not run: the suite at the leg's
mainfor comparison, bash 3.2, macOS and the assembly's composed check; the submodule was not initialized, so 6 tests skip.Test files this commit adds or changes:
tests/test_unsearchable_control_root_291.py, new (+442): 10 cases at the root head, ontest_lanes_index's estate. Five layouts, one case each (the lane's.lane-stateat mode 000; its control root readable and not searchable; a directory above the recordeddir's parent at 000, rung 2; a directory above$PROJECTS_ROOTat 000, rung 3; a.lane-statethat is a link into a directory that cannot be searched), in each of which a sync writes0 removedand keeps the rows,worktrees --allhas oneUNREADline and noPARTIAL,lane-treesandlane-stateexit 10, andlane-reconcileisindeterminate. Then: a write under a root nobody could look for fails and files nothing under$PROJECTS_ROOT's root; a.lane-statelink whose target is gone is 10 and, once the target is back, 0 and 8; atreeslink that cannot be followed, with the snapshot beside it read; atreesdirectory that cannot be searched, which passes on main; and a tree record that cannot be read. Only the cases that set a mode skip as root.The original's own record, from the root repository, at the root heads and not repeated in this leg: red on main's code (the first seven cases
6 failed, 1 passed, the two ruled cases2 failed, the reconcile-reason case1 failed);291 passedfortest_unsearchable_control_root_291 or test_lifecycle_fail_open_113_114_115 or test_repo_hygieneat82ac6b7; seven mutants of the fix, each killed, and the review's opposite one (the helper's-xtest removed), which fails every case.Release notes (v1.2.0)
Not established: whether lane 3 accepts this line, which it has not seen. No release-notes line was ever recorded for this PR; the line below was written from the PR's own text.
A lane's control root, or a directory above it, that this account cannot search is read as not read, not as no trees:
lane-stateandlane-treesexit 10 instead of 8 and say why,worktreesnames the laneUNREADso thatlanes-index synckeeps its rows,lane-reconcileisindeterminate, and a write under such a root fails instead of landing under$PROJECTS_ROOT. (opensoft/openRepoTools#291)The line is here, in the code PR of the pair, and only here: the paired spec PR, opensoft/openRepoTools-spec#8, repeats none.
Review
READY, lane 4's independent paired review of both chains, 2026-10-31. It found no MUST and no SHOULD against the commits, and three NITs. Report sha256:
4ac9e5441affc6aeac48e6d73419580f6786440dc0e5dd32722cc40b8070a682. The review read the chains before the message amend (tips5f2995d43893and8104f2cf965c, the same trees as the amended tips).lanes-indexlines 1904 to 1905, docstring only:lane_generation's docstring reads as the full list oflane-state's 10s, but since A lane control root that cannot be searched reads as "no trees", so an inventory sync removes its rows (from #289's review) openRepoTools#291 there is a third, a blocked lookup; optional, and the same text would arise when the root branches land.rc_tree_idlacks the 180-character cap, so the shell suite fails when the run root is longer than about 111 characters, and the shell suite does not setXDG_STATE_HOME, so run (c) left two files outside its run root.The original's recorded review, from the root repository (no lane 3 review comment exists for it): an independent review of
124b509, by an Opus reviewer that wrote none of the code, is LAND, with no MUST. It probed 23 lookup cases and 12 rung cases, identical under bash 3.2.57 and 5.2.21, and read every caller (lane-handoff,lane-start,lane-worktrees,lanes-index,lanes). Its findings were all fixed: SHOULD-1, the manual's rung section (7db059f, then3e97ae5, both manual-only, which are the paired spec PR's); NIT-1,lane_reconcilesaying that atreeslink nobody could follow is there (468b20e); NIT-2, the stale wording in alanes-indexcomment, the second exit-10 header and the manual'sunreadablerow (3af5006); NIT-3, the file-wide root skip (a3ea2af); and NIT-4,lookup_blockedreading mode bits (82ac6b7, the comment). The fix round was checked by a second read. The root's CI run 39642710526 at3e97ae5was green on all four jobs.Landing
It lands only on Brett Heap's word, PR by PR; nothing in this text is a landing.
fix/subfield-misreads-301(the code PR for Three ways a stored lane sub-field is misread: a TAB in a recorded dir drops the lane from worktrees --all, payload_subfield expands backslash escapes through awk -v, and request-handoff writes its by host sub-field unquoted openRepoTools#301, T017: A dir with a TAB or a backslash is listed, indexed and reached as recorded, a stored sub-field reads back as written, and request-handoff refuses a host or container the fence rejects (opensoft/openRepoTools#301) (part of opensoft/openRepoTools#186) #4), which lands before it. The original is independent of Three ways a stored lane sub-field is misread: a TAB in a recorded dir drops the lane from worktrees --all, payload_subfield expands backslash escapes through awk -v, and request-handoff writes its by host sub-field unquoted openRepoTools#301, and its patch applied here without conflict, at offsets only:lanes+29 lines andlanes-edit.sh+97 to +158 lines, from the two commits below it in this chain.fix/subfield-misreads-301, the branch of T017: A dir with a TAB or a backslash is listed, indexed and reached as recorded, a stored sub-field reads back as written, and request-handoff refuses a host or container the fence rejects (opensoft/openRepoTools#301) (part of opensoft/openRepoTools#186) #4.mainshowsMerge pull request #1). A merge commit keeps this commit's message and its receipt byte for byte, and the stacked chain lands as built, each PR after the one its base names. A PR stacked on this one is retargeted tomainbefore this PR's branch is deleted: deleting a merged base branch first closes the dependent PR.33ce26dinto the branch at open", with its push and the PR's own CI record:33ce26dis the root's frozenmain, and this leg's ownmainis the base here (the head is the commit above, with nothing merged in).[REFRESH AT PR TIME ...]and[REFRESH AT LAND TIME ...]brackets, which name the root's own CI runs and the root landing; this leg's own checks at open are recorded when the PR is opened (see Tests).Closes #Nline and itsgh pr merge --squash --match-head-commit --body-filesteps, with theland-ort4.shandland-ort3.shnotes: the commit's own message stands in, byte for byte, andCloses #Nbecomes "part of", with no closing keyword.lanes-edit.sh, equal at the base and at the head, and a call-site diff that prints nothing), on git objects only. The md5 isb0809a1e84722f1f518941b0063c926d(65 lines), lane 3's own recorded value, at the leg'smain2a057a6d84e5and at every commit of the chain, this commitca9b847f678eamong them. The call-site diff from this PR's base (the commita04c937f1b96of Three ways a stored lane sub-field is misread: a TAB in a recorded dir drops the lane from worktrees --all, payload_subfield expands backslash escapes through awk -v, and request-handoff writes its by host sub-field unquoted openRepoTools#301) to this commit prints no line, out of 180 changed lines oflanes-edit.sh, so the check is not vacuous; frommainto this commit it prints none either, out of 346. The term holds.Lane: openRepoTools-4
Claim: opensoft/openRepoTools#291 (comment 6313861277)
🤖 Generated with Claude Code
Summary by Sourcery
Preserve lane state and inventory data when control-root lookups are blocked, reporting inaccessible reads as indeterminate instead of treating them as empty.
New Features:
Bug Fixes:
Enhancements:
Tests: