wdi-method 0.6.30 → 0.6.32

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 (43) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/NOTICE +7 -2
  3. package/README.md +16 -11
  4. package/bin/wdi-method.js +396 -87
  5. package/kit/.constitution/method/document/architecture-guide.md +217 -209
  6. package/kit/.constitution/method/document/bmad-skill-register.md +107 -104
  7. package/kit/.constitution/method/document/corpus-guide.md +522 -517
  8. package/kit/.constitution/method/document/decision-guide.md +236 -216
  9. package/kit/.constitution/method/document/delivery-flow-guide.md +20 -0
  10. package/kit/.constitution/method/document/prd-guide.md +245 -245
  11. package/kit/.constitution/method/document/templates/design-system.md +96 -66
  12. package/kit/.constitution/method/document/templates/experience.md +62 -0
  13. package/kit/.constitution/method/document/templates/structure-codebase.md +131 -129
  14. package/kit/.constitution/method/document/templates/ux.md +78 -76
  15. package/kit/.constitution/method/document/ux-guide.md +161 -115
  16. package/kit/.constitution/method/method-glossary.md +3 -0
  17. package/kit/.constitution/method/scripts/validate.py +3375 -3200
  18. package/kit/.constitution/method/structure-guide.md +204 -202
  19. package/kit/.constitution/method/why/README.md +1 -1
  20. package/kit/.constitution/method/why/artifact-map.md +158 -157
  21. package/kit/.constitution/method/why/portability.md +19 -2
  22. package/kit/skills/wdi-autopilot/SKILL.md +32 -19
  23. package/kit/skills/wdi-blueprint/SKILL.md +271 -264
  24. package/kit/skills/wdi-build/SKILL.md +28 -19
  25. package/kit/skills/wdi-component/SKILL.md +179 -174
  26. package/kit/skills/wdi-daily-autopilot/SKILL.md +24 -13
  27. package/kit/skills/wdi-daily-what-to-build/SKILL.md +9 -6
  28. package/kit/skills/wdi-daily-what-to-test/SKILL.md +2 -0
  29. package/kit/skills/wdi-decision/SKILL.md +206 -203
  30. package/kit/skills/wdi-explain-to-me/SKILL.md +2 -0
  31. package/kit/skills/wdi-help/SKILL.md +130 -125
  32. package/kit/skills/wdi-init/SKILL.md +10 -5
  33. package/kit/skills/wdi-problem/SKILL.md +114 -108
  34. package/kit/skills/wdi-product/SKILL.md +167 -162
  35. package/kit/skills/wdi-prune-or-archive/SKILL.md +2 -0
  36. package/kit/skills/wdi-reconcile/SKILL.md +170 -169
  37. package/kit/skills/wdi-upgrade/SKILL.md +234 -215
  38. package/kit/skills/wdi-ux/SKILL.md +187 -169
  39. package/kit-overlay/AGENTS.md +15 -2
  40. package/kit-overlay/portability.md +19 -2
  41. package/lib/platforms.mjs +420 -248
  42. package/package.json +1 -1
  43. package/scaffold/.control/registry/index.yaml +2 -1
