From 89179dd7c9f85d66f09d041dc69ceb511ca69d03 Mon Sep 17 00:00:00 2001 From: sjat Date: Wed, 10 Jun 2026 14:28:20 +0200 Subject: [PATCH] =?UTF-8?q?docs(adr):=20revise=20spec+plan=20=E2=80=94=20f?= =?UTF-8?q?ull=20retroactive=20restructure=20of=20001-018?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../plans/2026-06-10-adr-structure.md | 152 ++++++++++-------- .../specs/2026-06-10-adr-structure-design.md | 59 +++++-- 2 files changed, 126 insertions(+), 85 deletions(-) diff --git a/docs/superpowers/plans/2026-06-10-adr-structure.md b/docs/superpowers/plans/2026-06-10-adr-structure.md index b63c22f..f057a73 100644 --- a/docs/superpowers/plans/2026-06-10-adr-structure.md +++ b/docs/superpowers/plans/2026-06-10-adr-structure.md @@ -23,7 +23,7 @@ - **Status lifecycle (3 states):** `Accepted (YYYY-MM-DD)` → optionally `Superseded by ADR-NNN (YYYY-MM-DD)` or `Deprecated (YYYY-MM-DD)`. No "Proposed" stage. - **No silent rewrites:** material reversal = new ADR + `Superseded by` marker; bidirectional link. - **Enforcement checks presence + parseable Status line, NOT section order.** Order is demonstrated by the template, not machine-enforced. -- **Backfill is Status-header-only** — no decision content touched. +- **Back-catalogue is fully restructured (no grandfathering)** — ADRs 001–018 are brought to all-four-section conformance. The restructure is **presentational**: relabel/regroup/demote existing headings, add a dated Status, assemble a Consequences section from implications the ADR already states. **The substance of no decision is changed.** If a faithful Consequences cannot be drawn from existing content, escalate that file rather than inventing one. --- @@ -280,18 +280,20 @@ Create `docs/decisions/023-adr-structure.md`. It must pass its own check (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. Resolves the FRICTION signal (2026-05-31) about ADR-writing policy -being unsettled. +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, and ADRs 001–018 predate it: 001–015 -carry no `## Status` section at all, and 016–018 have a trailing build-status note -rather than a lifecycle line. The result is structural drift and no uniform way to -tell an active decision from a superseded or deprecated one. +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 @@ -338,12 +340,22 @@ numbered ADR missing a mandatory section or with an unparseable Status line. It 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. -- One-time backfill churn: a Status header added to ADRs 001–018 (header only; no - decision content changed). +- 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. @@ -354,8 +366,8 @@ doctrine ADRs adding no CI gate). the `/review-repo` check and the template suffice. - **Machine-enforcing section order** — brittle for marginal value; left as a template-demonstrated convention. -- **Normalizing the body of 001–018** beyond adding `## Status` — out of scope; the - decisions themselves are untouched. +- **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 @@ -428,92 +440,96 @@ Co-Authored-By: Claude Opus 4.8 (1M context) " --- -## Task 5: Backfill `## Status` into ADRs 001–018 +## Task 5: Retroactively restructure ADRs 001–018 to full conformance -**Files:** -- Modify: `docs/decisions/001-architecture.md` … `015-control-host.md` (insert a Status section) -- Modify: `docs/decisions/016-mesh-vpn.md`, `017-service-ui-verification.md`, `018-logging.md` (prepend a parseable Accepted line to the existing Status section) +**Goal:** every ADR in 001–018 ends with all four mandatory sections present and a +parseable Status line, so the `adr-structure` check reports zero findings — **without +changing the substance of any decision.** + +**Files (current findings — the exact worklist):** +- Missing `Status` + `Consequences`: `001-architecture.md`, `002-security.md`, `004-docker-model.md`, `005-bootstrapping.md`, `014-knowledge-sourcing.md` +- Missing `Status` + `Decision` + `Consequences`: `006-terraform.md`, `007-network.md`, `008-testing.md`, `009-provisioning-handoff.md`, `010-forgejo-ci.md`, `011-update-management.md` +- Missing all four: `003-toolchain.md` +- Missing `Status` + `Decision`: `013-heritage-v4.md` +- Missing `Status` only: `012-hardware-capacity.md`, `015-control-host.md` +- Have unparseable `Status` + missing `Consequences`: `016-mesh-vpn.md`, `017-service-ui-verification.md`, `018-logging.md` + +(`010`/`011` use `## Decisions` (plural) → relabel to `## Decision`. The "missing +Decision" cases generally have the decision spread across topical `##` headings.) + +**THE FAITHFULNESS RULE (non-negotiable):** This is a *presentational* restructure. +You MAY: add a `## Status` section; relabel a heading (`## Decisions` → `## Decision`); +introduce a `## Decision` umbrella heading and **demote** existing topical `##` headings +to `###` beneath it; add a `## Consequences` section. You MUST NOT alter any existing +sentence of decision prose, reword arguments, or add new policy. A `## Consequences` +section is assembled **only** from implications the ADR already states (its trade-offs, +"what was ruled out", "open questions", named follow-on work). **If an ADR states +nothing that can be faithfully cast as a consequence, STOP and report it as +DONE_WITH_CONCERNS / escalate — do not invent consequences.** **Per-file date source:** the file's first git-commit (add) date — `git log --diff-filter=A --format=%as -- | tail -1` (yields `YYYY-MM-DD`). -- [ ] **Step 1: For each of 001–015, insert a Status section after the title** +- [ ] **Step 1: Add a dated `## Status` section to each ADR** -For each file `docs/decisions/NNN-*.md` in 001–015, get its date and insert a Status -section between the title line and the first `##` heading. Worked example for -`001-architecture.md` (its line 2 is blank, line 3 is `## Context`): - -```bash -f=docs/decisions/001-architecture.md -d=$(git log --diff-filter=A --format=%as -- "$f" | tail -1) -# Insert after the title's trailing blank line, before "## Context": -``` - -Use the Edit tool to change, in `001-architecture.md`: +For 001–015 (no Status today): insert, between the title line and the first `##` +heading, a Status section: ```markdown -# ADR-001 — Architecture overview - -## Context -``` - -to: - -```markdown -# ADR-001 — Architecture overview - ## Status Accepted () - -## Context ``` -where `` is the date from the command above. Repeat for 002–015, matching each -file's actual title text and its first `##` heading (the heading is not always -`## Context`). +where `` is the file's first-git-commit date. For 016/017/018 (unparseable Status +today): prepend a parseable `Accepted (). ` clause to the first line of their +existing `## Status` section so the build-state note becomes its tail, e.g. +`Accepted (2026-06-05). Designed. **Authorable now:** ...`. -- [ ] **Step 2: For 016, 017, 018, make the existing Status section parseable** +- [ ] **Step 2: Ensure a `## Decision` section exists** -These already have a `## Status` section whose first line is build-state prose. Get the -date (`git log --diff-filter=A --format=%as -- | tail -1`) and prepend a -parseable Accepted line so the existing note becomes a trailing clause. Worked example -for `018-logging.md` — change its Status section from: +For ADRs flagged "missing Decision" (003, 006, 007, 008, 009, 010, 011, 013): relabel a +plural/synonym heading where one exists (`## Decisions` → `## Decision` in 010/011), or +introduce a `## Decision` umbrella immediately after `## Context` and demote the existing +topical `##` body headings (e.g. in 003: "Execution engine", "Python environment", …) to +`###`. Do not move or rewrite the prose under them. -```markdown -## Status +- [ ] **Step 3: Ensure a `## Consequences` section exists** -Designed. **Authorable now:** ... -``` +For every ADR flagged "missing Consequences" (001, 002, 003, 004, 005, 006, 007, 008, +009, 010, 011, 014, 016, 017, 018): add a `## Consequences` section near the end, +assembled strictly from implications the ADR already states. Where an ADR has a trailing +section that *is* consequences under another name (e.g. "What was ruled out", "Open +questions", "Trade-offs"), you may keep that section and add a short `## Consequences` +that references/summarizes the already-stated trade-offs — without introducing new +claims. **Honour the faithfulness rule; escalate any ADR where no faithful Consequences +can be drawn.** -to: - -```markdown -## Status - -Accepted (). Designed. **Authorable now:** ... -``` - -Repeat for 016 and 017 with their own dates and existing first lines. - -- [ ] **Step 3: Verify the whole corpus now passes the check** +- [ ] **Step 4: Verify the whole corpus passes the check** Run: `python3 scripts/repo-scan.py 2>/dev/null | python3 -c "import json,sys; v=[f for f in json.load(sys.stdin)['findings'] if f['check']=='adr-structure']; print('adr-structure findings:', len(v)); [print(' ', f['path'], '—', f['detail']) for f in v]"` Expected: `adr-structure findings: 0`. -- [ ] **Step 4: Run the full repo-scan test suite** +- [ ] **Step 5: Verify faithfulness via diff** + +Run: `git diff --stat` and spot-check `git diff docs/decisions/003-toolchain.md`. +Expected: changes are heading additions/relabels/level-demotions, a new Status section, +and a new Consequences section — **no edits to existing decision sentences.** + +- [ ] **Step 6: Run the repo-scan test suite** Run: `.venv/bin/pytest tests/test_repo_scan.py -q` Expected: PASS — 5 passed. -- [ ] **Step 5: Commit** +- [ ] **Step 7: Commit** ```bash git add docs/decisions/0*.md docs/decisions/1*.md -git commit -m "docs(adr): backfill Status section into ADRs 001-018 +git commit -m "docs(adr): restructure ADRs 001-018 to ADR-023 conformance -Status header only (Accepted, dated from each file's first git-commit); -no decision content changed. Brings the back-catalogue to ADR-023 conformance. +Presentational only: add a dated Status section, relabel/regroup headings +under Decision, and add a Consequences section assembled from each ADR's +already-stated implications. No decision substance changed. Co-Authored-By: Claude Opus 4.8 (1M context) " ``` @@ -531,6 +547,6 @@ Co-Authored-By: Claude Opus 4.8 (1M context) " ## Self-review notes -- **Spec coverage:** §1 title/filename → Task 3 + template; §2 sections → Tasks 2/3 + check; §3 lifecycle → Task 3; §4 cross-refs → Task 3 `## Related`; §5 template → Task 2; §6 backfill → Task 5; §7 enforcement → Task 1 + Task 4. All covered. +- **Spec coverage:** §1 title/filename → Task 3 + template; §2 sections → Tasks 2/3 + check; §3 lifecycle → Task 3; §4 cross-refs → Task 3 `## Related`; §5 template → Task 2; §6 retroactive restructure → Task 5; §7 enforcement → Task 1 + Task 4. All covered. - **Order nuance:** spec says sections come "in this order"; the check enforces presence + Status only. This is intentional and stated in both the spec's enforcement wording ("the four mandatory sections and a parseable Status line") and ADR-023's Decision §5 / "What was ruled out". Not a gap. - **Type/name consistency:** `adr_structure_findings` and the `"adr-structure"` check key are used identically in the function, the `scan()` wiring, the tests, and both verification one-liners. diff --git a/docs/superpowers/specs/2026-06-10-adr-structure-design.md b/docs/superpowers/specs/2026-06-10-adr-structure-design.md index c14e84b..cd35dfe 100644 --- a/docs/superpowers/specs/2026-06-10-adr-structure-design.md +++ b/docs/superpowers/specs/2026-06-10-adr-structure-design.md @@ -39,14 +39,17 @@ lifecycle, ships a template, and reconciles the back-catalogue. - **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). + 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 @@ -103,12 +106,29 @@ 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. +### 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: @@ -120,7 +140,11 @@ a parseable `## Status` line. The template carries the convention forward for ne - 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). +- 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. @@ -128,5 +152,6 @@ a parseable `## Status` line. The template carries the convention forward for ne ## 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. +(`adr-template.md`), enforcement (`/review-repo`, no CI gate), and the **full +retroactive restructure** of 001–018 (no grandfathering) were all confirmed during +brainstorming.