Worktree-isolated subagents load the project CLAUDE.md (and its @imports) twice; the only workaround exclude pattern that doesn't drop subfolder CLAUDE.md files is non-obvious
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (reproduced on 2.1.284, and on 2.1.282; the 2.1.283 and 2.1.284 changelogs contain no related fix)
What's Wrong?
A subagent dispatched with isolation: "worktree" loads the project's CLAUDE.md twice:
- At startup it loads
<project>/CLAUDE.md(plus its@imports) from the parent project root. - On its first file read inside its worktree, it loads the worktree's copy again as
nested_memoryattachments:<project>/.claude/worktrees/agent-<id>/CLAUDE.md, plus the worktree copy of every file that CLAUDE.md@imports.
The worktree is created inside the project (.claude/worktrees/), so the worktree's root CLAUDE.md looks like a subdirectory CLAUDE.md of the parent project.
- Cost: it scales with CLAUDE.md size and dispatch volume. With a CLAUDE.md plus imports of about 100 KB, it adds roughly 25-30k tokens to every worktree subagent, and in our transcripts most worktree subagents carried the duplicate.
- The two copies can differ. The startup copy comes from the parent checkout's working directory, while the worktree is created from the last-fetched
origin/<default branch>. If the parent checkout is behind or has local edits, the subagent receives two different versions of its instructions, with no indication of which one applies.
What Should Happen?
A worktree subagent should load its project memory once. Either:
- recognise the worktree's root CLAUDE.md (and its imports) as the project memory it already loaded, while still loading genuine subfolder CLAUDE.md files, or
- root the subagent's memory in its worktree, so its CLAUDE.md matches the code it works on.
Error Messages/Logs
None; the duplicate shows up only in the subagent transcript.
Steps to Reproduce
- Create a fresh repo, with no
.claude/settings and no hooks:
````
git init -b main repro && cd repro
printf '# Tiny project\nMARKER-123: be concise.\n\n@docs/imp.md\n' > CLAUDE.md
mkdir -p docs sub src
echo 'IMPORT-MARKER: imported rules.' > docs/imp.md
printf '# Sub guide\nSUB-MARKER: sub rules.\n' > sub/CLAUDE.md
echo hello > src/a.txt && echo world > sub/b.txt
git add -A && git commit -m init
- Dispatch one worktree-isolated subagent. From the repo root, run this single shell command. It starts a headless Claude Code session and gives it the quoted prompt; that session then spawns the subagent, which runs in its own worktree under
.claude/worktrees/and does the file reads:
```
echo "Do not read files yourself. Spawn exactly ONE subagent with the Agent tool, subagent_type general-purpose, isolation worktree, and this prompt: 'Run pwd, then Read src/a.txt and then sub/b.txt using absolute paths inside your working directory. Reply with the path.'" | claude -p --output-format json --allowedTools Agent Bash Read
claude` in the repo root and paste the quoted prompt.
Interactive alternative: start
- Find the subagent's transcript. The JSON output contains
session_id. The transcript is~/.claude/projects/<project-dir>/<session_id>/subagents/agent-*.jsonl, where<project-dir>is the repo path with separators replaced by-. - Check what was loaded. In that file:
<repo>/CLAUDE.mdis loaded at startup (search forMARKER-123)- after the first Read, entries with
"type": "nested_memory"appear for<repo>/.claude/worktrees/agent-<id>/CLAUDE.mdand.../docs/imp.md, the duplicates IMPORT-MARKERappears 4 times instead of 2.
- Repeat with each exclude. Create
.claude/settings.local.jsonwith eachclaudeMdExcludesvalue from the table below, e.g.{"claudeMdExcludes": ["**/.claude/worktrees/*/CLAUDE.md"]}, and re-run step 2..claude/settings.local.jsonis local and uncommitted. Check whether anested_memoryentry for.../sub/CLAUDE.mdappears (search forSUB-MARKER).
Results on 2.1.284:
| claudeMdExcludes | Duplicate root CLAUDE.md + imports | sub/CLAUDE.md loaded | IMPORT-MARKER count |
|---|---|---|---|
| none | yes: root CLAUDE.md and docs/imp.md loaded again from the worktree | yes | 4 |
| **/.claude/worktrees/** | no | no (lost) | 2 |
| **/.claude/worktrees/*/CLAUDE.md | no | yes | 2 |
Claude Model
Sonnet (the behaviour does not appear to be model-dependent)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.284 (Claude Code); also reproduced on 2.1.282
Platform
Anthropic API
Operating System
Windows 11
Terminal/Shell
Git Bash / PowerShell, headless claude -p
Additional Information
- Workaround, and its trap:
claudeMdExcludes: ["**/.claude/worktrees/*/CLAUDE.md"]in the parent project's local, uncommitted settings removes the duplicate root CLAUDE.md, and its@importsgo with it. Subfolder CLAUDE.md files inside the worktree still load.- The more obvious
**/.claude/worktrees/**also removes the duplicate, but it silently drops every subfolder CLAUDE.md inside the worktree. That is a real loss of scoped instructions, and nothing warns about it. - Either pattern must stay out of version control. Committed, it reaches every worktree, and a top-level session started inside a worktree then loads no root CLAUDE.md at all.
- Path rules are unaffected by the exclude, because worktree subagents read
.claude/rules/*.mdfrom the parent project. However,paths:globs are matched relative to the parent root, so an anchored glob likesrc/**/*.tsdoesn't match.claude/worktrees/agent-<id>/src/x.ts, while**/src/**/*.tsdoes. This is likely the same root cause: the subagent's memory and rules are rooted in the parent project, not its worktree.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