← Back to Blog

Lesson 2: The decision record

About this artifact

essayon-the-recordmaintained

method · since 2026

What you'll learn: how to stop a fresh AI session from re-deciding what you already settled: one page per decision, an index of those pages, and one line that puts the index in front of every session before it starts work. What you need: the specs/ directory from Lesson 1, whose decision/ folder is still empty, and the agent you already use. Time: ten minutes to set up, five per decision after. Cost: zero new dollars.

The idea#

Your code shows what you built and never shows what you turned down, so a fresh session, which starts with no memory and full confidence, reads the code and proposes the alternative you rejected in March. Writing the choice down is half the fix, because a page nobody reads changes nothing; the other half is an index the session is told to read before it touches anything. That one page, with the alternative you rejected and the condition that would reopen it, is a decision record.

Two rows compare a fresh agent session with and without the decision index: without it, the session reads the code and proposes the option you already rejected; with one standing line pointing it at the index first, it reads the index, cites D-003, and stops.

Why start here#

Lesson 1 left a decision/ folder empty on purpose. It is the next file a fresh session should read after the specs, and the file that spec cannot replace: a spec says what should be true when a feature ships, a decision record says why the project is shaped the way it is. In May I audited every record I had written across my projects and found they had drifted from the code in enough places to produce seven fixes. The gap was never the writing. It was that nothing read them.

The template#

Save this as specs/decision/_template.md:

# D-000: <what we decided, as a noun phrase>

Status: draft        Date: YYYY-MM-DD
(draft until checked against the code; then accepted; later superseded by D-NNN)

## Context
What forced a choice, in one paragraph: budget, time, one person, a failure.

## Decision
One sentence, present tense. "We build the site as static files served from object storage."

## Alternatives considered
- <option>: rejected because <one line>.
- <option>: rejected because <one line>.

## Consequences
What this makes easier. What it costs. What it forbids.

## What would change our mind
The observable condition that reopens this decision: a number, an event, or a date.

Save this as specs/decision/README.md:

# Decisions

One file per settled choice. Read this table before proposing a change.
The index is the allocator: claim the next row here first, then create the
file with that number. Agents request a number; they never pick one.
Superseded records keep their file and number. Nothing is reused.

| Id | Title | Status | Decision |
|---|---|---|---|
| D-001 | ... | accepted | ... |

Add this to the file your agent reads at session start, next to the line from Lesson 1 that points it at specs/:

Before proposing or making any change, read specs/decision/README.md and open any
record the request touches. If the change would reverse an accepted decision, cite
its id, say whether its "What would change our mind" condition has been met, and stop.

Three blocks, under sixty lines. If your tool has no file it reads at session start, paste the index and that instruction as the first message of each session instead.

Each part, and the mistake it prevents#

Status prevents an old proposal from reading as today's policy. A record is draft until you have checked it against the code, because the most expensive record I have found was one marked accepted that described a runtime the code never used.

Context prevents a rule with no reason, which is the kind of rule the next session argues with. Name the constraint that mattered: no budget for another monthly service, one person on call, a failure you are not repeating.

Decision, one sentence in present tense, prevents a narrative nobody can grade. State what you use and what that rules out, so a later request can visibly conflict with it.

Alternatives considered is the part the code can never carry. Without it the record is a changelog entry, and the session proposes the rejected option again because nothing says it was rejected.

Consequences prevents the surprise six weeks later when the choice forbids something you wanted. Record the inconvenience you accepted beside the benefit.

What would change our mind prevents dogma. A decision with no reopening condition outlives the constraint that produced it, and "when we grow" is not a condition anyone can check.

The index row claimed first prevents two records sharing a number. On two separate days my own records collided, two files taking one number and then three taking another, because several decisions landed at once and nothing read the numbers back for months.

A worked example#

Save as specs/decision/D-003-static-site-export.md:

# D-003: Static site export instead of a server

Status: accepted        Date: 2026-09-10

## Context
One person, a personal site, about twelve dollars a month for hosting. Pages
change a few times a week. Nothing on the site is per-user.

## Decision
We build the site to static files and serve them from object storage behind a CDN.

## Alternatives considered
- Managed Node host: rejected because it adds a monthly bill and a process to
  patch for pages that never change per request.
- Serverless rendering: rejected because cold starts and per-request code buy
  nothing for content that is identical for every visitor.

## Consequences
Deploys are a file sync. Anything that needs per-user state, a form or a search
box, becomes a separate small API rather than a server behind the site.

## What would change our mind
A page needs per-user rendering, or the CDN bill passes twenty dollars in a month.

The index row: | D-003 | Static site export instead of a server | accepted | Static files on object storage behind a CDN |

From the field#

