From 933632ad9ef0af8b09da0907d46962f2c419d782 Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Wed, 19 Aug 2026 09:20:57 -0500 Subject: [PATCH 1/2] backlog: file #1294 -- no cleanup path for the worktrees the prune tool must not touch Measured on this clone: 36 registered worktrees, 11 reachable by prune-merged.ps1, 12 under .claude/worktrees and 13 others excluded. 30 percent reachable. The owner removed 12 of the excluded ones by hand the same day, from a list assembled manually, because nothing produces that list. Filed explicitly as NOT a coverage bug. The exclusions are deliberate: that directory is where EnterWorktree relocates live sessions, the tool once removed an occupied worktree and left a directory git no longer recognised, and its header records the resulting bias as fixed and not negotiable. The defect is the missing middle. Today a worktree is either removed by the tool or invisible; nothing says "these look finished, here are the commands, you decide". Proposes a REPORT-ONLY extension rather than a wider fence, because the occupancy fence's own receipt records a 29 percent blind spot in one of its two signals. Automating deletion of the directory live sessions occupy, behind that fence, would trade the property the incident bought. Also records that worktree_gate.ps1's refusal should NOT be relaxed to close this -- the reporter makes the refusal cheap to live with, which is the correct order. parse_items 310/230 -> 311/231, the expected +1/+1/0. --- docs/BACKLOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 1aa9d7a7..91d4ca4f 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -10308,3 +10308,27 @@ _FHIR_ID_RE.fullmatch("abc\n") -> False the fix **Cluster:** Ledger gate / worktree lifecycle. **Priority:** P2. **Verdict:** build. **Severity:** no deployment axis (sec. 0) -- this is developer tooling. The cost is a PR that cannot be landed by anyone and a ledger number burned with no way to reuse it, plus the sweep above being folk knowledge until it is written into the removal path. + +## 1294. no cleanup path exists for the 70 percent of worktrees the prune tool must never touch, so they accumulate until a human removes them by hand + +> 🔢 **Filed 2026-08-19 -- not started. THIS IS NOT "THE PRUNE TOOL HAS A COVERAGE BUG". ITS EXCLUSIONS ARE DELIBERATE, WERE PAID FOR BY AN INCIDENT, AND MUST STAY.** `scripts/worktree/prune-merged.ps1` refuses anything with a `.claude/worktrees/` path segment, anything nested inside another registered worktree, detached trees, Temp scratchpad trees, and the primary. Its header states why: that directory is **"the exact place EnterWorktree relocates a live session to"**, the tool **once removed an occupied worktree** -- deregistering it and then failing to delete the directory, after which every git command in the working session failed -- and the resulting bias is recorded as **"a false SKIP is a minor annoyance, a false PRUNE destroys a session"**. **THE CHANGE: give the excluded population a REPORTING path, not a removal path.** + +> **MEASURED 2026-08-19 on this clone:** +> +> total registered worktrees 36 +> reachable by prune-merged.ps1 11 (- siblings) +> .claude/worktrees/* 12 excluded BY DESIGN -- live sessions live here +> other (C:/mfw*, Temp, detached) 13 excluded +> +> So **30 percent** is reachable. On the same day the owner removed **12** of the excluded ones by hand, from a list assembled manually, because nothing produces that list. + +> **THE ACTUAL DEFECT IS THE ABSENCE OF A SAFE MIDDLE.** Today there are two states: the tool removes it, or nobody knows it exists. There is no artifact that says *"these excluded trees look finished -- here are the commands, you decide"*. That gap is what turns routine hygiene into a manual audit, and a manual audit is what gets skipped until 53 worktrees accumulate. + +> **WHY A REPORTER AND NOT A WIDER FENCE.** The occupancy fence's own receipt states it cannot see **"a session writing into a worktree by absolute path from elsewhere (29% of the writes by primary-seated sessions, measured on this repo)"**, a cwd recorded as UNC or 8.3, a session that never registered, or one that only edits files. The 36h git-metadata veto exists to cover that hole and is a proxy, not a fact. **Automating deletion of the directory where live sessions live, behind a fence with a measured 29 percent blind spot in one of its two signals, trades the exact property the incident bought.** A reporter carries none of that risk because it removes nothing. + +> **SHAPE THAT WOULD BE SAFE:** extend the existing decision table to *evaluate* every registered worktree, and for the excluded classes emit `REPORT-ONLY` rows plus a copy-pasteable command block -- never an `-Apply` path, no `-Name` override, no force. The dirty check, the branch-keeping rule, the orphan ledger and the fence receipt all already exist and would be reused unchanged. **The human stays the actuator for the dangerous population; only the discovery is automated.** + +> **A SECOND, SMALLER FINDING FROM THE SAME PASS.** `git worktree remove` is refused to an agent by `scripts/hooks/worktree_gate.ps1` for every tree but its own, and that refusal names the user as the only actor. That is consistent with the above and should NOT be relaxed to close this item -- the reporter makes the refusal cheap to live with, which is the correct order of operations. + +**Cluster:** Worktree lifecycle / developer tooling. **Priority:** P3. **Verdict:** build. +**Severity:** no deployment axis (sec. 0). The cost is unbounded worktree accumulation, a manual audit nobody schedules, and disk. **Explicitly NOT a licence to widen the prune fence** -- the item is closed by adding reporting, and would be mis-implemented by adding reach. From c3f15e4c9711888e09af6c1358250cdf53e2319f Mon Sep 17 00:00:00 2001 From: wshallwshall Date: Wed, 19 Aug 2026 09:29:37 -0500 Subject: [PATCH 2/2] backlog: record that #1293's route 1 was tested on the live instance and works Owner-directed. Recreated the worktree at the exact path recorded in #1264's alloc claim, resolved PR #397's conflict there, and committed from it. The ledger gate reported Passed on a commit that could not have been made from anywhere else, and #397 went DIRTY to MERGEABLE. Bounded deliberately: this confirms the mechanism (owns() compares a path string, so restoring the path restores the referent) and settles nothing about whether it should be the documented remedy -- it stays adjacent to the rename-workaround CLAUDE.md section 5 forbids. The distinction kept in the item is that it restores the condition the gate tests rather than bypassing the test. Also records two things a repeat must not skip: worktree add -B resets the local branch (here from 91a24061, verified to be an old main commit reachable from many refs before the reset was accepted), and the ledger conflict was resolved against a computed target -- 292/212 base, 293/213 branch, 310/230 main, so only 311/231 is correct, which is what parse_items returned. --- docs/BACKLOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 91d4ca4f..fe12434c 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -10304,6 +10304,10 @@ _FHIR_ID_RE.fullmatch("abc\n") -> False the fix > > **Whichever is chosen, it must not be "widen the gate".** The `--ci` leg already skips ownership by design (`not self.ci and not self.owns(...)`, `ledger_check.py:266` and `:355`), so a green CI is **not** evidence a number was properly allocated -- that hole should not be made bigger. +> **ROUTE 1 WAS TESTED ON THE LIVE INSTANCE, 2026-08-19, AND IT WORKS.** Owner-directed. `git worktree add` at the exact recorded path, on the stranded PR's own branch; resolve there; commit there. **The ledger gate reported `Passed`** on a commit that could not have been made from anywhere else, and PR #397 went `DIRTY` to `MERGEABLE`. **Note what this does and does not settle:** it confirms the mechanism -- `owns()` compares a path string, so restoring the path restores the referent -- and it settles nothing about whether this should be the *documented* remedy, because it is still uncomfortably adjacent to the rename-workaround CLAUDE.md sec. 5 forbids. **The distinction worth preserving: this RESTORES the condition the gate tests rather than bypassing the test.** A reassign flag would be the honest version of the same intent. +> +> Two details a repeat of this must not skip. **`git worktree add -B` resets the local branch**, and here it moved one from `91a24061` -- verify what that ref held before accepting the reset (it was an old `main` commit, reachable from many refs, so nothing was stranded; that was checked, not assumed). And **resolve the ledger conflict against a computed target, never by eye**: merge-base `292/212`, branch `293/213`, `origin/main` `310/230`, so the only correct result was `311/231`, and `parse_items` returning exactly that is what made "keep both sides" a verified claim instead of a hopeful one. + > **THE OPERATIONAL RULE THAT FALLS OUT, AND IT IS WORTH ADOPTING EVEN IF THE FIX IS DEFERRED:** before removing a worktree, check whether it owns any allocation whose item is not yet on `main`. That sweep is cheap -- read each `alloc/*/.json`, match `worktree` against the removal list, and grep `origin/main`'s ledger for the heading. **It was run against the 16 worktrees proposed for removal on 2026-08-19 and returned zero**, with #1264 used as the positive control to prove the sweep can actually detect the class. **Cluster:** Ledger gate / worktree lifecycle. **Priority:** P2. **Verdict:** build.