boma/docs/decisions/023-adr-structure.md
sjat 6d7d27b03b docs(adr): add Proposed lifecycle state; mark ADR-011 Proposed
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>
2026-06-10 14:48:55 +02:00

106 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 001018 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 (019022) converged on a clean shape — Status → Context → Decision →
Consequences → Related — but only by imitation. ADRs 001018 predate it and drifted
widely: most lacked a `## Status` section entirely (016018 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 001018 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 001018 (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 001018 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.