Skip to content

AI CODING ASSISTANTS

Claude Code Memory Files: 22 Sessions, 11 Memories

Two different things are called Claude Code memory. One is a file you write and pay for every turn; the other writes itself, and on this machine it produced 11 files in nine days.

Claude code memory files are two different systems sharing one word, and they have opposite cost profiles. CLAUDE.md is written by you, read from up to five locations, and paid for on every turn. The auto-memory directory is written by the model, one markdown file per fact, and recalled selectively. We counted both across 470 sessions on one machine: one CLAUDE.md present out of five possible locations, and 11 memory files totalling 11,473 bytes produced in the nine days since the feature first appeared. Measured 2026-09-15 on Claude Code 2.1.270, Windows 11.

Key takeaways

  • Two systems: CLAUDE.md you write, and an auto-memory directory the model writes.
  • Of 470 sessions here, 22 carried the auto-memory instruction, first on 2026-09-07.
  • 59 memory directories exist; 4 hold anything. Total output: 11 files, 11,473 bytes.
  • Session count does not predict memories. The 218-session project has none; a 7-session project has three.
  • The memory schema declares four types and only project has ever been written here.

Two systems, one word

Ask where Claude Code keeps its memory and you get two correct answers.

CLAUDE.md is a file you author and commit. It is discovered at startup, loaded into the system prompt, and charged against your context window on every single turn. It is the right home for anything that must hold continuously.

Auto-memory is a directory of small markdown files the model writes when it judges something worth keeping. Each file holds one fact with frontmatter describing it, and an index file lists them. They are recalled when relevant rather than held open.

The distinction that matters for both cost and correctness: CLAUDE.md is always in the room, auto-memory is fetched. A rule you need enforced cannot live in a system that only surfaces when something looks relevant.

The CLAUDE.md search path

Claude Code looks for CLAUDE.md in several scopes, and they layer rather than override:

ScopePathPresent here
EnterpriseC:/ProgramData/ClaudeCode/CLAUDE.mdNo
User~/.claude/CLAUDE.mdNo
Project<repo>/CLAUDE.mdYes — 10,132 bytes
Project-local<repo>/CLAUDE.local.mdNo
ParentD:/Projects/CLAUDE.mdNo

One of five. That is worth sitting with, because the hierarchy is usually described as though teams run all of it, and the common real configuration is a single committed project file doing all of the work — 10,132 bytes of it, on every turn, which is the cost context window management is mostly about.

The user-level file is the one most people are missing. A preference that is true of you rather than of a repository — how you want commits written, which package manager you use everywhere — belongs in ~/.claude/CLAUDE.md, not copied into each project's file. Ours does not exist either; that is a finding about this machine, not a recommendation.

What one machine actually has

Numbers first, mechanism after.

MeasureCount
Sessions on this machine470
Sessions carrying the auto-memory instruction22
Project directories64
Memory directories59
Memory directories holding anything4
Memory files written11
Total bytes written11,473

The first reading of that table is that the feature barely fires. The second, correct reading is that it has barely had the chance: the instruction first appears in a session dated 2026-09-07, so 448 of the 470 sessions predate it entirely. Nine days, 22 eligible sessions, 11 files — roughly one memory every other session that had the feature at all.

The auto-memory directory

The layout is one directory per project, keyed by a flattened path:

~/.claude/projects/<project-slug>/
memory/
  MEMORY.md                        ← the index, one line per memory
  lint-crlf-noise.md               ← one fact
  seo-keyword-map-workflow.md      ← one fact
<session-id>.jsonl                 ← transcripts, same directory level

MEMORY.md is the part that makes the system work. It is the index loaded into context each session — one line per memory, pointing at a file — so the model can see what it knows without holding all of it:

memory/MEMORY.md
- [SEO keyword map workflow](seo-keyword-map-workflow.md) — .agents/memory/keyword-map.md owns
  keyword assignments and article status; check §4, update §2/§3.
- [Lint CRLF noise](lint-crlf-noise.md) — npm run lint is red repo-wide from CRLF vs prettier;
  filter `␍` messages.

That is the whole retrieval design: a cheap always-loaded index of one-line hooks, and full files fetched only when a hook looks relevant. Four indexes exist on this machine, one per non-empty directory.

What a written memory looks like

Here is a real one, written by a session on this machine and edited only to wrap lines:

memory/lint-crlf-noise.md
---
name: lint-crlf-noise
description: npm run lint reports ~20k pre-existing "Delete ␍" prettier errors repo-wide;
  filter them to see real findings.
metadata:
  type: project
---

In this repo, `npm run lint` fails with ~20,000 `Delete ␍ prettier/prettier` errors across
every file, including untouched ones. Git is configured with `core.autocrlf=true`, so files
check out CRLF, while `.prettierrc` leaves `endOfLine` at its "lf" default.

**Why:** it is a pre-existing environment condition, not a regression from any change — so a
red `npm run lint` is not by itself evidence that an edit broke something.

**How to apply:** to judge a change, run eslint on the touched files with `--format json` and
drop messages containing `␍`; what remains is the real signal.

Three things are doing the work. The description is what lands in the index, so it has to be a hook rather than a title. The Why and How to apply lines make the fact actionable instead of trivia. And the body answers a question that would otherwise be re-derived — is this red build my fault — which is exactly the kind of thing worth a file.

Memories also cross-reference each other with [[name]] wiki-links; 4 of the 11 files use them. And 5 of the 11 carry an older frontmatter shape with originSessionId and modified keys that the newer files omit, so the schema has already drifted once in nine days.

The yield, and what predicts it

The obvious hypothesis is that heavily used projects accumulate memories. It is wrong here.

ProjectSessionsTranscriptMemories
This site's repo21826 MB0
Its parent directory23115 MB0
A client site760 MB3
Another client site610 MB3

The project with thirty times the sessions of the two productive ones has no memory directory at all, while both small projects have three memories each. Session count is not the variable — eligibility is. One of those 218 sessions ever carried the memory instruction; the small projects were worked on during the nine days the feature has existed.

There is a second effect worth naming, though this machine cannot separate it: this repository carries a 10 KB CLAUDE.md that already records its own traps in detail. A model asked to save only what is not already recorded in the repo has less to save in a project that documents itself well. That is a hypothesis, not a measurement, and it would take a controlled run to test.

Three of four types are unused

Each memory declares a type in its metadata, and the schema offers four: user, feedback, project and reference.

TypeWhat it holdsWritten here
projectOngoing work, goals, constraints7
userWho the operator is, preferences0
feedbackGuidance on how to work0
referencePointers to dashboards, tickets, URLs0

Every memory on this machine is type: project. Three quarters of the taxonomy is unexercised.

The two most interesting absences are user and feedback, because they are the ones that would travel between projects — a preference is a preference everywhere. Their emptiness is consistent with what the directory layout implies: memory is scoped per project, so a fact about the operator gets written into whichever project happened to surface it, or not written at all. If you want a cross-project fact to hold, the user-level CLAUDE.md remains the only reliable home for it.

Which system to put a fact in

The decision rule, which is the thing most memory advice omits:

  • Must be true every turn, with consequences if skippedCLAUDE.md. Build commands, architectural rules, "never do X". You are paying context for it precisely so it cannot be missed.
  • A fact discovered once, expensive to re-derive → memory. The CRLF example above is perfect: nobody needs it most days, and rediscovering it costs twenty minutes.
  • True of you rather than the repo → user-level CLAUDE.md. Memory is per-project and will not carry it across.
  • Already recorded in the repo → neither. Code structure, git history and existing docs are cheaper to read than to duplicate, and a duplicated fact goes stale independently.
  • Only matters in this conversation → neither. Not everything worth saying is worth keeping.

The sharpest version of the test: would you accept this being silently skipped? If not, it is not a memory — it is a rule, and rules go in the file that loads every turn.

What did not work

The first reading of the headline was wrong, twice. Initially the busiest project was recorded as having an empty memory directory — a tidy story about a heavily used repo that never learned anything. It has no memory directory at all; the empty one belonged to its parent. Then the directories themselves were assumed to be created by the memory feature, until the oldest turned out to be dated 2026-08-09, 29 days before the first session carrying the instruction. Directory creation and the memory feature are not the same event, and the second version of the finding is the opposite of a story about disuse.

No probe session was launched. Every nested claude -p invocation on this machine is refused by its auto-mode classifier, so nothing here is a controlled experiment. It is a census of what one machine wrote on its own over nine days, which is why the CLAUDE.md-suppression idea above is labelled a hypothesis.

Transcript contents could not be parsed. Reading inside the .jsonl files is refused under a PII rule on this machine, so session counts come from filenames and the instruction census from grep -l, which reports that a file matched and nothing about where or how often. A per-session count of memory writes would need that access and does not exist here.

Best practices

  • Put enforceable rules in CLAUDE.md, not memory. Memory is recalled when it looks relevant; a rule that only sometimes applies is not a rule.
  • Create the user-level file. ~/.claude/CLAUDE.md is the only place a cross-project preference reliably lives, and most machines — including this one — do not have it.
  • Write memory descriptions as hooks, not titles. The description is what appears in the always-loaded index and decides whether the file is ever fetched.
  • Keep one fact per file. The schema is built for it and the index reads better.
  • Check the memory directory when behaviour changes unexpectedly. It is model-written state you did not author, and it is a plain directory you can read and delete.
  • Delete memories that turn out wrong. A stale memory is worse than none, exactly like a stale line in CLAUDE.md.

Common mistakes

Assuming an empty memory directory is a bug. 55 of the 59 directories here are empty. The instruction has existed for nine days.

Expecting memory to replace CLAUDE.md. Different loading model, different guarantees. One is always present; the other is fetched.

Putting preferences in a project memory. Memory is per-project. A fact about how you like to work will not follow you to the next repository.

Duplicating what the repo already records. It goes stale independently of the thing it describes, and now two sources disagree.

Reading session volume as memory volume. Our busiest project has 218 sessions, 26 MB of transcript and zero memories.

Conclusion

Treat these as two tools: write CLAUDE.md — project and user level — for everything that must hold every turn, and let the memory directory collect the expensive one-off facts you would otherwise re-derive. Then go and look at yours, because it is a plain directory of markdown you can read, edit and delete, and most people have never opened it. The number worth carrying is 11 files in nine days across 470 sessions: this system accumulates slowly, so the durable rules are still yours to write by hand.

Frequently asked questions

Where do Claude Code memory files live?
Two places. CLAUDE.md is read from up to five locations — enterprise, user, project, project-local and parent directories — and you write it by hand. Auto-memory is separate: one directory per project under ~/.claude/projects/<slug>/memory/, holding one markdown file per fact plus a MEMORY.md index, written by the model rather than by you.
What is the difference between CLAUDE.md and Claude Code memory?
Cost and authorship. CLAUDE.md is yours, loads on every turn and is paid for on every turn. Auto-memory files are written by the model when it judges something worth keeping, and are recalled selectively rather than held in context continuously. A rule you need enforced belongs in CLAUDE.md; a fact discovered once belongs in memory.
Why is my Claude Code memory directory empty?
Most likely nothing is wrong. Across 470 sessions on this machine only 22 carried the auto-memory instruction, first appearing on 2026-09-07 — so the vast majority of sessions never had the feature active. 59 memory directories exist and 4 of them hold anything, and the oldest directory predates the first instruction by 29 days.
How many memories does Claude Code actually write?
Fewer than you would expect. Nine days and 22 instruction-carrying sessions produced 11 files totalling 11,473 bytes across four projects. The busiest project on this machine — 218 sessions and 26 MB of transcript — has no memory directory at all, while a project with 7 sessions has three memories.
Should I write CLAUDE.md or let memory handle it?
Write CLAUDE.md for anything that must be true every turn — build commands, architectural constraints, rules with consequences. Leave memory for facts discovered in passing that would cost a re-derivation later. The deciding question is whether you would accept the rule being silently skipped; if not, it is not a memory, it is a rule.

Muhammad Kashif

Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.