qfai 1.9.2 → 1.10.0

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 (123) hide show
  1. package/README.md +48 -2
  2. package/assets/init/.qfai/assistant/agents/acceptance-test-engineer.md +1 -0
  3. package/assets/init/.qfai/assistant/agents/backend-engineer.md +1 -0
  4. package/assets/init/.qfai/assistant/agents/completion-reviewer.md +13 -2
  5. package/assets/init/.qfai/assistant/agents/delivery-planner.md +9 -0
  6. package/assets/init/.qfai/assistant/agents/frontend-engineer.md +1 -0
  7. package/assets/init/.qfai/assistant/agents/implementation-reviewer.md +6 -0
  8. package/assets/init/.qfai/assistant/agents/orchestrator.md +2 -2
  9. package/assets/init/.qfai/assistant/agents/qa-gatekeeper.md +96 -3
  10. package/assets/init/.qfai/assistant/agents/test-design-analyst.md +20 -3
  11. package/assets/init/.qfai/assistant/catalog/cli-ux-guidelines.md +2 -2
  12. package/assets/init/.qfai/assistant/catalog/spec_required_files.json +2 -1
  13. package/assets/init/.qfai/assistant/catalog/test-layers.md +355 -14
  14. package/assets/init/.qfai/assistant/catalog/worklog-entry.schema.md +165 -0
  15. package/assets/init/.qfai/assistant/constitution/communication.md +1 -1
  16. package/assets/init/.qfai/assistant/constitution/drift-protocol.md +304 -10
  17. package/assets/init/.qfai/assistant/constitution/quality.md +35 -5
  18. package/assets/init/.qfai/assistant/constitution/requirements-decomposition.md +37 -0
  19. package/assets/init/.qfai/assistant/constitution/shared-skill-delegation-baseline.md +244 -8
  20. package/assets/init/.qfai/assistant/constitution/shared-skill-operating-baseline.md +122 -5
  21. package/assets/init/.qfai/assistant/constitution/workflow.md +53 -7
  22. package/assets/init/.qfai/assistant/manifest/agent-catalog.yml +316 -945
  23. package/assets/init/.qfai/assistant/manifest/agent-routing.yml +50 -4
  24. package/assets/init/.qfai/assistant/manifest/review-profiles.yml +9 -0
  25. package/assets/init/.qfai/assistant/process/migrations/v1.4.27-atdd-alignment.md +1 -1
  26. package/assets/init/.qfai/assistant/skills/qfai-atdd/SKILL.md +61 -21
  27. package/assets/init/.qfai/assistant/skills/qfai-atdd/references/test-case-depth-checklist.md +23 -4
  28. package/assets/init/.qfai/assistant/skills/qfai-configure/SKILL.md +15 -7
  29. package/assets/init/.qfai/assistant/skills/qfai-discussion/SKILL.md +8 -4
  30. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/design-md-brand-catalog.md +2 -2
  31. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/discussion-completion-matrix.md +17 -7
  32. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/rcp_footer.md +10 -4
  33. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/review-cycle-playbook.md +1 -1
  34. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui-bearing-playbook.md +4 -4
  35. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui_ux/review_audit_playbook.md +1 -1
  36. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui_ux/trend_scan_playbook.md +1 -1
  37. package/assets/init/.qfai/assistant/skills/qfai-discussion/references/ui_ux_best_practices.md +17 -7
  38. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/01_Context.md +1 -1
  39. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/02_Inception-Deck.md +1 -1
  40. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/03_Story-Workshop.md +11 -3
  41. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/05_Scope.md +5 -2
  42. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/07_NFR.md +1 -1
  43. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/09_Constraints.md +7 -4
  44. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/10_Policy.md +1 -1
  45. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/11_OQ-Register.md +1 -1
  46. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/12_OQ-Resolution-Log.md +1 -1
  47. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/14_Review-Request.md +14 -7
  48. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/99_delta.md +1 -1
  49. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/review/Rxx_reviewer.md +16 -7
  50. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/review/review_request.md +9 -6
  51. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/uiux/40_screen_contracts.md +3 -2
  52. package/assets/init/.qfai/assistant/skills/qfai-discussion/templates/uiux/50_review_input_bundle.md +4 -2
  53. package/assets/init/.qfai/assistant/skills/qfai-implement/SKILL.md +250 -128
  54. package/assets/init/.qfai/assistant/skills/qfai-implement/references/change-request-reset.md +93 -0
  55. package/assets/init/.qfai/assistant/skills/qfai-implement/references/checkpoint-verification.md +106 -0
  56. package/assets/init/.qfai/assistant/skills/qfai-implement/references/cross-spec-ownership.md +73 -0
  57. package/assets/init/.qfai/assistant/skills/qfai-implement/references/evidence-revision.md +77 -0
  58. package/assets/init/.qfai/assistant/skills/qfai-implement/references/execution-ledger.md +303 -0
  59. package/assets/init/.qfai/assistant/skills/qfai-implement/references/final-checklist.md +19 -0
  60. package/assets/init/.qfai/assistant/skills/qfai-implement/references/finding-classification.md +49 -0
  61. package/assets/init/.qfai/assistant/skills/qfai-implement/references/ledger-preconditions.md +55 -0
  62. package/assets/init/.qfai/assistant/skills/qfai-implement/references/oracle-strength.md +81 -0
  63. package/assets/init/.qfai/assistant/skills/qfai-implement/references/parallelization-policy.md +235 -0
  64. package/assets/init/.qfai/assistant/skills/qfai-implement/references/red-admissibility.md +91 -0
  65. package/assets/init/.qfai/assistant/skills/qfai-implement/references/red-not-observable.md +70 -0
  66. package/assets/init/.qfai/assistant/skills/qfai-implement/references/relevant-test-suite.md +87 -0
  67. package/assets/init/.qfai/assistant/skills/qfai-implement/references/review-artifact-layout.md +31 -0
  68. package/assets/init/.qfai/assistant/skills/qfai-implement/references/round-evidence.md +102 -0
  69. package/assets/init/.qfai/assistant/skills/qfai-implement/references/selector-granularity.md +24 -0
  70. package/assets/init/.qfai/assistant/skills/qfai-implement/references/volume-policy.md +149 -0
  71. package/assets/init/.qfai/assistant/skills/qfai-prototyping/SKILL.md +35 -18
  72. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/evidence-requirements.md +1 -1
  73. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/generator-prompt.md +106 -7
  74. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/handoff.md +40 -11
  75. package/assets/init/.qfai/assistant/skills/qfai-prototyping/references/iteration-loop.md +48 -1
  76. package/assets/init/.qfai/assistant/skills/qfai-prototyping/templates/DESIGN.md.sample +6 -0
  77. package/assets/init/.qfai/assistant/skills/qfai-sdd/SKILL.md +82 -22
  78. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/contract-artifact-rules.md +148 -0
  79. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/rcp_footer.md +10 -4
  80. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/review-cycle-playbook.md +1 -1
  81. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-execution-playbook.md +4 -2
  82. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-phase-checklists.md +18 -0
  83. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-quality-gate.md +44 -3
  84. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/sdd-triage.md +60 -7
  85. package/assets/init/.qfai/assistant/skills/qfai-sdd/references/spec-traceability-rules.md +157 -5
  86. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/change-request.md +125 -0
  87. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/contracts/db-contract.sample.sql +6 -0
  88. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/evidence/sdd-spec.md +92 -0
  89. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/01_Objective.md +27 -0
  90. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/02_Initiative.md +30 -0
  91. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/05_Contracts.md +16 -6
  92. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/06_Glossary.md +19 -0
  93. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/07_Constraints.md +25 -0
  94. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/08_Decisions.md +22 -2
  95. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/_policies/11_Slice-Policy.md +37 -10
  96. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/02_User-stories.md +20 -0
  97. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/03_Acceptance-Criteria.md +19 -0
  98. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/04_Business-Rules.md +32 -3
  99. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/06_Test-Cases.md +56 -0
  100. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/07_Decisions.md +29 -2
  101. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/10_Plan.md +41 -0
  102. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/16_Traceability-ledger.md +58 -0
  103. package/assets/init/.qfai/assistant/skills/qfai-sdd/templates/specs/spec/tdd/test-list.md +56 -0
  104. package/assets/init/.qfai/assistant/skills/qfai-verify/SKILL.md +57 -102
  105. package/assets/init/.qfai/assistant/skills/qfai-verify/references/articles.md +24 -0
  106. package/assets/init/.qfai/assistant/skills/qfai-verify/references/context-load.md +22 -0
  107. package/assets/init/.qfai/assistant/skills/qfai-verify/references/verify-output-contract.md +48 -0
  108. package/assets/init/.qfai/assistant/skills/qfai-verify/templates/verify-evidence.md +48 -0
  109. package/assets/init/.qfai/assistant/skills/web-research/SKILL.md +25 -7
  110. package/assets/init/.qfai/waivers.yml +11 -5
  111. package/assets/init/root/DESIGN.md +6 -0
  112. package/assets/init/root/qfai.config.yaml +15 -12
  113. package/dist/cli/index.cjs +11023 -7139
  114. package/dist/cli/index.cjs.map +1 -1
  115. package/dist/cli/index.mjs +10963 -7080
  116. package/dist/cli/index.mjs.map +1 -1
  117. package/dist/index.cjs +8782 -5257
  118. package/dist/index.cjs.map +1 -1
  119. package/dist/index.d.cts +280 -8
  120. package/dist/index.d.ts +280 -8
  121. package/dist/index.mjs +11776 -8264
  122. package/dist/index.mjs.map +1 -1
  123. package/package.json +18 -19
