qfai 2.1.1 → 2.2.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 (185) hide show
  1. package/README.md +56 -36
  2. package/assets/defaults/agent-routing/qfai-implement.yml +9 -0
  3. package/assets/defaults/agent-routing/qfai-triage.yml +18 -0
  4. package/assets/defaults/workflows/answer-in-session.yml +8 -0
  5. package/assets/defaults/workflows/quarantine-flaky.yml +3 -0
  6. package/assets/defaults/workflows/repair-consistency.yml +1 -0
  7. package/assets/defaults/workflows/run-operation.yml +11 -0
  8. package/assets/defaults/workflows/run-owned-cleanup.yml +12 -0
  9. package/assets/defaults/workflows/write-procedure.yml +18 -0
  10. package/assets/graph/codegraph-native.mjs +174 -0
  11. package/assets/graph/graphify-native.py +72 -0
  12. package/assets/init/.claude/settings.json +9 -9
  13. package/assets/init/.codex/hooks.json +8 -8
  14. package/assets/init/.qfai/assistant/agent/frontend-engineer.md +5 -0
  15. package/assets/init/.qfai/assistant/agent/product-experience-architect.md +4 -0
  16. package/assets/init/.qfai/assistant/agent/product-surface-reviewer.md +7 -0
  17. package/assets/init/.qfai/assistant/agent/requirements-analyst.md +8 -0
  18. package/assets/init/.qfai/assistant/rule/communication.md +18 -8
  19. package/assets/init/.qfai/assistant/rule/constitution.md +31 -15
  20. package/assets/init/.qfai/assistant/rule/drift-protocol.md +119 -2
  21. package/assets/init/.qfai/assistant/rule/references/asset-authoring.md +6 -4
  22. package/assets/init/.qfai/assistant/rule/references/codex-delegation.md +27 -0
  23. package/assets/init/.qfai/assistant/rule/references/griller-recommendations.md +21 -9
  24. package/assets/init/.qfai/assistant/rule/references/worker-edit-boundary.md +48 -0
  25. package/assets/init/.qfai/assistant/rule/review-convergence.md +25 -12
  26. package/assets/init/.qfai/assistant/rule/shared-skill-delegation-baseline.md +16 -25
  27. package/assets/init/.qfai/assistant/rule/shared-skill-operating-baseline.md +16 -11
  28. package/assets/init/.qfai/assistant/rule/ui-definition-protocol.md +6 -1
  29. package/assets/init/.qfai/assistant/rule/workflow.md +2 -1
  30. package/assets/init/.qfai/assistant/skill/qfai-configure/SKILL.md +3 -3
  31. package/assets/init/.qfai/assistant/skill/qfai-discussion/SKILL.md +6 -0
  32. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/discussion-completion-matrix.md +20 -15
  33. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui-bearing-playbook.md +2 -4
  34. package/assets/init/.qfai/assistant/skill/qfai-discussion/references/ui_ux_best_practices.md +1 -6
  35. package/assets/init/.qfai/assistant/skill/qfai-discussion/templates/uiux/00_index.md +0 -16
  36. package/assets/init/.qfai/assistant/skill/qfai-grilling/SKILL.md +30 -20
  37. package/assets/init/.qfai/assistant/skill/qfai-grilling/references/delegated-session.md +4 -3
  38. package/assets/init/.qfai/assistant/skill/qfai-grilling/references/session-record.md +8 -2
  39. package/assets/init/.qfai/assistant/skill/qfai-implement/SKILL.md +16 -3
  40. package/assets/init/.qfai/assistant/skill/qfai-implement/references/parallelization-policy.md +9 -1
  41. package/assets/init/.qfai/assistant/skill/qfai-implement/references/red-admissibility.md +46 -0
  42. package/assets/init/.qfai/assistant/skill/qfai-implement/references/ui-affecting.md +5 -2
  43. package/assets/init/.qfai/assistant/skill/qfai-maintain/SKILL.md +3 -3
  44. package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/SKILL.md +12 -6
  45. package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/references/migration-guide.md +1 -265
  46. package/assets/init/.qfai/assistant/skill/qfai-migration-v1-to-v2/references/migration-placement.md +273 -0
  47. package/assets/init/.qfai/assistant/skill/qfai-prototyping/SKILL.md +4 -3
  48. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/review-payload-schema.md +2 -3
  49. package/assets/init/.qfai/assistant/skill/qfai-prototyping/references/reviewer-prompt.md +17 -1
  50. package/assets/init/.qfai/assistant/skill/qfai-run/SKILL.md +52 -37
  51. package/assets/init/.qfai/assistant/skill/qfai-run/references/codex-cloud-install.md +80 -0
  52. package/assets/init/.qfai/assistant/skill/qfai-run/references/evidence-delivery.md +322 -0
  53. package/assets/init/.qfai/assistant/skill/qfai-run/references/extraction.md +83 -49
  54. package/assets/init/.qfai/assistant/skill/qfai-run/references/operator-screens.md +226 -11
  55. package/assets/init/.qfai/assistant/skill/qfai-run/references/plan.md +6 -3
  56. package/assets/init/.qfai/assistant/skill/qfai-run/references/reached-step-observations.md +132 -0
  57. package/assets/init/.qfai/assistant/skill/qfai-run/references/stage-points.md +14 -0
  58. package/assets/init/.qfai/assistant/skill/qfai-run/scripts/codex-cloud-install.mjs +117 -0
  59. package/assets/init/.qfai/assistant/skill/qfai-sdd/SKILL.md +4 -3
  60. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/contract-artifact-rules.md +1 -0
  61. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-anti-patterns.md +23 -5
  62. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/design-md-authoring.md +20 -7
  63. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/requirements-decomposition.md +8 -0
  64. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-execution-playbook.md +29 -4
  65. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-phase-checklists.md +1 -1
  66. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-pre-draft-grilling.md +4 -1
  67. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-quality-gate.md +1 -1
  68. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/sdd-triage.md +159 -4
  69. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/spec-traceability-rules.md +14 -2
  70. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-contract-guide.md +146 -0
  71. package/assets/init/.qfai/assistant/skill/qfai-sdd/references/ui-design-contract-normalization.md +4 -29
  72. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/change-request.md +38 -1
  73. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/contracts/ui-contract.sample.yaml +52 -5
  74. package/assets/init/.qfai/assistant/skill/qfai-sdd/templates/spec/decisions.md +12 -0
  75. package/assets/init/.qfai/assistant/skill/qfai-triage/SKILL.md +34 -21
  76. package/assets/init/.qfai/assistant/skill/qfai-verify/SKILL.md +5 -4
  77. package/assets/init/.qfai/assistant/skill/qfai-verify/references/context-load.md +9 -6
  78. package/assets/init/.qfai/assistant/skill/qfai-verify/references/verify-output-contract.md +80 -15
  79. package/assets/init/.qfai/assistant/skill/web-research/SKILL.md +35 -21
  80. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/brave-search/config.toml +2 -4
  81. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/brave-search/mcp-config.json +9 -5
  82. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/firecrawl/.mcp.json +1 -2
  83. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/firecrawl/config.toml +3 -3
  84. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/firecrawl/mcp-config.json +11 -8
  85. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/playwright/config.toml +1 -1
  86. package/assets/init/.qfai/assistant/skill/web-research/mcp-templates/playwright/mcp-config.json +7 -3
  87. package/assets/init/.qfai/assistant/step/common-design-md/STEP.md +33 -20
  88. package/assets/init/.qfai/assistant/step/common-gate-run/STEP.md +124 -8
  89. package/assets/init/.qfai/assistant/step/common-grilling-record/STEP.md +10 -2
  90. package/assets/init/.qfai/assistant/step/common-policy-check/STEP.md +1 -0
  91. package/assets/init/.qfai/assistant/step/common-review-cycle/STEP.md +26 -9
  92. package/assets/init/.qfai/assistant/step/discussion-interview/STEP.md +15 -8
  93. package/assets/init/.qfai/assistant/step/discussion-oq/STEP.md +5 -1
  94. package/assets/init/.qfai/assistant/step/discussion-pack/STEP.md +6 -1
  95. package/assets/init/.qfai/assistant/step/discussion-research/STEP.md +2 -0
  96. package/assets/init/.qfai/assistant/step/discussion-uiux/STEP.md +6 -4
  97. package/assets/init/.qfai/assistant/step/implement-backport/STEP.md +1 -1
  98. package/assets/init/.qfai/assistant/step/implement-benchmark/STEP.md +7 -4
  99. package/assets/init/.qfai/assistant/step/implement-dep-bump/STEP.md +11 -2
  100. package/assets/init/.qfai/assistant/step/implement-diagnose/STEP.md +2 -0
  101. package/assets/init/.qfai/assistant/step/implement-oracle-parity/STEP.md +2 -2
  102. package/assets/init/.qfai/assistant/step/implement-procedure/STEP.md +69 -0
  103. package/assets/init/.qfai/assistant/step/implement-quarantine/STEP.md +24 -7
  104. package/assets/init/.qfai/assistant/step/implement-refactor/STEP.md +2 -2
  105. package/assets/init/.qfai/assistant/step/implement-regression-fix/STEP.md +7 -6
  106. package/assets/init/.qfai/assistant/step/implement-retire/STEP.md +6 -3
  107. package/assets/init/.qfai/assistant/step/implement-revert/STEP.md +4 -3
  108. package/assets/init/.qfai/assistant/step/implement-seam/STEP.md +6 -5
  109. package/assets/init/.qfai/assistant/step/implement-stress-harness/STEP.md +34 -3
  110. package/assets/init/.qfai/assistant/step/implement-sweep/STEP.md +4 -3
  111. package/assets/init/.qfai/assistant/step/implement-tdd/STEP.md +39 -5
  112. package/assets/init/.qfai/assistant/step/implement-tooling/STEP.md +22 -2
  113. package/assets/init/.qfai/assistant/step/maintain-edit/STEP.md +18 -14
  114. package/assets/init/.qfai/assistant/step/prototyping-grill/STEP.md +4 -3
  115. package/assets/init/.qfai/assistant/step/prototyping-loop/STEP.md +4 -0
  116. package/assets/init/.qfai/assistant/step/prototyping-preflight/STEP.md +9 -3
  117. package/assets/init/.qfai/assistant/step/sdd-contract/STEP.md +11 -0
  118. package/assets/init/.qfai/assistant/step/sdd-flow/STEP.md +2 -0
  119. package/assets/init/.qfai/assistant/step/sdd-story/STEP.md +10 -0
  120. package/assets/init/.qfai/assistant/step/sdd-triage/STEP.md +34 -10
  121. package/assets/init/.qfai/assistant/step/triage-answer/STEP.md +7 -6
  122. package/assets/init/.qfai/assistant/step/triage-close/STEP.md +33 -23
  123. package/assets/init/.qfai/assistant/step/triage-cluster/STEP.md +5 -3
  124. package/assets/init/.qfai/assistant/step/triage-decompose/STEP.md +2 -0
  125. package/assets/init/.qfai/assistant/step/triage-dedupe/STEP.md +4 -0
  126. package/assets/init/.qfai/assistant/step/triage-handoff/STEP.md +13 -4
  127. package/assets/init/.qfai/assistant/step/triage-investigate/STEP.md +8 -5
  128. package/assets/init/.qfai/assistant/step/triage-operate/STEP.md +50 -0
  129. package/assets/init/.qfai/assistant/step/triage-owned-cleanup/STEP.md +86 -0
  130. package/assets/init/.qfai/assistant/step/triage-request-info/STEP.md +2 -0
  131. package/assets/init/.qfai/assistant/step/triage-security-intake/STEP.md +6 -3
  132. package/assets/init/.qfai/assistant/step/verify-change-note/STEP.md +1 -0
  133. package/assets/init/.qfai/assistant/step/verify-commit/STEP.md +37 -9
  134. package/assets/init/.qfai/assistant/step/verify-external/STEP.md +5 -2
  135. package/assets/init/.qfai/assistant/step/verify-qfai-gate/STEP.md +14 -16
  136. package/assets/init/.qfai/assistant/step/verify-release-notes/STEP.md +1 -0
  137. package/assets/init/.qfai/assistant/step/verify-repeat-run/STEP.md +46 -4
  138. package/assets/init/.qfai/assistant/step/verify-repo-gate/STEP.md +133 -30
  139. package/assets/init/root/.agents/rules/ai-readable-markdown.md +2 -2
  140. package/assets/init/root/.agents/rules/api-budget.md +187 -8
  141. package/assets/init/root/.agents/rules/document-schema.md +17 -0
  142. package/assets/init/root/.agents/rules/documentation-clarity.md +163 -34
  143. package/assets/init/root/.agents/rules/graph-exploration.md +150 -0
  144. package/assets/init/root/.agents/rules/grilling.md +100 -43
  145. package/assets/init/root/.agents/rules/minimal-implementation.md +3 -3
  146. package/assets/init/root/.agents/rules/reminders.json +27 -18
  147. package/assets/init/root/.agents/rules/session-feedback.md +131 -0
  148. package/assets/init/root/.agents/rules/temporary-files.md +33 -0
  149. package/assets/init/root/.agents/rules/untrusted-content.md +2 -2
  150. package/assets/init/root/.agents/rules/user-questions.md +51 -40
  151. package/assets/init/root/.github/workflows/qfai-docs.yml +3 -3
  152. package/assets/init/root/.github/workflows/qfai-tests.yml +88 -35
  153. package/assets/init/root/.github/workflows/qfai-validate.yml +2 -2
  154. package/assets/init/root/AGENTS.md +24 -22
  155. package/assets/sandbox-templates/default-deny.yaml +9 -3
  156. package/assets/schemas/workflow/extraction.schema.json +89 -5
  157. package/assets/scripts/check-mdschema.mjs +77 -8
  158. package/assets/uix-rev/comparison-review.md +1 -1
  159. package/assets/uix-rev/contracts-review.md +1 -2
  160. package/dist/cli/index.cjs +9444 -10071
  161. package/dist/cli/index.cjs.map +1 -1
  162. package/dist/cli/index.mjs +9421 -10041
  163. package/dist/cli/index.mjs.map +1 -1
  164. package/dist/graph/cli.cjs +1568 -0
  165. package/dist/graph/cli.cjs.map +1 -0
  166. package/dist/graph/cli.d.cts +9 -0
  167. package/dist/graph/cli.d.ts +9 -0
  168. package/dist/graph/cli.mjs +1541 -0
  169. package/dist/graph/cli.mjs.map +1 -0
  170. package/dist/graph/hook.cjs +817 -0
  171. package/dist/graph/hook.cjs.map +1 -0
  172. package/dist/graph/hook.d.cts +9 -0
  173. package/dist/graph/hook.d.ts +9 -0
  174. package/dist/graph/hook.mjs +792 -0
  175. package/dist/graph/hook.mjs.map +1 -0
  176. package/dist/index.cjs +7634 -8517
  177. package/dist/index.cjs.map +1 -1
  178. package/dist/index.d.cts +146 -658
  179. package/dist/index.d.ts +146 -658
  180. package/dist/index.mjs +7462 -8318
  181. package/dist/index.mjs.map +1 -1
  182. package/dist/process-C21nTvkT.d.cts +12 -0
  183. package/dist/process-C21nTvkT.d.ts +12 -0
  184. package/package.json +4 -6
  185. package/assets/init/root/CLAUDE.md +0 -46
