Replaces the Status-only backfill with a faithful presentational restructure bringing the whole back-catalogue to 4-section conformance (no grandfathering). Adds the faithfulness rule and per-file worklist. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
157 lines
8.5 KiB
Markdown
157 lines
8.5 KiB
Markdown
# Design — ADR structure & lifecycle
|
||
|
||
- **Date:** 2026-06-10
|
||
- **Status:** Approved design — implementation plan to follow
|
||
- **Resolves:** the absence of a written standard for how ADRs in
|
||
`docs/decisions/` are structured. The newest ADRs (019–022) have converged on a
|
||
clean pattern (`Status` → `Context` → `Decision` → `Consequences` → `Related`),
|
||
but it lives only as imitation; ADRs 001–018 predate it and most lack a `Status`
|
||
section.
|
||
- **Becomes:** ADR-023 (this design is the basis for that ADR).
|
||
- **Reuses:** boma's existing `*-template.md` convention (`service-security-template.md`,
|
||
`service-verify-template.md`, `service-access-template.md`, `service-backup-template.md`);
|
||
ADR-014 (knowledge-sourcing → the optional `Verified facts` section); ADR-019/020/021/022
|
||
(the emergent structure being codified); the `/review-repo` command (enforcement home).
|
||
|
||
---
|
||
|
||
## Problem
|
||
|
||
boma documents architectural decisions as numbered ADRs in `docs/decisions/`, and
|
||
CLAUDE.md treats them as load-bearing ("Before assuming a role, provider, or pipeline
|
||
exists, check STATUS.md"; the entire "Further reading" table points into them). Yet
|
||
there is no ADR that says how an ADR is written. The result:
|
||
|
||
- **Structural drift.** ADRs 001–018 are freeform; 019–022 converged on a consistent
|
||
shape but only by imitation. A new ADR's structure depends on which existing one the
|
||
author happened to copy.
|
||
- **No status discipline.** Most early ADRs have no `## Status` section, so there is no
|
||
uniform way to tell an active decision from a superseded or deprecated one — and no
|
||
written rule for how a decision gets reversed without silently rewriting history.
|
||
- **No scaffold.** Every other recurring document type in boma has a template
|
||
(`service-security-template.md`, etc.). ADRs do not.
|
||
|
||
This design codifies the structure 019–022 already demonstrate, pins a status
|
||
lifecycle, ships a template, and reconciles the back-catalogue.
|
||
|
||
## Scope
|
||
|
||
- **In:** the canonical section set (mandatory + optional); title and filename
|
||
convention; the `Accepted / Superseded / Deprecated` status lifecycle and the
|
||
no-silent-rewrite rule; cross-reference convention; an ADR template file; a
|
||
lightweight `/review-repo` structure check; a **one-time retroactive restructure of
|
||
ADRs 001–018** to full conformance (all four mandatory sections + a parseable Status
|
||
line), reorganizing existing content under canonical headings.
|
||
- **Out (for now):** *changing the substance of* any existing decision (the restructure
|
||
is presentational — relabel/regroup/demote existing content, add a dated Status, never
|
||
alter what was decided); a `make lint` / CI gate for ADR structure (explicitly
|
||
rejected in favour of the `/review-repo` check — consistent with boma's other doctrine
|
||
ADRs, which add no CI gate); a "Proposed" draft stage (rejected — boma is
|
||
single-contributor and trunk-based with no review gate, so ADRs are born Accepted);
|
||
grandfathering pre-convention ADRs from the check (rejected — the whole corpus is
|
||
brought to conformance instead).
|
||
|
||
## Decision
|
||
|
||
### 1. Title & filename
|
||
- Title line: `# ADR-NNN — <Title>: <optional clarifying subtitle>` (em-dash `—`,
|
||
matching every existing ADR).
|
||
- 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. Canonical sections
|
||
|
||
**Mandatory — every ADR, in this order:**
|
||
|
||
| Section | Holds |
|
||
|---|---|
|
||
| `## Status` | `Accepted (YYYY-MM-DD)`, plus an optional one-line note (what it resolves/supersedes, or a doctrine-not-yet-built caveat as ADR-022 uses) |
|
||
| `## Context` | the forces, the problem, what exists today, why now |
|
||
| `## Decision` | what we are doing — numbered sub-decisions for multi-part ADRs, as 020/021/022 do |
|
||
| `## Consequences` | results, trade-offs *explicitly accepted*, follow-on work |
|
||
|
||
**Optional — use only where genuinely applicable, never as padding:**
|
||
|
||
- `## Related` — links to other ADRs by number.
|
||
- `## Scope` — explicit in/out-of-scope boundaries.
|
||
- `## Guardrails` / `## Enforcement` — how the decision is mechanically enforced
|
||
(lint, CI, hooks).
|
||
- `## What was ruled out` — rejected alternatives, each with its reason.
|
||
- `## Verified facts (ADR-014)` — version-stamped facts per the knowledge-sourcing rule.
|
||
|
||
### 3. Status lifecycle
|
||
|
||
Three states; no "Proposed" stage.
|
||
|
||
- Born **`Accepted (YYYY-MM-DD)`** — the sole author commits to it on writing.
|
||
- Replaced by a later decision → the old ADR's Status becomes
|
||
**`Superseded by ADR-NNN (YYYY-MM-DD)`**; the superseding ADR records
|
||
`Supersedes ADR-MMM` in its own `## Status` and `## Related`. The link is
|
||
**bidirectional** — both files must point at each other.
|
||
- Retired with no replacement → **`Deprecated (YYYY-MM-DD)`** plus a one-line reason.
|
||
|
||
**Load-bearing rule — 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. The history of decisions stays legible.
|
||
|
||
### 4. Cross-references
|
||
Reference other ADRs by number inline (`ADR-019`), and collect the relationships in a
|
||
`## Related` section.
|
||
|
||
### 5. Template file
|
||
Ship `docs/decisions/adr-template.md` — consistent with boma's existing
|
||
`*-template.md` convention. It contains the mandatory section headers pre-filled with
|
||
short HTML-comment hints, and the optional sections listed as commented stubs to
|
||
uncomment when relevant. It is a skeleton, not a numbered decision, so it does not take
|
||
an ADR number.
|
||
|
||
### 6. Retroactive restructure (001–018)
|
||
A **separate step** after the ADR and template land: bring every pre-convention ADR to
|
||
full conformance — all four mandatory sections present and a parseable Status line. This
|
||
is a **presentational** restructure, governed by a strict faithfulness rule:
|
||
|
||
- **Add** a `## Status` section valued `Accepted (YYYY-MM-DD)`, the date reconstructed
|
||
from the file's **first git-commit date**. For 016–018, whose existing trailing
|
||
build-state note is unparseable, prepend the dated `Accepted (...)` clause so the note
|
||
becomes a parseable Status line's tail.
|
||
- **Reorganize** existing content under the canonical headings: relabel a synonym
|
||
(`## Decisions` → `## Decision`), or introduce a `## Decision` umbrella and **demote**
|
||
the existing topical `##` headings to `###` beneath it. No sentence of existing prose
|
||
is altered.
|
||
- **Add** a `## Consequences` section built **only** from implications the ADR already
|
||
states (trade-offs, "what was ruled out", "open questions", follow-on work already
|
||
named). If an ADR genuinely states nothing that can be faithfully cast as a
|
||
consequence, that file is escalated for a human decision rather than inventing one.
|
||
- **Never** change the substance of a decision. A `git diff` of the restructure should
|
||
show heading-level changes, a new Status section, and a Consequences section assembled
|
||
from existing material — not edits to existing argument.
|
||
|
||
ADRs already conformant (019–022) are left alone. End state: the `adr-structure` check
|
||
reports zero findings across the whole corpus, with no grandfathering.
|
||
|
||
### 7. Enforcement
|
||
Lightweight, no CI gate. The `/review-repo` command gains an ADR-structure check:
|
||
every file in `docs/decisions/` matching `NNN-*.md` has the four mandatory sections and
|
||
a parseable `## Status` line. The template carries the convention forward for new ADRs.
|
||
|
||
## Consequences
|
||
|
||
- New ADRs have one obvious shape and a scaffold to start from; structural drift stops.
|
||
- Every ADR declares its lifecycle state uniformly, and reversals are traceable rather
|
||
than silent — the back-catalogue becomes a legible decision history.
|
||
- One-time churn: a restructure touching ~18 files (heading reorganization + a Status
|
||
section + a Consequences section per file). Larger and more judgment-heavy than a
|
||
Status-only backfill, hence the faithfulness rule and per-file review.
|
||
- The whole corpus conforms — the check needs no grandfathering or number threshold, and
|
||
stays simple (presence + parseable Status, applied uniformly).
|
||
- `/review-repo` grows a new check; no new CI machinery, matching boma's habit of not
|
||
gating doctrine in CI.
|
||
- This ADR is itself the first conformant example — it must follow its own structure.
|
||
|
||
## Open questions
|
||
|
||
None outstanding — title/filename, the 3-state lifecycle, template name
|
||
(`adr-template.md`), enforcement (`/review-repo`, no CI gate), and the **full
|
||
retroactive restructure** of 001–018 (no grandfathering) were all confirmed during
|
||
brainstorming.
|