Agent isolation:"worktree" — concurrent dispatches sometimes execute early commands in the parent worktree, not the isolated one

Status Fixed / completed
Maintainer reply ✓ Yes — bcherny
Activity 3 comments · opened Jul 4, 2026 · closed Aug 19, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Summary

Agent tool calls with isolation: "worktree" appear to sometimes execute the
agent's first shell command(s) against the wrong working directory (the
parent/principal session's worktree) rather than the newly-created isolated
worktree, when several isolation: "worktree" agents are dispatched
concurrently. The agent's later commands in the same session then correctly
resolve to its own isolated worktree, and its own final self-report/footer
(worktreePath:/worktreeBranch:) can look entirely correct — but a stray
prefix of git-mutating commands has already landed in the shared parent
worktree.

What we observed

During an autonomous multi-agent release orchestration (Grimoire-framework
Noir paradigm, 14 Agent(isolation:"worktree") dispatches across 3 batches in
one release), the parent/orchestrator session's own principal worktree showed
a chain of 5 sequential git branch -m <name> events in its reflog — one per
dispatched agent in that batch, each renaming the shared checked-out branch,
each overwriting the previous rename. Each dispatch prompt instructed the
agent: "first, rename your current branch to <branch-name>" as its very
first action after landing in its own worktree.

Cross-checking each agent's own dedicated worktree afterward:

  • 3 of 7 agents in that batch ended up with a correctly-renamed branch and

their real work correctly committed, entirely inside their own isolated
worktree (no visible effect from the above).

  • 1 agent's own worktree never had its branch renamed at all (still sitting

on the default worktree-agent-<id> placeholder name) — but its actual
work was committed correctly inside its own worktree, under that
unrenamed branch. Its rename command appears to be the one that hit the
parent worktree's branch instead.

  • 1 agent (the most severe case) had its entire final commit — not just the

rename — land on the parent/principal worktree's checked-out branch. Its own
dedicated worktree was left completely untouched at the original base
commit, with no commits of its own. Its self-reported
worktreePath:/worktreeBranch: footer did not accurately reflect what had
happened.

Separately (and possibly related, possibly a distinct issue), several later
agents in subsequent, less-concurrent batches reported catching their own
worktree rooted at a stale/wrong base commit (an old ancestor far behind the
intended integration branch's live tip at dispatch time) before making any
commits, and self-corrected by re-rooting in place.

Why this looks like a race, not a one-off

  • The affected agents' substantive work (file edits, git commit) mostly

still landed correctly in their own isolated worktrees, even when an early
command (the rename) did not — suggesting the misdirection is transient,
present only in the first tool call(s) issued right after worktree
creation, and resolves itself shortly after.

  • The one full-cross-contamination case is consistent with the same

transient window simply lasting long enough to also catch that agent's
final commit, rather than a structurally different failure mode.

  • This only appeared with several isolation:"worktree" agents dispatched

in the same batch (i.e., concurrently) — not with single, sequential
dispatches earlier/later in the same session.

Impact

  • Silent branch/commit corruption in the parent session's checked-out

branch — recoverable via git reflog + manual branch renaming (which is
how we recovered here, with no data loss), but only detectable by manually
auditing the parent worktree's reflog and every dispatched worktree's
actual branch/HEAD state — the agent's own "done" report and the
worktreePath:/worktreeBranch: footer are not sufficient signals to
detect it.

  • Any workflow that trusts an isolated agent's self-report of having stayed

in its own worktree could silently merge or lose work incorrectly.

Ask

  1. Is isolation: "worktree" provisioning (creating the worktree, attaching

the spawned agent's shell cwd to it) synchronous with the agent's first
tool call, or is there a window where a fresh agent's Bash cwd could
still resolve to the parent session's directory? A race here would
explain everything above.

  1. If a race exists, could the Agent tool result reliably surface (or the

harness detect) when a spawned agent's commands executed outside its
assigned worktree, rather than relying on the agent's own self-report?

Happy to provide more detail (this was reconstructed from git reflog and
per-worktree branch/HEAD inspection after the fact, not captured live with
tracing) if useful.

View original on GitHub ↗

3 Comments

yurukusa · 3 months ago

Your forensic breakdown is convincing, and it lines up with a race where the agent's first command(s) run before its cwd has actually switched into the new worktree — so git branch -m / the first commit land on whatever the parent session had checked out. The parent reflog showing N sequential git branch -m (one per concurrent agent, each overwriting the last) is the signature. Two things help while this is unfixed: a guard the agent runs before any git mutation, and how to detect and recover damage that already landed on the parent.

Guard: make the first action prove it's in its own worktree

A worktree's identity is cheap to check and distinct from the parent's — I verified locally that a child worktree reports its own git rev-parse --show-toplevel (its own path, not the parent's) and its own branch (child-branch, not the parent's master). So gate the first git-mutating command on that:

# very first line of the agent's work, before any branch rename / commit:
here=$(git rev-parse --show-toplevel)
case "$here" in
  *"/worktree-agent-"*|*"$EXPECTED_WORKTREE"*) : ;;   # in my own isolated worktree — ok
  *) echo "isolation breach: landed in $here, not my worktree — aborting" >&2; exit 1 ;;
esac

If the dispatch prompt doesn't know the exact worktree path, the discriminator that always holds is that the isolated worktree starts on the placeholder branch worktree-agent-<id>: assert the current branch is NOT the parent's principal branch before renaming — if git branch --show-current comes back as the parent's branch, the race hit and you must not mutate.

Don't trust the self-report footer — trust the worktree's own log

The most dangerous part of what you saw is that the agent whose entire commit landed on the parent still emitted a correct-looking worktreePath:/worktreeBranch: footer. That's the classic "isolation wearing the face of success." Verify against ground truth after each batch, not the footer:

git -C "$agent_worktree" log --oneline -1     # should show the agent's commit, not the base commit
git -C "$parent_worktree" reflog | head       # N stray 'branch -m' or commits here = a leak

Recovering damage already on the parent

Nothing is lost — it's just on the wrong branch. The stray rename/commit is in the parent's reflog; move it where it belongs and restore the parent:

git -C "$parent_worktree" log -g --oneline    # find the stray commit hash
git -C "$agent_worktree" cherry-pick <hash>   # put it on the agent's own branch
git -C "$parent_worktree" branch -m <correct-parent-name>   # undo the stray rename
# if a commit (not just a rename) landed, reset the parent branch back to its pre-leak commit (find it in the reflog)

Real fix is on the harness side: don't run the agent's first command until its cwd is confirmed switched into the new worktree (close the race window), and derive the self-report footer from the actual worktree state rather than from intent.

(Honesty note: I verified the guard's basis — a child worktree's toplevel and branch are distinct from the parent and reliable to check — but I did not reproduce the concurrent race itself; it's timing-dependent and needs the Agent dispatch harness. The guard holds regardless, because it checks ground truth before mutating.)
</content>

bcherny collaborator · 1 month ago

Fixed in 2.1.203 — changelog: "Fixed worktree-isolated subagents sometimes running shell commands in the parent checkout instead of their own worktree" (https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md). Closing — reply to reopen if it still happens on the latest version.

🤖 Generated with Claude Code

github-actions[bot] · 4 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.