@@ -6,7 +6,7 @@
6
6
  {
7
7
  "type": "command",
8
8
  "statusMessage": "QFAI structured-question reminder",
9
- "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}catch{}\" structured-question",
9
+ "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}\" structured-question",
10
10
  "timeout": 10
11
11
  }
12
12
  ]
@@ -16,7 +16,7 @@
16
16
  {
17
17
  "type": "command",
18
18
  "statusMessage": "QFAI free-text entry reminder",
19
- "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}catch{}\" free-text-entry",
19
+ "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}\" free-text-entry",
20
20
  "timeout": 10
21
21
  }
22
22
  ]
@@ -50,7 +50,7 @@
50
50
  {
51
51
  "type": "command",
52
52
  "statusMessage": "QFAI grilling reminder: design artifact",
53
- "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))}catch{}let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}if(Number.isInteger((n-1)/20)){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}}catch{}\" grilling-design-artifact",
53
+ "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}\" grilling-design-artifact",
54
54
  "timeout": 10
55
55
  }
56
56
  ]
@@ -61,7 +61,7 @@
61
61
  {
62
62
  "type": "command",
63
63
  "statusMessage": "QFAI grilling reminder: delegation",
64
- "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))}catch{}let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}if(Number.isInteger((n-1)/20)){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}}catch{}\" grilling-delegation",
64
+ "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))||{}}catch{}let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}catch{}\" grilling-delegation",
65
65
  "timeout": 10
66
66
  }
