A symlinked rules/memory file is realpath-resolved before @imports expand, so relative imports resolve against the symlink target

Status Open
Reported on v2.1.282
Maintainer reply None cached
Activity 0 comments · opened Sep 28, 2026

Summary

When a memory/rules file is a symlink, its path appears to be resolved to the real path before @import expansion runs. Relative imports inside it then resolve against the symlink target's directory rather than the location the file was loaded from.

This is not only a missing-import problem. If a file happens to exist at that relative path on the target side, it is imported instead, silently — different content, no warning, no indication that the file you meant was not the file you got.

Why this bites

The common dotfiles pattern is to symlink config into place:

~/.claude/rules/my-rules.md  ->  ~/repos/dotfiles/claude/my-rules.md

A relative import inside my-rules.md — say @../framework/procedure.md — is naturally written to mean ~/.claude/framework/procedure.md, because that is where the file lives as far as Claude Code's own layout is concerned. Instead it resolves relative to ~/repos/dotfiles/claude/, so it silently misses (or, worse, silently picks up a different file that happens to exist at that relative path inside the repo).

Combined with the silent-import behaviour in the sibling report, the result is that the instruction file loads, the import does not, and nothing says so.

Expected

Either resolve relative imports against the path the file was loaded from (the symlink's own location), or document that symlinked instruction files resolve imports against their target, so authors can choose absolute paths instead.

Reproduction

mkdir -p ~/.claude/framework ~/tmp/repo/rules
echo 'MARKER-FROM-CLAUDE-DIR' > ~/.claude/framework/shared.md
printf '@../framework/shared.md\n' > ~/tmp/repo/rules/linked.md
ln -sf ~/tmp/repo/rules/linked.md ~/.claude/rules/linked.md
claude -p 'Reply with any marker strings you can see in your context, verbatim.'

Observed: MARKER-FROM-CLAUDE-DIR is absent. Replacing the symlink with a copy of the same file makes the import resolve and the marker appear — which is the workaround we ended up shipping in our own installer (copy rules files, symlink everything else).

Verified 2026-09-28 on 2.1.282, with a decoy to show which side resolves

Run in an isolated CLAUDE_CONFIG_DIR so nothing real was touched. Two files named
framework/shared.md existed, with distinct markers:

| location | marker |
|---|---|
| <config>/framework/shared.md | MARKERCONFIGDIR |
| <symlink target repo>/framework/shared.md | MARKERREPODIR |

Two rules files, both containing exactly @../framework/shared.md:

  • <config>/rules/direct.md — a real file
  • <config>/rules/linked.md — a symlink into the repo

Response contained both MARKERCONFIGDIR (from the real file — correct) and MARKERREPODIR
(from the symlink — resolved against the target's directory). The symlinked file did not fail to
import; it imported the wrong file, and nothing in the session indicated which one had been used.

Environment

macOS 25.6.0 (arm64). Measured 2026-09-01; workaround has been running in production since, most recently verified 2026-09-28 on 2.1.282 (Claude Code).

View original on GitHub ↗