Home / Blog / Claude Code

Claude Code memory: CLAUDE.md, auto memory, and rules

Claude Code has two memory systems. CLAUDE.md is what you write and it loads every session. Auto memory is what Claude writes about you, and only its index loads. Both are context, not enforcement, and the interesting failures come from forgetting that.

I run 679 memory files across the projects on this machine. The home project alone holds 119. A nightly job audits the active ones against their repositories and commits the result. That's more memory than most people want, and it's exactly enough to have watched every way agent memory goes wrong. This post covers what the current system does, verified against the docs on 2026-09-07, and what six months of running it at that scale taught me about keeping it true.

The two systems answer different questions. CLAUDE.md answers "how do we work here." Auto memory answers "what did I learn about you and this project that I cannot derive from the code." Put an instruction in the wrong one and it either costs you every session or never fires.

CLAUDE.md: where it loads from and in what order

Claude Code loads CLAUDE.md from your working directory and every directory above it, concatenated root first, so the file nearest to where you launched is read last. Subdirectory files load on demand when Claude reads files there.

The memory reference lists four scopes. A managed policy file at /Library/Application Support/ClaudeCode/CLAUDE.md on macOS or /etc/claude-code/CLAUDE.md on Linux, deployed by IT and impossible to exclude. Your user file at ~/.claude/CLAUDE.md for every project. The project file at ./CLAUDE.md or ./.claude/CLAUDE.md, shared through version control. And ./CLAUDE.local.md, which you gitignore and which is appended after the project file at the same level, so your personal notes are the last thing read.

The files are concatenated, not merged, and they don't override each other. Two rules that contradict each other both load, and the docs say Claude may pick one arbitrarily. That's the first thing to check when an instruction seems ignored: not whether it loaded, but whether something else loaded that disagrees with it.

Size has a stated target and a hard cap. Aim for under 200 lines per file. Claude Code loads a file up to 4 MiB in full and skips anything larger. Longer files reduce adherence, which is a polite way of saying that a 600-line CLAUDE.md is a file where the important line is invisible. The /doctor checkup, since v2.1.206, proposes trims for a checked-in CLAUDE.md by cutting what Claude could derive from the codebase, such as directory layouts and dependency lists, and keeping pitfalls, rationale, and conventions that differ from tool defaults. That's the right editorial rule even without the tool.

Three mechanics that are easy to get wrong. @path imports pull other files in at launch, up to four hops deep, and they still cost context, so splitting a big file into imports organizes it without shrinking it. A path inside backticks isn't an import. Block-level HTML comments are stripped before injection, so <!-- maintainer note --> is free, but a comment inside a code block is kept. And AGENTS.md isn't read. If another agent already uses one, write a CLAUDE.md whose first line is @AGENTS.md, or symlink it, and confirm with /context that it appears under Memory files.

Rules: the same thing, scoped

A .claude/rules/ directory splits instructions into topic files. Files without a paths field load at launch like CLAUDE.md. Files with one load only when Claude reads a matching file.

---
paths:
  - "src/api/**/*.ts"
---

# API handlers

- Every endpoint validates its input before touching the database
- Errors use the shared response shape in src/api/errors.ts

