wdi-method 0.6.15 → 0.6.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -43,6 +43,7 @@ Never a rule. When it disagrees with a guide, the guide wins and the disagreemen
43
43
  | [`language-guide.md`](language-guide.md) | Naming anything — a code identifier, a code file, a document file |
44
44
  | [`method-glossary.md`](method-glossary.md) | Unsure what a method term means — layer, wave, Product Component, ID code |
45
45
  | [`structure-guide.md`](structure-guide.md) | Writing or checking the two structure maps in `.control/` |
46
+ | [`ci-guide.md`](ci-guide.md) | Writing or changing a CI workflow; when a push may start a cloud run, and what MUST NOT |
46
47
 
47
48
  ## `document/` — document rules
48
49
 
@@ -0,0 +1,150 @@
1
+ ---
2
+ status: Accepted
3
+ ---
4
+
5
+ # CI Guide
6
+
7
+ **Loaded when:** writing or changing a GitHub Actions workflow, or deciding whether a push may start a
8
+ cloud run
9
+
10
+ Cloud runners are **metered**, and the meter is not flat: a Windows runner bills at **2×** the minutes it
11
+ uses and macOS at **10×**, and on a private repository every one of those minutes comes out of a monthly
12
+ allowance. One real run of `wdi-autopilot` over fifteen tickets pushed often enough to start CI dozens of
13
+ times and spent most of a month's allowance in two days.
14
+
15
+ **The fix is not fewer commits.** Commits stay granular — one per ticket, plus the memlog and registry
16
+ writes — because that is what makes a run reviewable and resumable. What changes is **what a push
17
+ triggers**.
18
+
19
+ ## One unit of work, one cloud run
20
+
21
+ | | Runs where | When |
22
+ |---|---|---|
23
+ | Build, typecheck, the full suite **during** the work | **Locally**, on the machine doing the work | Every ticket — `wdi-build` Phase 3 Step 2 already requires it, and it is free |
24
+ | The cloud workflow | GitHub Actions | **Once**, when the work is offered for review |
25
+
26
+ - A workflow MUST be configured so that an intermediate push — a ticket commit, a memlog rewrite, a
27
+ registry catch-up, a spec close — starts **nothing**.
28
+ - The cloud run MUST happen before the work is merged. Green CI on the **pushed head SHA** is still the
29
+ release evidence; what moves is how many times it is collected, not whether it is.
30
+ - Under a mandate the unit of work is the whole run, so the one cloud run belongs at `wdi-autopilot`
31
+ § Finish. That skill owns the sequence and this guide MUST NOT restate it.
32
+ - Where the repo's workflow cannot be changed — a shared org template, a workflow another team owns —
33
+ the run MUST keep intermediate work off the remote instead: hold the push, or make the pushed head
34
+ commit carry `[skip ci]`, which GitHub honours for `push` and `pull_request` events.
35
+
36
+ ## Trigger shape
37
+
38
+ | Event | Use it | Why |
39
+ |---|---|---|
40
+ | `workflow_dispatch` | **MUST** be present | The manual re-run. Without it, a red run can only be retried by pushing again |
41
+ | `pull_request:` `types: [ready_for_review]` | The one automatic trigger | A draft PR is work in progress; marking it ready is the moment somebody is asking for the verdict |
42
+ | `push:` `branches: [main]` | MAY | One run per merge, as the record of trunk health. Drop it where the allowance is tight |
43
+ | bare `on: push` | **MUST NOT** | Every branch, every commit, no filter. This is the setting that spends an allowance |
44
+
45
+ Two consequences worth stating, because both surprise people:
46
+
47
+ - With `types: [ready_for_review]` and no `synchronize`, a push **after** the PR is ready does not
48
+ re-run CI. Re-run it with `workflow_dispatch`, or convert the PR back to draft and mark it ready
49
+ again. That is the intended trade: the re-run is a decision, not a reflex.
50
+ - `concurrency` with `cancel-in-progress: true` stops two runs of the same ref from billing at once.
51
+ Every workflow below sets it.
52
+
53
+ ## What MUST NOT start a build
54
+
55
+ A change that touches only prose or only the corpus cannot break the code, so it MUST NOT start the
56
+ product's build. `paths-ignore` carries that: `**.md`, `.scratch/**`, and the method's own layers —
57
+ `.control/**`, `.what/**`, `.how/**`, `.constitution/**`, `_bmad-output/**`, `.work/**`.
58
+
59
+ The corpus workflow is the **mirror image** of that list and MUST stay a separate workflow: it runs the
60
+ validators, on Ubuntu, only when the corpus changed. Keeping the two apart is what lets the expensive one
61
+ be ignored while the cheap one still guards the registry.
62
+
63
+ ## Template — `.github/workflows/ci.yml`
64
+
65
+ The product's build and test. This is the expensive one; the `runs-on` and the two `run:` lines are the
66
+ product's, and they come from `.constitution/project/codebase-stack-guide.md`.
67
+
68
+ ```yaml
69
+ name: ci
70
+
71
+ on:
72
+ workflow_dispatch:
73
+ pull_request:
74
+ types: [ready_for_review]
75
+ paths-ignore:
76
+ - '**.md'
77
+ - '.scratch/**'
78
+ - '.control/**'
79
+ - '.what/**'
80
+ - '.how/**'
81
+ - '.constitution/**'
82
+ - '_bmad-output/**'
83
+ - '.work/**'
84
+ # One run per merge, as the record of trunk health. Delete this block where the allowance is tight.
85
+ push:
86
+ branches: [main]
87
+ paths-ignore:
88
+ - '**.md'
89
+ - '.scratch/**'
90
+ - '.control/**'
91
+ - '.what/**'
92
+ - '.how/**'
93
+ - '.constitution/**'
94
+ - '_bmad-output/**'
95
+ - '.work/**'
96
+
97
+ concurrency:
98
+ group: ci-${{ github.ref }}
99
+ cancel-in-progress: true
100
+
101
+ jobs:
102
+ build:
103
+ # A Windows runner bills 2× and macOS 10×. Name only the platforms the product actually ships on.
104
+ runs-on: ubuntu-latest
105
+ steps:
106
+ - uses: actions/checkout@v4
107
+ # Replace both lines with this product's build and test commands.
108
+ - run: echo "build command from codebase-stack-guide.md"
109
+ - run: echo "test command from codebase-stack-guide.md"
110
+ ```
111
+
112
+ ## Template — `.github/workflows/korpus.yml`
113
+
114
+ The corpus validators. Cheap, Ubuntu, and it runs only when the corpus moved — so it MAY keep the
115
+ default `pull_request` trigger, which gives a verdict on the registry while the expensive workflow stays
116
+ quiet. `korpus.yml` validates the corpus and **not** the code: a green run here is never build evidence.
117
+
118
+ ```yaml
119
+ name: korpus
120
+
121
+ on:
122
+ workflow_dispatch:
123
+ pull_request:
124
+ paths:
125
+ - '.control/**'
126
+ - '.what/**'
127
+ - '.how/**'
128
+ - '.constitution/**'
129
+
130
+ concurrency:
131
+ group: korpus-${{ github.ref }}
132
+ cancel-in-progress: true
133
+
134
+ jobs:
135
+ validate:
136
+ runs-on: ubuntu-latest
137
+ steps:
138
+ - uses: actions/checkout@v4
139
+ # The three scripts declare their dependencies inline (PEP 723); uv is what runs them.
140
+ - uses: astral-sh/setup-uv@v5
141
+ - run: uv run .constitution/method/scripts/validate.py
142
+ ```
143
+
144
+ ## Red flags
145
+
146
+ - `on: push` with no branch filter, in a repo whose runners are metered
147
+ - The product's build and the corpus validators in one workflow — the cheap half then cannot run alone
148
+ - A cloud run started to find out whether the code compiles, when the local suite answers that for free
149
+ - CI watched per ticket under a mandate, instead of once at § Finish
150
+ - A green `korpus.yml` read as a passing build
@@ -63,6 +63,12 @@ decision or a PRD MAY point into it. Research, brainstorming, forge, and PRFAQ r
63
63
  A run folder MUST NOT be deleted **while anything still needs it** — the `update` intents re-read the original
