Symlinked rule directories load from user-level ~/.claude/rules/ but never from project-level .claude/rules/ (2.1.241, 2.1.251)

Status Open
Reported on v2.1.241
Maintainer reply None cached
Activity 0 comments · opened Aug 29, 2026

Summary

Symlink resolution in the rules loader behaves differently depending on scope:

  • User-level ~/.claude/rules/: a symlinked directory is resolved and loaded correctly. ✅
  • Project-level .claude/rules/: symlinks are never loaded at all. ❌ Neither symlinked files nor symlinked directories; neither at startup (no frontmatter) nor via the paths: lazy-injection trigger when a matching file is read.

The exact same rule content, as the exact same kind of symlink, works at user level and is silently ignored at project level. Replacing the project-level symlink with a plain cp -r copy makes it load again — so specifically project-level symlink resolution is broken.

Environment

  • Claude Code 2.1.241 and 2.1.251 (both reproduced)
  • macOS (Darwin 24.6.0, arm64)
  • Verified with fresh headless sessions (claude -p) using marker strings

Minimal reproduction (symmetric A/B/C)

# Shared probe content, outside any project
mkdir -p ~/shared-rules/myteam
printf 'If this rule is loaded, the marker SYMLINK_RULE_PROBE_7f3a is present.\n' \
  > ~/shared-rules/myteam/probe.md

ASK='check whether your instructions contain SYMLINK_RULE_PROBE_7f3a. Answer yes/no only.'

# A) User level, symlinked directory → LOADS
ln -s ~/shared-rules/myteam ~/.claude/rules/myteam
claude -p "$ASK"          # → yes
rm ~/.claude/rules/myteam

# B) Project level, same symlink → DOES NOT LOAD
cd /path/to/project && mkdir -p .claude/rules
ln -s ~/shared-rules/myteam .claude/rules/myteam
claude -p "$ASK"          # → no

# C) Project level, plain copy instead of symlink → LOADS AGAIN
rm .claude/rules/myteam
cp -r ~/shared-rules/myteam .claude/rules/myteam
claude -p "$ASK"          # → yes

Only the location (user vs project) and the symlink-vs-copy difference change; everything else is identical.

The paths: variant is also affected

Rules with paths: frontmatter are lazily injected when a matching file enters context. This works for regular files in project .claude/rules/, but a symlinked directory containing such rules is never injected, even when a matching file (e.g. src/example.ts for **/*.ts) is read:

claude -p "Read @src/example.ts, then $ASK"   # symlinked dir → no; copied dir → yes

Symlinked files (.claude/rules/foo.md -> ~/shared-rules/foo.md) behave the same way: not loaded when symlinked, loaded when copied. The failure is independent of the symlink target location (home dir, /tmp, and other paths were all tested).

Behavior matrix (all verified empirically)

| Layout | Loaded? |
|---|---|
| user ~/.claude/rules/subdir (symlinked dir) | ✅ |
| project .claude/rules/*.md (regular file) | ✅ |
| project .claude/rules/subdir/*.md (regular dir, recursed) | ✅ |
| regular file with paths: + matching file read | ✅ (lazy injection works) |
| project .claude/rules/foo.md (symlinked file) | ❌ |
| project .claude/rules/subdir (symlinked dir) | ❌ |
| …symlinked dir + paths: rule + matching file read | ❌ (no injection) |

Expected behavior

The memory docs describe sharing rules across projects with symlinks ("Share rules across projects with symlinks"), with examples of symlinking directories from outside the project into .claude/rules/. The changelog also shows symlinked rules being actively maintained (2.1.239/2.1.240 fixes for claudeMdExcludes matching on symlinked .claude/rules files), which implies they are supposed to load. User-level symlink loading working confirms the intent — project level should behave the same.

Relationship to previous issues

  • #764 was closed as completed on 2.1.233 after the maintainer could not reproduce — but the verified scenario there was a user-level ~/.claude symlink (stow) with commands, which is exactly the scope that still works here. The broken case is the project-level .claude/rules/ scope, which that verification did not cover.
  • #25903 (user-level symlinked rule file + paths: not loaded) was closed as a duplicate of #764 by the reporter.

So there is currently no open issue tracking the project-level .claude/rules/ symlink case.

Testing note

When reproducing, make sure no stale copies of the rule files remain inside .claude/rules/ (e.g. renamed backups): the loader recurses into regular subdirectories, and leftover copies will make a symlink test falsely pass.

View original on GitHub ↗