67
67
  ]
@@ -72,7 +72,7 @@
72
72
  {
73
73
  "type": "command",
74
74
  "statusMessage": "QFAI api-budget reminder",
75
- "command": "node -e \"try{const j=JSON.parse(require('fs').readFileSync(0,'utf8')),i=j.tool_input.command;if(typeof i==='string'&&/[^\\w.-]gh[^\\w.-]|api\\.github\\.com/.test(' '+i+' ')){const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}if(Number.isInteger((n-1)/20)){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}}}catch{}\" api-budget",
75
+ "command": "node -e \"try{const j=JSON.parse(require('fs').readFileSync(0,'utf8')),i=j.tool_input.command;if(typeof i==='string'&&/[^\\w.-]gh[^\\w.-]|api\\.github\\.com/.test(' '+i+' ')){const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}}catch{}\" api-budget",
76
76
  "timeout": 10
77
77
  }
78
78
  ]
@@ -85,13 +85,13 @@
85
85
  {
86
86
  "type": "command",
87
87
  "statusMessage": "QFAI documentation-clarity reminder",
88
- "command": "node -e \"try{const j=JSON.parse(require('fs').readFileSync(0,'utf8')),i=j.tool_input.command;if(typeof i==='string'&&/^\\*\\*\\* Add File: .*\\.md\\r?\\n/m.test(i)){const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}if(Number.isInteger((n-1)/20)){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}}}catch{}\" documentation-clarity-after-write",
88
+ "command": "node -e \"try{const j=JSON.parse(require('fs').readFileSync(0,'utf8')),i=j.tool_input.command;if(typeof i==='string'&&/^\\*\\*\\* Add File: .*\\.md\\r?\\n/m.test(i)){const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}}catch{}\" documentation-clarity-after-write",
89
89
  "timeout": 10
90
90
  },
91
91
  {
92
92
  "type": "command",
93
93
  "statusMessage": "QFAI documentation-clarity reminder",
94
- "command": "node -e \"try{const j=JSON.parse(require('fs').readFileSync(0,'utf8')),i=j.tool_input.command;if(typeof i==='string'&&/^\\*\\*\\* Update File: .*\\.md\\r?\\n/m.test(i)){const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}if(Number.isInteger((n-1)/20)){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}}}catch{}\" documentation-clarity-after-edit",
94
+ "command": "node -e \"try{const j=JSON.parse(require('fs').readFileSync(0,'utf8')),i=j.tool_input.command;if(typeof i==='string'&&/^\\*\\*\\* Update File: .*\\.md\\r?\\n/m.test(i)){const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}}catch{}\" documentation-clarity-after-edit",
95
95
  "timeout": 10
96
96
  }
97
97
  ]
