claude-code - 💡(How to fix) Fix Worktree isolation: Edit-tool path validation + drift-detection in task-notification payload

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…

Error Message

character-prefix containment. The rejection should be a structured error

Fix Action

Fix / Workaround

Prior workarounds we have shipped at the orchestration layer:

  • Pre-dispatch prompt clause embedded in every parallel-worker spawn that instructs the agent to keep edits and git operations inside $PWD. Soft constraint; LLM-orchestrator subagents still violate on long procedures.

  • Pre-dispatch file-touch overlap detection that refuses parallel dispatch when briefs declare overlapping file-touch sets. Catches cross-worker file collisions but not worker-vs-meta-root path drift.

  • A post-fan-out capture/verify guard (shell script) that snapshots the meta-root HEAD

    • branch + working-tree hash before spawn and checks for drift after every agent completes. Post-hoc only; surfaces drift after agents complete, but cannot prevent mid-flight work loss.
  • A cd / git checkout PreToolUse hook for Bash. We considered this as a local workaround and rejected it: it cannot reach the Edit/Write surface that dominates the evidence above, and the CWD-based agent-identity heuristic is bypassed precisely when drift has already happened. Bash surface drift remains observable through Request B's meta_root_state_drift signal.

  • Per-tool sandboxing beyond path validation (e.g. capability tokens, syscall filters). Out of scope for this request; the path-validation approach is the minimal change that closes the documented evidence.

RAW_BUFFERClick to expand / collapse

1. Problem

When Claude Code spawns multiple agents in parallel via Task(isolation: "worktree"), each agent gets its own git worktree on disk. Each worktree has its own HEAD (under .git/worktrees/<name>/HEAD), but the refs database is shared with the common repo: refs/heads/<branch> is a single entry visible from every worktree, so a git checkout issued against any worktree's .git/worktrees/<name> moves only that worktree's HEAD — including the meta-root's worktree when an agent's CWD has drifted there. Two failure modes:

(1) Edit-tool path drift. An agent's Edit/Write call with an absolute file_path that happens to fall inside the meta-root checkout (e.g. an unqualified path that the agent inferred from session context) lands in the meta-root working tree, not the worktree. The agent's diff appears empty in the worktree; the meta-root accumulates uncommitted leakage from concurrent agents.

(2) Branch-checkout drift. An agent running git checkout <branch> from a CWD that has drifted to the meta-root (or via an explicit git -C <meta-root>) moves the meta-root's HEAD, displacing whatever other agents (or the operator) had checked out there. With N concurrent agents, HEAD can move N times during a single fan-out.

Concrete evidence from a single recent fan-out run in our orchestration framework:

  • Incident 1 — Edit calls landed at meta-root; the agent caught its own divergence and re-applied in the worktree.
  • Incident 2 — meta-root branch switched to a different feature branch mid-fan-out, displacing the operator's uncommitted in-progress work.
  • Incident 3 — 6 test files leaked into the meta-root working tree from a sibling agent; recovered via git stash.

Prior workarounds we have shipped at the orchestration layer:

  • Pre-dispatch prompt clause embedded in every parallel-worker spawn that instructs the agent to keep edits and git operations inside $PWD. Soft constraint; LLM-orchestrator subagents still violate on long procedures.
  • Pre-dispatch file-touch overlap detection that refuses parallel dispatch when briefs declare overlapping file-touch sets. Catches cross-worker file collisions but not worker-vs-meta-root path drift.
  • A post-fan-out capture/verify guard (shell script) that snapshots the meta-root HEAD
    • branch + working-tree hash before spawn and checks for drift after every agent completes. Post-hoc only; surfaces drift after agents complete, but cannot prevent mid-flight work loss.

2. Requested platform changes

Request A: Edit-tool path validation against worktree root

When the Agent SDK's Edit tool is invoked from inside an agent that was spawned with isolation: "worktree", the tool MUST reject any file_path whose canonicalized absolute form is not path-boundary-contained within the agent's worktree root — i.e. after realpath-resolution, the file_path must be the worktree root itself or a descendant of it on a path-component boundary. String-prefix matching is insufficient: a naive startswith check would incorrectly accept a sibling worktree at /tmp/wt-other/file.py when the root is /tmp/wt, contradicting the rejection of sibling worktrees in the behavior matrix below. Equivalent safe forms in stdlib include Path(file_path).resolve().is_relative_to(Path(worktree_root).resolve()) or an explicit parents-chain walk; the contract is path-component containment, not character-prefix containment. The rejection should be a structured error (e.g. WORKTREE_PATH_VIOLATION) carrying the offending path and the worktree root so the agent's diagnostic surface can recover or surface the violation.

Behavior matrix:

Agent isolationfile_path resolves inside worktreeResult
worktreeyesproceed
worktreeno (meta-root, sibling worktree, anywhere else)reject with WORKTREE_PATH_VIOLATION
not isolatedn/aunchanged (today's behavior)

Notes:

  • The same gate SHOULD apply to Write and NotebookEdit (parallel tools that mutate paths).
  • The gate SHOULD NOT apply to Read, since cross-tree reads are intentional (the prompt clause already directs agents to use absolute Read paths instead of cd).
  • Bash is outside scope of this request — see Request B for the drift-detection signal that closes the Bash surface programmatically.

Request B: Drift-detection field in Agent task-notification payload

When an Agent completes (the parent receives the task-notification message), the payload SHOULD include a boolean meta_root_state_drift field. The field is true iff any of:

  • the meta-root's HEAD ref differs from its state at agent-spawn time;
  • the meta-root's working-tree dirty state changed during the agent's lifetime;
  • the meta-root's branch HEAD changed during the agent's lifetime.

This lets orchestrators detect drift programmatically without each orchestrator having to reimplement the same git rev-parse + working-tree hash dance in a shell script. Once the field exists, our (and every other orchestrate-style framework's) local capture/verify guard can be deprecated and replaced with a single payload read.

3. Why prompt-only fixes are insufficient

Our coding convention for LLM pipeline design captures the structural limit:

"DO NOT silently fall back to X" or "MUST invoke subprocess Y" in an agent spec is a soft constraint: an LLM-orchestrator subagent will still violate when the procedure has many steps.

The agents observed in the evidence above had the Scope Guard prompt clause in their spawn prompts and still drifted. The platform-side fix in Request A makes the violations structurally impossible, not socially discouraged.

4. Compatibility / migration

  • Request A is backward-compatible for agents that do not use isolation: "worktree".
  • Request A may break workflows that intentionally write outside the worktree; we believe the right behavior is to fail loud and force the workflow author to make the cross-tree write explicit (e.g. via a new escape-hatch flag), but the request is open to Anthropic on the exact escape mechanism.
  • Request B is purely additive (new optional field); orchestrators that don't read it see no behavior change.

5. Out of scope (acknowledged)

  • A cd / git checkout PreToolUse hook for Bash. We considered this as a local workaround and rejected it: it cannot reach the Edit/Write surface that dominates the evidence above, and the CWD-based agent-identity heuristic is bypassed precisely when drift has already happened. Bash surface drift remains observable through Request B's meta_root_state_drift signal.
  • Per-tool sandboxing beyond path validation (e.g. capability tokens, syscall filters). Out of scope for this request; the path-validation approach is the minimal change that closes the documented evidence.

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

claude-code - 💡(How to fix) Fix Worktree isolation: Edit-tool path validation + drift-detection in task-notification payload