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>
This commit is contained in:
sjat 2026-06-10 14:48:55 +02:00
parent b3ca510380
commit 6d7d27b03b
7 changed files with 54 additions and 27 deletions

View file

@ -2,9 +2,8 @@
## Status
Accepted (2026-06-04)
**Status: Proposed — draft for discussion (not yet accepted).**
Proposed (2026-06-04) — draft for discussion; not yet accepted. The core decisions
below are settled in intent, but several specifics remain open (see "Open questions").
## Context

View file

@ -30,7 +30,8 @@ keeps its number and file. A new ADR is registered as a row in the CLAUDE.md
### 2. Mandatory sections, in this order
- `## Status``Accepted (YYYY-MM-DD)`, plus an optional one-line note.
- `## 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.
@ -42,10 +43,15 @@ keeps its number and file. A new ADR is registered as a row in the CLAUDE.md
### 4. Status lifecycle
Three states; no "Proposed" stage (boma is single-contributor and trunk-based with no
review gate, so an ADR is born committed-to).
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.
- Born **`Accepted (YYYY-MM-DD)`**.
- **`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**.
@ -85,7 +91,6 @@ a consistent, legible decision history.
## What was ruled out
- **A "Proposed" draft stage** — no review gate exists for it to serve.
- **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

View file

@ -8,8 +8,10 @@
## Status
Accepted (YYYY-MM-DD)
<!-- Lifecycle: "Accepted (YYYY-MM-DD)" → later "Superseded by ADR-NNN (YYYY-MM-DD)"
or "Deprecated (YYYY-MM-DD)" + one-line why. Optional trailing note OK, e.g.
<!-- Lifecycle: usually born "Accepted (YYYY-MM-DD)"; use "Proposed (YYYY-MM-DD)" for a
genuine draft (open questions), promoted to Accepted once settled. Later:
"Superseded by ADR-NNN (YYYY-MM-DD)" or "Deprecated (YYYY-MM-DD)" + one-line why.
Optional trailing note OK, e.g.
"Accepted (2026-06-10). Doctrine ADR — pins policy, builds nothing yet." -->
## Context

View file

@ -20,7 +20,7 @@
- **Mandatory sections, in this order:** `## Status`, `## Context`, `## Decision`, `## Consequences`.
- **Optional sections:** `## Related`, `## Scope`, `## Guardrails` / `## Enforcement`, `## What was ruled out`, `## Verified facts (ADR-014)`.
- **Status lifecycle (3 states):** `Accepted (YYYY-MM-DD)` → optionally `Superseded by ADR-NNN (YYYY-MM-DD)` or `Deprecated (YYYY-MM-DD)`. No "Proposed" stage.
- **Status lifecycle (4 states):** `Proposed (YYYY-MM-DD)` (genuine drafts, e.g. ADR-011) → `Accepted (YYYY-MM-DD)` (the common starting state) → optionally `Superseded by ADR-NNN (YYYY-MM-DD)` or `Deprecated (YYYY-MM-DD)`. (`Proposed` was added on the evidence of ADR-011, which is a real draft with open questions.)
- **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.
- **Back-catalogue is fully restructured (no grandfathering)** — ADRs 001018 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.
@ -305,7 +305,8 @@ keeps its number and file. A new ADR is registered as a row in the CLAUDE.md
### 2. Mandatory sections, in this order
- `## Status``Accepted (YYYY-MM-DD)`, plus an optional one-line note.
- `## 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.
@ -317,10 +318,15 @@ keeps its number and file. A new ADR is registered as a row in the CLAUDE.md
### 4. Status lifecycle
Three states; no "Proposed" stage (boma is single-contributor and trunk-based with no
review gate, so an ADR is born committed-to).
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.
- Born **`Accepted (YYYY-MM-DD)`**.
- **`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**.
@ -360,7 +366,6 @@ a consistent, legible decision history.
## What was ruled out
- **A "Proposed" draft stage** — no review gate exists for it to serve.
- **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

