Worktree torn down mid-session while still active cwd; git commands silently fall through to parent repo's .git
Summary
During a single Claude Code session working in a project at /Users/aljobson/Downloads/veyrnox-secure (with several .claude/worktrees/<name> worktrees in play), I hit two related worktree-lifecycle bugs:
Issue 1 (serious): active worktree torn down mid-use, causing silent cross-checkout mutation
My session's worktree (.claude/worktrees/festive-chaum-95f29c) was — while still my active working directory, mid-session, with the branch already merged upstream — deregistered from git (.git/worktrees/festive-chaum-95f29c removed, no entry in git worktree list) without its on-disk directory being removed at the same time. The directory itself remained, minus its .git file.
Because git searches upward through parent directories for .git when none is found locally, every subsequent git command I ran with that directory as cwd (git status, git fetch, git merge --ff-only, etc.) silently resolved to and mutated the parent/main repo checkout instead of erroring or refusing. This included a git merge --ff-only origin/main that I intended to run in an isolated worktree, but which actually fast-forwarded the shared main checkout — a checkout that other concurrent tooling/sessions were also actively syncing, causing a real race (I observed the branch pointer move on its own via reflog entries I hadn't triggered).
Impact: a torn-down worktree can cause an active session's git commands to silently apply to the wrong (shared, main) checkout with no error or warning — this is a correctness/safety hazard, not just a cleanup annoyance, especially in multi-agent/concurrent-worktree setups.
Suggested fix directions:
- Never remove a worktree's
.gitlink / registry entry while a process still has that directory as its cwd (or has any open handle/session bound to it). - If a worktree must be torn down, remove the directory and its git registration atomically, not the registration first.
- Consider having the harness detect/refuse git operations whose resolved
--git-dir/--work-treedoesn't match the expected worktree path, and surface a clear error instead of silently proceeding.
Issue 2 (minor, recurring): empty-shell directory recreated repeatedly under a reused name
Separately, a directory .claude/worktrees/competent-kapitsa-32c00d reappeared three times across the same session, each time as an empty shell — just a .claude/settings.local.json (sometimes also .claude/scheduled_tasks.lock), no .git file, no source checkout, and no entry in git worktree list or .git/worktrees/. Each time I removed it, it was recreated later in the session under the identical name.
This looks like ephemeral scratch space being provisioned under a recycled/reused directory name without being fully wired up as a real git worktree (or being torn down incompletely, same root cause as Issue 1). It's harmless on its own (nothing of value lives in it), but it's a repeated, avoidable cleanup burden and a symptom of the same underlying lifecycle-tracking gap.
Environment
- macOS (Darwin 25.5.0), Claude Code CLI
- Project used git worktrees under
.claude/worktrees/for parallel agent sessions - Multiple concurrent worktrees/sessions were active against the same repo during the incident