The Navigator · 11 min mission

CLAUDE.md Mastery: Project Memory That Works

Write a CLAUDE.md that makes every session smarter — and keep it from rotting.

memoryconfigurationbest practicesFact-checked 2026-06-13
On this page

Every Claude Code session starts with an empty context window. It does not remember your last session, your build command, or that one cursed test that only passes on a clean install. A CLAUDE.md file is how you hand that knowledge back to it — a plain markdown file Claude reads at the start of every conversation, before you type a single word.

That makes CLAUDE.md the highest-leverage file in your repo. Get it right and Claude works like a teammate who already read the onboarding doc. Get it wrong — bloated, vague, contradictory — and it quietly poisons every session you run.

What CLAUDE.md actually is

CLAUDE.md is not configuration that Claude is forced to obey. The official docs are blunt about this: its contents are delivered as a user message after the system prompt, so Claude reads it and tries to follow it, but there is no guarantee of strict compliance. That single fact drives every rule in this guide. Because it is advice rather than enforcement, the way you write it — how specific, how short, how well-organized — directly changes how reliably Claude follows it.

It also means CLAUDE.md is the wrong tool for anything that must happen with zero exceptions. To block an action regardless of what Claude decides — block writes to a path, run a check before every commit — you reach for a PreToolUse or Stop hook, not a more strongly-worded paragraph. Memory shapes behavior; hooks and settings enforce it.

The memory hierarchy

CLAUDE.md is not one file — it is a stack. Claude Code loads memory from several locations, ordered from broadest scope to most specific. Files higher up the stack appear in context first, and files closer to where you launched Claude are read last, so the most specific instruction is the freshest thing in mind. Nothing overrides anything; all discovered files are concatenated together.

There are four scopes worth knowing. Managed policy is an organization-wide file your IT or DevOps team deploys to a fixed system path — it always applies and individual users cannot exclude it. User instructions live in ~/.claude/CLAUDE.md and follow you across every project on your machine. Project instructions live in ./CLAUDE.md (or ./.claude/CLAUDE.md) and ship to your team through version control — this is the one you will edit most. Local instructions go in ./CLAUDE.local.md, which you add to .gitignore for personal, uncommitted notes like a sandbox URL or your preferred test data.

ScopeLocationGood forShared with
Managed policy/Library/Application Support/ClaudeCode/CLAUDE.md (macOS); /etc/claude-code/CLAUDE.md (Linux/WSL)Org-wide standards, security & complianceEveryone on the machine
User~/.claude/CLAUDE.mdYour personal preferences across all projectsJust you, everywhere
Project./CLAUDE.md or ./.claude/CLAUDE.mdArchitecture, build/test commands, conventionsYour team, via git
Local./CLAUDE.local.md (gitignored)Personal sandbox URLs, scratch notesJust you, this project
The four CLAUDE.md scopes, in load order from broadest to most specific. Managed policy paths are fixed system locations.

How files load: walking up, reading down

When you launch Claude in foo/bar/, it walks up the directory tree, checking each directory for CLAUDE.md and CLAUDE.local.md. So it picks up foo/bar/CLAUDE.md, then foo/CLAUDE.md, and so on toward the filesystem root. Everything found above your working directory is loaded in full at launch.

The ordering is deliberate. Across the tree, content is arranged root-first, working-directory-last — so foo/CLAUDE.md lands in context before foo/bar/CLAUDE.md. Within a single directory, CLAUDE.local.md is appended after CLAUDE.md, so your personal notes are the last thing Claude reads at that level. The practical upshot: the most specific, most local guidance is the freshest in Claude's mind.

Subdirectory files load on demand

Files below your working directory behave differently. Claude discovers CLAUDE.md files in subdirectories too, but instead of loading them at launch, it pulls one in only when it actually reads a file in that subtree. This is what makes per-directory memory cheap: a CLAUDE.md inside src/payments/ describing your money-handling rules costs zero context until Claude opens a file there — then it appears exactly when it is relevant.

One sharp edge worth knowing: after a /compact, your project-root CLAUDE.md is re-read from disk and re-injected, but nested subdirectory files are not re-injected automatically. They reload the next time Claude touches a file in that subtree.

init then inspect
… scroll to run this session
Run /init once to bootstrap, then /memory to see exactly which files are loaded this session — including nested ones.

