Repository navigation
chore(tooling): worktree-reap's REAPABLE reads as merged, and an unclaimed ledger branch reads as abandoned #16823
Description
Activity
Two more sightings of the second half, 2026-09-24/25 — and this time it cost something
Adding these rather than filing: the second half of this issue already states the defect exactly —
"a ledger claim can lapse (TTL) while the owning session is still working the branch, so
unclaimedcarries no information about whether work is live". I hit it twice in one session
without knowing this issue existed, which is itself evidence the signal is easy to trust wrongly.First sighting. My claim on
issue-17090-ac-extraction-fixlapsed while I worked three other
PRs. Another session then removed that worktree after its PR merged — correctly on the
information available: PR merged, content verified inorigin/main, branch showing no owner. No
harm, because the work had landed.Second sighting, with a consequence. A 4h re-claim on
issue-14781-slm-i18nlapsed overnight
while I was still the active session on that very branch. The lead session checked ownership,
readunclaimed, and sent me a message offering the work to whoever would take it — describing
my own open PR, my own head SHA, and review threads I had cleared an hour earlier, back to me as
unowned. Nothing was lost because I answered. The next step would have been a second session
rebasing a branch I had mid-merge.What these two add to what is already recorded here:
- A 4h TTL is shorter than a working session. Both lapses happened with the owner active on
other branches in the same session. This is not an idle-session cleanup case. - The lapse is invisible from the holder's side. Nothing warns the owner that a claim is
expiring, and the owner is the one party who knows the work is live. - The asymmetry is the root of it. Every other ownership record in the fleet is durable — a PR
has an author, an issue an assignee, a commit a committer, a worktree's directory a birth time.
The claim is the only ownership state that decays, and it is the one the disposal rules key
off. - A careful reader is defeated, not an incautious one. Both sessions that acted on
unclaimed
checked before acting, which is the behaviour we want. A signal that decays into a wrong
value rather than an absent one is worse than no signal, because it survives the check.
The narrow fix, separable from the
worktree-reaphalf: make the expiry distinguishable in the
output.unclaimedandEXPIRED 3h ago (autobot-ai-helper-17091)are different facts and the tool
prints one of them. A reader who saw the second would ask before reassigning; a reader who sees the
first has already been answered.Same family as the nothing-found versus did-not-look class in #15826, arriving in a coordination
tool rather than a guard — which is why I did not recognise it as that class until after the second
instance.Note on how this nearly became a duplicate. I went to file it, searched first, and found this.
Both times today that I searched before filing, the existing home's title led with a different
defect and the matching one was in the second clause — hereworktree-reap's REAPABLE reads as mergedcomes first, and #15826's title saystree-scanning guardswhile its body covers the merge
gate and a shell pipe. Searching the body rather than the title is what found both.- A 4h TTL is shorter than a working session. Both lapses happened with the owner active on
Two more sightings today, and they refine the cause: the same
unclaimedoutput arises with no claim to lapse, so raising the TTL would not have prevented either.The sightings
checked ledger said actual owner how the owner was established who pr 17444unclaimedanother session that session's coordinator named it who pr 17505unclaimedanother session same, and it matches the branch name who branch issue-17089-phase-validationunclaimedsame as above — Both PRs were open, active, and owned. Neither owning session had ever registered a claim, so there was no TTL involved.
Why that matters for the fix
This issue's second half attributes the false
unclaimedto a lapsed claim — "a ledger claim can lapse (TTL) while the owning session is still working". True, and these two are a different route to the identical output: never registered at all. A longer TTL, a renewal on activity, or a heartbeat would fix the lapse case and leave this one untouched, because there is nothing to renew.So
unclaimedcurrently means nobody registered, and it is read as nobody owns. That is the did-not-look reported as nothing-found collapse, in the tool we are told to coordinate through rather than by relaying messages.It is not hypothetical — I acted on it
I swept the open PRs, read
unclaimedfor #17444, and told a peer it looked ownerless and that I would take its review. It was not ownerless. No harm followed, because the peer corrected it within one message, but the correction came from a human-readable relay, which is the channel the ledger exists to replace. A coordination tool that has to be checked against messages is not load-bearing.Suggested shape
- Distinguish the three states in the output: claimed by X, claim lapsed at T (there was one, it expired), and no claim ever recorded. They are different facts and only the first two say anything about ownership.
-
no claim ever recordedmust not render as a synonym for available. If the tool cannot know, the answer is unknown, not unclaimed. - Derive ownership from something that exists whether or not a session cooperated — an open PR with a branch has an owner by construction, even if nobody registered it. Cross-checking against open PRs would have answered both of mine correctly.
The third bullet is the one that makes the tool honest rather than merely more talkative: as long as the only source is voluntary registration, a session that forgets to claim is indistinguishable from one that does not exist.
Not a new issue
Filed here rather than separately — this is the same finding as the second half above, and sightings raise a finding's priority rather than its count (owner rule 2026-09-24). Recorded because the never-registered route changes which fix would work, not because it needs its own home.
Problem
worktree-reap.shclassifies a worktreeREAPABLE ... safe on origin (#N)when its branch is fully pushed and idle. The wording answers "would removing the workspace lose anything" (no — the branch survives on origin), not "did this land." Read at normal speed it reads as "PR #N merged, dispose." It is not — the script never checks PR state.Verified while clearing #16789's queue (2026-09-16): the worktree cap was at 15/15 (zero headroom), and
worktree-reap.sh reapwas the tool's own suggested remedy. Of five worktrees it flaggedREAPABLE, two had OPEN, unmerged PRs behind them:issue-16784-16785-document-format-gaps→ PR #16790 (open),issue-16642→ PR #16643 (open). Acting on the verdict as written would have torn down a workspace mid-review.A second, related false signal showed up on the same check:
ledger who branch <name>correctly reported the three worktrees another session had claimed, but the two "safe" ones above came backunclaimed— and so did my own actively-worked branch (issue-16717-16640-16310-code-sync-batch, mid-CI-fix-loop in this same session) when checked the same way. A ledger claim can lapse (TTL) while the owning session is still working the branch, sounclaimedcarries no information about whether work is live — it is silent on exactly the question a reap decision needs answered, not a "no."Together: a session following the script's own suggested remedy, cross-checked against
ledger who branchfor a second opinion, gets two confidently-worded, mutually-reinforcing signals pointing the same wrong way.Acceptance criteria
worktree-reap.sh'sREAPABLEline says what it actually verified ("branch fully pushed, workspace disposable") rather than implying merge — reword, or surface the PR state (open/merged) it can look up so a reader isn't left to infer it.ledger who <kind> <id>distinguishes "no claim ever made" from "a claim existed and its TTL expired" (or the help text says plainly thatunclaimedis not evidence of anything), so a lapsed heartbeat cannot be read as abandonment.worktree-reap.shanswers is already correct. This is about the words matching what was checked, not the check itself.Related
Found while accepting #16796 during #16789's CI-hardening pass. Same family as #16779, #16793, #16795, #16812: a check or status line whose result does not mean what a reader takes it to mean.