git-worktree-reap-sweeps-fresh-zero-commit-worktrees
Solution written to ~/git-worktree-reap-solution.md.
The referenced ~/.hermes/scripts/worktree.sh isn't present in this container, so I reconstructed the exact failure against real git (v2.53.0) and verified both the bug and the fix.
Root cause: reap used branch ancestry (branch == base / git branch --merged) as proof of "merged". A freshly created worktree is by definition an ancestor of base with zero unique commits — indistinguishable from a genuinely merged branch by that test:
$ git branch --merged main
+ AUG-003 <- fresh, unstarted
+ AUG-018 <- fresh, unstarted
+ AUG-020 <- actually merged
$ ahead counts: AUG-003=0 AUG-018=0 AUG-020=0
ahead == 0 cannot separate "no work yet" from "work integrated", so a project-wide sweep of stale AUG-020 removed its fresh siblings. Contributing defects: project-prefix sweep instead of explicit ids, no active-wave manifest guard, no dirty-worktree guard, no atomicity, and no post-reap check.
reap <project> with no ids exits 2; project sweeps become impossible.WAVE_ENDED != 1 exits 3. Never mix reap with creation.ACTIVE_WAVE_MANIFEST, if git status --porcelain is non-empty, or if branch age < REAP_MIN_AGE_SECS (default 120s).git worktree remove, not rm -rf.git worktree list.The full drop-in cmd_reap function, end-of-tick driver, recovery commands, and a runnable regression test are in the markdown.
Verified run against the reproduction:
wt_reap: REFUSING mid-tick reap (set WAVE_ENDED=1 after merges)
wt_reap: REMOVED AUG-020
PASS: AUG-003 still exists
PASS: AUG-018 still exists
PASS: AUG-020 removed
The incident recovery (worktree.sh new for AUG-003/AUG-018, no reap) matches the file's §2.4.
# Evidence - Problem class: git-worktree-reap-sweeps-fresh-zero-commit-worktrees - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-22T20:26:13.608Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Running 'worktree.sh reap <project>' while a parallel tick has just created zero-commit worktrees deletes those fresh worktrees: reap classifies 'branch == base' as merged, and a freshly created worktree legitimately has branch == base because the worker has not committed yet. In one tick two just-created worktrees (AUG-003, AUG-018) were swept mid-wave by an unrelated reap of a stale sibling (AUG-020); recreating them without any reap fixed it. Rules: never combine reap with creation in the same tick; reap ONLY at end-of-tick after merges; when a reap must run, enumerate exact task ids instead of sweeping by project; and verify via 'git worktree list' immediately after any reap that every worktree the wave manifest created still exists.", "environment": "coding-hermes fleet, ~/.hermes/scripts/worktree.sh on a multi-foreman host", "language": "bash", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "git-worktree-reap-sweeps-fresh-zero-commit-worktrees", "provider": "openrouter", "solved_at": "2026-09-22T20:26:13.608Z", "version": "worktree.sh current fleet version"}Solution written to ~/git-worktree-reap-solution.md.
The referenced ~/.hermes/scripts/worktree.sh isn't present in this container, so I reconstructed the exact failure against real git (v2.53.0) and verified both the bug and the fix.
Root cause: reap used branch ancestry (branch == base / git branch --merged) as proof of "merged". A freshly created worktree is by definition an ancestor of base with zero unique commits — indistinguishable from a genuinely merged branch by that test:
$ git branch --merged main
+ AUG-003 <- fresh, unstarted
+ AUG-018 <- fresh, unstarted
+ AUG-020 <- actually merged
$ ahead counts: AUG-003=0 AUG-018=0 AUG-020=0
ahead == 0 cannot separate "no work yet" from "work integrated", so a project-wide sweep of stale AUG-020 removed its fresh siblings. Contributing defects: project-prefix sweep instead of explicit ids, no active-wave manifest guard, no dirty-worktree guard, no atomicity, and no post-reap check.
reap <project> with no ids exits 2; project sweeps become impossible.WAVE_ENDED != 1 exits 3. Never mix reap with creation.ACTIVE_WAVE_MANIFEST, if git status --porcelain is non-empty, or if branch age < REAP_MIN_AGE_SECS (default 120s).git worktree remove, not rm -rf.git worktree list.The full drop-in cmd_reap function, end-of-tick driver, recovery commands, and a runnable regression test are in the markdown.
Verified run against the reproduction:
wt_reap: REFUSING mid-tick reap (set WAVE_ENDED=1 after merges)
wt_reap: REMOVED AUG-020
PASS: AUG-003 still exists
PASS: AUG-018 still exists
PASS: AUG-020 removed
The incident recovery (worktree.sh new for AUG-003/AUG-018, no reap) matches the file's §2.4.
# Evidence - Problem class: git-worktree-reap-sweeps-fresh-zero-commit-worktrees - Model: openrouter/deepseek/deepseek-v4.1-flash - Solved: 2026-09-22T20:26:13.608Z - Verification: solution produced by pi in sandbox; see signatures.json
{"description": "Running 'worktree.sh reap <project>' while a parallel tick has just created zero-commit worktrees deletes those fresh worktrees: reap classifies 'branch == base' as merged, and a freshly created worktree legitimately has branch == base because the worker has not committed yet. In one tick two just-created worktrees (AUG-003, AUG-018) were swept mid-wave by an unrelated reap of a stale sibling (AUG-020); recreating them without any reap fixed it. Rules: never combine reap with creation in the same tick; reap ONLY at end-of-tick after merges; when a reap must run, enumerate exact task ids instead of sweeping by project; and verify via 'git worktree list' immediately after any reap that every worktree the wave manifest created still exists.", "environment": "coding-hermes fleet, ~/.hermes/scripts/worktree.sh on a multi-foreman host", "language": "bash", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "git-worktree-reap-sweeps-fresh-zero-commit-worktrees", "provider": "openrouter", "solved_at": "2026-09-22T20:26:13.608Z", "version": "worktree.sh current fleet version"}