[FEATURE] Claude Code memory traversal should respect git worktree boundaries

Status Open
Maintainer reply None cached
Activity 11 comments · opened Jan 7, 2026

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):

  1. Ignore the warning - Works but creates confusion and noise
  2. Use CLAUDE.local.md in parent - Prevents version control of memory files
  3. Don't use git worktrees - Severely limits developer workflow
  4. 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

  1. Developer maintains a FastAPI project with CLAUDE.md containing team coding standards
  2. 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
``

  1. 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

```

  1. Developer starts Claude Code in the worktree
  2. Claude Code loads BOTH:
  • worktrees/api-v2-migration/CLAUDE.md (v2 migration instructions)
  • ../../CLAUDE.md (main branch instructions)
  1. Warning appears: "imports files outside the current working directory"
  2. Developer is confused - both files are from their own repo
  3. 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)

View original on GitHub ↗

10 Comments

paul-mothapo · 7 months ago

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.

atournayre · 6 months ago

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:

  • Memory files: 109k tokens instead of ~54k (doubled)
  • Context at 73% capacity immediately after /clear
  • Premature compaction every 3-4 messages
  • Session and weekly quotas exhausted at 2x normal rate

Evidence

❯ /context
Memory files: 109k tokens (54.5%)

└ ~/PhpstormProjects/neo/CLAUDE.md: 1.1k tokens
└ ~/PhpstormProjects/neo/.claude/rules/00-regles-critiques.md: 1k tokens
[... 33 more files ...]

└ CLAUDE.md: 1.1k tokens                                  ← DUPLICATE
└ .claude/rules/00-regles-critiques.md: 1k tokens         ← DUPLICATE
[... 33 duplicate files ...]

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)

CybotTM · 6 months ago

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.

~/projects/acme
├── .bare/                  (common git dir or bare repo)
├── main/.git               (FILE: gitdir: ../.bare/worktrees/main)
└── feature_123/.git        (FILE: gitdir: ../.bare/worktrees/feature_123)
zzJinux · 6 months ago

I have a question. Why should linked worktrees be positioned like that?

Correct me if I miss anything.

The layout used by the OP

/Users/dev/my-repo/              # Main repo
├── .git/                        # Git directory (1)
├── main.py
├── CLAUDE.md                    # Main branch instructions
├── AGENTS.md
└── worktrees/
    ├── feature-branch/                # Worktree (CWD)
    │   ├── .git                       # FILE pointing to main .git (2)
    │   ├── main.py                    
    │   ├── CLAUDE.md                  # Feature branch instructions
    │   └── AGENTS.md
    └── bugfix-branch/                 # Another worktree
        ├── .git                       # FILE pointing to main .git (3)
        ├── main.py
        ├── CLAUDE.md                  # Bugfix branch instructions
        └── AGENTS.md

There are _three_ worktrees:

  1. _main worktree_ [^1] /Users/dev/my-repo/
  2. _linked worktrees_ [^1] /Users/dev/my-repo/worktrees/feature-branch and /Users/dev/my-repo/worktrees/bugfix-branch

Are 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

~/projects/acme/
├── main/                 main worktree
│   └── .git/             git common dir
└── feature_123/          linked worktree
    └── .git              git dir pointer -> ../main/.git/worktrees/feature_123

Alternative layout 2, where the main workree doesn't have its working tree (so-called bare repo) (the layout of @CybotTM)

~/projects/acme/
├── .bare/                git dir (bare repo && git common dir && main worktree)
├── main/                 linked worktree
│   └── .git              git dir pointer -> ../.bare/worktrees/main
└── feature_123/          linked worktree
    └── .git              git dir pointer -> ../.bare/worktrees/feature_123

Alternative layout 3, where, unlike layout 2, the bare repo is detached from the hierarchy (for some reason). This is my personal taste.

~/clones/acme.git/        git dir (bare repo && git common dir && main worktree)

~/worktrees/acme/
├── main/                 linked worktree
│   └── .git              git dir pointer -> ~/clones/acme.git/worktrees/main
└── feature_123/          linked worktree
    └── .git              git dir pointer -> ~/clones/acme.git/worktrees/feature_123

[^1]: Terms in Git documentation: https://git-scm.com/docs/git-worktree

PabloLION · 6 months ago

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):

---
memory:
  discovery_mode: repo      # repo | filesystem | manual
  ancestor_traversal: false # default false in repo mode
  subtree_discovery: true
  local_file: CLAUDE.local.md
  precedence: [org, local, nested, repo, user]
---

Why this helps:

  • repo mode fixes worktree bleed-through and cross-machine surprises.
  • filesystem mode preserves current behavior for teams that want inheritance.
  • manual supports explicit-import-only workflows.
  • CLAUDE.local.md remains 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.

haowjy · 6 months ago

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: true to 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/.git

and 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

m13v · 5 months ago

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.

~/agents/
  project.git/      (bare repo)
  main/             (.git FILE -> ../project.git/worktrees/main)
  feature-x/        (.git FILE -> ../project.git/worktrees/feature-x)

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.

m13v · 5 months ago

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

julianhyatt · 4 months ago

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.

CybotTM · 4 months ago
but the root fix should be: stop traversal at a git worktree boundary.

Maybe you started your Claude session in repo/ and not in repo/.worktrees/wt/? Which would make it absolutely correct to respect and load the CLAUDE.md in repo/ too.

We should not mix up CLAUDE.md loading hierarchy with worktrees boundaries.

For me: starting a session in /projects/acme and having worktrees somewhere under /projects/acme like /projects/acme/main and /projects/acme/feat-foobar, I explicitly want the CLAUDE.md in /projects/acme to be loaded.

~/projects/acme    <- start point of my Claude coding sessions
├── CLAUDE.md      <- explaining gitflow/gitworktree, how to work with git clones/checkouts in this project (using worktrees)
├── .bare/         (bare repo)
├── main/          (main branch with project CLAUDE.md)
└── feature_123/   (feature branch with project CLAUDE.md)

Showing cached comments. Read the full discussion on GitHub ↗