Revisits the lifecycle decision on the evidence of ADR-011 (a real draft with open questions). Adds a fourth state, Proposed (YYYY-MM-DD), to ADR-023, the template, the adr-structure check (+test), spec and plan. Sets ADR-011's Status to Proposed and removes its now-redundant inline 'Proposed' line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
106 lines
5.2 KiB
Markdown
106 lines
5.2 KiB
Markdown
# ADR-023 — ADR structure & lifecycle
|
||
|
||
## Status
|
||
|
||
Accepted (2026-06-10). Meta/doctrine ADR — pins how ADRs are written; the
|
||
`adr-structure` check (`scripts/repo-scan.py`) and `docs/decisions/adr-template.md`
|
||
ship with it, and ADRs 001–018 were retroactively restructured to conform. Resolves
|
||
the FRICTION signal (2026-05-31) about ADR-writing policy being unsettled.
|
||
|
||
## Context
|
||
|
||
boma records architectural decisions as numbered ADRs in `docs/decisions/`, and
|
||
CLAUDE.md treats them as load-bearing. Yet no ADR said how an ADR is written. The
|
||
newest ADRs (019–022) converged on a clean shape — Status → Context → Decision →
|
||
Consequences → Related — but only by imitation. ADRs 001–018 predate it and drifted
|
||
widely: most lacked a `## Status` section entirely (016–018 carried only a trailing
|
||
build-state note), and many lacked an explicit `## Decision` or `## Consequences`
|
||
heading, their decisions spread across ad-hoc topical sections. The result was
|
||
structural drift and no uniform way to tell an active decision from a superseded or
|
||
deprecated one.
|
||
|
||
## Decision
|
||
|
||
### 1. Title & filename
|
||
|
||
Title line: `# ADR-NNN — <Title>: <optional clarifying subtitle>` (em-dash). Filename:
|
||
`NNN-kebab-title.md`, zero-padded 3-digit, monotonic, never reused — a superseded ADR
|
||
keeps its number and file. A new ADR is registered as a row in the CLAUDE.md
|
||
"Further reading" table.
|
||
|
||
### 2. Mandatory sections, in this order
|
||
|
||
- `## Status` — a lifecycle line, usually `Accepted (YYYY-MM-DD)` (see §4), plus an
|
||
optional one-line note.
|
||
- `## Context` — the forces, the problem, what exists today, why now.
|
||
- `## Decision` — what we are doing; numbered sub-decisions for multi-part ADRs.
|
||
- `## Consequences` — results, trade-offs explicitly accepted, follow-on work.
|
||
|
||
### 3. Optional sections (use only where they genuinely apply)
|
||
|
||
`## Related`, `## Scope`, `## Guardrails` / `## Enforcement`, `## What was ruled out`,
|
||
`## Verified facts (ADR-014)`.
|
||
|
||
### 4. Status lifecycle
|
||
|
||
Four states. Because boma is single-contributor and trunk-based with no review gate,
|
||
most ADRs are **born `Accepted (YYYY-MM-DD)`** — committed-to on writing. A
|
||
**`Proposed`** state exists for a genuine draft whose core direction is recorded but
|
||
whose specifics are still open for discussion (e.g. ADR-011); it is promoted to
|
||
`Accepted` once settled.
|
||
|
||
- **`Proposed (YYYY-MM-DD)`** — drafted, under discussion, not yet committed-to. May
|
||
carry open questions. Promoted to `Accepted (YYYY-MM-DD)` when decided.
|
||
- **`Accepted (YYYY-MM-DD)`** — committed-to. The common starting state.
|
||
- Replaced → old ADR's Status becomes **`Superseded by ADR-NNN (YYYY-MM-DD)`**; the new
|
||
ADR records `Supersedes ADR-MMM` in its Status and `## Related`. The link is
|
||
**bidirectional**.
|
||
- Retired with no replacement → **`Deprecated (YYYY-MM-DD)`** + a one-line reason.
|
||
|
||
**No silent rewrites.** An Accepted ADR is not edited to reverse its decision. Typo and
|
||
clarity fixes are fine; a material reversal requires a new ADR and a `Superseded by`
|
||
marker on the old one.
|
||
|
||
### 5. Template & enforcement
|
||
|
||
`docs/decisions/adr-template.md` is the scaffold for new ADRs. The `/review-repo`
|
||
command's pre-scan (`scripts/repo-scan.py`) emits an `adr-structure` finding for any
|
||
numbered ADR missing a mandatory section or with an unparseable Status line. It checks
|
||
**presence and Status, not section order** — order is a convention the template carries,
|
||
deliberately not gated, to keep enforcement lightweight (consistent with boma's other
|
||
doctrine ADRs adding no CI gate).
|
||
|
||
### 6. Retroactive conformance of the back-catalogue
|
||
|
||
ADRs 001–018 are restructured to satisfy this standard rather than grandfathered. The
|
||
restructure is **presentational** — existing headings are relabelled, regrouped, or
|
||
demoted under a `## Decision` umbrella; a dated `## Status` is added; a `## Consequences`
|
||
section is assembled from implications the ADR already states. **The substance of no
|
||
decision is changed.** This keeps the check uniform (no number threshold) and the corpus
|
||
a consistent, legible decision history.
|
||
|
||
## Consequences
|
||
|
||
- New ADRs have one obvious shape and a scaffold; structural drift stops.
|
||
- Every ADR declares its lifecycle state uniformly, and reversals are traceable.
|
||
- The whole corpus conforms; the check needs no grandfathering and stays simple.
|
||
- One-time restructure churn across ADRs 001–018 (heading reorganization + a Status and
|
||
a Consequences section per file; no decision substance changed).
|
||
- `/review-repo` grows one deterministic check; no new CI machinery.
|
||
- This ADR is the first conformant example and is held to its own check.
|
||
|
||
## What was ruled out
|
||
|
||
- **A `make lint` / CI gate for ADR structure** — heavier than the risk warrants;
|
||
the `/review-repo` check and the template suffice.
|
||
- **Machine-enforcing section order** — brittle for marginal value; left as a
|
||
template-demonstrated convention.
|
||
- **Grandfathering 001–018 from the check** — rejected in favour of restructuring the
|
||
whole corpus to conform, so the standard applies uniformly with no exceptions.
|
||
|
||
## Related
|
||
|
||
- ADR-014 — knowledge sourcing (the `Verified facts` optional section).
|
||
- ADR-019/020/021/022 — the emergent structure this ADR codifies.
|
||
- `docs/decisions/adr-template.md` — the scaffold.
|
||
- `scripts/repo-scan.py` — the `adr-structure` enforcement check.
|