[FEATURE] Claude Code memory traversal should respect git worktree boundaries
Preflight Checklist
- [x] I have searched existing requests and this feature hasn't been requested yet
- [x] This is a single feature request (not multiple features)
Problem Statement
When using git worktrees, Claude Code's CLAUDE.md traversal loads memory files from both the current worktree AND the parent repository directory, causing a security warning:
This project's CLAUDE.md imports files outside the current working directory. Never allow this for third-party repositories.
The problem:
- Git worktrees are a common development pattern for working on multiple branches simultaneously
- Each worktree represents a different branch state with potentially different CLAUDE.md instructions
- Claude Code traverses up from CWD to filesystem root
/, loading ALL CLAUDE.md files it encounters - This causes the parent repo's CLAUDE.md to be loaded alongside the worktree's CLAUDE.md
- When the parent CLAUDE.md imports files (e.g.,
@./AGENTS.md), those paths resolve outside the worktree CWD - This triggers a misleading security warning for the developer's own repository
Why this matters:
- Teams version-control CLAUDE.md files with branch-specific instructions
- Different feature branches may need different memory/coding guidelines
- The warning is technically correct but contextually wrong (it's not a third-party repo security issue)
- No current way to prevent Claude Code from traversing above the worktree/git boundary
Proposed Solution
Claude Code should stop memory file traversal at git boundaries, specifically:
Option 1: Auto-detect git worktree roots (Preferred)
When Claude Code encounters a .git file (not directory) during traversal, recognize it as a worktree and stop there:
# In a worktree, .git is a file pointing to the actual git directory
$ cat /path/to/worktree/.git
gitdir: /path/to/main-repo/.git/worktrees/feature-branch
Option 2: Stop at git repository root
When Claude Code encounters a .git/ directory, recognize it as a repository root and stop there.
Option 3: Configurable setting
Add user configuration to control traversal behavior:
{
"memory.stopAtGitRoot": true,
"memory.stopAtWorktreeRoot": true,
"memory.maxTraversalDepth": 3
}
Alternative Solutions
Current workarounds (all have drawbacks):
- Ignore the warning - Works but creates confusion and noise
- Use
CLAUDE.local.mdin parent - Prevents version control of memory files - Don't use git worktrees - Severely limits developer workflow
- Inline AGENTS.md into CLAUDE.md - Defeats modularity, makes diffs messy
None of these are acceptable for teams that use both worktrees and version-controlled memory files.
Priority
High - Significant impact on productivity
Feature Category
CLI commands and flags
Use Case Example
Scenario: Multi-branch development with branch-specific coding guidelines
- Developer maintains a FastAPI project with CLAUDE.md containing team coding standards
- They create a worktree for a new feature branch:
``bash``
git worktree add worktrees/api-v2-migration api-v2-migration
cd worktrees/api-v2-migration
- This branch modifies CLAUDE.md to add migration-specific instructions:
```markdown
@./AGENTS.md
# API v2 Migration Guidelines
- Use Pydantic v2 syntax only
- Maintain backwards compatibility with v1 endpoints
```
- Developer starts Claude Code in the worktree
- Claude Code loads BOTH:
worktrees/api-v2-migration/CLAUDE.md(v2 migration instructions)../../CLAUDE.md(main branch instructions)
- Warning appears: "imports files outside the current working directory"
- Developer is confused - both files are from their own repo
- Memory instructions conflict between branches, causing inconsistent AI behavior
Expected behavior: Claude Code should only load the worktree's CLAUDE.md, respecting the git worktree boundary.
Additional Context
Git worktree structure example:
/Users/dev/my-repo/ # Main repo
├── .git/ # Git directory
├── CLAUDE.md # Main branch instructions
├── AGENTS.md
└── worktrees/
├── feature-branch/ # Worktree (CWD)
│ ├── .git # FILE pointing to main .git
│ ├── CLAUDE.md # Feature branch instructions
│ └── AGENTS.md
└── bugfix-branch/ # Another worktree
├── .git # FILE pointing to main .git
├── CLAUDE.md # Bugfix branch instructions
└── AGENTS.md
Technical detection:
# Check if current directory is a worktree
if [ -f .git ]; then
echo "This is a git worktree - stop traversal here"
else
echo "Continue traversing up"
fi
Similar tools:
- Many language servers stop at git root for configuration
- Build tools (npm, cargo) stop at package/crate root
Impact: Affects any workflow combining:
- Git worktrees (parallel branch development)
- Version-controlled CLAUDE.md files (team standards)
- Branch-specific AI coding instructions (different features/refactors)
Showing cached comments. Read the full discussion on GitHub ↗
10 Comments
Indeed this is what I saw, Scripts and utilities need to either use paths relative to their current location or properly determine the full path to guarantee they function within the appropriate working directory.
Still Present in v2.1.37 (Linux)
This issue is still actively causing problems in v2.1.37.
Current Impact
When working in git worktrees, memory files are loaded twice (absolute + relative paths):
~/project/neo/.claude/rules/*.md(35 files).claude/rules/*.md(same 35 files again)Result:
/clearEvidence
All 9 worktrees in the project exhibit this behavior.
Question for Anthropic
Since this bug has been wasting tokens for weeks/months across multiple versions:
How can users recover quota consumed due to this bug?
The 2x token consumption isn't user error—it's a platform bug causing real financial impact.
---
Related issues: #23565 (closed but regressed), #24283 (my detailed bug report)
Side note: with a layout like
/Users/dev/my-repo/and/Users/dev/my-repo/worktrees/feature-branch, tools that scan upward (toward parent directories) or downward (recursively through subfolders) can easily cross from one checkout/worktree into another, and that can affect more than just config files (anything discovered by filesystem traversal: rules, scripts, templates, metadata, etc.). If you keep each checkout/worktree in its own top-level directory (instead of sharing a common parent), you reduce accidental cross-tree discovery and make traversal-based tooling more predictable.That's at least what I've learned from using git worktrees.
Claude Code should still stop traversal at the worktree/repo boundary to avoid mixing branch-specific instructions and duplicate loads.
I have a question. Why should linked worktrees be positioned like that?
Correct me if I miss anything.
The layout used by the OP
There are _three_ worktrees:
/Users/dev/my-repo//Users/dev/my-repo/worktrees/feature-branchand/Users/dev/my-repo/worktrees/bugfix-branchAre there practical reasons that linked worktrees should be placed inside the main worktree's working tree?
If it's the only concern that the CLAUDE.md in the main worktree is unwantedly loaded when you start Claude Code inside a linked worktree, how about the following alternative layouts:
Alternative layout 1, where the main workree has its working tree
Alternative layout 2, where the main workree doesn't have its working tree (so-called bare repo) (the layout of @CybotTM)
Alternative layout 3, where, unlike layout 2, the bare repo is detached from the hierarchy (for some reason). This is my personal taste.
[^1]: Terms in Git documentation: https://git-scm.com/docs/git-worktree
Thanks for opening this. I agree with the worktree-boundary pain, but I’d avoid a hard global change to "always stop at git root" because some teams intentionally rely on inherited parent guidance.
Proposal: make discovery deterministic and configurable via frontmatter in
CLAUDE.md(runtime-resolved, not model-instruction-following):Why this helps:
repomode fixes worktree bleed-through and cross-machine surprises.filesystemmode preserves current behavior for teams that want inheritance.manualsupports explicit-import-only workflows.CLAUDE.local.mdremains deterministic/private per project (not a model best-effort import pattern).This seems like a better "one design that fits multiple valid workflows" than forcing one traversal policy globally.
I hit this when working with a git submodule. The parent repo depends on the submodule, not the other way around - but Claude was loading the parent's CLAUDE.md, injecting instructions for an almost completely different project (different language, different stack, conflicting conventions). I initially thought stale memories were the problem, but it turned out to be the upward walk pulling in irrelevant context the whole time.
The recently released https://code.claude.com/docs/en/memory#exclude-specific-claudemd-files workaround helps for specific files, but it's not possible to exclude all ancestors - you have to know ahead of time what the parent directory is named, and the pattern doesn't work portably across worktrees where directory names differ (which admittedly I guess would be rare to be using worktrees and git submodules).
I think the expected behavior should be to NOT scan upwards by default.
.claude/rules/,.claude/skills/,.claude/settings.json, etc. are all scoped to the project directory... (as one would expect). I guess it's a little different because they don't walk downwards either.If this behavior is too baked in to change or there are people relying on it, at the very least a setting like
claudeMdIgnoreAncestors: trueto let users turn this off would be nice.Also... isn't this a potential security vulnerability/prompt injection vector? If I have my project at
~/project/my-project/.gitand then something happens and I somehow downloaded ~/CLAUDE.md (maybe a malicious script or something), Its very hard to know CLAUDE.md is getting loaded besides subtle change in behavior. If I check my project, there could be nothing, but the ~/CLAUDE.md will still load containing instructions I didn't make and not in my project to easily scan
hit this exact problem running 5-6 parallel Claude Code agents across different worktrees. the doubled memory load (mentioned in this thread) was killing our context budgets.
the layout that fixed it for us: bare repo at the center, worktrees as siblings in a separate top-level dir, not nested inside each other. when worktrees are nested (worktrees/ subfolder inside the main checkout), the parent CLAUDE.md is always in the traversal path. when they're siblings with their own git-dir pointers, Claude's upward walk hits the .git FILE and nothing beyond it.
that said, detecting .git as a FILE vs a directory and treating it as a boundary would be a cleaner fix than relying on directory layout. option 1 in the proposal seems right.
the tmux-based multi-agent setup we use manages worktree creation and session isolation: https://github.com/m13v/tmux-background-agents/blob/main/SKILL.md - keeps each agent in its own worktree with a clean directory boundary
Concrete impact observed: When using git worktrees placed inside the repo directory (e.g. repo/.worktrees/wt/), the parent repo's CLAUDE.md is loaded in addition to the worktree's own CLAUDE.md, even though the worktree is a valid, independent git root (has .git file). The two files have identical content.
Over a 10-turn coding session this adds ~50k redundant input tokens — measurable as ~5–10% of session quota consumed on content that's already present. We worked around it by moving the worktree base directory to a sibling path outside the repo (../. worktrees/), but the root fix should be: stop traversal at a git worktree boundary.
Maybe you started your Claude session in
repo/and not inrepo/.worktrees/wt/? Which would make it absolutely correct to respect and load the CLAUDE.md inrepo/too.We should not mix up CLAUDE.md loading hierarchy with worktrees boundaries.
For me: starting a session in
/projects/acmeand having worktrees somewhere under/projects/acmelike/projects/acme/mainand/projects/acme/feat-foobar, I explicitly want the CLAUDE.md in/projects/acmeto be loaded.