Skip to content
Back to all field notes runtime · 7 min

Memory for coding agents: what should persist outside the chat

Not everything an agent learns deserves to be remembered, and the chat is the worst place to keep the parts that do. A practical taxonomy of what to persist, and in what form.

field note 7 min

If you’ve felt the tax of context loss between sessions, the reflex is to want the agent to “have memory.” Fair instinct, wrong framing. The question isn’t whether an agent has memory. It’s what deserves to be remembered, where it should live, and in what shape, so that a cold agent can load it and act on it correctly.

Most attempts at agent memory fail because they answer “remember everything” or “remember nothing,” and both are wrong. Remember everything and you rebuild the context-window problem in a database: a pile of stale, contradictory notes the agent can’t reason over. Remember nothing and every session starts cold. The useful version is selective and structured, and building it starts with a taxonomy.

Three kinds of thing worth persisting

Not all durable knowledge is the same. It fails differently, changes at different speeds, and wants different homes. I find three categories cover almost everything that actually needs to survive a session.

Decisions

A decision is a choice made among alternatives, with a reason. “We use optimistic locking here because the contention is low and the retry cost is trivial.” The valuable part is not the choice, it’s the rejected alternatives and the reasoning, because that’s what stops a future agent from re-proposing what you already ruled out.

Decisions are append-only by nature. You don’t edit a decision, you supersede it with a newer one, and the old one stays as history so the trail of why is intact. The right form is a log: dated entries, each with the choice, the alternatives, and the reason. A decision log is the single highest-leverage piece of agent memory, because it kills the most expensive form of context loss, re-litigating settled questions.

Invariants

An invariant is a rule that must hold across changes. “This table is append-only.” “Never call the network from inside this module.” “All money is stored in integer cents.” Invariants are different from decisions in a critical way: an agent needs to see them at the start of every session, before it writes anything, because their whole job is to constrain what gets written.

That means invariants belong in the file the agent reads first, the rules file, not buried in a log it might consult. This is exactly the material of a spec’s constraint section, and it’s why memory and specification blur together here. A durable invariant is really a piece of a living spec that stays alive across sessions instead of decaying in a chat. The constraint that survives the trip between tools — a portable spec rather than a note trapped in one chat — is the same constraint an agent has to honor next Tuesday.

State

State is the current status of the work: what’s done, what’s in flight, what’s blocked, what the next step is. Unlike decisions and invariants, state is not append-only and not permanent. It’s a snapshot that’s meant to be overwritten. Yesterday’s “in progress” is today’s “done,” and keeping the stale version around is actively harmful, because an agent that loads old state will redo finished work or resume from the wrong point.

State wants a different form: a small, current, overwrite-in-place file that answers “where are we.” A progress file, not a log. The failure mode of state memory is the opposite of decision memory. Decisions rot if you delete history. State rots if you keep it.

The form matters as much as the content

Notice that each category wants a different shape, and getting the shape wrong ruins the content.

  • Decisions want an append-only log. Editable history destroys the trail.
  • Invariants want a top-of-context rules file. A log the agent might not read is useless for a constraint that has to bind every write.
  • State wants a single overwritten snapshot. History here is noise that misleads.

Cram all three into one document, the way a lot of CLAUDE.md files grow, and each corrupts the others. The invariants get buried under state churn. The decision history gets flattened into a current-status blob. The agent reads a soup and honors none of it reliably. Separating by category isn’t tidiness, it’s what makes the memory loadable.

decision log

  • append-only
  • never edited
  • history is the value

state file

  • overwritten in place
  • no history
  • current only
Opposite forms for opposite failure modes: decisions rot if you delete their history, state rots if you keep it.

Memory is written by the work, not maintained beside it

Here’s the trap that kills most memory systems: they require a human to maintain them, so they rot the moment the human gets busy. A decision log nobody updates is worse than no log, because it lies with authority.

The only version that survives is memory the work writes as a side effect of doing the work. When a decision gets made in a session, it gets logged then, in that session, not in a documentation pass that never comes. When an invariant is established, it lands in the rules file as part of finishing the task, not later. The moment memory becomes a separate chore, it dies. The product-side name for this pattern is a system that maintains itself, the argument behind the product brain that maintains itself, and it’s the same requirement at the code layer: if keeping the memory current isn’t part of the loop, it won’t stay current.

Why this is the antidote to drift

Memory and drift are two sides of one coin. Agents will drift precisely when the thing they should be constrained by isn’t in front of them. An invariant that lives in durable memory and loads at the start of every session is an invariant the agent can’t silently violate, because it’s there in the context, binding the write. Take it out of memory and it becomes a rule that existed once, in a chat, that nobody can point to anymore.

This is also what makes running work in parallel survivable. When you keep several sessions alive at once, they can’t each carry the shared decisions in a private conversation, or they’ll diverge. Shared, external memory is the only thing they can all read, which is why persisting outside the chat isn’t a nicety, it’s the precondition for more than one agent working on the same product without contradicting each other.

The one-line version

Decisions go in an append-only log. Invariants go in the rules file the agent reads first. State goes in a single overwritten snapshot. All three live in the repo, outside any chat, and get written by the work rather than maintained beside it.

Do that and a cold agent stops being a blank one. It loads what the system knows, honors the constraints, and picks up where the last session left off. That’s the difference between an agent with a good context window and an agent that’s actually part of a system that remembers. The first is a smart tool. The second is what lets you stop being the runtime by hand.

↓ Download PaellaDoc · macOS

What’s the one decision you’ve had to re-explain to an agent more than twice? Tell me on the forum.

Frequently asked questions

What should a coding agent actually remember?

Not everything. Three kinds of thing earn a place: decisions (a choice among alternatives, with the reasoning that rules out re-proposing them), invariants (rules that must hold across changes), and state (the current status of the work). Remember everything and you rebuild the context-window problem in a database; remember nothing and every session starts cold.

Where should each kind of agent memory live?

In different shapes, because the form is half the content. Decisions want an append-only log where history is the value. Invariants want the rules file the agent reads first, so they bind every write. State wants a single overwritten snapshot with no history. Cram all three into one document and each corrupts the others.

Why do agent memory systems rot?

Because they require a human to maintain them, so they die the moment the human gets busy, and a decision log nobody updates lies with authority. The only version that survives is memory the work writes as a side effect: a decision logged in the session it’s made, an invariant landing in the rules file as part of finishing the task.

Should I just keep everything in CLAUDE.md?

Not all of it. That’s exactly where the three kinds collide: invariants get buried under state churn, decision history flattens into a current-status blob, and the agent reads a soup it honors none of reliably. Keep invariants in the rules file, decisions in a log, and state in its own snapshot.