[BUG] CLAUDE.md symlinked to AGENTS.md loads both files, duplicating content in context
Description
When ~/.claude/CLAUDE.md is a symlink to ~/AGENTS.md, Claude Code loads both files' contents into the system prompt as two separate instruction blocks instead of deduplicating them. The content is injected twice, verbatim, wasting context tokens.
Environment
- Claude Code 2.1.300 (macOS, Darwin 25.6.0, arm64)
- No
CLAUDE_CONFIG_DIRoverride (default~/.claude)
Steps to reproduce
ln -s AGENTS.md ~/.claude/CLAUDE.mdso that~/.claude/CLAUDE.md -> ../AGENTS.md(confirmed withls -la:lrwxr-xr-x ... /Users/x/.claude/CLAUDE.md -> ../AGENTS.md).~/AGENTS.mdis a real file with project/global instructions.- Start a session with cwd somewhere under
$HOME(e.g.~/tmp, not itself a git repo). - Inspect the system prompt / first system-reminder block.
Result
The system prompt contains the full content of the file twice, under two different labels:
Contents of /Users/x/.claude/CLAUDE.md (user's private global instructions for all projects):
<...full content...>
Contents of /Users/x/AGENTS.md (project instructions, checked into the codebase):
<...identical full content...>
Both paths resolve (via realpath) to the same inode — ~/.claude/CLAUDE.md is only a symlink — but the loader treats them as two independent sources: one found via the fixed global-config path, the other via ancestor-directory traversal looking for the nearest AGENTS.md. Neither loader resolves symlinks/realpaths before comparing against files already loaded in the session, so no dedup happens.
Expected
The loader should resolve symlinks (or compare by realpath/inode) before adding a file's content to the prompt, and skip a file whose resolved path is already loaded under another label — consistent with how issue #94463 describes the session_start loader already doing this correctly for literal CLAUDE.md-vs-CLAUDE.md collisions. This case is a different loader pair (global ~/.claude/CLAUDE.md vs ancestor-directory AGENTS.md) but the same missing-realpath-dedup root cause.
Related
- #94463 — same root cause category (path-string comparison instead of realpath comparison for dedup), different loader pair (
nested_traversalvssession_startforCLAUDE.md). - #96466 / #78977 / #31005 / #34235 / #62371 / #6235 — various requests around native
AGENTS.mdsupport; this issue assumes the current behavior of loading bothCLAUDE.mdandAGENTS.mdindependently is intended, and reports only the dedup failure when one is a symlink to the other.