64
64
  inputs in place. "Never deleted" is not the rule; the rule is a **retirement condition**, and it is below.
65
65
 
66
+ **What git ignores is not corpus.** A vendored upstream checkout kept for reading, a scratch download, a
67
+ build cache — if the product excludes it from git it is in no clone, nobody curates it, and the validators
68
+ do not read it. The other half of that rule is `corpus-in-git`: a folder the method itself keeps MUST NOT be
69
+ excluded, and the two lock together — material is either in git and checked, or ignored and not corpus. What
70
+ `.gitignore` MUST NOT be used for is quieting a finding about a file that really is this product's.
71
+
66
72
  ### A withdrawn promise STAYS in the registry
67
73
 
68
74
  A `BG` · `CAP` · `FR` · `NFR` · `UC` the product stops promising is marked, never deleted:
@@ -1,208 +1,216 @@
1
- ---
2
- status: Accepted
3
- ---
4
-
5
- # Decision Guide
6
-
7
- **Loaded when:** opening, accepting, or applying a `DEC-`
8
-
9
- A `DEC-` records a decision worth remembering. The old name was ADR — *Architecture Decision Record*
10
- — and the word "Architecture" forced the wrong question at the moment of writing: *"is this
11
- architectural?"*. That question throws away the decisions most worth keeping, the ones that sound
12
- small: *"the filter works like this"*, *"this list is sorted that way"*.
13
-
14
- ## A decision's first home is the document it governs
15
-
16
- **Changing a document NEVER requires a `DEC-`.** A `DEC-` is not permission to edit, not a record that
17
- an edit happened, and not a step between deciding and writing. Where the answer has a home in a design
18
- document — an `FR` in the PRD, a rule in `business-rules.md`, a boundary in an SDD, a line in the brief —
19
- **it is written there and nowhere else**, and there is nothing further to do.
20
-
21
- A `DEC-` is what you write for a decision with **no such home**. That is the whole of its job, plus the
22
- one mandatory case below.
23
-
24
- | The answer is about | Where it goes |
25
- |---|---|
26
- | What the product promises, does, or forbids | The design document that carries it |
27
- | How this repo builds — a convention, a stack choice | `.constitution/project/`, once code ratifies it |
28
- | Accepting a risk, cutting a scope, choosing between two paths **that no document holds** | A `DEC-` |
29
- | Contradicting an `AD-N` | A `DEC-`, mandatory |
30
-
31
- This matters most **early**. At G1 a brief is still being formed and at G2 a PRD is still being written;
32
- almost nothing there is homeless, so almost nothing there is a `DEC-`. Demanding one before the artifact
33
- it would govern even exists is ceremony at the moment the project can least afford it.
34
-
35
- ## Then one test decides whether to record it at all
36
-
37
- > **If someone asks in three months why it is like this, is the answer readable from the code?**
38
-
39
- Yes → it MUST NOT be recorded. No → it is recorded.
40
-
41
- **A `DEC-` records a state, never an event.** It answers *why is it like this* for someone about to
42
- change it — forward-looking, present tense. It is not a record that something changed, not a record that
43
- a document used to say otherwise, and not a record that a review found a conflict. Those are document
44
- history, and `corpus-guide.md` § The corpus is written in the present tense says they go nowhere.
45
-
46
- The practical form of the test: **would this file save the next person from a mistake they were about to
47
- make?** If the honest answer is *"no, but it explains what happened"*, there is no file.
48
-
49
- **Recording is not mandatory, and a decision nobody recorded is normal rather than negligence.** It
50
- MUST NOT be logged as debt, raised as a finding, or backfilled later from memory. Without this
51
- sentence, "not mandatory" is read as "mandatory but allowed to be late".
52
-
53
- **No case is mandatory any more, and one guard survives all of them.** Three rules used to demand a
54
- `DEC-` file. Each was protecting something real, and in each the protection was the **stop**, never the
55
- document:
56
-
57
- | Was | Is now |
58
- |---|---|
59
- | Contradicting an `AD-N` demanded a `DEC-` | It **stops and reaches the owner**. Nobody narrows an invariant silently. The record is the edited `AD-N`, present tense, plus the commit — a `DEC-` only if the reason cannot be read from the spine |
60
- | `risk_accepted: high` on a sensitive component demanded a risk-acceptance `DEC-` | `risk_accepted_by` names **a person and a date**, in `components.yaml` where the risk is set. `high-risk-named` checks it, and still resolves a `DEC-` id if one is given |
61
- | Cancelling >30% of a spec's tickets demanded a `DEC-` | The spec is **re-cut** rather than patched. `specs.yaml` is the record |
62
-
63
- What each of those bought was that a human made the call. That is preserved. What each of them also
64
- cost — a file, written at the moment the work was blocked — is not.
65
-
66
- An `AD-N` change stopping is the one that MUST NOT be softened further: it is what stops an architecture
67
- being narrowed twice by two passes neither of which thought it was doing anything.
68
-
69
- **Nothing else stops the work at all.** Where a change contradicts an `FR`, a `UC`, a business rule,
70
- or a document's wording, the agent states the consequence once and the owner decides. If they proceed the
71
- documents are edited to match — `delivery-flow-guide.md` § When something settled has to change owns the
72
- matrix, and the survey behind that warning is spent the moment the owner answers.
73
-
74
- ## `AD-N` and `DEC-NNN` are not the same thing
75
-
76
- | | `AD-N` | `DEC-NNN` |
77
- |---|---|---|
78
- | Is | A **living rule** in `ARCHITECTURE-SPINE.md` | A **decision event** |
79
- | Lives in | The spine | `.control/decisions/` |
80
- | Changes by | Being edited in place | Never, once `applied` — a new `DEC-` supersedes it |
81
- | Answers | What is forbidden from now on | What was chosen, and what it cost |
82
-
83
- An `AD-N` usually has a `DEC-` behind it. Neither MUST be written in place of the other, and one MUST
84
- NOT be converted into the other.
85
-
86
- ## Shape — three sections, and no more required
87
-
88
- | Section | States | Required |
89
- |---|---|---|
90
- | **Decision** | One sentence, present tense, quotable into a rule | always |
91
- | **Why** | The context that forced it, in a few lines | always |
92
- | **Cost** | What becomes harder. A decision with only benefits was not thought through | always |
93
- | Alternatives | What else was considered, and why each lost | see below |
94
- | Reversal trigger | The observable condition that makes revisiting this correct | see below |
95
- | Trace | Where it came from, and what it landed in | see below |
96
-
97
- The last three MUST be present when the decision reaches a Product Component whose `risk_accepted`
98
- is `low` in `components.yaml`. Everywhere else they are optional, and an empty one MUST be dropped
99
- rather than left as a heading with nothing under it.
100
-
101
- **A `DEC-` MUST NOT hold an open question.** Those belong to `.control/questions/`.
102
-
103
- **One page, and that is a bound, not a target.** A `DEC-` records **what was chosen and what it cost** —
104
- not how the answer was reached, not the transcript of the reasoning, not every reading of every clause
105
- that was weighed on the way. One real decision reached **124 lines** to record that one invariant does not
106
- reach one artifact; the sentence that mattered was one line and the cost was two.
107
-
108
- Three things MUST NOT appear in a `DEC-`, and each of them is the derivation leaking in:
109
-
110
- - The search that found the answer — which files were grepped, which clause was read first.
111
- - A meta-note about the decision itself: whether it should have been a `DEC-` at all, whether some other
112
- mechanism was considered and rejected. If that reasoning matters it is the **Why**; usually it does not
113
- matter and it is nothing.
114
- - A correction of an earlier draft of the same decision. Drafts are git's.
115
-
116
- `Alternatives` is the one place a rejected option belongs, it is a **line each**, and it is required only
117
- where the section table above says so.
118
-
119
- ## Frontmatter
120
-
121
- | Field | Rule |
122
- |---|---|
123
- | `id` | `DEC-NNN`, allocated from `.control/registry/decisions.yaml`, globally. MUST NOT restart per component or per release |
124
- | `status` | `draft` · `accepted` · `applied` · `superseded` · `rejected` |
125
- | `touches` | Empty at `draft`. Filled **when the decision is applied**, with the files it actually changed |
126
- | `type` | Free text, and optional. Written when it is useful — `risk-acceptance`, `course-correction`. One value has a fixed shape: `mandate`, opened by `wdi-autopilot`, whose parameters live under `mandate:` on the registry row and nowhere else |
127
- | `accepted_by` | Who raised it to `accepted`. A person and a date, the way `risk_accepted_by` is written — or the `DEC-` of a `type: mandate` that delegated the acceptance. `mandate-accept` checks the second form |
128
- | `supersedes` · `superseded_by` | Both sides of a supersession MUST exist |
129
-
130
- **There is no `layer:` and no `component:`.** Both were classifications demanded before anything was
131
- known, and both were guessed as often as they were derived. `touches:` replaces them, and it is filled
132
- from what happened rather than from what was predicted.
133
-
134
- The filename is `DEC-NNN-<slug>.md`. It MUST NOT carry a component name — a decision that turns out to
135
- reach a second component would otherwise need renaming, and the rename breaks every link to it.
136
- Filenames MUST obey the cross-OS naming rules in `structure-guide.md`.
137
-
138
- ## Status — and why `applied` exists
139
-
140
- `draft` → `accepted` → `applied`. A change of mind after that produces a **new** `DEC-`.
141
-
142
- | Status | Means |
143
- |---|---|
144
- | `draft` | Being argued. It changes nothing, and that is the point — proposing is cheap |
145
- | `accepted` | Ratified by the Product Owner. MAY still be corrected in place |
146
- | `applied` | The documents it governs now say it. **Frozen from here** |
147
- | `superseded` | Replaced. Names its replacement, and the replacement names it |
148
- | `rejected` | Seriously considered and turned down. A real status, and it MUST be used — it is what stops the same argument being had twice |
149
-
150
- **Applying is what freezes a decision, not accepting.** From `applied` onward nothing in the file MUST
151
- be touched — not the Decision, not the Cost, not a typo in the Why. Documents cite it, and editing it
152
- destroys the only evidence of what they were changed to match.
153
-
154
- Before that, an `accepted` `DEC-` MAY be corrected in place. Nothing has been built on it, so there is
155
- no divergent record to preserve, and **the correction is not recorded anywhere** — the file now reads
156
- correctly and git holds the change. Logging it would be a second home for a fact git already has, and a
157
- piece of document history that would save nobody.
158
-
159
- An agent MUST NOT accept its own `DEC-`. When work is blocked waiting on one, the block is reported,
160
- never resolved by self-approval.
161
-
162
- **The one exception is a delegation the owner made in person.** A `DEC-` of `type: mandate`, accepted by
163
- the owner with a name and a date and carrying an `expires`, lets `wdi-autopilot` accept decisions taken
164
- under it — `accepted_by: DEC-<mandate>`. What each of the three guards above protected — *a human made
165
- the call* — still holds: the human made one call, and it is on the record, dated, and ending. The mandate
166
- itself MUST NOT be accepted by delegation, and `mandate-accept` refuses one that is, one with no
167
- `expires`, and any decision dated after its mandate lapsed.
168
-
169
- ## Finding a decision
170
-
171
- `.control/generated/decisions.md` carries a flat table of every `DEC-` with its status and what it
172
- touches. It is generated, and MUST NOT be written by hand.
173
-
174
- Searching the memlog for decisions is **retired**. The memlog is a run log again the record of *why*
175
- while an artifact was written, and a source when writing a `DEC-`, never an index of them.
176
-
177
- ## Where decisions come from
178
-
179
- | Trigger | Route |
180
- |---|---|
181
- | A meeting reached one | `wdi-log` intent `meeting` writes the minutes; the decision still needs its own `DEC-` |
182
- | An open question was answered | `wdi-question` closes it; the answer becomes a `DEC-` when it binds |
183
- | `wdi-systematic-debugging` found a root cause in the design | A `DEC-`. It MUST NOT be absorbed as a code patch |
184
- | A planning assumption turned out to be void | `wdi-decision`, which wraps `bmad-correct-course`. The result is a `DEC-` of `type: course-correction` |
185
- | A ticket contradicts an `AD-N` | The ticket stops. This is the one mandatory case |
186
- | `wdi-reconcile` found two documents disagreeing with no clear winner | That is a decision, not drift |
187
- | `wdi-autopilot` took a decision with no home in any design document | A `DEC-`, accepted under the mandate. The ledger records every decision it took; only the homeless ones become a `DEC-` |
188
-
189
- Minutes MUST NOT be treated as a decision record. They say what was discussed; a `DEC-` says what was
190
- chosen and what it cost.
191
-
192
- **`SCP-` is retired.** A course correction is a decision, and it is a `DEC-` with
193
- `type: course-correction`. No second code names the same thing.
194
-
195
- ## Rules
196
-
197
- - A `DEC-` MUST be opened **before** the code that depends on it, never written afterwards to explain
198
- code that already exists.
199
- - The applying pass MUST NOT improvise. When an `accepted` `DEC-` does not say clearly what a document
200
- has to change, the decision is incomplete and is sent back.
201
- - Applying MUST NOT widen beyond what the decision says. A neighbouring paragraph that now looks wrong
202
- is a finding to report.
203
- - `applied-dec-touches` checks that every `applied` `DEC-` names a non-empty `touches`. It MUST NOT check that a decision
204
- serves an `FR` or `NFR` — *"the filter works like this"* serves none, and it is exactly the kind of
205
- decision this guide exists to keep.
206
- - `ADR-NNN` written inside a document frozen before 2026-08-18 is a **retired alias** for `DEC-NNN`
207
- with the same number. It MUST NOT be read as a second, missing record, and those documents MUST NOT
208
- be rewritten to change the prefix.
1
+ ---
2
+ status: Accepted
3
+ ---
4
+
5
+ # Decision Guide
6
+
7
+ **Loaded when:** opening, accepting, or applying a `DEC-`
8
+
9
+ A `DEC-` records a decision worth remembering. The old name was ADR — *Architecture Decision Record*
10
+ — and the word "Architecture" forced the wrong question at the moment of writing: *"is this
11
+ architectural?"*. That question throws away the decisions most worth keeping, the ones that sound
12
+ small: *"the filter works like this"*, *"this list is sorted that way"*.
13
+
14
+ ## A decision's first home is the document it governs
15
+
16
+ **Changing a document NEVER requires a `DEC-`.** A `DEC-` is not permission to edit, not a record that
17
+ an edit happened, and not a step between deciding and writing. Where the answer has a home in a design
18
+ document — an `FR` in the PRD, a rule in `business-rules.md`, a boundary in an SDD, a line in the brief —
19
+ **it is written there and nowhere else**, and there is nothing further to do.
20
+
21
+ A `DEC-` is what you write for a decision with **no such home**. That is the whole of its job, plus the
22
+ one mandatory case below.
23
+
24
+ | The answer is about | Where it goes |
25
+ |---|---|
26
+ | What the product promises, does, or forbids | The design document that carries it |
27
+ | How this repo builds — a convention, a stack choice | `.constitution/project/`, once code ratifies it |
28
+ | Accepting a risk, cutting a scope, choosing between two paths **that no document holds** | A `DEC-` |
29
+ | Contradicting an `AD-N` | A `DEC-`, mandatory |
30
+
31
+ This matters most **early**. At G1 a brief is still being formed and at G2 a PRD is still being written;
32
+ almost nothing there is homeless, so almost nothing there is a `DEC-`. Demanding one before the artifact
33
+ it would govern even exists is ceremony at the moment the project can least afford it.
34
+
35
+ ## Then one test decides whether to record it at all
36
+
37
+ > **If someone asks in three months why it is like this, is the answer readable from the code?**
38
+
39
+ Yes → it MUST NOT be recorded. No → it is recorded.
40
+
41
+ **A `DEC-` records a state, never an event.** It answers *why is it like this* for someone about to
42
+ change it — forward-looking, present tense. It is not a record that something changed, not a record that
43
+ a document used to say otherwise, and not a record that a review found a conflict. Those are document
44
+ history, and `corpus-guide.md` § The corpus is written in the present tense says they go nowhere.
45
+
46
+ The practical form of the test: **would this file save the next person from a mistake they were about to
47
+ make?** If the honest answer is *"no, but it explains what happened"*, there is no file.
48
+
49
+ **Recording is not mandatory, and a decision nobody recorded is normal rather than negligence.** It
50
+ MUST NOT be logged as debt, raised as a finding, or backfilled later from memory. Without this
51
+ sentence, "not mandatory" is read as "mandatory but allowed to be late".
52
+
53
+ **No case is mandatory any more, and one guard survives all of them.** Three rules used to demand a
54
+ `DEC-` file. Each was protecting something real, and in each the protection was the **stop**, never the
55
+ document:
56
+
57
+ | Was | Is now |
58
+ |---|---|
59
+ | Contradicting an `AD-N` demanded a `DEC-` | It **stops and reaches the owner**. Nobody narrows an invariant silently. The record is the edited `AD-N`, present tense, plus the commit — a `DEC-` only if the reason cannot be read from the spine |
60
+ | `risk_accepted: high` on a sensitive component demanded a risk-acceptance `DEC-` | `risk_accepted_by` names **a person and a date**, in `components.yaml` where the risk is set. `high-risk-named` checks it, and still resolves a `DEC-` id if one is given |
61
+ | Cancelling >30% of a spec's tickets demanded a `DEC-` | The spec is **re-cut** rather than patched. `specs.yaml` is the record |
62
+
63
+ What each of those bought was that a human made the call. That is preserved. What each of them also
64
+ cost — a file, written at the moment the work was blocked — is not.
65
+
66
+ An `AD-N` change stopping is the one that MUST NOT be softened further: it is what stops an architecture
67
+ being narrowed twice by two passes neither of which thought it was doing anything.
68
+
69
+ **Nothing else stops the work at all.** Where a change contradicts an `FR`, a `UC`, a business rule,
70
+ or a document's wording, the agent states the consequence once and the owner decides. If they proceed the
71
+ documents are edited to match — `delivery-flow-guide.md` § When something settled has to change owns the
72
+ matrix, and the survey behind that warning is spent the moment the owner answers.
73
+
74
+ ## `AD-N` and `DEC-NNN` are not the same thing
75
+
76
+ | | `AD-N` | `DEC-NNN` |
77
+ |---|---|---|
78
+ | Is | A **living rule** in `ARCHITECTURE-SPINE.md` | A **decision event** |
79
+ | Lives in | The spine | `.control/decisions/` |
80
+ | Changes by | Being edited in place | Never, once `applied` — a new `DEC-` supersedes it |
81
+ | Answers | What is forbidden from now on | What was chosen, and what it cost |
82
+
83
+ An `AD-N` usually has a `DEC-` behind it. Neither MUST be written in place of the other, and one MUST
84
+ NOT be converted into the other.
85
+
86
+ ## Shape — three sections, and no more required
87
+
88
+ | Section | States | Required |
89
+ |---|---|---|
90
+ | **Decision** | One sentence, present tense, quotable into a rule | always |
91
+ | **Why** | The context that forced it, in a few lines | always |
92
+ | **Cost** | What becomes harder. A decision with only benefits was not thought through | always |
93
+ | Alternatives | What else was considered, and why each lost | see below |
94
+ | Reversal trigger | The observable condition that makes revisiting this correct | see below |
95
+ | Trace | Where it came from, and what it landed in | see below |
96
+
97
+ The last three MUST be present when the decision reaches a Product Component whose `risk_accepted`
98
+ is `low` in `components.yaml`. Everywhere else they are optional, and an empty one MUST be dropped
99
+ rather than left as a heading with nothing under it.
100
+
101
+ **A `DEC-` MUST NOT hold an open question.** Those belong to `.control/questions/`.
102
+
103
+ **One page, and that is a bound, not a target.** A `DEC-` records **what was chosen and what it cost** —
104
+ not how the answer was reached, not the transcript of the reasoning, not every reading of every clause
105
+ that was weighed on the way. One real decision reached **124 lines** to record that one invariant does not
106
+ reach one artifact; the sentence that mattered was one line and the cost was two.
107
+
108
+ Three things MUST NOT appear in a `DEC-`, and each of them is the derivation leaking in:
109
+
110
+ - The search that found the answer — which files were grepped, which clause was read first.
111
+ - A meta-note about the decision itself: whether it should have been a `DEC-` at all, whether some other
112
+ mechanism was considered and rejected. If that reasoning matters it is the **Why**; usually it does not
113
+ matter and it is nothing.
114
+ - A correction of an earlier draft of the same decision. Drafts are git's.
115
+
116
+ `Alternatives` is the one place a rejected option belongs, it is a **line each**, and it is required only
117
+ where the section table above says so.
118
+
119
+ ## Frontmatter
120
+
121
+ | Field | Rule |
122
+ |---|---|
123
+ | `id` | `DEC-NNN`, allocated from `.control/registry/decisions.yaml`, globally. MUST NOT restart per component or per release |
124
+ | `status` | `draft` · `accepted` · `applied` · `superseded` · `rejected` |
125
+ | `touches` | Empty at `draft`. Filled **when the decision is applied**, with the files it actually changed |
126
+ | `type` | Free text, and optional. Written when it is useful — `risk-acceptance`, `course-correction`. One value has a fixed shape: `mandate`, opened by `wdi-autopilot`, whose parameters live under `mandate:` on the registry row and nowhere else |
127
+ | `accepted_by` | Who raised it to `accepted`. A person and a date, the way `risk_accepted_by` is written — or the `DEC-` of a `type: mandate` that delegated the acceptance. `mandate-accept` checks the second form |
128
+ | `supersedes` · `superseded_by` | Both sides of a supersession MUST exist |
129
+
130
+ **There is no `layer:` and no `component:`.** Both were classifications demanded before anything was
131
+ known, and both were guessed as often as they were derived. `touches:` replaces them, and it is filled
132
+ from what happened rather than from what was predicted.
133
+
134
+ The filename is `DEC-NNN-<slug>.md`. It MUST NOT carry a component name — a decision that turns out to
135
+ reach a second component would otherwise need renaming, and the rename breaks every link to it.
136
+ Filenames MUST obey the cross-OS naming rules in `structure-guide.md`.
137
+
138
+ ## Status — and why `applied` exists
139
+
140
+ `draft` → `accepted` → `applied`. A change of mind after that produces a **new** `DEC-`.
141
+
142
+ | Status | Means |
143
+ |---|---|
144
+ | `draft` | Being argued. It changes nothing, and that is the point — proposing is cheap |
145
+ | `accepted` | Ratified by the Product Owner. MAY still be corrected in place |
146
+ | `applied` | The documents it governs now say it. **Frozen from here** |
147
+ | `superseded` | Replaced. Names its replacement, and the replacement names it |
148
+ | `rejected` | Seriously considered and turned down. A real status, and it MUST be used — it is what stops the same argument being had twice |
149
+
150
+ **Applying is what freezes a decision, not accepting.** From `applied` onward nothing in the file MUST
151
+ be touched — not the Decision, not the Cost, not a typo in the Why. Documents cite it, and editing it
152
+ destroys the only evidence of what they were changed to match.
153
+
154
+ Before that, an `accepted` `DEC-` MAY be corrected in place. Nothing has been built on it, so there is
155
+ no divergent record to preserve, and **the correction is not recorded anywhere** — the file now reads
156
+ correctly and git holds the change. Logging it would be a second home for a fact git already has, and a
157
+ piece of document history that would save nobody.
158
+
159
+ An agent MUST NOT accept its own `DEC-`. When work is blocked waiting on one, the block is reported,
160
+ never resolved by self-approval.
161
+
162
+ **The one exception is a delegation the owner made in person.** A `DEC-` of `type: mandate`, accepted by
163
+ the owner with a name and a date and carrying an `expires`, lets `wdi-autopilot` accept decisions taken
164
+ under it — `accepted_by: DEC-<mandate>`. What each of the three guards above protected — *a human made
165
+ the call* — still holds: the human made one call, and it is on the record, dated, and ending. The mandate
166
+ itself MUST NOT be accepted by delegation, and `mandate-accept` refuses one that is, one with no
167
+ `expires`, and any decision dated after its mandate had ended.
168
+
169
+ **A mandate ends twice over, and the earlier end is the one that counts:** its `expires` passes, or it
170
+ is superseded. Superseding a mandate revokes the delegation from the day of the decision that replaced
171
+ it which is why a superseded mandate that delegated anything MUST carry `superseded_by`, and why
172
+ recording that pointer is the one edit an `applied` decision allows. What supersession does NOT do is
173
+ reach backwards: the decisions taken under the mandate while it stood remain accepted, their
174
+ `accepted_by` still names it, and the ledger it owes is still owed. A retired mandate is a mandate that
175
+ ended, never one that was never granted.
176
+
177
+ ## Finding a decision
178
+
179
+ `.control/generated/decisions.md` carries a flat table of every `DEC-` with its status and what it
180
+ touches. It is generated, and MUST NOT be written by hand.
181
+
182
+ Searching the memlog for decisions is **retired**. The memlog is a run log again the record of *why*
183
+ while an artifact was written, and a source when writing a `DEC-`, never an index of them.
184
+
185
+ ## Where decisions come from
186
+
187
+ | Trigger | Route |
188
+ |---|---|
189
+ | A meeting reached one | `wdi-log` intent `meeting` writes the minutes; the decision still needs its own `DEC-` |
190
+ | An open question was answered | `wdi-question` closes it; the answer becomes a `DEC-` when it binds |
191
+ | `wdi-systematic-debugging` found a root cause in the design | A `DEC-`. It MUST NOT be absorbed as a code patch |
192
+ | A planning assumption turned out to be void | `wdi-decision`, which wraps `bmad-correct-course`. The result is a `DEC-` of `type: course-correction` |
193
+ | A ticket contradicts an `AD-N` | The ticket stops. This is the one mandatory case |
194
+ | `wdi-reconcile` found two documents disagreeing with no clear winner | That is a decision, not drift |
195
+ | `wdi-autopilot` took a decision with no home in any design document | A `DEC-`, accepted under the mandate. The ledger records every decision it took; only the homeless ones become a `DEC-` |
196
+
197
+ Minutes MUST NOT be treated as a decision record. They say what was discussed; a `DEC-` says what was
198
+ chosen and what it cost.
199
+
200
+ **`SCP-` is retired.** A course correction is a decision, and it is a `DEC-` with
201
+ `type: course-correction`. No second code names the same thing.
202
+
203
+ ## Rules
204
+
205
+ - A `DEC-` MUST be opened **before** the code that depends on it, never written afterwards to explain
206
+ code that already exists.
207
+ - The applying pass MUST NOT improvise. When an `accepted` `DEC-` does not say clearly what a document
208
+ has to change, the decision is incomplete and is sent back.
209
+ - Applying MUST NOT widen beyond what the decision says. A neighbouring paragraph that now looks wrong
210
+ is a finding to report.
211
+ - `applied-dec-touches` checks that every `applied` `DEC-` names a non-empty `touches`. It MUST NOT check that a decision
212
+ serves an `FR` or `NFR` — *"the filter works like this"* serves none, and it is exactly the kind of
213
+ decision this guide exists to keep.
214
+ - `ADR-NNN` written inside a document frozen before 2026-08-18 is a **retired alias** for `DEC-NNN`
215
+ with the same number. It MUST NOT be read as a second, missing record, and those documents MUST NOT
216
+ be rewritten to change the prefix.