Three ways to add a memory

  1. Generate a starting point with /init

    /init analyzes your codebase and writes a CLAUDE.md with the build commands, test instructions, and conventions it can detect. If a CLAUDE.md already exists, it suggests improvements instead of overwriting. Treat the output as a first draft — refine it with the things Claude could never infer from code alone.

  2. Ask Claude to remember

    Tell Claude directly — "always use pnpm, not npm" or "add this to CLAUDE.md." It writes the note for you. General learnings flow into auto memory; say "add this to CLAUDE.md" explicitly when you want it in the shared, committed file.

  3. Browse and edit with /memory

    /memory lists every CLAUDE.md, CLAUDE.local.md, and rules file loaded in the current session and opens any of them in your editor. It is your ground truth for what Claude can actually see — if a file is not in the list, Claude is not reading it.

The router pattern: short file, deep docs on demand

Here is the tension at the heart of CLAUDE.md. It rides in every session's context window, consuming tokens before you have asked for anything. The official guidance is to target under 200 lines per file — longer files consume more context and, counterintuitively, reduce adherence. A bloated CLAUDE.md is worse than a short one because the rules that matter get lost in the noise, and Claude starts ignoring half of it.

The fix is the router pattern: keep CLAUDE.md short and shaped like a table of contents, not an encyclopedia. It states the handful of facts every session needs — build command, test runner, project layout, a few hard "always do X" rules — and then points to deeper docs that load only when relevant.

You point with the @path/to/import syntax. Imported files are expanded into context at launch, so this is about organization, not saving tokens — an imported file still costs what its contents cost. The real context savings come from a different tool: subdirectory CLAUDE.md files that load on demand, and skills that load only when invoked. Use @imports to compose what must always be present; use subdirectory memory and skills for what is only sometimes relevant.

Encyclopedia vs. router

Bloated (ignored)

A 600-line CLAUDE.md with full API docs pasted in, a paragraph explaining what each folder does, "write clean code and good tests," and three subtly contradictory rules about formatting.

Claude loses the real instructions in the noise — and silently stops following them.

Router (followed)

A 40-line CLAUDE.md: build/test commands, the path alias, two hard rules, and @docs/architecture.md for deep context. Money-handling rules live in src/payments/CLAUDE.md, loaded only when Claude works there.

Every line earns its place, so every line gets read.

Build your own router-shaped CLAUDE.md

Build your CLAUDE.md

Toggle what your repo needs — the file on the right rewrites itself as you click, following the same rules a good hand-written CLAUDE.md follows: short, grouped, zero fluff.

Project facts

First thing Claude reads each session — say what the repo is in one line.

Stack picks

Naming the stack stops Claude from guessing your toolchain.

Commands

Commands save Claude from guessing your scripts. Clear a field to drop it.

Conventions

House rules written once here beat repeating them in every prompt.

Warnings

Fence off the places where an eager edit does damage.

./CLAUDE.md
# CLAUDE.md
 
## Project
Acme Dashboard — Internal analytics dashboard for the sales team.
Stack: TypeScript, React.
 
## Commands
| Command | Purpose |
| --- | --- |
| `npm run dev` | Start the dev server |
| `npm test` | Run the test suite |
| `npm run lint` | Lint the codebase |
| `npm run build` | Production build |
 
## Conventions
- Never commit or push unless explicitly asked.
- Run the test suite before claiming a task is done.
 
## Warnings
- `dist/` is generated — do not edit it by hand.
Pick the scope, add only the facts that pass the deletion test, and watch the file stay lean. This is the shape you want before you ever run /init.

Tuning for adherence

Because CLAUDE.md is advice, how you phrase it matters. Three levers move the needle. Specificity: "Use 2-space indentation" beats "format code properly"; "Run npm test before committing" beats "test your changes." Write instructions concrete enough to verify. Structure: use markdown headers and bullets — Claude scans organized sections the way a reader does, and dense paragraphs hide rules. Emphasis: the official best-practices doc notes you can add words like "IMPORTANT" or "YOU MUST" to push adherence on a rule that keeps getting missed.

And treat CLAUDE.md like code. Review it when something goes wrong, prune it regularly, and check changes by watching whether Claude's behavior actually shifts. If Claude keeps doing something you have a rule against, the file is probably too long and the rule is drowning. If Claude asks questions the file already answers, the phrasing is ambiguous. Watch for contradictions across the stack — if your user file and project file disagree, Claude may pick one arbitrarily.

Knowledge check

Your project-root CLAUDE.md has grown to 500 lines and Claude has started ignoring a formatting rule that is clearly written in it. What is the best first move?

What CLAUDE.md is, under the hood

Two implementation details explain a lot of CLAUDE.md's behavior, and both are documented. First, the file is not part of the system prompt: its contents are delivered as a user message after the system prompt [V]. That is the mechanical reason there is no strict-compliance guarantee — Claude reads your rules the same way it reads your typed instructions, not as immutable configuration. If you genuinely need something at the system-prompt level, the official escape hatch is the --append-system-prompt flag, which you must pass on every invocation (so it suits scripts, not interactive use) [V].

