Native worktree GC (worktree.cleanupPeriodDays) deletes DIRTY and UNPUSHED agent-* worktrees, contradicting the documented clean+pushed-only cleanup behavior
Environment
- Claude Code ~v2.1.185-era, macOS (Darwin 25.x, APFS)
- Project
.claude/settings.json:"worktree": { "cleanupPeriodDays": 3 } - Subagent (isolation:worktree) worktrees under
<project>/.claude/worktrees/agent-*
Repro / observed
Two real subagent worktrees were tracked as controlled subjects; both were removed between
2026-07-02T10:42Z and 2026-07-03T11:12Z once their INTERNAL age (git committer-date / created-time
basis, not filesystem mtime -- touch -t backdating is ignored) crossed the 3-day TTL:
agent-a1792a15fd988a0cd-- DIRTY (uncommitted changes in the tree). REAPED.
The branch stub survived; the uncommitted work is unrecoverable.
agent-a3060652-- detached HEAD carrying an UNPUSHED commit (no branch ref). REAPED.
No surviving branch/ref anywhere; the commit was recovered only via git fsck --unreachable
and re-anchored as a rescue branch.
Trigger characterization (host-probed 2026-07-02): the sweep fires on a full INTERACTIVE claude
startup (regenerates ~/.claude/.last-cleanup); it does not complete under headless claude -p.
Expected
Per the official worktree docs (code.claude.com/docs/en/worktrees), the cleanup sweep removes only
worktrees that are CLEAN (no uncommitted/untracked changes) AND fully pushed; dirty or unpushed
worktrees are preserved.
Actual
agent-* worktrees are removed once internal age >= cleanupPeriodDays REGARDLESS of
dirty/unpushed state: uncommitted work is destroyed and unpushed commits are orphaned as
unreachable objects.
Workaround (what we deployed)
- Raised
worktree.cleanupPeriodDays3 -> 30 (native GC demoted to a distant backstop) and wired
our own fail-closed reaper (preserves locked/dirty/unpushed/unaged; never worktree remove --force)
as the sole intentional reaper.
- Recovery for an already-reaped unpushed commit:
git fsck --unreachable-> inspect the commit
objects -> git branch rescue/<name> <sha> + push. Works only until gc/prune expiry, so rescue
promptly.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