Use /context and the InstructionsLoaded hook to verify CLAUDE.md files actually load. Fix lazy-loading issues (nested files, @imports) before editing rule wording. Only rewrite rules after confirming they're in context.
You wrote the rule. The agent ignored it. Before you rewrite the wording — check whether the rule was ever in the context window at all. In my experience most "Claude ignored my CLAUDE.md" reports are actually loading problems, and loading problems are checkable in seconds.
Here are the three checks I run, in order.
Key Takeaways
- Use /context and the InstructionsLoaded hook to verify CLAUDE.md files actually load.
- Fix lazy-loading issues (nested files, @imports) before editing rule wording.
- Only rewrite rules after confirming they're in context.
1. /context — the ground truth for the current session
Run /context in your session and look at the Memory files list. That list is what actually loaded — not what exists on disk, not what should have loaded. If your file isn't there, no amount of prompt-wording work will help.
Don't confuse it with /memory. The /memory command lists memory file locations across user and project scopes — including entries for files that don't exist yet, so you can create them. It answers "where could instructions live?" /context answers "what is Claude actually reading right now?" When you're debugging, only the second question matters.
2. Know the two lazy-loading behaviors
Two loading rules produce almost all of the "my rule vanished" confusion:
Nested CLAUDE.md files load on demand. CLAUDE.md files in the directory hierarchy above your working directory load in full at launch. But a CLAUDE.md sitting in a subdirectory below it does not load until Claude actually reads a file in that subtree. Early in a session, that rule effectively does not exist — and /context will honestly show it missing until the first file access triggers it. If a rule must always apply, keep it in the root file (or a .claude/rules/ file without a path scope), not in a nested CLAUDE.md.
Imports have sharp edges. @path/to/file imports load at launch alongside the file that references them, and they can chain — but only to a maximum depth of four hops. Two details bite people:
- Import parsing skips code spans and fenced code blocks.
`@README`in backticks is literal text;@READMEoutside backticks is an import. If you documented an import inside a code fence while "cleaning up", you silently disabled it. - Relative paths resolve relative to the file containing the import, not your working directory. A fragment that imports
./shared.mdbreaks when you move it.
In practice I've never needed more than two hops on a real project (root importing AGENTS.md, which sometimes imports a shared fragment). If you're approaching four, the chain itself is usually the thing to fix.
3. InstructionsLoaded — log every load as it happens
Since you can't sit in a session running /context after every file access, Claude Code has a hook event for exactly this: InstructionsLoaded fires every time a CLAUDE.md or .claude/rules/*.md file enters context — at session start for eagerly-loaded files, and again mid-session when a nested file or path-scoped rule lazily loads.
The hook input tells you three things: file_path (which file), memory_type (User / Project / Local / Managed), and load_reason. The load_reason values are the interesting part:
-
session_start— loaded eagerly at launch -
nested_traversal— a subdirectory CLAUDE.md just lazy-loaded -
path_glob_match— a path-scoped rule matched a file Claude touched -
include— pulled in via an@pathimport -
compact— reloaded after context compaction
A minimal logger in .claude/settings.json:
{
"hooks": {
"InstructionsLoaded": [
{
"hooks": [
{
"type": "command",
"command": "jq -r '\"\(.load_reason)\t\(.file_path)\"' >> ~/.claude/instructions-loaded.log"
}
]
}
]
}
}
Now tail -f ~/.claude/instructions-loaded.log shows you the exact moment that nested CLAUDE.md finally entered context — instead of you inferring it from a behavior change three prompts later. The hook is observability-only (it runs async, exit codes are ignored), so it can't break anything.
You can also filter with a matcher on the load reason, e.g. "matcher": "nested_traversal|path_glob_match" to log only lazy loads.
The 30-second debugging flow
- Rule ignored? Run
/context. File not under Memory files → loading problem, stop editing the wording. - File is nested in a subdirectory → expected: it loads only after Claude reads that subtree. Move it to the root if it must always apply.
- File is imported → check the import isn't inside backticks or a code fence, check the relative path from the importing file, and count your hops (max four).
- File loaded but behavior is still wrong → now it's an instruction-quality problem. Rewrite for specificity, check for contradicting rules, and keep the file under ~200 lines.
Only step 4 is a prompt-engineering problem. Steps 1–3 are mechanical, and the InstructionsLoaded log turns them from guesswork into a grep.
For the full resolution order (which files load, in what order, and what wins), I walked through it in Which CLAUDE.md Files Claude Code Actually Loads (and in What Order).
Source: dev.to
Originally published on gentic.news

Top comments (0)