wdi-method 0.6.30 → 0.6.31

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.
Files changed (30) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/README.md +3 -0
  3. package/bin/wdi-method.js +121 -14
  4. package/kit/.constitution/method/document/architecture-guide.md +217 -209
  5. package/kit/.constitution/method/document/corpus-guide.md +522 -517
  6. package/kit/.constitution/method/document/decision-guide.md +236 -216
  7. package/kit/.constitution/method/document/delivery-flow-guide.md +20 -0
  8. package/kit/.constitution/method/document/prd-guide.md +245 -245
  9. package/kit/.constitution/method/document/templates/design-system.md +96 -66
  10. package/kit/.constitution/method/document/templates/experience.md +62 -0
  11. package/kit/.constitution/method/document/templates/structure-codebase.md +131 -129
  12. package/kit/.constitution/method/document/templates/ux.md +78 -76
  13. package/kit/.constitution/method/document/ux-guide.md +161 -115
  14. package/kit/.constitution/method/method-glossary.md +3 -0
  15. package/kit/.constitution/method/scripts/validate.py +3310 -3200
  16. package/kit/.constitution/method/structure-guide.md +204 -202
  17. package/kit/.constitution/method/why/artifact-map.md +158 -157
  18. package/kit/skills/wdi-blueprint/SKILL.md +271 -264
  19. package/kit/skills/wdi-component/SKILL.md +179 -174
  20. package/kit/skills/wdi-decision/SKILL.md +206 -203
  21. package/kit/skills/wdi-help/SKILL.md +127 -125
  22. package/kit/skills/wdi-init/SKILL.md +9 -4
  23. package/kit/skills/wdi-problem/SKILL.md +114 -108
  24. package/kit/skills/wdi-product/SKILL.md +167 -162
  25. package/kit/skills/wdi-reconcile/SKILL.md +170 -169
  26. package/kit/skills/wdi-upgrade/SKILL.md +234 -215
  27. package/kit/skills/wdi-ux/SKILL.md +187 -169
  28. package/kit-overlay/AGENTS.md +3 -1
  29. package/package.json +1 -1
  30. package/scaffold/.control/registry/index.yaml +2 -1
