Two places where the recovery-facing output claims more than it knows.
1. The READY line counts the trees that were POLLED, not the sidecars that were WRITTEN
lane-handoff's poll_writer increments before either the observation or the inventory write is known to have worked:
writer_count=$((writer_count + 1))
...
if [ -n "$lc_on" ] && [ "$pw_orc" = 0 ]; then
LANES_NO_FETCH=1 "$LANES_EDIT" set-lane-tree "$lane" "$pw_d" \
... >/dev/null 2>&1 ||
note "the worktree inventory entry for $pw_d could not be written — ..."
fi
and the READY line spends that count on a different sentence:
say " inventory: $writer_count worktree(s) recorded — lanes-edit.sh lane-trees $lane"
A tree whose observation refused (lane-tree-now exit 1, a checkout git cannot be read through) files no sidecar at all, and a tree whose set-lane-tree was refused (the fence, an unknown schema) files none either — both are still counted as recorded. The note is on stderr among the handoff's other notes; the READY block is the summary a person reads when they are recovering, and it says the inventory holds N entries that lane-trees does not have.
2. A stale registration is suppressed whenever a sidecar names the same path
lane_reconcile seeds the seen-set from every sidecar before the registration sweep:
[ -n "${lrc_p:-}" ] && lrc_seen="$lrc_seen $lrc_p $(lane_real_path "$lrc_p") "
and the sweep skips anything already in it:
case "$lrc_seen" in *" $lrc_wp "*|*" $lrc_wpr "*) continue ;; esac
if [ -d "$lrc_wp" ]; then
... unmanaged ...
else
... stale-registration ... `git -C %s worktree prune` is a person's act
So a tree that WAS inventoried and whose directory has since been removed is reported only as missing or possible-loss — the classification the sidecar branch gives it — and never as stale-registration, because the sidecar named the path first. git still holds the registration, the report omits the git worktree prune remedy, and the estate's resume refuses that leg for a stale registration the recovery report said nothing about.
These two pull in opposite directions and the tension is the reason this is filed rather than patched: the seen-set exists so that ONE tree is named ONCE (612ba5c, the symlinked-path defect where every tree was reported twice), and the registered-but-gone condition wants to be said about a path a sidecar has already spoken for.
Why this is filed rather than fixed on #97
Copilot review rounds are capped at two per pull request (Brett Heap's ruling of 2026-09-16); #97 is at round six and took only the two safety holes it could not land with. Neither of these misreports work as clean: the first over-counts a summary line whose detail is a command away, and the second names a real tree under the wrong heading.
What's open
For the count: track the inventory writes that succeeded and say N of M polled, so the number and lane-trees agree. For the registration: decide the registered-but-gone case BEFORE the path is deduplicated, and let one tree carry both facts on one row (possible-loss AND a live registration, with the prune named) rather than choosing between two rows and one heading.
Filed from Copilot round 6 on #97 — #97 (comment) and #97 (comment) — and named in openspec/changes/add-crash-consistent-lane-worktree-recovery/tasks.md section 7.
Two places where the recovery-facing output claims more than it knows.
1. The READY line counts the trees that were POLLED, not the sidecars that were WRITTEN
lane-handoff'spoll_writerincrements before either the observation or the inventory write is known to have worked:and the READY line spends that count on a different sentence:
say " inventory: $writer_count worktree(s) recorded — lanes-edit.sh lane-trees $lane"A tree whose observation refused (
lane-tree-nowexit 1, a checkout git cannot be read through) files no sidecar at all, and a tree whoseset-lane-treewas refused (the fence, an unknown schema) files none either — both are still counted as recorded. Thenoteis on stderr among the handoff's other notes; the READY block is the summary a person reads when they are recovering, and it says the inventory holds N entries thatlane-treesdoes not have.2. A stale registration is suppressed whenever a sidecar names the same path
lane_reconcileseeds the seen-set from every sidecar before the registration sweep:and the sweep skips anything already in it:
So a tree that WAS inventoried and whose directory has since been removed is reported only as
missingorpossible-loss— the classification the sidecar branch gives it — and never asstale-registration, because the sidecar named the path first. git still holds the registration, the report omits thegit worktree pruneremedy, and the estate'sresumerefuses that leg for a stale registration the recovery report said nothing about.These two pull in opposite directions and the tension is the reason this is filed rather than patched: the seen-set exists so that ONE tree is named ONCE (
612ba5c, the symlinked-path defect where every tree was reported twice), and the registered-but-gone condition wants to be said about a path a sidecar has already spoken for.Why this is filed rather than fixed on #97
Copilot review rounds are capped at two per pull request (Brett Heap's ruling of 2026-09-16); #97 is at round six and took only the two safety holes it could not land with. Neither of these misreports work as clean: the first over-counts a summary line whose detail is a command away, and the second names a real tree under the wrong heading.
What's open
For the count: track the inventory writes that succeeded and say N of M polled, so the number and
lane-treesagree. For the registration: decide the registered-but-gone case BEFORE the path is deduplicated, and let one tree carry both facts on one row (possible-lossAND a live registration, with the prune named) rather than choosing between two rows and one heading.Filed from Copilot round 6 on #97 — #97 (comment) and #97 (comment) — and named in
openspec/changes/add-crash-consistent-lane-worktree-recovery/tasks.mdsection 7.