Rules are discovered recursively, symlinks work so a shared rule set can be linked into many projects, and ~/.claude/rules/ gives you user-level rules that load before project rules so the project wins on conflict. The path budget is 1,000 expanded patterns and 4 MiB per rule, which only matters if you write brace expansions like {a,b}/{c,d}/*.{ts,tsx}.

The distinction the docs draw between rules and skills is the one I would underline. A rule loads into context every session or whenever a matching file opens. A Claude Code skill loads only when invoked or when Claude judges it relevant. When a section of CLAUDE.md has grown into a procedure rather than a fact, it belongs in a skill, where its body costs nothing until it runs. My own user CLAUDE.md is 6.4 KB and points at heavier reference files that load on demand for that reason.

Auto memory: what Claude writes about you

Auto memory is on by default. Claude saves four kinds of notes to ~/.claude/projects/<project>/memory/, one topic file per note plus a MEMORY.md index. Only the first 200 lines or 25 KB of the index load at session start.

The four types are recorded in each file's frontmatter. user is your role and preferences. feedback is corrections you gave and approaches you confirmed. project is ongoing work and decisions Claude can't derive from the code or git history. reference is where to find things outside the project, such as a dashboard or a tracker. Claude skips anything derivable from the codebase and anything CLAUDE.md already says, and it doesn't save every session.

The index is the only thing loaded automatically. Topic files are read on demand with the normal file tools. After Claude writes MEMORY.md, Claude Code measures it against the 200-line and 25 KB limits and reminds Claude to shorten it when it's close. When it's over, the write succeeds but Claude Code returns an error telling Claude to rewrite the index, because everything past the limit is dropped on the next load. That error was added after a stretch of silent truncation, and the reason it matters is in the next section.

Storage is per git repository, so all worktrees and subdirectories of one repo share a memory directory. It's machine-local and never synced. autoMemoryDirectory moves it, autoMemoryEnabled: false turns it off per project, and CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 turns it off everywhere. Since v2.1.214 Claude Code stamps a modified timestamp into the frontmatter on every write, so a memory carries the date it was last true. Memory files are excluded from the session retention sweep, so they stay until someone edits or deletes them.

Two boundaries worth knowing. The main conversation's auto memory isn't loaded into subagents, except a fork, which inherits the parent conversation. And a subagent definition can carry its own memory with a memory: user, project, or local field, which gives that agent a directory under ~/.claude/agent-memory/<name>/ for user, .claude/agent-memory/<name>/ for project, or .claude/agent-memory-local/<name>/ for local, the Read, Write, and Edit tools to manage it, and the first 200 lines of its own index in its system prompt. The docs recommend project scope so the knowledge ships with the repository.

What survives compaction

The project-root CLAUDE.md is re-read from disk and re-injected after /compact. Nested CLAUDE.md files and path-scoped rules reload when Claude next reads a file they apply to. An instruction given only in conversation is gone.

This is the most common "Claude forgot" report and it has a mechanical cause. If something must persist across a long session, it goes in CLAUDE.md, not in a message. And if it must happen at a fixed point, before every commit or after every edit, it's not a memory question at all. Both memory systems are delivered as context, and the docs are explicit that CLAUDE.md arrives as a user message after the system prompt with no guarantee of strict compliance. A PreToolUse hook executes regardless of what Claude decides. The docs draw this line and I would draw it harder: memory shapes behavior, hooks enforce it, and the practices that survived 21 client accounts all put enforcement in hooks.

To see what actually loaded, run /context and read the Memory files list. To see which instruction file loaded when and why, the InstructionsLoaded hook logs it. /memory opens any of the files in your editor and toggles auto memory. Since v2.1.216 a GUI editor no longer blocks the session while the file is open.

Six months of memory at scale: what goes wrong

Memory doesn't go stale in the direction you expect. The notes most worth writing describe broken things, and broken things are exactly what someone is fixing, so a defect note has the shortest half-life of anything in the corpus.

These are from my own rules file, each with the date it was verified, each written after the failure happened.

A memory that says a bug isn't yet fixed expires the moment someone commits. On 2026-08-29 three separate notes in one project described defects that a single commit the day before had already resolved. The check is one command: git log -S'<symbol>' --since=<note date>. If the symbol the note names has moved since the note was written, the note is a hypothesis.

A memory can be in the wrong project's directory and no staleness check will catch it, because every check asks whether the note is true and never whether it's here. On 2026-09-05 a note describing one repository's tooling had lived in another project's memory for weeks, surviving every audit. The tell is a file whose cited paths never match its own directory.

A gitignore safety net matches file names, not contents. A *token* pattern kept a memory file that explains how token authentication works, and contains no secret, invisible to git from the day it was created, while the index listed it and it looked perfectly maintained. Confirm with git ls-files --error-unmatch and git check-ignore -v, and read the content before renaming anything, because the inverse is a real credential laundered into history.

A benchmark memory isn't an inventory. On 2026-08-22 I read a note recording which model won an evaluation and told the person I work with that it was the model currently running. It wasn't; the served model had changed 131 days after the benchmark. A memory holding an evaluation verdict is historical by default. Deployment questions get answered from the live endpoint.

The structural fix for all of these is a nightly job. A scheduled task dispatches one auditor subagent per active project, at most four in parallel on Sonnet, each with the memory directory and the real repository it describes. The auditor moves stale notes into an archive, never deletes, and the run ends in one git commit under ~/.claude and one log line. A Sunday pass dedupes across projects, rotates through dormant ones, and consolidates the rules file into a retired section rather than dropping anything. That job is why the 679 files are usable. Without it they would be a very confident diary.

The production platform we run for clients reached the same design from the other direction. After trying a vector-store memory product for the client-facing agents, we retired it on 2026-08-20 in favor of files: a short profile, an index, a weekly summary with a length cap, and a rules file of one-line corrections with stable ids, each capped at 50 to 80 lines and checked by a validator script. A nightly task per agent group maintains them. Files can be read, diffed, and audited by the next agent. The vector store could only be queried.

Frequently asked questions

Where does Claude Code store memory?

CLAUDE.md files live in your project directory, its parents, and ~/.claude/CLAUDE.md for user-wide instructions. Auto memory lives in ~/.claude/projects/<project>/memory/, one directory per git repository, with a MEMORY.md index and one topic file per note. Both locations are plain markdown you can edit.

What is the difference between CLAUDE.md and auto memory?

You write CLAUDE.md and it loads in full every session as instructions. Claude writes auto memory from your corrections and preferences, and only the first 200 lines or 25 KB of its index load at start, with topic files read on demand. Use CLAUDE.md for rules and auto memory for learnings.

How do I make Claude Code remember something?

Ask it directly, for example "remember that the API tests need a local Redis instance," and it saves the note to auto memory. To put it in CLAUDE.md instead, say "add this to CLAUDE.md" or edit the file through /memory. For anything that must happen at a fixed point, write a hook rather than a memory.

Does CLAUDE.md survive compaction?

The project-root CLAUDE.md is re-read from disk and re-injected after /compact. Nested CLAUDE.md files and path-scoped rules reload when Claude reads files they apply to. Instructions given only in conversation don't survive, so move them into CLAUDE.md if they matter.

How do I turn off auto memory?

Open /memory in a session and use the auto memory toggle, which saves autoMemoryEnabled to your user settings. Set autoMemoryEnabled to false in a project's settings to disable it for that project only, or set CLAUDE_CODE_DISABLE_AUTO_MEMORY=1 to disable it everywhere.