@@ -1,162 +1,167 @@
1
- ---
2
- name: wdi-product
3
- description: Use at G2 Product — when a PRD is created or an existing promise changes. Two intents, prd and update. Checks position, dispatches bmad-prd, verifies the result against prd-guide.md, and lands the memlog. Never writes the PRD itself.
4
- ---
5
-
6
- # WDI Product
7
-
8
- G2 decides **what is built, and how it feels to use.** `bmad-prd` writes the PRD.
9
-
10
- This wrapper exists because `bmad-prd` was the only writer of a primary artifact in this method with no WDI
11
- wrapper at all — so nothing checked its position, nothing verified its result against the guide, and nothing
12
- landed its memlog where the next run would read it. Those three gaps were paid for at G2 every time.
13
-
14
- You MUST NOT write or edit `prd.md` yourself. If a check fails, name what is missing and re-dispatch.
15
-
16
- | Intent | When |
17
- |---|---|
18
- | `prd` | A functional area a reader would not think to look for in an existing PRD |
19
- | `update` | Anything else — the default, and by a wide margin |
20
-
21
- ## Inputs
22
-
23
- | Source | What it answers |
24
- |---|---|
25
- | `.what/_product-brief/brief.md` | The problem, the primary user, the boundary the PRD MUST respect |
26
- | `.what/_prd/*/prd.md` | Which initiatives already have a PRD, and what each already promises |
27
- | `.control/registry/goals.yaml` | The `BG` this initiative serves |
28
- | `.control/registry/requirements-*.yaml` | Every initiative's `FR`/`NFR`/`UJ`, so the next id continues the product's sequence |
29
- | `.control/decisions/` | `applied` decisions the PRD MUST already reflect |
30
- | `.constitution/method/document/prd-guide.md` | The rules the result is checked against |
31
- | `.control/product-glossary.md` | Terms already fixed |
32
-
33
- ## Step 1 — Position, and `update` is the default
34
-
35
- The decision this skill exists for. The test is the **reader**, not the calendar:
36
-
37
- > Would someone looking for this promise open an existing document?
38
-
39
- Yes → `update`, however large the change. No → `prd`. A PRD MUST NOT be split because it grew long, and a
40
- release is never a reason on its own. `prd-guide.md` owns the full table.
41
-
42
- Three asks that are not this skill:
43
-
44
- | Ask | Route |
45
- |---|---|
46
- | The problem itself has changed | `wdi-problem` — a re-cut plan under a wrong problem is wasted work |
47
- | Only the **wording** of an `FR` is wrong, while the promise is the same | The skill already at work fixes it directly. See below |
48
- | A planning assumption turned out to be void | `wdi-decision`, which wraps `bmad-correct-course` |
49
-
50
- ## Step 2 — Wording is not a promise
51
-
52
- The split that ended three corrections in "reported but not fixed". `prd-guide.md` owns it; what this skill
53
- owns is refusing to run for the wrong half.
54
-
55
- | What changed | Who does it |
56
- |---|---|
57
- | A wrong cross-reference, a retired term, a word inconsistent with an `applied` decision — **the promise is the same** | Whichever skill is already at work. Memlog records it; **one** Revision History row per pass, never one per correction |
58
- | Scope, the proof of done, an `FR` retired or born | This skill, intent `update` |
59
-
60
- You MUST NOT accept a wording correction as an `update` run. Doing so puts a trivial fix behind a gate, and
61
- that is exactly how the three earlier ones were dropped.
62
-
63
- ## Step 3 — Dispatch
64
-
65
- Invoke `bmad-prd` with the detected intent, scoped to **one initiative**. Do not restate the rules to it —
66
- they arrive through `persistent_facts` and `doc_standards` in `_bmad/custom/bmad-prd.toml`.
67
-
68
- Name the brief and, for `update`, the existing PRD and every `applied` decision that reaches it. The skill
69
- globs its own default locations, which this project redirects.
70
-
71
- ## Step 4 — Land the requirements
72
-
73
- Each feature's **Realizes:** line cites `FR-N`/`NFR-N` ids. The template gives the statement, the proof
74
- of done, and the enforcer no home inside `prd.md` any more — write them straight into
75
- `.control/registry/requirements-<slug>.yaml` — the slug being this PRD's own folder name — on the id's own row, as part of producing this PRD. The `CAP` row goes in the same file: one feature is one capability, and a feature lives in exactly one PRD. This is
76
- landing, not editing `prd.md`: the same duty `wdi-problem` Step 4 carries for `BG-N`.
77
-
78
- An `update` run that adds or changes a promise lands the same way — the registry row changes, the PRD
79
- keeps citing the id, and Revision History (Step 6 below) records what changed for a reader.
80
-
81
- ## Step 5 — Verify
82
-
83
- | # | Check | Fails when |
84
- |---|---|---|
85
- | 1 | Home | Anything outside `.what/_prd/<initiative>/`, or a folder still named `ISI-slug-inisiatif` |
86
- | 2 | Ids allocated from the registry | `FR-1` restarted, or an id invented in prose |
87
- | 3 | Every `FR` names its `capability`; every `NFR` names its `goal` | `chain-links` has nothing to check |
88
- | 4 | Every `FR`'s statement and proof of done, every `NFR`'s statement and `enforced_by`, live only in `requirements-<slug>.yaml` | Full prose written in `prd.md` instead of, or as well as, the registry row from Step 4 |
89
- | 5 | Cross-Cutting NFRs (§6) and Constraints and Guardrails (§7) both present | An absent section reads as "not checked" |
90
- | 6 | No solution shape | A framework, a table, or a transport named in `prd.md` rather than in `addendum.md` |
91
- | 7 | §1 Why This Initiative states a delta against the brief's `Why`, not a restatement of it | The product's own vision written out again on the first PRD |
92
- | 8 | No Document Purpose, Glossary, Non-Goals, Open Questions, or Assumptions Index section | Any of the five appeared instead of being routed to its real home — see `prd-guide.md` |
93
- | 9 | One Revision History row for this run, written for someone not in the room | Zero rows, several rows, or a row that says "Updated §4.2" |
94
- | 10 | Memlog at `.control/memlog/prd-<slug>.md`, slug matching the folder | A `.memlog.md` appeared inside `.what/` — `--workspace` was used |
95
- | 11 | `bmad-review` ran through `doc_standards` on `prd.md` and `addendum.md` | It did not fire |
96
-
97
- Check 10 MUST be fixed immediately rather than reported. `memlog-home` rejects a memlog inside the corpus.
98
-
99
- ## Step 6 — `owns:`, and the collision it prevents
100
-
101
- A new or changed `FR` that claims write authority over a domain entity MUST be checked against `owns:` in
102
- `components.yaml`. An entity has exactly one owning Product Component; an `FR` from another PRD that needs to
103
- change it MUST point at the owner's `FR` rather than promising to write it itself. `entity-one-writer` checks this, and the
104
- collision has already happened once for real.
105
-
106
- Report a collision. You MUST NOT resolve it by widening one PRD's claim.
107
-
108
- ## Step 7 — Impact
109
-
110
- A changed promise changes what other documents can still claim. Check, and **report** — never edit.
111
-
112
- | Found | Where it goes |
113
- |---|---|
114
- | A `UC` realising an `FR` whose promise moved | `wdi-component` intent `behaviour`, or `wdi-blueprint` when the catalogue line itself changes |
115
- | A blueprint inventory row with nothing promising it any more | `wdi-blueprint` |
116
- | A contradiction with an `applied` decision | `wdi-decision` — a new `DEC-`, never an edit to one already applied |
117
- | A component born by this initiative | `wdi-init` intent `component` |
118
-
119
- Then run the change-control matrix in `delivery-flow-guide.md` and **report** which gates reopen. You MUST
120
- NOT reopen one yourself.
121
-
122
- ### Withdrawing a promise — the row stays
123
-
124
- A `BG` · `CAP` · `FR` · `NFR` the product stops promising is **marked, never deleted**. Deleting it is
125
- how a repo ended up with twelve `refs-resolve` findings: two capabilities were withdrawn by decision,
126
- their rows removed, and eight `DEC-` rows still named them — six of the eight having genuinely served
127
- them at the time, which `corpus-guide.md` forbids editing away.
128
-
129
- 1. The withdrawal is a **decision first**. Route to `wdi-decision`; you MUST NOT withdraw a promise on
130
- your own authority, and `withdrawn_by` needs that `DEC-` id to point at.
131
- 2. Then mark the row, in place, in its own `requirements-<slug>.yaml`:
132
- `status: withdrawn` and `withdrawn_by: DEC-NNN`. Everything else on the row is left as it was — it
133
- is a record of what was promised, not a draft.
134
- 3. Withdraw **down the chain in the same pass**: an `FR` under a withdrawn `CAP`, an `NFR` under it,
135
- a `UC` satisfying a withdrawn `FR`. `withdrawn-recorded` reports a live row left hanging off a
136
- withdrawn one, and that finding is the whole point — a half-withdrawn chain still promises half of
137
- something.
138
- 4. The id is **spent**. `id-allocated-once` counts a withdrawn row, so the number is never handed to
139
- anything else.
140
-
141
- What you MUST NOT do: delete the row, renumber around the gap, or edit a `DEC-` that served it.
142
- `corpus-guide.md` § *A withdrawn promise STAYS in the registry* owns the rule.
143
-
144
- ## Rules
145
-
146
- - You MUST NOT write a second PRD for an area that already has one. The reader test decides, and its answer
147
- is `update` far more often than it feels.
148
- - You MUST NOT open G2 on a PRD that has not been through check 11. Gate time is for deciding, not
149
- proofreading.
150
- - The gate reads `prd.md` and `EXPERIENCE.md` together. A PRD that passes while the experience side is
151
- missing has answered half of what G2 decides.
152
- - Every unresolved `[ASSUMPTION]` MUST be filed through `wdi-question` before the gate opens.
153
- - You MUST NOT raise `status:`. Status is a stage; the `reviewed:` block is an event, and `wdi-review` writes
154
- it.
155
- - When the PRD cannot promise what was asked, say so and stop. Route to `wdi-problem`; do not quietly narrow
156
- the ask.
157
-
158
- ## Output
159
-
160
- Intent dispatched · what the promise now is in one line · the requirements landed in Step 4 · the result of
161
- all eleven checks naming the failures · the `owns:` check · impact found and where it was routed · the gates
162
- the matrix names · open questions filed.
1
+ ---
2
+ name: wdi-product
3
+ description: Use at G2 Product — when a PRD is created or an existing promise changes. Two intents, prd and update. Checks position, dispatches bmad-prd, verifies the result against prd-guide.md, and lands the memlog. Never writes the PRD itself.
4
+ ---
5
+
6
+ # WDI Product
7
+
8
+ G2 decides **what is built, and how it feels to use.** `bmad-prd` writes the PRD.
9
+
10
+ This wrapper exists because `bmad-prd` was the only writer of a primary artifact in this method with no WDI
11
+ wrapper at all — so nothing checked its position, nothing verified its result against the guide, and nothing
12
+ landed its memlog where the next run would read it. Those three gaps were paid for at G2 every time.
13
+
14
+ You MUST NOT write or edit `prd.md` yourself. If a check fails, name what is missing and re-dispatch.
15
+
16
+ | Intent | When |
17
+ |---|---|
18
+ | `prd` | A functional area a reader would not think to look for in an existing PRD |
19
+ | `update` | Anything else — the default, and by a wide margin |
20
+
21
+ ## Inputs
22
+
23
+ | Source | What it answers |
24
+ |---|---|
25
+ | `.what/_product-brief/brief.md` | The problem, the primary user, the boundary the PRD MUST respect |
26
+ | `.what/_prd/*/prd.md` | Which initiatives already have a PRD, and what each already promises |
27
+ | `.control/registry/goals.yaml` | The `BG` this initiative serves |
28
+ | `.control/registry/requirements-*.yaml` | Every initiative's `FR`/`NFR`/`UJ`, so the next id continues the product's sequence |
29
+ | `.control/decisions/` | `applied` decisions the PRD MUST already reflect |
30
+ | `.constitution/method/document/prd-guide.md` | The rules the result is checked against |
31
+ | `.control/product-glossary.md` | Terms already fixed |
32
+
33
+ ## Step 1 — Position, and `update` is the default
34
+
35
+ The decision this skill exists for. The test is the **reader**, not the calendar:
36
+
37
+ > Would someone looking for this promise open an existing document?
38
+
39
+ Yes → `update`, however large the change. No → `prd`. A PRD MUST NOT be split because it grew long, and a
40
+ release is never a reason on its own. `prd-guide.md` owns the full table.
41
+
42
+ Three asks that are not this skill:
43
+
44
+ | Ask | Route |
45
+ |---|---|
46
+ | The problem itself has changed | `wdi-problem` — a re-cut plan under a wrong problem is wasted work |
47
+ | Only the **wording** of an `FR` is wrong, while the promise is the same | The skill already at work fixes it directly. See below |
48
+ | A planning assumption turned out to be void | `wdi-decision`, which wraps `bmad-correct-course` |
49
+
50
+ ## Step 2 — Wording is not a promise
51
+
52
+ The split that ended three corrections in "reported but not fixed". `prd-guide.md` owns it; what this skill
53
+ owns is refusing to run for the wrong half.
54
+
55
+ | What changed | Who does it |
56
+ |---|---|
57
+ | A wrong cross-reference, a retired term, a word inconsistent with an `applied` decision — **the promise is the same** | Whichever skill is already at work. Memlog records it; **one** Revision History row per pass, never one per correction |
58
+ | Scope, the proof of done, an `FR` retired or born | This skill, intent `update` |
59
+
60
+ You MUST NOT accept a wording correction as an `update` run. Doing so puts a trivial fix behind a gate, and
61
+ that is exactly how the three earlier ones were dropped.
62
+
63
+ ## Step 3 — Dispatch
64
+
65
+ Invoke `bmad-prd` with the detected intent, scoped to **one initiative**. Do not restate the rules to it —
66
+ they arrive through `persistent_facts` and `doc_standards` in `_bmad/custom/bmad-prd.toml`.
67
+
68
+ Name the brief and, for `update`, the existing PRD and every `applied` decision that reaches it. The skill
69
+ globs its own default locations, which this project redirects.
70
+
71
+ ## Step 4 — Land the requirements
72
+
73
+ Each feature's **Realizes:** line cites `FR-N`/`NFR-N` ids. The template gives the statement, the proof
74
+ of done, and the enforcer no home inside `prd.md` any more — write them straight into
75
+ `.control/registry/requirements-<slug>.yaml` — the slug being this PRD's own folder name — on the id's own row, as part of producing this PRD. The `CAP` row goes in the same file: one feature is one capability, and a feature lives in exactly one PRD. This is
76
+ landing, not editing `prd.md`: the same duty `wdi-problem` Step 4 carries for `BG-N`.
77
+
78
+ An `update` run that adds or changes a promise lands the same way — the registry row changes, the PRD
79
+ keeps citing the id, and Revision History (Step 6 below) records what changed for a reader.
80
+
81
+ ## Step 5 — Verify
82
+
83
+ | # | Check | Fails when |
84
+ |---|---|---|
85
+ | 1 | Home | Anything outside `.what/_prd/<initiative>/`, or a folder still named `ISI-slug-inisiatif` |
86
+ | 2 | Ids allocated from the registry | `FR-1` restarted, or an id invented in prose |
87
+ | 3 | Every `FR` names its `capability`; every `NFR` names its `goal` | `chain-links` has nothing to check |
88
+ | 4 | Every `FR`'s statement and proof of done, every `NFR`'s statement and `enforced_by`, live only in `requirements-<slug>.yaml` | Full prose written in `prd.md` instead of, or as well as, the registry row from Step 4 |
89
+ | 5 | Cross-Cutting NFRs (§6) and Constraints and Guardrails (§7) both present | An absent section reads as "not checked" |
90
+ | 6 | No solution shape | A framework, a table, or a transport named in `prd.md` rather than in `addendum.md` |
91
+ | 7 | §1 Why This Initiative states a delta against the brief's `Why`, not a restatement of it | The product's own vision written out again on the first PRD |
92
+ | 8 | No Document Purpose, Glossary, Non-Goals, Open Questions, or Assumptions Index section | Any of the five appeared instead of being routed to its real home — see `prd-guide.md` |
93
+ | 9 | One Revision History row for this run, written for someone not in the room | Zero rows, several rows, or a row that says "Updated §4.2" |
94
+ | 10 | Memlog at `.control/memlog/prd-<slug>.md`, slug matching the folder | A `.memlog.md` appeared inside `.what/` — `--workspace` was used |
95
+ | 11 | `bmad-review` ran through `doc_standards` on `prd.md` and `addendum.md` | It did not fire |
96
+
97
+ Check 10 MUST be fixed immediately rather than reported. `memlog-home` rejects a memlog inside the corpus.
98
+
99
+ ## Step 6 — `owns:`, and the collision it prevents
100
+
101
+ A new or changed `FR` that claims write authority over a domain entity MUST be checked against `owns:` in
102
+ `components.yaml`. An entity has exactly one owning Product Component; an `FR` from another PRD that needs to
103
+ change it MUST point at the owner's `FR` rather than promising to write it itself. `entity-one-writer` checks this, and the
104
+ collision has already happened once for real.
105
+
106
+ Report a collision. You MUST NOT resolve it by widening one PRD's claim.
107
+
108
+ ## Step 7 — Impact
109
+
110
+ A changed promise changes what other documents can still claim. Check, and **report** — never edit.
111
+
112
+ | Found | Where it goes |
113
+ |---|---|
114
+ | A `UC` realising an `FR` whose promise moved | `wdi-component` intent `behaviour`, or `wdi-blueprint` when the catalogue line itself changes |
115
+ | A blueprint inventory row with nothing promising it any more | `wdi-blueprint` |
116
+ | A contradiction with an `applied` decision | `wdi-decision` — a new `DEC-`, never an edit to one already applied |
117
+ | A component born by this initiative | `wdi-init` intent `component` |
118
+
119
+ Then run the change-control matrix in `delivery-flow-guide.md` and **report** which gates reopen. You MUST
120
+ NOT reopen one yourself.
121
+
122
+ ### Withdrawing a promise — the row stays
123
+
124
+ A `BG` · `CAP` · `FR` · `NFR` the product stops promising is **marked, never deleted**. Deleting it is
125
+ how a repo ended up with twelve `refs-resolve` findings: two capabilities were withdrawn by decision,
126
+ their rows removed, and eight `DEC-` rows still named them — six of the eight having genuinely served
127
+ them at the time, which `corpus-guide.md` forbids editing away.
128
+
129
+ 1. The withdrawal is a **decision first**. Route to `wdi-decision`; you MUST NOT withdraw a promise on
130
+ your own authority, and `withdrawn_by` needs that `DEC-` id to point at.
131
+ 2. Then mark the row, in place, in its own `requirements-<slug>.yaml`:
132
+ `status: withdrawn` and `withdrawn_by: DEC-NNN`. Everything else on the row is left as it was — it
133
+ is a record of what was promised, not a draft.
134
+ 3. Withdraw **down the chain in the same pass**: an `FR` under a withdrawn `CAP`, an `NFR` under it,
135
+ a `UC` satisfying a withdrawn `FR`. `withdrawn-recorded` reports a live row left hanging off a
136
+ withdrawn one, and that finding is the whole point — a half-withdrawn chain still promises half of
137
+ something.
138
+ 4. The id is **spent**. `id-allocated-once` counts a withdrawn row, so the number is never handed to
139
+ anything else.
140
+
141
+ What you MUST NOT do: delete the row, renumber around the gap, or edit a `DEC-` that served it.
142
+ `corpus-guide.md` § *A withdrawn promise STAYS in the registry* owns the rule.
143
+
144
+ ## Rules
145
+
146
+ - You MUST NOT write a second PRD for an area that already has one. The reader test decides, and its answer
147
+ is `update` far more often than it feels.
148
+ - You MUST NOT open G2 on a PRD that has not been through check 11. Gate time is for deciding, not
149
+ proofreading.
150
+ - The gate reads `prd.md` and the experience together — `.what/experience.md` for what holds across components, the run's `EXPERIENCE.md` for the rest. A PRD that passes while the experience side is
151
+ missing has answered half of what G2 decides.
152
+ - Every unresolved `[ASSUMPTION]` MUST be filed through `wdi-question` before the gate opens.
153
+ - You MUST NOT raise `status:`. Status is a stage; the `reviewed:` block is an event, and `wdi-review` writes
154
+ it.
155
+ - When the PRD cannot promise what was asked, say so and stop. Route to `wdi-problem`; do not quietly narrow
156
+ the ask.
157
+
158
+ ## Step 8 — Record the gate
159
+
160
+ Ask the owner whether G2 passed. Write `G2` into `gates_passed` only on their explicit *yes* —
161
+ `delivery-flow-guide.md` § *Recording a gate that passed*. `wdi-init` intent `component` reads it next.
162
+
163
+ ## Output
164
+
165
+ Intent dispatched · what the promise now is in one line · the requirements landed in Step 4 · the result of
166
+ all eleven checks naming the failures · the `owns:` check · impact found and where it was routed · the gates
167
+ the matrix names · open questions filed · whether G2 was recorded.
@@ -6,6 +6,8 @@ disable-model-invocation: true
6
6
 
7
7
  # WDI Prune or Archive
8
8
 
9
+ > **Typed by the owner, or not at all.** Run this skill only when the person typed `wdi-prune-or-archive` — `/wdi-prune-or-archive` or this host's own syntax for it — in the turn that is running. Reached any other way (a description that looked relevant, another skill, a subagent), stop and name it instead. Hosts that can hold a skill to manual-only already do; on the others, this line is the lock.
10
+
9
11
  Standalone housekeeping skill for closed specifications. Moves completed spec directories to
10
12
  `.archive/specs/<spec-folder>/` or prunes completed tickets from disk using `lifecycle.py`, while
11
13
  strictly preserving requirement traceability and RTM metadata in `.control/registry/specs.yaml`.