@@ -92,10 +92,20 @@ forms caught:
92
92
 
93
93
  - Inline `border-radius: 12px` / `border-radius: 0.5rem`.
94
94
  - Tailwind arbitrary `rounded-[13px]`, `rounded-[0.5rem]`.
95
- - Tailwind scale aliases (`rounded`, `rounded-sm`, `rounded-md`,
96
- `rounded-lg`, `rounded-xl`, `rounded-2xl`, `rounded-3xl`,
97
- `rounded-full`, `rounded-none`) — all resolve to Tailwind defaults,
98
- not `DESIGN.md` tokens.
95
+ - Tailwind scale aliases with **no** `DESIGN.md.visual.radius` key of
96
+ the same name: bare `rounded` (Tailwind's `DEFAULT`), `rounded-xl`,
97
+ `rounded-2xl`, `rounded-3xl`, `rounded-none`. These resolve to
98
+ Tailwind defaults, not `DESIGN.md` tokens.
99
+ - `rounded-sm` / `rounded-md` / `rounded-lg` / `rounded-full` are
100
+ **allowed only in an iter whose own envelope re-binds that name**.
101
+ The schema's `visual.radius` keys are exactly `sm|md|lg|full`, and
102
+ the mandatory `theme.extend.borderRadius` injection re-binds those
103
+ four names to the `DESIGN.md` tokens — but the gate verifies that in
104
+ the iter's html rather than assuming it. An iter that omits the
105
+ envelope, drops a key from the `borderRadius` map, or binds it to a
106
+ different value renders Tailwind's default, and the alias is flagged
107
+ exactly as before. Same for the side/corner prefixes
108
+ (`rounded-t-md`, `rounded-tl-lg`, …).
99
109
 
100
110
  ### 4. box-shadow literal ban (including rgba color slot)
101
111
 
@@ -105,9 +115,23 @@ Authored forms caught:
105
115
 
106
116
  - Inline `box-shadow: 0 1px 2px rgba(15,23,42,0.05)`.
107
117
  - Tailwind arbitrary `shadow-[0_4px_6px_rgba(0,0,0,0.1)]`.
108
- - Tailwind scale aliases (`shadow`, `shadow-sm`, `shadow-md`,
109
- `shadow-lg`, `shadow-xl`, `shadow-2xl`, `shadow-inner`,
110
- `shadow-none`, `drop-shadow-*`).
118
+ - Tailwind scale aliases with **no** `DESIGN.md.visual.shadow` key of
119
+ the same name: bare `shadow` (Tailwind's `DEFAULT`), `shadow-xl`,
120
+ `shadow-2xl`, `shadow-inner`, `shadow-none`.
121
+ - Every `drop-shadow-<alias>` form, including `drop-shadow-sm|md|lg` —
122
+ the alias resolves through `theme.dropShadow`, which the mandatory
123
+ envelope does not inject, so it renders a Tailwind default no matter
124
+ what `visual.shadow` declares. The arbitrary form
125
+ `drop-shadow-[...]` carries its own literal and is judged like any
126
+ other arbitrary value: compliant when the literal is one of the
127
+ `visual.shadow` tokens, drift otherwise.
128
+ - `shadow-sm` / `shadow-md` / `shadow-lg` are **allowed only in an iter
129
+ whose own envelope re-binds that name**. The schema's `visual.shadow`
130
+ keys are exactly `sm|md|lg`, and the mandatory
131
+ `theme.extend.boxShadow` injection re-binds those three names to the
132
+ `DESIGN.md` tokens — but the gate verifies that in the iter's html
133
+ rather than assuming it, so an iter with a missing, incomplete, or
134
+ overwritten `boxShadow` map still has its aliases flagged.
111
135
 
112
136
  ### Safelisted CSS-wide keywords
113
137
 
@@ -151,6 +175,81 @@ The generator MUST express every styled surface as one of:
151
175
  - One self-contained HTML file; embedded CSS / JS minimal.
152
176
  - All declared spec screens reachable; loading / empty / error /
153
177
  success states representable.
178
+ - The compliance gate reports **one finding per distinct offending value**, not
179
+ one per occurrence: a single drifting token repeated across N screens is one
180
+ entry in `designMdViolations[]`. Fix the value once and the finding clears —
181
+ do not expect the count to track the number of places it appears.
182
+
183
+ ## Output layout — two trees, two shapes
184
+
185
+ There are two directory trees and they are NOT interchangeable. The
186
+ generator writes to exactly one of them.
187
+
188
+ | Tree | Shape | Written by | Read by |
189
+ | ------------------------------------- | -------------------------------------------------------- | ---------------------------------------- | ----------------------------------------------- |
190
+ | `.qfai/prototypes/iter-NN/` | one `index.html` | **the generator** (you) | `--auto-serve`, the operator, `/qfai-implement` |
191
+ | `.qfai/evidence/prototyping/iter-NN/` | `<screenId>.html` + `.png`, one pair per declared screen | `npx qfai prototyping iterate --capture` | `npx qfai prototyping certify`, the reviewer |
192
+
193
+ **The generator never writes the evidence tree.** The `--capture` step
194
+ performs the fan-out: it drives a browser to each declared screen's
195
+ contract `route`, and writes one HTML snapshot plus one screenshot per
196
+ screen into `.qfai/evidence/prototyping/iter-NN/`. `certify` reads only
197
+ that tree and hard-fails with "missing HTML for N declared screen(s)"
198
+ when a declared screen has no snapshot there.
199
+
200
+ ### N declared screens, one file
201
+
202
+ A spec declaring N screens is satisfied by **one** `index.html`
203
+ containing N client-side routes — not N files. Each declared screen
204
+ must be reachable at its own contract `route`, so the capture step can
205
+ navigate to it and snapshot it independently. This is what "all
206
+ declared spec screens reachable" above means operationally.
207
+
208
+ **Which routing shapes the capture step can actually reach.** Capture
209
+ navigates to `new URL(<contract route>, <target url>)` and treats any
210
+ HTTP status >= 400 as a failed screen, writing no evidence. What the
211
+ target server does with that URL therefore decides the routing shape:
212
+
213
+ - `--auto-serve` starts the built-in static file server. It resolves
214
+ every non-root URL to a real file under the served directory and
215
+ returns 404 when there is none — it has no SPA fallback. With it,
216
+ declare **hash routes** (`/#/settings`): the browser never sends the
217
+ fragment, so the server always serves `index.html` and the shell
218
+ routes client-side.
219
+ - **Path routes** (`/settings`) and any `history.pushState` shell need
220
+ a server that rewrites unknown paths to `index.html`. Run one
221
+ yourself and pass `--target-url`; they will 404 under `--auto-serve`.
222
+ - Per-screen files (`--emit-skeletons`, below) are a third option, but
223
+ only if the contract `route` names the file: `--emit-skeletons` writes
224
+ `<screenId>.html`, and the static server resolves a URL to a literal
225
+ path — it does not append `.html`, so a `/settings` route 404s while
226
+ `/settings.html` serves. `htmlSourceCopy` does not help here: it runs
227
+ after the capture has already navigated successfully, so it cannot
228
+ rescue a route the server could not resolve.
229
+
230
+ Pick the routing shape to match the server you will capture against,
231
+ and keep the contract `route` values consistent with it.
232
+
233
+ Opt-in **seed aid**, not an alternative output shape:
234
+ `npx qfai prototyping iterate --emit-skeletons` (cycle 0 only) writes one
235
+ placeholder `.qfai/prototypes/iter-00/<screenId>.html` per declared
236
+ screen, and the `htmlSourceCopy` capture option likewise operates on
237
+ per-screen files. Neither writes an `index.html`.
238
+
239
+ An accepted iteration must still carry `iter-NN/index.html`: Handoff
240
+ below copies it to `.qfai/prototypes/final/index.html`, and without it
241
+ `/qfai-implement` has nothing to read. So consolidate the skeletons into
242
+ the single-file envelope before the loop converges — including when a
243
+ cycle-0 skeleton set scores well enough that cycle 1 would otherwise
244
+ accept it as it stands. If you keep the per-screen files because your
245
+ capture routing needs them, author `index.html` alongside them so the
246
+ accepted iteration carries both.
247
+
248
+ ### Handoff
249
+
250
+ `.qfai/prototypes/final/index.html` is a copy of the accepted
251
+ `iter-NN/index.html` and is the deliverable `/qfai-implement` reads.
252
+ It is not a certify input; certify never opens the `prototypes/` tree.
154
253
 
155
254
  ## Cycle 0 (seed)
156
255
 
@@ -4,9 +4,19 @@
4
4
 
5
5
  `.qfai/prototypes/iter-<final>/index.html` — the final accepted
6
6
  iteration HTML. The "final" iter is whichever iteration was the latest
7
- when `qfai prototyping iterate` returned exit 64 (convergence) or 65
7
+ when `npx qfai prototyping iterate` returned exit 64 (convergence) or 65
8
8
  (max-iterations).
9
9
 
10
+ This is the **authoring** artifact — one self-contained file with one
11
+ client-side route per declared screen, written by the generator. It is
12
+ a distinct tree from the **capture** artifacts at
13
+ `.qfai/evidence/prototyping/iter-<final>/<screenId>.{html,png}`, which
14
+ `npx qfai prototyping iterate --capture` fans out one pair per declared
15
+ screen. Handoff mirrors the authoring artifact; `npx qfai prototyping
16
+ certify` gates on the capture artifacts and never opens the
17
+ `prototypes/` tree. Both must exist before handoff can complete: see
18
+ "Output layout" in `references/generator-prompt.md`.
19
+
10
20
  `DESIGN.md` (root) and `.qfai/contracts/design/DESIGN.md.lock.yaml`
11
21
  remain the brand SSOT through handoff.
12
22
 
@@ -57,16 +67,35 @@ the SSOT for brand identity. There is no preserve / adapt / copy split.
57
67
 
58
68
  ## Cert
59
69
 
60
- Order is load-bearing: `qfai prototyping certify` requires
61
- `.qfai/output/validate.json` (with `counts.error === 0`) and
62
- `.qfai/output/verify.json` (with `status === "PASS"`) to be present
63
- on disk before it will seal the certificate. Run the gates in this
64
- order, every time:
65
-
66
- 1. `qfai validate --profile prototyping --fail-on error` — writes
67
- `.qfai/output/validate.json`.
68
- 2. `/qfai-verify` — writes `.qfai/output/verify.json`.
69
- 3. `qfai prototyping certify` — produces
70
+ Order is load-bearing: `npx qfai prototyping certify` requires the configured
71
+ validate report (with `counts.error === 0`) and `.qfai/report/verify.json`
72
+ (with `status === "PASS"`) to be present on disk before it will seal the
73
+ certificate.
74
+
75
+ The two reads are not the same shape. The **validate** report is read from
76
+ whatever `output.validateJsonPath` names — one location, no fallback. Only
77
+ **`verify.json`** is canonical-first: it falls back to the legacy
78
+ `.qfai/output/verify.json` and prints a migration note when it does. That
79
+ fallback fires only when the canonical file is **absent** — a canonical file
80
+ that exists but is unparseable, non-object, or unreadable aborts certify with a
81
+ non-zero exit instead, so a leftover legacy `status: "PASS"` can never certify a
82
+ run whose real gate result was never readable. A canonical file that is missing
83
+ in both locations is reported as missing, not as a failing status.
84
+
85
+ Run the gates in this order, every time:
86
+
87
+ 1. `npx qfai validate --profile prototyping --fail-on error` — writes the path
88
+ configured at `output.validateJsonPath` (default
89
+ `.qfai/report/validate.json`), not a fixed `.qfai/output/` literal.
90
+ 2. `/qfai-verify` — writes `.qfai/report/verify.json` with
91
+ `status: "PASS"` and `scope: "prototyping"`. Certify accepts no
92
+ other scope: `atdd` / `implement` / `full` are refused by the
93
+ option-B phase-isolation contract, and a `full` run at this point
94
+ necessarily fails the stage-5 ATDD traceability rules
95
+ (`QFAI-ATDD-111/112/113`). The field list and the `scope` enum are
96
+ specified under "Verify Output Contract" in
97
+ `.qfai/assistant/skills/qfai-verify/SKILL.md`.
98
+ 3. `npx qfai prototyping certify` — produces
70
99
  `.qfai/evidence/prototyping/completion-certificate.json`. The
71
100
  certificate includes `designMdPath` + `designMdSha256` for the
72
101
  locked brand identity. Use `certify --check` to verify digests
@@ -21,7 +21,7 @@ one-line summary at each iter's end.
21
21
 
22
22
  ## Stop conditions (deterministic)
23
23
 
24
- Exit codes for `qfai prototyping iterate --cycle <n+1>`:
24
+ Exit codes for `npx qfai prototyping iterate --cycle <n+1>`:
25
25
 
26
26
  - `0` — continue.
27
27
  - `64` — convergence: latest iter has all 4 UX axes
@@ -73,3 +73,50 @@ proceed unless the two match; the lock value is then cached into
73
73
  diverging exits with `2`. To change brand identity mid-project, edit
74
74
  `DESIGN.md`, rerun `/qfai-sdd` Phase 0 to refreeze the lock, and
75
75
  start `/qfai-prototyping` from cycle 0.
76
+
77
+ ## Sealed loop
78
+
79
+ A loop is **sealed** once `prototyping.json` records
80
+ `stopReason: "axes-exceptional"` together with an `acceptedIterationIndex`.
81
+ That is the converged state — the only one `--check-convergence` reports as
82
+ converged and the only one `npx qfai prototyping certify` will seal. On a sealed
83
+ loop `npx qfai prototyping iterate --cycle N` refuses with exit `2` for any `N`
84
+ greater than the accepted index, and writes nothing — no `iter-NN/` directory
85
+ is created. That refusal is deliberate: such a directory is stale by
86
+ construction, and the stale-iteration-directory check in
87
+ `npx qfai prototyping certify` hard-fails on it.
88
+
89
+ `license-verify-fail` and `input-error` do **not** seal the loop. They are
90
+ states you are expected to fix and retry, so the same cycle can be re-run and
91
+ the fix verified.
92
+
93
+ `max-iterations` does not seal the loop either, but it is **not** retryable in
94
+ the same way: the recorded `iter-09` remains the last iteration, so
95
+ `shouldStop()` returns `max-iterations` again and every `--cycle N >= 1` exits
96
+ `65` before any path is assigned. Its only recovery is the cycle-0 reset
97
+ below.
98
+
99
+ Two paths remain open on a sealed loop:
100
+
101
+ - **Seal it** — run `npx qfai prototyping certify`. This is the normal
102
+ next step after convergence.
103
+ - **Start over** — run `npx qfai prototyping iterate --cycle 0
104
+ --target-url <url> --force`. Cycle 0 is a hard reset and is never
105
+ refused by the sealed-loop guard; it also deletes stale `iter-NN`
106
+ directories. `--force` is required, not optional: a converged loop
107
+ always has an `iter-00`, and the cycle-0 destructive-rerun gate
108
+ refuses to overwrite it without the flag. With it, `iter-00` is moved
109
+ to `iter-00.backup-<ISO>` before the reset, so the prior loop is
110
+ recoverable.
111
+
112
+ Re-running the accepted cycle itself (`--cycle <acceptedIterationIndex>`)
113
+ is not refused by the sealed-loop guard — that would be a redo of recorded
114
+ work, not an extension past the seal — but it does not re-run the cycle
115
+ either: the convergence gate reads the same recorded iteration, reports
116
+ `axes-exceptional` and exits `64` without assigning paths or writing
117
+ anything. Treat it as a state read, and prefer
118
+ `npx qfai prototyping iterate --check-convergence`, which reports the recorded
119
+ `stopReason` / `acceptedIterationIndex` without the exit-code ambiguity.
120
+
121
+ If an `iter-NN` directory was created that should not have been, delete
122
+ it before running certify; there is no reserved quarantine name.
@@ -43,6 +43,12 @@ accessibility:
43
43
  motion: "respect-reduced-motion"
44
44
  ---
45
45
 
46
+ <!-- QFAI-SAMPLE-DESIGN-MD: this is the qfai sample brand, NOT your product's brand.
47
+ Replace the front-matter and the body below with this product's own brand SSOT
48
+ (`/qfai-discussion` emits the draft), then DELETE this comment. While this comment
49
+ is present `qfai validate` reports QFAI-DCON-034 and /qfai-sdd Phase 0 refuses to
50
+ freeze the file's sha256 as the project's brand contract. -->
51
+
46
52
  # Brand Philosophy
47
53
 
48
54
  Acme Ledger is a calm, confident financial comparison surface. The interface
@@ -2,7 +2,7 @@
2
2
  name: qfai-sdd
3
3
  title: QFAI SDD Unified (Triage/Outline/Slice/Plan/Delta)
4
4
  description: "Triage incoming requirements against existing specs, then create or update layered SDD artifacts (_policies + spec-*) in one workflow."
5
- argument-hint: "[<spec-id-or-name>] [--auto]"
5
+ argument-hint: "[<spec-id-or-name>] [--contract <CON-ID>] [--auto]"
6
6
  allowed-tools: [Read, Glob, Write, TodoWrite, Task, Bash]
7
7
  roles:
8
8
  [
@@ -37,6 +37,8 @@ QFAI Skill Body (SSOT)
37
37
  ```
38
38
  Stage 0 Preflight -> Stage 1 Triage -> Phase 0 Contracts-first
39
39
  -> Phase 1 Outline -> Phase 2 Slice (per spec)
40
+ -> Phase 2b Seed tdd/test-list.md (per spec)
41
+ -> Phase 2c Obligation reconciliation (per spec)
40
42
  -> Phase 3 Plan finalize -> Phase 4 Delta update
41
43
  ```
42
44
 
@@ -114,7 +116,7 @@ Follow `.qfai/assistant/constitution/shared-skill-delegation-baseline.md`.
114
116
 
115
117
  ### Delegation Failure (Hard Stop)
116
118
 
117
- - Do not simulate roles. If the first required delegation fails, stop the stage and report remediation.
119
+ - Do not simulate roles. Classify the failure per the baseline taxonomy first: `unavailable` stops the stage with a remediation report; `saturated` uses the bounded retry branch and keeps the stage open.
118
120
 
119
121
  Stage minimum roles:
120
122
 
@@ -123,7 +125,13 @@ Stage minimum roles:
123
125
  - `test-design-analyst` drafts traceability, examples, and test-design.
124
126
  - `product-experience-architect` is added when the target is UI-bearing.
125
127
  - `orchestrator` integrates outputs and presents for confirmation; never drafts the primary artifact and never self-approves.
126
- - `completion-reviewer` is delegated independently. Required field: `Status (PASS/REVISE)`.
128
+ - `completion-reviewer` is delegated independently. Required field: `Status (PASS/REVISE/PENDING)`; `PENDING` marks an unrun gate and never counts as `PASS`.
129
+
130
+ Author↔reviewer separation (MUST): drafting roles and reviewing roles above are routed from one
131
+ list, but no sub-agent may review an artifact it drafted or edited in this run. `independent` is
132
+ defined normatively in
133
+ `.qfai/assistant/constitution/shared-skill-delegation-baseline.md#definition-independent-reviewer-normative`,
134
+ and every reviewer response must carry its `Authored/edited under review:` attestation.
127
135
 
128
136
  Reviewer routing is fixed by `.qfai/assistant/manifest/agent-routing.yml` and `.qfai/assistant/manifest/review-profiles.yml`.
129
137
 
@@ -132,7 +140,7 @@ Reviewer routing is fixed by `.qfai/assistant/manifest/agent-routing.yml` and `.
132
140
  - Default: `completion-reviewer`.
133
141
  - Conditional: `architecture-reviewer` (structural / contract / CLI), `product-surface-reviewer` (UI-bearing), `qa-gatekeeper` (validate / coverage / runtime / prototyping evidence affected).
134
142
  - Drift Protocol compliance is mandatory; reviewers MUST verify no rejected option was reintroduced and no drift from prior decisions.
135
- - Test-layer policy is checked against `.qfai/assistant/catalog/test-layers.md`; layer pinning is enforced (US→E2E, TC→Integration, CON-API→API).
143
+ - Test-layer policy is checked against `.qfai/assistant/catalog/test-layers.md`; annotation routing is enforced by `.qfai/assistant/catalog/test-layers.md#annotation-routing-is-by-id-type-not-by-level`, which owns the US / TC / CON-API mapping. A `TC-*`'s `Level` is **not** a constant: derive it per TC with `.qfai/assistant/catalog/test-layers.md#layer-derivation-procedure-normative`, keeping the row inside L1–L3 and defaulting to Integration only when the derivation is genuinely indeterminate. A missing `tests/integration/**` trace never rewrites a derived `Level`.
136
144
  - Coverage floors / ratios are planning signals, not gates; reviewers must not block on them.
137
145
  - Do not declare DONE until all routed blocking reviewers return `PASS`.
138
146
 
@@ -141,6 +149,10 @@ Reviewer routing is fixed by `.qfai/assistant/manifest/agent-routing.yml` and `.
141
149
  - Without argument: target all capabilities in `_policies/03_Capabilities.md`.
142
150
  - Run Contracts-first and Outline once per batch.
143
151
  - Delegate Slice in parallel per spec.
152
+ - Parallel delegation here is bound by the stage-independent Concurrency rules in
153
+ `.qfai/assistant/constitution/workflow.md#concurrency-stage-independent-mandatory`:
154
+ worktree separation, or the declared degraded mode, plus mandatory commit
155
+ scoping (`git add <paths>`; never `git add -A` / `git add .` / `git commit -a`).
144
156
  - Validate gate and Review gate run once at batch tail after all target specs are integrated.
145
157
 
146
158
  ## Work Orders Summary
@@ -151,7 +163,7 @@ Per-spec evidence at `.qfai/evidence/sdd-<spec-id>.md` is mandatory and MUST inc
151
163
 
152
164
  - Follow `references/review-cycle-playbook.md`.
153
165
  - Footer SSOT: `references/rcp_footer.md`.
154
- - Allowed reviewer verdicts: `PASS` and `FAIL`.
166
+ - Allowed in-flight reviewer verdicts: `PASS` and `REVISE` (`REVISE` serializes to `status: "FAIL"` in the pack summary).
155
167
 
156
168
  ## Workflow Convention
157
169
 
@@ -175,36 +187,65 @@ Follow `.qfai/assistant/constitution/shared-skill-operating-baseline.md#delta-re
175
187
 
176
188
  - With argument (`/qfai-sdd <spec-id-or-name> [--auto]`): update only the matched single spec target.
177
189
  - Without argument (`/qfai-sdd`): target all capabilities listed in `_policies/03_Capabilities.md`.
190
+ - Contract-scoped (`/qfai-sdd --contract <CON-ID>`): run Stage 0 + Phase 0 (Contracts-first) + Phase 4 (Delta update) only, against the named contract and the specs that reference it. This is the invocation `constitution/drift-protocol.md#when-drift-is-detected` step 4 names for a contract-class upstream artifact; without it, a contract-only Change Request had no rerun narrower than the whole spec.
178
191
  - Reordering capability-to-spec mapping is a Change Request decision and must not be done implicitly.
179
192
 
180
193
  ## Critical Constraints
181
194
 
182
- 1. Use only skill-local templates under `.qfai/assistant/skills/qfai-sdd/templates/contracts`, `templates/report`, and `templates/specs`.
195
+ 1. Use only templates under `.qfai/assistant/skills/qfai-sdd/templates/` — the whole directory, not an enumerated subset, so a new template directory is covered on the day it ships.
196
+ - Named cross-skill exception: `.qfai/assistant/skills/qfai-prototyping/templates/DESIGN.md.sample` (Phase 0 DESIGN.md Freeze). It is an exception, not a licence to read other skills' templates.
197
+ - Never invent a layout for an artifact a template already covers.
183
198
  2. Always write `.qfai/report/preflight_summary.md` before generating shared/spec artifacts.
184
199
  3. Contracts-first is mandatory; UI-bearing targets must be normalized into `.qfai/contracts/design/**` and `.qfai/contracts/ui/**` per `references/ui-design-contract-normalization.md`. UI-bearing targets MUST also validate the consuming-project root `DESIGN.md` and freeze its sha256 into `.qfai/contracts/design/DESIGN.md.lock.yaml` (see Phase 0 DESIGN.md Freeze below).
185
200
  4. `_policies/05_Contracts.md` must include a Contract Index aligned with `.qfai/contracts/**`.
201
+ - Phase 0 must also reconcile paired contracts against each other, not only validate each file: every terminal state, status enum value, and error code an API contract mandates must be representable in the paired DB contract. See `references/contract-artifact-rules.md#cross-contract-reconciliation-must`. The reviewer gate checks the pairing before sign-off.
186
202
  5. `_policies/04_Business-Flow.md` must be Markdown with Mermaid `flowchart` or `sequenceDiagram`.
187
203
  6. `05_Examples.md` must include `EX-ID` and `BR-Ref` mappings.
188
- 7. `06_Test-Cases.md` must include `TC-ID`, `EX-Ref`, `AC-Refs`, and `Type`, with normal-path plus error/boundary coverage.
189
- 8. Stop only when `qfai validate --profile sdd --fail-on error --format github | tee .qfai/report/validate.log` exits with `error=0`.
204
+ 7. `06_Test-Cases.md` must include `TC-ID`, `Level`, `EX-Ref`, `AC-Refs`, and `Type`, with normal-path plus error/boundary coverage. `Level` gates `TDDLIST_TC_NOT_COVERED` at `error`, so it is required, not optional.
205
+ 8. Stop only when `npx qfai validate --profile sdd --fail-on error --format github` exits with `error=0`.
190
206
 
191
207
  ## Required Process
192
208
 
193
209
  1. Stage 0: Preflight (stop on blockers).
194
210
  2. Stage 1: Triage (classify + approve + persist Triage table).
195
211
  3. Write `.qfai/report/preflight_summary.md`.
196
- 4. Phase 0: Contracts-first (UI-bearing targets normalize in this phase, and freeze root `DESIGN.md` per the Phase 0 DESIGN.md Freeze step below).
212
+ 4. Phase 0: Contracts-first (UI-bearing targets normalize in this phase, and freeze root `DESIGN.md` per the Phase 0 DESIGN.md Freeze step below). Close Phase 0 with the cross-contract reconciliation step in `references/contract-artifact-rules.md#cross-contract-reconciliation-must`.
197
213
  5. Phase 1: Outline (`_policies/01..11`).
198
- 6. Phase 2: Slice (per spec, gate each).
199
- 7. Phase 3: Plan finalize (after at least one slice gate passes).
200
- 8. Phase 4: Delta update.
201
- 9. Run validate; fix source-layer artifacts and rerun until `error=0`.
202
- 10. Triage density-smell warnings in `.qfai/report/specs-coverage/spec-*.md`.
214
+ 6. Phase 2: Slice (per spec, gate each with `npx qfai validate --profile sdd --fail-on error --spec <spec-id>` so a parallel worker gates on its own spec only and does not import a sibling agent's in-flight failures). A `--spec` run writes `<report>/validate.spec-<id>.json` and never the shared `validate.json` / `validate-<profile>.json`, so parallel workers cannot race on one file; an unknown or unparseable `--spec` value fails the run (`QFAI-SCOPE-001` / `QFAI-SCOPE-002`) instead of silently widening to the whole repo.
215
+ 7. Phase 2b: Seed each target spec's `tdd/test-list.md` from `06_Test-Cases.md`
216
+ — one row per coverage-target TC, `Status = todo`; copy
217
+ `templates/specs/spec/tdd/test-list.md` when absent. Without it
218
+ `/qfai-implement` starts with zero selectable items. **Seeding is a delta,
219
+ not a regeneration, in both directions**: unchanged rows keep their state,
220
+ new TCs append at `todo`, and changed / removed TCs are reset or retired
221
+ under the upstream-reset rule (`references/sdd-phase-checklists.md`).
222
+ 8. Phase 2c: Obligation reconciliation (per spec). Re-read `.qfai/contracts/**`
223
+ against the `BR` / `AC` Phase 2 produced: name the realizing contract for
224
+ each, and resolve every persisted attribute it names to a column, field or
225
+ enum member there — directly or by a stated join. Phase 0 froze the contracts
226
+ before these obligations existed, so this is the only step that checks they
227
+ are realizable. See
228
+ `references/contract-artifact-rules.md#obligation-reconciliation-must--phase-2c`.
229
+ Fix the contract or the obligation here; both are owned by this skill, and a
230
+ mismatch carried downstream reaches an implementer who can fix neither.
231
+ 9. Phase 3: Plan finalize (after at least one slice gate passes).
232
+ 10. Phase 4: Delta update.
233
+ 11. Run validate; fix source-layer artifacts and rerun until `error=0`.
234
+ 12. Triage density-smell warnings in `.qfai/report/specs-coverage/spec-*.md`.
235
+
236
+ ## Work-log entries
237
+
238
+ Write a `.qfai/steering/<id>.md` entry when this stage hits one of the conditions in the `kind` trigger table of `.qfai/assistant/catalog/worklog-entry.schema.md` — `blocker`, `handoff`, `consultation-needed` and `decision` are the ones this stage reaches most. `npx qfai validate` polices the surface but nothing else asks for an entry, so an unwritten one is simply lost.
203
239
 
204
240
  ## Mandatory Outputs
205
241
 
206
242
  - Shared `_policies/01..11` files
207
243
  - Target `spec-*/01..10` files (with valid `Status:` bullet)
244
+ - `spec-*/16_Traceability-ledger.md` when the spec's `BR-*` / `AC-*` are linked to implementation
245
+ files (optional artifact; create or refresh it in the same change as the BR/AC it links, from
246
+ `templates/specs/spec/16_Traceability-ledger.md`). Without it `QFAI-TRACE-002` is emitted and the
247
+ BR/AC ↔ implementation integrity check (`QFAI-TRACE-001`) is skipped for that spec. See
248
+ `references/spec-traceability-rules.md#traceability-ledger-16_traceability-ledgermd`.
208
249
  - Triage section in every changed `09_delta.md` (per-spec) or `_policies/10_delta.md` (cross-spec)
209
250
  - Updated contracts under `.qfai/contracts/**`; UI-bearing targets normalize design/ui contracts
210
251
  - `.qfai/report/preflight_summary.md`
@@ -220,16 +261,25 @@ When the target spec is UI-bearing, Phase 0 MUST freeze the brand SSOT:
220
261
  missing, stop and ask the user to run `/qfai-discussion` (which
221
262
  emits the draft) or to author it manually using the sample at
222
263
  `.qfai/assistant/skills/qfai-prototyping/templates/DESIGN.md.sample`.
223
- 2. Call `parseDesignMd(text)`. If the result is `{ error: ParseError }`,
264
+ 2. Call `isUnreplacedDesignMdSample(text)`. If it returns `true`, the file
265
+ is still a qfai sample brand and MUST NOT be frozen: stop and ask the
266
+ user to author this product's own brand SSOT, deleting the
267
+ `QFAI-SAMPLE-DESIGN-MD` marker comment if present (samples from
268
+ releases older than the marker are recognised by content instead).
269
+ `npx qfai init` seeds the sample into the project root and never overwrites
270
+ it, so step 1's missing-file check cannot catch this — an unreplaced
271
+ sample parses and validates by construction, and freezing it binds
272
+ `/qfai-prototyping` and the reviewer lock rule to a fictional brand.
273
+ 3. Call `parseDesignMd(text)`. If the result is `{ error: ParseError }`,
224
274
  stop and report `path` / `code` / `message` for the parse error.
225
275
  Otherwise the result is `{ data: DesignMd; body: string }`; pass
226
276
  `data` to `validateDesignMd(data)`. If that issue list is
227
277
  non-empty, stop and report each issue. Both functions, together with
228
278
  `hashDesignMd` and the `DesignMd` / `ParseError` / `ParseResult` /
229
279
  `ValidationIssue` types, are re-exported from the public `qfai`
230
- package entry (`import { parseDesignMd, validateDesignMd, hashDesignMd } from "qfai"`).
231
- 3. Call `hashDesignMd(text)` to compute sha256 over the raw bytes.
232
- 4. Write `.qfai/contracts/design/DESIGN.md.lock.yaml` from the
280
+ package entry (`import { parseDesignMd, validateDesignMd, hashDesignMd, isUnreplacedDesignMdSample } from "qfai"`).
281
+ 4. Call `hashDesignMd(text)` to compute sha256 over the raw bytes.
282
+ 5. Write `.qfai/contracts/design/DESIGN.md.lock.yaml` from the
233
283
  template at
234
284
  `templates/contracts/design-md-lock.sample.yaml` with these fields:
235
285
  - `designMdPath: "DESIGN.md"`
@@ -237,7 +287,7 @@ When the target spec is UI-bearing, Phase 0 MUST freeze the brand SSOT:
237
287
  - `frozenAt: <UTC ISO-8601>`
238
288
  - `schemaTokens.colors`, `fontFamilies`, `radii`, `shadows`
239
289
  enumerated per the sample.
240
- 5. Record the freeze in `_policies/05_Contracts.md` under the Contract
290
+ 6. Record the freeze in `_policies/05_Contracts.md` under the Contract
241
291
  Index. The lock yaml plus root `DESIGN.md` are the only brand
242
292
  contract; per-aspect brand yaml contracts have been removed.
243
293
 
@@ -250,7 +300,14 @@ Run the full checklist from `references/sdd-quality-gate.md`. The gate also cove
250
300
 
251
301
  ## Evidence
252
302
 
253
- Create `.qfai/evidence/sdd-<spec-id>.md` with: Objective, Inputs reviewed, Preflight summary path, Triage decisions (op + approver per row), Open questions, Decisions made, Work performed, Commands executed, Validate evidence paths, Work Orders Summary, Gaps / Open risks, Final status.
303
+ Create `.qfai/evidence/sdd-<spec-id>.md` from `templates/evidence/sdd-spec.md`. That template is the
304
+ canonical layout for this artifact — copy it, keep every `##` heading in order, and fill it in.
305
+
306
+ Required sections, in order (the template is authoritative if the two ever disagree): Objective,
307
+ Inputs reviewed, Preflight summary path, Triage decisions (op + approver per row), Open questions,
308
+ Decisions made, Work performed, Commands executed, Validate evidence paths, Work Orders Summary,
309
+ Gaps / Open risks, Final status. Work Orders Summary uses the fixed 6-column schema from
310
+ `shared-skill-delegation-baseline.md`; its `Status` and `Final status` accept only `PASS` or `REVISE`.
254
311
 
255
312
  ## Done Declaration
256
313
 
@@ -270,7 +327,8 @@ When this skill completes, provide a final user-facing message enumerating next
270
327
  - Proceed (recommended): `/qfai-prototyping`.
271
328
  - Test-first path: `/qfai-atdd`.
272
329
  - Spec pack needs correction: rerun `/qfai-sdd` and regenerate evidence.
273
- - Confirm contracts referenced by `_policies/05_Contracts.md` exist under `.qfai/contracts/**`.
330
+ - Confirm contracts referenced by `_policies/05_Contracts.md` exist under `.qfai/contracts/**`,
331
+ and that Phase 2c reconciled every `BR` / `AC` against them — existence is not realizability.
274
332
 
275
333
  ## Default Autopilot Policy
276
334
 
@@ -295,6 +353,8 @@ A skill MAY narrow the auto-decide bucket (drop entries) but MUST NOT widen it.
295
353
 
296
354
  project_memory:
297
355
 
298
- - Phase order is fixed: Stage 0 Preflight → Stage 1 Triage → Phase 0 Contracts-first → Phase 1 Outline → Phase 2 Slice → Phase 3 Plan finalize → Phase 4 Delta update; do not reorder.
356
+ - Phase order is fixed: Stage 0 Preflight → Stage 1 Triage → Phase 0 Contracts-first → Phase 1 Outline → Phase 2 Slice → Phase 2b Seed tdd/test-list.md → Phase 2c Obligation reconciliation → Phase 3 Plan finalize → Phase 4 Delta update; do not reorder.
357
+ - Phase 2c reconciles contracts against the BR/AC written after them: Contracts-first freezes the contract before its obligations exist, and Phase 2c is the only step that checks they are realizable.
358
+ - Phase 2b seeds each target spec's tdd/test-list.md from 06_Test-Cases.md (one row per coverage-target TC, Status = todo) and is a delta: existing rows keep their TDD-ID, Status, Test file, Selector, DR-ID and Evidence.
299
359
  - Append-first is the Stage 1 default: UPDATE on an active spec whose subject tokens overlap; CREATE only when there is zero overlap AND the REQ adds a new CAP-NNNN, registered before the CREATE row.
300
360
  - Phase 0 DESIGN.md Freeze is mandatory for UI-bearing targets; .qfai/contracts/design/DESIGN.md.lock.yaml is the brand-lock SSOT.