House rulesLesson
Decision records
Code shows what a project does, but not why it does it that way, or which other ways were tried and ruled out. An architecture decision record, or ADR, writes that down: one short file per decision, kept in the repository beside the code it explains.
Know ADRs already? Skip to In Terrarium.
Outside the box · General
The concept
An ADR records one decision that matters to the shape of a system: its context, what was decided, and why. A team numbers its ADRs and keeps them in a folder of the repository, such as docs/adr/, so they are versioned and reviewed like code. A future reader who wonders why the code looks the way it does finds the answer there, and writing one makes a group argue its way to an agreement before it commits.
Not every choice deserves one. A good test asks three things: the decision is hard to reverse, it would surprise a reader who lacks the context, and it came from a real trade-off between options. A record of an obvious choice adds nothing.
The classic practice treats an accepted ADR as fixed. When the decision changes, you don't edit the old record; you write a new one that supersedes it, and link the two. Martin Fowler's Architecture Decision Record describes it that way. The folder then reads as a history of decisions, and to learn what holds today you follow the chain to its end.
In Terrarium
Terrarium's ADRs live in docs/adr/, one numbered file each, and every one follows docs/adr/TEMPLATE.md: a # ADR-NNNN: heading, a short summary that covers the whole decision, then Scope, Why and Rejected alternatives, with optional sections left out rather than filled.
Terrarium breaks with the classic practice on one point: when a decision changes, its ADR is rewritten in place, and git keeps the old versions. ADR-0030 says what that rules in and out.
The enforces the shape. scripts/gate/validate-adr-rules.ts runs in pnpm validate:content, with its checks in scripts/lib/adr-rules.ts, and fails any ADR that doesn't open with its heading, or that holds one of these:
126/** History an ADR must not carry: git holds it (ADR-0030). */
127const HISTORY: [RegExp, string][] = [
128 [/\b\d{4}-\d{2}-\d{2}\b/, 'a date'],
129 [/^(Status|Date):/m, 'a status line'],
130 [/^>\s*\**(Amended|Superseded)\b/im, 'an amendment banner'],
131]
Each ADR has a summary (see Path-specific rules); the same script fails the Gate when an ADR changed since its summary was checked, or when a summary doesn't end with a line that points to its ADR and says when to read it in full.
A summary Rule reaches an agent when it reads a file the ADR governs. When the agent edits such a file, commits one with git commit, or names an ADR in what it writes, a hint, scripts/hooks/adr-read-hint.ts, points it to each ADR it hasn't yet read in full.
Why it's built this way
- Current state only. Dated amendments piled up, some reversing others, and one ADR's main text contradicted its own amendment, so an agent had to rebuild today's rule from the layers. Each ADR now states only the decision as it stands, rewritten in place. The cost: the same PR must also rewrite its summary rule and repoint what cites it. ADR-0030 (docs/adr/0030-adrs-document-a-decision-not-its-history.md).
- Git keeps history. Git already keeps every version with its change and reason, so history in the ADR was a second copy that made the first harder to read. The cost: a reader who wants a decision's path must run
git logon the file. Rejected alternatives stay, to stop the same idea being proposed again. ADR-0030 (docs/adr/0030-adrs-document-a-decision-not-its-history.md). - One home per fact. ADRs repeated numbers and steps that a script or Skill also held; the copies came apart and agents trusted the stale one. Now a number or command lives where it is enforced and the ADR points there, leaving it the reasoning no code comment or Skill records. The cost: a reader who wants the number follows the ADR's pointer to it. ADR-0030 (docs/adr/0030-adrs-document-a-decision-not-its-history.md).
- Summary Rule, full ADR. Loading each whole ADR as a Rule would cost context every time, forcing it to shrink to a summary and lose its reasons. So agents get a short summary Rule and read the full ADR when they need the why. The cost: two texts that can drift, which the Gate catches as described above. ADR-0030 (docs/adr/0030-adrs-document-a-decision-not-its-history.md).
What to watch for
- Any date fails the check. The pattern above matches a date anywhere in an ADR's text, even in an example.
- An ADR change waits for a person. Every file in
docs/adr/is on the list, so an agent may rewrite an ADR but not merge the change. - The pack's format differs. The
domain-modelingSkill in .agents/skills/domain-modeling/ has its own ADR template, with aStatusfield. Terrarium's template replaces that layout.
Try it in your project
Pick one decision in your code that a newcomer keeps questioning. Write it as an ADR of a summary, Why and Rejected alternatives, in a numbered file under docs/adr/. When the decision changes, decide once whether your team edits the record or supersedes it, and write that choice down as your first ADR.
Where next
- LessonPath-specific rules
- LessonMerge gate
- LessonProtected paths
Last updated · Latest source on GitHub ↗