docs(specs): make "a spec becomes an architecture doc" the written rule

The sixteen specs folded in last commit were folded because someone noticed
they had gone stale, not because anything said they should be. Without the rule
written down the directory drifts straight back to a mix of promises and
descriptions, and neither can be trusted: you cannot tell from a file whether it
describes the build or proposes a change to it.

So: docs/specs/ holds only unshipped work, there is no "Implemented" resting
state, and the fold-in and the deletion happen in the same commit.

The template now asks for the destination architecture doc **up front**, which
is a design check rather than bookkeeping — a feature that fits no existing doc
usually has an unclear layer assignment, and it is cheaper to find that out at
spec time. It also tells the author which half of what they are writing is
durable (invariants, rejected alternatives, the defect a decision prevents) and
which half dies with the file (phases, migration steps, acceptance criteria).

The review checklist gains a Lifecycle section, including the case that gets
lost otherwise: out-of-scope work worth doing has to be written where it will
still be found after the spec is gone.
This commit is contained in:
2026-08-21 18:29:09 +02:00
parent f6653e6a8b
commit 96abc3afef
3 changed files with 56 additions and 1 deletions
+28
View File
@@ -227,6 +227,34 @@ Before accepting a spec, run it past
a spec around "no Rust changes required" — correct layer placement is the goal,
not minimal backend churn.
### 🔴 A spec becomes an architecture doc when it ships
`docs/specs/` holds **only work that has not shipped**. There is no "Implemented"
resting state for a spec file: when the last acceptance criterion is met, fold
the design into [docs/architecture/](docs/architecture/README.md) and **delete
the spec in the same commit**.
This is not tidying. A directory that mixes promises with descriptions makes both
unreliable — you cannot tell from a file whether it describes the build or
proposes a change to it, and stale specs then quietly disagree with the code
while reading as authority.
- **Every spec names its destination up front** — the template's "Destination on
completion" line. Deciding at spec time which architecture doc will absorb it
is a design check in itself: a feature that fits no existing doc is usually a
feature whose layer assignment is unclear.
- **Carry the reasoning, not the plan.** The architecture doc gets the *why* a
future change still needs — invariants, rejected alternatives that would be
re-attempted, the defect a piece of code exists to prevent. Acceptance
criteria, phase breakdowns and migration steps die with the spec; git history
keeps them.
- **Deferred work outlives its spec.** Anything the spec listed as out-of-scope
and still worth doing goes beside the code it concerns, not into the void.
- **Rewrite inbound references before deleting** — source comments and CI
scripts cite spec paths, and `check-doc-links` only sees markdown.
- **Partially implemented is a real status.** A spec stays until *all* of it
ships, with the header naming what is left.
## Conventions
### Rust Backend
+12
View File
@@ -53,6 +53,18 @@ Copy the boxes into the review comment (or the PR) and tick them.
- [ ] Traceability coverage stays ≥ 88% (the CI gate — a ratchet, so check
`bun run traces:coverage` rather than trusting this number).
## Lifecycle
- [ ] **"Destination on completion" names a real architecture doc and section.**
This spec file is deleted when it ships; something has to absorb the
design. If nothing fits, the layer assignment is probably unclear — go back
to that table.
- [ ] The spec separates the **durable half** (invariants, rejected alternatives,
the defect a decision exists to prevent) from the **disposable half**
(phases, migration steps, acceptance criteria). Only the first is folded in.
- [ ] Anything listed as out of scope but still worth doing is written where it
will be found after this file is gone — beside the code it concerns.
## Conflicts & hygiene
- [ ] If this spec revises/supersedes another, the older spec gets a banner
+16 -1
View File
@@ -6,12 +6,27 @@
"Layer assignment" — read its comment before writing it.
Before merging a spec, run it past docs/specs/SPEC-REVIEW-CHECKLIST.md.
LIFECYCLE: this file is temporary. docs/specs/ holds only unshipped work — when
the last acceptance criterion is met, the design is folded into
docs/architecture/ and this file is deleted in the same commit. Write it
knowing that: the durable half is the reasoning (invariants, rejected
alternatives, the defect a decision prevents), and the disposable half is the
plan (phases, migration steps, acceptance criteria).
-->
**Status:** Proposed <!-- Proposed | Accepted | Implemented | Superseded -->
**Status:** Proposed <!-- Proposed | Accepted | Partially implemented | Superseded.
NOT "Implemented" — a fully shipped spec is folded into docs/architecture/
and deleted. See "Destination on completion" below. -->
**Requirements:** <!-- UR-xxx → DR-yyy; allocate new DRs in requirements.md. -->
**UX spec:** <!-- link to the relevant ux-flows.md section, or "n/a". -->
**Supersedes / revises:** <!-- link any spec this changes, or delete this line. -->
**Destination on completion:** <!--
Which architecture doc absorbs this design when it ships, and roughly which
section. e.g. "05-platform-backends.md — a new section beside
ExoPlayerBackend". Name it NOW: a feature that fits no existing doc usually
has an unclear layer assignment, which is worth finding out at spec time.
This spec file is deleted in the same commit that folds it in. -->
## Summary