Nested-project detection re-injects identical CLAUDE.md (same inode) and re-lists all skills when reading files under a stow-managed dotfiles source tree

Open 💬 0 comments Opened Jul 9, 2026 by wuchengwei1996

Environment

  • Claude Code 2.1.205, macOS (darwin 25.5.0)
  • Dotfiles managed with GNU Stow: everything under ~/.claude/ is a symlink into ~/projects/dotfiles/claude/.claude/ (a git repo), e.g.

~/.claude/CLAUDE.md -> ../projects/dotfiles/claude/.claude/CLAUDE.md

Repro

  1. Deploy user-level config via stow as above. ~/projects/dotfiles is a git repo whose claude/.claude/ tree contains the user CLAUDE.md and ~24 skills.
  2. Start a session anywhere (e.g. ~). User-level CLAUDE.md and the skill list load once — expected.
  3. In the session, use the Read tool on any file under ~/projects/dotfiles/claude/.claude/… (the source path).
  4. Claude Code now treats ~/projects/dotfiles/claude as a nested project root: a system-reminder re-injects that CLAUDE.md as a "project CLAUDE.md" and re-lists all ~24 skills with a projects/dotfiles/claude: prefix — duplicating what is already in context.

Evidence

  • stat -L shows both paths are the same physical file: identical inode (5937 bytes) for ~/.claude/CLAUDE.md and ~/projects/dotfiles/claude/.claude/CLAUDE.md. The injected "project CLAUDE.md" is byte-identical to the already-loaded user-level one.
  • Cost: ~4–5K tokens per affected session (duplicated CLAUDE.md ≈2.5K + skill re-listing ≈2K), paid on every subsequent turn of that session.

Expected behavior

  • Dedupe by physical file (realpath/inode): if a candidate project CLAUDE.md resolves to the already-loaded user-level file, skip injection; likewise skip re-listing skill files that are already registered at user level.
  • Alternatively: a setting to exclude a path from nested-project detection. claudeMdExcludes covers the CLAUDE.md half, but there is no equivalent for the skills re-listing.

Workaround (verified)

Reading via the symlink path (~/.claude/…) never triggers the re-injection — only Read via the source path does. Bash commands with cwd inside the repo don't trigger it either. We've adopted "always read via the symlink path" as a working habit, but it's easy to forget and silently costs tokens when violated.

View original on GitHub ↗