Your Agent's Memory Is Rotting — Audit It Like a Cache
I asked Claude Code to draft a reply to a customer. Most of it was fine. At the bottom, under a “Still unconfirmed” heading, one line said that since mid-October a third-party webhook integration hadn’t been delivering events, and that we were relying on an hourly polling fallback instead. The draft’s first step asked the customer to test for exactly that.
That was news to me. I ran a read-only check against production. The webhooks had been arriving the whole time, every day, since the fix shipped weeks earlier.
The agent hadn’t made it up. It had read it from its own memory. Weeks ago, during the incident, it had saved a note saying the webhooks weren’t arriving. Nobody ever told it the incident was over, so the note stayed true as far as the agent knew. It went straight into a draft meant for a customer.
Agent memory is a cache. Nothing invalidates it, so it goes stale.
What agent memory actually is
Claude Code keeps a per-project memory folder outside your repo, at ~/.claude/projects/<project-path>/memory/. It holds one Markdown file per fact, plus a MEMORY.md index that loads into every session in that project. When you correct the agent, or it learns something it thinks will matter later, it writes a file there.
That’s useful, and I wouldn’t turn it off. But look at what it’s doing: it caches facts about a codebase and a production system that change every day, and it never invalidates any of them. A memory written on a Tuesday about “the feature on the foo branch” is still being read on a Friday, after foo has been merged, deployed, and changed twice.
Memory also gets more trust than it should. A fact in memory reads like something the agent knows, not something it heard once three weeks ago. My agent did hedge: it put the claim under “Still unconfirmed.” But it still built a step of the customer email around it, because nothing it could see contradicted the note.
The audit: eight agents, one backup
I used Claude Code’s /goal command, which keeps the session working until a condition holds:
/goal audit your memory files, i bet there is a bunch of stale/wrong information
There were 344 files. The agent handled it the way I’d want a careful engineer to:
- Backed up the whole folder first, to the session scratchpad, so every change could be reversed.
- Split the files across eight parallel agents, about 43 each. Every agent checked each claim against the current code on
origin/mainand the git history. - Fixed wrong claims in place, but deleted nothing. Anything that looked obsolete got flagged for the lead agent instead.
- Collected the questions only I could answer. Did that contract get signed? Was that account ever created? It asked them all at once at the end, not one at a time.
- Checked production read-only for claims the code couldn’t settle.
The results, batch by batch, were bad:
- One batch: 9 files corrected, 34 confirmed accurate.
- Another: 20 corrected. Several said “built on a branch” or “awaiting deploy” for work that had shipped long ago.
- Another: 16 corrected. The worst one claimed the test suite ran on pushes to
main. CI had since been changed to run it only on pushes to the production branch. - Another: a note saying a framework feature needed a persistent cache to work, about a bug the code had since fixed.
In total, about 130 of 344 files were wrong. That’s more than a third of what the agent “knew” about the project, loaded into every session.
The audit also turned up two real bugs in the repo: a scheduling comment that described a timetable we’d since changed, and a config flag that no code read anymore. Checking memory against code checks the code too.
Correct is not the same as necessary
After the audit, 335 files were left, all of them now accurate. I looked at the number and asked the question I should have asked first:
335 is still a lot of files for memory. are they all necessary? do you really need a memory that says a feature works? wouldnt you just assume that it does without the memory?
A memory that says “the export feature works” doesn’t change anything. The agent assumes features work. A memory that says “the CSV export is done” is a project log, and git already keeps one. A memory that repeats a rule from CLAUDE.md costs context twice for no extra behaviour.
So the second pass sorted every file into three buckets:
- Keep: facts that change behaviour and can’t be found anywhere else. Gotchas, preferences, things that cost a debugging session to learn.
- Trim: files that are mostly noise around one useful line, cut down to that line.
- Drop: project logs, “shipped” status notes, incidents that were fixed, and anything already in
CLAUDE.md, the rules files, or the docs.
Four agents did the sorting without changing anything, and the lead agent applied the results after taking a fresh backup. 333 files became 263, and 762 KB became 306 KB. Size dropped much more than the file count, because most surviving files went from paragraphs to a couple of lines.
Then I let it go further: fold the small notes into topic files. That breaks the default one-fact-per-file layout, but the folder went from 263 files to 15: the MEMORY.md index plus 14 topic files like feedback_how_to_work.md and reference_prod_ops.md. The index header now tells the agent to add a new fact as a section in the matching topic file, not as a new file, so the count stays down.
What I’d tell anyone with an agent memory folder
Back it up before anything touches it. Memory lives outside your repo, so git won’t save you. My agent took three backups that day, one before each pass. It also pointed out that the backups were sitting in a temporary scratchpad and I’d have to copy them somewhere if I wanted to keep them.
Treat every memory as a claim with a date on it. The one thing that went right in my bad draft was the “Still unconfirmed” list. That label is how I spotted the claim. Ask your agent to keep separating what it verified this session from what it remembers, and to check a remembered claim before building on it.
Audit against the source of truth, in parallel. Checking hundreds of claims against the code is exactly the kind of task to give subagents: small batches, read-only, results returned to one agent that decides. Have them flag files for deletion rather than delete them.
Prune with one question: would the agent assume this anyway? If so, the memory is noise. If git, the docs, or CLAUDE.md already say it, it’s a duplicate. What’s left after that question is the stuff worth paying context for.
Watch for secrets and personal data. Mine had production IDs and customer names in incident notes. When I asked about backing the folder up to my dotfiles repo, the agent flagged that the repo pushes to GitHub. Memory collects whatever was on screen when it was written.
Expect writers to collide. If you run several sessions on one project, they all write to the same folder. During the consolidation, another session added a note mid-merge. The agent noticed and folded it in, but in a different run I’ve watched parallel agents overwrite one shared memory file and lose what was there. One writer at a time, or per-agent files merged later.
Memory needs maintenance
The appeal of agent memory is that you correct something once and it stays corrected. The catch is that it stays anything once written: corrected, outdated, wrong, all equally permanent, all loaded with the same authority.
Code gets refactored because people read it. Memory files don’t get read by anyone except the agent, and the agent doesn’t know which ones are lies. So someone has to check it on a schedule: back up, check against main, prune what the agent would assume anyway.
If nothing checks the memory, the agent will keep repeating whatever was true the day it was written.