@@ -102,7 +102,7 @@
102
102
  {
103
103
  "type": "command",
104
104
  "statusMessage": "QFAI minimal-implementation reminder",
105
- "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let go=true,j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))}catch{}try{const R=p.dirname(p.dirname(p.dirname(r))),n=(x=>{const s=x.split(p.sep),b=s.pop();return p.isAbsolute(x)||s[0]==='..'||s.some(y=>['tmp','test','tests','__tests__','node_modules','.qfai','.agents'].includes(y))||b[0]==='.'||/\\.(test|spec)\\./.test(b)||['.md','.mdx','.markdown','.txt','.rst','.yml','.yaml','.json','.jsonc','.json5','.toml','.ini','.csv','.tsv','.lock','.svg','.png','.jpg','.jpeg','.gif'].includes(p.extname(b).toLowerCase())}),a=[...j.tool_input.command.matchAll(/^\\*\\*\\* (?:(?:Add|Update|Delete) File|Move to): (.+)/gm)].map(m=>p.relative(R,p.resolve(process.cwd(),m[1].trim())));if(a.length>0&&a.every(n))go=false}catch{}if(go){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}if(Number.isInteger((n-1)/20)){const m=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];m===undefined||console.log(JSON.stringify(m))}}}}catch{}\" minimal-implementation",
105
+ "command": "node -e \"try{const f=require('fs'),p=require('path');let d=process.cwd(),r;for(;;){const c=p.join(d,'.agents','rules','reminders.json');if(r===undefined&&f.existsSync(c))r=c;if(f.existsSync(p.join(d,'.git')))break;const u=p.dirname(d);if(u===d){r=undefined;break}d=u}if(r){let go=true,j={};try{j=JSON.parse(f.readFileSync(0,'utf8'))}catch{}try{const R=p.dirname(p.dirname(p.dirname(r))),n=(x=>{const s=x.split(p.sep),b=s.pop();return p.isAbsolute(x)||s[0]==='..'||s.some(y=>['tmp','test','tests','__tests__','node_modules','.qfai','.agents'].includes(y))||b[0]==='.'||/\\.(test|spec)\\./.test(b)||['.md','.mdx','.markdown','.txt','.rst','.yml','.yaml','.json','.jsonc','.json5','.toml','.ini','.csv','.tsv','.lock','.svg','.png','.jpg','.jpeg','.gif'].includes(p.extname(b).toLowerCase())}),a=[...j.tool_input.command.matchAll(/^\\*\\*\\* (?:(?:Add|Update|Delete) File|Move to): (.+)/gm)].map(m=>p.relative(R,p.resolve(process.cwd(),m[1].trim())));if(a.length>0&&a.every(n))go=false}catch{}if(go){let n=1;if(typeof j.session_id==='string'){try{const t=p.join(require('os').tmpdir(),['qfai-reminder',j.session_id,j.agent_id||'main',process.argv[1]].join('-').replace(/[^\\w.-]/g,'_'));f.appendFileSync(t,'.');n=f.statSync(t).size}catch{}}const e=JSON.parse(f.readFileSync(r,'utf8'))[process.argv[1]];if(e===undefined){}else{const {briefContext,...m}=e;if(n>1&&typeof briefContext==='string')m.hookSpecificOutput.additionalContext=briefContext;console.log(JSON.stringify(m))}}}}catch{}\" minimal-implementation",
106
106
  "timeout": 10
107
107
  }
108
108
  ]
@@ -45,6 +45,11 @@ specialization_tags:
45
45
  a deliberate shortcut with its ceiling and the condition that lifts it.
46
46
  - Apply `.qfai/assistant/rule/ui-procurement.md`: install a component before writing one, and for anything the prototype does not show, take the adopted system's default rather than inventing one.
47
47
  - Apply `.agents/rules/interface-clarity.md`: label a control in the user's words, never the parameter's. The label stays; the sentence explaining it goes.
48
+ - Read each screen's `supplements` and `structure` in its UI contract before adding any text.
49
+ Show the title, the group headings, the labels and the declared supplements, and no other text.
50
+ A text the screen needs that the contract lacks, or a declared one the screen should not show,
51
+ is a change to the contract and goes back to `/qfai-sdd`.
52
+ Element ids stay the same when only wording changes.
48
53
 
49
54
  ## Inputs you must read
50
55
 
@@ -51,6 +51,10 @@ specialization_tags:
51
51
  - Apply `.agents/rules/minimal-implementation.md`: the first rung is this stage's — whether the thing needs to exist. After a spec row is agreed, that question is a Change Request.
52
52
  - Apply `.qfai/assistant/rule/ui-procurement.md`: the theme is adopted from a published one, and a screen's composition comes from a catalogue before it is drawn.
53
53
  - Apply `.agents/rules/interface-clarity.md`: a screen that needs explaining is a screen that needs redesigning. Hint text only where a need was demonstrated and the control was improved first.
54
+ - Record the decisions that rule leads to in the screen contract: the `supplements` a screen shows,
55
+ each with the reason it is needed, and the `structure` of any heading beyond the title
56
+ (`.qfai/assistant/skill/qfai-sdd/references/ui-contract-guide.md`).
57
+ Fix the control before adding a supplement for it.
54
58
 
55
59
  ## Inputs you must read
56
60
 
@@ -61,6 +61,13 @@ specialization_tags:
61
61
  - Require more work only on what `.qfai/assistant/rule/shared-skill-delegation-baseline.md#what-a-reviewer-may-demand-more-of-must` admits, and report any other gap as advisory.
62
62
  - Apply `.qfai/assistant/rule/ui-procurement.md`: report a region that was drawn where a catalogue had it, and name the item that would have served.
63
63
  - Apply `.agents/rules/interface-clarity.md`: report copy that explains the interface as a finding against the control under it, and name the control to fix.
64
+ - Compare the rendered text with each screen's `supplements` and `structure` in its UI contract.
65
+ A text no contract key declares is excess; a declared supplement that is not shown is an unmet need.
66
+ Name the screen, the task or state, the duplicated meaning or unmet need, and the text to remove or the control or structure to repair.
67
+ Apply `.qfai/assistant/skill/qfai-sdd/references/design-anti-patterns.md#copy-that-adds-no-meaning`.
68
+ Never cut a condition, cost, destination, result, recovery step, consequence, label, accessible name, role or state,
69
+ and never move one into a tooltip or placeholder.
70
+ Word counts are observations, never a verdict.
64
71
 
65
72
  ## Inputs you must read
66
73
 
@@ -64,6 +64,14 @@ specialization_tags:
64
64
  `/qfai-prototyping` at the validate lane, under `QFAI-AUD-001`. The shipped
65
65
  `ui-contract.sample.yaml` carries filled entries; replace them with the
66
66
  screen's own rather than emptying the list.
67
+ - For UI-bearing surfaces: every screen also declares `supplements` (`[]` when it
68
+ shows no text beyond its title, group headings and labels), and `structure` when
69
+ it shows a heading beyond its title. Check each proposed text element against the
70
+ screen's tasks and the information the user needs there, and write the ones that
71
+ stay into the contract with the reason. A text that only repeats a heading, an
72
+ action or a condition the user already has is left out. Discussion sidecars
73
+ inform these keys; they do not replace them. See
74
+ `.qfai/assistant/skill/qfai-sdd/references/ui-contract-guide.md`.
67
75
 
68
76
  ## Stop conditions
69
77
 
@@ -45,14 +45,24 @@ Applies to every text an agent produces: pull request and issue titles and
45
45
  bodies, source-code comments, and Markdown files — inside the current change
46
46
  only, never beyond its diff.
47
47
 