Second, block-level HTML comments are stripped before the content is injected [V]. A <!-- maintainer notes --> line in your CLAUDE.md never reaches Claude's context, so you can leave notes for human teammates without spending a single token on them. Two exceptions: comments inside code blocks are preserved, and when you open the file with the Read tool the comments stay visible [V]. This is a free, underused way to annotate a shared memory file.

Auto memory: the notes Claude writes itself

CLAUDE.md is what you write. Auto memory is the second, complementary system — notes Claude writes to itself across sessions: build commands it discovered, debugging insights, a preference you corrected once [V]. It is on by default and requires Claude Code v2.1.59 or later (claude --version to check) [V]. Claude does not save something every session; it decides what is worth keeping based on whether it would help a future conversation [V].

The storage model is the important part. Each repository gets a directory at ~/.claude/projects/<project>/memory/, where <project> is derived from the git repo — so all worktrees and subdirectories of the same repo share one auto-memory directory, and it is machine-local (not synced across machines or cloud) [V]. Inside lives a MEMORY.md index plus optional topic files (debugging.md, api-conventions.md, …). Only the first 200 lines of MEMORY.md, or the first 25KB, whichever comes first, load at the start of every conversation; topic files are pulled in on demand with normal file tools when Claude needs them [V]. So MEMORY.md works exactly like the router pattern you apply to CLAUDE.md — a concise index that points at depth loaded only when relevant. (Note: this 200-line/25KB cap applies only to MEMORY.md — CLAUDE.md still loads in full regardless of length [V].)

CLAUDE.md filesAuto memory
Who writes itYouClaude itself
What it holdsInstructions and rulesLearnings and patterns it discovers
ScopeProject, user, or orgPer repository, shared across worktrees
Loaded intoEvery session, in fullEvery session (first 200 lines or 25KB of MEMORY.md)
Lives at./CLAUDE.md, ~/.claude/CLAUDE.md, …~/.claude/projects/<project>/memory/
CLAUDE.md vs. auto memory — two memory systems, both loaded every session, both advice rather than enforcement.

Path-scoped rules: .claude/rules/

When CLAUDE.md starts to bloat, the official answer is not a longer file — it is to move situational instructions out of it into .claude/rules/ [V]. Drop one markdown file per topic (testing.md, security.md, api-design.md) into that directory; .md files are discovered recursively, so you can nest frontend/ and backend/ subfolders [V]. A rule with no frontmatter loads at launch with the same priority as .claude/CLAUDE.md [V].

The payoff is path scoping. Add YAML frontmatter with a paths: field of globs and the rule loads only when Claude reads a file matching one of them [V] — it triggers on file reads, not on every tool call. Personal rules go in ~/.claude/rules/ and apply across every project; user-level rules load before project rules, so project rules win on conflict [V].

markdown
---
paths:
  - "src/api/**/*.ts"
  - "tests/**/*.test.ts"
---
 
# API rules
 
- All endpoints must validate input.
- Use the standard error response format.

This is the real context win the router pattern promised: your money-handling or API rules cost zero tokens until Claude opens a matching file, then appear exactly when they are relevant.

Sharing memory with Codex (and other agents)

Claude Code reads CLAUDE.md, not AGENTS.md [V]. If your repo already keeps an AGENTS.md for Codex or another agent, you do not duplicate it — you make CLAUDE.md import it so both tools read one source of truth. Put @AGENTS.md at the top of CLAUDE.md, then append any Claude-specific lines below; Claude loads the imported file at session start, then appends the rest [V]:

markdown
@AGENTS.md
 
## Claude Code
Use plan mode for changes under `src/billing/`.

If you do not need to add Claude-only content, a symlink works just as well — ln -s AGENTS.md CLAUDE.md (on Windows this needs Administrator or Developer Mode, so prefer the @AGENTS.md import) [V]. And if you are starting fresh in a repo that already has an AGENTS.md, running /init reads it and folds the relevant parts into the generated CLAUDE.md — it also picks up .cursorrules, .devin/rules/, and .windsurfrules [V].

For private notes that should never be committed, drop a CLAUDE.local.md at the project root: it loads alongside CLAUDE.md and is treated identically, but you .gitignore it (choosing the personal option in /init does this for you) [V]. One worktree caveat — a gitignored CLAUDE.local.md only exists in the worktree where you made it, so to share personal instructions across worktrees, import a home-directory file instead, e.g. @~/.claude/my-project-instructions.md [V].

Reach the end and this star joins your charted sky.