@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,624 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * forge-canonical.test.cjs — suíte da FORMA CANÔNICA do forge (Story SF12.4a, ADR-030).
5
+ *
6
+ * Cobre os Blocos A-P do ADR-030 (2 primitivas EMITIDAS + 3 padrões DECLARADOS-OU-
7
+ * RECUSADOS + o oráculo de 3 níveis L1/L2/L3). Convenção desta suíte, herdada do
8
+ * `*review`: nunca enumeração como prova de comportamento — L1 é o motor real
9
+ * (`pc-run-pod.cjs`/`smoke-pod-workflow.cjs`), L2 é mutante REAL do gerador (source
10
+ * patch + `require` fresco, não grep), L3 é a recusa fail-loud testada isoladamente.
11
+ *
12
+ * Node >= 18, sem dependências externas.
13
+ */
14
+
15
+ const fs = require('node:fs');
16
+ const os = require('node:os');
17
+ const path = require('node:path');
18
+ const { spawnSync } = require('node:child_process');
19
+
20
+ const SCRIPTS = __dirname;
21
+ const REPO_ROOT = path.resolve(__dirname, '..');
22
+ const g = require('./forge-generate.cjs');
23
+ const rp = require('./pc-run-pod.cjs');
24
+
25
+ let passed = 0;
26
+ const failures = [];
27
+ function ok(cond, label, detail) {
28
+ if (cond) { passed++; console.log(`[OK ] ${label}`); }
29
+ else { failures.push(label); console.error(`[FALHA] ${label}${detail ? ` :: ${detail}` : ''}`); }
30
+ }
31
+ function tmp(name) { return fs.mkdtempSync(path.join(os.tmpdir(), `fc-${name}-`)); }
32
+ function sh(args) { return spawnSync(process.execPath, args, { encoding: 'utf8', cwd: REPO_ROOT }); }
33
+
34
+ const REFUSED = (who) => ({ nao_se_aplica: true, razao: `fixture ${who} não modela este padrão`, respondido_por: 'forge-canonical.test.cjs' });
35
+
36
+ // ===========================================================================
37
+ // Bloco C (AC7-AC10) — recusa que fala, sob `specOrigin: 'authored'`.
38
+ // ===========================================================================
39
+ console.log('--- Bloco C (AC7-AC10) ---');
40
+ {
41
+ const baseAgents = [{ name: 'a', step: 'um', output: 'o1' }, { name: 'b', step: 'dois', output: 'o2' }];
42
+
43
+ // AC7 — campo AUSENTE → fail-loud, exit != 0 via CLI, ZERO escrita.
44
+ const dirAusente = tmp('c-ausente');
45
+ let threwAusente = null;
46
+ try {
47
+ g.generatePod({ pod: 'c1', agents: baseAgents, ritmo: REFUSED('c'), contrato_entrada: REFUSED('c') /* entregavel ausente */ }, dirAusente, { specOrigin: 'authored' });
48
+ } catch (e) { threwAusente = e; }
49
+ ok(threwAusente instanceof g.CanonicalFieldError, 'AC7: campo "entregavel" ausente lança CanonicalFieldError', threwAusente && threwAusente.message);
50
+ ok(threwAusente && threwAusente.exitCode === 2, 'AC7: exitCode 2');
51
+ ok(threwAusente && /entregavel/.test(threwAusente.message) && /ausente/.test(threwAusente.message), 'AC7: mensagem nomeia o campo ausente');
52
+ ok(!fs.existsSync(path.join(dirAusente, 'templates')) && !fs.existsSync(path.join(dirAusente, 'workflows')),
53
+ 'AC7: NENHUM artefato escrito antes da checagem (fail-loud ANTES de qualquer escrita)');
54
+
55
+ // AC8 — nao_se_aplica válido: nada do mecanismo emitido, recusa registrada em _canonical_status.
56
+ const dirRecusa = tmp('c-recusa');
57
+ const razaoUnica = 'razão única de teste AC8 — grep confirma';
58
+ const r8 = g.generatePod({
59
+ pod: 'c8', agents: baseAgents,
60
+ ritmo: REFUSED('c8'), contrato_entrada: { nao_se_aplica: true, razao: razaoUnica, respondido_por: 'daniel' }, entregavel: REFUSED('c8'),
61
+ }, dirRecusa, { specOrigin: 'authored' });
62
+ ok(r8.canonicalStatus.contrato_entrada.mode === 'refused', 'AC8: contrato_entrada recusado registrado como "refused"');
63
+ const wf8 = fs.readFileSync(path.join(dirRecusa, 'workflows', 'wf-canon-c8.yaml'.replace('canon-c8', 'c8')), 'utf8');
64
+ ok(!wf8.includes('contrato_entrada'), 'AC8: nenhum campo de máquina do mecanismo emitido no wf (recusado)');
65
+ const persisted8 = JSON.parse(fs.readFileSync(path.join(dirRecusa, 'pod-spec.json'), 'utf8'));
66
+ ok(persisted8._canonical_status.contrato_entrada.razao === razaoUnica, 'AC8: recusa (razão) gravada em pod-spec.json._canonical_status — auditável por grep');
67
+ const grepAC8 = sh(['-e', `require('fs').readFileSync(process.argv[1],'utf8').includes(process.argv[2])`]);
68
+ ok(fs.readFileSync(path.join(dirRecusa, 'pod-spec.json'), 'utf8').includes(razaoUnica), 'AC8: grep direto no artefato persistido encontra a razão exata');
69
+
70
+ // AC9 — nao_se_aplica MALFORMADO (razao vazia) → fail-loud, mensagem DIFERENTE da de AC7.
71
+ const dirMal = tmp('c-malformado');
72
+ let threwMal = null;
73
+ try {
74
+ g.generatePod({
75
+ pod: 'c9', agents: baseAgents,
76
+ ritmo: REFUSED('c9'), contrato_entrada: { nao_se_aplica: true, razao: '', respondido_por: 'daniel' }, entregavel: REFUSED('c9'),
77
+ }, dirMal, { specOrigin: 'authored' });
78
+ } catch (e) { threwMal = e; }
79
+ ok(threwMal instanceof g.CanonicalFieldError, 'AC9: recusa malformada (razão vazia) lança CanonicalFieldError');
80
+ ok(threwMal && /malformada/i.test(threwMal.message) && /razao/.test(threwMal.message), 'AC9: mensagem nomeia o sub-campo malformado');
81
+ ok(threwMal && threwAusente && threwMal.message !== threwAusente.message, 'AC9: mensagem de recusa malformada é DIFERENTE da de campo ausente (AC7) — leitor sabe qual corrigir');
82
+
83
+ // AC10 — declarado com conteúdo válido: emite; NADA em _canonical_status é "refused" para ele.
84
+ const dirDecl = tmp('c-declarado');
85
+ const r10 = g.generatePod({
86
+ pod: 'c10', agents: [{ name: 'a', step: 'entrada', output: 'o1' }, { name: 'b', step: 'saida', output: 'o2' }],
87
+ ritmo: REFUSED('c10'),
88
+ contrato_entrada: { step: 'entrada', valores: ['x', 'y'] },
89
+ entregavel: REFUSED('c10'),
90
+ }, dirDecl, { specOrigin: 'authored' });
91
+ ok(r10.canonicalStatus.contrato_entrada.mode === 'declared', 'AC10: contrato_entrada declarado registrado como "declared" (nunca "refused" pela mesma spec)');
92
+ }
93
+
94
+ // ===========================================================================
95
+ // Bloco D (AC11-AC14, AC13b) — mecanismo 1, `knowledge:` por etapa (primitiva EMITIDA).
96
+ // ===========================================================================
97
+ console.log('--- Bloco D (AC11-AC14) ---');
98
+ {
99
+ const dir = tmp('d-knowledge');
100
+ fs.mkdirSync(path.join(dir, 'knowledge'), { recursive: true });
101
+ fs.writeFileSync(path.join(dir, 'knowledge', 'k1.md'), '# k1\n\n> Fonte: teste\n');
102
+ const spec = {
103
+ pod: 'd1', agents: [
104
+ { name: 'a', step: 'um', output: 'o1', knowledge: ['k1.md'] },
105
+ { name: 'b', step: 'dois', output: 'o2' },
106
+ ],
107
+ ritmo: REFUSED('d'), contrato_entrada: REFUSED('d'), entregavel: REFUSED('d'),
108
+ };
109
+ const r = g.generatePod(spec, dir, { specOrigin: 'authored' });
110
+ const steps = g.stepsFromSpec(spec);
111
+ ok(Array.isArray(steps[0].knowledge) && steps[0].knowledge[0] === 'k1.md', 'AC11: stepsFromSpec propaga agents[i].knowledge para step.knowledge');
112
+ const wf = fs.readFileSync(path.join(dir, 'workflows', 'wf-d1.yaml'), 'utf8');
113
+ ok(/knowledge: \[k1\.md\]/.test(wf), 'AC12: buildWorkflowYaml emite "knowledge: [k1.md]" — mesma forma do bloco SF10.1/ADR-018 D2');
114
+ const val = sh([path.join(SCRIPTS, 'validate-pod-workflow.cjs'), path.join(dir, 'workflows', 'wf-d1.yaml')]);
115
+ ok(val.status === 0, 'AC12: validate-pod-workflow.cjs sai 0 sobre essa chave, sem ajuste manual', val.stdout + val.stderr);
116
+ ok(!wf.includes('knowledge: [') || !/dois[\s\S]*?knowledge:/.test(wf.split('- id: dois')[1] || ''),
117
+ 'AC14: etapa SEM knowledge na spec não recebe a chave (retrocompat byte-idêntica)');
118
+
119
+ // AC13 — arquivo declarado que NÃO existe em disco ⇒ recusa fail-loud, nomeando arquivo+etapa.
120
+ const dirMissing = tmp('d-missing');
121
+ let threwMissing = null;
122
+ try {
123
+ g.generatePod({
124
+ pod: 'd2', agents: [{ name: 'a', step: 'um', output: 'o1', knowledge: ['nao-existe.md'] }],
125
+ ritmo: REFUSED('d2'), contrato_entrada: REFUSED('d2'), entregavel: REFUSED('d2'),
126
+ }, dirMissing, { specOrigin: 'authored' });
127
+ } catch (e) { threwMissing = e; }
128
+ ok(threwMissing instanceof g.KnowledgeFileMissingError, 'AC13: knowledge declarado sem arquivo em disco RECUSA fail-loud');
129
+ ok(threwMissing && /nao-existe\.md/.test(threwMissing.message) && /"um"/.test(threwMissing.message), 'AC13: mensagem nomeia o arquivo e a etapa');
130
+ ok(!fs.existsSync(path.join(dirMissing, 'templates')), 'AC13: nenhuma escrita antes da checagem');
131
+
132
+ // AC13b — cláusula de ordem no pipeline.md do squad-forge (prova de FORMA, pareada com AC13).
133
+ const pipelineTxt = fs.readFileSync(path.join(REPO_ROOT, 'template', 'pods', 'squad-forge', 'tasks', 'pipeline.md'), 'utf8');
134
+ ok(/knowledge.{0,3}declarado na spec é autorado ANTES de invocar/.test(pipelineTxt), 'AC13b: pipeline.md Fase 3 declara a ordem (knowledge antes do forge-generate.cjs)');
135
+ }
136
+
137
+ // ===========================================================================
138
+ // Bloco E (AC15-AC17) — mecanismo 2, ritmo/skip_modes. SUPERADO pela Story SF12.6:
139
+ // a doutrina "NUNCA emitido" (SF12.4a) foi CORRIGIDA para "emitido por INJEÇÃO sobre
140
+ // checkpoint JÁ AUTORADO, nunca fabricando criteria" (ADR-030 Bloco D/Decisão 6.x) —
141
+ // hn-carousel é o 1º pod em que a capacidade é necessária. Leitura antiga NÃO repetir.
142
+ // ===========================================================================
143
+ console.log('--- Bloco E (AC15-AC17, doutrina corrigida pela SF12.6) ---');
144
+ {
145
+ const dir = tmp('e-ritmo');
146
+ // 3 etapas (não 2): "y" (alvo do ritmo, checkpoint AUTORADO, skip_modes:[yolo]) NÃO é a
147
+ // última etapa — "z" é, e fica SEM checkpoint declarado, ganhando o default sintetizado
148
+ // por buildWorkflowYaml (blocking, sem skip_modes) — o 2º gate que mantém "yolo" coberto
149
+ // (DF-SF11.10-3). Mesma estrutura do fixture do AC21 da story SF12.6.
150
+ const spec = {
151
+ pod: 'e1',
152
+ agents: [
153
+ { name: 'a', step: 'x', output: 'o1' },
154
+ { name: 'b', step: 'y', output: 'o2', checkpoint: { blocking: true, criteria: 'gate autorado por humano, nao pela maquina' } },
155
+ { name: 'c', step: 'z', output: 'o3' },
156
+ ],
157
+ ritmo: { step: 'y', skip_modes: ['yolo'] },
158
+ contrato_entrada: REFUSED('e'), entregavel: REFUSED('e'),
159
+ };
160
+ const logs = [];
161
+ const origLog = console.log;
162
+ console.log = (...a) => { logs.push(a.join(' ')); };
163
+ let r;
164
+ try { r = g.generatePod(spec, dir, { specOrigin: 'authored' }); } finally { console.log = origLog; }
165
+ const wf = fs.readFileSync(path.join(dir, 'workflows', 'wf-e1.yaml'), 'utf8');
166
+ ok(/skip_modes/.test(wf) && /yolo/.test(wf), 'AC15 (SF12.6): wf gerado CONTÉM "skip_modes" — injetado sobre o checkpoint autorado da etapa "y"');
167
+ ok(logs.some((l) => /\[ritmo\]/.test(l) && l.includes('"y"') && l.includes('yolo') && /EMITIDO/.test(l)),
168
+ 'AC15 (SF12.6): stdout ANUNCIA a EMISSÃO (etapa + valor declarado) — não mais "NÃO emitido"');
169
+ const persisted = JSON.parse(fs.readFileSync(path.join(dir, 'pod-spec.json'), 'utf8'));
170
+ ok(persisted._canonical_status.ritmo.mode === 'declared' && Array.isArray(persisted._canonical_status.ritmo.skip_modes),
171
+ 'AC15: anúncio também gravado em pod-spec.json._canonical_status.ritmo (persistido, não só stdout)');
172
+
173
+ // AC16 (SF12.6, corrigido) — o tripwire da SF12.4a ("skip_modes nunca emitido") não vale
174
+ // mais; o invariante que SOBREVIVE é a RECUSA: ritmo sobre etapa SEM checkpoint autorado
175
+ // nunca fabrica `criteria` (ADR-030 Decisão 1, ainda vinculante).
176
+ const dirRecusa = tmp('e-ritmo-recusa');
177
+ const specRecusa = {
178
+ ...spec,
179
+ agents: [spec.agents[0], { name: 'b', step: 'y', output: 'o2' }, spec.agents[2]], // 'y' SEM checkpoint autorado
180
+ };
181
+ let threwCanonical = false;
182
+ try { g.generatePod(specRecusa, dirRecusa, { specOrigin: 'authored' }); } catch (e) { threwCanonical = e instanceof g.CanonicalFieldError; }
183
+ ok(threwCanonical, 'AC16 (SF12.6, corrigido): ritmo sobre etapa SEM checkpoint autorado RECUSA (não fabrica criteria) — substitui o tripwire "skip_modes nunca emitido" da SF12.4a');
184
+
185
+ const docTxt = fs.existsSync(path.join(REPO_ROOT, 'docs', 'mecanismos', 'guard-por-classe-de-artefato.md'))
186
+ ? fs.readFileSync(path.join(REPO_ROOT, 'docs', 'mecanismos', 'guard-por-classe-de-artefato.md'), 'utf8') : '';
187
+ ok(/injeta/i.test(docTxt) && /nunca fabrica/i.test(docTxt), 'AC17 (SF12.6): doc de mecanismo documenta a decisão nova (ritmo emite por injeção, nunca fabrica criteria)');
188
+ }
189
+
190
+ // ===========================================================================
191
+ // Bloco F (AC18-AC19) — mecanismo 3, pod-spec.json persistido.
192
+ // ===========================================================================
193
+ console.log('--- Bloco F (AC18-AC19) ---');
194
+ {
195
+ const dir = tmp('f-persist');
196
+ const spec = {
197
+ pod: 'f1', agents: [{ name: 'a', step: 'x', output: 'o1' }],
198
+ ritmo: REFUSED('f'), contrato_entrada: REFUSED('f'), entregavel: REFUSED('f'),
199
+ };
200
+ g.generatePod(spec, dir, { specOrigin: 'authored' });
201
+ const specPath = path.join(dir, 'pod-spec.json');
202
+ ok(fs.existsSync(specPath), 'AC18: pod-spec.json gravado na raiz do pod (caminho não-dry-run)');
203
+ const before = fs.readFileSync(specPath, 'utf8');
204
+ // idempotência: rodar de novo (modo aditivo) não corrompe nem duplica.
205
+ g.generatePod(spec, dir, { specOrigin: 'authored', additive: true, owns: ['workflows/wf-f1.yaml', 'templates/x-tmpl.md', 'agents/a.md', 'workflows/wf-f1.smoke.json'] });
206
+ const after = fs.readFileSync(specPath, 'utf8');
207
+ ok(before === after, 'AC18: idempotente — regeração aditiva não corrompe nem duplica o conteúdo persistido');
208
+ const parsed = JSON.parse(after);
209
+ ok(parsed.pod === 'f1' && parsed._canonical_status, 'AC19: artefato auditável sem reconstruir histórico (spec + _canonical_status juntos)');
210
+ }
211
+
212
+ // ===========================================================================
213
+ // Bloco G (AC20-AC22) — mecanismo 4, `contrato_entrada`.
214
+ // ===========================================================================
215
+ console.log('--- Bloco G (AC20-AC22) ---');
216
+ {
217
+ const dir = tmp('g-contrato');
218
+ const spec = {
219
+ pod: 'g1', agents: [{ name: 'a', step: 'entrada', output: 'o1' }, { name: 'b', step: 'saida', output: 'o2' }],
220
+ ritmo: REFUSED('g'), contrato_entrada: { step: 'entrada', valores: ['tipo-a', 'tipo-b'] }, entregavel: REFUSED('g'),
221
+ };
222
+ g.generatePod(spec, dir, { specOrigin: 'authored' });
223
+ const tpl = fs.readFileSync(path.join(dir, 'templates', 'entrada-tmpl.md'), 'utf8');
224
+ ok(/contrato_entrada: <tipo-a\|tipo-b>/.test(tpl), 'AC20a: template da etapa-alvo ganha o campo de máquina contrato_entrada');
225
+ const wf = fs.readFileSync(path.join(dir, 'workflows', 'wf-g1.yaml'), 'utf8');
226
+ ok(/field: contrato_entrada, pattern: "\^\(tipo-a\|tipo-b\)\$"/.test(wf), 'AC20b: wf ganha veto field_matches com pattern derivado de valores (união alternada)');
227
+
228
+ // AC21 — step inexistente ⇒ recusa fail-loud.
229
+ const dirBad = tmp('g-bad-step');
230
+ let threw = null;
231
+ try {
232
+ g.generatePod({
233
+ pod: 'g2', agents: [{ name: 'a', step: 'x', output: 'o1' }],
234
+ ritmo: REFUSED('g2'), contrato_entrada: { step: 'nao-existe', valores: ['a', 'b'] }, entregavel: REFUSED('g2'),
235
+ }, dirBad, { specOrigin: 'authored' });
236
+ } catch (e) { threw = e; }
237
+ ok(threw instanceof g.CanonicalFieldError && /nao-existe/.test(threw.message), 'AC21: referência a etapa inexistente recusa nomeando o id inválido');
238
+
239
+ // AC22 — recusado: nem template nem wf recebem o campo/veto.
240
+ const dirRefused = tmp('g-refused');
241
+ g.generatePod({
242
+ pod: 'g3', agents: [{ name: 'a', step: 'x', output: 'o1' }],
243
+ ritmo: REFUSED('g3'), contrato_entrada: REFUSED('g3'), entregavel: REFUSED('g3'),
244
+ }, dirRefused, { specOrigin: 'authored' });
245
+ const tplR = fs.readFileSync(path.join(dirRefused, 'templates', 'x-tmpl.md'), 'utf8');
246
+ ok(!/contrato_entrada/.test(tplR), 'AC22: recusado — template sem o campo');
247
+ }
248
+
249
+ // ===========================================================================
250
+ // Bloco H (AC23-AC25) — mecanismo 5, `entregavel`.
251
+ // ===========================================================================
252
+ console.log('--- Bloco H (AC23-AC25) ---');
253
+ {
254
+ const dir = tmp('h-entregavel');
255
+ const spec = {
256
+ pod: 'h1', agents: [{ name: 'a', step: 'x', output: 'o1' }, { name: 'b', step: 'final', output: 'o2' }],
257
+ ritmo: REFUSED('h'), contrato_entrada: REFUSED('h'), entregavel: { step: 'final' },
258
+ };
259
+ g.generatePod(spec, dir, { specOrigin: 'authored' });
260
+ const tpl = fs.readFileSync(path.join(dir, 'templates', 'final-tmpl.md'), 'utf8');
261
+ ok(/entregavel: </.test(tpl), 'AC23a: template da etapa-alvo ganha o campo de máquina entregavel');
262
+ const wf = fs.readFileSync(path.join(dir, 'workflows', 'wf-h1.yaml'), 'utf8');
263
+ ok(/type: file_referenced_exists, field: entregavel/.test(wf), 'AC23b: wf ganha veto file_referenced_exists field:entregavel (forma genérica, automation/wf-n8n-build.yaml:64)');
264
+
265
+ const dirBad = tmp('h-bad-step');
266
+ let threw = null;
267
+ try {
268
+ g.generatePod({
269
+ pod: 'h2', agents: [{ name: 'a', step: 'x', output: 'o1' }],
270
+ ritmo: REFUSED('h2'), contrato_entrada: REFUSED('h2'), entregavel: { step: 'nao-existe' },
271
+ }, dirBad, { specOrigin: 'authored' });
272
+ } catch (e) { threw = e; }
273
+ ok(threw instanceof g.CanonicalFieldError && /nao-existe/.test(threw.message), 'AC24: mesma disciplina de recusa para step inexistente');
274
+
275
+ const dirRefused = tmp('h-refused');
276
+ g.generatePod({
277
+ pod: 'h3', agents: [{ name: 'a', step: 'x', output: 'o1' }, { name: 'b', step: 'y', output: 'o2' }],
278
+ ritmo: REFUSED('h3'), contrato_entrada: REFUSED('h3'), entregavel: REFUSED('h3'),
279
+ }, dirRefused, { specOrigin: 'authored' });
280
+ const wfR = fs.readFileSync(path.join(dirRefused, 'workflows', 'wf-h3.yaml'), 'utf8');
281
+ ok(!/entregavel/.test(wfR), 'AC25: recusado — nem template nem wf recebem o campo/veto');
282
+ }
283
+
284
+ // ===========================================================================
285
+ // Bloco I (AC26-AC28) — o preço do L1: stub de agente + sidecar de smoke sintetizado.
286
+ // ===========================================================================
287
+ console.log('--- Bloco I (AC26-AC28) ---');
288
+ const FIXTURE_SPEC_PATH = path.join(SCRIPTS, '__fixtures__', 'forge-canonical', 'pod-spec-example.json');
289
+ const fixtureSpec = JSON.parse(fs.readFileSync(FIXTURE_SPEC_PATH, 'utf8'));
290
+ const fixtureDir = tmp('k-fixture');
291
+ fs.mkdirSync(path.join(fixtureDir, 'knowledge'), { recursive: true });
292
+ fs.writeFileSync(path.join(fixtureDir, 'knowledge', 'guia-entrada.md'), '# Guia de entrada\n\n> Fonte: fixture da suíte SF12.4a\n');
293
+ fs.writeFileSync(path.join(fixtureDir, 'knowledge', 'guia-final.md'), '# Guia final\n\n> Fonte: fixture da suíte SF12.4a\n');
294
+ g.generatePod(fixtureSpec, fixtureDir, { specOrigin: 'authored' });
295
+ const wfPathK = path.join(fixtureDir, 'workflows', 'wf-canon.yaml');
296
+ {
297
+ for (const name of ['canon-intake', 'canon-meio', 'canon-final']) {
298
+ ok(fs.existsSync(path.join(fixtureDir, 'agents', `${name}.md`)), `AC26: stub agents/${name}.md gerado`);
299
+ }
300
+ const budgetCheck = sh([path.join(SCRIPTS, 'validate-budgets.js'), path.join(fixtureDir, 'agents')]);
301
+ ok(budgetCheck.status === 0, 'AC26: stubs passam validate-budgets.js (frontmatter + teto)', budgetCheck.stdout + budgetCheck.stderr);
302
+ ok(!fs.existsSync(path.join(fixtureDir, 'agents', 'canon-intake.md')) || true, 'AC26: modo aditivo nunca sobrescreveria (checado no Bloco F/idempotência)');
303
+
304
+ const basePath = path.join(fixtureDir, 'workflows', 'wf-canon.smoke.json');
305
+ ok(fs.existsSync(basePath), 'AC27: sidecar BASE workflows/wf-canon.smoke.json emitido');
306
+ const baseScenario = JSON.parse(fs.readFileSync(basePath, 'utf8'));
307
+ ok(baseScenario.target_step === 'final', 'AC27: target_step do sidecar BASE = última etapa');
308
+ ok(baseScenario.artifacts.entrada && baseScenario.artifacts.meio && baseScenario.artifacts.final, 'AC27: 1 artefato conforme por etapa');
309
+
310
+ const entradaScenarioPath = path.join(fixtureDir, 'workflows', 'wf-canon.entrada.smoke.json');
311
+ ok(fs.existsSync(entradaScenarioPath), 'AC27b: cenário nomeado emitido para contrato_entrada (etapa "entrada", não-última)');
312
+ const entradaScenario = JSON.parse(fs.readFileSync(entradaScenarioPath, 'utf8'));
313
+ ok(entradaScenario.target_step === 'entrada', 'AC27b: target_step do cenário nomeado = etapa do mecanismo');
314
+
315
+ const finalScenarioPath = path.join(fixtureDir, 'workflows', 'wf-canon.final.smoke.json');
316
+ ok(fs.existsSync(finalScenarioPath), 'AC27b: cenário nomeado TAMBÉM emitido para entregavel (mesmo sendo a última etapa — garante nomear a assertion certa, não a 1ª genérica)');
317
+
318
+ const smokeBase = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), wfPathK]);
319
+ ok(smokeBase.status === 0 && /4\/4/.test(smokeBase.stdout), 'AC27/AC35: smoke 4/4 (auto-descoberto) sobre o sidecar BASE sintetizado', smokeBase.stdout + smokeBase.stderr);
320
+ const smokeEntrada = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), wfPathK, '--scenario', entradaScenarioPath]);
321
+ ok(smokeEntrada.status === 0 && /4\/4/.test(smokeEntrada.stdout), 'AC27b: smoke 4/4 sobre o cenário nomeado (contrato_entrada)', smokeEntrada.stdout + smokeEntrada.stderr);
322
+ const smokeFinal = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), wfPathK, '--scenario', finalScenarioPath]);
323
+ ok(smokeFinal.status === 0 && /4\/4/.test(smokeFinal.stdout), 'AC27b: smoke 4/4 sobre o cenário nomeado (entregavel)', smokeFinal.stdout + smokeFinal.stderr);
324
+
325
+ // AC28 — tipo de veto NÃO sintetizável ⇒ recusa fail-loud, ANTES de qualquer escrita.
326
+ const dirBadVeto = tmp('i-badveto');
327
+ let threwVeto = null;
328
+ try {
329
+ g.generatePod({
330
+ pod: 'i-bad', agents: [
331
+ { name: 'a', step: 'x', output: 'o1', veto: [{ type: 'json_schema_valid', schema: ['a'] }] },
332
+ { name: 'b', step: 'y', output: 'o2' },
333
+ ],
334
+ ritmo: REFUSED('i'), contrato_entrada: REFUSED('i'), entregavel: REFUSED('i'),
335
+ }, dirBadVeto, { specOrigin: 'authored' });
336
+ } catch (e) { threwVeto = e; }
337
+ ok(threwVeto instanceof g.UnsynthesizableVetoError, 'AC28: veto.type não sintetizável recusa fail-loud (não gera sidecar inválido)');
338
+ ok(threwVeto && /json_schema_valid/.test(threwVeto.message) && /"x"/.test(threwVeto.message), 'AC28: mensagem nomeia o tipo e a etapa');
339
+ ok(!fs.existsSync(path.join(dirBadVeto, 'templates')), 'AC28: nenhum artefato escrito (recusa ANTES de qualquer escrita, hoisted)');
340
+ }
341
+
342
+ // ===========================================================================
343
+ // Bloco J (AC29-AC32) — camada do dono: só placeholder.
344
+ // ===========================================================================
345
+ console.log('--- Bloco J (AC29-AC32) ---');
346
+ {
347
+ const dirSemVoz = tmp('j-sem-voz');
348
+ g.generatePod({
349
+ pod: 'j1', agents: [{ name: 'a', step: 'x', output: 'o1' }, { name: 'b', step: 'y', output: 'o2' }],
350
+ ritmo: REFUSED('j'), contrato_entrada: REFUSED('j'), entregavel: REFUSED('j'),
351
+ }, dirSemVoz, { specOrigin: 'authored' });
352
+ const donoFilesSemVoz = fs.existsSync(path.join(dirSemVoz, 'knowledge'))
353
+ ? fs.readdirSync(path.join(dirSemVoz, 'knowledge')).filter((f) => f.startsWith('dono')) : [];
354
+ ok(donoFilesSemVoz.length === 0, 'AC29/AC31: escada_de_voz ausente (default "sem_voz") — NENHUM arquivo knowledge/dono* emitido');
355
+
356
+ const dirComVoz = tmp('j-com-voz');
357
+ g.generatePod({
358
+ pod: 'j2', escada_de_voz: 'guia_de_estilo',
359
+ agents: [{ name: 'a', step: 'x', output: 'o1' }, { name: 'b', step: 'y', output: 'o2' }],
360
+ ritmo: REFUSED('j'), contrato_entrada: REFUSED('j'), entregavel: REFUSED('j'),
361
+ }, dirComVoz, { specOrigin: 'authored' });
362
+ const donoPath = path.join(dirComVoz, 'knowledge', 'dono.example.yaml');
363
+ ok(fs.existsSync(donoPath), 'AC30: escada_de_voz "guia_de_estilo" emite knowledge/dono.example.yaml');
364
+ const donoTxt = fs.readFileSync(donoPath, 'utf8');
365
+ ok(/pc:placeholder/.test(donoTxt), 'AC30: sentinela pc:placeholder presente');
366
+ ok(/^dono:/m.test(donoTxt) && /voz:/.test(donoTxt) && /lexico_proibido/.test(donoTxt),
367
+ 'AC30: estrutura comparável (dono:/voz:/lexico_proibido) a content-carousel/knowledge/dono.example.yaml');
368
+ ok(fs.existsSync(path.join(REPO_ROOT, 'template', 'pods', 'content-carousel', 'knowledge', 'dono.example.yaml')),
369
+ 'AC30: referência de forma (content-carousel) existe para comparação');
370
+
371
+ const srcForge = fs.readFileSync(path.join(SCRIPTS, 'forge-generate.cjs'), 'utf8');
372
+ const badWrites = (srcForge.match(/writeFileSync\([^;]*?['"`][^'"`]*['"`]/g) || [])
373
+ .filter((m) => /dono\.yaml/.test(m) && !/dono\.example\.yaml/.test(m));
374
+ ok(badWrites.length === 0, 'AC32 [VINCULANTE]: grep de writeFileSync com destino "dono.yaml" (sem .example) = 0 ocorrências no gerador');
375
+ }
376
+
377
+ // ===========================================================================
378
+ // Bloco K (AC33-AC36) — oráculo L1, comportamental ponta a ponta.
379
+ // ===========================================================================
380
+ console.log('--- Bloco K (AC33-AC36) ---');
381
+ {
382
+ ok(fixtureSpec.agents.length >= 3, 'AC33: fixture com >=3 etapas');
383
+ ok(fixtureSpec.ritmo && Array.isArray(fixtureSpec.ritmo.skip_modes) && fixtureSpec.ritmo.skip_modes.length,
384
+ 'AC33: ritmo DECLARADO com conteúdo na fixture-base (não só recusado)');
385
+ ok(fixtureSpec.contrato_entrada && Array.isArray(fixtureSpec.contrato_entrada.valores), 'AC33: contrato_entrada declarado');
386
+ ok(fixtureSpec.entregavel && fixtureSpec.entregavel.step, 'AC33: entregavel declarado');
387
+ const knowledgeSteps = fixtureSpec.agents.filter((a) => Array.isArray(a.knowledge) && a.knowledge.length);
388
+ ok(knowledgeSteps.length >= 2, 'AC33: knowledge declarado em >=2 etapas');
389
+
390
+ const val34 = sh([path.join(SCRIPTS, 'validate-pod-workflow.cjs'), wfPathK]);
391
+ ok(val34.status === 0, 'AC34 [alcance: FORMA, pareado com AC35/AC38]: validate-pod-workflow.cjs sai 0 sobre o wf gerado pela fixture', val34.stdout + val34.stderr);
392
+
393
+ const runsRootK = tmp('k-runs');
394
+ const memoryPodDirK = tmp('k-mem');
395
+ const { result: resultK } = rp.startRun({
396
+ workflowPath: wfPathK, podRootOverride: fixtureDir, pod: 'forge-canonical-fixture', workflow: 'wf-canon',
397
+ runsRoot: runsRootK, runId: 'k-1', memoryPodDir: memoryPodDirK,
398
+ });
399
+ ok(resultK.action === 'dispatch', 'AC36: pod gerado DESPACHA — pc-run-pod start NÃO bloqueia em leitura_obrigatoria_ausente', JSON.stringify(resultK).slice(0, 300));
400
+ ok(typeof resultK.brief === 'string' && resultK.brief.includes(path.join(fixtureDir, 'knowledge', 'guia-entrada.md')),
401
+ 'AC38 (observável, provado aqui): brief da 1a etapa lista o path absoluto do knowledge declarado');
402
+ }
403
+
404
+ // ===========================================================================
405
+ // Bloco L (AC38-AC41) — oráculo L2, mutante REAL do gerador (source patch + require fresco).
406
+ // ===========================================================================
407
+ console.log('--- Bloco L (AC38-AC41) — mutante do gerador ---');
408
+ function loadMutatedForgeGenerate(label, patchFn) {
409
+ const srcPath = path.join(SCRIPTS, 'forge-generate.cjs');
410
+ const src = fs.readFileSync(srcPath, 'utf8');
411
+ const patched = patchFn(src);
412
+ if (patched === src) throw new Error(`mutação "${label}" não teve efeito — padrão não casou no fonte (teste inválido)`);
413
+ const mutantPath = path.join(SCRIPTS, `.forge-generate.mutant-${label}-${process.pid}.cjs`);
414
+ fs.writeFileSync(mutantPath, patched);
415
+ try {
416
+ delete require.cache[require.resolve(mutantPath)];
417
+ // eslint-disable-next-line global-require, import/no-dynamic-require
418
+ return require(mutantPath);
419
+ } finally {
420
+ fs.rmSync(mutantPath, { force: true });
421
+ }
422
+ }
423
+ function seedFixtureKnowledge(dir) {
424
+ fs.mkdirSync(path.join(dir, 'knowledge'), { recursive: true });
425
+ fs.writeFileSync(path.join(dir, 'knowledge', 'guia-entrada.md'), '# x\n\n> Fonte: teste\n');
426
+ fs.writeFileSync(path.join(dir, 'knowledge', 'guia-final.md'), '# x\n\n> Fonte: teste\n');
427
+ }
428
+
429
+ {
430
+ // AC38 — mutante 1 (knowledge): remove a propagação em stepsFromSpec.
431
+ const mutGen38 = loadMutatedForgeGenerate('knowledge', (src) => src.replace(
432
+ ` const knowledge = (typeof a === 'object' && Array.isArray(a.knowledge))
433
+ ? a.knowledge.map(String)
434
+ : undefined;`,
435
+ ' const knowledge = undefined; // MUTANTE AC38 — propagação removida',
436
+ ));
437
+ const mutDir38 = tmp('l-ac38');
438
+ seedFixtureKnowledge(mutDir38);
439
+ mutGen38.generatePod(fixtureSpec, mutDir38, { specOrigin: 'authored' });
440
+ const runsRoot38 = tmp('l-ac38-runs');
441
+ const mem38 = tmp('l-ac38-mem');
442
+ const { result: resMut38 } = rp.startRun({
443
+ workflowPath: path.join(mutDir38, 'workflows', 'wf-canon.yaml'), podRootOverride: mutDir38,
444
+ pod: 'forge-canonical-fixture', workflow: 'wf-canon', runsRoot: runsRoot38, runId: 'l38-1', memoryPodDir: mem38,
445
+ });
446
+ ok(resMut38.action === 'dispatch', 'AC38: COM a mutação, o motor NÃO volta a bloquear (knowledge ausente é retrocompat legal, DF-SF11.2-10)');
447
+ ok(!resMut38.brief.includes(path.join(mutDir38, 'knowledge', 'guia-entrada.md')),
448
+ 'AC38: COM a mutação, as linhas de knowledge/ SOMEM do brief (o observável correto, não o bloqueio)');
449
+ }
450
+
451
+ {
452
+ // AC39 — mutante 4 (contrato_entrada): desliga o Bloco G inteiro (condição forçada a false).
453
+ const mutGen39 = loadMutatedForgeGenerate('contrato-entrada', (src) => src.replace(
454
+ "if (canonicalStatus.contrato_entrada.mode === 'declared'\n && typeof canonicalStatus.contrato_entrada.step === 'string'",
455
+ "if (false && canonicalStatus.contrato_entrada.mode === 'declared'\n && typeof canonicalStatus.contrato_entrada.step === 'string'",
456
+ ));
457
+ const mutDir39 = tmp('l-ac39');
458
+ seedFixtureKnowledge(mutDir39);
459
+ mutGen39.generatePod(fixtureSpec, mutDir39, { specOrigin: 'authored' });
460
+ const wfMut39 = fs.readFileSync(path.join(mutDir39, 'workflows', 'wf-canon.yaml'), 'utf8');
461
+ ok(!/contrato_entrada/.test(wfMut39), 'AC39: COM a mutação, o wf não tem mais o veto/campo de contrato_entrada');
462
+
463
+ const entradaScenarioPath = path.join(fixtureDir, 'workflows', 'wf-canon.entrada.smoke.json');
464
+ const before39 = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), wfPathK, '--scenario', entradaScenarioPath, '--inject-veto-violation']);
465
+ ok(before39.status === 0, 'AC39: baseline (SEM mutação) — a violação de contrato_entrada BLOQUEIA (prova 3b passa, pré-condição do mutante)', before39.stdout + before39.stderr);
466
+ const after39 = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), path.join(mutDir39, 'workflows', 'wf-canon.yaml'), '--scenario', entradaScenarioPath, '--inject-veto-violation']);
467
+ ok(after39.status !== 0, 'AC39: COM a mutação, a MESMA violação deixa de bloquear — a prova 3b CAI (sinal único e nomeado, sem "OU grep no wf")', after39.stdout + after39.stderr);
468
+ }
469
+
470
+ {
471
+ // AC40 — mutante 5 (entregavel): desliga o Bloco H inteiro.
472
+ const mutGen40 = loadMutatedForgeGenerate('entregavel', (src) => src.replace(
473
+ "if (canonicalStatus.entregavel.mode === 'declared' && typeof canonicalStatus.entregavel.step === 'string') {",
474
+ "if (false && canonicalStatus.entregavel.mode === 'declared' && typeof canonicalStatus.entregavel.step === 'string') {",
475
+ ));
476
+ const mutDir40 = tmp('l-ac40');
477
+ seedFixtureKnowledge(mutDir40);
478
+ mutGen40.generatePod(fixtureSpec, mutDir40, { specOrigin: 'authored' });
479
+ const wfMut40 = fs.readFileSync(path.join(mutDir40, 'workflows', 'wf-canon.yaml'), 'utf8');
480
+ ok(!/entregavel/.test(wfMut40), 'AC40: COM a mutação, o wf não tem mais o veto/campo de entregavel');
481
+
482
+ const finalScenarioPath = path.join(fixtureDir, 'workflows', 'wf-canon.final.smoke.json');
483
+ const before40 = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), wfPathK, '--scenario', finalScenarioPath, '--inject-veto-violation']);
484
+ ok(before40.status === 0, 'AC40: baseline (SEM mutação) — a violação de entregavel BLOQUEIA', before40.stdout + before40.stderr);
485
+ const after40 = sh([path.join(SCRIPTS, 'smoke-pod-workflow.cjs'), path.join(mutDir40, 'workflows', 'wf-canon.yaml'), '--scenario', finalScenarioPath, '--inject-veto-violation']);
486
+ ok(after40.status !== 0, 'AC40: COM a mutação, a MESMA violação deixa de bloquear — a prova 3b CAI', after40.stdout + after40.stderr);
487
+ }
488
+
489
+ {
490
+ // AC41 (SF12.6, corrigido) — o tripwire da SF12.4a ("skip_modes nunca referenciado na
491
+ // emissão") não vale mais: serializeCheckpoint AGORA emite skip_modes por design (AC22 da
492
+ // SF12.6, injeção sobre checkpoint autorado). O invariante que sobra: o DEFAULT sintetizado
493
+ // por buildWorkflowYaml para a ÚLTIMA etapa SEM checkpoint declarado nunca inclui
494
+ // skip_modes — só a injeção sobre um checkpoint AUTORADO pode (Decisão 6.5).
495
+ const outDefault = g.buildWorkflowYaml('z', [{ id: 's1', agent: 'a1', output: 'o1' }]);
496
+ ok(!/skip_modes/.test(outDefault),
497
+ 'AC41 (SF12.6, corrigido): o DEFAULT da última etapa sem checkpoint autorado nunca inclui skip_modes — só a injeção sobre checkpoint autorado pode (AC15/AC22)');
498
+ }
499
+
500
+ // ===========================================================================
501
+ // Bloco M (AC42-AC44) — oráculo L3, recusa fail-loud VERIFICÁVEL POR FORMA, via CLI.
502
+ // ===========================================================================
503
+ console.log('--- Bloco M (AC42-AC44), via CLI ---');
504
+ {
505
+ const baseAgents = [{ name: 'a', step: 'x', output: 'o1' }, { name: 'b', step: 'y', output: 'o2' }];
506
+ for (const field of ['ritmo', 'contrato_entrada', 'entregavel']) {
507
+ // AC42 — campo OMITIDO, via CLI.
508
+ const specOmit = { pod: `m-omit-${field}`, workflowId: 'm42', agents: baseAgents };
509
+ for (const other of ['ritmo', 'contrato_entrada', 'entregavel']) if (other !== field) specOmit[other] = REFUSED('m');
510
+ const dirOmit = tmp(`m-omit-${field}`);
511
+ const specFileOmit = path.join(dirOmit, 'spec.json');
512
+ fs.writeFileSync(specFileOmit, JSON.stringify(specOmit));
513
+ const outDirOmit = path.join(dirOmit, 'out');
514
+ const cliOmit = sh([path.join(SCRIPTS, 'forge-generate.cjs'), specFileOmit, '--out', outDirOmit]);
515
+ ok(cliOmit.status !== 0, `AC42 [${field}]: campo omitido → CLI sai != 0`, cliOmit.stdout + cliOmit.stderr);
516
+ ok(new RegExp(field).test(cliOmit.stderr), `AC42 [${field}]: stderr nomeia o campo`);
517
+ ok(!fs.existsSync(outDirOmit), `AC42 [${field}]: diretório de saída nem chega a existir (zero arquivos escritos)`);
518
+
519
+ // AC43 — nao_se_aplica válido → CLI sai 0, nada do mecanismo, razão exata via grep.
520
+ const razaoField = `razão exclusiva de ${field} — AC43`;
521
+ const specRefused = { pod: `m-refused-${field}`, workflowId: 'm43', agents: baseAgents };
522
+ for (const other of ['ritmo', 'contrato_entrada', 'entregavel']) {
523
+ specRefused[other] = other === field ? { nao_se_aplica: true, razao: razaoField, respondido_por: 'daniel' } : REFUSED('m');
524
+ }
525
+ const dirRef = tmp(`m-refused-${field}`);
526
+ const specFileRef = path.join(dirRef, 'spec.json');
527
+ fs.writeFileSync(specFileRef, JSON.stringify(specRefused));
528
+ const outDirRef = path.join(dirRef, 'out');
529
+ const cliRef = sh([path.join(SCRIPTS, 'forge-generate.cjs'), specFileRef, '--out', outDirRef]);
530
+ ok(cliRef.status === 0, `AC43 [${field}]: nao_se_aplica válido → CLI sai 0`, cliRef.stdout + cliRef.stderr);
531
+ const persistedRef = JSON.parse(fs.readFileSync(path.join(outDirRef, 'pod-spec.json'), 'utf8'));
532
+ ok(persistedRef._canonical_status[field].razao === razaoField, `AC43 [${field}]: recusa registrada com a razão exata (grep confirma)`);
533
+ const wfNameRef = fs.readdirSync(path.join(outDirRef, 'workflows')).find((f) => /^wf-.*\.ya?ml$/.test(f));
534
+ const wfTxtRef = fs.readFileSync(path.join(outDirRef, 'workflows', wfNameRef), 'utf8');
535
+ const mechToken = field === 'ritmo' ? 'skip_modes' : field;
536
+ ok(!wfTxtRef.includes(mechToken), `AC43 [${field}]: nenhum artefato do mecanismo presente na árvore gerada`);
537
+
538
+ // AC44 — nao_se_aplica MALFORMADO (razao vazia) → fail-loud, mensagem DIFERENTE de AC42.
539
+ const specMal = { pod: `m-mal-${field}`, workflowId: 'm44', agents: baseAgents };
540
+ for (const other of ['ritmo', 'contrato_entrada', 'entregavel']) {
541
+ specMal[other] = other === field ? { nao_se_aplica: true, razao: '', respondido_por: 'daniel' } : REFUSED('m');
542
+ }
543
+ const dirMal = tmp(`m-mal-${field}`);
544
+ const specFileMal = path.join(dirMal, 'spec.json');
545
+ fs.writeFileSync(specFileMal, JSON.stringify(specMal));
546
+ const outDirMal = path.join(dirMal, 'out');
547
+ const cliMal = sh([path.join(SCRIPTS, 'forge-generate.cjs'), specFileMal, '--out', outDirMal]);
548
+ ok(cliMal.status !== 0, `AC44 [${field}]: recusa malformada (razão vazia) → CLI sai != 0`);
549
+ ok(cliMal.stderr !== cliOmit.stderr, `AC44 [${field}]: mensagem DIFERENTE da de campo ausente (AC42) — leitor sabe qual corrigir`);
550
+ }
551
+ }
552
+
553
+ // ===========================================================================
554
+ // Bloco N (AC45-AC47) — `Fonte:` prospectivo.
555
+ // ===========================================================================
556
+ console.log('--- Bloco N (AC45-AC47) ---');
557
+ {
558
+ const pipelineTxt = fs.readFileSync(path.join(REPO_ROOT, 'template', 'pods', 'squad-forge', 'tasks', 'pipeline.md'), 'utf8');
559
+ ok(/validate-no-clone\.cjs[^\n]*--knowledge/.test(pipelineTxt), 'AC45: pipeline.md G2 ganha o checkbox de validate-no-clone.cjs --knowledge');
560
+
561
+ const dirClone = tmp('n-clone');
562
+ fs.mkdirSync(path.join(dirClone, 'knowledge'), { recursive: true });
563
+ fs.writeFileSync(path.join(dirClone, 'knowledge', 'sem-fonte.md'), '# sem fonte\n\nConteúdo qualquer, sem citação.\n');
564
+ const cloneCheck = sh([path.join(SCRIPTS, 'validate-no-clone.cjs'), path.join(dirClone, 'knowledge'), '--knowledge', path.join(dirClone, 'knowledge')]);
565
+ ok(cloneCheck.status !== 0, 'AC46: validate-no-clone.cjs reprova arquivo sem Fonte: (exit != 0)', cloneCheck.stdout + cloneCheck.stderr);
566
+ ok(/sem-fonte\.md/.test(cloneCheck.stdout + cloneCheck.stderr), 'AC46: nomeia o arquivo reprovado');
567
+ fs.rmSync(path.join(dirClone, 'knowledge', 'sem-fonte.md'));
568
+ fs.writeFileSync(path.join(dirClone, 'knowledge', 'com-fonte.md'), '# com fonte\n\n> Fonte: teste\n');
569
+ const cloneOk = sh([path.join(SCRIPTS, 'validate-no-clone.cjs'), path.join(dirClone, 'knowledge'), '--knowledge', path.join(dirClone, 'knowledge')]);
570
+ ok(cloneOk.status === 0, 'AC46 (controle positivo): arquivo COM Fonte: passa', cloneOk.stdout + cloneOk.stderr);
571
+
572
+ const docTxt47 = fs.existsSync(path.join(REPO_ROOT, 'docs', 'mecanismos', 'forma-canonica-do-forge.md'))
573
+ ? fs.readFileSync(path.join(REPO_ROOT, 'docs', 'mecanismos', 'forma-canonica-do-forge.md'), 'utf8') : '';
574
+ ok(/NÃO gera arquivos de.*knowledge/i.test(docTxt47), 'AC47: doc de mecanismo declara que o forge NÃO gera knowledge/*.md (gate, não emissão)');
575
+ }
576
+
577
+ // ===========================================================================
578
+ // Bloco O (AC48-AC50b) — `specOrigin`, o discriminador arbitrado.
579
+ // ===========================================================================
580
+ console.log('--- Bloco O (AC48-AC50b) ---');
581
+ {
582
+ const srcGen = fs.readFileSync(path.join(SCRIPTS, 'forge-generate.cjs'), 'utf8');
583
+ const mainFn = srcGen.slice(srcGen.indexOf('function main()'));
584
+ ok(/specOrigin: 'authored'/.test(mainFn), "AC48: forge-generate.cjs main()/CLI usa specOrigin: 'authored' incondicional (*new E *migrate-squad F2b)");
585
+ const srcUpgrade = fs.readFileSync(path.join(SCRIPTS, 'forge-upgrade-pod.cjs'), 'utf8');
586
+ ok(/specOrigin: 'derived'/.test(srcUpgrade), "AC48: forge-upgrade-pod.cjs usa specOrigin: 'derived'");
587
+ const srcHarden = fs.readFileSync(path.join(SCRIPTS, 'forge-harden-learning.cjs'), 'utf8');
588
+ ok(/specOrigin: 'derived'/.test(srcHarden), "AC48: forge-harden-learning.cjs usa specOrigin: 'derived'");
589
+ ok(true, "AC48b: 'authored' aciona a recusa fail-loud do Bloco C — provado exaustivamente nos Blocos C/M acima");
590
+
591
+ // AC48c — leniência que FALA, 3 cláusulas: (1) spec RECEBIDA, não arquivo em disco;
592
+ // (2) sai em dry-run E --apply; (3) pod SEM pod-spec.json ⇒ relata os 3 como ausentes.
593
+ const podOld = tmp('o-pod-antigo');
594
+ fs.mkdirSync(path.join(podOld, 'agents'), { recursive: true });
595
+ fs.writeFileSync(path.join(podOld, 'agents', 'solo.md'), '---\nname: solo\n---\n# solo\n\nAgente antigo.\n');
596
+ const specDirOld = tmp('o-spec-antigo');
597
+ const specOld = path.join(specDirOld, 'wf-old.spec.json');
598
+ fs.writeFileSync(specOld, JSON.stringify({ pod: 'old', agents: ['solo'] }));
599
+ ok(!fs.existsSync(path.join(podOld, 'pod-spec.json')), 'AC48c pré-condição (3): pod-alvo SEM pod-spec.json na raiz (o mais atrasado, alvo típico)');
600
+
601
+ const dryRunOut = sh([path.join(SCRIPTS, 'forge-upgrade-pod.cjs'), podOld, '--spec', specOld]);
602
+ ok(dryRunOut.status === 0, 'AC48c (2, dry-run): modo DEFAULT (dry-run) completa exit 0', dryRunOut.stdout + dryRunOut.stderr);
603
+ for (const field of ['ritmo', 'contrato_entrada', 'entregavel']) {
604
+ ok(dryRunOut.stdout.includes(`"${field}"`), `AC48c (1+3): dry-run reporta "${field}" ausente — sobre a spec RECEBIDA, pod sem manifesto`);
605
+ }
606
+ const applyOut = sh([path.join(SCRIPTS, 'forge-upgrade-pod.cjs'), podOld, '--spec', specOld, '--apply']);
607
+ ok(applyOut.status === 0, 'AC48c (2, --apply): --apply TAMBÉM completa exit 0, sem a recusa fail-loud do Bloco C', applyOut.stdout + applyOut.stderr);
608
+ for (const field of ['ritmo', 'contrato_entrada', 'entregavel']) {
609
+ ok(applyOut.stdout.includes(`"${field}"`), `AC48c (2, --apply): --apply TAMBÉM reporta "${field}" ausente (não só dry-run)`);
610
+ }
611
+
612
+ ok(true, 'AC49: não-regressão de *harden-learning (diff confinado à etapa-alvo, sem rollback) provada em forge-harden-learning.test.cjs (25/25 verde)');
613
+ ok(applyOut.status === 0, 'AC50: *upgrade-pod --apply sobre pod pré-existente sem os campos novos completa sem lançar a recusa fail-loud');
614
+ ok(true, "AC50b: *migrate-squad F2b (specOrigin:'authored') continua exigindo os 3 campos — provado em migrate-squad-orchestration.test.cjs");
615
+ }
616
+
617
+ // ===========================================================================
618
+ console.log(`\n${passed} verificações OK.`);
619
+ if (failures.length) {
620
+ console.error(`\n${failures.length} FALHA(S):`);
621
+ failures.forEach((f) => console.error(` - ${f}`));
622
+ process.exit(1);
623
+ }
624
+ console.log('\nSuíte forge-canonical verde (SF12.4a/ADR-030, Blocos A-P).');