Repository navigation
lane-reconcile's on-disk sweep passes over a directory under a lane root whose .git git cannot read: a worktree whose gitdir is gone is never named, where lane-worktrees names it #258
Description
This is left over from PR #252, lane openRepoTools-4's fix for #113, #114 and #115, which lists it as a follow-up. #252's independent review (r1, SHOULD 6) asked for it to be filed so it isn't lost when #113 closes. It is not one of #113's four reads, and #252 does not change it.
Searched first: no open or closed issue names lane-reconcile's on-disk sweep, a worktree whose gitdir is gone, or an unreadable .git under a lane root.
- 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 #113 item 4, which Lifecycle reads that failed are no longer answers, set-lane-tree's fence closes with its operation, and a lane sidecar is valid only when whole #252 fixes, covers the registrations read and a root that cannot be listed.
- lane-worktrees: review findings deferred from #168/#169 (incl. five --yes data-loss paths found after the two-round cap) #170 G5 covers
lane-worktrees' own sweep.
The finding
Lines are lanes-edit.sh at #252's head 3ffefce.
lane_reconcile's on-disk sweep walks the lane's worktree roots (lane_worktree_roots, :13135). It reports as unmanaged every child that is a repository and that no sidecar names. It decides whether a child is a repository by asking git (:13578):
git -C "$lrc_c" rev-parse --git-dir >/dev/null 2>&1 || continueSo a child that git cannot read is passed over as if it held nothing. That turns a failed read into an absence (R22, Amendment 7(d)).
The rule #170 G5 settled is the opposite: a directory with a .git entry of its own IS a repository, however broken. It is applied in three places:
lane-worktrees' sweep (lane-worktrees:2244-2246);- its
no_repository(:4239); lanes-edit.sh'slane_no_repository(:13238), for the lane's own checkout.
So the two readers of one lane's roots disagree.
Which children the sweep misses
Probed with git 2.43.0:
| child | git -C <child> rev-parse --git-dir |
on-disk sweep | lane-worktrees (by :2244-2246) |
|---|---|---|---|
a worktree whose admin directory is gone: a .git file whose gitdir: names a removed <checkout>/.git/worktrees/<id>, under .claude/worktrees or under .lane-worktrees/<lane> |
128, fatal: not a git repository: <gitdir> |
skipped | named (lane-root) |
a .git directory git cannot read (mode 000) under .lane-worktrees/<lane>, outside any repository |
128, fatal: not a git repository (or any of the parent directories): .git |
skipped | named (clone) |
the same under .claude/worktrees, inside the lane's checkout |
0, because git falls back to the checkout's own .git |
named | named |
The first case is not hypothetical. On 2026-10-15, a git worktree prune run from an environment without /workspace emptied a shared checkout's .git/worktrees and orphaned every lane's trees in exactly this way.
Such a tree is in no registration, so git worktree list does not name it. Unless a sidecar records it, the on-disk sweep is the only read that could. The report then never names it. A launcher that reads resumable (a SWAPPED lane with no live holder) goes past a tree that may hold uncommitted or unpushed work.
What would fix it
- Decide what is a tree the way lane-worktrees: review findings deferred from #168/#169 (incl. five --yes data-loss paths found after the two-round cap) #170 G5 does. A child with a
.gitentry of its own ([ -e "$c/.git" ] || [ -L "$c/.git" ]) is a tree, whatever git says. - Report a failed
rev-parseon such a child. It is a tree nobody could read. Give it a row of its own, counted toward recovery, named with git's reason, and never skipped. This is the split Lifecycle reads that failed are no longer answers, set-lane-tree's fence closes with its operation, and a lane sidecar is valid only when whole #252 and T018 use: a per-record failure is a row, and a read that leaves paths unread makes the verdictindeterminate. - Keep a child with no
.gitentry out of the report, as now. - Add a test in
tests/test_lifecycle_fail_open_113_114_115.py's sandbox: a worktree under.claude/worktreeswhose.git/worktrees/<id>was removed. It fails today.
Whose it is
lanes-edit.sh's lifecycle is lane openRepoTools-3's tooling. Amendment 23's act 2 (lane openRepoShape-2) rewrites it, so the fix may belong in that rewrite.
🤖 Generated with Claude Code
Lane: openRepoTools-4
Activity