You already know how to write a CLAUDE.md. Then the project grows: build commands, conventions, rules for the API, for tests, for markup. Every one of those lines loads into every session, including the one where you only wanted to fix a typo in the README. And when Claude ignores a rule anyway, the instinct is to write it again in capitals.
This is the fourth post in a series on the Claude Code: Pro Level course, based on the lesson project memory, done properly. Two tools keep memory in order — imports and rules — and two more tell you whether a file loaded at all.
Imports with @path: order, not savings
A line with @ and a path pulls another file in along with CLAUDE.md:
# The linkcheck project
@AGENTS.md
- npm commands: @package.json
- Architecture: @docs/architecture.md
This organises memory. It does not save context: the imported file always loads, together with the file that imports it. Splitting one long CLAUDE.md into five imported files gives you five tidy files and exactly the same context bill.
Two small things that trip people up:
- the path runs up to the first space, so do not put a full stop right after it;
- the path is relative to the file that contains the import, not to the folder where you started Claude.
Importing a file outside the working folder, such as @~/notes.md, asks for your approval the first time it is used in a project. Decline, and it stays off.
The AGENTS.md trap
Many projects already keep an AGENTS.md for other coding agents, and Claude Code reads it on its own — but only while there is no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md in your folder or above it. Here is the catch: the moment one of those exists, Claude reads the CLAUDE.md files instead, and your carefully written AGENTS.md is silently skipped. That includes a personal CLAUDE.local.md you added for yourself.
The fix is one line — make @AGENTS.md the first line of CLAUDE.md, and the rules live in one place. Or open /config and set Project instructions to claude-md-and-agents-md, which reads both. Where the other agents look for their files is its own topic: the lesson where each AI coding tool reads its instructions lines up Claude Code, Codex, Gemini CLI and Cursor.
Rules for a folder: load only when needed
Imports do not save context, but rules can. A rule is a Markdown file in .claude/rules/. Without a paths header it loads at startup, like an ordinary CLAUDE.md. With one, it loads only when Claude works with a matching file:
---
paths:
- "test/**/*.js"
---
# Tests
- Use node:test and node:assert/strict; do not add other libraries.
Patterns are relative to the project root. * does not cross a folder boundary, **/ goes through any number of folders, and braces such as src/**/*.{ts,tsx} work too.
The subtlety that matters: a rule with paths applies when Claude reads, writes or edits a matching file — with the Read, Write or Edit tool, or with a plain cat, head or tail on that one file in the shell. Listing a folder, piping the file into another command or reading several files at once does not count. So when you test a rule, ask exactly that: “read test/config.test.js”.
What loads, and when
| Instruction file | When it loads |
|---|---|
CLAUDE.md at the root, and a rule without paths | at session start |
| AGENTS.md, when there is no CLAUDE.md | at session start |
a file pulled in by an @path import | along with the file that imports it |
a rule with paths | when Claude reads, writes or edits a matching file |
| auto memory — notes Claude keeps itself | at startup: the first 200 lines or 25 KB of the MEMORY.md index |
For personal instructions in one project there is CLAUDE.local.md, which you create by hand and do not commit.
Claude is not listening? Check the load first
Before rewording a rule, find out whether the file loaded at all. The lesson’s point is blunt: much of what looks like disobedience is a pattern with a typo, an AGENTS.md without an import, or an edit made mid-session.
/context all. Plain /context can collapse the list; all expands it. The Memory files section shows what loaded at startup. Rules with paths appear there only after a matching file has been read.
The InstructionsLoaded hook. It fires every time a CLAUDE.md, a rule or an import loads — though not for an AGENTS.md that Claude reads directly, only for one a CLAUDE.md imports. Put it in a separate settings file and start a session with claude --settings:
{
"hooks": {
"InstructionsLoaded": [
{ "hooks": [ { "type": "command", "command": "jq -r '[.load_reason, .file_path] | @tsv' >> \"$CLAUDE_PROJECT_DIR/.practice/loaded.txt\"" } ] }
]
}
}
Every line in the log carries a reason: session_start for startup, include for an import, path_glob_match for a rule that applied through its pattern. No path_glob_match line for your rule? Compare the pattern with the real files:
git ls-files ':(glob)src/api/**/*.js'
Empty output means the pattern matches nothing. The classic: a rule for source/** in a project where the code lives in src/.
Editing mid-session
Claude Code reads the project’s CLAUDE.md once, at startup. An edit reaches the session only after /clear, /compact or a restart. To find instructions that contradict each other or point to deleted files, run /doctor prompt-audit — it suggests edits and changes nothing until you ask.
What does not belong in memory
CLAUDE.md is context, not a setting Claude is obliged to obey. Two consequences:
- A rule that must never be broken belongs in a hook. A
PreToolUsehook that exits with code 2 blocks the action every time; the previous post on Claude Code hooks shows how. - Long procedures for rare tasks belong in skills. Every line of CLAUDE.md loads into every session, so keep it short.
Frequently asked questions
Does an @path import save context?
No. The imported file always loads together with the file that imports it. Imports bring order; rules with a paths field are what load on demand.
Does Claude Code read AGENTS.md?
Yes, on its own, as long as the project has no CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md. Once one of them exists, Claude reads the CLAUDE.md files instead. Put @AGENTS.md on the first line of CLAUDE.md to use both, or set Project instructions in /config to read both.
Why did my rule in .claude/rules not load?
Usually the pattern matches no real file, or Claude has not read a matching file yet — listing a folder or piping a file through another command does not count. Check with /context all, the InstructionsLoaded hook, or git ls-files with the same glob.
Practice
The project memory lesson has a practice terminal: create a CLAUDE.md that imports @AGENTS.md, then pick the draft rule whose pattern matches the real source folder and move it into .claude/rules/. It is the same diagnosis you will run on a real project, just smaller.