@@ -1,215 +1,234 @@
1
- ---
2
- name: wdi-upgrade
3
- description: Use right after `wdi-method update` moved this repo to a newer method version. Finds every document and registry file still in the OLD shape, re-homes their content into the new one — registry rows out of prose, pointers where copies were, the rendered trees born — and verifies green. Runs once per version jump; safe to re-run.
4
- ---
5
-
6
- # WDI Upgrade
7
-
8
- `wdi-method update` does the mechanical half of a version jump: it overwrites the kit, renames files whose
9
- content needs no judgment, seeds what is new, prunes what is retired. It stops exactly where a decision
10
- about **content** begins — which PRD an `FR` belongs to, whether a sentence in the old brief was an
11
- assumption or a constraint. Those are this skill's half.
12
-
13
- **This skill's content is `.what/` and `.how/`** — the corpus a human reads. It does NOT cover
14
- `.control/memlog/` or a registry row: an autopilot ledger's filename is `update`'s own mechanical rename
15
- (the same way `waves.yaml` became `specs.yaml`), a stale mandate setting is a printed warning at `update`
16
- time because overwriting it would be overwriting a value the owner already chose, and a ledger's internal
17
- shape is healed by `wdi-autopilot` itself on its next run, because only it can read git and the registry to
18
- know where that run actually stands. Route those three there; this skill's report would have nothing to say
19
- about any of them.
20
-
21
- **This is the one skill allowed to edit `brief.md`, `prd.md`, an SRS, an SDD, or a C4 file directly.**
22
- Every other skill is forbidden, because a hand edit makes the memlog lie about how the document was
23
- produced. An upgrade produces nothing: it moves sentences that already exist into the home the new
24
- version gives them, word for word. The memlog stays true, and this skill's report is the record.
25
-
26
- ## Inputs
27
-
28
- | Source | What it answers |
29
- |---|---|
30
- | `.control/wdi-method.yaml` | The version now installed — the shape everything below MUST end in |
31
- | The `update` run's own output | The `upgrade` line lists what it detected as pending; start there |
32
- | `.control/registry/` | What is already a row, so nothing is landed twice |
33
- | `.what/_product-brief/brief.md` · `.what/_prd/*/prd.md` | The two documents whose shape changed most |
34
- | `.what/<pc>/SRS-<pc>.md` · `.how/<pc>/SDD-<pc>.md` · `.how/_platform/c4-l2-containers.md` | The three that used to carry a copy of a registry table |
35
- | `.constitution/method/document/templates/` | The target shape of every document above |
36
-
37
- ## Step 1 — Detect, and show the list before touching anything
38
-
39
- Probe each item below; a probe is a file or heading that exists only in the old shape. List every hit
40
- to the owner as a checklist, in the order below — it is a dependency order, and doing a later item
41
- before an earlier one lands content in a file that the earlier item is about to change.
42
-
43
- | # | Probe | Old shape | New home |
44
- |---|---|---|---|
45
- | 1 | `.control/registry/requirements.yaml` exists | one file for `BG` · `CAP` · `FR` · `NFR` · `UJ` | `goals.yaml` (`BG`) · `requirements-<slug>.yaml` per PRD (`CAP` · `FR` · `NFR` · `UJ`) |
46
- | 2 | a spec with a `W<n>` id, or carrying `epics:` / `stories:`, that is **not `closed`** | pre-rename plan | flattened in place — **this skill's**, and § *The pre-rename plan* below is the mapping. A `closed` spec is left alone: `Corpus.tickets()` reads it correctly and its ticket files are already allowed to be gone |
47
- | 3 | `brief.md` has `## Executive Summary`, `## Vision`, `## Assumptions`, or `## Prerequisites`; or `## Goals` lists `BG-` statements | 14-section brief | 8 sections: `Why` merges Summary + Vision; Goals is a pointer, its rows in `goals.yaml`; Assumptions → `questions/assumptions.md`; Prerequisites → `questions/external.md` |
48
- | 4 | any `prd.md` has a section **named** Document Purpose, Glossary, Non-Goals, Open Questions, or Assumptions Index — under whatever number that kit gave it — or `**Proof of done:**` under a feature | 12-section PRD with `FR` blocks | 7 sections; `FR`/`NFR` text → `requirements-<slug>.yaml`, the PRD keeps `Realizes:` ids; Glossary → `product-glossary.md`; §8/§9 → `questions/`; §1 becomes a delta |
49
- | 5 | any `SRS-<pc>.md` `## UC Catalogue` has `\| UC-` rows | catalogue copied from `usecases.yaml` | one pointer line; the rows live in `usecases.yaml` |
50
- | 6 | any `SDD-<pc>.md` `## Inherited Constraints` has a `Quoted rule` column, or `> ` blockquote lines under an `**AD-N — …**` heading, or the sentence `Quoted verbatim from` | `AD-N` text copied from the spine, in either of the two shapes SDDs were written in | ids only; the rendered SDD shows the text |
51
- | 7 | `c4-l2-containers.md` has a `\| Container \| Product Components living in it \|` table | matrix copied from `components.yaml` | one pointer line |
52
- | 8 | `.control/generated/brief.md`, `blueprint.md`, or `prd-*.md` exist | human pages in the machine folder | `.what-rendered/` · `.how-rendered/` — `render` clears the old ones |
53
- | 9 | `.what-rendered/` or `.how-rendered/` absent | no reader's tree yet | born by `render` |
54
- | 10 | any `.md` outside `.constitution/` cites `.control/generated/brief.md`, `blueprint.md`, or `prd-<slug>.md` | a pointer at a page that moved | `.what-rendered/_product-brief/brief.md` · `.how-rendered/blueprint.md` · `.what-rendered/_prd/<slug>/prd.md` — `cites-resolve` fails until it is repointed |
55
- | 11 | `docs/agents/issue-tracker.md` does not contain the words `seeded by ``wdi-method``` | the engines' config as `/setup-matt-pocock-skills` wrote it: everything in `.scratch/` with no registry behind it, `specs.yaml` never mentioned | the method's own answer. **`npx wdi-method engines --fix`** rewrites it and keeps the old text as `issue-tracker.md.bak`. Two of four live repos still had upstream's, which is why their tickets landed wherever the engine guessed |
56
- | 12 | a spec that is **not `closed`** has a `spec_folder` outside `.scratch/`, or a leaf that does not begin with its own id | spec folders under `_bmad-output/specs/`, leaf named freely — four repos wrote it four ways, one of them all four inside itself | `.scratch/<spec-id>-<slug>/`. Move the directory, rewrite the `spec_folder` row, then repoint every cite — `cites-resolve` is red until you do, and it is how you find them all. **A `closed` spec is left alone**: its folder is a record, moving it churns finished work, and its ticket files are already allowed to be gone |
57
- | 13 | `validate.py` reports `refs-resolve` on a `BG-` · `CAP-` · `FR-` · `NFR-` · `UC-` id — the finding itself is the probe | the row was **deleted** when the product stopped promising it, leaving every `DEC-` that served it pointing at nothing | the row comes BACK, marked `status: withdrawn` with `withdrawn_by` naming the decision that took it. Recover its text from git rather than retyping it — `git log -S"id: CAP-8" -- .control/registry/` finds the commit that held it. You MUST NOT edit the references instead: a `DEC-` records what happened, and it did serve that promise at the time. `corpus-guide.md` § *A withdrawn promise STAYS in the registry* owns the rule, `wdi-product` the procedure |
58
- | 14 | a `type: mandate` `DEC-` at `status: superseded` that some other `DEC-` names in its `accepted_by`, and neither side of the supersession is written | the mandate was retired — a setting changed, or the run ended — and nothing recorded which decision replaced it. Before 0.6.16 the validator read that status as present tense and turned every decision the run had taken red instead, with no legal repair | `superseded_by: DEC-<replacement>` on the retired mandate's row and `supersedes:` back on the replacement. That date is what revoked the delegation, and it is the one edit an `applied` decision allows. If nothing replaced it, the decision that ENDED the run is the replacement — open it through `wdi-decision`. You MUST NOT repair this by editing the decisions taken under the mandate: they were accepted while it stood |
59
-
60
- Items 11 and 12 are the engines' half of a version jump, and they come FIRST when both are hit:
61
- item 11 writes where a spec's files belong, item 12 moves them there. Doing 12 first means moving
62
- folders to a location the engines have not been told about, and the next `to-tickets` writes to the old
63
- one anyway.
64
-
65
- Anything not in the list is not this skill's. A brief that already has `## Why` is done; skip it.
66
-
67
- ## Step 2 — Registry first
68
-
69
- **1 — the requirement split.** A row written under an older kit carries `text:` where a newer one
70
- carries `title:`; both are the short label, the renderer and `timeline.py` read either, and one MUST
71
- NOT be copied into the other — that is two homes for one fact. Which PRD a row belongs to is read from
72
- the rows before it is read from the prose. A `CAP` or `UJ` with a `prd:` field goes to `requirements-<that slug>.yaml`; an `FR`
73
- follows its `capability:` to that CAP's file; an `NFR` follows its `component:` to the PRD whose CAPs
74
- own that component — a `BG` is product-level and never decides an NFR's home on its own, so `goal:` is
75
- only a tie-breaker when that component's CAPs span two PRDs. On a product with exactly **one** PRD every row belongs to that
76
- PRD by construction — write them all to its file and skip the citation scan. Otherwise, only a row with
77
- none of those fields falls back to the PRD whose prose cites its id — and an id cited by **two** PRDs
78
- is reported, not placed. The citation scan still runs on every row as a cross-check: a row whose
79
- structural home and citing PRD disagree is reported with both names. Write the row, unchanged, into
80
- its file; `goals:` rows go to `goals.yaml`. A sentence moved into a YAML value keeps its punctuation
81
- and its markup: when it holds `: ` or `#` or starts with a quote, wrap the value in double quotes or a
82
- `>-` block — never trade a colon for a dash or strip `**` and backticks to make it a plain scalar. A row with no home is reported by id, not guessed: the
83
- owner names it. When every row has moved, delete `requirements.yaml`; `id-allocated-once` fails if a
84
- row was copied instead of moved.
85
-
86
- **2 — the pre-rename plan.** A spec still shaped `epics: → stories:` is flattened into the one
87
- `tickets:` list the validator reads. Every part of this is a mapping; nothing here is a judgment, and
88
- nothing is invented:
89
-
90
- | Old | New |
91
- |---|---|
92
- | `waves:` as the file's top-level key | `specs:`. The rows below it do not otherwise change shape from this rename alone |
93
- | the spec's `W<n>` id | **unchanged.** It is a retired alias, and every memlog, `DEC-`, report and RTM row that names it MUST keep resolving. Renaming it to `SPEC-<n>` is the one thing this step MUST NOT do |
94
- | `epics:` → `stories:` nesting | one flat `tickets:` list, in the order the stories appear, epic by epic. The `epics` level is repealed: it grouped rows and bought nothing |
95
- | a story's own id (`"1"`, `"1-2"`) | `<spec-id>-<NN>`, renumbered from `01` in dependency order — `W3-01`, `W3-02`. A story id only ever promised uniqueness inside one epic, and this method keys tickets globally |
96
- | `depends_on: ["1-1"]` on a story | `blocked_by: [W3-01]` — the same edge, the new key, pointing at the new id |
97
- | `{spec_folder}/stories/1-2-<slug>.md` | `{spec_folder}/issues/<NN>-<slug>.md`, the `<NN>` matching the ticket's new id. `git mv`, so the file's history follows it |
98
- | a story's `touches:` | kept as `touches:`. A story never carried `component:`, and this step MUST NOT invent one — `wdi-init` owns that field |
99
-
100
- Two things stay untouched even here. The **status** of each ticket is read from its file and MUST NOT
101
- be copied into `specs.yaml` — `ticket-status-one-home` is what refuses that. And a `closed` spec is
102
- skipped whole: `validate.py` already flattens it in memory on every run, so rewriting the file changes
103
- no reading and only churns the record of finished work.
104
-
105
- Run `validate.py --check`. Green here means the registry is whole before any document starts pointing
106
- at it.
107
-
108
- ## Step 3 — Documents, oldest gate first
109
-
110
- **3 — brief.** Merge `## Executive Summary` and `## Vision` into one `## Why`, keeping every sentence
111
- that says something the other did not. Each `BG-N` statement under `## Goals` MUST already be a row in
112
- `goals.yaml` (Step 2); replace the list with the pointer line from the template. Each `## Assumptions`
113
- item becomes a row in `.control/questions/assumptions.md` with `Whose: owner`; each `## Prerequisites`
114
- item a row in `external.md` — **after** checking that no `OQ-` row already states it, because a brief
115
- written under an earlier kit usually landed them already and a second row is a copy. A prerequisite
116
- the brief itself marks satisfied is dropped, not landed as open. A `questions/` table written before
117
- the `Whose` column existed (`| id | Assumption | Cost if wrong | Taken | By |`) gets the column added —
118
- header and separator, and an empty cell on every existing row, which the validator counts as
119
- `unstated`, which is what they are. The rows this skill lands say `owner`. `Cost if wrong` is `—` when
120
- the source never stated one; it is not invented. An open question the source never marked blocking is
121
- filed in `assumptions.md`, as the template's three tests say. Delete both sections, and say how many
122
- items were already rows. Check `## Success Criteria` names one measurable
123
- figure — if it does not, that is a finding for the owner, not a sentence for this skill to invent.
124
-
125
- **4 — each PRD.** Sections are matched **by name, never by number**: the numbers moved between kits
126
- (one kit numbers Non-Goals §5 and Open Questions §8; an older one numbers Non-Goals §7, MVP Scope §8,
127
- Open Questions §10), so a step that says "delete §8" deletes MVP Scope on the wrong corpus. Delete
128
- `Document Purpose`. Every `Glossary` term not yet in `.control/product-glossary.md` is added there,
129
- verbatim; then delete `Glossary`. Every `Non-Goals` item MUST already be in the brief's Scope Out or
130
- this PRD's `MVP Scope → Out of Scope` — if neither holds it, add it to the one it belongs to; then
131
- delete `Non-Goals`. Every `Open Questions` and `Assumptions Index` item becomes a `questions/` row
132
- (after the same already-a-row check as the brief's); delete both.
133
- Under each feature, every `FR` block is folded into its row in `requirements-<slug>.yaml` (Step 2)
134
- before the block goes: the block's description paragraph — the prose between the `#### FR-N` heading
135
- and the first `**…:**` label — becomes the row's `statement:` when the row has none (the row's `title`
136
- stays). **This is the one move with no validator behind it**, so count it: blocks with a paragraph
137
- versus rows that now carry `statement:` MUST match, and Step 5 reports both numbers. A run that deletes
138
- the blocks and lands zero statements has thrown the requirement's own sentence away; its `**Consequences (testable):**` bullets move verbatim to
139
- `addendum.md` under `## Technical how — testable consequences per FR`, appended **after** the sections
140
- already there, one `### FR-N — title` each,
141
- because `prd-guide.md` repealed the double proof of done and that is where the technical restatement
142
- lives now; its `**Proof of done:**` is compared with the row's `proof` — when they differ, the
143
- **registry is kept** (it is the declared SSOT), the PRD's is dropped, and both texts are reported side
144
- by side for the owner, never merged. Then the block becomes `**Realizes:** FR-a, FR-b, NFR-c`, and a
145
- `**Functional Requirements:**` label left with nothing under it is deleted — the rendered page rebuilds
146
- the blocks from the rows. When the deletions are done, **put the surviving `##` sections in the
147
- template's order and renumber them** — moving a whole section is a move, not an edit, and a file whose
148
- `## 6` sits above its `## 4` tells the AI reader the numbers lie. The order — 1 Why This Initiative · 2 Target User · 3 Features · 4 MVP Scope · 5 Success
149
- Metrics · 6 Cross-Cutting NFRs · 7 Constraints and Guardrails — and the `###` beneath them to match
150
- (`### 8.2` → `### 4.2`). Numbers are not sentences; leaving `## 4. Features` beside `## 8. MVP Scope`
151
- tells the next reader two sections went missing. A `UJ-N` the prose names that has no row in any requirement file is
152
- not given one — it is marked with an HTML comment where it stands and reported; `wdi-product`
153
- allocates ids. A moved sentence that cites a section number (`§ 8`) of a section this step deletes
154
- keeps the number — it is reported as wording for the owner, not repointed, because its new home is a
155
- judgment. `## 1. Vision` becomes `## 1. Why This Initiative`: delete only the sentences
156
- that also appear, word for word, in the brief's `Why`; what remains is left whole under an HTML comment
157
- saying the new shape wants a delta, because deciding which paraphrases are copies is the owner's. On a
158
- single-initiative product that is one line pointing at the brief — write it and say so.
159
-
160
- **5 — each SRS.** Every `| UC-` row MUST already be in `usecases.yaml` with the same `critical`. A row
161
- missing there is landed first. Then the table becomes the template's pointer line.
162
-
163
- **6 — each SDD.** In `## Inherited Constraints`, drop the `Quoted rule` column; keep `AD` and `How it
164
- lands here`. In the blockquote shape, drop the `> ` lines and the `Quoted verbatim from` sentence; keep
165
- the `**AD-N — title**` heading and the landing prose under it, each heading on its own line. An `AD-N`
166
- cited here that is not in the spine is a finding.
167
-
168
- **7 — C4 L2.** Every PC listed in the table MUST have that container in its `containers:`. Then the
169
- table becomes the pointer line.
170
-
171
- **8 — pointers at the moved pages.** Every `.md` that cites `.control/generated/brief.md`,
172
- `blueprint.md`, or `prd-<slug>.md` **and that `cites-resolve` reads** is repointed to the new path — a
173
- path substitution, nothing else in the sentence changes. That includes a product's own scratch and
174
- issue notes. It excludes what the validator excludes: `.control/memlog/`, `.control/decisions/`,
175
- `.control/reports/`, `questions/answered.md`, and `_bmad-output/` — those describe the past, and a
176
- path rewritten there falsifies a record; the installer's probe skips them for the same reason.
177
-
178
- ## Step 4 — Render, then validate
179
-
180
- ```bash
181
- uv run .constitution/method/scripts/validate.py --generate
182
- ```
183
-
184
- This writes every reader's page into `.what-rendered/` and `.how-rendered/`, and clears the human pages
185
- that used to sit in `.control/generated/`. Then `--check` MUST be green. Every finding at this point is
186
- either a row that moved wrong in Step 2 or a pointer that points at nothing — both are this skill's to
187
- fix before it reports done.
188
-
189
- ## Step 5 — Report, and commit once
190
-
191
- What moved, file by file · what was landed into the registry, by id · what could not be placed and
192
- needs the owner · the rendered pages now waiting to be read, one per gate · validators green. Then
193
- **one** commit: `chore(method): upgrade <from> → <to>`. Not one per document — the upgrade is one
194
- event.
195
-
196
- ## Rules
197
-
198
- - You MUST NOT change a sentence while moving it. Wording that reads wrong in its new home is a
199
- finding for the owning skill, later.
200
- - You MUST NOT invent a home. An `FR` no PRD cites, a Non-Goal neither boundary holds, an `AD-N` not
201
- in the spine — each is reported by id and left where it was.
202
- - You MUST NOT write your findings into the corpus. A registry file carries rows and nothing about
203
- the upgrade that produced them; a gap in this skill goes in the Step 5 report. The one exception is
204
- the HTML comment the steps above name, placed where the owner will read the document.
205
- - You MUST NOT re-cut `specs.yaml`. That is `wdi-build`'s, where a human can see it.
206
- - You MUST NOT touch `.control/decisions/` or `.control/reports/`. A frozen `DEC-` that cites `V26` or
207
- `W3` is history; the alias rule in `corpus-guide.md` covers it.
208
- - Re-running on an upgraded repo MUST find nothing and say so. Every probe in Step 1 is idempotent.
209
-
210
- ## Output
211
-
212
- The Step 1 checklist with each item marked done · skipped (already new shape) · left for the owner, with
213
- the id list for the last · the registry rows landed · **statements landed / FR blocks that had a
214
- paragraph** · consequences moved · `questions/` rows added and rows found already present · proof-of-done
215
- divergences, both texts · the path of every rendered page · the validator result · the commit.
1
+ ---
2
+ name: wdi-upgrade
3
+ description: Use right after `wdi-method update` moved this repo to a newer method version. Finds every document and registry file still in the OLD shape, re-homes their content into the new one — registry rows out of prose, pointers where copies were, the rendered trees born — and verifies green. Runs once per version jump; safe to re-run.
4
+ ---
5
+
6
+ # WDI Upgrade
7
+
8
+ `wdi-method update` does the mechanical half of a version jump: it overwrites the kit, renames files whose
9
+ content needs no judgment, seeds what is new, prunes what is retired. It stops exactly where a decision
10
+ about **content** begins — which PRD an `FR` belongs to, whether a sentence in the old brief was an
11
+ assumption or a constraint. Those are this skill's half.
12
+
13
+ **This skill's content is `.what/` and `.how/`** — the corpus a human reads. It does NOT cover
14
+ `.control/memlog/` or a registry row: an autopilot ledger's filename is `update`'s own mechanical rename
15
+ (the same way `waves.yaml` became `specs.yaml`), a stale mandate setting is a printed warning at `update`
16
+ time because overwriting it would be overwriting a value the owner already chose, and a ledger's internal
17
+ shape is healed by `wdi-autopilot` itself on its next run, because only it can read git and the registry to
18
+ know where that run actually stands. Route those three there; this skill's report would have nothing to say
19
+ about any of them.
20
+
21
+ **This is the one skill allowed to edit `brief.md`, `prd.md`, an SRS, an SDD, or a C4 file directly.**
22
+ Every other skill is forbidden, because a hand edit makes the memlog lie about how the document was
23
+ produced. An upgrade produces nothing: it moves sentences that already exist into the home the new
24
+ version gives them, word for word. The memlog stays true, and this skill's report is the record.
25
+
26
+ ## Inputs
27
+
28
+ | Source | What it answers |
29
+ |---|---|
30
+ | `.control/wdi-method.yaml` | The version now installed — the shape everything below MUST end in |
31
+ | `upgrade_pending` in `.control/wdi-method.yaml` | What `update` detected as pending, numbered by the rows of Step 1; start there. `npx wdi-method upgrade-check` re-probes it at any time |
32
+ | `.control/registry/` | What is already a row, so nothing is landed twice |
33
+ | `.what/_product-brief/brief.md` · `.what/_prd/*/prd.md` | The two documents whose shape changed most |
34
+ | `.what/<pc>/SRS-<pc>.md` · `.how/<pc>/SDD-<pc>.md` · `.how/_platform/c4-l2-containers.md` | The three that used to carry a copy of a registry table |
35
+ | `.constitution/method/document/templates/` | The target shape of every document above |
36
+
37
+ ## Step 1 — Detect, and show the list before touching anything
38
+
39
+ Probe each item below; a probe is a file or heading that exists only in the old shape. List every hit
40
+ to the owner as a checklist, in the order below — it is a dependency order, and doing a later item
41
+ before an earlier one lands content in a file that the earlier item is about to change.
42
+
43
+ | # | Probe | Old shape | New home |
44
+ |---|---|---|---|
45
+ | 1 | `.control/registry/requirements.yaml` exists | one file for `BG` · `CAP` · `FR` · `NFR` · `UJ` | `goals.yaml` (`BG`) · `requirements-<slug>.yaml` per PRD (`CAP` · `FR` · `NFR` · `UJ`) |
46
+ | 2 | a spec with a `W<n>` id, or carrying `epics:` / `stories:`, that is **not `closed`** | pre-rename plan | flattened in place — **this skill's**, and § *The pre-rename plan* below is the mapping. A `closed` spec is left alone: `Corpus.tickets()` reads it correctly and its ticket files are already allowed to be gone |
47
+ | 3 | `brief.md` has `## Executive Summary`, `## Vision`, `## Assumptions`, or `## Prerequisites`; or `## Goals` lists `BG-` statements | 14-section brief | 8 sections: `Why` merges Summary + Vision; Goals is a pointer, its rows in `goals.yaml`; Assumptions → `questions/assumptions.md`; Prerequisites → `questions/external.md` |
48
+ | 4 | any `prd.md` has a section **named** Document Purpose, Glossary, Non-Goals, Open Questions, or Assumptions Index — under whatever number that kit gave it — or `**Proof of done:**` under a feature | 12-section PRD with `FR` blocks | 7 sections; `FR`/`NFR` text → `requirements-<slug>.yaml`, the PRD keeps `Realizes:` ids; Glossary → `product-glossary.md`; §8/§9 → `questions/`; §1 becomes a delta |
49
+ | 5 | any `SRS-<pc>.md` `## UC Catalogue` has `\| UC-` rows | catalogue copied from `usecases.yaml` | one pointer line; the rows live in `usecases.yaml` |
50
+ | 6 | any `SDD-<pc>.md` `## Inherited Constraints` has a `Quoted rule` column, or `> ` blockquote lines under an `**AD-N — …**` heading, or the sentence `Quoted verbatim from` | `AD-N` text copied from the spine, in either of the two shapes SDDs were written in | ids only; the rendered SDD shows the text |
51
+ | 7 | `c4-l2-containers.md` has a `\| Container \| Product Components living in it \|` table | matrix copied from `components.yaml` | one pointer line |
52
+ | 8 | `.control/generated/brief.md`, `blueprint.md`, or `prd-*.md` exist | human pages in the machine folder | `.what-rendered/` · `.how-rendered/` — `render` clears the old ones |
53
+ | 9 | `.what-rendered/` or `.how-rendered/` absent | no reader's tree yet | born by `render` |
54
+ | 10 | any `.md` outside `.constitution/` cites `.control/generated/brief.md`, `blueprint.md`, or `prd-<slug>.md` | a pointer at a page that moved | `.what-rendered/_product-brief/brief.md` · `.how-rendered/blueprint.md` · `.what-rendered/_prd/<slug>/prd.md` — `cites-resolve` fails until it is repointed |
55
+ | 11 | `docs/agents/issue-tracker.md` does not contain the words `seeded by ``wdi-method``` | the engines' config as `/setup-matt-pocock-skills` wrote it: everything in `.scratch/` with no registry behind it, `specs.yaml` never mentioned | the method's own answer. **`npx wdi-method engines --fix`** rewrites it and keeps the old text as `issue-tracker.md.bak`. Two of four live repos still had upstream's, which is why their tickets landed wherever the engine guessed |
56
+ | 12 | a spec that is **not `closed`** has a `spec_folder` outside `.scratch/`, or a leaf that does not begin with its own id | spec folders under `_bmad-output/specs/`, leaf named freely — four repos wrote it four ways, one of them all four inside itself | `.scratch/<spec-id>-<slug>/`. Move the directory, rewrite the `spec_folder` row, then repoint every cite — `cites-resolve` is red until you do, and it is how you find them all. **A `closed` spec is left alone**: its folder is a record, moving it churns finished work, and its ticket files are already allowed to be gone |
57
+ | 13 | **validator** — `validate.py` reports `refs-resolve` on a `BG-` · `CAP-` · `FR-` · `NFR-` · `UC-` id — the finding itself is the probe | the row was **deleted** when the product stopped promising it, leaving every `DEC-` that served it pointing at nothing | the row comes BACK, marked `status: withdrawn` with `withdrawn_by` naming the decision that took it. Recover its text from git rather than retyping it — `git log -S"id: CAP-8" -- .control/registry/` finds the commit that held it. You MUST NOT edit the references instead: a `DEC-` records what happened, and it did serve that promise at the time. `corpus-guide.md` § *A withdrawn promise STAYS in the registry* owns the rule, `wdi-product` the procedure |
58
+ | 14 | **validator** — `mandate-accept` reports a `type: mandate` `DEC-` at `status: superseded` that some other `DEC-` names in its `accepted_by`, and neither side of the supersession is written | the mandate was retired — a setting changed, or the run ended — and nothing recorded which decision replaced it. Before 0.6.16 the validator read that status as present tense and turned every decision the run had taken red instead, with no legal repair | `superseded_by: DEC-<replacement>` on the retired mandate's row and `supersedes:` back on the replacement. That date is what revoked the delegation, and it is the one edit an `applied` decision allows. If nothing replaced it, the decision that ENDED the run is the replacement — open it through `wdi-decision`. You MUST NOT repair this by editing the decisions taken under the mandate: they were accepted while it stood |
59
+ | 15 | `.how/_platform/design-system.md` has a section named Foundation, Information architecture, Voice and Tone, Flow map, or Cross-component behaviour | cross-component EXPERIENCE parked in the product's design system, because until `.what/experience.md` existed it had no home | `.what/experience.md`, section by section and word for word — the split is `ux-guide.md` § *Product level*. What stays in `design-system.md` is build: tokens, base elements, state patterns, interaction primitives, the surfaces that are not screens. A section that holds both is split at the sentence, by the redesign test |
60
+ | 16 | a file in `.what/` or `.how/` cites `_bmad-output/ux/**/DESIGN.md`, `EXPERIENCE.md`, or `design-system.md` | the corpus pointing back at the UX run after it landed — a behaviour reference to the run, or a *landed from* line at the top of a landed document | the cite names the landed copy instead; a *landed from* line becomes the landed document's frontmatter `landed_from` (item 18). Reported only once components exist — before that the run is the only copy. `ux-landed` is red until every one is gone |
61
+ | 17 | a container in `components.yaml` whose `repo:` is filled and whose id still has a heading in the code map, or `repo:` on a `built: false` container. A `repo:` naming THIS repository is `container-built`'s — it reads the `origin` remote | a product split across repositories, recorded before `repo:` had a meaning | `repo:` is filled only where the code lives in ANOTHER repository, and that container's heading leaves this repo's code map — it belongs in the map of the repo that holds it. A `repo:` naming THIS repo is removed. `wdi-init` intent `structure` re-derives the map |
62
+ | 18 | components exist, a UX run is in `_bmad-output/ux/`, and a landed `DESIGN.md` or `EXPERIENCE.md` has no `landed_from` in its frontmatter | landed before `landed_from` existed, so `ux-landed` cannot tell which run a landing discharged and reads every run as owed | `landed_from:` lists the run file(s) it came from, read off the old *landed from* line or `.control/memlog/ux.md`. A run that was never landed is landed now through `wdi-ux`, or marked `status: superseded` if the owner abandoned it |
63
+
64
+ **Gates passed before they were recorded** are not a numbered item — nothing moves, the owner answers.
65
+ Where `wdi-reconcile` or `wdi-help` shows downstream work for a gate `gates_passed` does not list, ask the
66
+ owner once per gate whether it passed, and write it on a yes (`delivery-flow-guide.md` § *Recording a gate
67
+ that passed*). The report says which were recorded and which the owner left open.
68
+
69
+ Items 11 and 12 are the engines' half of a version jump, and they come FIRST when both are hit:
70
+ item 11 writes where a spec's files belong, item 12 moves them there. Doing 12 first means moving
71
+ folders to a location the engines have not been told about, and the next `to-tickets` writes to the old
72
+ one anyway.
73
+
74
+ Anything not in the list is not this skill's. A brief that already has `## Why` is done; skip it.
75
+
76
+ ## Step 2 — Registry first
77
+
78
+ **1 — the requirement split.** A row written under an older kit carries `text:` where a newer one
79
+ carries `title:`; both are the short label, the renderer and `timeline.py` read either, and one MUST
80
+ NOT be copied into the other — that is two homes for one fact. Which PRD a row belongs to is read from
81
+ the rows before it is read from the prose. A `CAP` or `UJ` with a `prd:` field goes to `requirements-<that slug>.yaml`; an `FR`
82
+ follows its `capability:` to that CAP's file; an `NFR` follows its `component:` to the PRD whose CAPs
83
+ own that component — a `BG` is product-level and never decides an NFR's home on its own, so `goal:` is
84
+ only a tie-breaker when that component's CAPs span two PRDs. On a product with exactly **one** PRD every row belongs to that
85
+ PRD by construction — write them all to its file and skip the citation scan. Otherwise, only a row with
86
+ none of those fields falls back to the PRD whose prose cites its id — and an id cited by **two** PRDs
87
+ is reported, not placed. The citation scan still runs on every row as a cross-check: a row whose
88
+ structural home and citing PRD disagree is reported with both names. Write the row, unchanged, into
89
+ its file; `goals:` rows go to `goals.yaml`. A sentence moved into a YAML value keeps its punctuation
90
+ and its markup: when it holds `: ` or `#` or starts with a quote, wrap the value in double quotes or a
91
+ `>-` block — never trade a colon for a dash or strip `**` and backticks to make it a plain scalar. A row with no home is reported by id, not guessed: the
92
+ owner names it. When every row has moved, delete `requirements.yaml`; `id-allocated-once` fails if a
93
+ row was copied instead of moved.
94
+
95
+ **2 — the pre-rename plan.** A spec still shaped `epics: → stories:` is flattened into the one
96
+ `tickets:` list the validator reads. Every part of this is a mapping; nothing here is a judgment, and
97
+ nothing is invented:
98
+
99
+ | Old | New |
100
+ |---|---|
101
+ | `waves:` as the file's top-level key | `specs:`. The rows below it do not otherwise change shape from this rename alone |
102
+ | the spec's `W<n>` id | **unchanged.** It is a retired alias, and every memlog, `DEC-`, report and RTM row that names it MUST keep resolving. Renaming it to `SPEC-<n>` is the one thing this step MUST NOT do |
103
+ | `epics:` → `stories:` nesting | one flat `tickets:` list, in the order the stories appear, epic by epic. The `epics` level is repealed: it grouped rows and bought nothing |
104
+ | a story's own id (`"1"`, `"1-2"`) | `<spec-id>-<NN>`, renumbered from `01` in dependency order — `W3-01`, `W3-02`. A story id only ever promised uniqueness inside one epic, and this method keys tickets globally |
105
+ | `depends_on: ["1-1"]` on a story | `blocked_by: [W3-01]` — the same edge, the new key, pointing at the new id |
106
+ | `{spec_folder}/stories/1-2-<slug>.md` | `{spec_folder}/issues/<NN>-<slug>.md`, the `<NN>` matching the ticket's new id. `git mv`, so the file's history follows it |
107
+ | a story's `touches:` | kept as `touches:`. A story never carried `component:`, and this step MUST NOT invent one — `wdi-init` owns that field |
108
+
109
+ Two things stay untouched even here. The **status** of each ticket is read from its file and MUST NOT
110
+ be copied into `specs.yaml` — `ticket-status-one-home` is what refuses that. And a `closed` spec is
111
+ skipped whole: `validate.py` already flattens it in memory on every run, so rewriting the file changes
112
+ no reading and only churns the record of finished work.
113
+
114
+ Run `validate.py --check`. Green here means the registry is whole before any document starts pointing
115
+ at it.
116
+
117
+ ## Step 3 — Documents, oldest gate first
118
+
119
+ **3 — brief.** Merge `## Executive Summary` and `## Vision` into one `## Why`, keeping every sentence
120
+ that says something the other did not. Each `BG-N` statement under `## Goals` MUST already be a row in
121
+ `goals.yaml` (Step 2); replace the list with the pointer line from the template. Each `## Assumptions`
122
+ item becomes a row in `.control/questions/assumptions.md` with `Whose: owner`; each `## Prerequisites`
123
+ item a row in `external.md` — **after** checking that no `OQ-` row already states it, because a brief
124
+ written under an earlier kit usually landed them already and a second row is a copy. A prerequisite
125
+ the brief itself marks satisfied is dropped, not landed as open. A `questions/` table written before
126
+ the `Whose` column existed (`| id | Assumption | Cost if wrong | Taken | By |`) gets the column added —
127
+ header and separator, and an empty cell on every existing row, which the validator counts as
128
+ `unstated`, which is what they are. The rows this skill lands say `owner`. `Cost if wrong` is `—` when
129
+ the source never stated one; it is not invented. An open question the source never marked blocking is
130
+ filed in `assumptions.md`, as the template's three tests say. Delete both sections, and say how many
131
+ items were already rows. Check `## Success Criteria` names one measurable
132
+ figure — if it does not, that is a finding for the owner, not a sentence for this skill to invent.
133
+
134
+ **4 — each PRD.** Sections are matched **by name, never by number**: the numbers moved between kits
135
+ (one kit numbers Non-Goals §5 and Open Questions §8; an older one numbers Non-Goals §7, MVP Scope §8,
136
+ Open Questions §10), so a step that says "delete §8" deletes MVP Scope on the wrong corpus. Delete
137
+ `Document Purpose`. Every `Glossary` term not yet in `.control/product-glossary.md` is added there,
138
+ verbatim; then delete `Glossary`. Every `Non-Goals` item MUST already be in the brief's Scope Out or
139
+ this PRD's `MVP Scope → Out of Scope` — if neither holds it, add it to the one it belongs to; then
140
+ delete `Non-Goals`. Every `Open Questions` and `Assumptions Index` item becomes a `questions/` row
141
+ (after the same already-a-row check as the brief's); delete both.
142
+ Under each feature, every `FR` block is folded into its row in `requirements-<slug>.yaml` (Step 2)
143
+ before the block goes: the block's description paragraph — the prose between the `#### FR-N` heading
144
+ and the first `**…:**` label — becomes the row's `statement:` when the row has none (the row's `title`
145
+ stays). **This is the one move with no validator behind it**, so count it: blocks with a paragraph
146
+ versus rows that now carry `statement:` MUST match, and Step 5 reports both numbers. A run that deletes
147
+ the blocks and lands zero statements has thrown the requirement's own sentence away; its `**Consequences (testable):**` bullets move verbatim to
148
+ `addendum.md` under `## Technical how — testable consequences per FR`, appended **after** the sections
149
+ already there, one `### FR-N — title` each,
150
+ because `prd-guide.md` repealed the double proof of done and that is where the technical restatement
151
+ lives now; its `**Proof of done:**` is compared with the row's `proof` — when they differ, the
152
+ **registry is kept** (it is the declared SSOT), the PRD's is dropped, and both texts are reported side
153
+ by side for the owner, never merged. Then the block becomes `**Realizes:** FR-a, FR-b, NFR-c`, and a
154
+ `**Functional Requirements:**` label left with nothing under it is deleted — the rendered page rebuilds
155
+ the blocks from the rows. When the deletions are done, **put the surviving `##` sections in the
156
+ template's order and renumber them** — moving a whole section is a move, not an edit, and a file whose
157
+ `## 6` sits above its `## 4` tells the AI reader the numbers lie. The order — 1 Why This Initiative · 2 Target User · 3 Features · 4 MVP Scope · 5 Success
158
+ Metrics · 6 Cross-Cutting NFRs · 7 Constraints and Guardrails — and the `###` beneath them to match
159
+ (`### 8.2` → `### 4.2`). Numbers are not sentences; leaving `## 4. Features` beside `## 8. MVP Scope`
160
+ tells the next reader two sections went missing. A `UJ-N` the prose names that has no row in any requirement file is
161
+ not given one — it is marked with an HTML comment where it stands and reported; `wdi-product`
162
+ allocates ids. A moved sentence that cites a section number (`§ 8`) of a section this step deletes
163
+ keeps the number — it is reported as wording for the owner, not repointed, because its new home is a
164
+ judgment. `## 1. Vision` becomes `## 1. Why This Initiative`: delete only the sentences
165
+ that also appear, word for word, in the brief's `Why`; what remains is left whole under an HTML comment
166
+ saying the new shape wants a delta, because deciding which paraphrases are copies is the owner's. On a
167
+ single-initiative product that is one line pointing at the brief — write it and say so.
168
+
169
+ **5 — each SRS.** Every `| UC-` row MUST already be in `usecases.yaml` with the same `critical`. A row
170
+ missing there is landed first. Then the table becomes the template's pointer line.
171
+
172
+ **6 — each SDD.** In `## Inherited Constraints`, drop the `Quoted rule` column; keep `AD` and `How it
173
+ lands here`. In the blockquote shape, drop the `> ` lines and the `Quoted verbatim from` sentence; keep
174
+ the `**AD-N — title**` heading and the landing prose under it, each heading on its own line. An `AD-N`
175
+ cited here that is not in the spine is a finding.
176
+
177
+ **7 — C4 L2.** Every PC listed in the table MUST have that container in its `containers:`. Then the
178
+ table becomes the pointer line.
179
+
180
+ **8 — pointers at the moved pages.** Every `.md` that cites `.control/generated/brief.md`,
181
+ `blueprint.md`, or `prd-<slug>.md` **and that `cites-resolve` reads** is repointed to the new path — a
182
+ path substitution, nothing else in the sentence changes. That includes a product's own scratch and
183
+ issue notes. It excludes what the validator excludes: `.control/memlog/`, `.control/decisions/`,
184
+ `.control/reports/`, `questions/answered.md`, and `_bmad-output/` — those describe the past, and a
185
+ path rewritten there falsifies a record; the installer's probe skips them for the same reason.
186
+
187
+ ## Step 4 — Render, then validate
188
+
189
+ ```bash
190
+ uv run .constitution/method/scripts/validate.py --generate
191
+ ```
192
+
193
+ This writes every reader's page into `.what-rendered/` and `.how-rendered/`, and clears the human pages
194
+ that used to sit in `.control/generated/`. Then `--check` MUST be green. Every finding at this point is
195
+ either a row that moved wrong in Step 2 or a pointer that points at nothing — both are this skill's to
196
+ fix before it reports done.
197
+
198
+ Then close the record:
199
+
200
+ ```bash
201
+ npx wdi-method upgrade-check
202
+ ```
203
+
204
+ It re-runs the probes and rewrites `upgrade_pending` in `.control/wdi-method.yaml`. It MUST exit 0 —
205
+ the field is then gone, which is what tells `wdi-help` and the next session that nothing is owed. An
206
+ item still listed is either not moved yet or needs the owner, and the report says which.
207
+
208
+ ## Step 5 — Report, and commit once
209
+
210
+ What moved, file by file · what was landed into the registry, by id · what could not be placed and
211
+ needs the owner · the rendered pages now waiting to be read, one per gate · validators green. Then
212
+ **one** commit: `chore(method): upgrade <from> → <to>`. Not one per document — the upgrade is one
213
+ event.
214
+
215
+ ## Rules
216
+
217
+ - You MUST NOT change a sentence while moving it. Wording that reads wrong in its new home is a
218
+ finding for the owning skill, later.
219
+ - You MUST NOT invent a home. An `FR` no PRD cites, a Non-Goal neither boundary holds, an `AD-N` not
220
+ in the spine — each is reported by id and left where it was.
221
+ - You MUST NOT write your findings into the corpus. A registry file carries rows and nothing about
222
+ the upgrade that produced them; a gap in this skill goes in the Step 5 report. The one exception is
223
+ the HTML comment the steps above name, placed where the owner will read the document.
224
+ - You MUST NOT re-cut `specs.yaml`. That is `wdi-build`'s, where a human can see it.
225
+ - You MUST NOT touch `.control/decisions/` or `.control/reports/`. A frozen `DEC-` that cites `V26` or
226
+ `W3` is history; the alias rule in `corpus-guide.md` covers it.
227
+ - Re-running on an upgraded repo MUST find nothing and say so. Every probe in Step 1 is idempotent.
228
+
229
+ ## Output
230
+
231
+ The Step 1 checklist with each item marked done · skipped (already new shape) · left for the owner, with
232
+ the id list for the last · the registry rows landed · **statements landed / FR blocks that had a
233
+ paragraph** · consequences moved · `questions/` rows added and rows found already present · proof-of-done
234
+ divergences, both texts · the path of every rendered page · the validator result · the commit.