48
- - No issue or pull-request numbers, ticket identifiers, or names only this
49
- project understands, anywhere in source code or Markdown files. Those belong
50
- in the pull request, the commit message and the changelog.
51
- - No account of how the work went. State the current behaviour and why it is
52
- that way; the history is already in the git log and the pull request.
53
- - Cut what is self-evident, repeated, or wordy.
54
- - Ordinary vocabulary, one claim per sentence, lines short enough to scan.
55
- - Bullet lists and tables for parallel items.
48
+ - Keep out issue or pull-request numbers, ticket identifiers and names only
49
+ this project understands, anywhere in source code or Markdown files. Those
50
+ belong in the pull request, the commit message and the changelog.
51
+ Pull request and issue bodies may contain numbers and links.
52
+ - Keep out any account of how the work went. State the current behaviour and
53
+ why it is that way; the history is already in the git log and the pull
54
+ request.
55
+ - Settle the message, then open with the conclusion: the title or opening says
56
+ the result, with no preamble.
57
+ - Cut what is self-evident, repeated or wordy, and announcements, recaps and
58
+ empty intensifiers.
59
+ - Use ordinary vocabulary, one claim per sentence, and name who acts; software
60
+ does not know or decide.
61
+ - Mark what is unverified, and say what a claim rests on.
62
+ - Use bullet lists and tables for parallel items, with lines short enough to
63
+ scan.
64
+ - Open a report with the outcome, and separate what was checked from what was
65
+ not.
56
66
  - Re-read every changed line afterwards. Text that reads as a literal
57
67
  translation is a defect in any language pair — rewrite it the way someone
58
68
  writing natively in that language would.
@@ -3,6 +3,13 @@
3
3
  This document defines **non‑negotiable operating rules** for QFAI agents and subagents.
4
4
  It is inspired by proven “constitution / articles / guardrails” patterns in existing SDD toolchains, but adapted to QFAI’s minimal workflow.
5
5
 
6
+ Actual user instructions take priority over repository guidelines. Recorded
7
+ applicable user authority is defined in
8
+ `.agents/rules/grilling.md#explicit-delegation-for-a-discussion`: an explicit
9
+ in-scope delegation may authorize reversible requested judgment without a
10
+ fictional individual approval answer. It supplies no unknown fact or authority
11
+ for an operation outside its scope.
12
+
6
13
  Inside an `npx qfai workflow` run, what authorizes the run's work and the one target a work order binds its stage to are stated in `.qfai/assistant/rule/shared-skill-operating-baseline.md#what-authorizes-a-runs-work`. They add to these articles and except none.
7
14
 
8
15
  ---
@@ -260,7 +267,7 @@ sanctioned move, and neither is asking a sixth clarification anyway.
260
267
  **A grilling session survives exhaustion too**, and for the same reason: its
261
268
  questions never spent budget, so there is none left to run out of. A session
262
269
  already under way continues to its own end condition, a delegated one still
263
- putting its critical decisions to the user, and a stage reached after
270
+ requiring actual user authority for critical decisions, and a stage reached after
264
271
  exhaustion still opens one where its work calls for it. Treating a spent budget
265
272
  as the end of a session would put the design decisions back where this article
266
273
  found them — settled quietly, on an assumption nobody was asked about.
@@ -322,16 +329,18 @@ If confidence is low, inspect the repository further.
322
329
 
323
330
  **Stop on detection.** Where a contradiction in the spec, an unconsidered case,
324
331
  or a technical obstacle surfaces mid-run, stop rather than deciding alone.
325
- Settle it between the agents, and put a critical decision to the user
326
- (`.agents/rules/grilling.md#critical-decisions`). A contradiction with the spec
327
- is critical. Nothing is recorded beyond the stage's final report.
332
+ Settle it between the agents. A critical decision needs an actual user answer
333
+ or recorded applicable authorization under
334
+ `.agents/rules/grilling.md#explicit-delegation-for-a-discussion`; without it,
335
+ ask when permitted or register it open. A contradiction with the spec is
336
+ critical. Nothing is recorded beyond the stage's final report.
328
337
 
329
338
  **Only one outcome is the Drift Protocol's.**
330
339
 
331
- | The stop concludes | What follows |
332
- | ---------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
333
- | Settled input must change | `.qfai/assistant/rule/drift-protocol.md`: stop the dependent work, raise the Change Request, wait for approval |
334
- | The obstacle is this run's to solve — an unavailable dependency, an approach that failed | The run solves it. Nothing upstream changes, so there is nothing to approve |
340
+ | The stop concludes | What follows |
341
+ | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
342
+ | Settled input must change | `.qfai/assistant/rule/drift-protocol.md`: stop dependent work, raise the Change Request, record actual applicable user authority before changing it |
343
+ | The obstacle is this run's to solve — an unavailable dependency, an approach that failed | The run solves it. Nothing upstream changes, so there is nothing to approve |
335
344
 
336
345
  Stopping does not change settled input and is not a second way to. What it
337
346
  contributes to a Change Request is what that protocol asks of the class: for
@@ -366,7 +375,9 @@ rule master names.
366
375
  Rules:
367
376
 
368
377
  0. **A no-question mode is read first.** Where rule 4 applies, no question is put
369
- at all, so there is nothing whose availability rules 1 to 3 could judge. Read
378
+ to the user, so there is nothing whose availability rules 1 to 3 could judge.
379
+ Actual qualifying explicit delegation may still permit agent-to-agent rounds
380
+ under the master; no-question mode alone does not. Read
370
381
  the other way round, a mode that withholds the tool would make it unavailable,
371
382
  route the question to rule 3's plain-text fallback, and hand the agent a rule
372
383
  that says to ask beside one that says not to.
@@ -406,12 +417,14 @@ Rules:
406
417
  this rule is activated by the `--auto` flag alone.
407
418
  6. **A grilling session does not reach the user under `--auto`.** Its questions
408
419
  are exempt from the Article VI budget, not from rule 4: a no-question mode
409
- asks nothing, whatever the question is for. The session still runs — it
420
+ sends no question to the user, whatever it is for. The session still runs — it
410
421
  settles what the repository settles, dispatches sub-agents for the facts, and
411
- in a delegated session adopts the griller's recommendation for every decision
412
- that is not critical — and every **node** left over (in a delegated session,
413
- each critical decision and each fact only the user holds) is **opened as a
414
- question in the register the stage reads**, so the stage cannot complete over it. Every node, not every
422
+ in a delegated session adopts the griller's recommendation for non-critical
423
+ decisions. A critical decision needs an actual user answer or recorded
424
+ applicable authorization; without it the node stays open. It opens every **node**
425
+ left over, including uncovered critical decisions and unavailable user-held
426
+ facts, **as a question in the register the stage reads**, so the
427
+ stage cannot complete over it. Every node, not every
415
428
  decision: a fact only the user holds cannot be settled from evidence either,
416
429
  and opening the decisions while dropping the facts loses exactly the nodes no
