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
@@ -2,8 +2,87 @@
2
2
 
3
3
  This document is the SSOT for ATDD test-layer semantics and completion gates.
4
4
 
5
+ ## Layer vocabulary crosswalk (normative)
6
+
7
+ qfai spells the same layer four ways across shipped artifacts. This table is
8
+ the crosswalk; every artifact MUST use the spelling in its column.
9
+
10
+ | Code | Word | Tag | Test directory | `06_Test-Cases.md#Level` | `tdd/test-list.md#Layer` |
11
+ | ---- | ----------- | ------------------- | --------------------------- | ------------------------ | ------------------------ |
12
+ | L1 | Unit | `layer-unit` | project convention | `L1` | `Unit` |
13
+ | L2 | Component | `layer-component` | project convention | `L2` | `Component` |
14
+ | L3 | Integration | `layer-integration` | `<testsDir>/integration/**` | `L3` | `Integration` |
15
+ | L4 | API | `layer-api` | `<testsDir>/api/**` | — | `API` |
16
+ | L5 | E2E | `layer-e2e` | `<testsDir>/e2e/**` | — | `E2E` |
17
+
18
+ Rules:
19
+
20
+ - **One value per cell.** A `Level` cell and a `Layer` cell each hold exactly
21
+ one layer. An obligation spanning two layers is two rows, not one row with
22
+ two values.
23
+ - `06_Test-Cases.md` uses the **code** (`L1`…`L3`) in its `Level` column.
24
+ - `tdd/test-list.md` uses the **word** in its `Layer` column.
25
+ - Test-strategy tags in prompts and policy files use the **tag** form.
26
+ - **`<testsDir>` is `paths.testsDir` from `qfai.config.yaml`**, whose default
27
+ is `tests` — hence the shipped `tests/integration/**`, `tests/api/**` and
28
+ `tests/e2e/**`. A project that repoints `paths.testsDir` moves all three at
29
+ once; the ATDD traceability scan follows the configured value, so never
30
+ hard-code the literal `tests/` prefix in a project's own artifacts.
31
+ - L1 and L2 have no mandated directory: unit and component tests live wherever
32
+ the project's own convention puts them. Only L3-L5 are directory-pinned, and
33
+ only those directories are scanned by the ATDD traceability rules.
34
+ - **A `TC-*` row's `Level` is L1-L3.** The ATDD annotation hard gate routes an
35
+ obligation by its ID, not by its `Level`: `US-*` is answered from
36
+ `<testsDir>/e2e/**` (`QFAI-ATDD-111`), `TC-*` from
37
+ `<testsDir>/integration/**` (`QFAI-ATDD-112`) and `CON-API-*` from
38
+ `<testsDir>/api/**` (`QFAI-ATDD-113`) and `CON-DB-*` from
39
+ `<testsDir>/integration/**` (`QFAI-ATDD-115`), while a `TC-*` reference inside
40
+ `<testsDir>/api/**` or `<testsDir>/e2e/**` is rejected outright
41
+ (`QFAI-ATDD-121` / `QFAI-ATDD-122`). L4's goal is `CON-API-*` and L5's is
42
+ `US-*` (see the layer definitions below), so an oracle that lands at L4 or L5
43
+ means the obligation is misfiled: record it as `CON-API-*` or `US-*` rather
44
+ than as a `TC-*` row no test directory can carry.
45
+ - The two code-side word lists (`tddHelpers.ts#UNIT_COMPONENT_LAYERS` /
46
+ `#NON_COVERAGE_LAYERS`) accept both the code and the word form for the same
47
+ layer; they MUST stay in step with this table.
48
+
49
+ ## How this file is consumed
50
+
51
+ The layer set below is read by `core/layerPolicy.ts` and is the SSOT for two
52
+ checks: `QFAI-EX-005` on the legacy spec-pack layout, and `QFAI-EX-105` on the
53
+ layered layout `npx qfai init` produces. Until both consumed it, the file was
54
+ read, reported on, and then ignored on every modern project.
55
+
56
+ - A file that yields no layers raises `QFAI-SPACK-090` (error) rather than
57
+ silently widening to the built-in set.
58
+ - A declared set that disagrees with the built-in set raises `QFAI-SPACK-091`
59
+ (warning). Without it the two could drift in either direction and this file
60
+ would not be an SSOT.
61
+
62
+ ## Scaffolded tests
63
+
64
+ `npx qfai atdd scaffold` writes skeletons to `<testsDir>/integration/<spec-id>/`.
65
+ There is deliberately no `<testsDir>/atdd/**` row below: a fourth root would
66
+ need a rule for which layer such a file belongs to, and qfai has none. The
67
+ writer targets a declared layer instead.
68
+
5
69
  ## Layer definitions
