CLAUDE_CONFIG_DIR is not honored by .claude/rules/*.md auto-attachment (leaks default profile's rules into isolated profiles)

Status Open
Reported on v2.1.215
Maintainer reply None cached
Activity 1 comment · opened Jul 19, 2026

Description

When CLAUDE_CONFIG_DIR is set to a non-default directory, the automatic .claude/rules/*.md context-injection feature ("nested_memory" attachments, the mechanism behind the InstructionsLoaded hook and per-file-type coding-style/testing/security rules) still resolves rules from the hardcoded default ~/.claude/rules/ path, instead of $CLAUDE_CONFIG_DIR/rules/. This happens even though every other subsystem in the same session (session/transcript storage, settings.json, plugins, OAuth token) correctly honors CLAUDE_CONFIG_DIR.

Additionally, when a same-relative-path rules file does exist under $CLAUDE_CONFIG_DIR/rules/, both it and the default-profile file load simultaneously — there's no precedence or deduplication. The correctly-scoped file is tagged "type":"User" in the transcript; the leaked default-profile file is mistakenly tagged "type":"Project", suggesting the resolution logic isn't even aware it's reading from outside the configured root.

This defeats the isolation purpose of CLAUDE_CONFIG_DIR for anyone running multiple profiles on one machine (e.g. separate work/personal identities, or a profile with a specialized system persona) — content from one profile's rules/ silently bleeds into every other profile whenever a matching file type is read.

Reproduction

  1. Have a "default" profile at ~/.claude with a rules file using paths: frontmatter, e.g. ~/.claude/rules/typescript/coding-style.md:

```markdown
---
paths:

  • "**/*.ts"
  • "**/*.tsx"

---
# Some default-profile TypeScript rule
```

  1. Set up a second, isolated profile directory, e.g. ~/.claude-work, with no rules/ directory at all.
  2. Run a session scoped to the second profile:

``bash
CLAUDE_CONFIG_DIR="$HOME/.claude-work" claude -p "Read some-file.tsx and reply DONE"
``

  1. Inspect the resulting transcript at ~/.claude-work/projects/.../*.jsonl — it contains a nested_memory attachment entry whose path is /Users/you/.claude/rules/typescript/coding-style.md (the default profile's path), not anything under ~/.claude-work.

Additional case — even mirroring the file locally doesn't fix it:

  1. Create ~/.claude-work/rules/typescript/coding-style.md with the same paths: frontmatter and distinct content.
  2. Repeat step 3.
  3. The transcript now shows both attachments: the local ~/.claude-work/rules/typescript/coding-style.md ("type":"User") and the default-profile ~/.claude/rules/typescript/coding-style.md ("type":"Project") — the default-profile content loads unconditionally regardless of whether a local override exists.

Expected behavior

.claude/rules/*.md auto-attachment should resolve against $CLAUDE_CONFIG_DIR (falling back to ~/.claude only when CLAUDE_CONFIG_DIR is unset), consistent with how settings.json, plugin state, session storage, and OAuth credentials already behave under CLAUDE_CONFIG_DIR in the same version.

Actual behavior

Rules content from the default ~/.claude/rules/ is injected into sessions regardless of CLAUDE_CONFIG_DIR, and is not deduplicated against or superseded by a same-path file that does exist under the configured profile.

Environment

  • Claude Code version: 2.1.215
  • OS: macOS (Darwin 25.5.0)
  • Both profiles on the same machine, same user account, switched via a shell alias that sets CLAUDE_CONFIG_DIR and a separate CLAUDE_CODE_OAUTH_TOKEN per-invocation (not exported/persisted — confirmed the env var does not leak into later shell commands)

Additional context

Related past fixes suggest this is a recurring category of bug rather than a one-off — the changelog shows CLAUDE_CONFIG_DIR support being patched into individual subsystems one at a time after the fact (IDE shell-integration lock files, the workflow save dialog, /setup-vertex//setup-bedrock path display). The rules/nested_memory auto-attachment subsystem appears to be another one that was missed.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