417
430
  lookup could have reached. Where a document requires the field to hold
@@ -419,7 +432,10 @@ Rules:
419
432
  open question; a labelled value under an open question is not a settled
420
433
  decision. What rule 4 does not license is the assumption **alone**, which
421
434
  reads as settled to whoever finds it next. A fact declared undefaultable has
422
- no value to write down at all: the run stops and names it.
435
+ no value to write down at all: the run stops and names it. A qualifying
436
+ explicitly delegated session ends `adopted` only with the master's empty-tree,
437
+ actual-input and authority conditions met. No-question mode alone cannot
438
+ close a node or authorize a reserved decision.
423
439
 
424
440
  This article survives context compaction because `.qfai/assistant/rule/constitution.md` is a P1 reload target.
425
441
 
@@ -11,7 +11,22 @@ open-questions.md, and every file under paths.contractsDir. Production code and
11
11
  obligation may change in a later task, but the change must preserve or deliberately reapprove that obligation.
12
12
  QFAI-owned files under .qfai/assistant/rule/ are updated through the package, then synchronized by init.
13
13
 
14
- A new change-request row in decisions.md is permitted without an earlier change request. Existing rows are append-only records: a prior row may change its Status cell, while its ID, Content, and Approach stay fixed. A changed obligation needs a new row, even when an older row discusses the same topic.
14
+ A new change-request row in decisions.md is permitted without an earlier change request.
15
+ Existing rows are append-only records: a prior row may change its Status cell,
16
+ while its ID, Content, and Approach stay fixed. The sole ID-change exception is
17
+ explicit local renumbering of a unique branch-added decision absent from the
18
+ unique common ancestor of HEAD and a required local base. That operation changes
19
+ only its ID and provably branch-added exact references. Inherited row cells and
20
+ references stay fixed. A changed obligation needs a new row, even when an older
21
+ row discusses the same topic.
22
+
23
+ The sole Approach-change exception is the published decision prefix recovery
24
+ below. It preserves the target's ID, Content, Status and complete original
25
+ Approach suffix. It excludes inherited rows and open-question rows.
26
+
27
+ The sole row-removal exception is the merge-added identical decision copy
28
+ recovery below. It removes only proven raw-identical surplus copies of a
29
+ decision and keeps one original. It excludes open-question rows.
15
30
 
16
31
  ## Allowed exceptions (minimal whitelist)
17
32
 
@@ -25,6 +40,105 @@ diff changes no file under `01_policy/`, `02_business-flow/` or
25
40
  paths.contractsDir appends no `Change request:` row. A row naming a story file
26
41
  the bugfix did not touch would state an upstream change that did not happen.
27
42
 
43
+ ## Published decision prefix recovery
44
+
45
+ The SDD owner may prepend only an approved Evidence/Grounds prefix to a
46
+ published malformed decision added by the branch. Approval of this protocol
47
+ does not authorize any target. First obtain a separate new in-force recovery
48
+ change request naming the target and its repository-relative file.
49
+
50
+ 1. Fix the explicit base commit and its unique common ancestor with current
51
+ HEAD. The target ID must be absent from both. The documented published
52
+ source commit must be an ancestor of current HEAD and expose the unique
53
+ original row. Inherited records and open-question rows are excluded.
54
+ 2. Read the original raw cell bytes. The row must be single-line valid UTF-8,
55
+ with exactly one ASCII padding space at each Approach delimiter and no
56
+ additional boundary whitespace. Hash the stored Approach bytes with
57
+ SHA-256, excluding only the delimiter pipes and those two padding spaces.
58
+ Do not decode escapes, trim text or normalize it. Ambiguous boundaries,
59
+ additional padding or invalid UTF-8 stop recovery.
60
+ 3. Confirm the original Residual risk and Rollback items already satisfy the
61
+ required ordered, nonempty form. The new Approach must be exactly
62
+ `- Evidence: <actual file references> - Grounds: `, including its trailing
63
+ space, followed by the complete original Approach as a byte-identical
64
+ suffix. Preserve the original wording, order, facts, authority and scope.
65
+ The prefixed row must satisfy the existing form checks.
66
+ 4. Record the target ID, published source commit, fixed base and common
67
+ ancestor, old and new Approach digests, exact prefix, actual file evidence,
68
+ approval authority, recovery reason, residual risk and rollback in the new
69
+ request. The old digest must match the published source Approach; the new
70
+ digest must match the exact prefix plus that source value. Record actual
71
+ authority; do not invent a user answer or approval.
72
+ 5. Immediately before writing, recheck the unique current target, its
73
+ byte-identical ID, Content and Status, and its recorded old Approach digest.
74
+ Unknown or unavailable provenance, inherited or duplicate targets, stale
75
+ data or a change beyond the approved prefix stops recovery before writing.
76
+ Apply only that prefix. Keep every other row byte-identical and run the existing
77
+ validation without an exemption.
78
+ 6. Rollback removes only the recorded prefix when the current new Approach
79
+ digest still matches. Otherwise preserve the current row and stop. Removal
80
+ restores the original Approach and its validation failure. Do not reset,
81
+ amend or force push.
82
+
83
+ Status changes, supersession and a diagnostic's rewrite suggestion supply no
84
+ recovery authority and clear no form error. Inherited malformed records need a
85
+ separate owning change request; this operation cannot repair them. These are
86
+ owner instructions, not an automatic recovery command or a concurrency guarantee.
87
+
88
+ ## Merge-added identical decision copy recovery
89
+
90
+ The SDD owner may manually remove only raw-identical surplus copies of a
91
+ decision row that an exact two-parent bad merge introduced. Approval of this
92
+ protocol authorizes no target. First append a separate new in-force change
93
+ request that names the exact `decisions.md` path and the decision ID.
94
+
95
+ 1. Name the bad merge. It must be an available ancestor of current HEAD with
96
+ exactly two parents, and it must expose the introduced surplus. Name both
97
+ parents and their unique common ancestor. Each of the three must hold exactly
98
+ one byte-identical original of the row. Fix the explicit base and its unique
99
+ comparison ancestor; both must hold one original and no surplus selected for
100
+ removal. A missing or non-ancestor merge, a third parent, a non-unique
101
+ ancestor or a source that does not hold exactly one original stops recovery.
102
+ 2. Read the complete stored UTF-8 raw row of every selected current occurrence:
103
+ all four cells, delimiters, padding and line ending. Hash each with SHA-256,
104
+ including the line ending. Do not trim, decode escapes or normalize endings.
105
+ Nonidentical cells, padding or endings are a different collision and are
106
+ outside this recovery.
107
+ 3. Record in the request the path, the ID, the bad merge, both parents, the
108
+ ancestor, the base and its comparison ancestor, the location of each
109
+ occurrence, the raw hash, the retain/remove mapping, the actual authority,
110
+ the residual risk and the rollback. Record actual authority; invent no
111
+ answer. The request fixes input lineage only.
112
+ 4. After the request is appended, have the evidence for the write reviewed
113
+ independently and adopted. It fixes the actual write HEAD, base and
114
+ comparison ancestor, the complete preimage and replacement bytes and hashes,
115
+ the mapping and the current authority. The request does not name its own
116
+ commit, so the actual write revision stays distinct from the lineage above.
117
+ This adds no receipt grammar and no validator input.
118
+ 5. Immediately before writing, recheck the refs, their availability, the path,
119
+ the authority, the occurrence count, the mapping and the bytes. Stale,
120
+ missing, ambiguous, conflicting or changed proof stops before writing.
121
+ Changed observations need refreshed reviewed evidence. A changed target or
122
+ operation needs a new request; do not rewrite the cells of an earlier one.
123
+ 6. Remove only the mapped surplus occurrences. Keep one complete original, every
124
+ distinct decision and reference, every unrelated current byte and the Git
125
+ history. Do not change a Status, rewrite a reference or reorder other
126
+ history. Open-question rows, nonidentical collisions, ordinary duplicate
127
+ cleanup and surplus that the base or its comparison ancestor already holds
128
+ are excluded.
129
+ 7. Rollback restores the recorded complete preimage only while the current file
130
+ is byte-identical to its owned replacement. Otherwise keep the current bytes,
131
+ stop and name the unrestored path. Restoring brings back the duplicate
132
+ failure. Do not reset, amend or force push.
133
+
134
+ The existing duplicate check, positional drift, consumed-DONE authorization and
135
+ valid Status-only rules keep applying without an exemption. Other drift that
136
+ the file already has stays diagnosed; it is not a prerequisite for this
137
+ correction. An actual required stage gate still stops dependent work. Correcting
138
+ the copies is not a whole-target PASS, and the request stays at WIP while a named
139
+ action or required check is unfinished. These are owner instructions, not a
140
+ command, an executor or a concurrency, atomicity or power-loss guarantee.
141
+
28
142
  ## Drift classes
29
143
 
30
144
  Intent drift means an approved obligation should change. Record the current obligation, the proposed behavior, realistic
@@ -112,6 +226,9 @@ copy of a locked file before deciding its lock is wrong.
112
226
  ## Non-negotiable constraints
113
227
 
114
228
  - Downstream stages do not patch protected upstream artifacts before an in-force Change request: decision row authorizes the path. The drift profile compares the branch against baseBranch and reports QFAI-DRIFT-001 for an unapproved protected change. The SDD owner may create the request row itself without a prior row.
115
- - Existing decisions.md and open-questions.md rows retain their ID, Content, and Approach. A former row may change Status; new content is appended as a new row.
229
+ QFAI-DRIFT-003 is a warning that nothing was compared, because baseBranch does not resolve.
230
+ QFAI-DRIFT-004 is a warning that an uncommitted edit to a protected file is not compared:
231
+ record the Change request row before you commit the change.
232
+ - Existing decisions.md and open-questions.md rows retain their ID, Content, and Approach, except for the explicit branch-added decision renumbering, published decision prefix recovery and merge-added identical decision copy recovery above. Inherited rows retain those cells without exception. A former row may change Status; new content is appended as a new row.
116
233
  - Vendored assistant rules are changed in the package and synchronized into projects by `npx qfai init --force`, which overwrites a local edit. A project rule lives in a `*.local.md` overlay beside the vendored one.
117
234
  - When approval is unavailable, keep the affected items stopped and report the decision needed. Continue unrelated work.
@@ -14,11 +14,13 @@ A `SKILL.md` states the contract and points at the file that carries the detail.
14
14
  - **One topic per file.** Do not replace an oversized `SKILL.md` with an oversized `references/everything.md`; that is the same problem one directory down. Split by topic and keep each file readable on its own — a reader who followed one pointer should not have to scan past three unrelated subjects to reach the one they came for.
15
15
  - **Every pointer resolves.** A line that moves detail out must name the file (and anchor, when the file covers more than one topic) so the reader is never left guessing where the rule went.
16
16
 
17
- A hard line ceiling backs this up: **800 lines per assistant asset file**, for every `.qfai/assistant/**/*.{md,yml,yaml}` file, counted as `content.split(/\r?\n/).length` — blank lines included. `npx qfai doctor` measures it and reports every file over the ceiling as `assets.lineBudget`. The ceiling is a backstop, not the rule: a file approaching it is a signal to move a section out, not
18
- to raise the number.
17
+ A hard line ceiling backs this up: **500 lines per Markdown assistant asset file** and **800 lines per YAML assistant asset file**, for every `.qfai/assistant/**/*.{md,yml,yaml}` file, counted as `content.split(/\r?\n/).length` — blank lines included.
18
+ `npx qfai doctor` measures it and reports every file over its ceiling as `assets.lineBudget`.
19
+ The ceiling is a backstop, not the rule: a file approaching it is a signal to move a section out, not to raise the number.
19
20
 
20
- It was raised from 500 once, and on measurement rather than on the "this file is long" claim the number exists to refuse: three skill bodies had converged on that ceiling, and the changes in flight against one of them added 161 lines to a body already at 498 — eleven of them crossing the ceiling on their own. The detail those bodies carry is required to sit in the body rather than behind
21
- a pointer, so splitting could not absorb it. Converging on the limit was itself the signal — a body at the ceiling stops shedding topics and starts packing them into longer lines, and a line count cannot see that. Raise it again only against evidence of that kind.
21
+ The Markdown ceiling is the limit an agent can follow in one file: it follows fewer instructions as its context grows, and follows those in the middle of a long file worst. A Markdown file has a `references/` home to move detail into. A YAML asset is parsed as data and cannot be split that way (see below), which is why its ceiling is higher.
22
+
23
+ A body at its ceiling stops shedding topics and starts packing them into longer lines, and a line count cannot see that. The width ceiling below is what catches it.
22
24
 
23
25
  **A width ceiling makes the count honest: 400 characters per line.** A count of lines bounds reading cost only while a line is a roughly constant unit of reading, and packing broke that — one line in the shipped tree ran 9,104 characters against a median of 118, and cost the budget one unit. The two are read together, because each permits what the other refuses: width alone allows a thin
24
26
  file of a thousand short lines, and the count alone allows a packed one.