On May 17 and 18 I audited every decision record across my repositories: 166 records, two days, seven fix pull requests. The finding that paid for the audit was a record marked accepted which said the backend used a vendor's agent toolkit as its default runtime. The code never had. A later decision routed calls directly to the vendor's API, and because the older record still said "adopt the toolkit," new sessions kept proposing to adopt it. I marked that record superseded rather than deleting it, kept the file so the number is never reused, and closed two placeholder records that existed only to satisfy forward references with a note that says do not revive this. The records were not the problem. Nothing was reading them before proposing changes, so a wrong one and a missing one cost the same.

Exercise#

  1. Create specs/decision/ with _template.md and README.md copied from this page.
  2. Add the standing instruction to the file your agent reads at session start, or plan to paste it as the first message.
  3. Write one record for a choice you made this month. Let the agent draft Context and Alternatives from the conversation where you made it; write the Decision and the reopening condition yourself, then check the record against the code before you mark it accepted.
  4. Write two more for the choices you keep re-explaining to new sessions. If you cannot name a rejected alternative, it is not a decision yet; leave it out.
  5. Claim the three index rows, then save the three files.
  6. Open a fresh agent session. Give it nothing but the standing file. Ask for the change one of your records forbids, for the example above "switch us to a server so we can render per user," and do not mention that a record exists.
  7. Watch what it does before it edits anything.

You're done when#

The fresh session opens the record, cites it by id, and either stops or asks you whether the reopening condition has been met, without being told the record exists. Two results mean not done. The session complies and starts building, which means the standing instruction is not in front of it. Or the session repeats the index line, or cites the code ("the site is currently static") instead of the record, which means it never opened the page and cannot tell a choice from a coincidence. Pasting the record into the session yourself passes nothing; the test is whether it can find the reason on its own.

Common mistakes#

  • A record with no rejected alternative. If nothing was turned down, nothing was decided.
  • Recording a preferred future design as if the code already used it. Draft until checked.
  • No reopening condition, so the record outlives the constraint that justified it.
  • Deleting superseded records. Mark them, keep the number, link forward.
  • Letting the agent write the Decision line. It can draft the rest; the choice is yours.
  • Pasting every record into the standing file. The standing file holds the pointer; the pages hold the decisions, and a buried line is an unread line.

Five minutes per record, ten to set up, and zero new dollars; the only ongoing cost is reading one table at the start of each session, which the instruction makes the agent's job, not yours.

Next lesson#

Why a rule that lives only in prose gets broken by the first session that never reads it, and the small check that turns advice into a guarantee.

Questions this post answers

What goes in a decision record?
One page: the constraint that forced a choice, the decision in one present-tense sentence, the alternatives considered and why each was rejected, the consequences accepted, and an observable condition that would reopen it. A status line marks it draft, accepted, or superseded.
Why does writing decision records not stop an AI agent from reversing them?
A page nobody reads changes nothing. The record only works when a standing instruction puts the index of decisions in front of every fresh session before it starts work, and asks it to cite any decision a request would reverse.
How do I keep two decision records from getting the same number?
The index is the allocator. Claim the next row in the index first, then create the file with that number. Agents request a number; they never pick one themselves. Superseded records keep their file and number so nothing is reused.
How do I know the decision records are working?
Open a fresh agent session with only the standing instruction and the index, ask it to make a change one record forbids, and watch whether it opens the record and cites it before doing anything. If it complies, or only repeats the index line, it is not working yet.

Keep reading

Demo

Watch the agent write

A polish agent drafts an essay against a pre-approved topic.

Read
Post

Seven articles to a platform that improves itself

The full path to an agent platform that edits its own instructions is already published, scattered across seven articles that never mention each other. I run that architecture as one person: supervisor and worker tiers, verification guards, external memory, a work queue, and a weekly pass that sweeps corrections back into the instruction files. Here is the platform, layer by layer, with the article that teaches each layer.

Read
Post

Lesson 1: The spec directory

First lesson in a series on running an AI-powered software team of one. Before you ask an agent to build, give your project a home for intent: a small directory of numbered specs grown from one template your agent fills in and you approve. Directory skeleton, spec template, worked example, and a fifteen-minute exercise included.

Read
Post

When your method repo and your product repo don't talk to each other

I built a method as a public repo and the product that runs it as two private ones, and none of them treated the others as a source of truth. The domain enum lived in four places. A persona drifted between its lens file and its API contract. Here is what that cost, and the one structural change that turned the whole class of bug into a failing test.

Read
Post

Context architecture beats documentation dumps

Dumping the whole corpus into an AI agent makes it worse, not better. The fix is architectural: each task loads a curated slice, not everything you have. Here is the method, and the same move at three different layers: specs, sensor data, and evaluation lenses.

Read
Post

SDD isn't about managing AI agents, it's about managing context

Spec-driven development reads like a methodology for controlling AI agents. It isn't. It's a methodology for managing context across stateless sessions. The spec is the persistent memory.

Read

Follow the work

New tools and writing as they ship — pick a channel.

Written by Eric Caskey. I build AI tools you can actually use. Explore the Tools or see the case studies.