ADRs and Session Handoffs: Giving AI Coding Agents a Memory That Survives
I once watched a coding agent spend forty minutes talking itself into a design I had rejected two days earlier. It wasn't being stupid. It read the code, noticed what looked like an obvious improvement, and proposed it with a lot of confidence. The problem was that I had already tried that exact improvement, measured it, and thrown it out. That knowledge lived in my head and in a chat transcript that no longer existed. The repo didn't remember, so the agent didn't either.
That afternoon is why I started taking architecture decision records for AI coding agents seriously, along with a short handoff file I now write at the end of every long session. This post covers the system I settled on after a stretch of long, mostly autonomous sessions with Claude Code and Codex on a codebase that kept growing. It isn't a framework. It's three habits and three kinds of file, and together they let a fresh session pick up in minutes instead of re-litigating the last week.
Why Agents Forget and Repos Don't
Every agent session starts with an empty head. The context window is working memory. It's big, and it's gone when the session ends. Some tools add persistent memory features, and those help, but they're opaque, tied to one tool, and hard to review in a pull request.
The repository is the opposite. It outlives every session, every tool switch and every teammate. Anything you write into it gets versioned, diffed, reviewed and read by whatever agent you point at it next week, whether that's Claude Code, Codex or Cursor.
So the conclusion is almost boring: if the context window resets and the repo doesn't, the repo has to be the agent's memory. Not the transcript. Not your head. Files that sit next to the code and get read at the start of every session.
The catch is that agents trust what they read. A person who finds a stale README gets suspicious. An agent that finds one acts on it. Stale docs mislead an agent faster than they mislead a person, so this kind of memory only works if it stays honest. Most of what follows is about keeping it honest.
The One-Line Rule
After a few sessions of watching decisions evaporate, I boiled the discipline down to one sentence I can paste into any instruction file:
Don't claim anything you haven't measured, don't decide anything without writing down the alternative you rejected, and don't defer anything without giving it an ID.
Each clause exists because of a specific failure:
- Measure before you claim. Agents are fluent. "This is faster" and "this fixes the flake" come out sounding equally sure whether or not anyone checked. If a claim goes into a doc, it needs a command or a test behind it. Otherwise it's labelled as unverified.
- Write down the rejected alternative. The decision on its own isn't what stops the re-arguing. The record of what you tried and why it lost is. Without it, the next session rediscovers the "obvious improvement" and proposes it again, which is exactly what happened to me.
- Give every deferral an ID. "We'll handle that later" in a transcript is gone forever.
D-07: retry policy for webhook delivery, deferred until the queue landsin a file is something the next session can find, grep for and close.
The rule is short on purpose. A long methodology document turns into one more file the agent half-reads. One sentence it reads every time.
An ADR Format Built for Agents
Michael Nygard's original architecture decision record format (context, decision, consequences) is still the right base. I added three things, because an agent reads an ADR differently from a person. A person reads it once to understand the history. An agent reads it every session to decide what it's allowed to change.
Here is the skeleton I use. These are headings only, written from scratch to be generic:
# ADR-NNN: <short decision title>
Status: proposed | accepted | superseded by ADR-MMM
Amends: ADR-XXX (what changes in it, in one line)
## Context
What forced the decision. Link the question or defect ID.
## Decision
What we do now, stated as a rule the code can be checked against.
## Rejected alternative
What we tried or seriously considered, and the measured reason it lost.
## Evidence
The command, test or benchmark that backs the decision.
## I would revert this if...
The concrete signal that would make this decision wrong.
The three additions do most of the work:
Rejected alternative. This is the section that stops an agent re-proposing a dead idea. When the agent reads "we considered an in-memory cache with a higher threshold; it hid stale reads under load", it has a reason not to suggest it again, and a precise thing to argue with if conditions really have changed.
Explicit amendments. Decisions change. If ADR-012 quietly contradicts ADR-004, the agent now holds two conflicting rules and will pick one more or less at random. An Amends: line on the new record, with the old record's status derived from it (by a script, not by hand), turns the conflict into a chain the agent can follow.
"I would revert this if..." This is my favourite line, because it changes the agent's job from defending the decision to watching for one condition. "I would revert this if p95 latency on the read path goes above the budget in the perf test" gives the next session a tripwire. If the tripwire hasn't fired, the decision stands and nobody needs to debate it.
Keep ADRs small. One decision per record, a page at most. If it needs three pages, it's probably a spec, and it belongs in your spec-driven development flow rather than in the decision log.
IDs for Everything Left Open
ADRs record what got closed. You need an equally boring place for what's still open. I keep a single open-items file with three prefixes:
| Prefix | Meaning | Example |
|---|---|---|
Q- | Open question nobody has answered yet | Q-03: should exports run sync or queued? |
D- | Deferred work, deliberately postponed | D-07: retry policy for webhook delivery |
M- | Measurement owed: a claim that still needs proof | M-02: confirm the new index helps the list query |
The letters don't matter. Having IDs does. An ID turns a vague worry into something you can reference in a commit message, an ADR's context section, a test name or a handoff file. When an agent finds a TODO in the code, it can look up D-07 and see why the work was postponed and what it's waiting on, instead of "helpfully" building it now.
Deliberate debt gets the same treatment. If you knowingly ship a shortcut, it gets a D- ID and a line saying what would make it worth paying back. Debt without an ID tends to come back as a surprise refactor some agent decided to do on a Tuesday.
The Handoff File: What NEXT_SESSION Should Contain
ADRs are long-lived. The open-items list changes every week. The handoff file is the most short-lived of the three: one per workstream, rewritten at the end of every session, and read first at the start of the next.
I call mine NEXT_SESSION.md and keep it short enough to read in a minute. It answers four questions:
- Current state. What's working, on which branch, as of which commit.
- Verified vs. unverified. What I actually ran and saw pass, kept apart from what I believe works. This is the split that matters most.
- Next three steps. Concrete, ordered, each small enough for one sitting. Three, not ten.
- Traps found. The flaky test, the env var that has to be set, the file that looks unused but isn't.
Just as important is what it leaves out. No narrative of the session, no recap of decisions (link the ADR instead), no aspirational roadmap. A handoff that copies the ADRs will drift away from them, and then the agent has two versions of the truth.
Here's the difference it makes, as a hypothetical but representative comparison:
| Without a handoff | With a handoff | |
|---|---|---|
| First thing the agent does | Re-reads large parts of the codebase to rebuild context | Reads one short file, then the ADRs it links |
| Settled decisions | Often re-proposed, sometimes re-implemented | Respected, or challenged against the revert condition |
| Known traps | Rediscovered the hard way | Avoided from the first command |
| Time to first useful change | Most of a session's opening, say half an hour | A few minutes |
The numbers depend on your codebase. The shape doesn't. The handoff turns the start of a session from archaeology into execution.
Keeping CLAUDE.md / AGENTS.md Truthful
The project-state file, CLAUDE.md for Claude Code or AGENTS.md for the open convention that Codex and others read, is the agent's front door. It gets loaded every session, so every line in it costs context and every wrong line costs correctness.
The failure mode I see most often is the sales pitch: a file that describes what the project is meant to be, lists features that are half-built, and keeps growing a changelog at the bottom. I wrote a whole post on why your CLAUDE.md is probably a changelog. The short version: the project-state file should be a state table plus pointers, not prose.
Something shaped like this:
## State
| Area | Status | Source of truth |
|---------------|-------------|--------------------------|
| Auth | stable | docs/adr/ (auth ADRs) |
| Export jobs | in progress | NEXT_SESSION (exports) |
| Search | deferred | open-items: D-04 |
## Rules
- Read the handoff for your workstream before editing.
- Never change an accepted ADR's decision without a new ADR.
- Claims need evidence; unverified work is labelled as such.
Each kind of file has its own job, its own reader and its own way of going stale:
| File | Holds | Who reads it | When | Goes stale when |
|---|---|---|---|---|
| ADR | One decision, the rejected alternative, the revert condition | Any agent about to change that area | Before touching the area | A new decision isn't recorded as an amendment |
Project-state file (CLAUDE.md / AGENTS.md) | State table, rules, pointers | Every session, automatically | Session start | Status rows aren't updated when work lands |
Workstream handoff (NEXT_SESSION.md) | Current state, verified vs. unverified, next steps, traps | The next session on that workstream | First thing | The session ends without rewriting it |
| Open-items list | Q-, D-, M- items with IDs | Anyone planning or picking up work | Planning, and on finding a TODO | Items get closed in code but not in the list |
The last column is the useful one. Each failure is a missed step at a predictable moment, which means you can put it on a checklist.
Test the Rule, Not the Number
A decision that lives only in prose is a suggestion. The strongest way to make it stick is to freeze the rule into a test, so an agent that breaks it gets a red build, not a lecture.
The trick is to test the rule rather than the measurement. "The list query returns in under 80 ms" is a brittle number that flakes on a slow CI runner. "The list query uses the composite index", checked with an explain plan, or "the client never retries a non-idempotent call", checked by asserting on the retry policy, are rules. They hold on any machine, and they encode why the ADR exists.
I name these tests after the ADR or open item they protect. When one fails, the agent finds the reason in the test name and goes to read the record rather than "fixing" the test. That's the loop closing:
Note step B: "read what exists, checked against code." Docs can be wrong. Before an agent builds on an ADR or a handoff claim, it should confirm that the code still matches. When it doesn't, that mismatch is itself a finding with an ID, not something to paper over quietly.
This is the same idea behind evaluating what your agent does, not what it says: trust the observable behaviour, and make the expectations executable.
Checklist: Ending a Session So the Next One Starts Clean
This is the part I actually run, usually by asking the agent to do it and then reviewing the diff. In Claude Code it fits well as a skill; I covered how those work in my agents and skills guide.
Before you close the session:
- Every decision made this session has an ADR, including the rejected alternative and an "I would revert this if..." line.
- Any ADR that changes an earlier one says
Amends:, and the earlier one's status is regenerated, not hand-edited. - Every "later", "TODO" or "out of scope" from the session has a
Q-,D-orM-ID in the open-items list. - Every claim in the docs is either backed by a command or test, or labelled unverified.
- At least one new test freezes a rule from today's decisions, named after its ADR or item.
-
NEXT_SESSION.mdfor this workstream is rewritten: current state, verified vs. unverified, next three steps, traps. - The state table in
CLAUDE.md/AGENTS.mdmatches reality, and nothing was appended as a changelog entry. - Closed items are removed from the open-items list, with the commit or ADR that closed them.
How to start if you have none of this today:
- Paste the one-line rule into your project-state file.
- Create an empty
docs/adr/folder and an open-items file. Don't backfill history. Start with the next decision. - At the end of your next long session, ask the agent to write
NEXT_SESSION.mdusing the four headings above, then edit it yourself. - At the start of the following session, have the agent read the handoff first and tell you what it plans to do. If it repeats something already rejected, you know what the next ADR is.
- After a couple of weeks, turn the checklist into a skill or command so ending a session cleanly takes one step.
My public spec-dev-ralph repo explores the same idea from the spec side: the repo carries the state between agent iterations so each loop starts from files, not from memory. In Akashi I lean on the same habits to keep a multi-package project coherent across sessions.
Trade-offs Worth Being Honest About
None of this is free. Writing the rejected alternative takes a few extra minutes per decision. Rewriting the handoff takes a few minutes per session. On a weekend prototype I skip most of it, and that's fine.
The real risk is the opposite one: a decision log so heavy that the agent spends its context reading history instead of writing code. The fix is the same discipline as with specs. Keep each file short, link instead of copying, and delete what's closed. If your ADR folder is turning into a novel, the project-state file should point only to the ones still in force.
It also doesn't replace review. These files make an agent consistent, not correct. A consistent agent still needs a human looking at what it ships, which is where risk-tiered approval comes in.
Conclusion
Agents will keep getting longer sessions and more autonomy, and their context windows will still reset. The teams that get compounding value from them will be the ones whose repos remember: decisions with their rejected alternatives, open items with IDs, and a handoff that tells the next session where to start and what to avoid.
Start small. One rule, one ADR, one handoff file. The first time a fresh session reads the record and doesn't propose the thing you already rejected, you'll see the point.
Running agents across long-lived codebases and fighting the same context-loss problems? Reach out. I'm always happy to compare notes.