claude-code - 💡(How to fix) Fix Subagent worktrees not auto-cleaned when reviewer writes scratch files

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When a subagent is dispatched via the Agent tool with isolation: "worktree", the agent-SDK's auto-cleanup hook does not fire reliably on subagent teardown if the subagent has written any scratch file inside its worktree during the dispatch. The worktree ends up in locked state per git worktree list and persists indefinitely, accumulating one stray worktree per dispatch.

Root Cause

We chose to keep the workaround in CLAUDE.md rather than in the skill template at ~/.agents/skills/requesting-code-review/ because that skill is managed by an obra/superpowers sync that overwrites local edits — any in-place edit there gets silently lost on next sync.

Fix Action

Fix / Workaround

When a subagent is dispatched via the Agent tool with isolation: "worktree", the agent-SDK's auto-cleanup hook does not fire reliably on subagent teardown if the subagent has written any scratch file inside its worktree during the dispatch. The worktree ends up in locked state per git worktree list and persists indefinitely, accumulating one stray worktree per dispatch.

  1. Dispatch a general-purpose subagent via the Agent tool with isolation: "worktree".
  2. The subagent writes any file under its worktree path during the run (e.g. git show <sha>:<path> > /tmp/<file> where /tmp/ happens to land inside the worktree, or tee-ing evaluator output to a path inside the worktree).
  3. The subagent finishes and returns its summary normally.
  4. Inspect the parent repo: git worktree list shows the agent's worktree still present, marked (detached HEAD) locked. The auto-created worktree-agent-<id> branch is still listed in git branch.

Three consecutive PRs in a single project chain (PRs #18, #19, #20 in ArkaneSapien/hiatus, dispatched roughly weekly apart, all using general-purpose agents under isolation: "worktree") exhibited the same failure mode. In every case:

Code Example

git worktree unlock /path/to/.claude/worktrees/agent-<id>
git worktree remove --force /path/to/.claude/worktrees/agent-<id>
git branch -D worktree-agent-<id>
RAW_BUFFERClick to expand / collapse

Summary

When a subagent is dispatched via the Agent tool with isolation: "worktree", the agent-SDK's auto-cleanup hook does not fire reliably on subagent teardown if the subagent has written any scratch file inside its worktree during the dispatch. The worktree ends up in locked state per git worktree list and persists indefinitely, accumulating one stray worktree per dispatch.

Reproducer

  1. Dispatch a general-purpose subagent via the Agent tool with isolation: "worktree".
  2. The subagent writes any file under its worktree path during the run (e.g. git show <sha>:<path> > /tmp/<file> where /tmp/ happens to land inside the worktree, or tee-ing evaluator output to a path inside the worktree).
  3. The subagent finishes and returns its summary normally.
  4. Inspect the parent repo: git worktree list shows the agent's worktree still present, marked (detached HEAD) locked. The auto-created worktree-agent-<id> branch is still listed in git branch.

When the subagent makes zero filesystem changes inside the worktree, auto-cleanup fires correctly and git worktree list is clean. The breakage correlates with reviewer-side scratch writes.

Observed in the wild

Three consecutive PRs in a single project chain (PRs #18, #19, #20 in ArkaneSapien/hiatus, dispatched roughly weekly apart, all using general-purpose agents under isolation: "worktree") exhibited the same failure mode. In every case:

  • The worktree is at the PR base SHA or some intermediate commit.
  • nothing to commit, working tree clean — no uncommitted work.
  • git worktree list reports the worktree as (detached HEAD) locked. The lock state is what blocks auto-cleanup.

Manual recovery (used three times now)

git worktree unlock /path/to/.claude/worktrees/agent-<id>
git worktree remove --force /path/to/.claude/worktrees/agent-<id>
git branch -D worktree-agent-<id>

The auto-created branch ref persists even after worktree remove --force (tracked separately from the worktree dir), so the third branch -D step is required to actually clean up git branch -a output.

Suggested fix

When a subagent exits, the SDK should treat its worktree as transient by definition:

  1. git worktree unlock <path> if the worktree is locked.
  2. git worktree remove --force <path> regardless of dirty state.
  3. git branch -D worktree-agent-<id> to drop the ref.

The current behaviour appears to use git worktree remove (without --force), which fails when the worktree has any modifications. Subagent worktrees are inherently transient — there's no reason to preserve them across exits. If a future use case needs post-exit inspection (debugging the subagent's filesystem state), an opt-in keepWorktree: true flag would be cleaner than the current default behaviour.

Impact

  • Confuses subsequent review passes. Each new dispatched subagent sees the leftover worktree(s) in git worktree list and either flags them as suspicious (one reviewer in our chain flagged a leftover as a security finding before being corrected) or wastes context investigating.
  • Accumulates over time. Three review passes on a single PR × multiple PRs per milestone could leave a dozen stray worktrees before anyone notices. Each holds an inode-level snapshot of the repo.
  • Pollutes review-comment audit trails. Three Linear comments across our PRs had to mention this leftover — workflow-debt baked into project audit logs.

Workaround in place

We've added a dispatch-time cleanup block to our project's CLAUDE.md (https://github.com/ArkaneSapien/hiatus/pull/21) that we append to every code-reviewer subagent's prompt before dispatching. The block instructs the subagent to run git worktree unlock + remove --force + branch -D on its own worktree before exiting. This works in practice but is brittle (relies on every dispatched subagent following the prepended instructions, plus a parent-side belt-and-braces check) — the right fix is SDK-side.

We chose to keep the workaround in CLAUDE.md rather than in the skill template at ~/.agents/skills/requesting-code-review/ because that skill is managed by an obra/superpowers sync that overwrites local edits — any in-place edit there gets silently lost on next sync.

Environment

  • Claude Code CLI (Mac).
  • Agent tool dispatches with subagent_type: "general-purpose" and isolation: "worktree".
  • Subagent worktrees created at <repo>/.claude/worktrees/agent-<id>.

Happy to provide more reproductions or test a candidate fix.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING