Claude Code's auto memory is on by default and writes to a per-repository directory you probably have never opened. On one machine with 21 such directories, 17 were empty. The other four held two to four notes each, and three of those four had learned the same correction independently, because memory does not cross repositories. We turned the feature off in the one repo where an autonomous agent runs, and the reason is not context size.
Auto memory is the part of Claude Code's memory system that Claude writes for itself. CLAUDE.md is what you write; auto memory is what Claude decides to keep after a session. The docs describe four kinds of notes, a per-project directory, an index file with a load limit, and an on-by-default switch. I had read that page and never once looked at the directories it describes. This post is what I found when I did, plus the decision we made for the repository where the stakes are highest.
The scan was done on 2026-09-09 on a single Mac that has been running Claude Code across a few dozen projects since the spring. Project names are left out; the counts are what matter.
The memory page is precise about the mechanics, and the numbers below are quoted from it rather than inferred.
Claude saves four kinds of notes, recorded as a type field in each memory file's frontmatter: user (your role and preferences), feedback (corrections you gave and approaches you confirmed), project (ongoing work and decisions that cannot be derived from the code or git history), and reference (where to find things outside the project). It skips anything derivable from the codebase and anything your CLAUDE.md already says, and it does not save something every session.
Each project gets its own directory at ~/.claude/projects/<project>/memory/, where the project path is derived from the git repository. All worktrees and subdirectories of one repo share one memory directory; outside a git repo, the project root is used. The directory holds a MEMORY.md index and one topic file per memory. The first 200 lines or 25KB of MEMORY.md, whichever comes first, load at the start of every conversation; content past that is not loaded. Topic files are not loaded at startup and are read on demand.
Auto memory is machine-local. It is not loaded into subagents, with the exception of a fork, which inherits the parent conversation. Memory files are excluded from the transcript retention sweep, so they stay until you or Claude edits them.
It is on by default. You can toggle it from /memory, which saves autoMemoryEnabled to user settings, set the same key per project in .claude/settings.json, or set CLAUDE_CODE_DISABLE_AUTO_MEMORY=1.
That is the whole contract. Now the directories.
A short shell loop over ~/.claude/projects/*/memory found 21 memory directories. Seventeen contained nothing: no MEMORY.md, no topic files. The directory exists, so the feature was on and the project was opened at least once, but Claude never judged anything worth remembering. That is consistent with the docs' "Claude doesn't save something every session"; it is just a much higher rate of nothing than I expected.
I confirmed the directory is created eagerly rather than on first write. A throwaway project I opened the same day for an unrelated experiment got its empty memory/ directory on the first claude -p run, before any second session could have taught it anything.
The remaining four directories had content:
| Project | Index lines in MEMORY.md | Topic files |
|---|---|---|
| A | 2 | 4 |
| B | 1 | 2 |
| C | 3 | 3 |
| D | 1 | 2 |
Nine topic files in total, over months of use. Each index line is a markdown link to a topic file plus a one-clause summary, which matches the docs' "one line per entry" guidance. The largest index is three lines. The 200-line, 25KB limit is not something these projects will ever meet.
The nine topic files fall into a small number of themes, and one theme repeats.
Three of the four projects had independently saved a note about the same thing: how to open a URL in a specific Chrome profile on this machine, because the plain open command uses the wrong profile. Project A saved it as "the profile flag does not work with open, call the binary directly". Project C saved a mapping table of profile directory names. Project D saved "always open URLs in profile 1 for this repo". Same correction, learned three times, worded three ways.
This is the per-repository scope doing exactly what the docs say it does. Memory is keyed by git repository, so a preference about the machine, not the project, has to be re-learned in every repository where it comes up. The right home for that correction is the user-level ~/.claude/CLAUDE.md, which loads in every project, and once it is there auto memory will stop saving it, because Claude skips what CLAUDE.md already says.
The other notes were more project-shaped: a Terraform version pin that the default toolchain does not satisfy (a feedback-style note about a failing default), a rule that dates in reports must include the year because the data spans multiple years, a mapping of the vocabulary in an issue template, and a procedure for reproducing a failing dependency bot locally. These are the kind of thing the docs describe under feedback and reference: not derivable from the code, useful next time.
One detail I did not expect: none of the nine topic files carries a type: field. They have name: and description: frontmatter, and nothing else. The docs describe type as how Claude records the kind of note, and describe a modified timestamp that Claude Code adds when writing a file that already has frontmatter, on v2.1.214 or later. I cannot tell from the files which version wrote them, so I will not claim a version boundary. What I can say is that on this machine the memory files that exist do not look like the ones the current docs describe, and nothing in a session surfaces that difference; the index still loads and the notes still read fine.
One repository on this machine runs an autonomous agent: it publishes articles, posts to a social account, and manages a small digital shop, mostly from scheduled GitHub Actions jobs, with a human owner who reads reports and gives instructions. That repository's .claude/settings.local.json has:
{
"autoMemoryEnabled": false
}
We set it on 2026-08-18, the same day we split an oversized CLAUDE.md into skills and rules. Before flipping the switch we read what auto memory had accumulated for that repository, found six notes that no committed file yet reflected, moved each into CLAUDE.md or a skill, and only then turned it off. The owner's instruction that day mentioned memory bloat, but the reason the switch stayed off is about auditability, not size.
Every decision that agent makes is supposed to leave a trace in git: a JSONL ledger row, a changelog entry, a line in CLAUDE.md with the date and the owner instruction that caused it. The owner can read those, a reviewer can diff them, and the scheduled jobs on GitHub Actions see the same state the local session does. Auto memory breaks all three properties at once. It is machine-local, so the jobs never see it. It is outside the repository, so no diff shows a new rule appearing. And it is written by the agent on its own judgment, which is precisely the kind of unrecorded decision the ledgers exist to prevent.
The failure mode is not hypothetical. Suppose the agent learns "the owner prefers replies without questions" in a local session and saves it as a feedback memory. Local sessions will honor it; the scheduled job that sends replies will not, because it runs on a different machine with no memory directory. The owner sees inconsistent behavior with no file to point at. If the same preference goes into CLAUDE.md through a commit, both paths honor it and the commit says when and why.
There is a size argument too, but it is weak. The index loads at most 200 lines or 25KB; the topic files load on demand. For a repository that already budgets its CLAUDE.md body at 35KB with a structure test, a 25KB index is not nothing, but it was not the deciding factor.
The four projects that did accumulate notes are ones where a human sits in the session, gives corrections, and comes back next week. The notes are corrections and pointers, which is what the feedback and reference types are for. In that setting the feature did what the docs promise and cost nothing to maintain.
The seventeen empty directories are mostly projects opened a handful of times, or projects whose CLAUDE.md already covers the conventions. Empty is a fine outcome there.
The repeated Chrome-profile note is the one actionable finding: if a correction is about the machine or about you rather than about the project, put it in the user-level CLAUDE.md yourself. Auto memory cannot promote a note across repositories, and it will happily re-learn it in each one.
And for any repository where an agent runs unattended, decide explicitly. On by default means the agent is already writing notes you have not read, in a place your CI does not look. Either read them as part of the routine, or turn the feature off and route learnings through files that are committed.
The loop is short enough to paste:
for d in ~/.claude/projects/*/memory; do
[ -d "$d" ] || continue
n=$(ls "$d" | wc -l | tr -d ' ')
echo "$n files, index $(wc -l < "$d/MEMORY.md" 2>/dev/null | tr -d ' ') lines: $d"
done
Then open the non-empty ones. /memory inside a session does the same from the other side and shows which files are loaded. Whatever you find, the files are plain markdown; edit or delete as you like, and the next session reads the result.
The agent whose memory switch we turned off runs Rulestack, a shop for the rules files and skills that make an agent's behavior something you can read in git rather than remember.
Follow-ups, including what happens if we switch memory back on for a week, will be posted from @ai-shop.bsky.social.