How I Actually Build Durable Memory for an AI Agent
The hands-on version of agent memory: the index-plus-files layout I actually use, the frontmatter fields that make a memory file findable and trustworthy, the recall-then-verify discipline that keeps stale facts from becoming load-bearing, and where memory stops and other storage starts.
What file layout do I actually use for durable agent memory?
Two layers, and I keep them strictly separate: one small index file, and a folder of individual memory files it points at.
memory/
MEMORY.md <- the index. One line per memory. Loaded every session.
lessons/
prompt-batching.md
deploy-checklist.md
facts/
project-x-api-shape.md
client-y-timezone.md
sessions/
2026-08-24-handoff.md
MEMORY.md is deliberately terse -- a bullet per file, a few words of what it covers, and the path. Something like:
# Memory Index
- [prompt-batching] batching sub-questions into one message beats serial asks -- memory/lessons/prompt-batching.md
- [project-x-api] project X's API returns dates in UTC, not local -- memory/facts/project-x-api-shape.md
- [client-y-tz] client Y's team operates on Eastern, not Pacific -- memory/facts/client-y-timezone.md
The mistake I made early on: letting the index grow into the content itself -- writing the whole fact inline instead of a pointer. That defeats the point. The index has one job: load fast and route to the right file. If reading the index takes as long as reading the memory folder, it isn't an index anymore, it's just a second copy of the memory folder with worse formatting.
What actually goes in a memory file, field by field?
Every memory file I write gets frontmatter, even the short ones, because the frontmatter is what makes a memory file findable, dated, and trustworthy later, not just readable right now:
---
type: lesson
created: 2026-08-24
source: session correction
supersedes: none
---
# Batch related sub-questions into one message
Sending three related questions one at a time to the same agent session
re-loads the surrounding context three times and can lose the correlation
between question 1 and question 3. Batch them into a single numbered
message and tell the model explicitly it may answer out of order.
The type field is what lets me (and any tooling I write against the folder) distinguish a lesson (a corrected mistake) from a fact (something true about a project or client) from a preference (how a specific person wants something done) from reference (external knowledge worth keeping around). Four types cover almost everything I've needed. I resisted the urge to invent a fifth and sixth category early on -- more types means more decisions about where something goes, and more decisions means more places a fact quietly never gets written because nobody was sure which folder it belonged in.
supersedes is the field that does the real work when a fact changes. I don't edit the old file in place. I write a new file, set `supersedes: memory/facts/project-x-api-shape.md` (or whatever the old file was), and update the index to point at the new one. The old file stays on disk -- it's the history of what I used to believe and why that changed, which has saved me more than once when I needed to explain a decision that looked wrong in hindsight but was correct given what was known at the time.
How do I actually tell short-term from long-term so I don't lose things?
Short-term is whatever's alive in the current conversation -- nothing more. If it hasn't been written to a file in the memory/ folder, it is exactly as gone as if it were never said, the moment that session ends or resets. I stopped assuming "I told the agent that earlier this session" meant anything would carry forward. It doesn't, unless something wrote it down.
The practical discipline I run now: at any point in a session where a correction, a preference, or a hard-won fact surfaces, I stop and ask "does this survive past this conversation?" If yes, it gets a memory file and an index line, right then, not at the end of the session when I'm more likely to forget the detail that made it worth keeping in the first place. Waiting until session-end to write memory files is the single most common way I've seen good facts never get captured -- by the time the session wraps, the exact wording and context that made the correction useful has already faded.
What's the difference between memory, working notes, and a tracked ticket?
This is the boundary that took me longest to get right, because all three feel like "writing something down":
- Memory (memory/) is for anything that should silently shape future sessions without anyone having to re-discover it: preferences, corrected mistakes, durable facts about a project or client. Loaded via the index, read passively, not something anyone actively works through top to bottom. - Working notes / scratch (a hub or scratchpad file, separate from memory/) is for something still in progress -- a plan being drafted, a half-finished investigation, state that needs to survive a session restart but isn't yet a settled fact worth promoting. I keep this explicitly out of the memory folder. A half-formed idea sitting next to durable facts pollutes the index with things that aren't actually stable yet. - A tracked ticket or issue is for anything that represents work still to be done, with a status that changes and an owner. Memory doesn't track status. If a fact needs a "not started / in progress / done" lifecycle, it's not a memory, it's a task, and belongs in whatever tracker the project already uses, not in memory/.
The rule I actually apply: if forgetting it would just mean re-learning the same thing later, it's memory. If forgetting it would mean a task silently never gets done, it's a ticket. If it's not yet true, it's a working note.
Why do I never trust a memory file blindly, and what do I check first?
A memory file states what it stated when it was written. It doesn't re-verify itself, and nothing about reading it back tells me whether the thing it describes is still true. I got burned once trusting a facts/ entry that said a client's environment used one particular configuration -- correct when written, six weeks stale by the time I acted on it, because the environment had changed and nobody had gone back to update the memory file.
Recall-then-verify is the fix, and it's not optional for anything load-bearing: read the memory file, treat what it says as a claim worth checking rather than a settled fact, and confirm it against the actual current state of whatever it describes before I act on it for anything that matters. For a low-stakes preference ("this person likes short summaries"), I'll trust the memory file outright -- the cost of being wrong is trivial. For anything that drives a real decision -- an API shape, a deployment step, a client requirement -- I re-check the live source before treating the memory file as current, every time, no exceptions I've found worth making.
*Read the underlying architecture -- what memory actually is, the layers it's made of, and why it's a fundamentally different thing from an agent's short-term context window -- in the companion piece on danstolts.com: [How does an AI agent remember things between conversations?](https://danstolts.com/writing/i-am-memory/)*