fix(cb-7suc): the worktree pruner removes finished trees in a repo with submodules or squash merges - #471
Conversation
…th submodules or squash merges Two faults kept every finished tree on a consumer with submodules that squash-merges: git refuses to remove a working tree containing a submodule, whatever its state, so the pruner and the view's tidy kept each one as "git would not remove it"; and a squash-merged branch's commits are never ancestors of main, so when gh could not answer the tree was kept as holding work not on main. The safety library now judges a tree landed when a commit on main names its bead, the fleet's own delivery test, before asking gh; and a clean, landed tree git refuses only for its submodule is deleted directly and its registration pruned, a locked tree still refused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKiLr59caM9f6DxkrsDYR8
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
It performs destructive rm -rf removals and broadens the "landed" heuristic in shared, all-consumer worktree-janitor logic, which warrants final human review.
Review effort: Balanced
Findings: 1
What changed in this PR
This PR fixes bead cb-7suc (P0): the shared worktree-pruning safety logic in scripts/worktree-safety.sh (used by both prune-worktrees.sh and release-bead --worktree) was leaving finished worktrees behind on consumers that squash-merge or use submodules, causing large disk buildup (reported: 80 trees, 442 GB). It addresses two independent faults with one change to the shared file, so both callers benefit.
Changes:
- Adds a network-free "delivered" test:
cerebro_worktree_landednow also treats a tree as landed when a commit onorigin/<base>names the tree's bead (viascripts/bead-delivery.sh), mapping the directory name to candidate bead ids (raw, then minus a-mockupor numeric suffix). - Makes
cerebro_worktree_removedelete a tree thatgit worktree removerefuses solely because it contains a populated submodule (guarded by a newcerebro_worktree_lockedcheck and the presence of.gitmodules), then prunes the registration. - Adds tests for squash-merge landing, suffix-name mapping, an undelivered bead still kept, and submodule-tree removal; updates the pruner paragraph in
docs/agent-workflow.md.
| File | Description |
|---|---|
scripts/worktree-safety.sh |
New cerebro_worktree_bead/cerebro_worktree_delivered/cerebro_worktree_locked helpers; landed gains the delivery-commit test; remove deletes submodule-containing trees git refuses. |
tests/worktree-safety.sh |
Adds coverage for squash-merged/suffixed/undelivered trees and for removing a clean, landed tree containing an initialised submodule. |
docs/agent-workflow.md |
Rewrites the pruner paragraph to describe the bead-name delivery check and direct submodule-tree removal. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| # less the one `assign-bead' adds when a name is taken (`<bead>-2'). Candidates rather than one | ||
| # answer, because a bead id ends in `-<word>' itself (`cb-7suc', `sq-1') and stripping blindly |
There was a problem hiding this comment.
Right: the directory is always the bead, the suffix goes on the branch. The numeric case is dropped; the candidates are the name as it stands, less -mockup, or less -retro (the verifier's retrospective tree). The test now asserts sq-1 yields only itself.
…submodule source has an identity Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKiLr59caM9f6DxkrsDYR8
…ner's default Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GKiLr59caM9f6DxkrsDYR8

Bead cb-7suc (P0 bug). Seen on neser: 80 trees, 442 GB, the pruner removing nothing.
Both faults live in
scripts/worktree-safety.sh, whichprune-worktrees.shand the fleet view's tidy (release-bead --worktree) share, so one fix covers both callers.cerebro_worktree_landednow asks, after ancestry and beforemerged_check, whether a commit on origin/main names the tree's bead, throughscripts/bead-delivery.sh, the same test that calls a bead delivered. It needs no network, so aghthat cannot answer no longer keeps a delivered tree for ever. The tree's name is tried as it stands, then less a-mockupor-2suffix, since a bead id ends in-<word>itself.git worktree removerefuses any tree containing a populated submodule.cerebro_worktree_removenow, on a refusal, deletes a tree that carries.gitmodulesand is not locked directly and prunes the registration; the caller's clean-and-landed tests already hold, and a locked tree is still refused.Tests first in
tests/worktree-safety.sh: a squash-merged bead's tree is landed withghanswering no, the-2and-mockupnames are judged by their bead, an undelivered bead is still kept, and a tree with an initialised submodule that git refuses is removed with its branch and registration gone. Every bash suite green.docs/agent-workflow.md's pruner paragraph says what the pruner now does.🤖 Generated with Claude Code
https://claude.ai/code/session_01GKiLr59caM9f6DxkrsDYR8