---
name: architecture-decision-record
description: Capture an architectural decision as an ADR once it has been reached — a choice that constrains future work, had viable alternatives that were rejected, and would be expensive to revisit. Use when a design discussion has converged on such a decision, when the user asks for an ADR, or when a recorded decision changes. Do NOT use for reversible implementation choices, library picks with no lasting constraint, or to minute a discussion that has not concluded.
license: MIT
---

# Architecture Decision Record

An ADR records **why** the system is shaped the way it is, for a reader who was not in the room and cannot ask.

## ALWAYS apply the three tests before writing

Write an ADR only when a decision has been **reached** and meets all three:

1. **It constrains future work.** Later changes must live with it.
2. **Viable alternatives were rejected**, and the rejection is not self-evident.
3. **It would be expensive to revisit** — reversing it means reworking code, data, or infrastructure.

A decision that fails any test is a normal implementation choice. It belongs in the code and the component's documentation, not here.

**NEVER open an ADR because a discussion started.** Agents and users weigh trade-offs constantly — which library, how to shape a service, whether to add a cache. Minuting all of that buries the handful of records that matter, and this repository's retrieval already ranks documents by standing. The trigger is *convergence*, not debate.

Worked distinctions:

| Situation | ADR? | Why |
|---|---|---|
| "Should this service use a queue or direct calls?" — still being argued | **No** | No decision yet. Wait for convergence. |
| Decided: schema deploys as one idempotent SQL file, no migration framework | **Yes** | Constrains every future schema change; alternatives existed; reversing means retrofitting migrations. |
| Chose `System.Text.Json` over `Newtonsoft.Json` in one service | **No** | Reversible in an afternoon, constrains nothing else. |
| Decided: the browser never holds an access token; the BFF owns the session | **Yes** | A security boundary the whole front end is built around. |
| Named a class `OrderProcessor` rather than `OrderService` | **No** | Not a decision, a preference. |
| Decided: agent roles stay synchronised across all six harnesses | **Yes** | Every future agent change inherits the obligation. |

## NEVER write one without approval

The ADR is a governed document and creating documentation requires user approval. Present the decision, the alternatives, and the consequences **in the conversation first**, and ask. Write the file only after an explicit yes.

If the user declines, do not write it and do not raise it again in the same session.

## Placement and identity

Active ADRs live in the path declared as **Architecture Decision Records** in the repository root `AGENTS.md` Config Registry; a record wholly replaced by one on a different subject moves to the archive path recorded there. Read the repository documentation standard for the lifecycle, and the documentation ontology for the frontmatter schema, before writing.

- Filename: `ADR-{YYYY-MM-DD}-{kebab-slug}.md`
- Frontmatter `id`: `adr/{YYYY-MM-DD}-{kebab-slug}` — the date is the decision date, and it never changes afterwards.
- `doc-type: adr`, `status: current`.
- `supersedes`: the `id` of an ADR this one genuinely replaces, a record on a different subject. Otherwise `[]`. A changed decision is never a new record.
- `code-refs` / `api-endpoints`: symbols and routes the decision governs, when the decision is about specific code. These are validated against the code graph, so every entry must resolve.

## Structure

```markdown
---
id: adr/{date}-{slug}
title: "ADR-{date}: {Short Decision Title}"
doc-type: adr
status: current
owner: {team from the catalog}
last-reviewed: {date}
code-refs: []
api-endpoints: []
decided-by: []
supersedes: []
---
# ADR-{date}: {Short Decision Title}

**Status:** Accepted
**Date:** {date}

## Context

The forces in play: the constraint, the problem, what was true at the time. Enough that a
reader who was not present can judge whether the reasoning still holds. State facts, not
narrative — no meeting history, no attributions.

## Decision

What was decided, in the active voice and one paragraph. "Schema is deployed via a single
idempotent SQL file applied by sqlcmd." Not "we discussed and felt that…".

## Alternatives considered

Each viable option that was rejected, and why. An ADR with no rejected alternatives is
either not a decision or is hiding the interesting part.

## Consequences

What this commits the system to — including the costs. An ADR that lists only benefits is
advocacy, not a record. State what becomes harder, what is now manual, what a future
change will have to work around.
```

## Changing a decision

**An accepted ADR is edited in place to state only the decision in force.** When a decision changes, rewrite the record so it says what is true now, and remove the superseded wording rather than keeping it beside the new text as history. git holds the history. An `Amended <date>: <one-line reason>` note is optional when the change matters to a reader.

Only a decision on a different subject that replaces an existing record gets a new ADR. That ADR names the replaced `id` in `supersedes`. The replaced record's `status` is set to `superseded`, it moves to the archive path, and documents that referenced it through `decided-by` are updated to point at the new one.

## Linking the decision to what it governs

A decision no one can find does not constrain anything. Add the ADR's `id` to the `decided-by` frontmatter of every document the decision governs — that key is what carries the decision through the documentation graph to the components it binds.

## Verify before finishing

Run the repository's documentation validation and drift checks. `code-refs` and `api-endpoints` resolve against the code graph, and a `decided-by` or `supersedes` pointing at a non-existent id fails validation.
