@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,1303 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * forge-generate.cjs — geração proporcional de artefatos de pod (Story SF1.4).
5
+ *
6
+ * Consome o veredito de `forge-assess-tier.cjs` (ADR-004) e gera, DISCO-FIRST
7
+ * (M2 — grava em disco antes de qualquer outra ação, sem rede/vault), os
8
+ * artefatos proporcionais ao tier:
9
+ *
10
+ * SIMPLE → no máximo 1 workflow mínimo + templates das 1–2 saídas.
11
+ * SEM squad-io.yaml, SEM veto-engine obrigatório, SEM memória.
12
+ * STANDARD/COMPLEX→ workflows/wf-<id>.yaml (DAG+checkpoints+veto de máquina, no
13
+ * schema da SF1.1) a partir de templates/workflow-tmpl.yaml,
14
+ * 1 template por etapa, squad-io.yaml (contrato de handoff) e
15
+ * scaffold de memória (learnings.jsonl vazio +
16
+ * learnings.consolidated.md só com cabeçalho — SEM lifecycle).
17
+ *
18
+ * O `wf-<id>.yaml` gerado passa `scripts/validate-pod-workflow.cjs` sem ajuste
19
+ * manual (AC4): DAG linear, cada etapa com agent+template(em disco)+outputs+veto,
20
+ * handoffs fechados (output de cada etapa consumido pela seguinte; última = sink).
21
+ *
22
+ * Uso:
23
+ * node scripts/forge-generate.cjs <pod-spec.json> --out <podDir>
24
+ *
25
+ * Spec: { pod, agents:[{name, step, output}], workflowId?, dna?, critical?,
26
+ * handoffs?, outputs?:[...] }. `agents` ordenados = etapas do DAG.
27
+ *
28
+ * Node >= 18, sem dependências externas.
29
+ */
30
+
31
+ const fs = require('node:fs');
32
+ const path = require('node:path');
33
+ const { classifyTier, TIER_SIMPLE } = require('./forge-assess-tier.cjs');
34
+ const podMemory = require('./pod-memory.cjs');
35
+ // SF12.6/ADR-030 Bloco C/D — a auto-validação (AC25) e o guard por classe (AC11-AC20) INVOCAM
36
+ // o validador estático já existente, nunca duplicam a regra (juiz único: checkCheckpoint).
37
+ const { parseYaml: parseWfYaml, checkCheckpoint } = require('./validate-pod-workflow.cjs');
38
+
39
+ function slug(s) {
40
+ return String(s).toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
41
+ }
42
+
43
+ // ---------------------------------------------------------------------------
44
+ // MODO ADITIVO (Decisão C.2, SF3.1). Preserva o validado; adiciona o novo; RECUSA
45
+ // (exit 2) colisão de artefato validado fora de owns[]. `owns[]` = paths que a
46
+ // linha corrente É DONA (planejados no open-line do manifest) — regenerados
47
+ // deterministicamente (retomada idempotente), nunca tratados como colisão.
48
+ // ---------------------------------------------------------------------------
49
+ class AdditiveCollisionError extends Error {
50
+ constructor(relPath, kind) {
51
+ super(`colisão aditiva: ${kind} "${relPath}" já existe e NÃO pertence a esta linha (owns[]) — ` +
52
+ `modo aditivo recusa editar/sobrescrever artefato validado (isso é evolução do pod, ciclo próprio).`);
53
+ this.name = 'AdditiveCollisionError';
54
+ this.exitCode = 2;
55
+ this.collision = { path: relPath, kind };
56
+ }
57
+ }
58
+
59
+ // ---------------------------------------------------------------------------
60
+ // Forma STANDARD-interface (ADR-004 adendo 2026-07-25, caso de fronteira SF6.2).
61
+ // `interface: true` suprime wf-*.yaml + squad-io.yaml (zero handoff artefato-a-artefato
62
+ // entre os agentes do pod). É VÁLIDA SOMENTE com `handoffs: 0` — com handoff declarado
63
+ // há processo real e o pod deve gerar workflow. Fail-loud (exit 2) contra fabricação.
64
+ // ---------------------------------------------------------------------------
65
+ class InterfaceContractError extends Error {
66
+ constructor(handoffs) {
67
+ super(`pod-interface inválido: interface:true exige handoffs:0 (recebido handoffs:${handoffs}). ` +
68
+ `A forma STANDARD-interface (ADR-004 adendo 2026-07-25) suprime wf/squad-io SOMENTE onde há zero ` +
69
+ `handoff interno; com handoff real o pod tem processo e deve gerar workflow/contrato — não pode declarar-se interface.`);
70
+ this.name = 'InterfaceContractError';
71
+ this.exitCode = 2;
72
+ this.handoffs = handoffs;
73
+ }
74
+ }
75
+
76
+ /** Extrai os handoffs (from/to/artifact) de um texto squad-io.yaml. */
77
+ function extractHandoffTriples(text) {
78
+ const lines = String(text).split(/\r?\n/);
79
+ const triples = [];
80
+ let cur = null;
81
+ for (const line of lines) {
82
+ const from = line.match(/^\s*-\s*from:\s*(.+?)\s*$/);
83
+ if (from) { if (cur) triples.push(cur); cur = { from: from[1], to: '', artifact: '' }; continue; }
84
+ if (cur) {
85
+ const to = line.match(/^\s*to:\s*(.+?)\s*$/);
86
+ const art = line.match(/^\s*artifact:\s*(.+?)\s*$/);
87
+ if (to) cur.to = to[1];
88
+ else if (art) cur.artifact = art[1];
89
+ }
90
+ }
91
+ if (cur) triples.push(cur);
92
+ return triples.filter((t) => t.from && t.to);
93
+ }
94
+
95
+ /** Quebra um squad-io.yaml em preâmbulo (comentários) + seções de topo (`key:`). */
96
+ function splitTopSections(text) {
97
+ const lines = String(text).split(/\r?\n/);
98
+ const preamble = [];
99
+ const sections = [];
100
+ let cur = null;
101
+ for (const line of lines) {
102
+ const isTop = /^[A-Za-z_][\w-]*:\s*(.*)$/.test(line) && !/^\s/.test(line);
103
+ if (isTop) { if (cur) sections.push(cur); cur = { key: line.match(/^([A-Za-z_][\w-]*):/)[1], lines: [line] }; }
104
+ else if (cur) cur.lines.push(line);
105
+ else preamble.push(line);
106
+ }
107
+ if (cur) sections.push(cur);
108
+ return { preamble, sections };
109
+ }
110
+
111
+ /**
112
+ * MERGE POR SEÇÃO do squad-io.yaml (Decisão C.2). Preserva TODAS as seções
113
+ * pré-existentes (inclusive customizadas fora do gerador) e faz UNIÃO dos handoffs
114
+ * (dedup por from|to|artifact). Não sobrescreve conteúdo validado — só adiciona.
115
+ */
116
+ function mergeSquadIo(existing, generated) {
117
+ const ex = splitTopSections(existing);
118
+ const gen = splitTopSections(generated);
119
+ const exKeys = new Set(ex.sections.map((s) => s.key));
120
+ const union = [];
121
+ const seen = new Set();
122
+ for (const h of [...extractHandoffTriples(existing), ...extractHandoffTriples(generated)]) {
123
+ const k = `${h.from}|${h.to}|${h.artifact}`;
124
+ if (!seen.has(k)) { seen.add(k); union.push(h); }
125
+ }
126
+ const handoffLines = ['handoffs:'];
127
+ for (const h of union) handoffLines.push(` - from: ${h.from}`, ` to: ${h.to}`, ` artifact: ${h.artifact}`);
128
+ const out = [];
129
+ if (ex.preamble.some((l) => l.trim())) out.push(...ex.preamble);
130
+ for (const s of ex.sections) {
131
+ if (s.key === 'handoffs') out.push(...handoffLines);
132
+ else out.push(...s.lines);
133
+ }
134
+ for (const s of gen.sections) {
135
+ if (exKeys.has(s.key)) continue;
136
+ if (s.key === 'handoffs') { out.push(...handoffLines); continue; }
137
+ out.push(...s.lines);
138
+ }
139
+ return `${out.join('\n').replace(/\n{3,}/g, '\n\n').replace(/\s*$/, '')}\n`;
140
+ }
141
+
142
+ /** Escreve arquivo criando os diretórios pais. Disco-first, síncrono. */
143
+ function writeDisk(file, content) {
144
+ fs.mkdirSync(path.dirname(file), { recursive: true });
145
+ fs.writeFileSync(file, content);
146
+ }
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // SF12.4a/ADR-030 Bloco O (`DF-SF12.4-11`, Emenda 1) — `opts.specOrigin`, OBRIGATÓRIO,
150
+ // SEM DEFAULT. Substitui a leitura ambígua de `opts.additive` (acerta 0 de 4 chamadores,
151
+ // anti-correlacionado em 2 — ver `*review` da SF12.4a). 3 call sites reais:
152
+ // forge-generate.cjs main()/CLI → 'authored' (incondicional — *new E
153
+ // *migrate-squad F2b, `--additive` NÃO muda isto)
154
+ // forge-upgrade-pod.cjs → 'derived'
155
+ // forge-harden-learning.cjs → 'derived'
156
+ // Sem default: um consumidor novo que esquecer o parâmetro PARA na hora, nomeando a
157
+ // ausência — a mesma doutrina da Decisão 3 (Bloco C) aplicada à própria API do gerador.
158
+ // ---------------------------------------------------------------------------
159
+ const SPEC_ORIGINS = new Set(['authored', 'derived']);
160
+
161
+ class SpecOriginError extends Error {
162
+ constructor(value) {
163
+ super(`generatePod: opts.specOrigin é OBRIGATÓRIO e deve ser "authored" ou "derived" (recebido: ${JSON.stringify(value)}). ` +
164
+ `"authored" = spec autorada fresca (*new, *migrate-squad F2b) — aciona a recusa fail-loud dos campos canônicos (Bloco C). ` +
165
+ `"derived" = spec sintetizada de um pod já em disco (*upgrade-pod, *harden-learning) — preserva o comportamento atual e reporta ` +
166
+ `os campos canônicos ausentes (ADR-030 Emenda 1, DF-SF12.4-11). Sem default: nomeie a escolha.`);
167
+ this.name = 'SpecOriginError';
168
+ this.exitCode = 2;
169
+ }
170
+ }
171
+
172
+ function validateSpecOrigin(value) {
173
+ if (!SPEC_ORIGINS.has(value)) throw new SpecOriginError(value);
174
+ }
175
+
176
+ // ---------------------------------------------------------------------------
177
+ // SF12.6/ADR-030 Bloco C (Decisão 7) — guard POR CLASSE de artefato. Escopo:
178
+ // `specOrigin: 'derived'` (regeneração a partir de um pod já em disco — *upgrade-pod,
179
+ // *harden-learning) E o path NÃO pertencer a `opts.owns[]` (harden-learning já usa
180
+ // --additive + owns para regenerar deterministicamente o SEU alvo; essa é uma
181
+ // autoridade de escrita PRÉ-EXISTENTE e mais estreita — o guard novo não a reabre,
182
+ // só protege o que NINGUÉM declarou possuir). Três classes:
183
+ // wf → recusa fail-loud (Decisão 7.1), leniência POR PATH (Decisão 7.2).
184
+ // templates/* → preserva-por-default (reporta em preserved[], nunca lança).
185
+ // squad-io.yaml → sempre-merge (mergeSquadIo, incondicional — ver bloco (3) abaixo).
186
+ // pod-spec.json / memory/ → FORA, explicitamente (AC16) — já são write-once/merge
187
+ // por outros mecanismos pré-existentes (guarda de existência, consolidate()).
188
+ // ---------------------------------------------------------------------------
189
+ class DivergentRegenerationError extends Error {
190
+ constructor(relPath, diskLen, genLen, firstDiffLine) {
191
+ super(
192
+ `recusa fail-loud (ADR-030 Decisão 7.1, guard por classe): "${relPath}" (classe wf — fail-loud) ` +
193
+ `diverge do que a spec regeneraria — bytes disco×gerado: ${diskLen}×${genLen}; 1ª linha divergente: ` +
194
+ `${JSON.stringify(firstDiffLine)}. A causa: a spec está atrás do artefato — alguém editou o wf ` +
195
+ 'diretamente sem levar a mudança para pod-spec.json (ADR-030 Emenda 3: o wf é DERIVADO da spec, ' +
196
+ 'nunca o contrário). Duas saídas, NESTA ordem: (1) declarar na spec o que falta, para a próxima ' +
197
+ 'regeneração já bater — fecha o vão de verdade; (2) se a perda for aceitável, nomeie o path ' +
198
+ `explicitamente: --allow-divergent-overwrite ${relPath} (ADR-030 Decisão 7.2 — a leniência é ` +
199
+ 'sempre POR PATH, nunca "sim para tudo").',
200
+ );
201
+ this.name = 'DivergentRegenerationError';
202
+ this.exitCode = 2;
203
+ this.path = relPath;
204
+ }
205
+ }
206
+
207
+ class AllowDivergentOverwriteError extends Error {
208
+ constructor(value) {
209
+ super(`generatePod: opts.allowDivergentOverwrite deve ser um array de paths (recebido ` +
210
+ `${JSON.stringify(value)}) — a leniência é SEMPRE por path, nunca "sim para tudo" (ADR-030 ` +
211
+ 'Decisão 7.2). Ex.: allowDivergentOverwrite: ["workflows/wf-x.yaml"].');
212
+ this.name = 'AllowDivergentOverwriteError';
213
+ this.exitCode = 2;
214
+ }
215
+ }
216
+
217
+ /** Forma nua (`true`/qualquer não-array) é RECUSADA (Decisão 7.2). Ausente ⇒ []. */
218
+ function validateAllowDivergentOverwrite(value) {
219
+ if (value === undefined) return [];
220
+ if (!Array.isArray(value)) throw new AllowDivergentOverwriteError(value);
221
+ return value.map(String);
222
+ }
223
+
224
+ /** 1ª linha em que `a`/`b` divergem (para a mensagem de recusa, Decisão 7.3). */
225
+ function firstDivergentLine(a, b) {
226
+ const la = String(a).split(/\r?\n/);
227
+ const lb = String(b).split(/\r?\n/);
228
+ const n = Math.max(la.length, lb.length);
229
+ for (let i = 0; i < n; i++) {
230
+ if (la[i] !== lb[i]) return lb[i] !== undefined ? lb[i] : la[i];
231
+ }
232
+ return '';
233
+ }
234
+
235
+ /**
236
+ * SF12.6/ADR-030 Bloco D (AC25/DEBT-SF12.6-E) — generatePod valida a PRÓPRIA saída ANTES
237
+ * de escrever: parseYaml(wfContent) → checkCheckpoint. INVOCA o validador estático já
238
+ * existente (validate-pod-workflow.cjs) — nunca duplica a regra de DF-SF11.10-3. Recusa
239
+ * (não escreve nada) se a saída violaria o invariante do gate humano.
240
+ */
241
+ class CheckpointInvariantError extends Error {
242
+ constructor(wfRelPath, cause) {
243
+ super(`generatePod: recusa escrever "${wfRelPath}" — a saída violaria o invariante do gate ` +
244
+ 'humano (ADR-024 DF-SF11.10-3), auto-validado via checkCheckpoint ANTES de qualquer escrita ' +
245
+ `(ADR-030 Bloco D, DEBT-SF12.6-E): ${cause}`);
246
+ this.name = 'CheckpointInvariantError';
247
+ this.exitCode = 2;
248
+ }
249
+ }
250
+
251
+ // ---------------------------------------------------------------------------
252
+ // SF12.4a/ADR-030 Bloco C (`DF-SF12.4-3`, Decisão 3) — os 3 padrões DECLARADOS-OU-
253
+ // RECUSADOS (`ritmo`, `contrato_entrada`, `entregavel`, Bloco A/AC2). Três estados,
254
+ // nunca dois: declarado com conteúdo (emite o mecanismo, Blocos E/G/H); recusado —
255
+ // `nao_se_aplica: true` + `razao` + `respondido_por` não-vazios (emite nada, registra a
256
+ // recusa); ausente (recusa fail-loud SOB `specOrigin: 'authored'` — Bloco O/AC48b). A
257
+ // máquina confere FORMA (campo existe? razão não-vazia? atribuição presente?), nunca
258
+ // julga se a razão é boa (fronteira ADR-002, `DEBT-SF12.4-A`).
259
+ // ---------------------------------------------------------------------------
260
+ const CANONICAL_FIELDS = ['ritmo', 'contrato_entrada', 'entregavel'];
261
+
262
+ class CanonicalFieldError extends Error {
263
+ constructor(field, reason) {
264
+ super(`campo canônico "${field}" ${reason} — declare com conteúdo ou registre ` +
265
+ `{ nao_se_aplica: true, razao: "<motivo>", respondido_por: "<quem>" } (ADR-030 Decisão 3, DF-SF12.4-3).`);
266
+ this.name = 'CanonicalFieldError';
267
+ this.exitCode = 2;
268
+ this.field = field;
269
+ }
270
+ }
271
+
272
+ /** Referência de etapa (AC21/AC24) — mesma disciplina de `anti_path.mechanized_by`. */
273
+ function validateCanonicalStepRef(field, stepId, steps) {
274
+ if (!steps.some((s) => s.id === stepId)) {
275
+ throw new CanonicalFieldError(field, `referencia a etapa "${stepId}" que não existe entre os ids de ` +
276
+ `stepsFromSpec(spec) — ids válidos: ${steps.map((s) => s.id).join(', ') || '(nenhuma etapa)'}`);
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Lê e valida (FORMA, não semântica) UM campo canônico da spec — usado sob
282
+ * `specOrigin: 'authored'` (fail-loud, Bloco C/AC7-AC10/AC42-AC44). Retorna
283
+ * `{ mode: 'declared', value }` ou `{ mode: 'refused', razao, respondido_por }`;
284
+ * nunca retorna para o caso "ausente" — lança CanonicalFieldError.
285
+ */
286
+ function readCanonicalField(spec, field, steps) {
287
+ const raw = spec[field];
288
+ if (raw == null) {
289
+ throw new CanonicalFieldError(field, 'ausente na spec');
290
+ }
291
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
292
+ throw new CanonicalFieldError(field, 'deve ser um objeto — declarado com conteúdo OU recusa (ver AC2)');
293
+ }
294
+ if (raw.nao_se_aplica === true) {
295
+ // AC9 — a recusa TEM forma exigida: razão e atribuição não-vazias. Mensagem
296
+ // DIFERENTE da de "ausente" (AC44) — quem lê o erro sabe qual dos dois corrigir.
297
+ const razaoOk = typeof raw.razao === 'string' && raw.razao.trim() !== '';
298
+ const respOk = typeof raw.respondido_por === 'string' && raw.respondido_por.trim() !== '';
299
+ if (!razaoOk || !respOk) {
300
+ const falta = !razaoOk ? 'razao' : 'respondido_por';
301
+ throw new CanonicalFieldError(field, `tem recusa MALFORMADA — "${falta}" ausente ou vazio ` +
302
+ `(nao_se_aplica exige razao E respondido_por não-vazios)`);
303
+ }
304
+ return { mode: 'refused', razao: raw.razao, respondido_por: raw.respondido_por };
305
+ }
306
+ // Conteúdo declarado — forma exata por campo (AC2).
307
+ if (field === 'ritmo') {
308
+ if (typeof raw.step !== 'string' || raw.step.trim() === ''
309
+ || !Array.isArray(raw.skip_modes) || raw.skip_modes.length === 0
310
+ || !raw.skip_modes.every((m) => typeof m === 'string' && m.trim() !== '')) {
311
+ throw new CanonicalFieldError(field, 'tem conteúdo malformado — esperado { step: <id>, skip_modes: [<modo...>] } (≥1 modo)');
312
+ }
313
+ // SF12.6/ADR-030 AC21c — mesma disciplina que contrato_entrada/entregavel já aplicam:
314
+ // referência a etapa inexistente RECUSA, nunca retorna `declared` em silêncio.
315
+ validateCanonicalStepRef(field, raw.step, steps);
316
+ } else if (field === 'contrato_entrada') {
317
+ if (typeof raw.step !== 'string' || raw.step.trim() === ''
318
+ || !Array.isArray(raw.valores) || raw.valores.length < 2
319
+ || !raw.valores.every((v) => typeof v === 'string' && v.trim() !== '')) {
320
+ throw new CanonicalFieldError(field, 'tem conteúdo malformado — esperado { step: <id>, valores: [<≥2 strings>] }');
321
+ }
322
+ validateCanonicalStepRef(field, raw.step, steps);
323
+ } else if (field === 'entregavel') {
324
+ if (typeof raw.step !== 'string' || raw.step.trim() === '') {
325
+ throw new CanonicalFieldError(field, 'tem conteúdo malformado — esperado { step: <id> }');
326
+ }
327
+ validateCanonicalStepRef(field, raw.step, steps);
328
+ }
329
+ return { mode: 'declared', value: raw };
330
+ }
331
+
332
+ /**
333
+ * Estado dos 3 campos canônicos, best-effort (nunca lança) — usado para persistência
334
+ * (Bloco F/AC18-19), relato do modo `derived` (Bloco O/AC48c) e o anúncio do ritmo
335
+ * (Bloco E/AC15), independente de `specOrigin`.
336
+ */
337
+ function computeCanonicalStatus(spec) {
338
+ const out = {};
339
+ for (const field of CANONICAL_FIELDS) {
340
+ const raw = spec[field];
341
+ if (raw == null) {
342
+ out[field] = { mode: 'ausente' };
343
+ } else if (raw && typeof raw === 'object' && !Array.isArray(raw) && raw.nao_se_aplica === true) {
344
+ out[field] = { mode: 'refused', razao: raw.razao, respondido_por: raw.respondido_por };
345
+ } else if (raw && typeof raw === 'object' && !Array.isArray(raw)) {
346
+ out[field] = { mode: 'declared', ...raw };
347
+ } else {
348
+ out[field] = { mode: 'malformed', value: raw };
349
+ }
350
+ }
351
+ return out;
352
+ }
353
+
354
+ /**
355
+ * Template de saída de uma etapa.
356
+ * FORMA = as seções `##` (verificadas por checkTemplateConformance no runner).
357
+ * VETO ORTOGONAL (ADR-005 Decisão A): o template SEMPRE termina com um campo de
358
+ * máquina NÃO-heading — a linha `confianca: <alta|media|baixa>`, cujo placeholder
359
+ * NÃO casa o pattern do veto default (força preenchimento real). Nunca se usa
360
+ * `section_exists` de uma seção do próprio template como veto (seria implicado pela
361
+ * conformidade de forma — a classe degenerada que o piloto expôs).
362
+ */
363
+ /**
364
+ * Nota de semântica contável do veto `count_gte` (débito #1 do gate SF1.5, finding
365
+ * id:1). O piloto expôs que 2/5 runs formataram uma seção contável como TABELA e o
366
+ * contador (bullets-only, à época) contou 0 → fix evitável. Agora o template GERADO
367
+ * PRESCREVE a forma contável junto à seção-alvo, para o agente de etapa saber, ANTES
368
+ * de escrever, o que conta. Casa a semântica real de countListItemsInSection (SF2.3):
369
+ * bullets, lista numerada e linhas de tabela contam; prosa não.
370
+ */
371
+ function countGteNote(field) {
372
+ return `<!-- count_gte "${field}": conte itens de lista sob este heading — bullets (\`- \`/\`* \`), ` +
373
+ `linhas numeradas (\`1.\` \`2.\` ...) OU linhas de tabela (\`| ... |\`, o separador \`|---|---|\` NÃO conta). ` +
374
+ `Prosa corrida NÃO conta. -->`;
375
+ }
376
+
377
+ /** Índice case-insensitive dos vetos count_gte por campo (mesma regra de contains do contador). */
378
+ function countGteFields(veto) {
379
+ const out = [];
380
+ if (Array.isArray(veto)) {
381
+ for (const a of veto) {
382
+ if (a && a.type === 'count_gte' && a.field != null) out.push({ field: String(a.field), n: a.n });
383
+ }
384
+ }
385
+ return out;
386
+ }
387
+
388
+ /**
389
+ * SF12.4a/ADR-030 Blocos G/H (AC20a/AC23a) — campos de máquina extras dos padrões
390
+ * `contrato_entrada`/`entregavel`, quando declarados. Emitidos ANTES de `confianca`
391
+ * (mesma zona ortogonal fora dos headings — ADR-005 Decisão A). `extraFields`:
392
+ * `[{ field, placeholder }]`.
393
+ */
394
+ function stepTemplate(stepId, agent, output, sections, veto, extraFields) {
395
+ const secs = Array.isArray(sections) && sections.length ? sections : ['## Contexto', '## Output'];
396
+ const cg = countGteFields(veto);
397
+ const body = secs
398
+ .map((s) => {
399
+ const label = String(s).replace(/^#+\s*/, '').trim();
400
+ // Anexa a nota de forma contável quando a seção casa o field de um count_gte
401
+ // (mesma semântica CONTAINS/case-insensitive do contador do runner).
402
+ const match = cg.find((c) => label.toLowerCase().includes(c.field.toLowerCase())
403
+ || c.field.toLowerCase().includes(label.toLowerCase()));
404
+ const note = match ? `${countGteNote(match.field)}\n` : '';
405
+ return `${s}\n${note}<!-- preencher: conteúdo de "${label}" -->\n`;
406
+ })
407
+ .join('\n');
408
+ const extras = (Array.isArray(extraFields) && extraFields.length)
409
+ ? `${extraFields.map((e) => `${e.field}: <${e.placeholder}>`).join('\n')}\n`
410
+ : '';
411
+ return `# Template de saída — etapa \`${stepId}\` (agente \`${agent}\`, artefato \`${output}\`)
412
+
413
+ > Gerado por forge-generate.cjs (SF1.4 + ADR-005). Preencher com o artefato real da etapa.
414
+ > FORMA (seções \`##\`) verificada por checkTemplateConformance; VETO ORTOGONAL via o
415
+ > campo de máquina \`confianca\` abaixo (fora dos headings — ADR-005 Decisão A).
416
+
417
+ ${body}
418
+ <!-- Campo(s) de máquina do(s) padrão(ões) declarado(s) (SF12.4a/ADR-030 Blocos G/H), quando aplicável — NÃO é heading. -->
419
+ ${extras}<!-- Campo de máquina do veto default (ADR-005) — NÃO é heading. Troque o placeholder por alta|media|baixa. -->
420
+ confianca: <alta|media|baixa>
421
+ `;
422
+ }
423
+
424
+ /** Serializa UMA veto.assertion para o flow-map inline do contrato wf-*.yaml. */
425
+ function serializeAssertion(a) {
426
+ // SF7.2: enc RECURSIVO — arrays viram flow-list `[..]` e objetos viram flow-map `{..}`,
427
+ // para emitir corretamente `command_check.args` (lista) e `json_schema_valid.extract`
428
+ // (mapa). Antes, String(array)="a,b" e String(obj)="[object Object]" corrompiam a emissão.
429
+ const enc = (v) => {
430
+ if (v === null || v === undefined) return 'null';
431
+ if (typeof v === 'number' || typeof v === 'boolean') return String(v);
432
+ if (Array.isArray(v)) return `[${v.map(enc).join(', ')}]`;
433
+ if (typeof v === 'object') {
434
+ return `{ ${Object.entries(v).map(([k, val]) => `${k}: ${enc(val)}`).join(', ')} }`;
435
+ }
436
+ const s = String(v);
437
+ // SF7.6: string PURAMENTE numérica é emitida QUOTADA — sem aspas o parser YAML-lite
438
+ // a releria como number e corromperia o tipo (ex.: command_check.args ["--max","0"]
439
+ // viraria [--max, 0] e o validador estático recusa "args" não-string). Round-trip
440
+ // fiel: string entra, string sai.
441
+ return /^[A-Za-z0-9_]+$/.test(s) && !/^\d+$/.test(s) ? s : JSON.stringify(s);
442
+ };
443
+ const parts = [`type: ${a.type}`];
444
+ for (const [k, v] of Object.entries(a)) {
445
+ if (k === 'type') continue;
446
+ parts.push(`${k}: ${enc(v)}`);
447
+ }
448
+ return `- { ${parts.join(', ')} }`;
449
+ }
450
+
451
+ /** Constrói o texto YAML do wf-<id>.yaml a partir das etapas (schema SF1.1). */
452
+ /**
453
+ * Serializa um checkpoint por-etapa declarado na spec para o flow-map inline do
454
+ * wf-*.yaml. Preserva `blocking` e `criteria` (fonte do legado citada na spec).
455
+ * SF3.4/finding F2: sem isto o checkpoint mid-DAG autorado era DROPADO — degradando
456
+ * fidelidade E mascarando o gatilho checkpoint:mid do feature-diff (computeFeatureSet
457
+ * lê step.checkpoint.blocking). Newlines viram espaço (o flow-map é single-line).
458
+ */
459
+ function serializeCheckpoint(cp) {
460
+ const blocking = cp.blocking === true ? 'true' : 'false';
461
+ const criteria = String(cp.criteria == null ? '' : cp.criteria)
462
+ .replace(/[\r\n]+/g, ' ')
463
+ .replace(/\\/g, '\\\\')
464
+ .replace(/"/g, '\\"');
465
+ // SF12.6/ADR-030 Bloco D (AC22) — skip_modes, quando presente (INJETADO por `ritmo`, nunca
466
+ // lido de agents[i].checkpoint.skip_modes diretamente — ver injeção em generatePod), é
467
+ // emitido ANTES de `criteria` (mesma ordem do wf hand-authored — fecha o AC23).
468
+ const skipModes = (Array.isArray(cp.skip_modes) && cp.skip_modes.length)
469
+ ? `skip_modes: [${cp.skip_modes.join(', ')}], `
470
+ : '';
471
+ return `{ blocking: ${blocking}, ${skipModes}criteria: "${criteria}" }`;
472
+ }
473
+
474
+ function buildWorkflowYaml(workflowId, steps) {
475
+ const lines = [];
476
+ lines.push(`# wf-${workflowId}.yaml — gerado por forge-generate.cjs (SF1.4).`);
477
+ lines.push(`# Contrato: templates/workflow-tmpl.yaml. Validador: scripts/validate-pod-workflow.cjs.`);
478
+ lines.push('');
479
+ lines.push('state_machine:');
480
+ lines.push(' states: [init, running, review, complete, failed]');
481
+ lines.push(' initial: init');
482
+ lines.push(' terminal: [complete, failed]');
483
+ lines.push('');
484
+ lines.push('steps:');
485
+ steps.forEach((step, i) => {
486
+ const inputs = i === 0 ? '[brief]' : `[${steps[i - 1].output}]`;
487
+ const dependsOn = i === 0 ? '[]' : `[${steps[i - 1].id}]`;
488
+ // Checkpoint por-etapa: se a spec declarou `step.checkpoint` (mid ou final),
489
+ // PROPAGA-o conforme declarado (blocking/criteria). Senão, mantém o default:
490
+ // última etapa ganha checkpoint bloqueante genérico; demais, null.
491
+ const isLast = i === steps.length - 1;
492
+ const checkpoint = (step.checkpoint && typeof step.checkpoint === 'object')
493
+ ? serializeCheckpoint(step.checkpoint)
494
+ : (isLast ? '{ blocking: true, criteria: "revisão da etapa final aprovada" }' : 'null');
495
+ lines.push(` - id: ${step.id}`);
496
+ lines.push(` agent: ${step.agent}`);
497
+ lines.push(` template: templates/${step.id}-tmpl.md`);
498
+ lines.push(` inputs: ${inputs}`);
499
+ lines.push(` outputs: [${step.output}]`);
500
+ // SF12.4a/ADR-030 Bloco D (AC12) — mecanismo 1 (`requiredReading`, primitiva EMITIDA):
501
+ // `knowledge:` por etapa, MESMA forma já validada por validate-pod-workflow.cjs
502
+ // (bloco SF10.1/ADR-018 D2). Etapa sem `knowledge` na spec: nada emitido (AC14 —
503
+ // retrocompat byte-idêntica, não é caso de recusa do Bloco C).
504
+ if (Array.isArray(step.knowledge) && step.knowledge.length) {
505
+ lines.push(` knowledge: [${step.knowledge.join(', ')}]`);
506
+ }
507
+ lines.push(` depends_on: ${dependsOn}`);
508
+ lines.push(` checkpoint: ${checkpoint}`);
509
+ lines.push(' veto:');
510
+ lines.push(' assertions:');
511
+ // Veto ORTOGONAL ao template (ADR-005): default = asserção de conteúdo declarado
512
+ // (campo de máquina `confianca` fora dos headings). Vetos de domínio declarados na
513
+ // spec (step.veto[]) SUBSTITUEM o default (sempre ≥1). Nunca section_exists de
514
+ // seção do próprio template (implicado pela conformidade de forma).
515
+ const assertions = (Array.isArray(step.veto) && step.veto.length)
516
+ ? step.veto
517
+ : [{ type: 'field_matches', field: 'confianca', pattern: '^(alta|media|baixa)$' }];
518
+ for (const a of assertions) lines.push(` ${serializeAssertion(a)}`);
519
+ // SF7.3/ADR-016 — emissão OPT-IN de anti_path: só quando a spec da etapa o declara
520
+ // (ausente por padrão — diferente de `confianca`, que é default por ortogonalidade).
521
+ if (Array.isArray(step.anti_path) && step.anti_path.length) {
522
+ lines.push(' anti_path:');
523
+ for (const it of step.anti_path) {
524
+ const mb = (it.mechanized_by == null) ? 'null' : encAntiScalar(it.mechanized_by);
525
+ lines.push(` - { rule: ${encAntiScalar(it.rule)}, mechanized_by: ${mb} }`);
526
+ }
527
+ }
528
+ lines.push('');
529
+ });
530
+ return lines.join('\n');
531
+ }
532
+
533
+ /** Escalar do anti_path para o flow-map inline (aspas só quando não é token simples). */
534
+ function encAntiScalar(v) {
535
+ const s = String(v);
536
+ return /^[A-Za-z0-9_-]+$/.test(s) ? s : JSON.stringify(s);
537
+ }
538
+
539
+ /** squad-io.yaml — contrato de handoff do pod (inputs↔outputs entre etapas). */
540
+ function buildSquadIo(pod, steps) {
541
+ const lines = [];
542
+ lines.push(`# squad-io.yaml — contrato de handoff do pod \`${pod}\` (SF1.4).`);
543
+ lines.push(`# Cada handoff liga o output de uma etapa ao input da etapa a jusante.`);
544
+ lines.push(`pod: ${pod}`);
545
+ lines.push('handoffs:');
546
+ for (let i = 1; i < steps.length; i++) {
547
+ lines.push(` - from: ${steps[i - 1].id}`);
548
+ lines.push(` to: ${steps[i].id}`);
549
+ lines.push(` artifact: ${steps[i - 1].output}`);
550
+ }
551
+ if (steps.length < 2) lines.push(' [] # pod de etapa única — sem handoff interno');
552
+ lines.push('');
553
+ return lines.join('\n');
554
+ }
555
+
556
+ /** Normaliza a lista de etapas a partir da spec (agents ordenados = etapas). */
557
+ function stepsFromSpec(spec) {
558
+ const agents = Array.isArray(spec.agents) ? spec.agents : [];
559
+ const steps = agents.map((a, i) => {
560
+ const name = typeof a === 'string' ? a : a.name;
561
+ const id = slug((typeof a === 'object' && a.step) || name || `step-${i + 1}`);
562
+ const output = (typeof a === 'object' && a.output) || `${id}-artifact`;
563
+ // ADR-005: propaga as seções do template e o veto de domínio declarados na spec.
564
+ const sections = (typeof a === 'object' && Array.isArray(a.sections)) ? a.sections : undefined;
565
+ const veto = (typeof a === 'object' && Array.isArray(a.veto)) ? a.veto : undefined;
566
+ // ADR-005/SF3.4: propaga o checkpoint por-etapa declarado na spec (mid ou final).
567
+ const checkpoint = (typeof a === 'object' && a.checkpoint && typeof a.checkpoint === 'object') ? a.checkpoint : undefined;
568
+ // SF7.3/ADR-016: propaga anti_path da etapa da spec (opt-in). A emissão pelo
569
+ // buildWorkflowYaml só é ALCANÇÁVEL pela rota real (generatePod→stepsFromSpec) se o
570
+ // campo for propagado aqui — antes era descartado (CONCERNS-1). Valida a forma na
571
+ // ENTRADA da spec (fail-loud, coerente com o estático): item malformado ou mechanized_by
572
+ // sem id correspondente no veto da etapa recusa a geração.
573
+ const anti_path = (typeof a === 'object' && Array.isArray(a.anti_path))
574
+ ? validateSpecAntiPath(a.anti_path, veto, id)
575
+ : undefined;
576
+ // SF12.4a/ADR-030 Bloco D (AC11) — propaga agents[i].knowledge (opcional, retro-
577
+ // compat: ausente ⇒ undefined ⇒ nenhuma chave `knowledge:` emitida, AC14).
578
+ const knowledge = (typeof a === 'object' && Array.isArray(a.knowledge))
579
+ ? a.knowledge.map(String)
580
+ : undefined;
581
+ return { id, agent: name, output, sections, veto, checkpoint, anti_path, knowledge };
582
+ });
583
+ return injectRitmoSkipModes(spec, steps);
584
+ }
585
+
586
+ /**
587
+ * SF12.6/ADR-030 Bloco D (Decisão 6.1-6.5) — `ritmo` INJETA checkpoint.skip_modes sobre um
588
+ * checkpoint JÁ AUTORADO (mesmo padrão de contrato_entrada/entregavel, Blocos G/H — mas
589
+ * aplicado AQUI, em `stepsFromSpec`, não em `generatePod`: a emissão precisa ser alcançável
590
+ * também pela rota direta `stepsFromSpec → buildWorkflowYaml`, sem passar por `generatePod`
591
+ * — AC23). NUNCA fabrica `criteria` (Decisão 1): RECUSA quando a etapa-alvo não tem
592
+ * checkpoint autorado — inclusive quando o único "checkpoint" seria só o DEFAULT sintetizado
593
+ * por `buildWorkflowYaml` na última etapa (nunca aparece aqui: esse default só é aplicado
594
+ * DEPOIS, dentro de `buildWorkflowYaml` — Decisão 6.5, `DEBT-SF12.6-F`, sem AC próprio).
595
+ * Muta `steps` em memória (array recém-construído por este mesmo `stepsFromSpec`) — nunca a
596
+ * spec de entrada. Mutação silenciosa quando `ritmo` está ausente/recusado/malformado: a
597
+ * disciplina de FORMA (fail-loud sob `specOrigin:'authored'`) é do Bloco C/`readCanonicalField`,
598
+ * não deste ponto — aqui só se decide SE a injeção é POSSÍVEL sobre o alvo referenciado.
599
+ */
600
+ function injectRitmoSkipModes(spec, steps) {
601
+ const ritmo = spec && spec.ritmo;
602
+ const isCandidate = ritmo && typeof ritmo === 'object' && !Array.isArray(ritmo) && ritmo.nao_se_aplica !== true
603
+ && typeof ritmo.step === 'string' && ritmo.step.trim() !== ''
604
+ && Array.isArray(ritmo.skip_modes) && ritmo.skip_modes.length
605
+ && ritmo.skip_modes.every((m) => typeof m === 'string' && m.trim() !== '');
606
+ if (!isCandidate) return steps;
607
+ validateCanonicalStepRef('ritmo', ritmo.step, steps); // AC21c — referência a etapa inexistente RECUSA.
608
+ const idx = steps.findIndex((s) => s.id === ritmo.step);
609
+ const targetCp = steps[idx].checkpoint;
610
+ if (!targetCp || typeof targetCp !== 'object' || typeof targetCp.criteria !== 'string' || targetCp.criteria.trim() === '') {
611
+ throw new CanonicalFieldError('ritmo', `aponta para a etapa "${ritmo.step}" que NÃO tem checkpoint ` +
612
+ 'autorado (blocking+criteria escritos por humano) — "ritmo" só INJETA skip_modes sobre um gate que já ' +
613
+ 'existe; a máquina NUNCA fabrica criteria (ADR-030 Decisão 1/6.1/6.5, AC21b/DEBT-SF12.6-F).');
614
+ }
615
+ steps[idx] = { ...steps[idx], checkpoint: { ...targetCp, skip_modes: ritmo.skip_modes } };
616
+ return steps;
617
+ }
618
+
619
+ /** Valida a forma do anti_path na entrada da spec (fail-loud). Retorna o array validado. */
620
+ function validateSpecAntiPath(ap, veto, stepId) {
621
+ const ids = new Set((Array.isArray(veto) ? veto : [])
622
+ .filter((a) => a && typeof a.id === 'string' && a.id.trim()).map((a) => a.id));
623
+ ap.forEach((item, i) => {
624
+ if (!item || typeof item !== 'object' || Array.isArray(item)) {
625
+ throw new Error(`anti_path[${i}] da etapa "${stepId}" deve ser um mapa { rule, mechanized_by }`);
626
+ }
627
+ if (typeof item.rule !== 'string' || item.rule.trim() === '') {
628
+ throw new Error(`anti_path[${i}].rule da etapa "${stepId}" ausente ou vazio`);
629
+ }
630
+ if (item.mechanized_by != null) {
631
+ if (typeof item.mechanized_by !== 'string' || !ids.has(item.mechanized_by)) {
632
+ throw new Error(`anti_path[${i}].mechanized_by "${item.mechanized_by}" da etapa "${stepId}" não referencia o id de nenhuma veto.assertion declarada na spec desta etapa`);
633
+ }
634
+ }
635
+ });
636
+ return ap;
637
+ }
638
+
639
+ /**
640
+ * Gera os artefatos do pod em `podDir`, proporcionais ao tier.
641
+ * Retorna { tier, written[], skipped[] } — written = paths criados (disco-first).
642
+ */
643
+ // ---------------------------------------------------------------------------
644
+ // SF12.4a/ADR-030 Bloco I — o preço do L1: stub de agente + sidecar `.smoke.json`
645
+ // sintetizado. Síntese ESTRUTURAL (satisfaz a FORMA das veto.assertions declaradas),
646
+ // nunca redação de conteúdo — mesma fronteira ADR-002 do resto do framework.
647
+ // ---------------------------------------------------------------------------
648
+
649
+ class KnowledgeFileMissingError extends Error {
650
+ constructor(stepId, file) {
651
+ super(`etapa "${stepId}": knowledge declarado "${file}" não existe em <podDir>/knowledge/${file} — ` +
652
+ `o gerador CONFERE existência, não cria (pipeline.md Fase 3: knowledge é autorado ANTES de invocar ` +
653
+ `forge-generate.cjs). AC13/ADR-030 Bloco D.`);
654
+ this.name = 'KnowledgeFileMissingError';
655
+ this.exitCode = 2;
656
+ }
657
+ }
658
+
659
+ /** AC13 — pré-condição que o motor verifica no dispatch, conferida NA GERAÇÃO. */
660
+ function assertKnowledgeExists(steps, podDir) {
661
+ for (const step of steps) {
662
+ if (!Array.isArray(step.knowledge)) continue;
663
+ for (const k of step.knowledge) {
664
+ const abs = path.join(podDir, 'knowledge', k);
665
+ if (!fs.existsSync(abs)) throw new KnowledgeFileMissingError(step.id, k);
666
+ }
667
+ }
668
+ }
669
+
670
+ class UnsynthesizableVetoError extends Error {
671
+ constructor(stepId, assertion) {
672
+ const extra = assertion.type === 'field_matches'
673
+ ? ` com pattern "${assertion.pattern}" (o sintetizador só deriva alternação simples "^(a|b|c)$" ou literal puro)`
674
+ : '';
675
+ super(`sidecar de smoke (Bloco I): a etapa "${stepId}" declara veto.assertions do tipo "${assertion.type}"${extra} ` +
676
+ `que o sintetizador não sabe satisfazer — RECUSA fail-loud (não gera .smoke.json/cenário inválido; ` +
677
+ `um sidecar que não prova nada seria pior que ausente — mesma doutrina do Bloco C, AC28).`);
678
+ this.name = 'UnsynthesizableVetoError';
679
+ this.exitCode = 2;
680
+ this.stepId = stepId;
681
+ this.assertionType = assertion.type;
682
+ }
683
+ }
684
+
685
+ const SYNTHESIZABLE_VETO_TYPES = new Set(['field_matches', 'count_gte', 'file_referenced_exists', 'section_exists']);
686
+
687
+ /** Deriva um valor literal que CASA o pattern — só alternação simples "^(a|b|c)$" ou literal puro. Senão, null. */
688
+ function deriveFieldMatchesValue(pattern) {
689
+ let p = String(pattern == null ? '' : pattern);
690
+ if (p.startsWith('^')) p = p.slice(1);
691
+ if (p.endsWith('$')) p = p.slice(0, -1);
692
+ const alt = p.match(/^\(([^()]+)\)$/);
693
+ if (alt) {
694
+ const first = alt[1].split('|')[0];
695
+ if (first && !/[\\.*+?[\]{}^$]/.test(first)) return first;
696
+ return null;
697
+ }
698
+ if (p && !/[\\.*+?()[\]{}^$|]/.test(p)) return p;
699
+ return null;
700
+ }
701
+
702
+ /** Escapa um literal para uso seguro dentro de uma alternação de regex `(a|b|c)`. */
703
+ function escapeRegexAlt(v) {
704
+ return String(v).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
705
+ }
706
+
707
+ function stepAssertions(step) {
708
+ return (Array.isArray(step.veto) && step.veto.length)
709
+ ? step.veto
710
+ : [{ type: 'field_matches', field: 'confianca', pattern: '^(alta|media|baixa)$' }];
711
+ }
712
+
713
+ function stepSections(step) {
714
+ return Array.isArray(step.sections) && step.sections.length ? step.sections : ['## Contexto', '## Output'];
715
+ }
716
+
717
+ /** AC28 — recusa fail-loud ANTES de qualquer escrita se algum veto declarado não é sintetizável. */
718
+ function assertSynthesizableVeto(steps) {
719
+ for (const step of steps) {
720
+ for (const a of stepAssertions(step)) {
721
+ if (!SYNTHESIZABLE_VETO_TYPES.has(a.type)) throw new UnsynthesizableVetoError(step.id, a);
722
+ if (a.type === 'field_matches' && deriveFieldMatchesValue(a.pattern) == null) {
723
+ throw new UnsynthesizableVetoError(step.id, a);
724
+ }
725
+ }
726
+ }
727
+ }
728
+
729
+ /**
730
+ * Sintetiza o texto de UM artefato de etapa: `{}` = conforme (caminho feliz);
731
+ * `{ dropFirstSection: true }` = quebra de FORMA (template_violation, AC27);
732
+ * `{ breakAssertion: <assertion> }` = quebra de UMA assertion nomeada (veto_violation,
733
+ * AC27/AC27b — nunca uma assertion genérica/arbitrária).
734
+ */
735
+ function synthesizeArtifactText(step, opts = {}) {
736
+ const sections = stepSections(step);
737
+ const useSections = opts.dropFirstSection ? sections.slice(1) : sections;
738
+ const assertions = stepAssertions(step);
739
+ const countGte = assertions.filter((a) => a.type === 'count_gte');
740
+
741
+ const body = useSections.map((s) => {
742
+ const label = String(s).replace(/^#+\s*/, '').trim();
743
+ const cg = countGte.find((c) => label.toLowerCase().includes(String(c.field).toLowerCase())
744
+ || String(c.field).toLowerCase().includes(label.toLowerCase()));
745
+ if (cg) {
746
+ const broken = opts.breakAssertion === cg;
747
+ const n = broken ? 0 : Math.max(1, Number(cg.n) || 1);
748
+ const items = Array.from({ length: n }, (_, i) => `- item de canário ${i + 1}`).join('\n');
749
+ return `${s}\n${items ? `${items}\n` : ''}`;
750
+ }
751
+ return `${s}\nConteúdo de canário (sintetizado por forge-generate.cjs, ADR-030 Bloco I — síntese ESTRUTURAL, não redação).\n`;
752
+ }).join('\n');
753
+
754
+ const fieldLines = [];
755
+ const seen = new Set();
756
+ for (const a of assertions) {
757
+ if (a.type === 'field_matches') {
758
+ const val = deriveFieldMatchesValue(a.pattern);
759
+ const broken = opts.breakAssertion === a;
760
+ fieldLines.push(`${a.field}: ${broken ? `${val}--quebrado-pelo-smoke` : val}`);
761
+ seen.add(a.field);
762
+ } else if (a.type === 'file_referenced_exists') {
763
+ const broken = opts.breakAssertion === a;
764
+ // O agente-stub do AC26 garante existência real de agents/<agent>.md sob podRoot
765
+ // (base de resolução confirmada pelo `*review` — pc-run-pod.cjs:466).
766
+ fieldLines.push(`${a.field}: ${broken ? 'arquivo-inexistente-smoke.md' : `agents/${step.agent}.md`}`);
767
+ seen.add(a.field);
768
+ }
769
+ }
770
+ if (!seen.has('confianca')) fieldLines.push('confianca: alta');
771
+
772
+ return `${body}\n${fieldLines.join('\n')}\n`;
773
+ }
774
+
775
+ /**
776
+ * Monta UM sidecar `.smoke.json` (schema medido em `wf-forge.smoke.json` — Contexto (f)
777
+ * da story): `artifacts` conforme por etapa (TODAS as etapas do wf); `template_violation`/
778
+ * `veto_violation` da etapa-alvo. `breakField`, quando dado, nomeia a assertion a quebrar
779
+ * (AC27b — mecanismo específico); default: a 1ª assertion da etapa-alvo (AC27 — base).
780
+ */
781
+ function buildSmokeSidecar(steps, targetStepId, breakField) {
782
+ const artifacts = {};
783
+ for (const step of steps) artifacts[step.id] = synthesizeArtifactText(step);
784
+ const targetStep = steps.find((s) => s.id === targetStepId);
785
+ const templateViolation = synthesizeArtifactText(targetStep, { dropFirstSection: true });
786
+ const assertions = stepAssertions(targetStep);
787
+ const breakAssertion = (breakField && assertions.find((a) => a.field === breakField)) || assertions[0];
788
+ const vetoViolation = synthesizeArtifactText(targetStep, { breakAssertion });
789
+ return {
790
+ _comment: 'Cenário de smoke SINTETIZADO por forge-generate.cjs (SF12.4a/ADR-030 Bloco I) — canário ' +
791
+ 'ESTRUTURAL (satisfaz a forma das veto.assertions declaradas), não redação de conteúdo (fronteira ADR-002). ' +
792
+ `target_step="${targetStepId}"${breakField ? ` — cenário do mecanismo "${breakField}" (AC27b)` : ' — sidecar BASE (AC27)'}.`,
793
+ target_step: targetStepId,
794
+ artifacts,
795
+ template_violation: templateViolation,
796
+ veto_violation: vetoViolation,
797
+ };
798
+ }
799
+
800
+ /** AC26 — frontmatter + 3 blocos mínimos (Papel/Comandos/Regras de ouro), dentro do teto de validate-budgets.js. */
801
+ function agentStubContent(name) {
802
+ return `---
803
+ name: ${name}
804
+ description: "Stub gerado por forge-generate.cjs (SF12.4a/ADR-030 Bloco I). Use para: pod executável até o forge-chief preencher o conteúdo real."
805
+ model: sonnet
806
+ budget_tokens: 400
807
+ ---
808
+
809
+ # Papel
810
+
811
+ Stub gerado automaticamente para que o pod seja EXECUTÁVEL pelo motor real (o runner
812
+ exige \`agents/<nome>.md\` — ADR-030 M2/DF-SF12.4-1). Conteúdo REAL é autoria do
813
+ \`forge-chief\` em prosa (\`DEBT-SF12.4-F\`) — este stub não é bom, é estruturalmente válido.
814
+
815
+ ## Comandos
816
+
817
+ - *comando-exemplo: preencher com os comandos reais deste agente.
818
+
819
+ ## Regras de ouro
820
+
821
+ - Preencher com as regras de ouro reais deste agente antes de considerar o pod pronto para uso.
822
+ `;
823
+ }
824
+
825
+ // ---------------------------------------------------------------------------
826
+ // SF12.4a/ADR-030 Bloco J (`DF-SF12.4-5`/`-6`) — camada do dono, SÓ PLACEHOLDER. Nunca
827
+ // escreve instância (`dono.yaml`); emite `knowledge/dono.example.yaml` com sentinela
828
+ // `pc:placeholder`, no MESMO formato estrutural de `content-carousel/knowledge/dono.example.yaml`
829
+ // (generalizado — sem os campos específicos de carrossel/CTA).
830
+ // ---------------------------------------------------------------------------
831
+ const ESCADA_DE_VOZ_VALUES = new Set(['sem_voz', 'guia_de_estilo', 'dna_completo']);
832
+
833
+ function donoExampleContent(pod) {
834
+ return `# dono.example.yaml — forma declarada da configuração de dono do pod \`${pod}\` (ADR-010 + ADR-013).
835
+ # O template NUNCA distribui dado pessoal real: este arquivo é o placeholder.
836
+ # pc:placeholder <- SENTINELA (ADR-027): enquanto esta marca existir, o arquivo
837
+ # ainda NÃO foi personalizado. Apague-a ao preencher de verdade.
838
+ # É ela que o oráculo de contenção do framework exige de todo
839
+ # arquivo da camada do dono que entra no pacote.
840
+ # Na instância: copie para \`knowledge/dono.yaml\` e preencha. Se \`dono.yaml\` estiver
841
+ # ausente ou ainda igual a este placeholder, o agente ELICITA os dados na 1ª ativação.
842
+ #
843
+ # Gerado por forge-generate.cjs (SF12.4a/ADR-030 Bloco J) — SÓ emissão de placeholder,
844
+ # NUNCA escrita de instância (\`DF-SF12.4-6\`, vinculante).
845
+ dono:
846
+ nome: "<Nome do Dono>" # quem assina a peça (nome civil ou de marca)
847
+ handle: "<@handle>" # handle do perfil onde a peça é publicada
848
+ autoridade: "<uma linha factual de enquadramento de autoridade, sem persona nem promessa que o conteúdo não sustente>"
849
+ # Guia de estilo do dono (ADR-013) — degrau intermediário da escada de voz
850
+ # (sem voz / GUIA DE ESTILO / DNA completo do dono). Machine-readable.
851
+ voz:
852
+ pessoa: "primeira" # o dono é o AUTOR; NUNCA citado em 3ª pessoa como fonte externa
853
+ lexico_proibido: ["—", "–"] # PISO do framework (classe P) — subir é livre, descer exige atribuição explícita
854
+ tom: "<uma linha: registro e energia da voz do dono, ex.: direto, técnico sem jargão, sem hype>"
855
+ `;
856
+ }
857
+
858
+ function generatePod(spec, podDir, opts = {}) {
859
+ // SF12.4a/ADR-030 Bloco O (AC48) — PRIMEIRA checagem, antes de qualquer outra.
860
+ validateSpecOrigin(opts.specOrigin);
861
+ const specOrigin = opts.specOrigin;
862
+ // C.1/SF3.1 — tierOverride INTERCEPTA a classificação: com override presente,
863
+ // NÃO re-classifica pela spec isolada (que ignoraria o pod existente). O modo
864
+ // aditivo e o upgrade-in-place SEMPRE passam o tier AGREGADO do disco. Sem
865
+ // override, comportamento SF1.4 inalterado (classifica pela spec recebida).
866
+ const tier = opts.tierOverride != null ? opts.tierOverride : classifyTier(spec).tier;
867
+ const steps = stepsFromSpec(spec);
868
+ const workflowId = slug(spec.workflowId || spec.pod || 'main');
869
+ const written = [];
870
+ const skipped = [];
871
+
872
+ // dry-run (SF6.2): computa o PLANO (written[]/skipped[] preenchidos) sem tocar disco.
873
+ // Requisito da diretiva ADR-004 §2: forge-upgrade-pod em dry-run = zero escrita.
874
+ const dryRun = Boolean(opts.dryRun);
875
+ const emit = (file, content) => { if (!dryRun) writeDisk(file, content); };
876
+
877
+ // Forma STANDARD-interface (ADR-004 adendo): `interface:true` só é válido com zero
878
+ // handoff interno. Fail-loud ANTES de qualquer escrita (vale também em dry-run).
879
+ const isInterface = spec.interface === true;
880
+ if (isInterface && Number(spec.handoffs || 0) !== 0) {
881
+ throw new InterfaceContractError(Number(spec.handoffs || 0));
882
+ }
883
+ // Memória em SIMPLE é OPCIONAL (SPEC §G2 tabela — "memória opcional", não proibida):
884
+ // habilitada só sob pedido explícito via flag CLI `--with-memory` (opts.withMemory)
885
+ // ou campo `spec.memoryOpt: true`. STANDARD/COMPLEX sempre recebe.
886
+ const withMemory = Boolean(opts.withMemory) || spec.memoryOpt === true;
887
+
888
+ // Modo aditivo: `owns[]` (resolvidos a absoluto) são os paths que ESTA linha
889
+ // possui — regeneráveis; qualquer outro artefato pré-existente é intocável.
890
+ const additive = Boolean(opts.additive);
891
+ const ownsSet = new Set((opts.owns || []).map((p) => path.resolve(podDir, p)));
892
+ const merged = [];
893
+
894
+ // SF12.6/ADR-030 Bloco C — leniência POR PATH (Decisão 7.2), forma nua recusada.
895
+ const allowDivergentOverwrite = validateAllowDivergentOverwrite(opts.allowDivergentOverwrite);
896
+ const allowSet = new Set(allowDivergentOverwrite.map((p) => path.resolve(p)));
897
+ const divergent = [];
898
+ const preserved = [];
899
+ // Guarda aditiva: existe + não-owned ⇒ colisão (exit 2). Existe + owned ⇒ regenera.
900
+ const guardCollision = (abs, kind) => {
901
+ if (!additive) return;
902
+ if (!fs.existsSync(abs)) return;
903
+ if (ownsSet.has(path.resolve(abs))) return; // owned → retomada idempotente (regenera determinístico)
904
+ throw new AdditiveCollisionError(path.relative(podDir, abs), kind);
905
+ };
906
+
907
+ // SF12.4a/ADR-030 Bloco D (AC13) — pré-condição de knowledge ANTES de qualquer escrita.
908
+ assertKnowledgeExists(steps, podDir);
909
+
910
+ // SF12.4a/ADR-030 Blocos C/E/G/H/O (AC7-AC25, AC42-AC44, AC48b/AC48c) — os 3 campos
911
+ // canônicos (ritmo, contrato_entrada, entregavel). `specOrigin: 'authored'` ⇒ recusa
912
+ // fail-loud de campo AUSENTE ou malformado, ANTES de qualquer escrita; `'derived'` ⇒
913
+ // preserva o comportamento atual e reporta o que falta (leniência que FALA).
914
+ if (specOrigin === 'authored') {
915
+ for (const field of CANONICAL_FIELDS) readCanonicalField(spec, field, steps);
916
+ }
917
+ const canonicalStatus = computeCanonicalStatus(spec);
918
+
919
+ // stepExtraFields: stepId -> [{ field, placeholder }] — campos de máquina extras
920
+ // (Blocos G/H) a injetar no template da etapa-alvo, ANTES de `confianca`.
921
+ const stepExtraFields = new Map();
922
+
923
+ // Bloco G (AC20-22) — contrato_entrada declarado: campo de máquina + veto field_matches.
924
+ if (canonicalStatus.contrato_entrada.mode === 'declared'
925
+ && typeof canonicalStatus.contrato_entrada.step === 'string'
926
+ && Array.isArray(canonicalStatus.contrato_entrada.valores) && canonicalStatus.contrato_entrada.valores.length >= 2) {
927
+ const { step: targetId, valores } = canonicalStatus.contrato_entrada;
928
+ const idx = steps.findIndex((s) => s.id === targetId);
929
+ if (idx === -1) {
930
+ throw new CanonicalFieldError('contrato_entrada', `referencia a etapa "${targetId}" que não existe entre os ids de stepsFromSpec(spec)`);
931
+ }
932
+ const pattern = `^(${valores.map(escapeRegexAlt).join('|')})$`;
933
+ const baseVeto = (Array.isArray(steps[idx].veto) && steps[idx].veto.length)
934
+ ? steps[idx].veto
935
+ : [{ type: 'field_matches', field: 'confianca', pattern: '^(alta|media|baixa)$' }];
936
+ steps[idx] = {
937
+ ...steps[idx],
938
+ veto: [...baseVeto, {
939
+ type: 'field_matches', field: 'contrato_entrada', pattern,
940
+ _sources: 'ADR-030 Bloco G (DF-SF12.4-1) — âncora de MÁQUINA do padrão contrato_entrada, declarado na spec (mesma forma de content-carousel/workflows/wf-content-carousel.yaml).',
941
+ }],
942
+ };
943
+ stepExtraFields.set(targetId, [...(stepExtraFields.get(targetId) || []), { field: 'contrato_entrada', placeholder: valores.join('|') }]);
944
+ }
945
+
946
+ // Bloco H (AC23-25) — entregavel declarado: campo de máquina + veto file_referenced_exists.
947
+ if (canonicalStatus.entregavel.mode === 'declared' && typeof canonicalStatus.entregavel.step === 'string') {
948
+ const targetId = canonicalStatus.entregavel.step;
949
+ const idx = steps.findIndex((s) => s.id === targetId);
950
+ if (idx === -1) {
951
+ throw new CanonicalFieldError('entregavel', `referencia a etapa "${targetId}" que não existe entre os ids de stepsFromSpec(spec)`);
952
+ }
953
+ const baseVeto = (Array.isArray(steps[idx].veto) && steps[idx].veto.length)
954
+ ? steps[idx].veto
955
+ : [{ type: 'field_matches', field: 'confianca', pattern: '^(alta|media|baixa)$' }];
956
+ steps[idx] = {
957
+ ...steps[idx],
958
+ veto: [...baseVeto, {
959
+ type: 'file_referenced_exists', field: 'entregavel',
960
+ _sources: 'ADR-030 Bloco H (DF-SF12.4-1) — forma GENÉRICA já em produção (automation/wf-n8n-build.yaml:64), declarada na spec.',
961
+ }],
962
+ };
963
+ stepExtraFields.set(targetId, [...(stepExtraFields.get(targetId) || []), { field: 'entregavel', placeholder: 'path/relativo/ao/entregável' }]);
964
+ }
965
+
966
+ // wf — SIMPLE: no máx. 1 workflow mínimo (só se ≥2 etapas; comando/agente único ⇒
967
+ // nenhum). STANDARD/COMPLEX: sempre. EXCEÇÃO (ADR-004 adendo): pod-interface (zero
968
+ // handoff interno) NÃO fabrica workflow nem squad-io. Calculado AQUI (hoisted) porque
969
+ // o Bloco D (injeção de ritmo) e o Bloco I (AC28) precisam dele ANTES de qualquer escrita.
970
+ const wantsWorkflow = !isInterface && (tier !== TIER_SIMPLE || steps.length >= 2);
971
+
972
+ // SF12.6/ADR-030 Bloco D (Decisão 6.3) — a INJEÇÃO de fato (mutação de `steps[idx].checkpoint`
973
+ // + recusa quando a etapa-alvo não tem checkpoint autorado) já aconteceu dentro de
974
+ // `stepsFromSpec` (função `injectRitmoSkipModes`, acima) — precisa ser alcançável também
975
+ // pela rota direta `stepsFromSpec → buildWorkflowYaml`, sem passar por `generatePod` (AC23).
976
+ // Se chegamos aqui com `canonicalStatus.ritmo.mode === 'declared'` e forma válida, a injeção
977
+ // JÁ teve sucesso (senão `stepsFromSpec`, chamado no topo desta função, já teria lançado) —
978
+ // este bloco só ANUNCIA (nunca duplica a regra).
979
+ const ritmoAnnouncement = (canonicalStatus.ritmo.mode === 'declared'
980
+ && typeof canonicalStatus.ritmo.step === 'string'
981
+ && Array.isArray(canonicalStatus.ritmo.skip_modes) && canonicalStatus.ritmo.skip_modes.length)
982
+ ? `[ritmo] etapa "${canonicalStatus.ritmo.step}" declarou checkpoint.skip_modes ` +
983
+ `${JSON.stringify(canonicalStatus.ritmo.skip_modes)} — EMITIDO no wf gerado, injetado sobre o ` +
984
+ 'checkpoint JÁ AUTORADO (ADR-030 Bloco D, Decisão 6.3). Registrado em pod-spec.json._canonical_status.ritmo.'
985
+ : null;
986
+ if (ritmoAnnouncement) console.log(ritmoAnnouncement);
987
+
988
+ // Bloco O (AC48c) — modo `derived`: leniência que FALA, nos 2 modos (dry-run e --apply,
989
+ // ambos passam por aqui — a checagem não depende de `dryRun`) e mesmo sem pod-spec.json
990
+ // pré-existente (a spec RECEBIDA é a fonte, nunca leitura de arquivo em disco). SF12.6/
991
+ // AC27/`DEBT-SF12.5b-B`: quando `spec._<campo>_deferido` está presente (marcador irmão
992
+ // inerte, mesmo que o campo canônico fique ausente), a mensagem DISCRIMINA "deferido por X"
993
+ // de "ausente, legado" — não é a mesma frase genérica para os dois casos.
994
+ if (specOrigin === 'derived') {
995
+ for (const field of CANONICAL_FIELDS) {
996
+ if (canonicalStatus[field].mode === 'ausente') {
997
+ const deferido = spec[`_${field}_deferido`];
998
+ const razaoOk = deferido && typeof deferido === 'object' && typeof deferido.razao === 'string' && deferido.razao.trim() !== '';
999
+ if (razaoOk) {
1000
+ console.log(`[specOrigin:derived] campo canônico "${field}" AUSENTE, mas deferido-com-registro: ` +
1001
+ `${deferido.razao}` +
1002
+ (typeof deferido.respondido_por === 'string' && deferido.respondido_por.trim() ? ` (respondido por ${deferido.respondido_por})` : '') +
1003
+ ' — não é esquecimento, é decisão registrada (ADR-030, paga DEBT-SF12.5b-B).');
1004
+ } else {
1005
+ console.log(`[specOrigin:derived] campo canônico ausente no pod pré-existente: "${field}" ` +
1006
+ '(DEBT-SF12.4-B — pod anterior à forma canônica; declare ou recuse quando levá-lo à forma canônica, ADR-030 Emenda 1).');
1007
+ }
1008
+ }
1009
+ }
1010
+ }
1011
+
1012
+ // SF12.4a/ADR-030 Bloco I (AC28) — recusa fail-loud ANTES de qualquer escrita, se algum
1013
+ // veto declarado não é sintetizável (mesma disciplina do Bloco C: um artefato inválido
1014
+ // seria pior que ausente). Só quando o Bloco I de fato vai rodar (`specOrigin:'authored'`
1015
+ // + workflow) — nunca penaliza specs `derived`/sem workflow por um tipo de veto que o
1016
+ // sintetizador não precisa satisfazer.
1017
+ if (specOrigin === 'authored' && wantsWorkflow && steps.length >= 1) {
1018
+ assertSynthesizableVeto(steps);
1019
+ }
1020
+
1021
+ // SF12.6/ADR-030 Bloco C+D — o `wf` é computado UMA VEZ aqui (reusado no write real do
1022
+ // passo (2) abaixo) para poder: (i) auto-validar a PRÓPRIA saída via checkCheckpoint
1023
+ // (AC25/DEBT-SF12.6-E) e (ii) recusar fail-loud ANTES de escrever QUALQUER arquivo do pod
1024
+ // se divergir do disco (AC11) — nem os templates que não divergiam (por isso este bloco
1025
+ // roda ANTES do passo (1)/templates, não junto do passo (2)/workflow).
1026
+ let wfContent = null;
1027
+ const wfPath = (wantsWorkflow && steps.length >= 1)
1028
+ ? path.join(podDir, 'workflows', `wf-${workflowId}.yaml`)
1029
+ : null;
1030
+ if (wfPath) {
1031
+ wfContent = buildWorkflowYaml(workflowId, steps);
1032
+ try {
1033
+ checkCheckpoint(parseWfYaml(wfContent));
1034
+ } catch (e) {
1035
+ throw new CheckpointInvariantError(path.relative(podDir, wfPath), e.message);
1036
+ }
1037
+ // Guard por classe (Decisão 7.1): só sob `specOrigin:'derived'` (regeneração de um pod
1038
+ // já em disco) E path NÃO owned (harden-learning já tem autoridade de escrita mais
1039
+ // estreita e pré-existente via --additive + owns — o guard novo não a reabre).
1040
+ if (specOrigin === 'derived' && !ownsSet.has(path.resolve(wfPath)) && fs.existsSync(wfPath)) {
1041
+ const diskWf = fs.readFileSync(wfPath, 'utf8');
1042
+ if (diskWf !== wfContent) {
1043
+ divergent.push(wfPath);
1044
+ if (!allowSet.has(path.resolve(wfPath)) && !dryRun) {
1045
+ const diskLen = Buffer.byteLength(diskWf, 'utf8');
1046
+ const genLen = Buffer.byteLength(wfContent, 'utf8');
1047
+ throw new DivergentRegenerationError(path.relative(podDir, wfPath), diskLen, genLen, firstDivergentLine(diskWf, wfContent));
1048
+ }
1049
+ }
1050
+ }
1051
+ }
1052
+
1053
+ // (1) templates por etapa — SEMPRE (SIMPLE gera só das 1–2 saídas; a spec já limita a
1054
+ // contagem de agentes de um SIMPLE a ≤2). Disco-first. SF12.6/ADR-030 Bloco C: sob
1055
+ // `specOrigin:'derived'` (e path NÃO owned), um template pré-existente cujo conteúdo
1056
+ // diverge do que seria gerado é PRESERVADO por default (preserved[]) — nunca sobrescrito
1057
+ // numa regeneração (write-once, mesmo regime que generatePod já pratica em 5 outros
1058
+ // lugares — stub de agente, sidecar de smoke, sidecar de cenário, dono.example, README).
1059
+ for (const step of steps) {
1060
+ const tpl = path.join(podDir, 'templates', `${step.id}-tmpl.md`);
1061
+ const tplContent = stepTemplate(step.id, step.agent, step.output, step.sections, step.veto, stepExtraFields.get(step.id));
1062
+ if (specOrigin === 'derived' && !ownsSet.has(path.resolve(tpl)) && fs.existsSync(tpl)) {
1063
+ const diskTpl = fs.readFileSync(tpl, 'utf8');
1064
+ if (diskTpl !== tplContent) {
1065
+ preserved.push(tpl);
1066
+ continue; // preserva-por-default — não sobrescreve conteúdo curado.
1067
+ }
1068
+ }
1069
+ guardCollision(tpl, 'template/step-id'); // colisão de step-id/output fora de owns[] ⇒ exit 2
1070
+ emit(tpl, tplContent);
1071
+ written.push(tpl);
1072
+ }
1073
+
1074
+ // (2) workflow — `wfPath`/`wfContent` já computados acima (hoisted para o Bloco C/D/I).
1075
+ if (wfPath) {
1076
+ guardCollision(wfPath, 'workflowId'); // colisão de workflowId fora de owns[] ⇒ exit 2
1077
+ emit(wfPath, wfContent);
1078
+ written.push(wfPath);
1079
+ } else if (isInterface) {
1080
+ skipped.push('wf/squad-io (pod-interface: zero handoff interno; enforcement do crítico no ciclo pc-*)');
1081
+ } else {
1082
+ skipped.push('workflow (comando/agente único no caminho SIMPLE)');
1083
+ }
1084
+
1085
+ // SF12.4a/ADR-030 Bloco I — o preço do L1: stub de agente (AC26) + sidecar de smoke
1086
+ // sintetizado (AC27/AC27b/AC28). Só quando o pod TEM workflow (é o motor que consome
1087
+ // leitura obrigatória + smoke) E `specOrigin: 'authored'` (Decisão 7/Emenda 1: os
1088
+ // caminhos `derived` — `*upgrade-pod`/`*harden-learning` — PRESERVAM o comportamento
1089
+ // atual; impor a obrigação nova do L1 sobre um pod pré-existente regenerado é
1090
+ // exatamente o risco de regressão que a Decisão 7 existe para evitar — AC49/AC50).
1091
+ // Nunca sobrescreve artefato já existente (mesma guarda do README de memória) —
1092
+ // hand-authored (`*migrate-squad` F2a, `wf-forge.smoke.json`) ou já gerado antes,
1093
+ // preservado byte-a-byte.
1094
+ if (specOrigin === 'authored' && wantsWorkflow && steps.length >= 1) {
1095
+ const distinctAgents = [...new Set(steps.map((s) => s.agent))];
1096
+ for (const agentName of distinctAgents) {
1097
+ const agentPath = path.join(podDir, 'agents', `${agentName}.md`);
1098
+ if (!fs.existsSync(agentPath)) {
1099
+ emit(agentPath, agentStubContent(agentName));
1100
+ written.push(agentPath);
1101
+ }
1102
+ }
1103
+
1104
+ const lastStep = steps[steps.length - 1];
1105
+ const basePath = path.join(podDir, 'workflows', `wf-${workflowId}.smoke.json`);
1106
+ if (!fs.existsSync(basePath)) {
1107
+ // AC28 já foi checado ANTES de qualquer escrita (ver acima, hoisted com `wantsWorkflow`).
1108
+ emit(basePath, `${JSON.stringify(buildSmokeSidecar(steps, lastStep.id), null, 2)}\n`);
1109
+ written.push(basePath);
1110
+ }
1111
+
1112
+ // AC27b — 1 cenário adicional por mecanismo EMITIDO (contrato_entrada/entregavel),
1113
+ // SEMPRE — inclusive quando a etapa-alvo É a última: o sidecar BASE (AC27) quebra a
1114
+ // 1ª assertion da etapa por padrão (âncora L1 genérica), que pode NÃO ser a do
1115
+ // mecanismo; o cenário nomeado do AC27b é o único que GARANTE nomear a assertion do
1116
+ // mecanismo especificamente (AC39/AC40 dependem disso, `target_step` escalar único,
1117
+ // `smoke-pod-workflow.cjs:219`).
1118
+ const mechanismSteps = [];
1119
+ if (canonicalStatus.contrato_entrada.mode === 'declared' && canonicalStatus.contrato_entrada.step) {
1120
+ mechanismSteps.push({ field: 'contrato_entrada', stepId: canonicalStatus.contrato_entrada.step });
1121
+ }
1122
+ if (canonicalStatus.entregavel.mode === 'declared' && canonicalStatus.entregavel.step) {
1123
+ mechanismSteps.push({ field: 'entregavel', stepId: canonicalStatus.entregavel.step });
1124
+ }
1125
+ for (const m of mechanismSteps) {
1126
+ if (!steps.some((s) => s.id === m.stepId)) continue;
1127
+ const scenarioPath = path.join(podDir, 'workflows', `wf-${workflowId}.${m.stepId}.smoke.json`);
1128
+ if (!fs.existsSync(scenarioPath)) {
1129
+ emit(scenarioPath, `${JSON.stringify(buildSmokeSidecar(steps, m.stepId, m.field), null, 2)}\n`);
1130
+ written.push(scenarioPath);
1131
+ }
1132
+ }
1133
+ }
1134
+
1135
+ // (3) squad-io.yaml — SÓ STANDARD/COMPLEX NÃO-interface (SIMPLE e interface não
1136
+ // recebem contrato de handoff). SF12.6/ADR-030 Bloco C (Decisão 7.1, classe
1137
+ // squad-io.yaml → sempre-merge): squad-io pré-existente NUNCA é colisão nem
1138
+ // sobrescrita — SEMPRE faz MERGE por seção (C.2) quando o arquivo já existe, em
1139
+ // QUALQUER modo (a exigência de `additive:true` foi removida — `*upgrade-pod` nunca
1140
+ // passava `additive`, e por isso squad-io era sobrescrito incondicionalmente numa
1141
+ // regeneração, AC15).
1142
+ if (tier !== TIER_SIMPLE && !isInterface) {
1143
+ const io = path.join(podDir, 'squad-io.yaml');
1144
+ const content = buildSquadIo(spec.pod || workflowId, steps);
1145
+ // squad-io é POD-GLOBAL (agrega handoffs de TODOS os workflows). Merge é idempotente
1146
+ // (união dedup), seguro na retomada — nunca perde os handoffs/seções de outros workflows
1147
+ // nem conteúdo customizado hand-authored fora do gerador.
1148
+ if (fs.existsSync(io)) {
1149
+ emit(io, mergeSquadIo(fs.readFileSync(io, 'utf8'), content));
1150
+ merged.push(io);
1151
+ } else {
1152
+ emit(io, content);
1153
+ written.push(io);
1154
+ }
1155
+ } else if (!isInterface) {
1156
+ skipped.push('squad-io.yaml (SIMPLE não recebe contrato de handoff)');
1157
+ } // isInterface: wf+squad-io já cobertos pela mensagem combinada em (2).
1158
+
1159
+ // (4) MEMÓRIA OPERANTE proporcional ao tier (SF2.3, SPEC §G1/§G2). Substitui o
1160
+ // scaffold morto da SF1.4: a fábrica INSTALA a memória usando o MOTOR REAL
1161
+ // (pod-memory.cjs, SF2.1) — não hand-escreve um cabeçalho. O pod NASCE cabeado ao
1162
+ // mecanismo que o runner (SF2.2) consome: o consolidado fica no path canônico
1163
+ // (`memory/learnings.consolidated.md`, lido por getConsolidatedForBrief) e o log cru
1164
+ // (`memory/learnings.jsonl`) já existe para o append da run. "Operância" = o pod está
1165
+ // corretamente ligado ao motor pronto — nenhuma ação manual entre gerar e rodar.
1166
+ // STANDARD/COMPLEX → sempre. SIMPLE → só sob pedido explícito (withMemory).
1167
+ if (tier !== TIER_SIMPLE || withMemory) {
1168
+ written.push(...installMemory(spec.pod || workflowId, podDir, tier, { additive, dryRun }));
1169
+ } else {
1170
+ skipped.push('memória (SIMPLE sem --with-memory/spec.memoryOpt — opcional, não gerada)');
1171
+ }
1172
+
1173
+ // SF12.4a/ADR-030 Bloco J (AC29-32) — camada do dono, SÓ placeholder, quando
1174
+ // `escada_de_voz` indica pod de CONTEÚDO (qualquer valor ≠ "sem_voz"/ausente).
1175
+ // NUNCA escreve instância (`dono.yaml`) — `DF-SF12.4-6`, vinculante.
1176
+ const escadaDeVoz = ESCADA_DE_VOZ_VALUES.has(spec.escada_de_voz) ? spec.escada_de_voz : 'sem_voz';
1177
+ if (escadaDeVoz !== 'sem_voz') {
1178
+ const donoPath = path.join(podDir, 'knowledge', 'dono.example.yaml');
1179
+ if (!fs.existsSync(donoPath)) {
1180
+ emit(donoPath, donoExampleContent(spec.pod || workflowId));
1181
+ written.push(donoPath);
1182
+ }
1183
+ }
1184
+
1185
+ // SF12.4a/ADR-030 Bloco F (AC18-19) — pod-spec.json persistido na raiz do pod (fonte
1186
+ // da verdade, ADR-005; generalização da persistência que `*harden-learning` já fazia).
1187
+ // `_canonical_status` torna a recusa/anúncio/relato AUDITÁVEL por grep no artefato.
1188
+ if (!dryRun) {
1189
+ const specDest = path.join(podDir, 'pod-spec.json');
1190
+ const persisted = { ...spec, _canonical_status: canonicalStatus };
1191
+ emit(specDest, `${JSON.stringify(persisted, null, 2)}\n`);
1192
+ written.push(specDest);
1193
+ }
1194
+
1195
+ return { tier, workflowId, steps, written, skipped, merged, divergent, preserved, canonicalStatus, ritmoAnnouncement };
1196
+ }
1197
+
1198
+ /**
1199
+ * Instala a memória OPERANTE do pod usando o motor real pod-memory.cjs (SF2.1).
1200
+ * Cria o log cru vazio e materializa o consolidado canônico via `consolidate()` — o
1201
+ * mesmo caminho que o lifecycle usa em produção, provando que o pod está cabeado ao
1202
+ * motor (não a um cabeçalho morto). Idempotente. Retorna os paths gravados.
1203
+ */
1204
+ function installMemory(pod, podDir, tier, opts = {}) {
1205
+ const additive = Boolean(opts.additive);
1206
+ const dryRun = Boolean(opts.dryRun);
1207
+ const paths = podMemory.memoryPaths(pod, { podDir });
1208
+ const readme = path.join(paths.dir, 'README.md');
1209
+ // dry-run (SF6.2): retorna o PLANO (paths que SERIAM gravados) sem tocar disco.
1210
+ if (dryRun) return [paths.raw, paths.consolidated, readme];
1211
+ fs.mkdirSync(paths.dir, { recursive: true });
1212
+ // Log cru vazio (append-only, alimentado pelo runner). Não sobrescreve se já existir
1213
+ // (upgrade-in-place preserva histórico da SF2.1 — o runner é o único escritor).
1214
+ if (!fs.existsSync(paths.raw)) fs.writeFileSync(paths.raw, '');
1215
+ // Consolidado canônico via o MOTOR. No modo aditivo, um consolidado JÁ EXISTENTE é
1216
+ // PRESERVADO byte-a-byte (a migração NUNCA escreve na memória do pod destino, ADR-006
1217
+ // §F; o critério de integridade compara sha256 de learnings.jsonl+.consolidated.md).
1218
+ if (!(additive && fs.existsSync(paths.consolidated))) podMemory.consolidate(pod, { podDir });
1219
+ // Marcador de cabeamento. GUARD DE EXISTÊNCIA (M2, fecha o furo do sobrescrever
1220
+ // incondicional): README pré-existente NÃO é sobrescrito — preserva conteúdo curado.
1221
+ if (!fs.existsSync(readme)) fs.writeFileSync(readme, memoryWiringNote(pod, tier));
1222
+ return [paths.raw, paths.consolidated, readme];
1223
+ }
1224
+
1225
+ /** Nota de cabeamento da memória — prova documental de operância (SF2.3). */
1226
+ function memoryWiringNote(pod, tier) {
1227
+ return `# Memória do pod \`${pod}\` — OPERANTE (SF2.3)
1228
+
1229
+ > Instalada por forge-generate.cjs via o motor \`scripts/pod-memory.cjs\` (SF2.1).
1230
+ > Tier: ${tier}. Esta memória NÃO é scaffold morto: nasce cabeada ao lifecycle.
1231
+
1232
+ ## Cabeamento (nenhuma ação manual necessária)
1233
+
1234
+ - \`learnings.jsonl\` — log cru append-only. O runner (\`pc-run-pod\`, SF2.2) faz \`append()\`
1235
+ ao fim de cada etapa/run. O AGENTE NUNCA lê este arquivo (auditoria apenas).
1236
+ - \`learnings.consolidated.md\` — top-N curado (\`consolidate()\`), no path canônico que
1237
+ \`getConsolidatedForBrief()\` entrega ao brief da run. É o ÚNICO que o agente lê.
1238
+ - Auditoria de aderência por run: \`config/heuristic-watcher-rubric.yaml\` +
1239
+ \`scripts/watcher-audit.cjs\` (executado pelo \`pod-critic\`).
1240
+
1241
+ Assim que este pod rodar via \`pc-run-pod\`, injeção do consolidado no brief e append de
1242
+ learnings funcionam sem qualquer passo manual — a operância é o cabeamento ao motor pronto.
1243
+ `;
1244
+ }
1245
+
1246
+ function main() {
1247
+ const args = process.argv.slice(2);
1248
+ let out = null;
1249
+ let withMemory = false;
1250
+ let additive = false;
1251
+ const owns = [];
1252
+ const rest = [];
1253
+ for (let i = 0; i < args.length; i++) {
1254
+ if (args[i] === '--out') out = args[++i];
1255
+ else if (args[i] === '--with-memory') withMemory = true;
1256
+ else if (args[i] === '--additive') additive = true;
1257
+ else if (args[i] === '--owns') owns.push(...String(args[++i]).split(','));
1258
+ else rest.push(args[i]);
1259
+ }
1260
+ if (!rest[0] || !out) {
1261
+ console.error('Uso: node scripts/forge-generate.cjs <pod-spec.json> --out <podDir> [--with-memory] [--additive] [--owns p1,p2]');
1262
+ process.exit(2);
1263
+ }
1264
+ const spec = JSON.parse(fs.readFileSync(rest[0], 'utf8'));
1265
+ let r;
1266
+ try {
1267
+ // SF12.4a/ADR-030 Bloco O (AC48) — o CLI é SEMPRE 'authored', incondicional: serve
1268
+ // `*new` (sem flag) E `*migrate-squad` F2b (`--additive`). `--additive` governa só a
1269
+ // guarda de colisão (ortogonal) — NÃO muda `specOrigin`.
1270
+ r = generatePod(spec, path.resolve(out), { withMemory, additive, owns, specOrigin: 'authored' });
1271
+ } catch (e) {
1272
+ console.error(`[erro] ${e.message}`);
1273
+ process.exit(e && e.exitCode ? e.exitCode : 1);
1274
+ }
1275
+ console.log(`tier: ${r.tier}`);
1276
+ console.log('artefatos gravados (disco-first):');
1277
+ for (const w of r.written) console.log(` + ${path.relative(process.cwd(), w)}`);
1278
+ if (r.merged && r.merged.length) {
1279
+ console.log('merge aditivo (seções preservadas + novas):');
1280
+ for (const m of r.merged) console.log(` ~ ${path.relative(process.cwd(), m)}`);
1281
+ }
1282
+ if (r.skipped.length) {
1283
+ console.log('não gerados (proporcionalidade):');
1284
+ for (const s of r.skipped) console.log(` - ${s}`);
1285
+ }
1286
+ process.exit(0);
1287
+ }
1288
+
1289
+ if (require.main === module) main();
1290
+
1291
+ module.exports = {
1292
+ generatePod, installMemory, stepsFromSpec, buildWorkflowYaml, buildSquadIo, slug, stepTemplate,
1293
+ countGteNote, countGteFields, serializeAssertion, serializeCheckpoint, mergeSquadIo,
1294
+ extractHandoffTriples, splitTopSections, AdditiveCollisionError, InterfaceContractError,
1295
+ // SF12.4a/ADR-030
1296
+ SpecOriginError, CanonicalFieldError, KnowledgeFileMissingError, UnsynthesizableVetoError,
1297
+ validateSpecOrigin, readCanonicalField, computeCanonicalStatus, CANONICAL_FIELDS,
1298
+ deriveFieldMatchesValue, synthesizeArtifactText, buildSmokeSidecar, agentStubContent,
1299
+ donoExampleContent, ESCADA_DE_VOZ_VALUES, assertKnowledgeExists, assertSynthesizableVeto,
1300
+ // SF12.6/ADR-030 Bloco C/D
1301
+ DivergentRegenerationError, AllowDivergentOverwriteError, CheckpointInvariantError,
1302
+ validateAllowDivergentOverwrite, validateCanonicalStepRef, firstDivergentLine,
1303
+ };