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
- Deploy user-level config via stow as above.
~/projects/dotfilesis a git repo whoseclaude/.claude/tree contains the user CLAUDE.md and ~24 skills. - Start a session anywhere (e.g.
~). User-level CLAUDE.md and the skill list load once — expected. - In the session, use the Read tool on any file under
~/projects/dotfiles/claude/.claude/…(the source path). - Claude Code now treats
~/projects/dotfiles/claudeas a nested project root: a system-reminder re-injects that CLAUDE.md as a "project CLAUDE.md" and re-lists all ~24 skills with aprojects/dotfiles/claude:prefix — duplicating what is already in context.
Evidence
stat -Lshows both paths are the same physical file: identical inode (5937 bytes) for~/.claude/CLAUDE.mdand~/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.
claudeMdExcludescovers 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.