A coding agent has three places state can live: the context window, which is gone the moment the session ends; a written file the agent re-reads next time, like CLAUDE.md or a plan file; and the repository itself, which is the only one that is actually durable. What each is good for, and when a memory file just accumulates stale instructions nobody prunes.
the question
Ask an agent framework how it handles memory and you get an answer about a feature: a notes file, a vector store, a summarization step that runs when the context window fills up. That answer is not wrong, but it skips past the more useful question, which is where a coding agent's state actually lives and how durable each place is.
There are three places, and they are not equally durable. Ranked from least to most:
The argument here is not that notes files are useless. It is that the third layer is the one doing the real work, and treating the second layer as if it were the record instead of a cache leads to a specific, avoidable kind of failure.
1. the context window: gone unless captured
Everything an agent reasoned through during a session, every file it read, every dead end it ruled out, lives in the context window for exactly as long as that session runs. Close the session and none of that reasoning is retrievable. There is no default mechanism in a chat-based coding agent that writes the context window to disk when you are done with it.
Some tools soften this with automatic summarization once a conversation gets long: an older stretch of the transcript is compressed into a shorter description so the session can keep going without exceeding the model's context limit. That summary can outlive the turns it replaced, but it is a lossy compression made under token pressure, not a memory feature designed to be reread later. It is there to let the current session continue, not to brief the next one.
The practical consequence: if a fact only exists because an agent figured it out this session, it does not exist tomorrow unless something wrote it down somewhere else. "The agent already knows this" is true only within the boundary of one running process.
2. the notes file: durable, but only as good as its last edit
The obvious fix is a file the agent reads at the start of every session and writes to during it: a CLAUDE.md, a plan file, whatever a given tool calls its memory. This survives a session ending, which is real progress over the context window. It is still not the repository, and the distinction matters.
A notes file is exactly as reliable as the discipline that maintains it, and nothing enforces that discipline. Nobody reviews a memory file the way they review a diff. Nothing fails a CI check because an instruction in it went stale six weeks ago when the build command changed. It sits there, read uncritically at the start of every new session, indistinguishable in format from a current instruction and a wrong one.
# CLAUDE.md, six months in
## Build
Run `npm run build` before committing. <- still true
Use yarn, not npm, for installs. <- changed in March, nobody updated this line
The API lives in src/api/. <- moved to src/server/api/ in the April refactor
## Notes from past sessions
- Auth was refactored to use JWT (see PR #212)
- Decided against GraphQL for now
- TODO: revisit the caching layer
None of those lines announce which ones are wrong. A fresh session reads the whole file with equal weight and inherits whichever mistakes accumulated since the last prune, on top of whatever it got right.
3. the repository: the actual durable record
The working tree and the commit history are true independent of which session produced them. Code that exists does what it does regardless of whether the agent that wrote it remembers writing it. A commit message is memory that explains itself, timestamped, attributed, and sitting next to the diff it describes rather than in a separate file that can drift away from the change it was meant to document.
This did not require anyone to build a memory system. Version control has been solving "what changed, when, and why" since long before agents existed, for the same reason: humans forget too, and a team is really just several sessions of the same problem running in parallel across different people. An agent inherits that durability for free the moment it works inside a git repository instead of a scratch directory.
What this looks like in practice
- git log and blame answer "why is this here" more reliably than asking an agent to recall its own past session, because the answer does not depend on any session still existing.
- A failing test is a memory of a requirement. It persists the fact that some behavior matters far more durably than a prose note claiming the same thing, because it cannot be silently skimmed past.
- Types and interfaces persist decisions structurally. A return type that says a field is optional survives every session in a way a comment saying "remember this field can be null" does not, because the type is load-bearing and the comment is not.
None of this is exotic advice. It is the same reason experienced engineers write commit messages and keep small commits: not for the agent, for the same durability everyone else relies on. Agents just make the payoff show up sooner, because they have no other way to reconstruct yesterday's reasoning.
when a notes file genuinely earns its place
None of this means skip the notes file. It means use it for the narrow job it is actually good at: standing instructions a fresh session has no other way to learn, because they are not encoded anywhere in the code itself.
- Build and run commands. The repository cannot tell an agent how to invoke its own test suite; that information has to live somewhere outside the code, and a notes file read at session start is a reasonable place.
- Directory conventions. "New API routes go in
src/server/api/, notsrc/api/" is true today, is unlikely to change often, and is not discoverable by reading any single file. - House style the linter does not enforce. Naming conventions, preferred patterns, things a human reviewer would flag that no tool checks automatically.
What these have in common: they are slow-changing, they are true independent of any one session's work, and there is no better place to put them. That is the profile of a good notes file entry.
when it just accumulates
The same file rots the moment it starts holding things that already have a better home. Three patterns account for most of it.
- A running log of decisions. "Decided against GraphQL for now" belongs in a commit message or a design doc with a date on it, not a file that gets reread verbatim forever with no indication the decision is four months stale.
- Facts the code already encodes. If the type system, a config file, or a test already says something, restating it in prose creates two sources that can disagree, and only one of them fails a build when it is wrong.
- Anything appended and never removed. A notes file with no pruning step grows monotonically. Length alone degrades it: a five-line file gets read carefully, a three-hundred-line one gets skimmed, and skimmed instructions are the ones that get silently followed even after they stop being true.
a practical split
Put differently: treat the context window as scratch space that will be thrown away, treat a notes file as a small, deliberately curated cache of things not worth rediscovering every session, and treat the repository as the source of truth that both of the others are only ever approximating.
When you are unsure which bucket something belongs in, ask whether it would still be true if every current session ended right now. If yes, it belongs somewhere durable. If it is only true because of reasoning happening in front of you, let it stay in the context window and disappear when the session does. Most memory problems in agent workflows are really this question answered wrong once and then copied forward.