@@ -0,0 +1,27 @@
1
+ # Codex Delegation
2
+
3
+ Read before the first `spawn_agent` call when the host is Codex.
4
+
5
+ ## Codex delegation notes
6
+
7
+ A sub-agent should cost the orchestrator one brief and one final answer. A call
8
+ that only checks progress, such as a short wait, a sleep or an agent listing,
9
+ is one more model call, so the orchestrator makes none.
10
+
11
+ - **Self-contained brief.** Call `spawn_agent` with `fork_turns: "none"` and a
12
+ `message` that holds everything the sub-agent needs: the task, the files to
13
+ read, the paths it may write, the checks to run and the shape of its answer.
14
+ `fork_turns` defaults to `"all"`, which copies the whole parent thread into
15
+ the sub-agent. Where the tool takes `fork_context` instead, leave it out or
16
+ pass `false`.
17
+ - **One final answer, no interim messages.** The brief tells the sub-agent to
18
+ send no `send_message` updates and to return its result once, as its final
19
+ answer. Where `wait_agent` takes no `targets`, every queued message ends the
20
+ orchestrator's wait early.
21
+ - **Long wait.** With no work of its own left, the orchestrator calls
22
+ `wait_agent` with a `timeout_ms` of minutes within the maximum the tool states,
23
+ such as `600000`, not 10 to 60 seconds. A wait that times out is
24
+ followed by another long wait.
25
+ - **No polling.** Never wait with `sleep`, a loop of short `wait_agent` calls or
26
+ repeated `list_agents` calls. `followup_task` gives a sub-agent a new task; it
27
+ never asks for progress.
@@ -6,15 +6,27 @@ Read when a griller's recommendation is used.
6
6
 
7
7
  A grilling session puts a recommended answer beside each question (`.agents/rules/grilling.md`). Who settled the decision decides what happens next.
8
8
 
9
- | The decision was settled | What follows |
10
- | ----------------------------------------------------- | --------------------------------------------------------------------------------- |
11
- | By the user, from the recommendation among the inputs | The decision is theirs. The griller may review the artifact |
12
- | Agent to agent, not critical | The decision stands. The griller that recommended it does not review the artifact |
13
- | Agent to agent, critical, with no user adjudication | The artifact is wrong, and no reviewer can clear it |
14
-
15
- **The second row is how a delegated session is meant to end** (`.agents/rules/grilling.md`), not a finding. The reviewer checks that the decision is in the final report and is not critical. A reviewer that doubts its merit raises that as an ordinary finding against the artifact, under its own remit, as it would for any other content.
16
-
17
- **The third row is not a routing problem.** A critical decision is the user's in every session, and a run that could not ask records it as an open question rather than adopting it. An agent-adopted critical decision is therefore an artifact carrying something nobody with the standing decided, and handing it to a different reviewer would launder it.
9
+ | The decision was settled | What follows |
10
+ | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
11
+ | By the user, from the recommendation among the inputs | The decision is theirs. The griller may review the artifact |
12
+ | Agent to agent, non-critical or with recorded applicable human authority | The decision stands. The griller that recommended it does not review the artifact |
13
+ | Agent to agent, critical, without an actual user answer or recorded applicable authority | The artifact is wrong, and no reviewer can clear it |
14
+
15
+ **The second row is how a delegated session is meant to end**
16
+ (`.agents/rules/grilling.md`), not a finding. The reviewer checks the final
17
+ report and, for a critical decision, the actual
18
+ applicable user authority defined in
19
+ `.agents/rules/grilling.md#explicit-delegation-for-a-discussion`.
20
+ An explicit delegation covering reversible requested judgment is authority,
21
+ not an individual human option answer. The recommending griller remains
22
+ disqualified from reviewing an agent adoption. A reviewer that doubts its merit
23
+ raises an ordinary finding against the artifact under its own remit.
24
+
25
+ **The third row is not a routing problem.** Without an actual answer or
26
+ recorded applicable user authority, a critical decision stays open. A run that
27
+ cannot ask records it as an open question rather than adopting it. Agent
28
+ agreement cannot supply authority, and a different reviewer cannot repair its
29
+ absence. No-question mode alone authorizes no decision.
18
30
  The reviewer must return `REVISE` and name the decision: it is reopened and put to the user, or recorded open where no question can be asked.
19
31
 
20
32
  The first row needs a reason, because the intuitive one is wrong. A sub-agent starting with a reset context cannot defer to something it does not remember, so deference is not the risk. **Correlation** is: a fresh instance of the same agent, on the same model, over the same evidence, re-derives the preference that produced the recommendation and finds it good on the merits. Resetting the
@@ -0,0 +1,48 @@
1
+ # Worker edits and work orders
2
+
3
+ Read when assigning worker write paths or handling a refused edit.
4
+
5
+ ## Edit boundary
6
+
7
+ The work order names the checkout assigned to the task by the host, the worker's
8
+ write paths and the permitted edit method. Creating a Git worktree does not
9
+ grant the host's tools permission to edit it. Even an assigned checkout can
10
+ receive a refusal; verify the actual edit result rather than claiming access.
11
+
12
+ If an edit is refused, stop writes to that target. Report the tool, working
13
+ directory, target path and refusal text, omitting secrets. Return the owned
14
+ paths and a reviewable diff that can be prepared within the worker's permitted
15
+ area. Do not bypass the refusal through a shell, script or another tool.
16
+ This does not forbid shell authoring where the host already permits it.
17
+
18
+ The orchestrator checks the diff against the current files in its own permitted
19
+ checkout before applying it. If that edit is refused too, leave the application
20
+ pending and continue only independent work. Follow the existing worktree or
21
+ shared-index mode in `.qfai/assistant/rule/workflow.md#concurrency-stage-independent-mandatory`
22
+ when assigning writers and integrating their paths.
23
+
24
+ ## Work order template
25
+
26
+ ```text
27
+ Task title: <short>
28
+ Role: <sub-agent role>
29
+ Review series: <reviewed artifact> + <reviewer role> + <replacement ordinal> # review work orders only
30
+ Goal: <what to decide/produce>
31
+ Checkout: <absolute checkout assigned to this task by the host>
32
+ Write paths: <exact owned paths, or none for a read-only task>
33
+ Edit method: <host-permitted tool; worktree creation does not grant access>
34
+ If an edit is refused: stop that target; return tool, cwd, target, refusal text, owned paths and a reviewable diff
35
+ Inputs (refs):
36
+ - <file/section>
37
+ - .qfai/assistant/rule/drift-protocol.md#core-rule <!-- the protected set, in front of the agent -->
38
+ Constraints:
39
+ - must: enforce Drift Protocol
40
+ - must: follow applicable test-layer or validation policy
41
+ - must_not: patch upstream artifacts directly; every upstream change requires
42
+ STOP + Change Request + owner rerun per .qfai/assistant/rule/drift-protocol.md
43
+ Output format:
44
+ - <headings / bullet schema>
45
+ Time budget: none | <seconds> # advisory: nothing stops at it. See .qfai/assistant/rule/stage-cost.md
46
+ Elapsed line: end every message with `elapsed <seconds>s / <budget>s`, or `elapsed <seconds>s` when the budget is none
47
+ Acceptance bar: <accept when ...> | <rework when ...> # never `PASS`/`REVISE`: that is the reviewer's vocabulary and the completion gate matches on it, so a doer told to report in it emits a verdict on its own work
48
+ ```