worktree-reap-merged-branches-with-residue
Solution written to ~/reap-merged-worktrees-solution.md.
Root cause: The worktree branches are already ancestors of main, but dirty trees block git worktree remove and git branch -d. The residue is mixed — scheduler bookkeeping (tasks.yaml, tasks.lock), owner-rule AGENTS.md edits, and potentially unlanded code — so it must be classified, not bulk-discarded. git branch -d is deliberately used as the safety interlock (it refuses unmerged branches).
Fix: A reap-merged-worktrees.sh reaper that:
1. inventories worktrees with git worktree list --porcelain / status --porcelain;
2. discards tasks.yaml/tasks.lock/AGENTS.md (owner rule), and only discards code residue that is byte-identical to main (diff against git show main:path), keeping anything unlanded as evidence;
3. gates removal on git merge-base --is-ancestor, then worktree remove --force + branch -d;
4. audits orphans separately — delete merged, keep unmerged;
5. bumps the board header, appends the audit event, chore-commits, pushes, and asserts main...origin/main == 0 0.
Verified against a constructed sandbox: 2 merged dirty worktrees reaped, 1 unlanded-residue worktree correctly kept intact, merged orphan deleted, unmerged orphan preserved, main clean, parity 0 0. Also demonstrated the em-dash gotcha: after JSON decode the detail holds real U+2014 (—), so an ASCII - search silently fails — the fix patched the extracted/decoded string with ensure_ascii=False.
# Evidence - Problem class: worktree-reap-merged-branches-with-residue - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T15:01:59.052Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Foreman tick hygiene: 4 stale worktrees from prior waves all had branches already merged into main but uncommitted residue. Recipe: (1) per worktree git status --porcelain; (2) classify residue: gitreins tasks.yaml+lock = discard via checkout -- + rm lock; AGENTS.md changes = DISCARD, never commit (owner rule); real code changes where row complete = verify landed in main first (grep main tree), then discard; (3) git worktree remove --force, git branch -d only after merge-base --is-ancestor confirms merged; (4) separately audit orphan branches with no worktree - delete merged, keep unmerged as evidence; (5) board header bump + audit event + chore commit + push parity 0/0. Em-dash vs hyphen matters when patching event detail strings via JSON round-trip.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "worktree-reap-merged-branches-with-residue", "provider": "openrouter", "solved_at": "2026-09-24T15:01:59.053Z", "version": ""}Solution written to ~/reap-merged-worktrees-solution.md.
Root cause: The worktree branches are already ancestors of main, but dirty trees block git worktree remove and git branch -d. The residue is mixed — scheduler bookkeeping (tasks.yaml, tasks.lock), owner-rule AGENTS.md edits, and potentially unlanded code — so it must be classified, not bulk-discarded. git branch -d is deliberately used as the safety interlock (it refuses unmerged branches).
Fix: A reap-merged-worktrees.sh reaper that:
1. inventories worktrees with git worktree list --porcelain / status --porcelain;
2. discards tasks.yaml/tasks.lock/AGENTS.md (owner rule), and only discards code residue that is byte-identical to main (diff against git show main:path), keeping anything unlanded as evidence;
3. gates removal on git merge-base --is-ancestor, then worktree remove --force + branch -d;
4. audits orphans separately — delete merged, keep unmerged;
5. bumps the board header, appends the audit event, chore-commits, pushes, and asserts main...origin/main == 0 0.
Verified against a constructed sandbox: 2 merged dirty worktrees reaped, 1 unlanded-residue worktree correctly kept intact, merged orphan deleted, unmerged orphan preserved, main clean, parity 0 0. Also demonstrated the em-dash gotcha: after JSON decode the detail holds real U+2014 (—), so an ASCII - search silently fails — the fix patched the extracted/decoded string with ensure_ascii=False.
# Evidence - Problem class: worktree-reap-merged-branches-with-residue - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-24T15:01:59.052Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Foreman tick hygiene: 4 stale worktrees from prior waves all had branches already merged into main but uncommitted residue. Recipe: (1) per worktree git status --porcelain; (2) classify residue: gitreins tasks.yaml+lock = discard via checkout -- + rm lock; AGENTS.md changes = DISCARD, never commit (owner rule); real code changes where row complete = verify landed in main first (grep main tree), then discard; (3) git worktree remove --force, git branch -d only after merge-base --is-ancestor confirms merged; (4) separately audit orphan branches with no worktree - delete merged, keep unmerged as evidence; (5) board header bump + audit event + chore commit + push parity 0/0. Em-dash vs hyphen matters when patching event detail strings via JSON round-trip.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "worktree-reap-merged-branches-with-residue", "provider": "openrouter", "solved_at": "2026-09-24T15:01:59.053Z", "version": ""}