qfai 2.1.0 → 2.1.2

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 (184) hide show
  1. package/README.md +327 -55
  2. package/assets/defaults/agent-routing/qfai-triage.yml +9 -0
  3. package/assets/defaults/workflows/change-tooling.yml +0 -4
  4. package/assets/defaults/workflows/run-owned-cleanup.yml +12 -0
  5. package/assets/init/.claude/settings.json +27 -9
  6. package/assets/init/.codex/hooks.json +20 -8
  7. package/assets/init/.qfai/assistant/agent/frontend-engineer.md +5 -0
  8. package/assets/init/.qfai/assistant/agent/implementation-reviewer.md +14 -1
  9. package/assets/init/.qfai/assistant/agent/product-experience-architect.md +4 -0
  10. package/assets/init/.qfai/assistant/agent/product-surface-reviewer.md +7 -0
  11. package/assets/init/.qfai/assistant/agent/qa-gatekeeper.md +29 -0
  12. package/assets/init/.qfai/assistant/agent/requirements-analyst.md +8 -0
  13. package/assets/init/.qfai/assistant/rule/communication.md +18 -8
  14. package/assets/init/.qfai/assistant/rule/constitution.md +5 -4
  15. package/assets/init/.qfai/assistant/rule/drift-protocol.md +72 -5
  16. package/assets/init/.qfai/assistant/rule/references/asset-authoring.md +72 -0
  17. package/assets/init/.qfai/assistant/rule/references/context-summary.md +30 -0
  18. package/assets/init/.qfai/assistant/rule/references/gate-failure-autorepair.md +42 -0
  19. package/assets/init/.qfai/assistant/rule/references/griller-recommendations.md +23 -0
  20. package/assets/init/.qfai/assistant/rule/references/host-backstops.md +60 -0
  21. package/assets/init/.qfai/assistant/rule/references/placeholder-scan.md +25 -0
  22. package/assets/init/.qfai/assistant/rule/references/reviewer-findings.md +41 -0
  23. package/assets/init/.qfai/assistant/rule/references/reviewer-remit.md +35 -0
  24. package/assets/init/.qfai/assistant/rule/references/worker-edit-boundary.md +48 -0
  25. package/assets/init/.qfai/assistant/rule/shared-skill-delegation-baseline.md +39 -162
  26. package/assets/init/.qfai/assistant/rule/shared-skill-operating-baseline.md +14 -145
  27. package/assets/init/.qfai/assistant/rule/stage-cost.md +1 -1
  28. package/assets/init/.qfai/assistant/rule/test-layers.md +42 -0
  29. package/assets/init/.qfai/assistant/rule/ui-definition-protocol.md +6 -1
  30. package/assets/init/.qfai/assistant/skill/qfai-configure/SKILL.md +11 -45
  31. package/assets/init/.qfai/assistant/skill/qfai-configure/references/principles.md +25 -0
  32. package/assets/init/.qfai/assistant/skill/qfai-discussion/SKILL.md +1 -1
  33. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/discussion-completion-matrix.md +12 -9
  34. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui-bearing-playbook.md +2 -4
  35. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux/platform_baselines.md +11 -0
  36. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux/review_audit_playbook.md +11 -0
  37. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux_best_practices.md +13 -42
  38. package/assets/init/.qfai/assistant/skill/qfai-discussion/templates/uiux/00_index.md +0 -16
  39. package/assets/init/.qfai/assistant/skill/qfai-grill/SKILL.md +1 -1
  40. package/assets/init/.qfai/assistant/skill/qfai-grilling/SKILL.md +16 -88
  41. package/assets/init/.qfai/assistant/skill/qfai-grilling/references/delegated-session.md +58 -0
  42. package/assets/init/.qfai/assistant/skill/qfai-grilling/references/session-record.md +42 -0
  43. package/assets/init/.qfai/assistant/skill/qfai-implement/SKILL.md +17 -4
  44. package/assets/init/.qfai/assistant/skill/qfai-implement/references/credential-reuse.md +9 -0
  45. package/assets/init/.qfai/assistant/skill/qfai-implement/references/cross-spec-ownership.md +41 -0
  46. package/assets/init/.qfai/assistant/skill/qfai-implement/references/oracle-strength.md +6 -1
  47. package/assets/init/.qfai/assistant/skill/qfai-implement/references/parallelization-policy.md +9 -1
  48. package/assets/init/.qfai/assistant/skill/qfai-implement/references/red-admissibility.md +47 -1
  49. package/assets/init/.qfai/assistant/skill/qfai-implement/references/ui-affecting.md +4 -3
  50. package/assets/init/.qfai/assistant/skill/qfai-maintain/SKILL.md +4 -4
  51. package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/SKILL.md +16 -10
  52. package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/references/migration-guide.md +10 -260
  53. package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/references/migration-placement.md +273 -0
  54. package/assets/init/.qfai/assistant/skill/qfai-prototyping/SKILL.md +5 -4
  55. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/design-md-spec.md +14 -5
  56. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/generator-prompt.md +10 -0
  57. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/handoff.md +1 -1
  58. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/iteration-loop.md +3 -4
  59. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/review-payload-schema.md +12 -5
  60. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/reviewer-prompt.md +33 -4
  61. package/assets/init/.qfai/assistant/skill/qfai-run/SKILL.md +34 -36
  62. package/assets/init/.qfai/assistant/skill/qfai-run/references/evidence-delivery.md +322 -0
  63. package/assets/init/.qfai/assistant/skill/qfai-run/references/extraction.md +67 -24
  64. package/assets/init/.qfai/assistant/skill/qfai-run/references/operator-screens.md +246 -5
  65. package/assets/init/.qfai/assistant/skill/qfai-run/references/plan.md +29 -5
  66. package/assets/init/.qfai/assistant/skill/qfai-run/references/stage-points.md +43 -0
  67. package/assets/init/.qfai/assistant/skill/qfai-sdd/SKILL.md +5 -4
  68. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/concrete-abstract-cycle.md +10 -1
  69. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/contract-artifact-rules.md +22 -9
  70. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-anti-patterns.md +41 -6
  71. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-md-authoring.md +11 -5
  72. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-md-brand-catalog.md +17 -2
  73. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/requirements-decomposition.md +10 -2
  74. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-execution-playbook.md +34 -9
  75. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-phase-checklists.md +4 -4
  76. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-quality-gate.md +2 -2
  77. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-triage.md +177 -7
  78. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/spec-traceability-rules.md +29 -5
  79. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-contract-guide.md +130 -1
  80. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-design-contract-normalization.md +4 -30
  81. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/change-request.md +39 -2
  82. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/contracts/db-contract.sample.sql +2 -2
  83. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/contracts/ui-contract.sample.yaml +52 -5
  84. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/03_contract/cli/cli-NNNN-title.md +2 -2
  85. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/03_contract/tech.md +1 -1
  86. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/decisions.md +7 -0
  87. package/assets/init/.qfai/assistant/skill/qfai-triage/SKILL.md +30 -20
  88. package/assets/init/.qfai/assistant/skill/qfai-verify/SKILL.md +6 -5
  89. package/assets/init/.qfai/assistant/skill/qfai-verify/references/context-load.md +9 -6
  90. package/assets/init/.qfai/assistant/skill/qfai-verify/references/verify-output-contract.md +92 -13
  91. package/assets/init/.qfai/assistant/skill/web-research/SKILL.md +37 -31
  92. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/brave-search/config.toml +2 -4
  93. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/brave-search/mcp-config.json +9 -5
  94. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/firecrawl/.mcp.json +1 -2
  95. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/firecrawl/config.toml +3 -3
  96. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/firecrawl/mcp-config.json +11 -8
  97. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/playwright/config.toml +1 -1
  98. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/playwright/mcp-config.json +7 -3
  99. package/assets/init/.qfai/assistant/step/common-design-md/STEP.md +17 -7
  100. package/assets/init/.qfai/assistant/step/common-gate-run/STEP.md +126 -13
  101. package/assets/init/.qfai/assistant/step/common-review-cycle/STEP.md +3 -2
  102. package/assets/init/.qfai/assistant/step/discussion-pack/STEP.md +1 -0
  103. package/assets/init/.qfai/assistant/step/discussion-research/STEP.md +2 -0
  104. package/assets/init/.qfai/assistant/step/discussion-uiux/STEP.md +12 -4
  105. package/assets/init/.qfai/assistant/step/implement-backport/STEP.md +1 -1
  106. package/assets/init/.qfai/assistant/step/implement-benchmark/STEP.md +7 -4
  107. package/assets/init/.qfai/assistant/step/implement-dep-bump/STEP.md +11 -1
  108. package/assets/init/.qfai/assistant/step/implement-diagnose/STEP.md +2 -0
  109. package/assets/init/.qfai/assistant/step/implement-oracle-parity/STEP.md +2 -2
  110. package/assets/init/.qfai/assistant/step/implement-quarantine/STEP.md +3 -3
  111. package/assets/init/.qfai/assistant/step/implement-refactor/STEP.md +2 -2
  112. package/assets/init/.qfai/assistant/step/implement-regression-fix/STEP.md +4 -3
  113. package/assets/init/.qfai/assistant/step/implement-retire/STEP.md +6 -3
  114. package/assets/init/.qfai/assistant/step/implement-revert/STEP.md +4 -3
  115. package/assets/init/.qfai/assistant/step/implement-scaffold/STEP.md +3 -3
  116. package/assets/init/.qfai/assistant/step/implement-seam/STEP.md +9 -6
  117. package/assets/init/.qfai/assistant/step/implement-stress-harness/STEP.md +1 -1
  118. package/assets/init/.qfai/assistant/step/implement-sweep/STEP.md +4 -3
  119. package/assets/init/.qfai/assistant/step/implement-tdd/STEP.md +42 -6
  120. package/assets/init/.qfai/assistant/step/implement-test-fix/STEP.md +2 -0
  121. package/assets/init/.qfai/assistant/step/implement-tooling/STEP.md +22 -2
  122. package/assets/init/.qfai/assistant/step/maintain-edit/STEP.md +18 -14
  123. package/assets/init/.qfai/assistant/step/prototyping-grill/STEP.md +4 -3
  124. package/assets/init/.qfai/assistant/step/prototyping-loop/STEP.md +4 -0
  125. package/assets/init/.qfai/assistant/step/prototyping-preflight/STEP.md +1 -3
  126. package/assets/init/.qfai/assistant/step/sdd-contract/STEP.md +11 -0
  127. package/assets/init/.qfai/assistant/step/sdd-flow/STEP.md +4 -0
  128. package/assets/init/.qfai/assistant/step/sdd-gate/STEP.md +4 -0
  129. package/assets/init/.qfai/assistant/step/sdd-story/STEP.md +10 -0
  130. package/assets/init/.qfai/assistant/step/sdd-triage/STEP.md +37 -11
  131. package/assets/init/.qfai/assistant/step/triage-answer/STEP.md +5 -4
  132. package/assets/init/.qfai/assistant/step/triage-close/STEP.md +33 -23
  133. package/assets/init/.qfai/assistant/step/triage-cluster/STEP.md +5 -3
  134. package/assets/init/.qfai/assistant/step/triage-decompose/STEP.md +2 -0
  135. package/assets/init/.qfai/assistant/step/triage-dedupe/STEP.md +4 -0
  136. package/assets/init/.qfai/assistant/step/triage-handoff/STEP.md +13 -4
  137. package/assets/init/.qfai/assistant/step/triage-investigate/STEP.md +8 -5
  138. package/assets/init/.qfai/assistant/step/triage-owned-cleanup/STEP.md +86 -0
  139. package/assets/init/.qfai/assistant/step/triage-request-info/STEP.md +2 -0
  140. package/assets/init/.qfai/assistant/step/triage-security-intake/STEP.md +6 -3
  141. package/assets/init/.qfai/assistant/step/verify-change-note/STEP.md +9 -5
  142. package/assets/init/.qfai/assistant/step/verify-commit/STEP.md +39 -6
  143. package/assets/init/.qfai/assistant/step/verify-external/STEP.md +5 -2
  144. package/assets/init/.qfai/assistant/step/verify-qfai-gate/STEP.md +10 -2
  145. package/assets/init/.qfai/assistant/step/verify-release-notes/STEP.md +1 -0
  146. package/assets/init/.qfai/assistant/step/verify-repeat-run/STEP.md +7 -5
  147. package/assets/init/.qfai/assistant/step/verify-repo-gate/STEP.md +136 -28
  148. package/assets/init/.qfai/waivers.yml +1 -1
  149. package/assets/init/root/.agents/rules/ai-readable-markdown.md +98 -0
  150. package/assets/init/root/.agents/rules/api-budget.md +191 -13
  151. package/assets/init/root/.agents/rules/documentation-clarity.md +192 -35
  152. package/assets/init/root/.agents/rules/grilling.md +4 -4
  153. package/assets/init/root/.agents/rules/interface-clarity.md +5 -5
  154. package/assets/init/root/.agents/rules/minimal-implementation.md +11 -3
  155. package/assets/init/root/.agents/rules/reminders.json +27 -14
  156. package/assets/init/root/.agents/rules/session-feedback.md +136 -0
  157. package/assets/init/root/.agents/rules/temporary-files.md +47 -5
  158. package/assets/init/root/.agents/rules/untrusted-content.md +3 -3
  159. package/assets/init/root/.agents/rules/user-questions.md +74 -36
  160. package/assets/init/root/.gitattributes +9 -0
  161. package/assets/init/root/.github/workflows/qfai-docs.yml +44 -12
  162. package/assets/init/root/.github/workflows/qfai-tests.yml +58 -15
  163. package/assets/init/root/.github/workflows/qfai-validate.yml +49 -42
  164. package/assets/init/root/AGENTS.md +23 -21
  165. package/assets/mdschema/story/03_contract/tech.mdschema.yml +7 -3
  166. package/assets/sandbox-templates/default-deny.yaml +9 -5
  167. package/assets/schemas/workflow/extraction.schema.json +85 -5
  168. package/assets/schemas/workflow/plan.schema.json +9 -2
  169. package/assets/scripts/check-mdschema.mjs +172 -5
  170. package/assets/uix-rev/comparison-review.md +1 -1
  171. package/assets/uix-rev/contracts-review.md +1 -2
  172. package/dist/cli/index.cjs +6499 -7688
  173. package/dist/cli/index.cjs.map +1 -1
  174. package/dist/cli/index.mjs +6449 -7641
  175. package/dist/cli/index.mjs.map +1 -1
  176. package/dist/index.cjs +5548 -7823
  177. package/dist/index.cjs.map +1 -1
  178. package/dist/index.d.cts +40 -666
  179. package/dist/index.d.ts +40 -666
  180. package/dist/index.mjs +5397 -7648
  181. package/dist/index.mjs.map +1 -1
  182. package/docs/MIGRATION-2.0.0.md +1 -1
  183. package/package.json +5 -7
  184. package/assets/init/root/CLAUDE.md +0 -45
