EnterWorktree(path=...) to a valid but non-nested worktree reports success and moves Bash cwd, but the Write/Edit guard silently keeps the original binding

Status Open
Maintainer reply None cached
Activity 1 comment · opened Sep 18, 2026

Summary

EnterWorktree(path=...), called from a session already running in a linked worktree, reports success and correctly moves the Bash tool's cwd to the target — but only actually rebinds the Write/Edit cross-worktree guard when the target worktree is nested under .claude/worktrees/. For a target that is a valid, already-registered worktree of the same repository but lives outside .claude/worktrees/ (e.g. created directly under the repo root via git worktree add), the switch silently half-applies: Bash cwd updates, the success message is unconditional and gives no indication anything is incomplete, but Write/Edit continue to enforce the original launch worktree as the binding — and the guard's own error message misidentifies the new (valid, git-registered) worktree as "the base repo checkout," then suggests a "corrected" path that nonsensically nests the target worktree underneath the original launch worktree on disk.

Reproduction

Both cases below were run from the same session, launched pinned to worktree A (.claude/worktrees/unruffled-cray-b06846), in the same repository.

Case 1 — compliant target (nested under .claude/worktrees/):

  1. git worktree add a second worktree at .claude/worktrees/bugrepro-test on a new branch.
  2. EnterWorktree(path=".../.claude/worktrees/bugrepro-test") → reports success.
  3. Bash pwd → correctly reports the new worktree.
  4. Write a new file inside the new worktree → succeeds, no guard error.

Case 2 — non-compliant target (a valid worktree of the same repo, NOT nested under .claude/worktrees/):

  1. A second worktree already exists at <repo-root>/fewer-permission-prompts-allowlist (created via git worktree add directly under the repo's parent directory, appears correctly in git worktree list on its own branch).
  2. EnterWorktree(path="<repo-root>/fewer-permission-prompts-allowlist") → reports success: "Entered worktree at ... The session is now working in the worktree."
  3. Bash pwd → correctly reports the new worktree path.
  4. Write a new file inside the new worktree → refused:

> This session is running in an isolated git worktree at <repo-root>/.claude/worktrees/unruffled-cray-b06846, but <repo-root>/fewer-permission-prompts-allowlist/BUG_REPRO_TEST.md is in the base repo checkout. Edits there do not land on this session's branch and may corrupt the user's primary working copy. Use the worktree path instead: <repo-root>/.claude/worktrees/unruffled-cray-b06846/fewer-permission-prompts-allowlist/BUG_REPRO_TEST.md

  1. Repeated after ExitWorktree(action: "keep") followed by a fresh EnterWorktree(path=...) on the same target → byte-identical failure, ruling out simple staleness.

The corrected path suggested in step 4 does not reflect where the target worktree actually lives on disk — it nests the second worktree's relative path underneath the first worktree, which is not a real location and would not resolve to the target's actual git working tree if followed.

Expected behavior

Either:

  • EnterWorktree(path=...) should fully honor the switch for any worktree that is valid and registered in git worktree list for the same repository (matching what its own tool description implies for "entering an existing worktree" generally), rebinding the Write/Edit guard the same way it rebinds Bash's cwd; or
  • If nesting under .claude/worktrees/ is a hard requirement for the guard to follow the switch, EnterWorktree(path=...) should say so and refuse (or clearly warn) rather than reporting unconditional success for a target it cannot fully switch into.

In either case, the guard's error message should correctly identify a valid linked worktree as such (not "the base repo checkout"), and any suggested "corrected path" should reflect where the target worktree actually resides on disk.

Actual behavior

EnterWorktree(path=...) unconditionally reports success and moves Bash's cwd for any git-valid target, but the Write/Edit guard's worktree binding is only updated for targets nested under .claude/worktrees/. For any other valid worktree of the same repo, the tools disagree about where the session is: Bash believes the switch happened, Write/Edit still enforce the pre-switch worktree, and the block message's own diagnosis (calling the target "the base repo checkout") and remediation (a path that nests the target under the original worktree) are both incorrect.

Environment

  • OS: Windows 11 Enterprise 10.0.26200
  • Claude Code CLI / Agent SDK session, main thread (not a subagent)
  • Both worktrees are of the same git repository; the non-compliant one was created via git worktree add (plus a --no-checkout + checkout -f workaround for an unrelated Windows colon-in-filename issue), landing directly under the repo's parent directory rather than under .claude/worktrees/

Related issues

Two open issues describe adjacent but distinct symptoms of what may be the same underlying binding-update gap:

  • #82354 — Write/Edit stay pinned to a subagent's launch-time worktree after it calls EnterWorktree to switch into a new one it just created.
  • #87890 — after EnterWorktree, PreToolUse hook subprocesses keep resolving CLAUDE_PROJECT_DIR/cwd against the original directory rather than the new worktree (Windows).

Neither covers this specific case: a main-thread session switching via path to an existing, git-valid, already-registered worktree of the same repository that simply isn't nested under .claude/worktrees/, with the "base repo checkout" mislabel and the nonsensical nested "corrected" path.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