@daniellins/power-claude 0.16.3

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.

Potentially problematic release.


This version of @daniellins/power-claude might be problematic. Click here for more details.

Files changed (467) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -0
  3. package/bin/cli.js +124 -0
  4. package/installer/__tests__/doctor-ready-oracles.test.cjs +140 -0
  5. package/installer/__tests__/merge-settings.test.cjs +330 -0
  6. package/installer/adopt.js +161 -0
  7. package/installer/copy.js +203 -0
  8. package/installer/doctor.js +266 -0
  9. package/installer/install.js +155 -0
  10. package/installer/manifest.js +102 -0
  11. package/installer/motor-scripts.js +50 -0
  12. package/installer/motor-scripts.json +39 -0
  13. package/installer/uninstall.js +99 -0
  14. package/installer/update.js +376 -0
  15. package/installer/util.js +113 -0
  16. package/installer/wizard.js +64 -0
  17. package/package.json +26 -0
  18. package/scripts/__fixtures__/anti-path-demo/agents/ap-writer.md +13 -0
  19. package/scripts/__fixtures__/anti-path-demo/templates/write-tmpl.md +7 -0
  20. package/scripts/__fixtures__/anti-path-demo/workflows/wf-ap.smoke.json +10 -0
  21. package/scripts/__fixtures__/anti-path-demo/workflows/wf-ap.yaml +25 -0
  22. package/scripts/__fixtures__/assess-tier/retrofit-leve/pod/agents/content-chief.md +1 -0
  23. package/scripts/__fixtures__/assess-tier/retrofit-leve/pod/workflows/wf-criacao-redteam.yaml +3 -0
  24. package/scripts/__fixtures__/assess-tier/retrofit-leve/pod-spec.json +13 -0
  25. package/scripts/__fixtures__/fidelity/fake-green-average-in-band.yaml +21 -0
  26. package/scripts/__fixtures__/fidelity/fake-green-average.yaml +18 -0
  27. package/scripts/__fixtures__/fidelity/fake-green-denominator.yaml +19 -0
  28. package/scripts/__fixtures__/fidelity/fake-green-missing-evidence.yaml +18 -0
  29. package/scripts/__fixtures__/fidelity/fake-green-threshold.yaml +18 -0
  30. package/scripts/__fixtures__/fidelity/fake-green-weight.yaml +18 -0
  31. package/scripts/__fixtures__/fidelity/golden.yaml +31 -0
  32. package/scripts/__fixtures__/fidelity/trindade-incoherent.yaml +15 -0
  33. package/scripts/__fixtures__/forge-canonical/pod-spec-example.json +37 -0
  34. package/scripts/__fixtures__/migrate-squad/authored/agents/offer-engineer.md +17 -0
  35. package/scripts/__fixtures__/migrate-squad/authored/knowledge/no-source.md +7 -0
  36. package/scripts/__fixtures__/migrate-squad/authored/knowledge/value-stack-method.md +8 -0
  37. package/scripts/__fixtures__/migrate-squad/authored/specs/wf-sample.smoke.json +24 -0
  38. package/scripts/__fixtures__/migrate-squad/authored/specs/wf-sample.spec.json +45 -0
  39. package/scripts/__fixtures__/migrate-squad/legacy-squad/HEADLINE.md +3 -0
  40. package/scripts/__fixtures__/migrate-squad/legacy-squad/README.md +4 -0
  41. package/scripts/__fixtures__/migrate-squad/legacy-squad/agents/hormozi-offer.md +14 -0
  42. package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/dead-redundant.md +4 -0
  43. package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/method-guide.md +6 -0
  44. package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/veto-screen.md +4 -0
  45. package/scripts/__fixtures__/migrate-squad/legacy-squad/config/config.yaml +2 -0
  46. package/scripts/__fixtures__/migrate-squad/legacy-squad/data/case-library.md +3 -0
  47. package/scripts/__fixtures__/migrate-squad/legacy-squad/scripts/validate-mvn.py +16 -0
  48. package/scripts/__fixtures__/migrate-squad/legacy-squad/sops/sop-sample.md +3 -0
  49. package/scripts/__fixtures__/migrate-squad/legacy-squad/squad-io.yaml +14 -0
  50. package/scripts/__fixtures__/migrate-squad/legacy-squad/tasks/gather-context.md +7 -0
  51. package/scripts/__fixtures__/migrate-squad/legacy-squad/templates/output-tmpl.md +5 -0
  52. package/scripts/__fixtures__/migrate-squad/legacy-squad/workflows/wf-sample.yaml +24 -0
  53. package/scripts/__fixtures__/pipeline/pipelines/pipeline-checkpoint.yaml +21 -0
  54. package/scripts/__fixtures__/pipeline/pipelines/pipeline-demo.yaml +22 -0
  55. package/scripts/__fixtures__/pipeline/pods/pipe-a/agents/maker.md +13 -0
  56. package/scripts/__fixtures__/pipeline/pods/pipe-a/memory/learnings.consolidated.md +8 -0
  57. package/scripts/__fixtures__/pipeline/pods/pipe-a/memory/learnings.jsonl +83 -0
  58. package/scripts/__fixtures__/pipeline/pods/pipe-a/templates/make-tmpl.md +8 -0
  59. package/scripts/__fixtures__/pipeline/pods/pipe-a/workflows/wf-a.yaml +16 -0
  60. package/scripts/__fixtures__/pipeline/pods/pipe-b/agents/user.md +13 -0
  61. package/scripts/__fixtures__/pipeline/pods/pipe-b/memory/learnings.consolidated.md +8 -0
  62. package/scripts/__fixtures__/pipeline/pods/pipe-b/memory/learnings.jsonl +14 -0
  63. package/scripts/__fixtures__/pipeline/pods/pipe-b/templates/use-tmpl.md +6 -0
  64. package/scripts/__fixtures__/pipeline/pods/pipe-b/workflows/wf-b.yaml +16 -0
  65. package/scripts/__fixtures__/pod-run/agents/forge-chief.md +13 -0
  66. package/scripts/__fixtures__/pod-run/agents/pod-critic.md +13 -0
  67. package/scripts/__fixtures__/pod-run/templates/build-tmpl.md +8 -0
  68. package/scripts/__fixtures__/pod-run/templates/gate-tmpl.md +6 -0
  69. package/scripts/__fixtures__/pod-run/workflows/wf-min.yaml +43 -0
  70. package/scripts/__fixtures__/pod-run/workflows/wf-skip-modes.yaml +50 -0
  71. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-all-skippable.yaml +30 -0
  72. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-criteria-escalar-de-bloco.yaml +40 -0
  73. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-skip-mode-desconhecido.yaml +30 -0
  74. package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-unknown-key.yaml +29 -0
  75. package/scripts/__fixtures__/pod-workflow/invalid-cycle.yaml +26 -0
  76. package/scripts/__fixtures__/pod-workflow/invalid-fix-loop-backedge.yaml +29 -0
  77. package/scripts/__fixtures__/pod-workflow/invalid-fix-loop-forward.yaml +29 -0
  78. package/scripts/__fixtures__/pod-workflow/invalid-handoff-open.yaml +27 -0
  79. package/scripts/__fixtures__/pod-workflow/invalid-knowledge-shape.yaml +18 -0
  80. package/scripts/__fixtures__/pod-workflow/invalid-missing-agent.yaml +15 -0
  81. package/scripts/__fixtures__/pod-workflow/invalid-missing-outputs.yaml +15 -0
  82. package/scripts/__fixtures__/pod-workflow/invalid-missing-template.yaml +15 -0
  83. package/scripts/__fixtures__/pod-workflow/invalid-missing-veto.yaml +13 -0
  84. package/scripts/__fixtures__/pod-workflow/invalid-template-not-on-disk.yaml +16 -0
  85. package/scripts/__fixtures__/pod-workflow/invalid-veto-prose.yaml +17 -0
  86. package/scripts/__fixtures__/pod-workflow/knowledge/a.md +4 -0
  87. package/scripts/__fixtures__/pod-workflow/knowledge/b.md +4 -0
  88. package/scripts/__fixtures__/pod-workflow/knowledge/c.md +4 -0
  89. package/scripts/__fixtures__/pod-workflow/templates/step-tmpl.md +7 -0
  90. package/scripts/__fixtures__/pod-workflow/valid-checkpoint-skip-modes.yaml +29 -0
  91. package/scripts/__fixtures__/pod-workflow/valid-fix-loop.yaml +30 -0
  92. package/scripts/__fixtures__/pod-workflow/valid-knowledge.yaml +30 -0
  93. package/scripts/__fixtures__/pod-workflow/valid-veto-always-true.yaml +20 -0
  94. package/scripts/__fixtures__/pod-workflow/valid.yaml +27 -0
  95. package/scripts/__fixtures__/smoke-canary/agents/canary-reporter.md +13 -0
  96. package/scripts/__fixtures__/smoke-canary/agents/canary-researcher.md +13 -0
  97. package/scripts/__fixtures__/smoke-canary/agents/canary-synth.md +13 -0
  98. package/scripts/__fixtures__/smoke-canary/memory/learnings.consolidated.md +10 -0
  99. package/scripts/__fixtures__/smoke-canary/memory/learnings.jsonl +448 -0
  100. package/scripts/__fixtures__/smoke-canary/templates/report-tmpl.md +9 -0
  101. package/scripts/__fixtures__/smoke-canary/templates/research-tmpl.md +7 -0
  102. package/scripts/__fixtures__/smoke-canary/templates/synth-tmpl.md +9 -0
  103. package/scripts/__fixtures__/smoke-canary/workflows/wf-canary.smoke.json +11 -0
  104. package/scripts/__fixtures__/smoke-canary/workflows/wf-canary.yaml +53 -0
  105. package/scripts/__fixtures__/source-audit/fake-green-bronze-unsignaled.yaml +10 -0
  106. package/scripts/__fixtures__/source-audit/fake-green-counts.yaml +9 -0
  107. package/scripts/__fixtures__/source-audit/fake-green-missing-evidence.yaml +9 -0
  108. package/scripts/__fixtures__/source-audit/fake-green-unclassified.yaml +10 -0
  109. package/scripts/__fixtures__/source-audit/fake-green-verdict.yaml +10 -0
  110. package/scripts/__fixtures__/source-audit/golden-blocked.yaml +10 -0
  111. package/scripts/__fixtures__/source-audit/golden.yaml +11 -0
  112. package/scripts/__fixtures__/sweep-forms/agents/forms-count.md +13 -0
  113. package/scripts/__fixtures__/sweep-forms/agents/forms-field.md +13 -0
  114. package/scripts/__fixtures__/sweep-forms/agents/forms-fileref.md +13 -0
  115. package/scripts/__fixtures__/sweep-forms/agents/forms-json.md +13 -0
  116. package/scripts/__fixtures__/sweep-forms/agents/forms-section.md +13 -0
  117. package/scripts/__fixtures__/sweep-forms/specs/wf-forms.spec.json +5 -0
  118. package/scripts/__fixtures__/sweep-forms/templates/t_count.md +5 -0
  119. package/scripts/__fixtures__/sweep-forms/templates/t_field.md +3 -0
  120. package/scripts/__fixtures__/sweep-forms/templates/t_fileref.md +3 -0
  121. package/scripts/__fixtures__/sweep-forms/templates/t_json.md +0 -0
  122. package/scripts/__fixtures__/sweep-forms/templates/t_section.md +3 -0
  123. package/scripts/__fixtures__/sweep-forms/workflows/wf-forms.smoke.json +13 -0
  124. package/scripts/__fixtures__/sweep-forms/workflows/wf-forms.yaml +67 -0
  125. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-1-noop-state-json.fixture.md +18 -0
  126. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-2-crash-buildworkflowyaml.fixture.md +26 -0
  127. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-3-nasce-verde-skip-modes.fixture.md +29 -0
  128. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-4-guarda-sem-irmao.fixture.md +17 -0
  129. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-5a-diff-stat-sem-guarda.fixture.md +17 -0
  130. package/scripts/__fixtures__/validate-story-acs-corpus/corpus-5b-test-f-sobre-diretorio.fixture.md +20 -0
  131. package/scripts/__fixtures__/veto-checks/copy-clean.md +4 -0
  132. package/scripts/__fixtures__/veto-checks/copy-dashes.md +4 -0
  133. package/scripts/__fixtures__/veto-checks/n8n-bad.md +18 -0
  134. package/scripts/__fixtures__/veto-checks/n8n-ok.md +22 -0
  135. package/scripts/__fixtures__/veto-checks/page-bad.html +7 -0
  136. package/scripts/__fixtures__/veto-checks/phash/copy.png +0 -0
  137. package/scripts/__fixtures__/veto-checks/phash/distinct.png +0 -0
  138. package/scripts/__fixtures__/veto-checks/phash/distinct2.png +0 -0
  139. package/scripts/__fixtures__/veto-checks/phash/make-fixtures.cjs +203 -0
  140. package/scripts/__fixtures__/veto-checks/phash/ref.png +0 -0
  141. package/scripts/__fixtures__/veto-checks/slide-ok.html +15 -0
  142. package/scripts/__fixtures__/veto-checks/slide-overflow.html +14 -0
  143. package/scripts/__fixtures__/veto-checks/sweep-pod/agents/vc-checker.md +13 -0
  144. package/scripts/__fixtures__/veto-checks/sweep-pod/templates/check-tmpl.md +6 -0
  145. package/scripts/__fixtures__/veto-checks/sweep-pod/workflows/wf-vc.smoke.json +10 -0
  146. package/scripts/__fixtures__/veto-checks/sweep-pod/workflows/wf-vc.yaml +19 -0
  147. package/scripts/__fixtures__/voz-do-dono/README.md +23 -0
  148. package/scripts/__fixtures__/voz-do-dono/dono-escalar.yaml +9 -0
  149. package/scripts/__fixtures__/voz-do-dono/dono-guia.yaml +10 -0
  150. package/scripts/__fixtures__/voz-do-dono/peca-3a-pessoa.md +6 -0
  151. package/scripts/__fixtures__/voz-do-dono/peca-com-lexico-proibido.md +6 -0
  152. package/scripts/__fixtures__/voz-do-dono/peca-limpa-1a-pessoa.md +6 -0
  153. package/scripts/__tests__/derive-ready-transitions.test.cjs +269 -0
  154. package/scripts/__tests__/enforce-cycle-authority.test.cjs +869 -0
  155. package/scripts/__tests__/enforce-git-push-authority.test.cjs +701 -0
  156. package/scripts/__tests__/enforce-ready-oracles.test.cjs +745 -0
  157. package/scripts/__tests__/motor-scripts.test.cjs +339 -0
  158. package/scripts/__tests__/pc-run-pod-counter.test.cjs +58 -0
  159. package/scripts/__tests__/pc-run-pod.test.cjs +578 -0
  160. package/scripts/__tests__/validate-pod-workflow.test.cjs +90 -0
  161. package/scripts/__tests__/validate-story-acs.test.cjs +607 -0
  162. package/scripts/anti-path.test.cjs +163 -0
  163. package/scripts/arena.cjs +514 -0
  164. package/scripts/arena.test.cjs +213 -0
  165. package/scripts/brief-leitura-obrigatoria.test.cjs +364 -0
  166. package/scripts/consolidate-knowledge-runner.test.cjs +249 -0
  167. package/scripts/default-funcional.test.cjs +153 -0
  168. package/scripts/derive-ready-transitions.cjs +325 -0
  169. package/scripts/forge-additive.test.cjs +158 -0
  170. package/scripts/forge-assess-tier.cjs +205 -0
  171. package/scripts/forge-assess-tier.test.cjs +114 -0
  172. package/scripts/forge-canonical.test.cjs +624 -0
  173. package/scripts/forge-generate.cjs +1303 -0
  174. package/scripts/forge-harden-learning.cjs +921 -0
  175. package/scripts/forge-harden-learning.test.cjs +242 -0
  176. package/scripts/forge-ingest-memory.cjs +136 -0
  177. package/scripts/forge-upgrade-pod.cjs +368 -0
  178. package/scripts/forge.test.cjs +609 -0
  179. package/scripts/harden-knowledge.test.cjs +612 -0
  180. package/scripts/harness-g5-lite.cjs +243 -0
  181. package/scripts/harness-g5-lite.test.cjs +134 -0
  182. package/scripts/hn-carousel-contrato-entrada.test.cjs +96 -0
  183. package/scripts/migrate-squad-orchestration.test.cjs +146 -0
  184. package/scripts/migration-manifest-sf3.2.test.cjs +382 -0
  185. package/scripts/migration-manifest-sf3.3.test.cjs +170 -0
  186. package/scripts/migration-manifest-sf5.5.test.cjs +189 -0
  187. package/scripts/migration-manifest.cjs +1146 -0
  188. package/scripts/migration-manifest.test.cjs +186 -0
  189. package/scripts/npm-scope-invocation.test.cjs +194 -0
  190. package/scripts/owner-layer.cjs +442 -0
  191. package/scripts/owner-layer.test.cjs +358 -0
  192. package/scripts/pc-run-pipeline.cjs +289 -0
  193. package/scripts/pc-run-pipeline.test.cjs +256 -0
  194. package/scripts/pc-run-pod-countgte.test.cjs +66 -0
  195. package/scripts/pc-run-pod.cjs +1355 -0
  196. package/scripts/phash-gate.test.cjs +433 -0
  197. package/scripts/pod-export-compile.cjs +487 -0
  198. package/scripts/pod-export-compile.test.cjs +312 -0
  199. package/scripts/pod-export-executor.cjs +678 -0
  200. package/scripts/pod-export-executor.test.cjs +1341 -0
  201. package/scripts/pod-export-hardening.test.cjs +531 -0
  202. package/scripts/pod-export-postprocess.cjs +427 -0
  203. package/scripts/pod-export-postprocess.test.cjs +558 -0
  204. package/scripts/pod-export-queue-race.test.cjs +865 -0
  205. package/scripts/pod-export-release.cjs +473 -0
  206. package/scripts/pod-export-release.test.cjs +267 -0
  207. package/scripts/pod-export-service.cjs +1297 -0
  208. package/scripts/pod-export-service.test.cjs +489 -0
  209. package/scripts/pod-export-tools.cjs +470 -0
  210. package/scripts/pod-export-ui.cjs +805 -0
  211. package/scripts/pod-export-ui.test.cjs +301 -0
  212. package/scripts/pod-memory-budget.test.cjs +309 -0
  213. package/scripts/pod-memory-note.cjs +357 -0
  214. package/scripts/pod-memory-note.test.cjs +452 -0
  215. package/scripts/pod-memory.cjs +1252 -0
  216. package/scripts/pod-memory.test.cjs +951 -0
  217. package/scripts/render-png.test.cjs +307 -0
  218. package/scripts/smoke-isolation.test.cjs +100 -0
  219. package/scripts/smoke-pod-workflow.cjs +755 -0
  220. package/scripts/smoke-sweep.test.cjs +170 -0
  221. package/scripts/validate-adopt.js +235 -0
  222. package/scripts/validate-branding.js +84 -0
  223. package/scripts/validate-budgets.js +140 -0
  224. package/scripts/validate-fidelity-output.cjs +267 -0
  225. package/scripts/validate-fidelity-output.test.cjs +101 -0
  226. package/scripts/validate-install.js +230 -0
  227. package/scripts/validate-no-clone.cjs +220 -0
  228. package/scripts/validate-no-clone.test.cjs +77 -0
  229. package/scripts/validate-pipeline.cjs +145 -0
  230. package/scripts/validate-pod-workflow.cjs +878 -0
  231. package/scripts/validate-source-audit.cjs +170 -0
  232. package/scripts/validate-source-audit.test.cjs +90 -0
  233. package/scripts/validate-story-acs.cjs +862 -0
  234. package/scripts/validate-update-scope-motor.js +421 -0
  235. package/scripts/veto-checks/html-valid.cjs +92 -0
  236. package/scripts/veto-checks/lexical-check.cjs +62 -0
  237. package/scripts/veto-checks/phash-gate.cjs +589 -0
  238. package/scripts/veto-checks/render-gate.cjs +165 -0
  239. package/scripts/veto-checks/render-png.cjs +358 -0
  240. package/scripts/veto-checks.test.cjs +219 -0
  241. package/scripts/watcher-audit.cjs +178 -0
  242. package/template/.claude/CLAUDE.md +103 -0
  243. package/template/.claude/agents/pc-analyst.md +100 -0
  244. package/template/.claude/agents/pc-architect.md +32 -0
  245. package/template/.claude/agents/pc-dev.md +27 -0
  246. package/template/.claude/agents/pc-devops.md +45 -0
  247. package/template/.claude/agents/pc-planner.md +32 -0
  248. package/template/.claude/agents/pc-po.md +25 -0
  249. package/template/.claude/agents/pc-qa.md +25 -0
  250. package/template/.claude/commands/power/academy.md +9 -0
  251. package/template/.claude/commands/power/automation.md +9 -0
  252. package/template/.claude/commands/power/content-carousel.md +267 -0
  253. package/template/.claude/commands/power/content.md +9 -0
  254. package/template/.claude/commands/power/dev.md +9 -0
  255. package/template/.claude/commands/power/forge.md +224 -0
  256. package/template/.claude/commands/power/hormozi.md +221 -0
  257. package/template/.claude/commands/power/modo.md +57 -0
  258. package/template/.claude/hooks/README.md +114 -0
  259. package/template/.claude/hooks/context-budget.cjs +64 -0
  260. package/template/.claude/hooks/enforce-cycle-authority.cjs +457 -0
  261. package/template/.claude/hooks/enforce-git-push-authority.cjs +925 -0
  262. package/template/.claude/hooks/enforce-ready-oracles.cjs +502 -0
  263. package/template/.claude/rules/authority.md +76 -0
  264. package/template/.claude/rules/curadoria-de-conhecimento.md +30 -0
  265. package/template/.claude/rules/model-matrix.md +34 -0
  266. package/template/.claude/rules/orchestration.md +133 -0
  267. package/template/.claude/settings.json +50 -0
  268. package/template/.claude/skills/pc-apply-qa-fixes/SKILL.md +108 -0
  269. package/template/.claude/skills/pc-apply-qa-fixes/references/fix-loop.md +40 -0
  270. package/template/.claude/skills/pc-apply-qa-fixes/references/fix-request-template.md +70 -0
  271. package/template/.claude/skills/pc-arena/SKILL.md +96 -0
  272. package/template/.claude/skills/pc-close-story/SKILL.md +105 -0
  273. package/template/.claude/skills/pc-commit/SKILL.md +108 -0
  274. package/template/.claude/skills/pc-develop-story/SKILL.md +116 -0
  275. package/template/.claude/skills/pc-full-cycle/SKILL.md +150 -0
  276. package/template/.claude/skills/pc-pipeline/SKILL.md +63 -0
  277. package/template/.claude/skills/pc-pipeline/references/gate-templates.md +46 -0
  278. package/template/.claude/skills/pc-review-story/SKILL.md +129 -0
  279. package/template/.claude/skills/pc-review-story/references/false-positive-detection.md +50 -0
  280. package/template/.claude/skills/pc-review-story/references/nfr-assess.md +47 -0
  281. package/template/.claude/skills/pc-review-story/references/risk-profile.md +75 -0
  282. package/template/.claude/skills/pc-review-story/references/security-checklist.md +69 -0
  283. package/template/.claude/skills/pc-review-story/references/test-design.md +37 -0
  284. package/template/.claude/skills/pc-review-story/references/trace-requirements.md +46 -0
  285. package/template/.claude/skills/pc-run-pod/SKILL.md +123 -0
  286. package/template/.claude/skills/pc-validate-story/SKILL.md +114 -0
  287. package/template/.claude/skills/pc-wave/SKILL.md +133 -0
  288. package/template/.env.example +10 -0
  289. package/template/knowledge/analyst/brainstorming.md +40 -0
  290. package/template/knowledge/analyst/brief-skeleton.md +32 -0
  291. package/template/knowledge/analyst/competitor-skeleton.md +39 -0
  292. package/template/knowledge/analyst/elicitation-menu.md +49 -0
  293. package/template/knowledge/analyst/market-research-skeleton.md +36 -0
  294. package/template/knowledge/analyst/research-prompt-structure.md +48 -0
  295. package/template/knowledge/analyst/roi-skeleton.md +51 -0
  296. package/template/knowledge/voice-dna/README.md +99 -0
  297. package/template/knowledge/voice-dna/voice-dna.example.yaml +157 -0
  298. package/template/pods/README.md +64 -0
  299. package/template/pods/academy/README.md +37 -0
  300. package/template/pods/academy/agents/academy-chief.md +27 -0
  301. package/template/pods/academy/examples/palestra.example.md +142 -0
  302. package/template/pods/academy/pod-spec.json +44 -0
  303. package/template/pods/academy/templates/materiais-tmpl.md +17 -0
  304. package/template/pods/academy/templates/revisao-didatica-tmpl.md +20 -0
  305. package/template/pods/academy/workflows/wf-materiais-revisao.yaml +30 -0
  306. package/template/pods/authority-content/README.md +43 -0
  307. package/template/pods/authority-content/agents/content-chief.md +28 -0
  308. package/template/pods/authority-content/pod-spec.json +45 -0
  309. package/template/pods/authority-content/templates/criacao-na-voz-tmpl.md +17 -0
  310. package/template/pods/authority-content/templates/red-team-factual-tmpl.md +23 -0
  311. package/template/pods/authority-content/workflows/wf-criacao-redteam.yaml +30 -0
  312. package/template/pods/automation/README.md +37 -0
  313. package/template/pods/automation/agents/langgraph-architect.md +30 -0
  314. package/template/pods/automation/agents/n8n-architect.md +30 -0
  315. package/template/pods/automation/examples/README.md +46 -0
  316. package/template/pods/automation/examples/n8n-content-radar.example.json +380 -0
  317. package/template/pods/automation/knowledge/langgraph-patterns.md +126 -0
  318. package/template/pods/automation/knowledge/n8n-patterns.md +131 -0
  319. package/template/pods/automation/memory/README.md +16 -0
  320. package/template/pods/automation/memory/learnings.consolidated.md +8 -0
  321. package/template/pods/automation/memory/learnings.jsonl +0 -0
  322. package/template/pods/automation/pod-spec.json +109 -0
  323. package/template/pods/automation/specs/wf-langgraph-build.spec.json +107 -0
  324. package/template/pods/automation/squad-io.yaml +28 -0
  325. package/template/pods/automation/templates/lg-build-tmpl.md +17 -0
  326. package/template/pods/automation/templates/lg-design-tmpl.md +23 -0
  327. package/template/pods/automation/templates/lg-discover-tmpl.md +20 -0
  328. package/template/pods/automation/templates/lg-handoff-tmpl.md +20 -0
  329. package/template/pods/automation/templates/lg-test-tmpl.md +20 -0
  330. package/template/pods/automation/templates/n8n-build-tmpl.md +14 -0
  331. package/template/pods/automation/templates/n8n-design-tmpl.md +23 -0
  332. package/template/pods/automation/templates/n8n-discover-tmpl.md +20 -0
  333. package/template/pods/automation/templates/n8n-handoff-tmpl.md +20 -0
  334. package/template/pods/automation/templates/n8n-test-tmpl.md +20 -0
  335. package/template/pods/automation/workflows/wf-langgraph-build.smoke.json +11 -0
  336. package/template/pods/automation/workflows/wf-langgraph-build.yaml +64 -0
  337. package/template/pods/automation/workflows/wf-n8n-build.smoke.json +11 -0
  338. package/template/pods/automation/workflows/wf-n8n-build.yaml +65 -0
  339. package/template/pods/content-carousel/README.md +91 -0
  340. package/template/pods/content-carousel/agents/carousel-builder.md +28 -0
  341. package/template/pods/content-carousel/agents/carousel-visual.md +24 -0
  342. package/template/pods/content-carousel/agents/hn-curator.md +74 -0
  343. package/template/pods/content-carousel/config/heuristic-watcher-rubric.yaml +59 -0
  344. package/template/pods/content-carousel/knowledge/aquisicao-ancora.md +443 -0
  345. package/template/pods/content-carousel/knowledge/cta-brief.md +25 -0
  346. package/template/pods/content-carousel/knowledge/dono.example.yaml +45 -0
  347. package/template/pods/content-carousel/knowledge/identidade-visual-mascote.md +62 -0
  348. package/template/pods/content-carousel/knowledge/image-generation.md +62 -0
  349. package/template/pods/content-carousel/knowledge/selecao-de-ancora.md +60 -0
  350. package/template/pods/content-carousel/knowledge/slides-html.md +56 -0
  351. package/template/pods/content-carousel/memory/README.md +18 -0
  352. package/template/pods/content-carousel/memory/learnings.consolidated.md +6 -0
  353. package/template/pods/content-carousel/memory/learnings.jsonl +0 -0
  354. package/template/pods/content-carousel/pod-spec.json +100 -0
  355. package/template/pods/content-carousel/squad-io.yaml +10 -0
  356. package/template/pods/content-carousel/templates/assembly-tmpl.md +23 -0
  357. package/template/pods/content-carousel/templates/digest-tmpl.md +29 -0
  358. package/template/pods/content-carousel/templates/visuals-tmpl.md +20 -0
  359. package/template/pods/content-carousel/workflows/wf-content-carousel.smoke.json +23 -0
  360. package/template/pods/content-carousel/workflows/wf-content-carousel.yaml +80 -0
  361. package/template/pods/dev-ia/README.md +45 -0
  362. package/template/pods/dev-ia/agents/db-architect.md +33 -0
  363. package/template/pods/dev-ia/agents/ux-architect.md +29 -0
  364. package/template/pods/dev-ia/knowledge/db/migration-protocol.md +95 -0
  365. package/template/pods/dev-ia/knowledge/db/rls-patterns.md +101 -0
  366. package/template/pods/dev-ia/knowledge/db/schema-review-checklist.md +76 -0
  367. package/template/pods/dev-ia/memory/README.md +16 -0
  368. package/template/pods/dev-ia/memory/learnings.consolidated.md +8 -0
  369. package/template/pods/dev-ia/memory/learnings.jsonl +0 -0
  370. package/template/pods/dev-ia/pod-spec.json +34 -0
  371. package/template/pods/dev-ia/templates/schema-review-tmpl.md +23 -0
  372. package/template/pods/dev-ia/templates/ux-flow-review-tmpl.md +23 -0
  373. package/template/pods/dev-ia/tools/db-ops/backup-manager.sh +321 -0
  374. package/template/pods/dev-ia/tools/db-ops/best-practices-auditor.sh +534 -0
  375. package/template/pods/dev-ia/tools/db-ops/bootstrap-runner.sh +255 -0
  376. package/template/pods/dev-ia/tools/db-ops/data-loader.sh +616 -0
  377. package/template/pods/dev-ia/tools/db-ops/detect-database.sh +113 -0
  378. package/template/pods/dev-ia/tools/db-ops/explain-analyzer.sh +285 -0
  379. package/template/pods/dev-ia/tools/db-ops/health-checker.sh +323 -0
  380. package/template/pods/dev-ia/tools/db-ops/migration-runner.sh +308 -0
  381. package/template/pods/dev-ia/tools/db-ops/postgresql-schema-loader.sh +334 -0
  382. package/template/pods/dev-ia/tools/db-ops/query-runner.sh +171 -0
  383. package/template/pods/dev-ia/tools/db-ops/rls-policy-installer.sh +480 -0
  384. package/template/pods/dev-ia/tools/db-ops/rls-test-suite.sh +172 -0
  385. package/template/pods/dev-ia/tools/db-ops/schema-context-loader.sh +176 -0
  386. package/template/pods/hormozi-method/README.md +52 -0
  387. package/template/pods/hormozi-method/agents/acquisition-strategist.md +33 -0
  388. package/template/pods/hormozi-method/agents/conversion-copywriter.md +34 -0
  389. package/template/pods/hormozi-method/agents/monetization-architect.md +35 -0
  390. package/template/pods/hormozi-method/agents/offer-engineer.md +37 -0
  391. package/template/pods/hormozi-method/knowledge/core-four-leads.md +51 -0
  392. package/template/pods/hormozi-method/knowledge/grand-slam-offer.md +66 -0
  393. package/template/pods/hormozi-method/knowledge/growth-decision.md +38 -0
  394. package/template/pods/hormozi-method/knowledge/hooks-and-ads.md +56 -0
  395. package/template/pods/hormozi-method/knowledge/launch-email.md +31 -0
  396. package/template/pods/hormozi-method/knowledge/money-model.md +46 -0
  397. package/template/pods/hormozi-method/knowledge/opportunity-screening.md +44 -0
  398. package/template/pods/hormozi-method/knowledge/pages.md +25 -0
  399. package/template/pods/hormozi-method/knowledge/pos-lancamento-leads.md +56 -0
  400. package/template/pods/hormozi-method/knowledge/pricing.md +36 -0
  401. package/template/pods/hormozi-method/knowledge/retention-ltv.md +37 -0
  402. package/template/pods/hormozi-method/knowledge/scale-roadmap.md +35 -0
  403. package/template/pods/hormozi-method/knowledge/swipefiles-curados.md +44 -0
  404. package/template/pods/hormozi-method/knowledge/value-equation-diagnostic.md +77 -0
  405. package/template/pods/hormozi-method/memory/README.md +16 -0
  406. package/template/pods/hormozi-method/memory/learnings.consolidated.md +6 -0
  407. package/template/pods/hormozi-method/memory/learnings.jsonl +0 -0
  408. package/template/pods/hormozi-method/pod-spec.json +50 -0
  409. package/template/pods/hormozi-method/specs/wf-full-launch-sequence.spec.json +149 -0
  410. package/template/pods/hormozi-method/specs/wf-lead-magnet-pipeline.spec.json +125 -0
  411. package/template/pods/hormozi-method/specs/wf-money-model-design.spec.json +117 -0
  412. package/template/pods/hormozi-method/specs/wf-paid-ads-campaign.spec.json +132 -0
  413. package/template/pods/hormozi-method/specs/wf-scaling-diagnostic.spec.json +121 -0
  414. package/template/pods/hormozi-method/squad-io.yaml +43 -0
  415. package/template/pods/hormozi-method/templates/conversion-copywriter-tmpl.md +20 -0
  416. package/template/pods/hormozi-method/templates/launch-execucao-tmpl.md +17 -0
  417. package/template/pods/hormozi-method/templates/launch-fechamento-tmpl.md +17 -0
  418. package/template/pods/hormozi-method/templates/launch-pos-tmpl.md +17 -0
  419. package/template/pods/hormozi-method/templates/launch-preparacao-tmpl.md +17 -0
  420. package/template/pods/hormozi-method/templates/lm-copy-tmpl.md +17 -0
  421. package/template/pods/hormozi-method/templates/lm-distribuicao-tmpl.md +18 -0
  422. package/template/pods/hormozi-method/templates/lm-estrategia-tmpl.md +17 -0
  423. package/template/pods/hormozi-method/templates/mm-arquitetura-tmpl.md +18 -0
  424. package/template/pods/hormozi-method/templates/mm-copy-tmpl.md +17 -0
  425. package/template/pods/hormozi-method/templates/mm-diagnostico-tmpl.md +14 -0
  426. package/template/pods/hormozi-method/templates/monetization-architect-tmpl.md +17 -0
  427. package/template/pods/hormozi-method/templates/offer-engineer-tmpl.md +20 -0
  428. package/template/pods/hormozi-method/templates/paid-criativos-tmpl.md +19 -0
  429. package/template/pods/hormozi-method/templates/paid-fundacao-tmpl.md +17 -0
  430. package/template/pods/hormozi-method/templates/paid-teste-escala-tmpl.md +17 -0
  431. package/template/pods/hormozi-method/templates/scaling-diagnostico-tmpl.md +17 -0
  432. package/template/pods/hormozi-method/templates/scaling-priorizacao-tmpl.md +17 -0
  433. package/template/pods/hormozi-method/templates/scaling-roadmap-tmpl.md +17 -0
  434. package/template/pods/hormozi-method/workflows/wf-full-launch-sequence.smoke.json +12 -0
  435. package/template/pods/hormozi-method/workflows/wf-full-launch-sequence.yaml +56 -0
  436. package/template/pods/hormozi-method/workflows/wf-grand-slam-offer.smoke.json +11 -0
  437. package/template/pods/hormozi-method/workflows/wf-grand-slam-offer.yaml +45 -0
  438. package/template/pods/hormozi-method/workflows/wf-lead-magnet-pipeline.smoke.json +9 -0
  439. package/template/pods/hormozi-method/workflows/wf-lead-magnet-pipeline.yaml +45 -0
  440. package/template/pods/hormozi-method/workflows/wf-money-model-design.smoke.json +11 -0
  441. package/template/pods/hormozi-method/workflows/wf-money-model-design.yaml +45 -0
  442. package/template/pods/hormozi-method/workflows/wf-paid-ads-campaign.smoke.json +9 -0
  443. package/template/pods/hormozi-method/workflows/wf-paid-ads-campaign.yaml +46 -0
  444. package/template/pods/hormozi-method/workflows/wf-scaling-diagnostic.smoke.json +11 -0
  445. package/template/pods/hormozi-method/workflows/wf-scaling-diagnostic.yaml +44 -0
  446. package/template/pods/squad-forge/README.md +53 -0
  447. package/template/pods/squad-forge/agents/forge-chief.md +31 -0
  448. package/template/pods/squad-forge/agents/mind-extractor.md +29 -0
  449. package/template/pods/squad-forge/agents/pod-critic.md +26 -0
  450. package/template/pods/squad-forge/config/heuristic-watcher-rubric.yaml +74 -0
  451. package/template/pods/squad-forge/knowledge/fidelity-rubric.yaml +176 -0
  452. package/template/pods/squad-forge/knowledge/g6-memory-check.md +17 -0
  453. package/template/pods/squad-forge/knowledge/memory-ingest.md +16 -0
  454. package/template/pods/squad-forge/knowledge/source-audit-rubric.yaml +60 -0
  455. package/template/pods/squad-forge/memory/README.md +16 -0
  456. package/template/pods/squad-forge/memory/learnings.consolidated.md +8 -0
  457. package/template/pods/squad-forge/memory/learnings.jsonl +0 -0
  458. package/template/pods/squad-forge/pod-spec.json +166 -0
  459. package/template/pods/squad-forge/squad-io.yaml +13 -0
  460. package/template/pods/squad-forge/tasks/pipeline.md +158 -0
  461. package/template/pods/squad-forge/templates/discovery-tmpl.md +40 -0
  462. package/template/pods/squad-forge/templates/generation-tmpl.md +18 -0
  463. package/template/pods/squad-forge/templates/red-team-tmpl.md +17 -0
  464. package/template/pods/squad-forge/templates/register-tmpl.md +17 -0
  465. package/template/pods/squad-forge/templates/workflow-tmpl.yaml +101 -0
  466. package/template/pods/squad-forge/workflows/wf-forge.smoke.json +12 -0
  467. package/template/pods/squad-forge/workflows/wf-forge.yaml +60 -0
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * arena.test.cjs — suíte do motor arena.cjs (Story SF7.4, AC4; ADR-009 Decisão 2).
5
+ * Prova, com experimentos sintéticos em diretório temporário:
6
+ * (1) REGRESSÃO bug-class 1 (indexação 1-based portada p/ 0-based): reproduz a classe
7
+ * exata do erro (mapa 1-indexed → braço undefined/perdido) e prova que
8
+ * validatePermutation a PEGA; e que permute() gera bijeção total sobre 200 seeds.
9
+ * (2) REGRESSÃO bug-class 2 (estado/contador perdido entre iterações): reproduz a classe
10
+ * (mutar cópia sem persistir → disco não avança) e prova que o motor PERSISTE o estado
11
+ * entre invocações separadas (recarga fresca do disco), de modo que o reveal-gate lê a
12
+ * contagem PERSISTIDA.
13
+ * (3) uniquidade/bijeção da permutação + N≥3 rejeita identidade.
14
+ * (4) reveal-gate: revelação prematura é RECUSADA (fail-loud), nenhum mapa lido.
15
+ * (5) aritmética: weightedRoundScore exato, verdictForAxis (vitória/empate/K-de-N),
16
+ * tally consistente; fake-green (nota fora de faixa / passed>checks / pesos≠1) FALHA.
17
+ * (6) N≥2 parametrizável.
18
+ *
19
+ * Node >= 18. Exit 0/1.
20
+ */
21
+
22
+ const fs = require('node:fs');
23
+ const os = require('node:os');
24
+ const path = require('node:path');
25
+ const A = require('./arena.cjs');
26
+
27
+ let passed = 0;
28
+ const failures = [];
29
+ function ok(cond, label) {
30
+ if (cond) { passed++; console.log(`[OK ] ${label}`); }
31
+ else { failures.push(label); console.error(`[FALHA] ${label}`); }
32
+ }
33
+ function throwsArena(fn) {
34
+ try { fn(); return false; } catch (e) { if (e instanceof A.ArenaError) return true; throw e; }
35
+ }
36
+ function tmpExp() { return fs.mkdtempSync(path.join(os.tmpdir(), 'arena-')); }
37
+
38
+ const RUBRIC = {
39
+ scale: 5,
40
+ dimensions: [
41
+ { name: 'Q', weight: 0.5, type: 'human' },
42
+ { name: 'S', weight: 0.3, type: 'machine', checks: 2 },
43
+ { name: 'P', weight: 0.2, type: 'human' },
44
+ ],
45
+ verdict: { minDiff: 0.3, kOfN: 2 },
46
+ axes: [{ name: 'A1 vs A2', a: 'A1', b: 'A2' }],
47
+ };
48
+
49
+ // ===========================================================================
50
+ // (1) BUG-CLASS 1 — indexação 1-based portada para 0-based
51
+ // ===========================================================================
52
+ console.log('--- (1) regressão bug-class 1: indexação 0/1 ---');
53
+ {
54
+ const arms = ['A1', 'A2', 'A3', 'A4'];
55
+ const labels = ['W', 'X', 'Y', 'Z'];
56
+ // Reproduz a CLASSE: um port 1-indexed constrói map[labels[i]] = arms[i] com i = 1..N.
57
+ const buggyMap = {};
58
+ labels.forEach((lab, idx) => { const oneBased = idx + 1; buggyMap[lab] = arms[oneBased]; }); // arms[4]=undefined, arms[0] perdido
59
+ ok(buggyMap.Z === undefined && !Object.values(buggyMap).includes('A1'),
60
+ 'bug-class 1 REPRODUZIDA: mapa 1-indexed perde A1 e gera braço undefined');
61
+ ok(throwsArena(() => A.validatePermutation(buggyMap, arms, labels)),
62
+ 'motor IMUNE: validatePermutation PEGA o mapa 1-indexed (braço undefined/perdido)');
63
+ // permute() do motor: bijeção total sobre 200 seeds distintas.
64
+ let allValid = true;
65
+ for (let s = 0; s < 200; s++) {
66
+ const m = A.permute(arms, labels, s);
67
+ const covered = new Set(Object.values(m));
68
+ if (covered.size !== 4 || Object.values(m).includes(undefined)) { allValid = false; break; }
69
+ }
70
+ ok(allValid, 'permute() gera bijeção total (4 braços distintos, sem undefined) em 200 seeds');
71
+ }
72
+
73
+ // ===========================================================================
74
+ // (2) BUG-CLASS 2 — estado/contador perdido entre iterações
75
+ // ===========================================================================
76
+ console.log('--- (2) regressão bug-class 2: estado perdido entre iterações ---');
77
+ {
78
+ const exp = tmpExp();
79
+ A.init(exp, { arms: ['A1', 'A2'], rounds: 3, rubric: RUBRIC, seed: 7 });
80
+ // Reproduz a CLASSE: mutar uma CÓPIA em memória sem persistir → disco não avança.
81
+ const s0 = A.loadState(exp);
82
+ const copy = JSON.parse(JSON.stringify(s0));
83
+ copy.scores['1'] = { W: {}, X: {} }; // mutou a cópia, NÃO salvou
84
+ ok(A.scoredRoundsCount(A.loadState(exp)) === 0,
85
+ 'bug-class 2 REPRODUZIDA: mutar cópia sem persistir deixa o disco em 0 rodadas');
86
+ // Motor IMUNE: cada recordScore é uma invocação separada que RECARREGA e SALVA no disco.
87
+ const sc = { Q: 4, S: { passed: 2 }, P: 3 };
88
+ for (let r = 1; r <= 3; r++) {
89
+ A.recordScore(exp, r, 'W', sc); // invocação separada: loadState fresco + saveState atômico
90
+ A.recordScore(exp, r, 'X', sc);
91
+ ok(A.scoredRoundsCount(A.loadState(exp)) === r,
92
+ `estado PERSISTE entre invocações: após rodada ${r} pontuada, disco mostra ${r} (contador não se perde)`);
93
+ }
94
+ }
95
+
96
+ // ===========================================================================
97
+ // (3) uniquidade + N≥3 rejeita identidade
98
+ // ===========================================================================
99
+ console.log('--- (3) uniquidade / anti-identidade ---');
100
+ {
101
+ const arms = ['A1', 'A2', 'A3'];
102
+ const labels = ['W', 'X', 'Y'];
103
+ let neverIdentity = true;
104
+ for (let s = 0; s < 100; s++) {
105
+ const m = A.permute(arms, labels, s);
106
+ if (labels.every((l, i) => m[l] === arms[i])) { neverIdentity = false; break; }
107
+ }
108
+ ok(neverIdentity, 'N≥3: permute() nunca devolve a permutação identidade (tell óbvio) em 100 seeds');
109
+ }
110
+
111
+ // ===========================================================================
112
+ // (4) reveal-gate
113
+ // ===========================================================================
114
+ console.log('--- (4) reveal-gate ---');
115
+ {
116
+ const exp = tmpExp();
117
+ A.init(exp, { arms: ['A1', 'A2'], rounds: 2, rubric: RUBRIC, seed: 1 });
118
+ const sc = { Q: 4, S: { passed: 2 }, P: 3 };
119
+ A.recordScore(exp, 1, 'W', sc); A.recordScore(exp, 1, 'X', sc); // só rodada 1
120
+ ok(throwsArena(() => A.reveal(exp)), 'reveal PREMATURO (1/2 rodadas) é RECUSADO fail-loud');
121
+ ok(A.loadState(exp).revealed === false, 'estado NÃO marca revealed após recusa (nenhum mapa liberado)');
122
+ A.recordScore(exp, 2, 'W', sc); A.recordScore(exp, 2, 'X', sc); // completa
123
+ const map = A.reveal(exp);
124
+ ok(map && map['1'] && map['2'], 'reveal LIBERA o mapa após todas as rodadas pontuadas');
125
+ ok(A.loadState(exp).revealed === true, 'estado marca revealed=true após revelação legítima');
126
+ // Mapa selado em disco com permissão restrita (best-effort POSIX).
127
+ const st = fs.statSync(A.sealedPath(exp));
128
+ const mode = st.mode & 0o777;
129
+ ok(mode === 0o600 || process.platform === 'win32', `mapa selado com chmod 600 (mode=${mode.toString(8)})`);
130
+ }
131
+
132
+ // ===========================================================================
133
+ // (5) aritmética da agregação + fake-green
134
+ // ===========================================================================
135
+ console.log('--- (5) aritmética / veredito ---');
136
+ {
137
+ // weightedRoundScore exato: 4*0.5 + (2/2*5)*0.3 + 3*0.2 = 2 + 1.5 + 0.6 = 4.1
138
+ const s = A.weightedRoundScore({ Q: 4, S: { passed: 2 }, P: 3 }, RUBRIC);
139
+ ok(Math.abs(s - 4.1) < 1e-9, `weightedRoundScore recomputa 4.1 exato (obteve ${s})`);
140
+ // machine dim parcial: passed 1/2 → 2.5*0.3=0.75 → 2 + 0.75 + 0.6 = 3.35
141
+ const s2 = A.weightedRoundScore({ Q: 4, S: { passed: 1 }, P: 3 }, RUBRIC);
142
+ ok(Math.abs(s2 - 3.35) < 1e-9, `machine dim proporcional (1/2 checks) recomputa 3.35 (obteve ${s2})`);
143
+ // fake-green: nota humana fora de faixa / passed > checks / rubrica com pesos ≠ 1 FALHAM
144
+ ok(throwsArena(() => A.weightedRoundScore({ Q: 9, S: { passed: 2 }, P: 3 }, RUBRIC)), 'fake-green: nota humana > escala FALHA');
145
+ ok(throwsArena(() => A.weightedRoundScore({ Q: 4, S: { passed: 5 }, P: 3 }, RUBRIC)), 'fake-green: machine passed > checks FALHA');
146
+ ok(throwsArena(() => A.validateRubric({ scale: 5, dimensions: [{ name: 'Q', weight: 0.7, type: 'human' }], verdict: { minDiff: 0.3, kOfN: 1 } })), 'fake-green: pesos ≠ 1.0 FALHA');
147
+
148
+ // verdictForAxis: vitória (A ≥0.3 em ≥2 de 3), empate, K-de-N
149
+ const win = A.verdictForAxis([4.5, 4.6, 4.4], [4.0, 4.2, 4.05], 0.3, 2);
150
+ ok(win.winner === 'A' && win.tie === false, 'veredito: A vence (≥0.3 em ≥2/3 rodadas)');
151
+ const tie = A.verdictForAxis([4.1, 4.0, 4.2], [4.0, 4.05, 4.1], 0.3, 2);
152
+ ok(tie.winner === 'tie' && tie.tie === true, 'veredito: diferença < limiar em rodadas suficientes → EMPATE (válido)');
153
+ const notSustained = A.verdictForAxis([5, 4, 4], [4, 4, 4], 0.3, 2); // A vence só 1 rodada por ≥0.3
154
+ ok(notSustained.winner === 'tie', 'veredito: vantagem não sustentada em K rodadas → EMPATE (não forçado)');
155
+
156
+ // tally full: consistência (soma por braço == soma por rótulo, por rodada)
157
+ const exp = tmpExp();
158
+ A.init(exp, { arms: ['A1', 'A2'], rounds: 2, rubric: RUBRIC, seed: 3 });
159
+ A.recordScore(exp, 1, 'W', { Q: 5, S: { passed: 2 }, P: 4 });
160
+ A.recordScore(exp, 1, 'X', { Q: 3, S: { passed: 1 }, P: 2 });
161
+ A.recordScore(exp, 2, 'W', { Q: 4, S: { passed: 2 }, P: 3 });
162
+ A.recordScore(exp, 2, 'X', { Q: 4, S: { passed: 2 }, P: 3 });
163
+ const t = A.tally(exp);
164
+ const armSum = t.perRoundByArm.A1.reduce((a, b) => a + b, 0) + t.perRoundByArm.A2.reduce((a, b) => a + b, 0);
165
+ // R1: W=5*.5+(2/2*5)*.3+4*.2=4.8 ; X=3*.5+(1/2*5)*.3+2*.2=2.65 ; R2: W=X=4*.5+5*.3+3*.2=4.1
166
+ ok(Math.abs(armSum - (4.8 + 2.65 + 4.1 + 4.1)) < 1e-6, 'tally: soma por braço == soma por rótulo (join via mapa selado correto)');
167
+ ok(t.axes.length === 1 && ('winner' in t.axes[0]), 'tally: veredito por eixo presente');
168
+ }
169
+
170
+ // ===========================================================================
171
+ // (6) N≥2 parametrizável
172
+ // ===========================================================================
173
+ console.log('--- (6) N parametrizável ---');
174
+ {
175
+ ok(throwsArena(() => A.init(tmpExp(), { arms: ['A1'], rounds: 2, rubric: RUBRIC })), 'init com 1 braço FALHA (N≥2)');
176
+ const exp = tmpExp();
177
+ const st = A.init(exp, { arms: ['A1', 'A2', 'A3', 'A4', 'A5'], rounds: 2, rubric: RUBRIC, seed: 9 });
178
+ ok(st.arms.length === 5 && st.labels.length === 5, 'init com N=5 braços OK (N não é constante 4)');
179
+ }
180
+
181
+ // ===========================================================================
182
+ // (7) CONCERNS-1 — agregado de checks-de-máquina PASS/FAIL, SEPARADO da média
183
+ // ===========================================================================
184
+ console.log('--- (7) agregado estrutural de máquina no relatório ---');
185
+ {
186
+ const exp = tmpExp();
187
+ A.init(exp, { arms: ['A1', 'A2'], rounds: 2, rubric: RUBRIC, seed: 5 });
188
+ // Um rótulo reprova 1 check de máquina (passed 1/2) numa rodada; o outro passa tudo.
189
+ A.recordScore(exp, 1, 'W', { Q: 4, S: { passed: 2 }, P: 3 });
190
+ A.recordScore(exp, 1, 'X', { Q: 4, S: { passed: 1 }, P: 3 }); // reprova 1 check estrutural
191
+ A.recordScore(exp, 2, 'W', { Q: 4, S: { passed: 2 }, P: 3 });
192
+ A.recordScore(exp, 2, 'X', { Q: 4, S: { passed: 2 }, P: 3 });
193
+ const t = A.tally(exp);
194
+ const failing = Object.entries(t.machineAggregate).filter(([, m]) => !m.pass);
195
+ ok(failing.length === 1 && failing[0][1].passed < failing[0][1].total,
196
+ 'tally.machineAggregate: braço com check reprovado tem pass=false e passed<total');
197
+ ok(Object.values(t.machineAggregate).some((m) => m.pass && m.passed === m.total),
198
+ 'tally.machineAggregate: o outro braço tem PASS integral (passed==total)');
199
+ // O agregado é SEPARADO da média: os dois braços têm agregados estruturais distintos.
200
+ const aggPassed = Object.values(t.machineAggregate).map((m) => m.passed);
201
+ ok(new Set(aggPassed).size === 2, 'agregado estrutural distingue os braços (sinal separado da média ponderada)');
202
+ const rep = A.report(exp).text;
203
+ ok(/Estrutura verificável por máquina — PASS\/FAIL agregado/.test(rep),
204
+ 'report() EXPÕE a seção de agregado estrutural (visível, não escondido na média)');
205
+ ok(/\*\*NÃO\*\*/.test(rep), 'report() marca **NÃO** para o braço com falha estrutural desqualificante');
206
+ }
207
+
208
+ if (failures.length > 0) {
209
+ console.error(`\nSuíte arena FALHOU: ${failures.length} de ${passed + failures.length}.`);
210
+ process.exit(1);
211
+ }
212
+ console.log(`\n${passed} asserções verdes. Motor arena provado.`);
213
+ process.exit(0);
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * brief-leitura-obrigatoria.test.cjs — suíte da Story SF11.2 (ADR-021).
5
+ *
6
+ * Duas ondas, mesmo ponto do código (`buildBrief`):
7
+ * PARTE A (`DF-SF11.2-5`) — `_sources` sai do brief de TODA `veto.assertion`, de todo pod,
8
+ * sem classificação por `type`/`field`/`id` e sem nome de pod. O caso honesto não regride:
9
+ * `type`/`field`/`pattern`/`n`/`command`/`args` continuam no brief.
10
+ * PARTE B (`DF-SF11.2-1`,`-4`,`-6`,`-10`) — seção "Leitura obrigatória ANTES de agir" com os
11
+ * paths ABSOLUTOS de `agents/<step.agent>.md` + cada `knowledge/<k>` NA ORDEM DECLARADA,
12
+ * existência VERIFICADA em disco (fail-closed no despacho), renderizada ANTES de `## Veto`,
13
+ * com o veto rotulado como verificação POSTERIOR.
14
+ *
15
+ * O que esta suíte NÃO prova, e não pode ser lida como se provasse (`DF-SF11.2-3`):
16
+ * que o agente LEIA ou OBEDEÇA. Presença ≠ obediência. O canal garante que a referência
17
+ * CHEGA e que os arquivos EXISTEM — ler e obedecer segue sendo disciplina do agente.
18
+ *
19
+ * Node >= 18. Exit 0/1.
20
+ */
21
+
22
+ const fs = require('node:fs');
23
+ const os = require('node:os');
24
+ const path = require('node:path');
25
+
26
+ const rp = require('./pc-run-pod.cjs');
27
+ const vp = require('./validate-pod-workflow.cjs');
28
+
29
+ const REPO_ROOT = path.resolve(__dirname, '..');
30
+ const DEV_ROOT = path.resolve(REPO_ROOT, '..');
31
+
32
+ let passed = 0;
33
+ const failures = [];
34
+ function ok(cond, label) {
35
+ if (cond) { passed++; console.log(`[OK ] ${label}`); }
36
+ else { failures.push(label); console.error(`[FALHA] ${label}`); }
37
+ }
38
+ function throwsFail(fn) { try { fn(); return false; } catch (e) { if (e instanceof vp.Fail) return true; throw e; } }
39
+
40
+ const tmpdirs = [];
41
+ function tmp(label) { const d = fs.mkdtempSync(path.join(os.tmpdir(), `sf11.2-${label}-`)); tmpdirs.push(d); return d; }
42
+
43
+ /**
44
+ * Pod sintético mínimo e COMPLETO (agents/ + knowledge/ + templates/ + workflows/), para as
45
+ * provas não dependerem de nenhum pod real. `knowledge` = null → etapa SEM o campo (retrocompat).
46
+ */
47
+ function makePod(dir, { knowledge = ['k1.md', 'k2.md'], assertions } = {}) {
48
+ fs.mkdirSync(path.join(dir, 'agents'), { recursive: true });
49
+ fs.mkdirSync(path.join(dir, 'knowledge'), { recursive: true });
50
+ fs.mkdirSync(path.join(dir, 'templates'), { recursive: true });
51
+ fs.mkdirSync(path.join(dir, 'workflows'), { recursive: true });
52
+ fs.writeFileSync(path.join(dir, 'agents', 'solo.md'), '---\nname: solo\nmodel: sonnet\n---\n\n# solo\n\nAgente de fixture.\n');
53
+ for (const k of (knowledge || [])) {
54
+ fs.writeFileSync(path.join(dir, 'knowledge', k), `# ${k}\n\nKnowledge de fixture.\n`);
55
+ }
56
+ fs.writeFileSync(path.join(dir, 'templates', 'solo-tmpl.md'), '# Artefato\n\n## Corpo\n\n(preencher)\n');
57
+ const vetoList = assertions || [
58
+ { type: 'count_gte', field: 'Corpo', n: 1, _sources: 'RACIONAL DE MANUTENCAO que o agente nao deve ver — ADR-020 / AUTO-ATESTADA / INVARIANTE DE MANUTENCAO' },
59
+ { type: 'field_matches', field: 'contrato_entrada', pattern: '^(ancora:hn)$', _sources: 'segundo racional AUTO-ATESTADA com pHash citado' },
60
+ ];
61
+ const wf = [
62
+ 'state_machine:',
63
+ ' states: [init, running, review, complete, failed]',
64
+ ' initial: init',
65
+ ' terminal: [complete, failed]',
66
+ '',
67
+ 'steps:',
68
+ ' - id: solo',
69
+ ' agent: solo',
70
+ ' template: templates/solo-tmpl.md',
71
+ ' inputs: [brief]',
72
+ ' outputs: [artefato]',
73
+ ...(knowledge ? [` knowledge: [${knowledge.join(', ')}]`] : []),
74
+ ' depends_on: []',
75
+ ' checkpoint: null',
76
+ ' veto:',
77
+ ' assertions:',
78
+ ...vetoList.map((a) => ` - ${JSON.stringify(a)}`),
79
+ '',
80
+ ].join('\n');
81
+ const wfPath = path.join(dir, 'workflows', 'wf-solo.yaml');
82
+ fs.writeFileSync(wfPath, wf);
83
+ return wfPath;
84
+ }
85
+
86
+ function startOn(wfPath, label) {
87
+ const runsRoot = tmp(`${label}-runs`);
88
+ const memoryPodDir = tmp(`${label}-mem`);
89
+ return rp.startRun({
90
+ workflowPath: wfPath, pod: 'solo', workflow: 'wf-solo', runsRoot, runId: `${label}-1`, memoryPodDir,
91
+ });
92
+ }
93
+
94
+ // ===========================================================================
95
+ // (AC1/AC2/AC3) PARTE A — `_sources` fora do brief, sem regressão do caso honesto
96
+ // ===========================================================================
97
+ console.log('--- (AC1/AC2/AC3) Parte A: `_sources` fora do brief ---');
98
+ {
99
+ const pod = tmp('a-pod');
100
+ const wfPath = makePod(pod);
101
+ const { result } = startOn(wfPath, 'a');
102
+ const brief = result.brief;
103
+ ok(result.action === 'dispatch' && typeof brief === 'string', 'pod completo despacha e emite brief');
104
+ ok(!/_sources/.test(brief), 'AC1: a string "_sources" NÃO aparece no brief');
105
+ for (const sonda of ['AUTO-ATESTADA', 'INVARIANTE DE MANUTENCAO', 'ADR-020', 'pHash', 'RACIONAL DE MANUTENCAO']) {
106
+ ok(!brief.includes(sonda), `AC1: sonda de racional "${sonda}" ausente do brief`);
107
+ }
108
+ ok(/"type":"count_gte"/.test(brief) && /"field":"Corpo"/.test(brief) && /"n":1/.test(brief),
109
+ 'AC2: caso honesto preservado — type/field/n da assertion de contagem seguem no brief');
110
+ ok(/"field":"contrato_entrada"/.test(brief) && brief.includes('^(ancora:hn)$'),
111
+ 'AC2: caso honesto preservado — field/pattern da assertion de discriminação seguem no brief');
112
+ }
113
+ {
114
+ // AC3 (genérico, sem classificação): projeção pura, provada em assertions de tipos e campos
115
+ // arbitrários — não há caminho no código que dependa de `type`, `field` ou `id`.
116
+ const casos = [
117
+ { type: 'field_matches', field: 'qualquer_campo_novo', pattern: '^x$', id: 'v1', _sources: 'corta' },
118
+ { type: 'command_check', command: 'lexical-check.cjs', args: ['--max', '0'], timeout_ms: 1000, _sources: 'corta' },
119
+ { type: 'section_exists', section: '## Seção', _sources: 'corta' },
120
+ { type: 'json_schema_valid', schema: { a: 1 }, _sources: 'corta' },
121
+ ];
122
+ let todosCortam = true;
123
+ let todosPreservam = true;
124
+ for (const c of casos) {
125
+ const proj = rp.vetoAssertionForBrief(c);
126
+ if ('_sources' in proj) todosCortam = false;
127
+ for (const k of Object.keys(c)) {
128
+ if (k === '_sources') continue;
129
+ if (JSON.stringify(proj[k]) !== JSON.stringify(c[k])) todosPreservam = false;
130
+ }
131
+ }
132
+ ok(todosCortam, 'AC3: `_sources` cortado em assertions de 4 tipos diferentes (sem classificação)');
133
+ ok(todosPreservam, 'AC3: TODOS os demais campos preservados byte-a-byte (inclusive `id`, `args`, `schema`)');
134
+ ok(rp.vetoAssertionForBrief(null) === null && rp.vetoAssertionForBrief('x') === 'x',
135
+ 'AC3: entrada não-objeto atravessa intacta (sem throw)');
136
+ const src = fs.readFileSync(path.join(__dirname, 'pc-run-pod.cjs'), 'utf8');
137
+ const fn = src.slice(src.indexOf('function vetoAssertionForBrief'), src.indexOf('function buildBrief'));
138
+ ok(!/hn-|carousel|contrato_entrada|digest|visuals|assembly/.test(fn),
139
+ 'AC3: o corpo de `vetoAssertionForBrief` não cita pod, etapa nem nome de campo (verificável no fonte)');
140
+ }
141
+
142
+ // ===========================================================================
143
+ // (AC6) PARTE B — seção presente, absoluta, na ordem declarada, ANTES do veto
144
+ // ===========================================================================
145
+ console.log('--- (AC6) Parte B: seção de leitura obrigatória ---');
146
+ {
147
+ const pod = tmp('b-pod');
148
+ const wfPath = makePod(pod, { knowledge: ['k2.md', 'k1.md'] }); // ordem DECLARADA invertida de propósito
149
+ const { result } = startOn(wfPath, 'b');
150
+ const brief = result.brief;
151
+ const iSec = brief.indexOf('## Leitura obrigatória ANTES de agir');
152
+ const iVeto = brief.indexOf('## Veto');
153
+ ok(iSec !== -1, 'AC6-i: seção "Leitura obrigatória ANTES de agir" presente');
154
+ ok(iVeto !== -1 && iSec < iVeto, `AC6: seção renderizada ANTES de "## Veto" (indexOf ${iSec} < ${iVeto})`);
155
+ const agentAbs = path.join(pod, 'agents', 'solo.md');
156
+ const k2Abs = path.join(pod, 'knowledge', 'k2.md');
157
+ const k1Abs = path.join(pod, 'knowledge', 'k1.md');
158
+ ok(brief.includes(`- ${agentAbs}`), 'AC6-i: path ABSOLUTO do agente da etapa no brief');
159
+ ok(brief.includes(`- ${k2Abs}`) && brief.includes(`- ${k1Abs}`), 'AC6-ii: paths absolutos de TODOS os knowledge da etapa');
160
+ ok(brief.indexOf(agentAbs) < brief.indexOf(k2Abs) && brief.indexOf(k2Abs) < brief.indexOf(k1Abs),
161
+ 'AC6-ii: ORDEM = agente, depois knowledge na ordem DECLARADA no wf (k2 antes de k1)');
162
+ ok(/HALTE e reporte/.test(brief), 'AC6-iii: instrução de HALT se algum arquivo não abrir');
163
+ ok(/verificação POSTERIOR do runner|não é a especificação da tarefa/.test(brief),
164
+ 'AC6-iv: `## Veto` rotulado como verificação POSTERIOR, não especificação da tarefa');
165
+ ok(/existe em disco/.test(brief), 'AC6: o brief declara que a existência foi verificada pelo runner');
166
+ }
167
+
168
+ // ===========================================================================
169
+ // (AC7) Gate de existência MORDE — agente ausente e knowledge ausente
170
+ // ===========================================================================
171
+ console.log('--- (AC7) gate fail-closed de existência ---');
172
+ /**
173
+ * [SF11.2 — interação registrada, não escondida] Para KNOWLEDGE existem DOIS gates fail-closed
174
+ * em série, e o mais precoce vence no caminho normal: `startRun`/`openRun` chamam
175
+ * `loadWorkflow` → `validateWorkflow`, que já reprova a referência morta (paridade estática,
176
+ * `DF-SF11.2-4` item 2) ANTES de `advance` rodar. O gate do despacho (`blocked`) segue
177
+ * ALCANÇÁVEL e é o que sustenta o invariante quando o arquivo some DEPOIS do preflight
178
+ * (deleção/rename no meio do run, pod-root remontado) — provado logo abaixo com `resetStep`,
179
+ * o mesmo caminho que `revise` usa para devolver uma etapa a `pending`.
180
+ */
181
+ function advanceComLeituraQuebrada(label, quebrar, opts) {
182
+ const pod = tmp(label);
183
+ const wfPath = makePod(pod, opts);
184
+ const { ctx } = startOn(wfPath, label);
185
+ rp.resetStep(ctx, 'solo'); // etapa volta a `pending` (mesmo mecanismo de `revise`)
186
+ quebrar(pod, ctx);
187
+ return { pod, ctx, result: rp.advance(ctx) };
188
+ }
189
+ {
190
+ const { result } = advanceComLeituraQuebrada('k-missing', (pod) => fs.rmSync(path.join(pod, 'knowledge', 'k2.md')));
191
+ ok(result.action === 'blocked', `AC7: knowledge ausente BLOQUEIA o despacho (action=${result.action})`);
192
+ ok(result.reason === 'leitura_obrigatoria_ausente', `AC7: reason="leitura_obrigatoria_ausente" (veio "${result.reason}")`);
193
+ ok(result.brief === undefined, 'AC7: NENHUM brief é emitido quando há referência morta');
194
+ ok((result.failures || []).some((f) => f.includes('knowledge/k2.md')), 'AC7: a falha NOMEIA o path ausente');
195
+ }
196
+ {
197
+ const { result } = advanceComLeituraQuebrada('a-missing', (pod) => fs.rmSync(path.join(pod, 'agents', 'solo.md')));
198
+ ok(result.action === 'blocked' && result.reason === 'leitura_obrigatoria_ausente',
199
+ 'AC7: agente ausente BLOQUEIA com a MESMA razão (paridade com pod-export-compile.cjs:214)');
200
+ ok(result.brief === undefined && (result.failures || []).some((f) => f.includes('agents/solo.md')),
201
+ 'AC7: sem brief, e a falha nomeia `agents/solo.md`');
202
+ }
203
+ {
204
+ // O gate ESTÁTICO em série: com knowledge morto, o run nem começa (fail-closed mais cedo).
205
+ const pod = tmp('k-preflight');
206
+ const wfPath = makePod(pod);
207
+ fs.rmSync(path.join(pod, 'knowledge', 'k2.md'));
208
+ let msg = '';
209
+ try { startOn(wfPath, 'kp'); } catch (e) { msg = e.message; }
210
+ ok(/knowledge declarado inexistente em disco/.test(msg) && /knowledge\/k2\.md/.test(msg),
211
+ 'AC7/AC8: o preflight do runner (loadWorkflow→validateWorkflow) já recusa ABRIR o run com knowledge morto');
212
+ }
213
+ {
214
+ // Agente ausente NÃO é pego pelo estático (decisão registrada) — o run abre e o DESPACHO bloqueia.
215
+ const pod = tmp('a-preflight');
216
+ const wfPath = makePod(pod);
217
+ fs.rmSync(path.join(pod, 'agents', 'solo.md'));
218
+ const { result } = startOn(wfPath, 'ap');
219
+ ok(result.action === 'blocked' && result.reason === 'leitura_obrigatoria_ausente',
220
+ 'AC7: no caminho NORMAL (start), agente ausente bloqueia no despacho — gate alcançável de ponta a ponta');
221
+ }
222
+ {
223
+ // Invariante fechado no ponto de emissão: nenhum OUTRO chamador consegue um brief com
224
+ // referência morta — `buildBrief` lança em vez de renderizar path inexistente.
225
+ const pod = tmp('direct');
226
+ const wfPath = makePod(pod);
227
+ const { ctx } = startOn(wfPath, 'd');
228
+ fs.rmSync(path.join(pod, 'knowledge', 'k1.md'));
229
+ let lancou = false;
230
+ try { rp.buildBrief(ctx, 'solo'); } catch (e) { lancou = /leitura_obrigatoria_ausente/.test(e.message); }
231
+ ok(lancou, 'AC7: `buildBrief` chamado DIRETO com referência morta LANÇA (invariante fechado no emissor)');
232
+ }
233
+ {
234
+ // Confinamento: `knowledge` que escapa da raiz do pod nunca vira linha do brief — bloqueia
235
+ // no runner, e o validador estático recusa o mesmo wf pela mesma régua.
236
+ // Defesa em profundidade: o preflight estático já recusa esse wf (provado logo abaixo), então
237
+ // o caminho do runner só é alcançável com o doc já carregado — é exatamente o que se quer
238
+ // provar aqui: o resolver NUNCA emite path fora da raiz, mesmo se o estático for contornado.
239
+ const { result } = advanceComLeituraQuebrada('escape', (_pod, ctx) => {
240
+ rp.stepById(ctx.doc, 'solo').knowledge = ['../../etc/passwd'];
241
+ });
242
+ ok(result.action === 'blocked' && result.reason === 'leitura_obrigatoria_ausente',
243
+ 'AC16-vi: path que ESCAPA da raiz do pod bloqueia (fail-closed), nunca vira linha do brief');
244
+ ok((result.failures || []).some((f) => /escapa da raiz do pod/.test(f)),
245
+ 'AC16-vi: a falha diz explicitamente que o path escapa da raiz do pod');
246
+ const podEsc = tmp('escape-static');
247
+ const wfEsc = makePod(podEsc, { knowledge: ['k1.md'] });
248
+ fs.writeFileSync(wfEsc, fs.readFileSync(wfEsc, 'utf8').replace('knowledge: [k1.md]', 'knowledge: ["../../etc/passwd"]'));
249
+ ok(throwsFail(() => vp.validateWorkflow(wfEsc, undefined)),
250
+ 'AC16-vi: o validador estático recusa o MESMO wf (mesma régua de confinamento)');
251
+ }
252
+
253
+ // ===========================================================================
254
+ // (AC8) Paridade estática no validador + varredura de não-regressão
255
+ // ===========================================================================
256
+ console.log('--- (AC8) paridade estática em validate-pod-workflow.cjs ---');
257
+ {
258
+ const pod = tmp('static');
259
+ const wfPath = makePod(pod);
260
+ ok(!throwsFail(() => vp.validateWorkflow(wfPath, undefined)), 'AC8: wf com knowledge existente valida (exit 0)');
261
+ fs.rmSync(path.join(pod, 'knowledge', 'k1.md'));
262
+ ok(throwsFail(() => vp.validateWorkflow(wfPath, undefined)), 'AC8: knowledge declarado inexistente REPROVA a validação');
263
+ const pod2 = tmp('static-noknow');
264
+ const wf2 = makePod(pod2, { knowledge: null });
265
+ ok(!throwsFail(() => vp.validateWorkflow(wf2, undefined)), 'AC8/AC9: wf SEM `knowledge:` valida (campo opcional, no-op)');
266
+ // [SF11.2 — decisão registrada] a EXISTÊNCIA DO AGENTE não é checada aqui de propósito:
267
+ // `forge-generate.cjs` emite o wf antes de os agentes serem autorados. O gate de agente
268
+ // vive no DESPACHO (provado no AC7 acima) e no export (`pod-export-compile.cjs:214`).
269
+ const pod3 = tmp('static-noagent');
270
+ const wf3 = makePod(pod3);
271
+ fs.rmSync(path.join(pod3, 'agents', 'solo.md'));
272
+ ok(!throwsFail(() => vp.validateWorkflow(wf3, undefined)),
273
+ 'AC8: agente ausente NÃO reprova o validador estático (wf recém-gerado pela fábrica é estado legítimo) — o gate é no despacho');
274
+ }
275
+ {
276
+ // Varredura de não-regressão: TODOS os wf reais dos dois repos + fixtures.
277
+ function collect(dir) {
278
+ const out = []; const st = [dir];
279
+ while (st.length) {
280
+ const d = st.pop();
281
+ if (!fs.existsSync(d)) continue;
282
+ for (const e of fs.readdirSync(d, { withFileTypes: true })) {
283
+ const p = path.join(d, e.name);
284
+ if (e.isDirectory()) st.push(p);
285
+ else if (path.basename(d) === 'workflows' && /^wf-.*\.ya?ml$/.test(e.name)) out.push(p);
286
+ }
287
+ }
288
+ return out.sort();
289
+ }
290
+ const roots = [
291
+ path.join(REPO_ROOT, 'template', 'pods'),
292
+ path.join(REPO_ROOT, 'spike'),
293
+ path.join(DEV_ROOT, 'second-brain', 'pods'), // instalação real, quando presente ao lado
294
+ ];
295
+ let total = 0; let mortas = 0;
296
+ for (const r of roots) {
297
+ for (const wf of collect(r)) {
298
+ const doc = vp.parseYaml(fs.readFileSync(wf, 'utf8'));
299
+ const podRoot = vp.podRootFor(wf);
300
+ for (const s of (doc.steps || [])) {
301
+ if (!Array.isArray(s.knowledge)) continue;
302
+ for (const k of s.knowledge) {
303
+ total++;
304
+ if (!fs.existsSync(path.join(podRoot, 'knowledge', String(k)))) {
305
+ mortas++; console.error(` referência morta: ${wf} :: ${s.id} :: knowledge/${k}`);
306
+ }
307
+ }
308
+ }
309
+ }
310
+ }
311
+ ok(mortas === 0, `AC8: varredura dos wf reais — 0 referências de knowledge mortas (${total} referência(s) checada(s))`);
312
+ }
313
+
314
+ // ===========================================================================
315
+ // (AC9) Retrocompat multi-pod — wf SEM `knowledge:` lista só o agente
316
+ // ===========================================================================
317
+ console.log('--- (AC9) retrocompat: etapa sem `knowledge:` ---');
318
+ {
319
+ const pod = tmp('retro');
320
+ const wfPath = makePod(pod, { knowledge: null });
321
+ const { result } = startOn(wfPath, 'r');
322
+ ok(result.action === 'dispatch', 'AC9: etapa sem `knowledge:` despacha normalmente (sem erro, sem bloqueio)');
323
+ const linhas = result.brief.split('\n').filter((l) => l.startsWith('- /'));
324
+ ok(linhas.length === 1 && linhas[0].endsWith(path.join('agents', 'solo.md')),
325
+ `AC9: a seção lista SÓ o arquivo do agente (${linhas.length} linha(s))`);
326
+ ok(!/knowledge\//.test(result.brief), 'AC9: nenhuma menção a knowledge/ no brief da etapa sem o campo');
327
+ }
328
+ {
329
+ // Genérico por construção: o helper não conhece pod nenhum.
330
+ const src = fs.readFileSync(path.join(__dirname, 'pc-run-pod.cjs'), 'utf8');
331
+ const fn = src.slice(src.indexOf('function requiredReading(ctx, step)'), src.indexOf('function requiredReadingBriefLines'));
332
+ ok(!/hn-|carousel|hormozi|squad-forge|content-/.test(fn),
333
+ 'AC9/B5: `requiredReading` não cita nome de pod nem de arquivo específico (derivado do schema)');
334
+ }
335
+
336
+ // ===========================================================================
337
+ // Custo medido (AC4/AC12) — o desenho REDUZ o brief; a seção nova é barata
338
+ // ===========================================================================
339
+ console.log('--- (AC4/AC12) custo do brief ---');
340
+ {
341
+ const pod = tmp('cost');
342
+ const wfPath = makePod(pod);
343
+ const { ctx, result } = startOn(wfPath, 'c');
344
+ const step = rp.stepById(ctx.doc, 'solo');
345
+ const rr = rp.requiredReading(ctx, step);
346
+ const secao = rp.requiredReadingBriefLines(rr.files).join('\n');
347
+ const assertions = step.veto.assertions;
348
+ const cru = assertions.map((a) => JSON.stringify(a)).join('\n').length;
349
+ const podado = assertions.map((a) => JSON.stringify(rp.vetoAssertionForBrief(a))).join('\n').length;
350
+ ok(podado < cru, `AC4: o render de veto encolhe (${cru} B → ${podado} B)`);
351
+ ok(secao.length > 0 && result.brief.includes(secao.split('\n')[0]),
352
+ `AC12: a seção de leitura obrigatória custa ${Buffer.byteLength(secao, 'utf8')} B (referência, não conteúdo)`);
353
+ }
354
+
355
+ // ---------------------------------------------------------------------------
356
+ for (const d of tmpdirs) { try { fs.rmSync(d, { recursive: true, force: true }); } catch { /* ok */ } }
357
+
358
+ console.log(`\n${passed}/${passed + failures.length} verificações passaram.`);
359
+ if (failures.length > 0) {
360
+ console.error('\nSF11.2 (leitura obrigatória / veto sem cola) FALHOU:');
361
+ for (const f of failures) console.error(` - ${f}`);
362
+ process.exit(1);
363
+ }
364
+ console.log('\n[SUCESSO] SF11.2: Parte A (veto sem `_sources`) + Parte B (leitura obrigatória verificada).');