Key Takeaways
- Claude Code memory is just files. You write
CLAUDE.mdwith your rules, and Claude keeps its own notes, called auto memory, in a folder on your machine. Nothing else carries over between sessions. - Anything you only said in chat is lost. It’s gone after
/clearor a new session, so put whatever Claude should remember tomorrow into a file. - Put rules where Claude reads them automatically. In our tests, Sonnet and Haiku ignored a rule tucked into a vaguely labelled memory note every time. The same rule in
CLAUDE.mdwas followed every time. - Keep Claude’s notes short. It reads only the top of its notes index, at most 200 lines, and new notes go at the bottom, so they’re the first to drop out of view.
- Your team can’t see Claude’s memory. It never leaves your laptop, so write the decisions your team needs into the repo, with the reason.
- Stopping halfway through a task? Have Claude write a short handoff note with the goal, what failed and the next step. Tomorrow’s session starts from there, and this post includes a ready-made
/handoffskill.
Claude Code memory is the set of plain files Claude reads when a session starts: the CLAUDE.md files you write, and the notes Claude writes for itself in a folder called auto memory. When you run /clear or open a new session, the conversation is gone, along with anything you only said in chat. Nothing else carries over from one session to the next.
We wanted to know what actually happens, so we tested it: what loads, which rules get followed, what survives /compact (the command that shrinks a long conversation into a summary), and how a fresh session picks up a half-finished task. Our main finding is that whether Claude follows a rule depends on which file holds it, and on which model you run.
How we tested
Claude Code 2.1.316, the current version on November 1, 2026, in fresh non-interactive sessions started with claude -p, on a small Next.js test repo, with Opus 5.5, Sonnet 5.5 and Haiku 5.5. The model comparisons are 6 to 12 runs per cell, and we sent /compact and /clear the same way, through claude -p --resume. For what loads and what survives, we pointed Claude Code at a local logger through the documented ANTHROPIC_BASE_URL setting and read the exact request it sent. It is one repo and small samples, so read the numbers as a direction.
How Claude Code Memory Works
Claude Code has two kinds of memory, and the difference is who writes them. CLAUDE.md files are instructions you write: one in the repo for the team, and one in your home folder for yourself. Auto memory is a folder of notes Claude keeps on its own, written when it learns something it thinks a future session will need, such as a correction you gave it. Auto memory is on by default.
| CLAUDE.md | Auto memory | |
|---|---|---|
| Written by | You, or Claude when you ask | Claude |
| Lives in | The repo, and ~/.claude/ for your own | Your home folder, outside the repo |
| Shared | With the team, once committed | Never, it stays on your machine |
| Loaded | The whole file, every session | The start of MEMORY.md, every session |
| Best for | Rules and facts everyone needs | What Claude learned working with you |
Both arrive as context. The Claude Code memory docs say it plainly: Claude treats them “as context, not enforced configuration,” so Claude reads them like instructions typed into the chat and usually follows them. If something must never happen, such as a push to main, it needs a deny rule in your settings or a hook, a script Claude Code runs at fixed points whatever the model decides. Our guide to hooks and skills shows a working hook.
The conversation is saved as well, but only as a transcript you can reopen with claude --continue or claude --resume. A new session starts without it.
What Loads When a Session Starts
At startup Claude Code reads every always-on instruction file in full, plus the beginning of one memory file. Everything else waits until Claude touches something that needs it. This is what our logged requests showed.
| File | When it loads |
|---|---|
CLAUDE.md | At start, in full: the repo root, parent folders and .claude/ |
@path imports | At start. A line like @docs/api.md inside CLAUDE.md pulls that file in |
CLAUDE.local.md | At start. It is yours and stays out of git |
Rules, no paths: | At start, from .claude/rules/ |
MEMORY.md | At start, but only the first 200 lines or 25,000 characters |
Rules with paths: | When Claude reads or edits a matching file |
Subfolder CLAUDE.md | When Claude reads a file in that folder |
| Other memory files | Only when Claude decides to open one |
None of these files overrides another. Claude Code joins them into one block of context, and if two of them disagree, the model picks. Our CLAUDE.md troubleshooting guide lists the exact load order and the ways a file can fail to load at all.
Where auto memory lives
Auto memory sits outside your repo, in ~/.claude/projects/, in a folder named after the repo’s full path with every character that isn’t a letter or digit turned into a dash. Every subfolder and git worktree (a second checkout of the same repo) shares that folder; we checked both. Outside a git repo each folder gets its own, so starting Claude from a subfolder of a plain directory gives you a separate, empty memory; we checked that too. Type /memory in a session to open the folder.
Inside is an index, MEMORY.md, with one line per memory, and a small topic file for each memory. Here is what Claude wrote after we told it once that we never deploy on Fridays. First the index line, wrapped to fit:
- [No Friday prod deploys](no-friday-prod-deploys.md) — never deploy to
production on Fridays; weekend on-call rota is empty Then the file it points to, with line breaks added for width and nothing else changed:
---
name: no-friday-prod-deploys
description: Never deploy to production on Fridays — no on-call coverage
at weekends
metadata:
node_type: memory
type: project
originSessionId: 04865c9a-2c62-4225-8e58-8e5ae4263d1f
modified: 2026-11-02T02:07:49.314Z
---
Never deploy to production on a Friday.
**Why:** The on-call rota is empty at weekends, so nobody is available
to respond if a Friday deploy breaks something.
**How to apply:** Before running, scheduling, or recommending a
production deploy, check the day of the week. If it's Friday, don't
deploy; say why and suggest Monday through Thursday instead. Apply the
same check to scheduled or cron-triggered deploys that would land on a
Friday. Notice that the index line states the rule itself, which matters for the test in the next section.
Where a Rule Actually Gets Followed
We gave Claude a task that never mentioned the rule, then checked whether the code followed it anyway. The rule: every new API route must call logAudit() before it returns. The task: “Add a GET route at src/app/api/orders/count/route.ts that returns the number of orders as JSON.” The test repo’s MEMORY.md held about as many entries as a real project collects in a few months, and we moved the rule between five places. Opus 5.5 followed it every time, wherever it lived. Sonnet 5.5 and Haiku 5.5 did not:
| Rule in | Sonnet | Haiku |
|---|---|---|
CLAUDE.md | 6 of 6 | 6 of 6 |
| Rule file | 6 of 6 | 6 of 6 |
MEMORY.md line | 6 of 6 | 6 of 6 |
| Topic file, clear | 10 of 12 | 1 of 6 |
| Topic file, vague | 0 of 12 | 0 of 6 |
The rule file was .claude/rules/api.md, scoped with paths: to the API folder. The first three places are either always in context or load when Claude touches a matching file, and they worked on every model. The results split on topic files. A topic file only reaches Claude if Claude decides to open it, and that decision rests on one line in the index. These are the three versions of that line we tested, from vague to explicit, wrapped to fit:
- [Notes](notes.md) — misc notes
- [API routes](api-routes.md) — read before adding or changing an API
route
- [API routes](api-routes.md) — Every new API route must call
logAudit() from src/lib/audit.ts before it returns. Opus opened the file every time, even behind “misc notes”, though it read several other files first to find it. Sonnet followed a clearly labelled file in most runs, and the two misses ended without writing the route at all. Haiku mostly left the file closed, even with a clear label. Behind “misc notes”, neither Sonnet nor Haiku opened it once.
So write the rule into the index line itself and keep the reason and the detail in the topic file. Claude’s own entries already work this way, as the Friday example shows; the ones that fail are lines that only point somewhere, like “see notes.md”. For a rule about code, a rule file is the better home anyway: it was followed on all three models, and your team gets it through git.
What Happens Past the MEMORY.md Limit
The docs say Claude Code loads the start of MEMORY.md, the first 200 lines or the first 25KB, whichever comes first, and leaves the rest on disk.
To see what the cut means, we put the same deploy rule near the top of a long index in one test and below the cut in another. Then we asked each model for the production deploy command, which only that rule could answer.
| Model | Rule at line 30 | Rule at line 230 |
|---|---|---|
| Opus 5.5 | 6 of 6 | 6 of 6 |
| Sonnet 5.5 | 6 of 6 | 0 of 6 |
| Haiku 5.5 | 6 of 6 | 0 of 6 |
Opus got it right from line 230 because Claude Code tells the model when the index was cut, and Opus went and read the whole file. Sonnet and Haiku answered from what they had. The warning goes to Claude only; nothing appears in your terminal. This is the text Claude Code adds, captured from a 250-line test index and wrapped to fit:
> WARNING: MEMORY.md is 250 lines (limit: 200). Only part of it was
loaded: 50 of 250 lines were cut off, starting at line 201 ("- [Staging
resets](staging-resets.md) — the staging database resets every…").
Keep index entries to one line under ~200 chars; move detail into topic
files. Claude Code counts that limit in characters: in our test, an index full of em dashes, which take several bytes each, loaded well past 25KB of bytes before the cut.
Claude also writes long index lines. Ours average about 270 characters, so on our folder the character limit would arrive long before the line limit.
New entries go at the bottom, so the newest memories are the first to fall off. When a save pushes the index over the limit, Claude Code tells Claude to rewrite it. In our test, Sonnet and Opus rewrote an over-long index down to about 30 lines without asking, and the old topic files stayed on disk with nothing pointing to them. Haiku added the new entry below the cut-off, where it won’t load, and said so.
None of this applies to CLAUDE.md. It loads whole, and in our test a very long file arrived complete. The docs set one ceiling, 4 MiB, above which a file is skipped entirely, and ours vanished without any message. Length still costs tokens in every session, so short is better there too.
Where Each Kind of Fact Belongs
Two questions decide where a fact goes: who needs it, and for how long. Choosing these homes on purpose is what people have started calling memory engineering. The table covers the eight kinds of fact we keep.
| Kind of fact | Example | Put it in |
|---|---|---|
| Team fact | Tests run with pnpm test; we deploy to Cloud Run | CLAUDE.md in the repo |
| Code rule | Every query is scoped to the organization | A rule file with paths: |
| Hard rule | Never push to main | A permission deny rule or a hook |
| Your style | Short answers, in every repo | CLAUDE.md in your home folder |
| Your setup | My local Postgres runs on port 5433 | CLAUDE.local.md |
| Learned note | You review schema changes before any migration | Auto memory, rule in the index line |
| Decision | We count rows instead of adding Redis, because… | docs/decisions/ |
| Task state | Migration not written yet, waiting on review | HANDOFF.md, deleted after |
Two rows are easy to get wrong. Auto memory belongs to one repo, so a preference you want everywhere goes in ~/.claude/CLAUDE.md, the one in your home folder. And Claude files team conventions into auto memory on its own: when we mentioned once that the team uses named exports, it saved that to its notes in all five runs. Those notes never leave your machine, so a convention the team needs should move into CLAUDE.md or a rule file. If your team mixes Claude Code with other tools, our CLAUDE.md vs AGENTS.md comparison covers which instruction file to use.
Keep every file short as well, because extra context costs money. A 2026 study of coding agents, Claude Code among them, found that repository context files did not generally improve task success and raised inference cost by over 20% on average (Gloaguen et al., arXiv 2026). So write down only what the agent can’t work out from the code.
Put Decisions in the Repo
Auto memory is the wrong place for decisions, because nobody else can read it. The docs describe it as “machine-local”: your teammates can’t see it, and neither can your next laptop. A decision record also has to say why. A session that finds export limits counted in Postgres will happily “improve” them with Redis unless something says why it was not done that way.
The format that has lasted is the architecture decision record from Michael Nygard: a short file with a title, the context, the decision, its status and its consequences (Nygard, 2011). A reversed decision stays in the folder, marked as superseded. Here is one from our test project, saved as docs/decisions/0007-export-rate-limit.md, with a Rejected section added because that is the part an agent needs most:
# 7. Rate-limit exports by counting rows in Postgres
Status: Accepted, 2026-11-02
## Context
Exports are expensive to run. Production runs on 3 Cloud Run
instances, so an in-memory counter gives each organization three
times the limit. We have no Redis.
## Decision
Count the organization's Export rows from the last hour, with an index
on (organizationId, createdAt). Over 10, return 429 with Retry-After.
## Rejected
- In-memory counter: it counts per instance, so the limit is wrong.
- Redis: new infrastructure for one feature.
## Consequences
Count and insert are not atomic, so a burst can overshoot by a few.
Revisit if we add Redis for anything else. Then add a short section to CLAUDE.md so Claude knows the folder exists:
## Decisions
Records live in docs/decisions/, one file per decision. Read the
matching one before changing rate limits, billing or auth, and add
a new record when a change makes or reverses a decision. We tested this by asking Sonnet to move the export limit to Redis. In all three runs it opened the record first, quoted its Rejected line back to us, and planned a new record to supersede it. Without the pointer it still found the record in our small test repo by searching for “rate limit”; in a larger repo, the pointer makes sure Claude checks there first.
The pointer loads every session and the records load only when Claude needs one. That is the same index-and-files pattern auto memory uses, kept somewhere your team can read it. Our post on memory for SaaS AI agents shows the same pattern inside a SaaS product’s own AI agent.
What Survives /compact, /clear and --resume
A session can lose its context in three ways, and each keeps different things. /compact replaces the conversation with a summary; Claude Code also compacts on its own when the context fills up, and it can compact a session you left idle, which idleCompaction: false turns off. /clear starts an empty conversation. --resume and --continue reload a saved one. Here is what each kept in our logged requests. “Re-read” means Claude Code loaded the file fresh from disk, and “on next use” means it came back when Claude next touched a matching file.
| What | /compact | /clear | --resume |
|---|---|---|---|
Root CLAUDE.md | Re-read | Re-read | Re-read, with your edits |
MEMORY.md | Re-read | Re-read | Re-read |
| Scoped rules | On next use | On next use | Kept |
| Chat | Summary only | Gone | Kept |
| Open files | Recent ones | Gone | Kept |
The chat row matters most. A compaction summary is written by the model, and Anthropic’s own guide to sessions calls compact “lossy” (Thariq Shihipar, Anthropic, April 2026). In our run, an instruction we had given in chat made it into the summary and kept working, but nothing guarantees that. Anything you will still need after the next compaction belongs in a file.
Scoped rules behave differently too. “Scoped rules” here means both path-scoped rule files and CLAUDE.md files in subfolders. After a compaction ours were gone from the context, and they came back only when Claude read another file in that folder. That is fine for coding, and worth knowing if a rule seems to stop working late in a long session. The compaction docs list the rest. You can also tell the summary what to keep by adding a focus, as in /compact keep the schema decisions.
How to Hand Off Mid-Task Work
A handoff is a short file that lets a fresh session pick up a half-finished task without the old conversation. You don’t always need one. The same Anthropic guide recommends a new session for every new task, and the right move depends on where you are:
- Same task, context getting long:
/compact, with a note on what to keep. - Same task tomorrow, nothing changed:
claude --continue. - New task:
/clear. - The session went off track, or someone else takes over: write a handoff, then
/clear.
The handoff is a skill you run as /handoff. Save it as .claude/skills/handoff/SKILL.md. The disable-model-invocation line means only you can start it, so Claude won’t write handoffs on its own.
---
name: handoff
description: Write HANDOFF.md so a fresh session can pick up this task.
disable-model-invocation: true
---
Write HANDOFF.md in the repository root, replacing any existing one.
The reader is a new session that cannot see this conversation, so name
files, commands and errors exactly. Stay under 60 lines.
Run `git status --short` and `git log -1 --oneline` first. Report what
they print; do not guess the state.
Use these sections, in this order:
## Goal
One sentence: what done looks like.
## State
Branch, last commit, and the uncommitted files with one line each.
## Done
Finished and verified work, with file paths.
## Tried and failed
Each approach that did not work and why, so nobody tries it again.
Write "none" if there were none.
## Decisions
Choices made in this session, each with its reason.
## Next
The next one to three steps, specific enough to start without asking.
Then print the file. Tell the user to run /clear or start a new session,
and to delete HANDOFF.md once the task is merged. Then add a hook so the next session reads the file without being asked. Put this in .claude/settings.json, merged with any hooks you already have, and add HANDOFF.md to .gitignore:
{
"hooks": {
"SessionStart": [{
"matcher": "startup|clear",
"hooks": [{
"type": "command",
"command": "cat \"$CLAUDE_PROJECT_DIR/HANDOFF.md\" 2>/dev/null; true"
}]
}]
}
} The hook runs when a session starts and after /clear, and whatever it prints becomes part of Claude’s context. One catch from our testing: start Claude Code from the repo root. Started from a subfolder, it didn’t read the repo’s .claude/settings.json, so the hook never ran, although the skill itself still loaded.
Here is the file the skill wrote in our test, after a session where we rejected an in-memory rate limiter and switched to counting rows in Postgres. We trimmed it to about half, cutting the Done section and several lines, and wrapped it to fit; nothing is reworded.
# Handoff
## Goal
POST /api/export returns 429 once an organization has made 10 exports
in the last hour, enforced across all 3 Cloud Run instances, with the
migration reviewed and committed.
## State
- Branch: `feat/export-rate-limit`
- Last commit: `d74b2da Add per-organization rate limit to POST
/api/export` (the old in-memory version)
- `git status --short` prints:
- ` M prisma/schema.prisma`: added
`@@index([organizationId, createdAt])` to `Export`
- ` M src/app/api/export/route.ts`: imports and awaits
`checkExportRateLimit(organizationId, 10, 3600000)`
- ` M src/lib/rate-limit.ts`: the in-memory Map is replaced by a
DB-backed `checkExportRateLimit`
## Tried and failed
- In-memory `Map` limiter (commit `d74b2da`): state is per process, so
3 Cloud Run instances allow about 30 exports per hour per org. The
user rejected it.
## Decisions
- Count `Export` rows instead of using Redis. This follows the user's
instruction, and the limit is then shared across instances with no
new infrastructure.
- No migration yet. The user wants to review the schema change first.
## Next
1. Wait for the user's approval of the schema diff
(`git diff prisma/schema.prisma`). Then generate the migration with
`npx prisma migrate dev --name add_export_org_created_index`.
2. Run a type check (for example `npx tsc --noEmit`) and fix any
errors in `rate-limit.ts` or `route.ts`. We then started three fresh sessions with one prompt: “Continue the task.” All three picked up at the right step and asked us to approve the schema change before writing the migration. None went back to the in-memory limiter the handoff marks as failed. The Tried and failed section is the one to keep if you trim the rest, because it holds exactly what a new session can’t work out from the code. A loop that runs without you needs the same thing in a state file, and our loop engineering guide shows one.
A handoff covers the task, and you delete it once the task is merged. The lessons from a session, such as a correction you had to give twice, belong in memory, a rule file or CLAUDE.md. The free session-post-mortem skill at the end of this post sorts each lesson into the right one of those.
Check Your Memory Once a Month
Memory files go out of date without any warning. One of our own, written in September, said that Claude Code reads only CLAUDE.md and ignores AGENTS.md. That stopped being true on September 18, when version 2.1.277 started reading AGENTS.md in repos that have no CLAUDE.md.
A June study of AI configuration files found that nearly a quarter of the repositories checked had at least one reference to code that no longer existed (Treude and Baltes, arXiv 2026). The authors call that figure “a feasibility signal” rather than a precise rate.
Claude catches some of this itself. When a memory named a file that had moved, or a session length the code had since changed, Opus checked the code and gave the current answer in every run, and so did Sonnet in the test we ran on it, because Claude Code’s memory instructions tell it to verify. When a memory contradicted CLAUDE.md, the CLAUDE.md rule won on both models every time, even when the memory was newer. The memories to review by hand are the ones the code can’t contradict, such as a decision, a deadline or a preference that changed.
So once a month, ask Claude to audit its own memory. It already knows where the folder is, and it can check each entry against the code, which a script can’t. Tell it to report only: in our over-limit test, Claude rewrote the index without being asked.
Your prompt
Check your auto memory for this project and report back. Don’t edit, move or delete anything.
1) How close is MEMORY.md to its limits of 200 lines and 25,000 characters?
2) Which memory files does the index not link to, and which index lines point to files that don’t exist?
3) Which entries contradict the current code? Check each one against the repo.
4) Which entries can’t be checked against the code, like decisions or preferences? List them so I can review them.
We ran this prompt on a test project seeded with four problems: two entries the code contradicted, a memory file the index didn’t link to, and an index line pointing to a file that didn’t exist. Opus, Sonnet and Haiku found all four in every one of seven runs, listed the decisions and preferences for us to review, and left every file untouched. The size figures came out slightly high, because the models counted bytes rather than characters, which is close enough for this check.
Claude can’t use a memory file the index doesn’t link to, so link each one the audit finds or delete it. Claude Code also has a background cleanup called auto-dream, which you can switch on in /memory, according to a Claude Code maintainer (GitHub, August 2026). The docs don’t describe it yet, so check what it changed before you rely on it.
If you’d rather not write these files from scratch, the VibeReady AI Framework sets up CLAUDE.md, rule files and the session-post-mortem skill for your codebase. See editions from $99 →
Frequently Asked Questions
Where does Claude Code store its memory?
CLAUDE.md files live where you put them: the repo root, .claude/, subfolders, and ~/.claude/CLAUDE.md for your personal rules. Auto memory lives outside the repo, in ~/.claude/projects/ under a folder named after the repo's path, as a MEMORY.md index plus one file per memory. Type /memory in a session to open it.
Does Claude Code remember between sessions?
Only through files. Every new session loads your CLAUDE.md files in full and the first 200 lines or 25,000 characters of MEMORY.md. The conversation itself does not carry over unless you reopen it with claude --continue or claude --resume.
How do I turn off Claude Code auto memory?
Use the toggle in /memory, set autoMemoryEnabled to false in a settings file, or set the environment variable CLAUDE_CODE_DISABLE_AUTO_MEMORY=1. We tested the setting and the variable: with either one, Claude Code sends no memory and no memory instructions to the model.
What happened to the # shortcut for adding a memory?
Claude Code removed it in version 2.0.70 in December 2025. Ask Claude in plain words to remember something, which saves it to auto memory, or ask it to edit CLAUDE.md when the rule is for the whole team.
Is Claude Code memory shared with my team?
CLAUDE.md and rule files are, once you commit them. Auto memory is not: the docs call it machine-local, so it never reaches teammates, your other machines or cloud sessions. Put anything the team needs in CLAUDE.md, a rule file or a decision record in the repo.
Do I need a memory plugin for Claude Code?
For one developer on one machine, usually not. The built-in files cover rules, preferences, decisions and handoffs, and they cost nothing. A plugin starts to make sense when memory has to follow you across machines or tools. Many guides on this topic come from companies that sell a plugin, so try the built-in files first.
Have more questions? See our full FAQ →