View file

@ -46,10 +46,12 @@ lifecycle, ships a template, and reconciles the back-catalogue.
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).
ADRs, which add no CI gate); grandfathering pre-convention ADRs from the check
(rejected — the whole corpus is brought to conformance instead).
The lifecycle uses four states — `Proposed / Accepted / Superseded / Deprecated`. An
earlier draft of this design omitted `Proposed`, but ADR-011 (a real draft with open
questions) is evidence boma occasionally needs it, so it was kept.
## Decision
@ -82,9 +84,13 @@ lifecycle, ships a template, and reconciles the back-catalogue.
### 3. Status lifecycle
Three states; no "Proposed" stage.
Four states. Most ADRs are **born `Accepted (YYYY-MM-DD)`** — the sole author commits
to it on writing (boma is single-contributor and trunk-based with no review gate).
- Born **`Accepted (YYYY-MM-DD)`** — the sole author commits to it on writing.
- **`Proposed (YYYY-MM-DD)`** — a genuine draft whose core direction is recorded but
whose specifics are still open (e.g. ADR-011, which carries open questions). Promoted
to `Accepted (YYYY-MM-DD)` once settled.
- **`Accepted (YYYY-MM-DD)`** — committed-to; the common starting state.
- 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
@ -151,7 +157,8 @@ a parseable `## Status` line. The template carries the convention forward for ne
## Open questions
None outstanding — title/filename, the 3-state lifecycle, template name
None outstanding — title/filename, the **4-state lifecycle** (`Proposed / Accepted /
Superseded / Deprecated`; `Proposed` adopted on the evidence of ADR-011), template name
(`adr-template.md`), enforcement (`/review-repo`, no CI gate), and the **full
retroactive restructure** of 001018 (no grandfathering) were all confirmed during
brainstorming.
brainstorming and execution.

View file

@ -47,7 +47,8 @@ RESOLVE_WORD_RE = re.compile(r"\b(?:resolv\w*|decid\w*|address\w*|complet\w*|don
ADR_FILE_RE = re.compile(r"^\d{3}-.*\.md$")
ADR_REQUIRED_SECTIONS = ("Status", "Context", "Decision", "Consequences")
ADR_STATUS_LINE_RE = re.compile(
r"^(Accepted \(\d{4}-\d{2}-\d{2}\)"
r"^(Proposed \(\d{4}-\d{2}-\d{2}\)"
r"|Accepted \(\d{4}-\d{2}-\d{2}\)"
r"|Superseded by ADR-\d{3}"
r"|Deprecated \(\d{4}-\d{2}-\d{2}\))")
@ -134,8 +135,9 @@ def adr_structure_findings(adr_files):
if not ADR_STATUS_LINE_RE.match(status_text):
out.append({"check": "adr-structure", "severity": "medium",
"path": rpath, "line": headings["Status"] + 1,
"detail": "Status not parseable (want 'Accepted (YYYY-MM-DD)', "
"'Superseded by ADR-NNN', or 'Deprecated (YYYY-MM-DD)'); "
"detail": "Status not parseable (want 'Proposed (YYYY-MM-DD)', "
"'Accepted (YYYY-MM-DD)', 'Superseded by ADR-NNN', or "
"'Deprecated (YYYY-MM-DD)'); "
f"got: {status_text[:60]!r}"})
return out

View file

@ -46,6 +46,13 @@ def test_superseded_status_is_accepted():
assert out == []
def test_proposed_status_is_accepted():
lines = [("Proposed (2026-06-04)\n" if ln == "Accepted (2026-06-10)\n"
else ln) for ln in GOOD]
out = _checks(rs.adr_structure_findings({"docs/decisions/099-example.md": lines}))
assert out == []
def test_non_numbered_file_is_skipped():
bare = ["# ADR template\n", "\n", "## Status\n", "\n", "<!-- hint -->\n"]
out = _checks(rs.adr_structure_findings({"docs/decisions/adr-template.md": bare}))