How to Give Claude Code Persistent Memory Across Sessions
Claude Code forgets everything between sessions. You correct it, the session ends, and the correction dies with the transcript. Next session makes the same mistake.
I wrote about how I found this out the hard way, when an outreach agent addressed women creators as “man” in 11 of 19 drafts. This post is the setup itself, stripped of the story, so you can copy it.
What is a memory file?
One markdown file holding one fact, with frontmatter carrying a name, a one-line description, and a type. The body explains the rule and why it exists.
Here is a real one from my repo, unedited:
---
name: feedback_outreach_gender_neutral_address
description: "All outreach/comment copy must be gender-neutral, never 'man'/'bro'/'girl'/'sis'; a transcript doesn't tell you who's speaking"
metadata:
type: feedback
---
Reno (2026-07-16), reviewing DayOne comment-drafter test output: "let's be gender
neutral, it returned man and it was a woman speaking."
**Why:** The drafter only sees a transcript, which carries no reliable gender signal.
A gendered address at the wrong person is worse than any AI tell, it proves nobody
watched the video. The tic came from listing 'man' as an allowed casual filler in the
prompt; the model then used it in 11/19 drafts.
**How to apply:** Any generated copy addressed at a specific unknown person bans
gendered address outright: no 'man', 'bro', 'dude', 'girl', 'sis', 'babe'. Address
with 'you' or not at all. Never put a gendered word in a filler allowlist, allowlists
become tics.
The Why and How to apply sections are not decoration. A rule without a reason gets misapplied at the edges, and a rule without an application clause gets read and ignored.
Where do memory files live?
A flat directory. Mine sits alongside the project, with a single index file at the root of it:
memory/
MEMORY.md <- the index, loads every session
feedback_outreach_gender_neutral_address.md
reference_asc_rest_data_pull.md
project_portfolio_north_star_10k.md
...
Flat matters. Nested folders make the agent guess at paths, and a guess is a failed read.
How does the agent know which file to read?
It does not read them all. That would blow the context window by file 40.
MEMORY.md is an index: one line per memory, each with a link and a compressed hook.
- [Gender-neutral outreach](feedback_outreach_gender_neutral_address.md) never man/bro/girl; allowlisted fillers become tics
- [ASC REST data pull](reference_asc_rest_data_pull.md) KEY_ID mismatch returns 401
The agent loads the index at session start, scans the descriptions against what it is about to do, and pulls only the matching files. An outreach session reads the outreach and voice files. A data-pipeline session reads the pipeline files. Nothing else enters the context.
The description field is the entire retrieval mechanism. It is the only thing the agent sees before deciding whether to spend context on the full file. Write it as the specific claim, not the topic. “Gendered address words banned in outreach copy” lets a session decide in one read. “General writing guidelines” does not.
What tells the agent to do any of this?
A few lines in CLAUDE.md, the project instruction file Claude Code loads automatically:
Memory files are in /path/to/memory/. MEMORY.md loads automatically.
Read MEMORY.md at session start. For any file whose description is relevant
to what you're about to do, read that file before proceeding.
Save learnings to memory immediately when they occur, do not wait until
session end.
That last line is the one that matters most, and it is the one people leave out.
When should a memory file get written?
The instant a correction happens, before the agent does anything else.
Not at session end. Sessions get interrupted, hit context limits, or drift onto another task, and the lesson evaporates. The moment you say “no, do it this way,” that is the write trigger.
Concretely, the triggers I use: I correct the agent, it hits an error and finds the workaround, it discovers a mismatch between what a doc claims and what the code does, or a script fails in a way that will fail again.
What are the four memory types?
Splitting by type is what keeps the index scannable as it grows.
| Type | What it stores |
|---|---|
| feedback | Rules: what not to do, with why and when to apply it |
| reference | Where to find things: endpoints, scripts, data shapes, API quirks |
| project | Current state: what is live, decisions made, timelines |
| user | Working style and preferences |
My current split across 216 files: 124 feedback, 57 reference, 30 project, 5 user. Feedback dominates, which makes sense. Most of what an agent needs to carry forward is a correction, not a fact.
What mistakes should you avoid?
Writing memories for things the repo already records. Code structure, past fixes, git history. The agent can read those. A memory file should hold what is not derivable from the codebase.
Vague descriptions. Covered above, and it is the single highest-leverage field in the system.
Letting memories go stale without a pass. A memory that names a file, function, or flag can outlive the thing it names. Anything load-bearing gets verified against the current code before it is acted on.
Assuming machines share memory. Mine do not. My Mac mini and my laptop each keep their own directory. A correction made on one does not exist on the other, and that gap has bitten me. If you run agents on more than one machine, either commit the memory directory to the repo or accept that the two piles diverge.
Does this actually work?
For the failure mode it targets, yes, and the effect is not subtle. The gendered-address tic went from 11 of 19 drafts to zero in the next 5, and has not come back. A data bug that overstated congressional trade amounts by 100x has not recurred since its file was written. A push script that silently failed 3 times in a row on the same post now documents its own failure branch.
What it does not fix: drift inside a single long session, since memory loads at the start and a 2-hour session with 50 tool calls compresses its early context anyway. And it cannot capture a correction you never actually made. The system only knows what someone bothered to write down.
That is the honest boundary. It is a cheap fix for the specific problem of repeated mistakes across sessions, and it is not a memory architecture. Flat markdown files the model reads before starting work.
For the longer version, with the incident that caused it and the nightly retro layer that promotes corrections into playbooks, read How I Made My AI Agents Stop Repeating the Same Mistake Twice.