6
70
 
71
+ ### L1 Unit
72
+
73
+ - Scope: pure decision logic — a single module's inputs and return values, with
74
+ no port collaboration and no real infrastructure.
75
+ - Goal: verify `TC-*` obligations whose oracle observes inputs and outputs only.
76
+ - Location rule: `tests/unit/**`.
77
+
78
+ ### L2 Component
79
+
80
+ - Scope: collaboration with a port through a fixture adapter (fake / in-memory),
81
+ with no real infrastructure.
82
+ - Goal: verify `TC-*` obligations whose oracle observes the interaction with a
83
+ port rather than infrastructure state.
84
+ - Location rule: `tests/component/**`.
85
+
7
86
  ### L3 Integration
8
87
 
9
88
  - Scope: real infrastructure integration (for example DB/queue/filesystem) within service boundaries.
@@ -22,8 +101,103 @@ This document is the SSOT for ATDD test-layer semantics and completion gates.
22
101
  - Goal: verify `US-*` obligations from specs.
23
102
  - Location rule: `tests/e2e/**`.
24
103
 
104
+ ## Layer derivation procedure (normative)
105
+
106
+ Deciding a `TC-*`'s layer is a per-TC judgement, not a constant. Use the
107
+ falsifying-oracle rule:
108
+
109
+ 1. **Find the oracle.** Identify the single assertion whose removal would let a
110
+ wrong implementation pass. That assertion, not the test's setup, is what the
111
+ TC verifies.
112
+ 2. **Restrict it to the parent BR's obligations.** Anything the oracle observes
113
+ that the parent business rule does not own is incidental and does not raise
114
+ the layer.
115
+ 3. **Read the layer off what the oracle observes:**
116
+ - inputs and return values only → **L1 Unit**
117
+ - collaboration with a port through a fixture adapter (no real
118
+ infrastructure) → **L2 Component**
119
+ - real infrastructure state — DB rows, queue messages, files → **L3 Integration**
120
+ - values at the service boundary — status codes, response bodies, auth and
121
+ error contracts → **L4 API**
122
+ - a full-system journey across UI/API/data → **L5 E2E**
123
+
124
+ ### Worked examples
125
+
126
+ | Oracle asserts | Layer |
127
+ | ----------------------------------------------------------------------------- | -------------- |
128
+ | `price(order) === 1250` for a given input | L1 Unit |
129
+ | the repository port was called with the normalized key, via a fixture adapter | L2 Component |
130
+ | the row is present in the database after commit | L3 Integration |
131
+ | `POST /orders` returns `422` with `code: "OUT_OF_AREA"` | L4 API |
132
+ | a user can register, order, and see the order in their history | L5 E2E |
133
+
134
+ ### Obligation spanning more than one layer
135
+
136
+ **Split the row.** One TC = one oracle = one layer. If an obligation is
137
+ observable at two layers, it is two obligations: write one TC per oracle and
138
+ give each its own `Level`.
139
+
140
+ - A multi-valued `Level` cell (`L3/L5`) is **illegal**. Nothing consumes it and
141
+ no validator can route it.
142
+ - If splitting is genuinely impossible, escalate through the Drift Protocol
143
+ rather than inventing a combined value.
144
+
145
+ ### Direction of authority (anti-pattern)
146
+
147
+ The test driver follows the declared layer. **The layer is never inferred from
148
+ how a test happens to be driven.** A unit-level obligation — one whose oracle,
149
+ after step 2, observes only inputs and return values — exercised through an
150
+ HTTP client is still L1 badly implemented; it is not an L4 test.
151
+
152
+ **Step 2 outranks step 3.** Restriction to the parent BR's obligations runs
153
+ first, so a transport the parent BR does not own is incidental and never
154
+ raises the layer. A BR that owns only the price calculation, asserted as
155
+ `response.body.price` over HTTP, reads as L1: the price is the BR-owned value,
156
+ the response envelope is the incidental transport. Step 3's "response bodies →
157
+ L4" applies only when the service-boundary values are themselves what the
158
+ parent BR owns. Inverting this is what collapses a designed pyramid into an
159
+ all-integration suite.
160
+
161
+ ### Annotation routing is by ID type, not by `Level`
162
+
163
+ The derived `Level` records which oracle owns the obligation. It does **not**
164
+ move the traceability annotation. The [ATDD annotation hard gate](#atdd-annotation-hard-gate)
165
+ routes by obligation ID: `US-*` is answered from `tests/e2e/**`
166
+ (`QFAI-ATDD-111`), `TC-*` from `tests/integration/**` (`QFAI-ATDD-112`), and
167
+ `CON-API-*` from `tests/api/**` (`QFAI-ATDD-113`); a `TC-*` reference inside
168
+ `tests/api/**` or `tests/e2e/**` is rejected outright (`QFAI-ATDD-121` /
169
+ `QFAI-ATDD-122`). Two consequences bind every `TC-*` row:
170
+
171
+ - **A `TC-*` row's `Level` stays within L1–L3.** L4's goal is `CON-API-*` and
172
+ L5's goal is `US-*` (see the layer definitions above), so an oracle that
173
+ derives to L4 or L5 means the obligation is misfiled, not that the TC is an
174
+ L4/L5 test. Re-file it as `CON-API-*` or `US-*`.
175
+ **Re-filing is an upstream change, never a bare row deletion.** By step 2 the
176
+ derivation reaches L4/L5 only when the parent `BR-*` itself owns the
177
+ service-boundary contract or the journey, so the `TC-*` row is removed only
178
+ together with the `EX-*` it verifies and the `BR-*`/`AC-*` that EX
179
+ concretizes. Dropping the row alone leaves the parent EX with no `EX-Ref`
180
+ and `npx qfai validate --profile sdd --fail-on error` reports `QFAI-COV-203`
181
+ (and `QFAI-COV-201` when that TC was the AC's only cover); dropping the EX
182
+ but keeping its BR reports `QFAI-COV-202`. Move the whole chain in one
183
+ change through the Drift Protocol so `04_Business-Rules.md`,
184
+ `05_Examples.md`, `06_Test-Cases.md` and the contract stay consistent. If the
185
+ parent BR keeps a spec-side obligation, split the row instead: keep a `TC-*`
186
+ for the part the BR owns — by step 2 that part derives to L1–L3 — and re-file
187
+ only the boundary assertion as `CON-API-*` / `US-*`.
188
+ - **An L1/L2 `Level` does not relax `QFAI-ATDD-112`, and the gate does not
189
+ change the `Level`.** The two are independent: the `Level` records what the
190
+ oracle observes and is derived before any test exists, while the
191
+ `QFAI:SPEC-XXXX:TC-YYYY` annotation is still owed to `tests/integration/**`.
192
+ Never rewrite a derived L1/L2 to L3 because no integration trace exists yet —
193
+ that would make the recorded oracle depend on implementation order and hide
194
+ the unit/component work `/qfai-implement` selects. The missing annotation is
195
+ an open ATDD obligation to satisfy, not evidence that the `Level` was wrong.
196
+
25
197
  ## TestKind resolution (single source)
26
198
 
199
+ - `tests/unit/**` -> Unit
200
+ - `tests/component/**` -> Component
27
201
  - `tests/e2e/**` -> E2E
28
202
  - `tests/api/**` -> API
29
203
  - `tests/integration/**` -> Integration
@@ -31,46 +205,213 @@ This document is the SSOT for ATDD test-layer semantics and completion gates.
31
205
  ## Annotation schema (code-side)
32
206
 
33
207
  - Smallest trace unit is ID.
34
- - Multiple IDs per test file are allowed.
208
+ - Multiple IDs per test file are allowed — but this is a trace rule, not
209
+ licence to aggregate a whole spec into one module. See Test-file granularity
210
+ below.
35
211
  - AC annotations are optional (indirect coverage through TC is acceptable).
36
212
  - Allowed forms:
37
213
  - `QFAI:SPEC-0001:US-0001`
38
214
  - `QFAI:SPEC-0001:TC-0001`
39
215
  - `QFAI:CON-API-0001`
216
+ - `QFAI:CON-DB-0001`
40
217
 
41
218
  ## ATDD annotation hard gate
42
219
 
43
220
  - E2E obligations:
44
- - Every `US-*` in specs must be referenced at least once from `tests/e2e/**` (no exception).
221
+ - Every `US-*` in a **user-facing** spec must be referenced at least once from
222
+ `tests/e2e/**`. "User-facing" is the same surface **union**
223
+ `/qfai-prototyping` enforces. Any one of these signals puts a spec in it:
224
+ - frontmatter `surface_type: ui-bearing` in `01_Spec.md`;
225
+ - a matching UI contract `.qfai/contracts/ui/<spec-id>*.yaml` — a project
226
+ that declares its surfaces only through contracts is still in scope;
227
+ - a legacy `# … prototyping …` heading in `01_Spec.md`;
228
+ - the spec pinned by `qfai.config.yaml#prototyping.primarySpecId`.
229
+
230
+ A spec that declares no user-facing surface by **any** of those signals
231
+ owes no E2E reference, and `QFAI-ATDD-111` does not fire for it.
232
+
233
+ - Scoping applies only when the project declares at least one UI-bearing
234
+ spec. A project that has never declared a surface has not opted into
235
+ surface typing, so the obligation stays project-wide for it.
236
+ - Do not create an E2E tree whose only purpose is to receive annotations.
237
+ That is the "convert all obligations into E2E" anti-pattern below.
45
238
  - Use `QFAI:SPEC-XXXX:US-YYYY` annotations.
46
- - Integration obligations:
47
- - Every `TC-*` in specs must be referenced at least once from `tests/integration/**`.
48
- - Use `QFAI:SPEC-XXXX:TC-YYYY` annotations.
239
+
240
+ - Integration obligations (enforced today):
241
+ - Every `TC-*` in specs must be referenced at least once from the directory
242
+ its declared `Level` routes to: L3/Integration -> `tests/integration/**`,
243
+ L4/API -> `tests/api/**`, L5/E2E -> `tests/e2e/**`. A TC with no declared
244
+ `Level` defaults to `tests/integration/**`. This is what `QFAI-ATDD-112`
245
+ checks. - Use `QFAI:SPEC-XXXX:TC-YYYY` annotations.
246
+ - `tests/api/**` and `tests/e2e/**` must not carry `TC-*` annotations
247
+ (`QFAI-ATDD-121` / `QFAI-ATDD-122`), so an L4 obligation is discharged as a
248
+ `CON-API-*` reference, never as a `TC-*` one.
249
+ - Every declared `CON-DB-*` must be referenced at least once from
250
+ `tests/integration/**` (`QFAI-ATDD-115`). Use `QFAI:CON-DB-XXXX`
251
+ annotations. L3 owns this because a DB contract is only exercised against
252
+ real infrastructure, which is L3's declared scope; a `CON-DB` reference
253
+ from `tests/e2e/**` is not counted, or an end-to-end assertion that never
254
+ touches the schema could close the obligation. A contract outside the
255
+ current slice defers with a `-- x-qfai-status: planned` comment line,
256
+ reported at `info` by `QFAI-ATDD-116` so the deferral stays visible.
257
+
258
+ - Per-level routing (target state — **not enforced, do not follow yet**):
259
+ - The intended end state is one required location per declared `Level`:
260
+ L1 -> `tests/unit/**`, L2 -> `tests/component/**`,
261
+ L3 -> `tests/integration/**`. L4 stays `CON-API-*` in `tests/api/**` and
262
+ L5 stays `US-*` in `tests/e2e/**`.
263
+ - **This is not live.** `buildAtddTestGlobs` scans only
264
+ `tests/{e2e,api,integration}`, so an annotation placed in `tests/unit/**`
265
+ or `tests/component/**` is invisible to the scanner and `QFAI-ATDD-112`
266
+ still reports the TC as uncovered. Until the scanner and `QFAI-ATDD-112`
267
+ resolve per-TC, keep discharging every `TC-*` in `tests/integration/**`.
49
268
  - API obligations:
50
269
  - Every declared `CON-API-*` must be referenced at least once from `tests/api/**`.
51
270
  - Use `QFAI:CON-API-XXXX` annotations.
271
+ - **Deferral.** `/qfai-sdd` authors contracts in Phase 0 (Contracts-first) and
272
+ slices them in Phase 2, so a contract legitimately exists before its slice
273
+ ships. A contract that declares `x-qfai-status: planned` is excluded from
274
+ the `QFAI-ATDD-113` obligation and reported as `QFAI-ATDD-114` (`info`)
275
+ instead. Remove the marker when the slice is implemented — leaving it in
276
+ place on a shipped slice is a review finding, not a tool finding.
277
+ - The marker counts only at the **document root**. The same key nested under
278
+ an operation defers nothing: one path must not be able to drop the
279
+ API-test obligation for every other `CON-API-*` the file declares.
280
+ - Deferral removes the test obligation, not the declaration. A deferred
281
+ `CON-API-*` stays a known ID, so writing its API test ahead of the slice
282
+ is fine and never raises `QFAI-ATDD-103`.
283
+ - Deferred IDs are recorded in `report/atdd-traceability/summary.{json,md}`
284
+ under `deferred.conApi`, so an empty `missing.conApi` can be told apart
285
+ from a project where every contract is still planned.
52
286
  - Forbidden references:
53
- - `tests/api/**` must not include `QFAI:SPEC-XXXX:TC-YYYY`.
54
- - `tests/e2e/**` must not include `QFAI:SPEC-XXXX:TC-YYYY`.
287
+ - `tests/api/**` must not include `QFAI:SPEC-XXXX:TC-YYYY` **for a TC whose
288
+ declared `Level` is not L4/API**. A TC that declares an API-level
289
+ obligation belongs in `tests/api/**`, and its annotation there counts as
290
+ coverage. The rule exists to stop obligations drifting into the wrong
291
+ layer, not to make the correct layer unusable.
292
+ - `tests/e2e/**` must not include `QFAI:SPEC-XXXX:TC-YYYY` **for a TC whose
293
+ declared `Level` is not L5/E2E**. Same reason as above: the routing rule
294
+ and the forbidden rule must agree, or the layer the routing selects
295
+ becomes unusable.
296
+ - `tests/integration/**` must not include `QFAI:SPEC-XXXX:TC-YYYY` **for a TC
297
+ whose declared `Level` is not L3/Integration** (`QFAI-ATDD-123`). The rule
298
+ is symmetric so "exactly one directory" holds in both directions: an
299
+ annotation left behind here after the TC moved to L4/L5 is as wrong as one
300
+ filed early into `tests/api/**`.
55
301
  - Unknown references (`US/TC/CON-API` not declared) are errors.
302
+ - A `TC-*` annotation outside the directory its `Level` routes to is a
303
+ misplacement, whichever directory it lands in — including the two new
304
+ locations `tests/unit/**` and `tests/component/**`.
56
305
  - AC annotations are not required in code; AC coverage is treated as indirect through TC coverage.
57
306
  - `QFAI:CON-API-*` in `tests/e2e/**` is not forbidden, but contract guarantee belongs to API tests.
58
307
 
308
+ ## Test-file granularity
309
+
310
+ - Default: **one test module per `TC-*`**. A TDD ledger row's `Test file`
311
+ names that module.
312
+ - Grouping several `TC-*` into one module is allowed when they verify the same
313
+ BR and the module stays reviewable in one pass. Above that, split by BR, then
314
+ by AC.
315
+ - A single `Test file` value shared by every row of a spec is an anti-pattern:
316
+ it makes the per-item "relevant test suite" indistinguishable from the whole
317
+ spec suite, and it puts the whole spec's test code in front of every
318
+ in-context reviewer gate.
319
+ - `qfai-sdd` should emit a per-item `Test file` value, not a per-spec one.
320
+
59
321
  ## Volume policy
60
322
 
61
- - Floors and ratios are signals, not completion gates.
323
+ - Floors and ratios are signals, not completion gates. This is the only
324
+ statement in this section: no volume observation blocks completion, and none
325
+ triggers a Change Request.
62
326
  - Completion gate is validation pass with no errors:
63
- - `qfai validate --fail-on error`
327
+ - `npx qfai validate --fail-on error`
328
+
329
+ If an observed layer distribution looks wrong:
330
+
331
+ 1. Do not auto-adjust the distribution to make it look better.
332
+ 2. Record the observed distribution and the rationale for it in the running
333
+ stage's evidence file under `.qfai/evidence/` — for example
334
+ `.qfai/evidence/atdd-<spec-id>.md` or
335
+ `.qfai/evidence/implement-<spec-id>.md`.
336
+ 3. Continue. The stage is not blocked.
64
337
 
65
- If a volume signal is unmet:
338
+ The record goes to evidence, not into the spec, on purpose.
339
+ `constitution/drift-protocol.md` lists `*_delta.md` and the per-spec Open
340
+ Questions file among the upstream SSOT a downstream stage must not edit without
341
+ explicit user approval, and whitelists `.qfai/evidence/**` append/update as an
342
+ allowed exception. Pointing this step at the spec would send ATDD and implement
343
+ straight back into the STOP-and-wait state the policy exists to avoid.
66
344
 
67
- 1. STOP auto-adjustment.
68
- 2. Raise a Change Request with 3 options and recommendation.
69
- 3. Wait for explicit user approval.
70
- 4. Update upstream artifacts via owner-phase rerun when required.
345
+ The owner phase (`/qfai-sdd`) is the one that may carry the note into the
346
+ spec's own Open Questions file on a later run: `08_Open-questions.md` in a
347
+ layered spec, `15_Open-questions.md` in a spec pack.
348
+ (`09_Open-questions.md` is the shared `_policies` file, not a per-spec one.)
349
+
350
+ A Change Request is reserved for `constitution/drift-protocol.md`-class events
351
+ — an actual conflict with an upstream SSOT decision. A volume observation is not
352
+ one, and it stays non-blocking either way. What differs is how much of it is
353
+ measurable:
354
+
355
+ - **No configured guardrail.** qfai ships no default floor, ratio or threshold,
356
+ and no validator emits a volume rule, so "unmet" is a judgement call with no
357
+ tool-checkable meaning. Record the observation and the reasoning.
358
+ - **A configured guardrail.** When a project sets
359
+ `validation.testStrategy.maxE2eScenarioRatio` or `maxE2eScenarioCount` to a
360
+ non-null value, `report.ts` measures it and reports `ratioExceeded` /
361
+ `countExceeded` with a warning. That is the project's own stated limit, not a
362
+ subjective read: record the configured value, the measured value and the
363
+ report warning in the evidence, so the breach is auditable rather than
364
+ paraphrased.
365
+
366
+ **What it counts is narrow.** These two knobs measure **Gherkin scenarios
367
+ parsed out of each spec's Examples file**, bucketed by their `@layer-*` tags.
368
+ They never inspect `<testsDir>/e2e/**` or any other code test. A project that
369
+ writes no Gherkin — the normal shape for a layered project whose E2E lives in
370
+ code — measures zero scenarios, so the guardrail stays silent however many
371
+ code E2E tests exist. Treat them as a guardrail over the **scenario**
372
+ distribution, not over the real test-layer distribution: when they are silent,
373
+ the observation is the judgement call above, and the evidence entry must say
374
+ how the distribution was counted so it is not read as a tool measurement.
375
+
376
+ Either way completion is not blocked and no Change Request is raised: a
377
+ user-blocking Change Request against a project's own tuning knob cannot conclude
378
+ anything actionable.
71
379
 
72
380
  ## Anti-patterns
73
381
 
74
382
  - Do not treat `scenario.feature` or a coverage ledger as mandatory completion input.
75
383
  - Do not convert all obligations into E2E.
76
384
  - Do not inflate tests only to satisfy floor numbers.
385
+ - Do not over-concentrate obligations into a single layer, module, or selector. Collapsing a matrix-shaped `TC-*` into one integration module — or into one test function behind one `test-list.md` selector — is the same failure mode as converting everything into E2E, and it additionally destroys the RED observation: a test function fails once, so only the first failing assert is ever observed. Split per independently observable boundary (see `qfai-implement/references/execution-ledger.md#selector-granularity-must`).
386
+ - Do not re-label an existing obligation's declared layer to change how a
387
+ distribution reads. Re-labelling is the cheapest way to clear a signal and
388
+ the one that destroys the most information; the layer of an obligation is
389
+ determined by what it verifies, never by how the totals look.
390
+
391
+ ### Concentration signals (non-gating)
392
+
393
+ Treat these as review signals in the same class as volume floors — worth a finding, never a hard gate:
394
+
395
+ - one test module holding a disproportionate share of a spec's `assert` statements
396
+ - a very low `test_` functions per file ratio in a module that carries many obligations
397
+ - a single selector whose recorded runtime grows monotonically across RED rounds
398
+
399
+ ## Test stub detection (QFAI-TEST-001 / QFAI-TEST-002)
400
+
401
+ `QFAI-TEST-001` (error) reports the silent-placeholder construct of each
402
+ supported stack:
403
+
404
+ | Extensions | Construct |
405
+ | -------------------- | ------------------------------------------------------------------- |
406
+ | `.ts` / `.js` family | `it.todo(` / `test.todo(` / `describe.todo(` |
407
+ | `.py` | `pytest.skip(`, `@pytest.mark.skip/skipif/xfail`, `@unittest.skip*` |
408
+ | `.go` | `t.Skip*(` |
409
+ | `.java` / `.kt` | `@Disabled` / `@Ignore` |
410
+ | `.rs` | `#[ignore]` |
411
+ | `.rb` | a line starting `skip` / `pending` |
412
+ | `.cs` | `[Ignore` / `Skip = "` |
413
+
414
+ `QFAI-TEST-002` (info) names any extension the scan opened that has no dialect.
415
+ Without it a clean run on an unsupported stack is indistinguishable from a
416
+ checked one — the detector used to be JS-only while file selection was
417
+ stack-agnostic, so every other stack got a clean result that meant nothing.
@@ -0,0 +1,165 @@
1
+ # Work-log Entry Schema Contract
2
+
3
+ - Contract scope: frontmatter and body schema for `.qfai/steering/*.md` entries
4
+ - Owners: the validate stage (schema enforcement), the implement stage (primary writer) and the Reviewer-Gate stage (consumer)
5
+ - Used-by: All implementation/review-phase skills, `npx qfai validate`, Reviewer subagents
6
+ - SSOT modules:
7
+ - `packages/qfai/src/core/worklog/parseEntry.ts` (pure parser: `string → Result<Entry, SchemaError>`)
8
+ - `packages/qfai/src/core/worklog/validateLinks.ts` (link-integrity check)
9
+
10
+ ## Storage model
11
+
12
+ - Per-project, project-root location: `.qfai/steering/`.
13
+ - The **surface** lives at `.qfai/steering/`, not under `.qfai/assistant/`. This
14
+ **schema** ships with the package and `npx qfai init` seeds it at
15
+ `.qfai/assistant/catalog/worklog-entry.schema.md` — the seeded README and entry
16
+ template used to point at an unpublished path, so the contract was
17
+ unresolvable on every consuming project.
18
+ - By default `.gitignore` excludes the directory; projects MAY opt in via override.
19
+ - Filename: `.qfai/steering/<id>.md` where `<id>` is kebab-case ASCII; the frontmatter `id` MUST match the filename stem.
20
+ - Templates live at `.qfai/steering/_templates/`; templates MUST NOT contain entry-shaped frontmatter (validator ignores `_templates/`).
21
+
22
+ ## Frontmatter schema
23
+
24
+ ```yaml
25
+ ---
26
+ id: 2026-05-22-recut-design-call # required; string; kebab-case; matches filename stem
27
+ status: active # required; enum: active | handoff | archived
28
+ kind: decision # required; enum: see below
29
+ created: 2026-05-22 # required; ISO-8601 date (YYYY-MM-DD)
30
+ updated: 2026-05-22 # required; ISO-8601 date; >= created
31
+ scope: spec-0003 # required; "global" or "spec-NNNN"
32
+ blocking: false # required; boolean
33
+ promote-to: spec-0003/07_Decisions.md # required; string (path) OR null
34
+ links: # required; array (may be empty)
35
+ - spec-0003
36
+ - discussion-20260522081618995
37
+ closure-rationale: null # required when status=archived AND no promote-to satisfied; else null/omitted
38
+ promoted-to: null # required when status=archived AND promote-to was satisfied; value is the DR-ID (Decision Row identifier, e.g. `DR-3`) of the appended row in the target `07_Decisions.md`
39
+ ---
40
+ ```
41
+
42
+ ### `kind` enum (REQ-0004)
43
+
44
+ The `kind` field MUST be exactly one of:
45
+
46
+ | `kind` | Write trigger |
47
+ | --------------------- | ---------------------------------------------------------------------------- |
48
+ | `milestone` | Task milestone reached |
49
+ | `decision` | A decision was made during work that needs durable capture |
50
+ | `risk` | A risk was identified |
51
+ | `consultation-needed` | The skill needs user input to proceed |
52
+ | `unexpected` | An unexpected event occurred during work |
53
+ | `unscoped-discovery` | Out-of-scope concern discovered; current task continues unblocked (REQ-0016) |
54
+ | `handoff` | Work needs to pause; another session/operator will resume |
55
+ | `blocker` | The skill is stuck (e.g. root-cause hunt stalled) |
56
+ | `scope-up` | Work volume larger than expected |
57
+ | `scope-down` | Planned work is no longer required |
58
+ | `spike` | Exploratory investigation logged |
59
+
60
+ ### `status` enum
61
+
62
+ | `status` | Meaning |
63
+ | ---------- | --------------------------------------------------------------------------------------------- |
64
+ | `active` | Open; participates in drift/promote/stale checks |
65
+ | `handoff` | Open and awaiting resumption; body MUST satisfy the handoff-brief schema (REQ-0017) |
66
+ | `archived` | Closed; either promoted (`promoted-to` set) or closed-without-promotion (`closure-rationale`) |
67
+
68
+ ### `scope` semantics
69
+
70
+ - `global` — applies project-wide; visible to every skill invocation.
71
+ - `spec-NNNN` — applies only to the named spec. Implementation-phase skills filter on `scope ∈ {global, current-spec}` before reading.
72
+
73
+ ### `promote-to` semantics
74
+
75
+ - `null` — entry will not promote.
76
+ - non-`null` — string of the form `spec-NNNN/07_Decisions.md` (target Decisions row to append). The promote-gate surfaces `W-PENDING-PROMOTION` until satisfied (REQ-0007).
77
+
78
+ ### `links` array
79
+
80
+ Each element MUST resolve to one of:
81
+
82
+ - `spec-NNNN` — an existing spec directory under `.qfai/specs/`
83
+ - `discussion-*` — an existing discussion pack under `.qfai/discussion/`
84
+ - `<entry-id>` — another `.qfai/steering/<id>.md` entry
85
+
86
+ Broken links surface `W-WORKLOG-BROKEN-LINK` (REQ-0015).
87
+
88
+ ## Body schema
89
+
90
+ The body (everything after the closing `---`) is free-form Markdown except for two `kind`-specific schemas below.
91
+
92
+ ### `kind: handoff` body — required sections (REQ-0017)
93
+
94
+ ```markdown
95
+ ## State of the task
96
+
97
+ <one paragraph: where am I, what is done, what remains>
98
+
99
+ ## Next single action
100
+
101
+ <one bullet: the very next thing to do on resume>
102
+
103
+ ## Constraints to preserve
104
+
105
+ - <bulleted list of invariants that the next operator MUST preserve>
106
+
107
+ ## Open questions
108
+
109
+ - <bulleted list; may be empty>
110
+
111
+ ## References to consult first
112
+
113
+ - <bulleted list of entry IDs / spec IDs / discussion IDs>
114
+ ```
115
+
116
+ Reviewer Gate emits `R-HANDOFF-INCOMPLETE` if any of the five sections is missing or empty.
117
+
118
+ ### `kind: decision` body — recommended sections
119
+
120
+ ```markdown
121
+ ## Context
122
+
123
+ <what triggered the decision>
124
+
125
+ ## Decision
126
+
127
+ <what was decided>
128
+
129
+ ## Alternatives considered
130
+
131
+ <bulleted; mark each as accepted | rejected | deferred>
132
+
133
+ ## Rationale
134
+
135
+ <why this option>
136
+
137
+ ## Consequences
138
+
139
+ <what changes downstream>
140
+ ```
141
+
142
+ The body is consulted by Reviewer Gate when emitting `R-WORKLOG-DRIFT`; the structured shape improves the false-positive rate.
143
+
144
+ ## Unit-test obligations (NFR-0010)
145
+
146
+ `parseEntry.ts` MUST have ≥ 12 unit tests covering:
147
+
148
+ 1. Well-formed entry
149
+ 2. Missing required field (e.g. omitted `kind`)
150
+ 3. Invalid enum value
151
+ 4. Broken YAML (parse error)
152
+ 5. UTF-8 BOM tolerated
153
+ 6. CRLF line endings tolerated
154
+ 7. `scope: spec-NNNN`
155
+ 8. `scope: global`
156
+ 9. `promote-to: null`
157
+ 10. `promote-to: spec-NNNN/07_Decisions.md`
158
+ 11. `links: []`
159
+ 12. `links` array with broken reference (parser returns ok; validator emits `W-WORKLOG-BROKEN-LINK`)
160
+
161
+ ≥ 90 % line coverage of `parseEntry.ts` in CI.
162
+
163
+ ## Distributed-surface obligations
164
+
165
+ The seeded `_templates/entry.md`, and any sample entry shipped via `assets/init/`, MUST carry no internal spec ids, version markers or trace ids. The authoritative list of forbidden shapes is the scanner itself; entries use placeholder ids and dates only.
@@ -36,7 +36,7 @@ When an agent needs to ask the user a question, the following rules apply (see a
36
36
  All SKILL.md files MUST include a
37
37
  `## User Questions (AskUserQuestion Protocol)` section with MUST-level wording.
38
38
  SSOT: `packages/qfai/assets/init/.qfai/assistant/skills/*/SKILL.md`.
39
- Deployed copy (updated by `qfai init`): `.qfai/assistant/skills/*/SKILL.md`.
39
+ Deployed copy (updated by `npx qfai init`): `.qfai/assistant/skills/*/SKILL.md`.
40
40
 
41
41
  ## Error handling
42
42