wdi-method 0.5.4 → 0.5.9

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.
package/bin/wdi-method.js CHANGED
@@ -61,6 +61,15 @@ const AGENT_LABELS = {
61
61
  const BMAD_INSTALL = `npx bmad-method install`;
62
62
  const REPO_URL = "https://github.com/wiradigitalid/wdi-method";
63
63
  const HELP_SKILL = "wdi-help";
64
+ const INIT_SKILL = "wdi-init";
65
+ // The room's readers file is seeded as a skeleton and is useless until a product writes it. The
66
+ // flag is the skeleton's own declaration, so this reads the same thing the engine does rather than
67
+ // guessing from the file's size or its age.
68
+ function readersAreSkeleton(target) {
69
+ const file = path.join(target, ".constitution", "project", "inventory-readers.py");
70
+ if (!fs.existsSync(file)) return false;
71
+ return /^SKELETON\s*=\s*True\b/m.test(fs.readFileSync(file, "utf8"));
72
+ }
64
73
  const BMAD_REPO = "https://github.com/bmad-code-org/BMAD-METHOD";
65
74
  const WDI_REPO = "https://github.com/wiradigitalid/wdi-method";
66
75
 
@@ -800,6 +809,14 @@ function printSummary(target, agents, { first, was, written, skipped, skills, to
800
809
  }
801
810
  summaryLine("agents", agents.join(", ") || "none");
802
811
  console.log("");
812
+ // The readers are the one seeded file that does nothing until somebody writes it, and its
813
+ // silence is expensive: inventory.py refuses to run and the reason is a folder deep. One line
814
+ // here, only while it is still the skeleton, so it stops appearing once it is done.
815
+ if (readersAreSkeleton(target)) {
816
+ summaryLine("todo", `${DIM}.constitution/project/inventory-readers.py${RESET} is a skeleton — ` +
817
+ `run the ${INIT_SKILL} skill, intent ${DIM}readers${RESET}, ` +
818
+ `to write it for this repo's stack`);
819
+ }
803
820
  summaryLine("next", `invoke the ${HELP_SKILL} skill and ask what to do`);
804
821
  summaryLine("", REPO_URL);
805
822
  console.log(`${DIM}${"─".repeat(62)}${RESET}`);
@@ -68,6 +68,8 @@ used to reject a change.
68
68
  ## `scripts/`
69
69
 
70
70
  `validate.py` — registry gates and the `.control/generated/` generator. `timeline.py` — the time
71
- dimension from git history. `inventory.py` — derives the three inventories from code and reports the
72
- difference against the plan; it MUST NOT patch either side into agreement. All three run through
73
- `uv run`.
71
+ dimension from git history. `inventory.py` — compares the three inventories against the code
72
+ and reports the difference; it MUST NOT patch either side into agreement. It reads no code itself:
73
+ the patterns live in `../project/inventory-readers.py`, because comparing is generic and reading a
74
+ stack is not. That file ships as a skeleton and `wdi-init` intent `readers` writes it for the repo
75
+ in front of it. All three run through `uv run`.
@@ -1,209 +1,209 @@
1
- ---
2
- status: Accepted
3
- ---
4
-
5
- # Architecture Guide
6
-
7
- **Loaded when:** writing or changing the architecture spine, an `AD-N`, or the C4 set
8
-
9
- The spine holds the invariants that stop separately built components from diverging. It is not a design
10
- document — one component's design is its `SDD-<pc>.md`. If a statement only affects one component, it does
11
- not belong here.
12
-
13
- The spine, the C4 set, `cross-cutting.md`, and the three inventories are all **G3 Blueprint** output, written
14
- by `wdi-blueprint` intent `platform`. All of them exist at every `mode`, including `catalog`: they belong to
15
- the blueprint, and the blueprint is not touched by the depth knob.
16
-
17
- ## Home
18
-
19
- `.how/_platform/ARCHITECTURE-SPINE.md` — a file directly in `_platform/`. The `architecture/` sub-folder is
20
- **repealed**; one file does not earn a folder, and the extra level made the spine harder to find than the
21
- things it constrains.
22
-
23
- ## Invariants only
24
-
25
- An entry earns a place when breaking it in one component would break another. Everything else is a **seed**:
26
- useful as a starting point, not binding.
27
-
28
- | Kind of statement | Spine? |
29
- |---|---|
30
- | "Every service authenticates through the same token format" | Yes — invariant |
31
- | "Money is stored as integer minor units, never float" | Yes — invariant |
32
- | "We use MariaDB 11 and Go 1.23" | No — seed. It informs, it does not forbid |
33
- | "The repo is laid out as `src/` and `web/`" | No — seed, and `structure-codebase.md` describes it |
34
- | "The member portal caches its dashboard for 60 seconds" | No — one component's design |
35
-
36
- Stack, tree shape, and data shapes are seeds and MUST be marked as such. Writing them as contracts makes the
37
- spine wrong the first time anything is upgraded, and a spine that is wrong in a visible place stops being
38
- read in the places where it is right.
39
-
40
- ## Every `AD-N` carries three things
41
-
42
- | Field | States |
43
- |---|---|
44
- | **Binds** | Which components or containers this holds for. "All" is a valid answer and MUST be written, not left blank |
45
- | **Prevents** | The concrete failure this exists to stop. Written as the thing going wrong, not as a principle |
46
- | **Rule** | One sentence, quotable, checkable. If a reviewer cannot tell whether code obeys it, it is not a rule yet |
47
-
48
- An `AD-N` with no **Prevents** is a preference. Preferences belong in
49
- `.constitution/project/codebase-conventions-guide.md`, where nothing has to justify itself.
50
-
51
- ## The spine stops being touched every story
52
-
53
- This is the change that ends the tax that was being paid before the information existed.
54
-
55
- - **The spine holds invariants and nothing else.** It changes only when an `AD-N` is born or reversed, and
56
- both are decision events. For a mid-sized product: once at the start, around 6–10 `AD-N`, then almost
57
- never.
58
- - **A story MUST NOT touch the spine.** A story that contradicts an `AD-N` **stops** and opens a `DEC-` —
59
- the one case where recording a decision is still mandatory.
60
- - **Editing an `AD-N` MUST NOT quietly reverse it.** A reversal is a decision, and it goes through
61
- `wdi-decision` first.
62
- - **An inventory is not the spine.** The three inventories are living registers, derived from code once code
63
- exists. A plan-versus-reality difference is a validator finding, not a spine amendment.
64
-
65
- ## `AD-N` versus `DEC-NNN`
66
-
67
- `AD-N` is a **living rule**, edited in place as understanding improves. `DEC-NNN` is a **decision event**,
68
- never edited once `applied`, only superseded.
69
-
70
- An `AD-N` usually has a `DEC-` behind it: the invariant is what people obey, the decision record is why it
71
- exists and what it cost. The full comparison is in `decision-guide.md` and MUST NOT be restated here.
72
-
73
- The spine MUST NOT name a decision's alternatives or its cost. Those live in the `DEC-` behind it; repeating
74
- them here creates a second version that will drift.
75
-
76
- ## The C4 set, and what else a run produces
77
-
78
- `bmad-architecture` runs at **initiative** altitude for the spine. Anything else the run produces at feature
79
- altitude — a deck, a fuller solution document — is a **rendering**, not the spine, and the two MUST NOT be
80
- confused:
81
-
82
- | Rendering | Has a slot | What follows |
83
- |---|---|---|
84
- | The **C4 set** — L1, L2, and one L3 per `built: true` container **holding more than one Product Component** | Yes: `.how/_platform/c4-l1-system-context.md` · `c4-l2-containers.md` · `c4-l3-<container>.md` | `wdi-blueprint` lands it, amending rather than overwriting. Once landed it is corpus, and `c4-l2-containers.md` **owns** the container list. The next section owns what a container *is* |
85
- | Deck, solution document, anything else | No | Stays in the run folder, cited by path, never promoted |
86
-
87
- **Placement is what makes a C4 binding, and only for what its level owns.** Before placement it is one run's
88
- drawing; after, it is the corpus's description of the system. The spine still wins on any invariant, because
89
- the C4 set describes and the spine forbids. A C4 file MUST NOT be used to justify a rule; a rule that matters
90
- belongs in an `AD-N`.
91
-
92
- The C4 set is **living**. It is amended when a container is added or changed — it MUST NOT be regenerated from
93
- scratch, which would drop the annotations three waves of amendment put there.
94
-
95
- ## What counts as a container, and what does not
96
-
97
- Two questions, and **both MUST be yes**:
98
-
99
- 1. **Does it run its own code or store its own data?** Its own process, its own browser page, its own
100
- engine — not a folder inside another container's process.
101
- 2. **Can it be replaced without rebuilding another container?** It has its own build or publish path.
102
-
103
- **Shipping two containers in one release does not merge them.** One atomic deploy swapping a binary and
104
- two browser bundles together is a deployment choice, and a deployment choice MUST NOT be read as an
105
- architectural one. The failure this guards against is the quiet one: a browser bundle with its own
106
- JavaScript filed as "static assets" of the web server, carrying real behaviour with no row, no owner, and
107
- no NFR. Handwritten HTML that runs **no** code of its own is content the web server delivers; the moment
108
- it carries a script, it passes question 1.
109
-
110
- ### `built` — the one boolean, and its four consequences
111
-
112
- A container is inside the boundary whether or not we wrote it. `built:` records which:
113
-
114
- | | `built: true` | `built: false` |
115
- |---|---|---|
116
- | An L3 | Yes, where it holds more than one PC | **Never.** No box inside it is ours to draw |
117
- | An `LC` naming it as `container` | Yes | **Never** |
118
- | A heading in `structure-codebase.md` | **Required** | **Never** — no code of ours lives there |
119
- | Listed in a PC's `containers:` | Yes | **Never** — see the matrix below |
120
-
121
- V25 checks all four. This is what makes the class settled rather than re-argued: a database and a web
122
- server are containers, they carry NFRs, and they still produce no design artifact of ours.
123
-
124
- **What IS ours about a `built: false` container MUST have a home outside the C4 set** — its configuration
125
- and any invariant it enforces belong in an `AD-N` or in `cross-cutting.md`. A rule surviving only as a
126
- note beside a C4 box is a rule nobody can find.
127
-
128
- ### External system — outside the boundary
129
-
130
- **The line is who deploys the runtime.** We deploy it, whoever wrote it → container. Someone else
131
- runs it and we call it or configure it through their control plane → external system.
132
-
133
- An external system appears at **C4 L1 and nowhere else**. It MUST NOT be registered in `containers`,
134
- MUST NOT be an `LC`'s `container`, and MUST NOT get a heading in the codebase map. What the product
135
- depends on it for lives in `cross-cutting.md` or an integration contract.
136
-
137
- ### The PC × container matrix
138
-
139
- A PC and a container cross, so neither list implies the other — and the crossing is what a builder needs
140
- first: *which container does this promise live in, and is it more than one?*
141
-
142
- - The **SSOT is each PC's `containers:`** in `components.yaml`; C4 L2 renders it, and V25 fails when the
143
- two disagree.
144
- - Complete at **G3** for every PC. It is blueprint content, so `mode` does not touch it.
145
- - A PC MUST list every `built: true` container it lives in. Listing only the main one is the error the
146
- matrix exists to catch.
147
-
148
- ### Which C4 files exist, and when
149
-
150
- `artifact-map.md` owns the schedule: all three at **G3**, at every `mode`. Two conditions belong here.
151
-
152
- **L2 MUST be complete** — every container, plus the matrix. **L3 exists once per `built: true` container
153
- holding more than one PC**; a one-PC container needs none because the matrix already places it.
154
-
155
- **Not one of the three waits for a wave.** Which container a PC lives in cannot be discovered by a wave,
156
- because a wave picks its stories from that answer — a wave forced to invent it answers a G3 question with
157
- a fraction of G3's information.
158
-
159
- ## Cross-cutting
160
-
161
- `.how/_platform/cross-cutting.md` holds what is defined once for the whole product, the error envelope first
162
- among them. Every contract references it rather than restating it.
163
-
164
- A rule that belongs there MUST NOT also be written as an `AD-N` unless breaking it in one component breaks
165
- another. One fact, one home.
166
-
167
- **The platform MAY own things, and owning one costs a row here.** `_platform` is a legitimate owner in every
168
- position that asks which component owns something — a domain entity through `platform_owns`, an inventory row,
169
- an `LC`. Whatever it owns MUST be described under `## Platform-owned` in this file: what it is, its kind, why
170
- no component's promise explains it, who touches it, and the shape every toucher obeys. V21 checks that second
171
- half, because a platform that owns something without documenting it has taken ownership without taking
172
- responsibility.
173
-
174
- Four kinds qualify today — data, endpoint, job, screen — and the list is open. What is **not** open is the
175
- test, and `corpus-guide.md` owns it: no single component's promise explains it, **and** more than one
176
- component depends on it. Failing either half, it belongs to a component.
177
-
178
- `_platform` is **not** a Product Component — `corpus-guide.md` owns that distinction and the test for when an
179
- entity legitimately belongs here. It has no `mode` and no `risk_accepted`: the documents in this folder exist
180
- at every mode.
181
-
182
- ## Binding order
183
-
184
- Spine first, then the SDD, then `bmad-spec`. An `SDD-<pc>.md` written before the spine will be rewritten,
185
- because the constraints it was supposed to inherit did not exist yet. A `SPEC.md` written before the SDD has
186
- nothing to project. `bmad-spec` MUST keep `AD` ids stable so downstream artifacts can cite them.
187
-
188
- ## Inheritance downward
189
-
190
- From `mode: guarded` up, every `AD-N` that reaches a component MUST appear in that component's SDD under
191
- **Inherited Constraints**, quoted rather than paraphrased. Below `guarded` the quoting is not written, and the
192
- spine still binds — an invariant does not stop holding because a document is thin.
193
-
194
- ## Review is manual here
195
-
196
- `bmad-architecture` excludes the spine from `doc_standards` deliberately — the spine is terse by design and
197
- prose polish softens rules that need to stay rigid. Two consequences:
198
-
199
- - This guide is installed as `persistent_facts`, not `doc_standards`.
200
- - `bmad-review` over the spine MUST be invoked through `wdi-review` before G3 closes.
201
-
202
- ## Rules
203
-
204
- - The spine MUST stay short enough to be read in one sitting. An `AD-N` nobody remembers is not an invariant,
205
- it is a document.
206
- - An `AD-N` MUST NOT be added because something feels important. The test is only ever: does breaking this in
207
- one place break another?
208
- - Structure statements MUST be marked as seeds, and MUST NOT be checked as if they were rules.
209
- - Memlog goes to `.control/memlog/spine.md` via `--path`, never beside the spine.
1
+ ---
2
+ status: Accepted
3
+ ---
4
+
5
+ # Architecture Guide
6
+
7
+ **Loaded when:** writing or changing the architecture spine, an `AD-N`, or the C4 set
8
+
9
+ The spine holds the invariants that stop separately built components from diverging. It is not a design
10
+ document — one component's design is its `SDD-<pc>.md`. If a statement only affects one component, it does
11
+ not belong here.
12
+
13
+ The spine, the C4 set, `cross-cutting.md`, and the three inventories are all **G3 Blueprint** output, written
14
+ by `wdi-blueprint` intent `platform`. All of them exist at every `mode`, including `catalog`: they belong to
15
+ the blueprint, and the blueprint is not touched by the depth knob.
16
+
17
+ ## Home
18
+
19
+ `.how/_platform/ARCHITECTURE-SPINE.md` — a file directly in `_platform/`. The `architecture/` sub-folder is
20
+ **repealed**; one file does not earn a folder, and the extra level made the spine harder to find than the
21
+ things it constrains.
22
+
23
+ ## Invariants only
24
+
25
+ An entry earns a place when breaking it in one component would break another. Everything else is a **seed**:
26
+ useful as a starting point, not binding.
27
+
28
+ | Kind of statement | Spine? |
29
+ |---|---|
30
+ | "Every service authenticates through the same token format" | Yes — invariant |
31
+ | "Money is stored as integer minor units, never float" | Yes — invariant |
32
+ | "We use version 11 of our database and version 1.23 of our language" | No — seed. It informs, it does not forbid |
33
+ | "The repo is laid out as one folder per deployable" | No — seed, and `structure-codebase.md` describes it |
34
+ | "The member portal caches its dashboard for 60 seconds" | No — one component's design |
35
+
36
+ Stack, tree shape, and data shapes are seeds and MUST be marked as such. Writing them as contracts makes the
37
+ spine wrong the first time anything is upgraded, and a spine that is wrong in a visible place stops being
38
+ read in the places where it is right.
39
+
40
+ ## Every `AD-N` carries three things
41
+
42
+ | Field | States |
43
+ |---|---|
44
+ | **Binds** | Which components or containers this holds for. "All" is a valid answer and MUST be written, not left blank |
45
+ | **Prevents** | The concrete failure this exists to stop. Written as the thing going wrong, not as a principle |
46
+ | **Rule** | One sentence, quotable, checkable. If a reviewer cannot tell whether code obeys it, it is not a rule yet |
47
+
48
+ An `AD-N` with no **Prevents** is a preference. Preferences belong in
49
+ `.constitution/project/codebase-conventions-guide.md`, where nothing has to justify itself.
50
+
51
+ ## The spine stops being touched every story
52
+
53
+ This is the change that ends the tax that was being paid before the information existed.
54
+
55
+ - **The spine holds invariants and nothing else.** It changes only when an `AD-N` is born or reversed, and
56
+ both are decision events. For a mid-sized product: once at the start, around 6–10 `AD-N`, then almost
57
+ never.
58
+ - **A story MUST NOT touch the spine.** A story that contradicts an `AD-N` **stops** and opens a `DEC-` —
59
+ the one case where recording a decision is still mandatory.
60
+ - **Editing an `AD-N` MUST NOT quietly reverse it.** A reversal is a decision, and it goes through
61
+ `wdi-decision` first.
62
+ - **An inventory is not the spine.** The three inventories are living registers, derived from code once code
63
+ exists. A plan-versus-reality difference is a validator finding, not a spine amendment.
64
+
65
+ ## `AD-N` versus `DEC-NNN`
66
+
67
+ `AD-N` is a **living rule**, edited in place as understanding improves. `DEC-NNN` is a **decision event**,
68
+ never edited once `applied`, only superseded.
69
+
70
+ An `AD-N` usually has a `DEC-` behind it: the invariant is what people obey, the decision record is why it
71
+ exists and what it cost. The full comparison is in `decision-guide.md` and MUST NOT be restated here.
72
+
73
+ The spine MUST NOT name a decision's alternatives or its cost. Those live in the `DEC-` behind it; repeating
74
+ them here creates a second version that will drift.
75
+
76
+ ## The C4 set, and what else a run produces
77
+
78
+ `bmad-architecture` runs at **initiative** altitude for the spine. Anything else the run produces at feature
79
+ altitude — a deck, a fuller solution document — is a **rendering**, not the spine, and the two MUST NOT be
80
+ confused:
81
+
82
+ | Rendering | Has a slot | What follows |
83
+ |---|---|---|
84
+ | The **C4 set** — L1, L2, and one L3 per `built: true` container **holding more than one Product Component** | Yes: `.how/_platform/c4-l1-system-context.md` · `c4-l2-containers.md` · `c4-l3-<container>.md` | `wdi-blueprint` lands it, amending rather than overwriting. Once landed it is corpus, and `c4-l2-containers.md` **owns** the container list. The next section owns what a container *is* |
85
+ | Deck, solution document, anything else | No | Stays in the run folder, cited by path, never promoted |
86
+
87
+ **Placement is what makes a C4 binding, and only for what its level owns.** Before placement it is one run's
88
+ drawing; after, it is the corpus's description of the system. The spine still wins on any invariant, because
89
+ the C4 set describes and the spine forbids. A C4 file MUST NOT be used to justify a rule; a rule that matters
90
+ belongs in an `AD-N`.
91
+
92
+ The C4 set is **living**. It is amended when a container is added or changed — it MUST NOT be regenerated from
93
+ scratch, which would drop the annotations three waves of amendment put there.
94
+
95
+ ## What counts as a container, and what does not
96
+
97
+ Two questions, and **both MUST be yes**:
98
+
99
+ 1. **Does it run its own code or store its own data?** Its own process, its own browser page, its own
100
+ engine — not a folder inside another container's process.
101
+ 2. **Can it be replaced without rebuilding another container?** It has its own build or publish path.
102
+
103
+ **Shipping two containers in one release does not merge them.** One atomic deploy swapping a binary and
104
+ two browser bundles together is a deployment choice, and a deployment choice MUST NOT be read as an
105
+ architectural one. The failure this guards against is the quiet one: a browser bundle with its own
106
+ JavaScript filed as "static assets" of the web server, carrying real behaviour with no row, no owner, and
107
+ no NFR. Handwritten HTML that runs **no** code of its own is content the web server delivers; the moment
108
+ it carries a script, it passes question 1.
109
+
110
+ ### `built` — the one boolean, and its four consequences
111
+
112
+ A container is inside the boundary whether or not we wrote it. `built:` records which:
113
+
114
+ | | `built: true` | `built: false` |
115
+ |---|---|---|
116
+ | An L3 | Yes, where it holds more than one PC | **Never.** No box inside it is ours to draw |
117
+ | An `LC` naming it as `container` | Yes | **Never** |
118
+ | A heading in `structure-codebase.md` | **Required** | **Never** — no code of ours lives there |
119
+ | Listed in a PC's `containers:` | Yes | **Never** — see the matrix below |
120
+
121
+ V25 checks all four. This is what makes the class settled rather than re-argued: a database and a web
122
+ server are containers, they carry NFRs, and they still produce no design artifact of ours.
123
+
124
+ **What IS ours about a `built: false` container MUST have a home outside the C4 set** — its configuration
125
+ and any invariant it enforces belong in an `AD-N` or in `cross-cutting.md`. A rule surviving only as a
126
+ note beside a C4 box is a rule nobody can find.
127
+
128
+ ### External system — outside the boundary
129
+
130
+ **The line is who deploys the runtime.** We deploy it, whoever wrote it → container. Someone else
131
+ runs it and we call it or configure it through their control plane → external system.
132
+
133
+ An external system appears at **C4 L1 and nowhere else**. It MUST NOT be registered in `containers`,
134
+ MUST NOT be an `LC`'s `container`, and MUST NOT get a heading in the codebase map. What the product
135
+ depends on it for lives in `cross-cutting.md` or an integration contract.
136
+
137
+ ### The PC × container matrix
138
+
139
+ A PC and a container cross, so neither list implies the other — and the crossing is what a builder needs
140
+ first: *which container does this promise live in, and is it more than one?*
141
+
142
+ - The **SSOT is each PC's `containers:`** in `components.yaml`; C4 L2 renders it, and V25 fails when the
143
+ two disagree.
144
+ - Complete at **G3** for every PC. It is blueprint content, so `mode` does not touch it.
145
+ - A PC MUST list every `built: true` container it lives in. Listing only the main one is the error the
146
+ matrix exists to catch.
147
+
148
+ ### Which C4 files exist, and when
149
+
150
+ `artifact-map.md` owns the schedule: all three at **G3**, at every `mode`. Two conditions belong here.
151
+
152
+ **L2 MUST be complete** — every container, plus the matrix. **L3 exists once per `built: true` container
153
+ holding more than one PC**; a one-PC container needs none because the matrix already places it.
154
+
155
+ **Not one of the three waits for a wave.** Which container a PC lives in cannot be discovered by a wave,
156
+ because a wave picks its stories from that answer — a wave forced to invent it answers a G3 question with
157
+ a fraction of G3's information.
158
+
159
+ ## Cross-cutting
160
+
161
+ `.how/_platform/cross-cutting.md` holds what is defined once for the whole product, the error envelope first
162
+ among them. Every contract references it rather than restating it.
163
+
164
+ A rule that belongs there MUST NOT also be written as an `AD-N` unless breaking it in one component breaks
165
+ another. One fact, one home.
166
+
167
+ **The platform MAY own things, and owning one costs a row here.** `_platform` is a legitimate owner in every
168
+ position that asks which component owns something — a domain entity through `platform_owns`, an inventory row,
169
+ an `LC`. Whatever it owns MUST be described under `## Platform-owned` in this file: what it is, its kind, why
170
+ no component's promise explains it, who touches it, and the shape every toucher obeys. V21 checks that second
171
+ half, because a platform that owns something without documenting it has taken ownership without taking
172
+ responsibility.
173
+
174
+ Four kinds qualify today — data, endpoint, job, screen — and the list is open. What is **not** open is the
175
+ test, and `corpus-guide.md` owns it: no single component's promise explains it, **and** more than one
176
+ component depends on it. Failing either half, it belongs to a component.
177
+
178
+ `_platform` is **not** a Product Component — `corpus-guide.md` owns that distinction and the test for when an
179
+ entity legitimately belongs here. It has no `mode` and no `risk_accepted`: the documents in this folder exist
180
+ at every mode.
181
+
182
+ ## Binding order
183
+
184
+ Spine first, then the SDD, then `bmad-spec`. An `SDD-<pc>.md` written before the spine will be rewritten,
185
+ because the constraints it was supposed to inherit did not exist yet. A `SPEC.md` written before the SDD has
186
+ nothing to project. `bmad-spec` MUST keep `AD` ids stable so downstream artifacts can cite them.
187
+
188
+ ## Inheritance downward
189
+
190
+ From `mode: guarded` up, every `AD-N` that reaches a component MUST appear in that component's SDD under
191
+ **Inherited Constraints**, quoted rather than paraphrased. Below `guarded` the quoting is not written, and the
192
+ spine still binds — an invariant does not stop holding because a document is thin.
193
+
194
+ ## Review is manual here
195
+
196
+ `bmad-architecture` excludes the spine from `doc_standards` deliberately — the spine is terse by design and
197
+ prose polish softens rules that need to stay rigid. Two consequences:
198
+
199
+ - This guide is installed as `persistent_facts`, not `doc_standards`.
200
+ - `bmad-review` over the spine MUST be invoked through `wdi-review` before G3 closes.
201
+
202
+ ## Rules
203
+
204
+ - The spine MUST stay short enough to be read in one sitting. An `AD-N` nobody remembers is not an invariant,
205
+ it is a document.
206
+ - An `AD-N` MUST NOT be added because something feels important. The test is only ever: does breaking this in
207
+ one place break another?
208
+ - Structure statements MUST be marked as seeds, and MUST NOT be checked as if they were rules.
209
+ - Memlog goes to `.control/memlog/spine.md` via `--path`, never beside the spine.
@@ -159,11 +159,11 @@ outbound channel) · **endpoint** (`/health`, `robots.txt` — plumbing no `FR`
159
159
  (none yet).
160
160
 
161
161
  Failing either half, it belongs to a Product Component — and the component is found by asking which `FR`
162
- would have to be withdrawn for the entity to stop being needed. Two examples of the trap, both real:
162
+ would have to be withdrawn for the entity to stop being needed. Two examples of the trap:
163
163
 
164
164
  | Entity | Looks platform-shaped | Actually |
165
165
  |---|---|---|
166
- | `member_activity_events` | member-wide telemetry, several components write it | **`referral`** — `FR-65` promises a Sponsor sees the login and click counts of their downline. One `FR` explains it |
166
+ | `activity_events` | product-wide telemetry, several components write it | **one component** an `FR` promises somebody can SEE those counts, and withdrawing it is what would make the table unnecessary |
167
167
  | `email_logs` | one component sends first | **`_platform`** — it is the trace of one outbound channel that order notifications and password recovery both use, and neither promise is why the channel exists |
168
168
 
169
169
  **One guard, and it is what stops this becoming a drawer:** everything `_platform` owns — in any position —
@@ -159,8 +159,9 @@ An unlabelled claim is read as verified. That is why the labels are mandatory an
159
159
 
160
160
  - A claim is raised to verified only after the evidence it names has actually been read. Reading the
161
161
  controller does not verify the repository.
162
- - The raise MUST record **what was read**. "Verified against `src/internal/referral/pool.go` and its
163
- integration test" is a raise; "verified" is not.
162
+ - The raise MUST record **what was read**. "Verified against `<the file that was read>` and its
163
+ integration test" is a raise; "verified" is not. There is no example path here on purpose: a guide
164
+ is portable, and naming a tree — or even a file extension — teaches one product's stack, not the rule.
164
165
  - `[PARTIAL]` MUST state what is **not** covered. A partial that only says "partial" is an unlabelled claim
165
166
  wearing a label.
166
167
  - A claim MUST NOT be raised because it has survived several readings. Familiarity is not evidence.