package/README.md CHANGED
@@ -79,11 +79,21 @@ Then open your AI coding agent in the repository and describe the change in your
79
79
 
80
80
  > Let each customer register up to five notification addresses, with no duplicates.
81
81
 
82
+ If you only have an idea, say so: the run starts with a discussion that structures scope and open questions.
83
+
82
84
  The agent announces the goal, the stages it will run and the files it may change, then runs them.
83
85
  It asks you only what it cannot decide for you, such as whether to create a new story.
86
+ Answer its questions, or say `stop` to end the run.
84
87
  The stages fill the seeded story tree and follow the project's Standard commands in
85
88
  `<paths.contractsDir>/tech.md` (by default `.qfai/spec/03_contract/tech.md`) for its quality gates.
86
89
 
90
+ When the run ends, check the result:
91
+
92
+ ```bash
93
+ npx qfai validate
94
+ npx qfai report
95
+ ```
96
+
87
97
  To drive the stages yourself instead:
88
98
  Run `/qfai-discussion` and `/qfai-sdd` to fill the seeded story tree.
89
99
  See [Invoking a stage directly](#invoking-a-stage-directly-expert-path).
@@ -92,7 +102,7 @@ See [Invoking a stage directly](#invoking-a-stage-directly-expert-path).
92
102
 
93
103
  You state the change once, in your own words.
94
104
  The `qfai-run` skill reads it into facts: what it asks for, its risks, and how sure that reading is.
95
- `npx qfai workflow plan` then picks one of 35 fixed routes from those facts by ordered decision rules
105
+ `npx qfai workflow plan` then picks one of the fixed routes from those facts by ordered decision rules
96
106
  and returns the route's plan, writing nothing. The session runs each step of the plan in order and
97
107
  asks you only the decisions that are yours and each release. You type no stage name.
98
108
 
@@ -313,27 +323,22 @@ flowchart LR
313
323
  (`assistant/rule/`, `skill/`, `agent/` and `prompt/`), plus `qfai.config.yaml`.
314
324
  - Options:
315
325
 
316
- | Flag | Effect |
317
- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
318
- | `--dir <path>` | Output directory (default: the current directory). Wins over `--root` when both are given. |
319
- | `--root <path>` | Every other command reads this as the target directory; `init` reads it as the output directory too, but only when `--dir` is omitted. |
320
- | `--force` | Overwrite the shipped skills, steps, agents and rules, the rule masters under `.agents/rules/`, their host wrappers, and generated Copilot instructions, whether or not you edited them. Project content, `qfai.config.yaml`, `DESIGN.md`, `AGENTS.md`, `CLAUDE.md` and routing overrides are preserved. The managed `.gitignore` block and `core.symlinks` setting are repaired on every non-dry-run. |
321
- | `--dry-run` | Report what would change and write nothing. Use it to rehearse `--upgrade-assistant-tree`. |
322
- | `--upgrade-assistant-tree` | Copy recognized legacy assistant files into the singular tree without deleting a source or overwriting a destination. Migrate old spec packs with `/qfai-migration-v1-to-v2`. Unrecognized assistant files stay in place. |
323
- | `--yes` | Reserved for a future interactive mode; no behavioural difference today. |
324
- | `--verbose` | Expand the run report's `skipped` list to the full path listing. Off by default, so a no-op re-run prints the skip count and a pointer to this flag instead of every shipped asset path. It does not gate the written or removed listings: those are printed whenever they have entries, with or without this flag. |
325
- | `--help`, `-h` | Print the CLI usage banner and exit without writing anything. Accepted by every command, `init` included, and handled before the command runs. |
326
- | `--version`, `-V` | Print the installed QFAI version to stdout and exit 0. Accepted by every command, `init` included, and handled before the command runs, so it works outside a project too. |
327
-
328
- - `D-DEPRECATED-PATH` means the legacy assistant layout remains past its
329
- supported window. Run `npx qfai init --upgrade-assistant-tree` to copy
330
- recognized files into the current tree without deleting their sources.
326
+ | Flag | Effect |
327
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
328
+ | `--dir <path>` | Output directory (default: the current directory). Wins over `--root` when both are given. |
329
+ | `--root <path>` | Every other command reads this as the target directory; `init` reads it as the output directory too, but only when `--dir` is omitted. |
330
+ | `--force` | Overwrite the shipped skills, steps, agents and rules, the rule masters under `.agents/rules/`, their host wrappers, and generated Copilot instructions, whether or not you edited them. Project content, `qfai.config.yaml`, `DESIGN.md`, `AGENTS.md` and routing overrides are preserved. The managed `.gitignore` block and `core.symlinks` setting are repaired on every non-dry-run. |
331
+ | `--dry-run` | Report what would change and write nothing. |
332
+ | `--yes` | Reserved for a future interactive mode; no behavioural difference today. |
333
+ | `--verbose` | Expand the run report's `skipped` list to the full path listing. Off by default, so a no-op re-run prints the skip count and a pointer to this flag instead of every shipped asset path. It does not gate the written or removed listings: those are printed whenever they have entries, with or without this flag. |
334
+ | `--help`, `-h` | Print the CLI usage banner and exit without writing anything. Accepted by every command, `init` included, and handled before the command runs. |
335
+ | `--version`, `-V` | Print the installed QFAI version to stdout and exit 0. Accepted by every command, `init` included, and handled before the command runs, so it works outside a project too. |
331
336
 
332
337
  - `npx qfai validate`
333
338
  - Validates the story tree, contracts and test obligations, writes `.qfai/report/validate.json`,
334
339
  and appends run logs to `.qfai/report/run-*/`; use `--fail-on error` (or `--fail-on warning`) to turn it into a CI gate,
335
340
  and `--format github` to emit GitHub-friendly annotations.
336
- Use `--flow BF-0001` to scope a run to one business flow. `--spec` is retired.
341
+ Use `--flow BF-0001` to scope a run to one business flow.
337
342
  Use `--profile discussion|sdd|prototyping|atdd|tdd|verify` for local skill-owned checks; CI should use default/full validation (or `verify` / `tdd` for the dedicated CI gates).
338
343
  - `npx qfai report`
339
344
  - Produces a human-readable report (`report.md` by default) or an internal JSON export (`report.json`) from `validate.json`; use `--base-url` to link file paths in Markdown to your repository viewer.
@@ -403,6 +408,8 @@ paths:
403
408
  testsDir: tests
404
409
  validation:
405
410
  failOn: error # error | warning | never
411
+ staleTerms: # terms the specs must no longer state, such as a rejected option
412
+ - SMTP
406
413
  traceability:
407
414
  testFileGlobs:
408
415
  - "src/**/*.test.ts"
@@ -416,6 +423,7 @@ uiux:
416
423
 
417
424
  Notes.
418
425
 
426
+ - `validation.staleTerms` lists terms a project no longer wants in its specs. `qfai validate` warns on each line of a spec document that states one, apart from `decisions.md` and `open-questions.md`.
419
427
  - `validate.json` is a **public** surface: its keys are documented in
420
428
  `.qfai/assistant/skill/qfai-verify/references/validate-json-schema.md`.
421
429
  Changes to its keys follow the `@api` classification in
@@ -423,15 +431,29 @@ Notes.
423
431
  issue order are not stable; match on `issues[].code`.
424
432
  - `report.json`, `doctor.json`, and `run-*` JSON logs are internal exports and are not a stable external contract; prefer `report.md` for integrations that must survive tool upgrades.
425
433
 
426
- ## Minimal tutorial
434
+ ## Dependency security
435
+
436
+ The glob-matching dependency `braces` has an unresolved
437
+ [stack-exhaustion denial-of-service advisory](https://github.com/advisories/GHSA-vfj7-8cjw-p6xm).
438
+ QFAI accepts this residual risk only for reviewed project patterns and paths,
439
+ trusted configuration and trusted SDK callers. This is risk acceptance, not a
440
+ clean audit or proof that QFAI is unaffected. Reproduction and exploitation in
441
+ QFAI remain unverified.
442
+
443
+ Review include, ignore and negative patterns, constructed paths and SDK inputs.
444
+ `validateProject` accepts a caller-supplied `ConfigLoadResult` without invoking
445
+ the configuration loader; the caller must review those values too. Catching
446
+ errors and limiting results after glob expansion do not defend against stack
447
+ exhaustion during expansion.
427
448
 
428
- 1. `npx qfai init`
429
- 2. Open your AI coding agent in the repository and describe the change in your own words.
430
- If you only have an idea, say so: the run starts with a discussion that structures scope and open questions.
431
- 3. Answer the questions the work puts to you, or say `stop` to end it.
432
- 4. Run `npx qfai validate` then `npx qfai report`.
449
+ Untrusted pull requests, externally supplied configuration and service request
450
+ inputs are outside this acceptance. Review those inputs and restrict access;
451
+ do not expose glob processing to them until your mitigation and security review
452
+ cover them.
433
453
 
434
- To choose each stage yourself, see [Invoking a stage directly](#invoking-a-stage-directly-expert-path).
454
+ Reassess the risk when a compatible patch becomes available or untrusted input
455
+ is needed. A forced downgrade to `qfai@0.4.0` is not a compatible fix for the
456
+ current major API; do not apply it through `npm audit fix --force`.
435
457
 
436
458
  ## FAQ
437
459
 
@@ -464,10 +486,16 @@ there, and all three open with a ``# Generated by `qfai init` `` line. All three
464
486
  every push to `main` or `master` and on every pull request, and all three run on the
465
487
  runner `vars.QFAI_CI_RUNNER` names (`ubuntu-latest` when you set nothing).
466
488
 
489
+ `npx qfai init` writes a workflow again whenever its file is missing. To stop it
490
+ writing one, set `workflow.skipShipped` in `qfai.config.yaml` to a list of file
491
+ names, for example `[qfai-tests.yml]`, and delete the file.
492
+
467
493
  - `qfai-validate.yml` runs `npx qfai validate --profile full --fail-on error`,
468
- and on a pull request also `npx qfai validate --profile drift --fail-on error`.
469
- The `full` profile evaluates every gate group except drift, so without the
470
- second run nothing there could fail on a downstream edit to upstream SSOT.
494
+ and on a pull request also `npx qfai validate --profile drift --fail-on error`,
495
+ as a second step of the same job. The `full` profile evaluates every gate group
496
+ except drift, so without the second run nothing there could fail on a downstream
497
+ edit to upstream SSOT. The second step runs after a failing first one, so one run
498
+ reports both profiles, and the dependencies are installed once.
471
499
  It installs dependencies from whichever lockfile the repository has (pnpm /
472
500
  yarn / npm) and falls back to `npm install` when there is none, and it takes
473
501
  the Node version from your `.nvmrc` or `.node-version`, warning and
@@ -477,9 +505,10 @@ runner `vars.QFAI_CI_RUNNER` names (`ubuntu-latest` when you set nothing).
477
505
  holding a `pnpm-lock.yaml` with no such field fails the job with an
478
506
  annotation naming the field rather than reporting a validation it never ran.
479
507
  Declare `"packageManager": "pnpm@X.Y.Z"` so CI matches the version you
480
- develop against. The `full` profile includes the `QFAI-TEST-001` test-todo
481
- stub gate, so the job can fail your default branch on findings your existing
482
- CI never checked.
508
+ develop against. The `full` profile includes the test stub gate
509
+ (`QFAI-TEST-001` for a todo, `QFAI-TEST-003` for a skip), so the job can fail
510
+ your default branch on findings your existing CI never checked. See
511
+ "Skipped and todo tests" below for what the gate reports and what it cannot see.
483
512
  - `qfai-tests.yml` declares one lane per test layer (unit, component,
484
513
  integration, api, e2e) and runs none of them until you opt in: a lane runs
485
514
  only when your `package.json` declares the matching `test:<layer>` script
@@ -506,7 +535,32 @@ A push to `main` or `master` runs the test lanes and the document checks again b
506
535
  default, because nothing in the files can tell whether that commit passed a pull
507
536
  request first. If your branch protection requires these checks before every merge,
508
537
  set the repository variable `QFAI_CI_PUSH_POLICY` to `protected`: the push then runs
509
- neither, and `qfai validate` still runs as the post-merge check.
538
+ neither, and `qfai validate` still runs as the post-merge check. If you run
539
+ `qfai validate` in a post-merge job of your own, set it to `none` instead: the
540
+ push then starts no job of the validate workflow either.
541
+
542
+ `protected` is safe only when GitHub requires the branch to be up to date before
543
+ a pull request can merge: the branch protection option "Require branches to be up
544
+ to date before merging", called `strict` in the API. Without it, two pull requests
545
+ can each pass on their own and break the branch once both are merged. One that
546
+ changes a function signature and one that starts calling the old signature is the
547
+ usual case. The default branch is then red while every pull request was green.
548
+
549
+ Requiring up-to-date branches makes every parallel pull request update and rerun
550
+ after each merge. If that costs too much, there is a middle option between running
551
+ the full lanes again and running nothing: keep `QFAI_CI_PUSH_POLICY` at
552
+ `protected`, and add a small post-merge job of your own to a workflow you
553
+ maintain.
554
+
555
+ - It runs only what can break when two pull requests combine: the type check, and
556
+ the schema or contract consistency checks. The shipped `qfai-validate.yml`
557
+ already runs the validator on every push, so the job does not repeat it.
558
+ - It installs dependencies once, and uses one concurrency group with
559
+ `cancel-in-progress: true`, so a newer push cancels an older run. The newest
560
+ commit contains the older ones, so nothing goes unchecked.
561
+ - It leaves out the test suites. A break that only they would catch shows up on
562
+ the next pull request, which fails on code it did not change. In one project the
563
+ job took about two billed minutes where the full lanes took about 29.
510
564
 
511
565
  All three files are copied create-only — `qfai init` writes a workflow only
512
566
  where no file of that name exists, and never overwrites one, not even with
@@ -572,11 +626,231 @@ Waiver policy.
572
626
  - Expired waivers are reported as warnings (`QFAI-WAIVER-003`) and must be renewed or removed with evidence.
573
627
  - Suppressed findings remain visible in reports as `suppressed=true`; waivers do not erase findings.
574
628
 
629
+ When upgrading a project with `.qfai/waivers.yml`, back up that file. Check the
630
+ current public finding codes and the release notes for confirmed removals or
631
+ renames. Update a `rule:` value only when the documented rename preserves the
632
+ finding's meaning and scope. Remove confirmed retired or inapplicable waivers,
633
+ keeping the backup and recording each removal's reason for the operator. Do not
634
+ rename whole code prefixes or automatically delete an unknown rule.
635
+
636
+ Rerun full validation and require zero errors. An unknown waiver rule produces
637
+ `QFAI-WAIVER-004` at `warning` and is not applied in that run; investigate it
638
+ against the current diagnostics and release notes.
639
+
575
640
  Typical customizations.
576
641
 
577
642
  - Add a `doctor` step before validate if you want to fail fast on path/glob/config issues.
578
643
  - Publish `.qfai/report/validate.json`, `report.md`, and relevant `.qfai/report/run-*/` logs as CI artifacts.
579
644
 
645
+ ### What a run costs
646
+
647
+ On a private repository with GitHub-hosted runners, cost follows the number of
648
+ jobs, not how long they run. Each job rounds up to a whole minute, a skipped job
649
+ costs nothing, and a workflow that starts several quick jobs on every event costs
650
+ more than one slow job. A closed pull request starts a run too, but every job in
651
+ it declines, so nothing is billed.
652
+
653
+ Jobs the shipped workflows start on each `opened`, `synchronize` and `reopened`
654
+ event of a pull request:
655
+
656
+ | Workflow | Nothing to run | Lane runs |
657
+ | ------------------- | --------------------------------- | --------------------------------------------------- |
658
+ | `qfai-validate.yml` | 1: `qfai validate`, both profiles | 1 |
659
+ | `qfai-tests.yml` | 1: `change detection` | 2, plus one per selected lane (at most 5) |
660
+ | `qfai-docs.yml` | 1: `change scope` | 4: adds the `shape` and `mermaid` checks and `docs` |
661
+
662
+ When nothing is selected, `verdict` is skipped, and when nothing a document check
663
+ reads changed, `docs` is skipped. Each workflow then starts one job, and the
664
+ detection and scope jobs say in a notice annotation why. One pull request event
665
+ starts 3 jobs in all, and the most it can start is 12. On a push to the default
666
+ branch, `qfai-validate.yml` starts 1 job, `QFAI_CI_PUSH_POLICY=protected` brings the
667
+ other two workflows down to their idle counts, and `none` also skips the validate
668
+ job, which leaves `change detection` and `change scope`.
669
+
670
+ Four of those jobs install nothing and run no test: `change detection`, `verdict`,
671
+ `change scope` and `docs`. They take seconds, so each bills one minute.
672
+ They read `vars.QFAI_CI_LIGHT_RUNNER`, so the price of that minute is a choice.
673
+ On a private repository with GitHub-hosted runners, set it to `ubuntu-slim`:
674
+
675
+ ```bash
676
+ gh variable set QFAI_CI_LIGHT_RUNNER --body ubuntu-slim
677
+ ```
678
+
679
+ `ubuntu-slim` has 1 CPU and 5 GB of memory, runs unprivileged, limits a job to 15
680
+ minutes, and bills at a lower per-minute rate than `ubuntu-latest`; GitHub's
681
+ pricing page has the current figure. The light jobs run only Bash, Git and Node, all
682
+ of which it carries, and the workflows keep `ubuntu-latest` as the default, so this
683
+ is your variable to set. Do not point `QFAI_CI_RUNNER` at it: the jobs that install
684
+ dependencies, need Docker or can run past 15 minutes do not belong on it. A label
685
+ GitHub does not offer to your account leaves a job queued rather than failing it.
686
+
687
+ Each workflow file states its own counts in a "Jobs per pull request event" row of
688
+ its header table. `npx qfai doctor` reports the same counts for the workflows
689
+ installed in your repository, with the runner each job asks for. It is an `info`
690
+ finding and reads the files only. It reads none of your repository variables, so it
691
+ shows the variables a job falls back through and not the label they resolve to.
692
+
693
+ ### Required checks and branch protection
694
+
695
+ Require these three checks, by exactly these names:
696
+
697
+ - From `qfai-tests.yml`: `verdict`
698
+ - From `qfai-docs.yml`: `qfai docs (document shape and Mermaid syntax)`
699
+ - From `qfai-validate.yml`: `qfai validate (full profile, fail on error)`
700
+
701
+ Each one fails when the work behind it fails. `verdict` and the `qfai docs` check
702
+ are skipped when nothing was selected or nothing a document check reads changed,
703
+ and GitHub counts a skipped job as satisfying a required check. The jobs behind
704
+ them are named by their matrix value, such as `qfai tests (unit)`, and a skipped
705
+ matrix job is reported under its unexpanded name, so a required check cannot name
706
+ them.
707
+
708
+ A check name is what branch protection matches. When you rename a job, or change
709
+ what it covers, change the required check in the same step. While the two differ
710
+ the required check is never reported, and every pull request is blocked. The
711
+ validate check above is the one to watch: on a pull request it covers the `full`
712
+ and `drift` profiles, and if you change the profiles you run, its name keeps
713
+ saying "full profile".
714
+
715
+ A job that an `if:` condition skips is reported as skipped, and that satisfies a
716
+ required check. A workflow that a workflow-level `paths` or `paths-ignore` filter
717
+ keeps from starting reports nothing, so its required check stays pending and the
718
+ pull request cannot merge. Put the decision in a job, and skip jobs. The shipped
719
+ `change detection` and `change scope` jobs do that. Do not put a `paths` filter on
720
+ a workflow with a required check.
721
+
722
+ The "Required check" row of each workflow's header table names the job to require.
723
+
724
+ ### Checking workflow files
725
+
726
+ GitHub starts no job from a workflow file it cannot parse. The run shows as failed
727
+ with no jobs, on every push to every branch, and a change to the file does not show
728
+ up in its own pull request's checks, because the broken workflow never starts. A key
729
+ that appears twice in one mapping is enough, such as `env:` twice in one step after a
730
+ scripted edit, and many YAML loaders accept it without a word.
731
+
732
+ `npx qfai doctor` parses each `.yml` and `.yaml` file under `.github/workflows/` and
733
+ `.github/actions/` and warns with the file and the line. Run `actionlint` as well, and
734
+ run it after any scripted edit to a workflow. It also reports duplicate keys with line
735
+ numbers, and it checks expressions and job references that a YAML parser cannot. It
736
+ can be one step of a job you already run, such as a change-detection job, so it adds
737
+ no job.
738
+
739
+ ### Skipped and todo tests
740
+
741
+ The stub gate is a static scan of the files that
742
+ `validation.traceability.testFileGlobs` selects. It blanks comments and string
743
+ literals first, then matches text patterns. The shipped workflow runs
744
+ `--fail-on error`, and every finding below is an error.
745
+
746
+ | What the scan finds | Finding |
747
+ | -------------------------------------------------------------------------------------------------------- | --------------- |
748
+ | `it.todo`, `test.todo`, `describe.todo` | `QFAI-TEST-001` |
749
+ | `it.skip`, `test.skip`, `describe.skip`, with a modifier before or after (`.skip.each`, `.concurrent`) | `QFAI-TEST-003` |
750
+ | The other stacks' placeholders: `pytest.skip`, `t.Skip`, `@Disabled`, `@Ignore`, `#[ignore]`, `[Ignore]` | `QFAI-TEST-001` |
751
+
752
+ It cannot see a skip that is not a text pattern, and none of these is reported:
753
+
754
+ - a conditional skip: `describe.skipIf(...)`, `it.skipIf(...)` and `it.runIf(...)`
755
+ - a form that selects the skip at run time, such as
756
+ `const suite = enabled ? describe : describe.skip`
757
+ - a skip called inside a running test, such as `ctx.skip()`
758
+ - a suite that returns early when the environment is missing, such as an unset
759
+ database URL
760
+ - a test file that the globs do not select; with no globs set the scan reports
761
+ `QFAI-TEST-002` and reads nothing
762
+
763
+ A suite skipped for a missing database therefore passes CI, and so does a suite
764
+ whose database failed to start. Only the test runner knows what was skipped in a
765
+ given run. Have it write a JSON report, and fail the job when a file skips more
766
+ tests than a list of named, allowed skips permits. Jest and Vitest write the same
767
+ shape: `jest --json --outputFile=test-report.json`, or
768
+ `vitest run --reporter=json --outputFile=test-report.json`. Add the flags to
769
+ your `test:<layer>` script, save this script as `scripts/check-skipped-tests.mjs`,
770
+ and run it in a step of `qfai-tests.yml` after the step that runs the tests:
771
+
772
+ ```js
773
+ import { readFileSync } from "node:fs";
774
+ import path from "node:path";
775
+
776
+ const allowed = JSON.parse(readFileSync("allowed-skips.json", "utf8"));
777
+ const report = JSON.parse(readFileSync("test-report.json", "utf8"));
778
+ let failed = false;
779
+ for (const file of report.testResults) {
780
+ const name = path.relative(process.cwd(), file.name).split(path.sep).join("/");
781
+ const skipped = file.assertionResults.filter(
782
+ (test) => test.status !== "passed" && test.status !== "failed",
783
+ ).length;
784
+ if (skipped > (allowed[name] ?? 0)) {
785
+ console.error(`${name}: ${skipped} tests did not run, ${allowed[name] ?? 0} allowed`);
786
+ failed = true;
787
+ }
788
+ }
789
+ process.exit(failed ? 1 : 0);
790
+ ```
791
+
792
+ `allowed-skips.json` maps a test file to the number of tests it may skip, such as
793
+ `{ "orders.test.ts": 2 }`. Start from the count you measure today. The
794
+ list should only ever shrink: a pull request may remove an entry or lower a count,
795
+ and raising one is a decision a reviewer makes on purpose.
796
+
797
+ ### Writing a change-scope gate for your own workflows
798
+
799
+ If you write a gate that skips your own heavy jobs on a small change, copy the
800
+ shape of the shipped `change detection` job: it falls back to running every lane
801
+ when the base commit is missing, the clone is shallow, the base is unreachable, the
802
+ diff fails, or a changed path is outside the set it knows. The rules that keep a
803
+ gate from skipping what it should have run:
804
+
805
+ - Decide from what the runners and tests read, not from directory names.
806
+ - Fail open. When the input is unknown, run everything: a skipped check claims the
807
+ change is fine, and a check that runs costs only its minutes.
808
+ - Run the gate's own table of cases, sample paths with the decision each must
809
+ get, before the gate. A pull request that changes the gate runs the checks that
810
+ cover the gate.
811
+ - Keep job names fixed, so required checks stay satisfied.
812
+ - Decide in a job and skip jobs with `if:`. Never use a workflow-level `paths`
813
+ filter, as the section above explains.
814
+
815
+ Three mistakes this prevents:
816
+
817
+ - **Excluding a directory the runner reads.** A gate treated all of
818
+ `tests/integration/` as irrelevant to end-to-end tests, but the browser runner's
819
+ configuration runs the `*.browser.spec.ts` files in it. A pull request that fixed
820
+ one of them ran no end-to-end job and passed. Exclude only the files the runner
821
+ does not read.
822
+ - **Trusting a general plan over the repository.** "A change to tests only can
823
+ skip the production build" is wrong where the build type-checks the tests
824
+ through the compiler's `include`. Read the configuration before you write the
825
+ rule.
826
+ - **A gate that skips its own tests.** See the third rule above.
827
+
828
+ ### Measuring Actions minutes
829
+
830
+ Cost on a private repository is hard to read from the outside. The billing
831
+ endpoint for an organization needs an admin scope and may answer with an error,
832
+ the timing endpoint for a single run reports `billable` as zero, and the detailed
833
+ usage report needs an admin. This method needs only read access to the repository:
834
+
835
+ 1. List the runs of one pull request, by its head commit:
836
+ `gh api "repos/OWNER/REPO/actions/runs?head_sha=SHA" --jq '.workflow_runs[].id'`
837
+ 2. For each run, list its jobs:
838
+ `gh api "repos/OWNER/REPO/actions/runs/RUN_ID/jobs?per_page=100" --jq '.jobs[] | [.name, .conclusion, .started_at, .completed_at] | @tsv'`
839
+ 3. A job bills `max(1, ceil(seconds / 60))` minutes, where the seconds run from
840
+ `started_at` to `completed_at`. A skipped job bills zero.
841
+ 4. Compare pull requests by the same author and of the same kind of change, and
842
+ compare jobs and runs as well as minutes. Pull requests that only changed CI
843
+ settings are the case a change helps most, so they overstate the saving.
844
+ Compare pull requests that changed source and tests.
845
+ 5. Count the runs of a day with
846
+ `gh api "repos/OWNER/REPO/actions/runs?created=YYYY-MM-DD&per_page=1" --jq .total_count`.
847
+ It is one cheap call per day, and it shows days that stand out. It also follows
848
+ the amount of development, so it cannot prove a reduction by itself.
849
+
850
+ A job that never started looks different from a job that failed: it has no steps,
851
+ an empty runner name and a duration of about two seconds. Its logs are not found,
852
+ which is not the logs having expired.
853
+
580
854
  ### Keeping QFAI itself up to date
581
855
 
582
856
  A `qfai` bump is the one dependency update that is not finished when the version
@@ -590,6 +864,23 @@ Moving a project from the spec-pack layout to the story tree also requires
590
864
  `/qfai-migration-v1-to-v2`. `init --force` refreshes shipped assets; the
591
865
  migration skill moves project content and reports items that need a person.
592
866
 
867
+ To upgrade by hand:
868
+
869
+ 1. Install the new version with your package manager, for example
870
+ `npm i -D qfai@<version>`.
871
+ 2. Move every edit you made to a shipped skill, step, agent or rule into
872
+ `.qfai/assistant/skill.local/` or a rule overlay. The next step overwrites
873
+ the shipped copies.
874
+ 3. Run `npx qfai init --force`. It replaces the skills and steps an agent may
875
+ be reading, so an agent upgrading the project reads them again from disk
876
+ afterwards.
877
+ 4. Delete the files the release notes of the new version list as retired, once
878
+ nothing you keep reads them.
879
+ 5. Run `npx qfai doctor` and `npx qfai validate`, and fix what they report.
880
+ 6. Commit the result in one change.
881
+
882
+ An agent asked to upgrade `qfai` follows these steps as a dependency update.
883
+
593
884
  Merging the bump on its own leaves the repository claiming a version whose
594
885
  skills it does not have. If you use Renovate, QFAI publishes a preset that keeps
595
886
  the two together:
@@ -668,7 +959,6 @@ commit that bumps the package, and to keep the two from being merged separately.
668
959
  │ │ └── open-questions.md
669
960
  │ └── waivers.yml
670
961
  ├── AGENTS.md
671
- ├── CLAUDE.md
672
962
  └── qfai.config.yaml
673
963
  ```
674
964
 
@@ -705,17 +995,6 @@ provide those defaults to the agent.
705
995
  The `.claude` / `.github` agent wrappers are symlinks and follow the canonical document
706
996
  automatically; the Codex profiles are generated files, so rerun `npx qfai init --force`
707
997
  to refresh them (and any other wrapper asset that has drifted).
708
- `--force` deletes as well as overwrites: it removes the command and prompt wrappers
709
- under `.claude/commands/` and `.github/prompts/`, and the skill wrappers under
710
- `.claude/skills/`, `.agents/skills/`, `.codex/skills/` and `.github/skills/` for skills that are
711
- not in the current release, whether those wrappers are directories or symlinks.
712
- Files it did not write — a project's own slash command, prompt file or skill,
713
- including one published from a project-authored `.qfai/assistant/skill/` entry — are left in place,
714
- whatever they are named. Ownership is read from the file, not the name: a wrapper is removed only when
715
- it carries the delegation line to the canonical document of the same name, or is a symlink into
716
- `.qfai/assistant/skill/`. One consequence of that: a symlink has no content to prove who wrote it, so
717
- if you publish a canonical skill of your own under a name QFAI itself once shipped, `--force` removes
718
- that link. Your `.qfai/assistant/skill/` entry is untouched; re-create the link to publish it again.
719
998
 
720
999
  ### Supported hosts
721
1000
 
@@ -725,7 +1004,9 @@ No host is declared supported in this release.
725
1004
  ### Cross-AI rules and the writing reminder
726
1005
 
727
1006
  `npx qfai init` seeds `.agents/rules/**`, the rule set every AI coding agent in
728
- the project shares, and points `AGENTS.md` and `CLAUDE.md` at it. One of those
1007
+ the project shares, and points `AGENTS.md` at it, with a line for each rule that says when
1008
+ to read it. Claude Code reads `AGENTS.md` when the project has no `CLAUDE.md` (Claude Code
1009
+ 2.1.277 or later). One of those
729
1010
  rules, `documentation-clarity.md`, sets the writing standard for pull requests,
730
1011
  issues, code comments and Markdown: no project-local identifiers, no account of
731
1012
  how the work went, cut to what a reader outside the team can follow.
@@ -747,8 +1028,8 @@ Each hook runs `node` directly, with no shell and no network, and prints one
747
1028
  message from `.agents/rules/reminders.json`. A missing or unreadable message
748
1029
  file prints nothing. `qfai init` refreshes that file wherever the project has
749
1030
  not edited it, so a new release's wording reaches an existing project without
750
- changing `.claude/settings.json`. Remove the entries to turn the reminder off;
751
- the rule still applies.
1031
+ changing `.claude/settings.json`. Remove the entries from that file and from
1032
+ `.codex/hooks.json` to turn the reminder off; the rule still applies.
752
1033
 
753
1034
  Codex gets the same reminders from `.codex/hooks.json` once you trust the
754
1035
  project's hooks with `/hooks`. Each is one command line that runs the same under
@@ -764,15 +1045,6 @@ kept, and the run names it. To update one by hand, run
764
1045
  `npx qfai init --dir <scratch-dir>` in an unused scratch directory and compare
765
1046
  its `.claude/settings.json` with your project's file, group by group.
766
1047
 
767
- ## Contributing (for QFAI maintainers)
768
-
769
- This repository is a monorepo, and the distributable package is under `packages/qfai`.
770
- The repository root `README.md` and `packages/qfai/README.md` are kept aligned by
771
- `scripts/check-readme-alignment.mjs`, which CI runs as part of `pnpm ci:lint`: every line
772
- outside a `readme-align:ignore-start` / `readme-align:ignore-end` HTML-comment block must be
773
- identical in both files. When you change documentation, apply the edit to both READMEs, or
774
- wrap the intentionally file-specific part in those markers.
775
-
776
1048
  ## License
777
1049
 
778
1050
  MIT
@@ -4,6 +4,15 @@
4
4
  routing:
5
5
  # The triage steps change no tracked file, so each has one author and no
6
6
  # build or test phase.
7
+ - step: triage-owned-cleanup
8
+ phases:
9
+ - id: cleanup
10
+ mandatory_agents: [devops-ci-engineer]
11
+ conditional_agents: []
12
+ parallel_groups: []
13
+ rerun_policy: changed-scope-dependents
14
+ review_profile: default
15
+
7
16
  - step: triage-close
8
17
  phases:
9
18
  - id: record
@@ -1,13 +1,9 @@
1
1
  route: change-tooling
2
2
  family: upkeep
3
3
  stages:
4
- - id: diagnose
5
- kind: diagnose
6
- steps: [implement-diagnose]
7
4
  - id: edit
8
5
  kind: implement
9
6
  steps: [implement-tooling]
10
- after: [diagnose]
11
7
  - id: note
12
8
  kind: verify
13
9
  steps:
@@ -0,0 +1,12 @@
1
+ route: run-owned-cleanup
2
+ family: upkeep
3
+ stages:
4
+ - id: cleanup
5
+ kind: triage
6
+ steps: [triage-owned-cleanup]
7
+ - id: close
8
+ kind: triage
9
+ steps: [triage-close]
10
+ after: [cleanup]
11
+ decisionPoints: []
12
+ branchPoints: []