diff --git a/docs/superpowers/specs/2026-06-10-adr-structure-design.md b/docs/superpowers/specs/2026-06-10-adr-structure-design.md new file mode 100644 index 0000000..c14e84b --- /dev/null +++ b/docs/superpowers/specs/2026-06-10-adr-structure-design.md @@ -0,0 +1,132 @@ +# 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 backfill of `## Status` into + the ADRs that lack one. +- **Out (for now):** rewriting the *decisions* in any existing ADR; normalizing the + body section names of 001–018 beyond adding `Status`; 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). + +## Decision + +### 1. Title & filename +- Title line: `# ADR-NNN — : <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 backfill (001–018) +A **separate follow-up step** after the ADR and template land: add a `## Status` +section to every ADR that lacks one. Status value is `Accepted (YYYY-MM-DD)` where the +date is reconstructed from each file's **first git-commit date**. **Only the Status +header is added — no decision content is touched.** ADRs already carrying a `## Status` +(019–022) are left alone. + +### 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 backfill commit touching ~18 files (Status header only). +- `/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 +Status-only backfill were all confirmed during brainstorming.