Skip to content

chore(tooling): worktree-reap's REAPABLE reads as merged, and an unclaimed ledger branch reads as abandoned #16823

Description

@mrveiss

Problem

worktree-reap.sh classifies a worktree REAPABLE ... 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 reap was the tool's own suggested remedy. Of five worktrees it flagged REAPABLE, 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 back unclaimed — 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, so unclaimed carries 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 branch for a second opinion, gets two confidently-worded, mutually-reinforcing signals pointing the same wrong way.

Acceptance criteria

  • worktree-reap.sh's REAPABLE line 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 that unclaimed is not evidence of anything), so a lapsed heartbeat cannot be read as abandonment.
  • Neither fix changes the underlying safety logic — the "would anything be lost" question worktree-reap.sh answers 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.

Activity

  1. mrveiss commented on Sep 25, 2026

    @mrveiss
    OwnerAuthor

    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
    unclaimed carries 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-fix lapsed 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 in origin/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-i18n lapsed overnight
    while I was still the active session on that very branch. The lead session checked ownership,
    read unclaimed, 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:

    1. 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.
    2. 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.
    3. 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.
    4. 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-reap half: make the expiry distinguishable in the
    output. unclaimed and EXPIRED 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 — here worktree-reap's REAPABLE reads as merged comes first, and #15826's title says tree-scanning guards while its body covers the merge
    gate and a shell pipe. Searching the body rather than the title is what found both.

  2. mrveiss commented on Sep 26, 2026

    @mrveiss
    OwnerAuthor

    Two more sightings today, and they refine the cause: the same unclaimed output 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 17444 unclaimed another session that session's coordinator named it
    who pr 17505 unclaimed another session same, and it matches the branch name
    who branch issue-17089-phase-validation unclaimed same 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 unclaimed to 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 unclaimed currently 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 unclaimed for #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 recorded must 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.

  3. added this to the v0.9.5 - CI and guards milestone on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions