@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,267 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * validate-fidelity-output.cjs — validador estático de FORMA + ARITMÉTICA do output
5
+ * do comando `*fidelity-score` (e do bloco `trindade:` de `*extract`/`*validate`) do
6
+ * mind-extractor (squad-forge). ADR-008 Decisão 2/4.
7
+ *
8
+ * FRONTEIRA (ADR-002/ADR-008): prova o que é MECANIZÁVEL — a aritmética de agregação
9
+ * e a coerência estrutural do bloco — e NUNCA julga CONTEÚDO. Ele não decide se o "4"
10
+ * é o score certo, nem se a citação sustenta o score, nem se um Playbook de fato existe
11
+ * no corpus: isso é JULGAMENTO do agente, revisado por QA/humano. Deixar a aritmética à
12
+ * LLM é o vetor real de fake-green do Risco 1 (score "premium" com denominador inflado,
13
+ * peso trocado ou threshold arredondado através de uma banda) — é isso que este script fecha.
14
+ *
15
+ * Verifica (fidelity_score): (i) 8 camadas presentes, cada uma `scored` (int 1-5 +
16
+ * `evidence` não-vazia) OU `insufficient_data`; (ii) `insufficient_data` FORA do
17
+ * denominador; (iii) peso de cada camada bate com o tipo (Observable 0.8 / Cognitive-Deep
18
+ * 1.0); (iv) média ponderada RECOMPUTADA casa com a reportada; (v) threshold/maturidade
19
+ * consistente com a média computada. Verifica (trindade): 3 componentes nomeados, cada um
20
+ * present|missing, e `verdict: blocked` SSE algum componente `missing` (coerência
21
+ * veredito<->status — nunca a presença real do componente no corpus).
22
+ *
23
+ * Uso: node scripts/validate-fidelity-output.cjs <output.yaml> [<output.yaml> ...]
24
+ * Exit 0 = bloco bem-formado e aritmética honesta; exit 1 = primeira violação nomeada.
25
+ *
26
+ * Fonte única do formato: template/pods/squad-forge/knowledge/fidelity-rubric.yaml
27
+ * (seção output_contract:) — mudar o formato exige tocar rubrica + este validador (dívida
28
+ * de acoplamento declarada no ADR-008 Consequências).
29
+ *
30
+ * Node >= 18, sem dependências externas (reusa o parser YAML-lite do validador de workflow).
31
+ */
32
+
33
+ const fs = require('node:fs');
34
+ const path = require('node:path');
35
+ const { parseYaml, Fail } = require('./validate-pod-workflow.cjs');
36
+
37
+ const REPO_ROOT = path.resolve(__dirname, '..');
38
+
39
+ // Espelho do knowledge/fidelity-rubric.yaml (threshold_bands + camadas). Acoplamento
40
+ // declarado no ADR-008; a fixture da rubrica (validate-fidelity-output.test.cjs) confere
41
+ // que a rubrica em disco carrega os mesmos 8 pesos/tipos, guardando contra drift.
42
+ const LAYERS = [
43
+ { layer: 1, type: 'Observable', weight: 0.8 },
44
+ { layer: 2, type: 'Observable', weight: 0.8 },
45
+ { layer: 3, type: 'Observable', weight: 0.8 },
46
+ { layer: 4, type: 'Observable', weight: 0.8 },
47
+ { layer: 5, type: 'Cognitive', weight: 1.0 },
48
+ { layer: 6, type: 'Deep Identity', weight: 1.0 },
49
+ { layer: 7, type: 'Deep Identity', weight: 1.0 },
50
+ { layer: 8, type: 'Deep Identity', weight: 1.0 },
51
+ ];
52
+ const CANON = new Map(LAYERS.map((l) => [l.layer, l]));
53
+ const THRESHOLD_BANDS = {
54
+ below_basic: { min: 1.0, max: 3.0, maturity: 'v0' },
55
+ basic: { min: 3.0, max: 3.75, maturity: '1.0' },
56
+ intermediate: { min: 3.75, max: 4.25, maturity: '2.0' },
57
+ premium: { min: 4.25, max: 4.85, maturity: '3.0+' },
58
+ elite: { min: 4.65, max: 5.0, maturity: '3.0+' },
59
+ };
60
+ const EPS = 0.01; // folga na checagem de pertencimento à banda de threshold
61
+ const AVG_TOL = 0.01; // tolerância de arredondamento da média ponderada (2 casas)
62
+ const TRINDADE_COMPONENTS = ['playbook', 'framework', 'swipe_file'];
63
+
64
+ function fail(msg) {
65
+ throw new Fail(msg);
66
+ }
67
+
68
+ function isPlainMap(v) {
69
+ return v !== null && typeof v === 'object' && !Array.isArray(v);
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Bloco fidelity_score — FORMA + ARITMÉTICA.
74
+ // ---------------------------------------------------------------------------
75
+
76
+ function validateFidelityBlock(fsb) {
77
+ if (!isPlainMap(fsb)) fail('fidelity_score não é um mapa');
78
+ const layers = fsb.layers;
79
+ if (!Array.isArray(layers)) fail('fidelity_score.layers ausente ou não é lista');
80
+ if (layers.length !== 8) fail(`fidelity_score.layers deve ter as 8 camadas — tem ${layers.length}`);
81
+
82
+ const seen = new Set();
83
+ let sumWeighted = 0;
84
+ let sumWeights = 0;
85
+ let scored = 0;
86
+ for (const L of layers) {
87
+ if (!isPlainMap(L)) fail('camada não é um mapa');
88
+ const n = L.layer;
89
+ if (!CANON.has(n)) fail(`camada com número inválido: ${JSON.stringify(n)} (esperado 1-8)`);
90
+ if (seen.has(n)) fail(`camada duplicada: ${n}`);
91
+ seen.add(n);
92
+ const canon = CANON.get(n);
93
+ if (L.type !== canon.type) {
94
+ fail(`camada ${n}: type "${L.type}" != "${canon.type}" da rubrica`);
95
+ }
96
+ if (L.weight !== canon.weight) {
97
+ fail(`camada ${n}: weight ${JSON.stringify(L.weight)} != ${canon.weight} (peso da rubrica para type ${canon.type})`);
98
+ }
99
+ if (L.status === 'scored') {
100
+ if (!Number.isInteger(L.score) || L.score < 1 || L.score > 5) {
101
+ fail(`camada ${n} scored: score deve ser inteiro 1-5 — recebeu ${JSON.stringify(L.score)}`);
102
+ }
103
+ if (typeof L.evidence !== 'string' || L.evidence.trim() === '') {
104
+ fail(`camada ${n} scored: evidence obrigatória e não-vazia (No Invention — score sem evidência citável é ficção)`);
105
+ }
106
+ sumWeighted += L.score * canon.weight;
107
+ sumWeights += canon.weight;
108
+ scored++;
109
+ } else if (L.status === 'insufficient_data') {
110
+ // Fora do denominador — não soma peso nem score (invariante ii).
111
+ } else {
112
+ fail(`camada ${n}: status inválido "${L.status}" (esperado scored | insufficient_data)`);
113
+ }
114
+ }
115
+ for (let i = 1; i <= 8; i++) if (!seen.has(i)) fail(`camada ${i} ausente`);
116
+
117
+ const insufficient = 8 - scored;
118
+ if (fsb.scored_count !== scored) {
119
+ fail(`scored_count reportado ${JSON.stringify(fsb.scored_count)} != ${scored} camadas scored reais`);
120
+ }
121
+ if (fsb.insufficient_count !== insufficient) {
122
+ fail(`insufficient_count reportado ${JSON.stringify(fsb.insufficient_count)} != ${insufficient} reais`);
123
+ }
124
+
125
+ if (scored === 0) {
126
+ // Nada pontuável: sem média, sem maturidade; threshold só pode ser insufficient_data.
127
+ if (fsb.weighted_average !== null && fsb.weighted_average !== undefined) {
128
+ fail('scored_count=0: weighted_average deve ser null (nenhuma camada sustenta média)');
129
+ }
130
+ if (fsb.threshold !== 'insufficient_data') {
131
+ fail(`scored_count=0: threshold deve ser "insufficient_data" — recebeu ${JSON.stringify(fsb.threshold)}`);
132
+ }
133
+ if (fsb.maturity !== null && fsb.maturity !== undefined) {
134
+ fail('scored_count=0: maturity deve ser ausente/null');
135
+ }
136
+ return;
137
+ }
138
+
139
+ const computed = sumWeighted / sumWeights;
140
+ if (typeof fsb.weighted_average !== 'number') {
141
+ fail(`weighted_average ausente ou não numérico: ${JSON.stringify(fsb.weighted_average)}`);
142
+ }
143
+ if (Math.abs(fsb.weighted_average - computed) > AVG_TOL) {
144
+ fail(`weighted_average reportado ${fsb.weighted_average} != ${computed.toFixed(4)} recomputado ` +
145
+ `(soma ${sumWeighted.toFixed(3)} / pesos ${sumWeights.toFixed(3)} sobre ${scored} camadas scored) — ` +
146
+ `fake-green aritmético (denominador inflado por insufficient_data, peso trocado ou média forjada)`);
147
+ }
148
+
149
+ const band = THRESHOLD_BANDS[fsb.threshold];
150
+ if (!band) {
151
+ fail(`threshold inválido "${fsb.threshold}" (esperado ${Object.keys(THRESHOLD_BANDS).join('|')})`);
152
+ }
153
+ if (computed < band.min - EPS || computed > band.max + EPS) {
154
+ fail(`threshold "${fsb.threshold}" (banda ${band.min}-${band.max}) inconsistente com a média computada ` +
155
+ `${computed.toFixed(3)} — threshold inflado/deflacionado (rótulo fora da própria banda)`);
156
+ }
157
+ if (fsb.maturity !== band.maturity) {
158
+ fail(`maturity "${fsb.maturity}" != "${band.maturity}" esperada para threshold "${fsb.threshold}"`);
159
+ }
160
+ }
161
+
162
+ // ---------------------------------------------------------------------------
163
+ // Bloco trindade — COERÊNCIA veredito<->status (nunca a presença real no corpus).
164
+ // ---------------------------------------------------------------------------
165
+
166
+ function validateTrindadeBlock(tr) {
167
+ if (!isPlainMap(tr)) fail('trindade não é um mapa');
168
+ const computedMissing = [];
169
+ for (const c of TRINDADE_COMPONENTS) {
170
+ const comp = tr[c];
171
+ if (!isPlainMap(comp)) fail(`trindade.${c} ausente ou não é um mapa {status, evidence}`);
172
+ if (comp.status === 'present') {
173
+ if (typeof comp.evidence !== 'string' || comp.evidence.trim() === '') {
174
+ fail(`trindade.${c} present: evidence obrigatória e não-vazia`);
175
+ }
176
+ } else if (comp.status === 'missing') {
177
+ computedMissing.push(c);
178
+ } else {
179
+ fail(`trindade.${c}: status inválido "${comp.status}" (esperado present | missing)`);
180
+ }
181
+ }
182
+ if (tr.verdict !== 'complete' && tr.verdict !== 'blocked') {
183
+ fail(`trindade.verdict inválido "${tr.verdict}" (esperado complete | blocked)`);
184
+ }
185
+ const reported = Array.isArray(tr.missing) ? tr.missing.slice().sort() : null;
186
+ if (reported === null) fail('trindade.missing ausente (use [] quando completo)');
187
+ const expected = computedMissing.slice().sort();
188
+ if (reported.join(',') !== expected.join(',')) {
189
+ fail(`trindade.missing reportado [${reported.join(', ')}] != [${expected.join(', ')}] (componentes com status missing)`);
190
+ }
191
+ // Coerência bloqueante: verdict=blocked SSE há componente missing (ADR-008 Decisão 4).
192
+ if (computedMissing.length > 0 && tr.verdict !== 'blocked') {
193
+ fail(`trindade.verdict deve ser "blocked" com componente(s) missing [${expected.join(', ')}] — ` +
194
+ `PROIBIDO aprovar com Trindade incompleta (AN003; ADR-008 Decisão 4)`);
195
+ }
196
+ if (computedMissing.length === 0 && tr.verdict !== 'complete') {
197
+ fail('trindade.verdict deve ser "complete" quando nenhum componente está missing');
198
+ }
199
+ }
200
+
201
+ // ---------------------------------------------------------------------------
202
+ // Documento (aceita fidelity_score e/ou trindade; exige ao menos um).
203
+ // ---------------------------------------------------------------------------
204
+
205
+ function validateFidelityOutput(doc) {
206
+ if (!isPlainMap(doc)) fail('raiz do output não é um mapa YAML');
207
+ const hasFs = 'fidelity_score' in doc;
208
+ const hasTr = 'trindade' in doc;
209
+ if (!hasFs && !hasTr) {
210
+ fail('nenhum bloco reconhecido — esperado fidelity_score e/ou trindade (output_contract da rubrica)');
211
+ }
212
+ if (hasFs) validateFidelityBlock(doc.fidelity_score);
213
+ if (hasTr) validateTrindadeBlock(doc.trindade);
214
+ }
215
+
216
+ function validateFile(filePath) {
217
+ let doc;
218
+ try {
219
+ doc = parseYaml(fs.readFileSync(filePath, 'utf8'));
220
+ } catch (e) {
221
+ fail(`YAML malformado: ${e.message}`);
222
+ }
223
+ validateFidelityOutput(doc);
224
+ }
225
+
226
+ function main() {
227
+ const targets = process.argv.slice(2);
228
+ if (targets.length === 0) {
229
+ console.error('Uso: node scripts/validate-fidelity-output.cjs <output.yaml> [...]');
230
+ process.exit(2);
231
+ }
232
+ let ok = 0;
233
+ for (const t of targets) {
234
+ const rel = path.relative(REPO_ROOT, path.resolve(t));
235
+ if (!fs.existsSync(t)) {
236
+ console.error(`[FALHA] ${rel}: arquivo não encontrado`);
237
+ process.exit(1);
238
+ }
239
+ try {
240
+ validateFile(t);
241
+ console.log(`[OK ] ${rel}`);
242
+ ok++;
243
+ } catch (e) {
244
+ if (e instanceof Fail) {
245
+ console.error(`[FALHA] ${rel}: ${e.message}`);
246
+ process.exit(1);
247
+ }
248
+ throw e;
249
+ }
250
+ }
251
+ console.log(`\n${ok} output(s) de fidelidade validado(s). Forma + aritmética honestas.`);
252
+ process.exit(0);
253
+ }
254
+
255
+ if (require.main === module) {
256
+ main();
257
+ }
258
+
259
+ module.exports = {
260
+ validateFidelityOutput,
261
+ validateFidelityBlock,
262
+ validateTrindadeBlock,
263
+ LAYERS,
264
+ THRESHOLD_BANDS,
265
+ TRINDADE_COMPONENTS,
266
+ Fail,
267
+ };
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * validate-fidelity-output.test.cjs — suíte do validador de FORMA+ARITMÉTICA do
5
+ * *fidelity-score (Story SF7.1, AC5; ADR-008 Decisão 2/4). Prova:
6
+ * (1) o output golden (7 scored + 1 insufficient + trindade completa) PASSA;
7
+ * (2) cada fake-green FALHA (média forjada, insufficient no denominador, evidência
8
+ * ausente, threshold inflado, peso adulterado, Trindade incoerente);
9
+ * (3) a rubrica em disco carrega a FORMA exigida (8 camadas, weight numérico, Fonte:,
10
+ * thresholds, 3 componentes da Trindade, output_contract) — AC5 fixture estrutural;
11
+ * (4) SEM DRIFT: pesos/tipos da rubrica == constantes do validador.
12
+ *
13
+ * Node >= 18.
14
+ */
15
+
16
+ const fs = require('node:fs');
17
+ const path = require('node:path');
18
+ const {
19
+ validateFidelityOutput, validateTrindadeBlock, LAYERS, THRESHOLD_BANDS, Fail,
20
+ } = require('./validate-fidelity-output.cjs');
21
+ const { parseYaml } = require('./validate-pod-workflow.cjs');
22
+
23
+ const REPO_ROOT = path.resolve(__dirname, '..');
24
+ const FIX = path.join(__dirname, '__fixtures__', 'fidelity');
25
+ const RUBRIC = path.join(REPO_ROOT, 'template', 'pods', 'squad-forge', 'knowledge', 'fidelity-rubric.yaml');
26
+
27
+ let passed = 0;
28
+ const failures = [];
29
+ function ok(cond, label) {
30
+ if (cond) { passed++; console.log(`[OK ] ${label}`); }
31
+ else { failures.push(label); console.error(`[FALHA] ${label}`); }
32
+ }
33
+ function parseFix(name) {
34
+ return parseYaml(fs.readFileSync(path.join(FIX, name), 'utf8'));
35
+ }
36
+ /** True se validateFidelityOutput lançou Fail sobre o doc. */
37
+ function throwsFail(doc) {
38
+ try { validateFidelityOutput(doc); return false; }
39
+ catch (e) { if (e instanceof Fail) return true; throw e; }
40
+ }
41
+
42
+ // --- (1) Golden PASSA (fidelity_score + trindade) ---
43
+ let goldenOk = true;
44
+ try { validateFidelityOutput(parseFix('golden.yaml')); } catch (e) { goldenOk = false; console.error(e.message); }
45
+ ok(goldenOk, 'AC5 (1) golden (7 scored + 1 insufficient + trindade completa) PASSA');
46
+
47
+ // --- (2) Fake-greens FALHAM, cada um pelo seu vetor ---
48
+ // NOTA de redação (CONCERNS-1): fake-green-average (avg 4.60/premium) e fake-green-denominator
49
+ // (avg 3.47/basic) reportam um threshold INCONSISTENTE com a média recomputada, então quem os
50
+ // pega PRIMEIRO é o threshold-check — não isolam o average-recompute. O isolamento do
51
+ // average-recompute é provado por fake-green-average-in-band (abaixo), único fixture que só ele pega.
52
+ ok(throwsFail(parseFix('fake-green-average.yaml')), 'AC5 (2) média + threshold forjados (4.60/premium) FALHA (pego pelo threshold-check)');
53
+ ok(throwsFail(parseFix('fake-green-denominator.yaml')), 'AC5 (2) insufficient_data no denominador (3.47/basic) FALHA (pego pelo threshold-check)');
54
+ ok(throwsFail(parseFix('fake-green-average-in-band.yaml')),'AC5 (2) ISOLA o average-recompute: média forjada 4.20 DENTRO da banda intermediate (threshold consistente) — só o recompute pega, FALHA');
55
+ ok(throwsFail(parseFix('fake-green-missing-evidence.yaml')),'AC5 (2) score sem evidência FALHA');
56
+ ok(throwsFail(parseFix('fake-green-threshold.yaml')), 'AC5 (2) threshold inflado (premium sobre 4.03) FALHA');
57
+ ok(throwsFail(parseFix('fake-green-weight.yaml')), 'AC5 (2) peso adulterado (Cognitive 0.8) FALHA');
58
+ ok(throwsFail(parseFix('trindade-incoherent.yaml')), 'AC5 (2) Trindade: verdict complete com componente missing FALHA');
59
+
60
+ // --- Sanidade inline: coerência da Trindade nos dois sentidos ---
61
+ ok(!throwsFail({ trindade: {
62
+ playbook: { status: 'present', evidence: 'x' }, framework: { status: 'present', evidence: 'x' },
63
+ swipe_file: { status: 'present', evidence: 'x' }, verdict: 'complete', missing: [] } }),
64
+ 'sanidade: trindade completa (todos present + verdict complete) PASSA');
65
+ ok(throwsFail({ trindade: {
66
+ playbook: { status: 'missing', evidence: '' }, framework: { status: 'present', evidence: 'x' },
67
+ swipe_file: { status: 'present', evidence: 'x' }, verdict: 'blocked', missing: [] } }),
68
+ 'sanidade: verdict blocked mas missing:[] (lista incoerente) FALHA');
69
+ ok(throwsFail({}), 'sanidade: doc sem fidelity_score nem trindade FALHA');
70
+
71
+ // --- (3) Rubrica em disco: FORMA estrutural (AC5 fixture) ---
72
+ const rubricText = fs.readFileSync(RUBRIC, 'utf8');
73
+ const rubric = parseYaml(rubricText);
74
+ ok(Array.isArray(rubric.layers) && rubric.layers.length === 8, 'AC5 (3) rubrica tem 8 camadas');
75
+ ok(rubric.layers.every((l) => typeof l.weight === 'number'), 'AC5 (3) toda camada tem weight numérico');
76
+ ok(/^\s*#?\s*Fonte:/im.test(rubricText), 'AC5 (3) rubrica tem cabeçalho Fonte:');
77
+ ok(rubric.scoring && rubric.scoring.thresholds && rubric.threshold_bands, 'AC5 (3) thresholds presentes');
78
+ ok(rubric.trindade && rubric.trindade.components
79
+ && ['playbook', 'framework', 'swipe_file'].every((c) => c in rubric.trindade.components),
80
+ 'AC5 (3) 3 componentes da Trindade nomeados');
81
+ ok(!!rubric.output_contract, 'AC5 (3) output_contract presente');
82
+
83
+ // --- (4) Sem drift: rubrica == constantes do validador ---
84
+ const noDrift = LAYERS.every((canon) => {
85
+ const r = rubric.layers.find((l) => l.layer === canon.layer);
86
+ return r && r.type === canon.type && r.weight === canon.weight;
87
+ });
88
+ ok(noDrift, 'AC5 (4) pesos/tipos da rubrica == constantes LAYERS do validador (sem drift)');
89
+ const bandsMatch = Object.keys(THRESHOLD_BANDS).every((k) => {
90
+ const r = rubric.threshold_bands[k];
91
+ return r && r.min === THRESHOLD_BANDS[k].min && r.max === THRESHOLD_BANDS[k].max
92
+ && r.maturity === THRESHOLD_BANDS[k].maturity;
93
+ });
94
+ ok(bandsMatch, 'AC5 (4) threshold_bands da rubrica == constantes do validador (sem drift)');
95
+
96
+ if (failures.length > 0) {
97
+ console.error(`\nSuíte do validador de fidelidade FALHOU: ${failures.length} de ${passed + failures.length}.`);
98
+ process.exit(1);
99
+ }
100
+ console.log(`\n${passed} asserções verdes. Validador de fidelidade provado.`);
101
+ process.exit(0);
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * validate-install.js — teste E2E do instalador POWER CLAUDE.
5
+ *
6
+ * Cria um tmpdir, roda `node bin/cli.js install --yes` com cwd no tmpdir e
7
+ * verifica o resultado. Exit 0/1 com relatório.
8
+ *
9
+ * Node >= 18, sem dependências externas.
10
+ */
11
+
12
+ const fs = require('node:fs');
13
+ const os = require('node:os');
14
+ const path = require('node:path');
15
+ const { spawnSync } = require('node:child_process');
16
+
17
+ const { motorEntries } = require('../installer/motor-scripts');
18
+ const { sha256OfFile } = require('../installer/manifest');
19
+
20
+ const REPO_ROOT = path.resolve(__dirname, '..');
21
+ const CLI = path.join(REPO_ROOT, 'bin', 'cli.js');
22
+ const TEMPLATE_DIR = path.join(REPO_ROOT, 'template');
23
+
24
+ const results = [];
25
+ function check(label, ok, detail) {
26
+ results.push({ label, ok, detail });
27
+ console.log(`[${ok ? 'OK ' : 'FALHA'}] ${label}${detail ? ` — ${detail}` : ''}`);
28
+ return ok;
29
+ }
30
+
31
+ function countTemplateFiles(dir) {
32
+ let n = 0;
33
+ const stack = [dir];
34
+ while (stack.length > 0) {
35
+ const d = stack.pop();
36
+ for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
37
+ const p = path.join(d, entry.name);
38
+ if (entry.isDirectory()) stack.push(p);
39
+ else if (entry.isFile()) n++;
40
+ }
41
+ }
42
+ return n;
43
+ }
44
+
45
+ function listPodDirs(dir) {
46
+ const podsDir = path.join(dir, 'pods');
47
+ if (!fs.existsSync(podsDir)) return [];
48
+ return fs
49
+ .readdirSync(podsDir, { withFileTypes: true })
50
+ .filter((e) => e.isDirectory())
51
+ .map((e) => e.name)
52
+ .sort();
53
+ }
54
+
55
+ function main() {
56
+ console.log('POWER CLAUDE — validate-install (E2E)\n');
57
+
58
+ const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'power-claude-e2e-'));
59
+ console.log(`Diretório de teste: ${tmpdir}\n`);
60
+
61
+ try {
62
+ // 1. Instalação não-interativa
63
+ const install = spawnSync(process.execPath, [CLI, 'install', '--yes'], {
64
+ cwd: tmpdir,
65
+ encoding: 'utf8',
66
+ timeout: 120000,
67
+ });
68
+ const installed = check('install --yes termina com exit 0', install.status === 0, `exit ${install.status}`);
69
+ if (!installed) {
70
+ console.error('\n--- stdout ---\n' + (install.stdout || ''));
71
+ console.error('\n--- stderr ---\n' + (install.stderr || ''));
72
+ finish(tmpdir);
73
+ return;
74
+ }
75
+
76
+ // 2. Arquivos essenciais
77
+ check('.claude/CLAUDE.md existe', fs.existsSync(path.join(tmpdir, '.claude', 'CLAUDE.md')));
78
+
79
+ const settingsPath = path.join(tmpdir, '.claude', 'settings.json');
80
+ let settings = null;
81
+ let settingsOk = false;
82
+ try {
83
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
84
+ settingsOk = true;
85
+ } catch {
86
+ settingsOk = false;
87
+ }
88
+ check('.claude/settings.json parseia', settingsOk);
89
+ check(
90
+ '.claude/settings.json tem hooks',
91
+ Boolean(settings && settings.hooks && typeof settings.hooks === 'object' && Object.keys(settings.hooks).length > 0)
92
+ );
93
+
94
+ check(
95
+ '.claude/hooks/enforce-git-push-authority.cjs existe',
96
+ fs.existsSync(path.join(tmpdir, '.claude', 'hooks', 'enforce-git-push-authority.cjs'))
97
+ );
98
+
99
+ // 3. Pods e knowledge
100
+ check('pods/README.md existe', fs.existsSync(path.join(tmpdir, 'pods', 'README.md')));
101
+
102
+ const expectedPods = listPodDirs(TEMPLATE_DIR);
103
+ const installedPods = listPodDirs(tmpdir);
104
+ const allPodsPresent = expectedPods.every((p) => installedPods.includes(p));
105
+ check(
106
+ `5 diretórios de pods instalados (${expectedPods.length} esperados)`,
107
+ expectedPods.length >= 5 && allPodsPresent,
108
+ `instalados: ${installedPods.join(', ') || 'nenhum'}`
109
+ );
110
+
111
+ check('knowledge/voice-dna/ existe', fs.existsSync(path.join(tmpdir, 'knowledge', 'voice-dna')));
112
+
113
+ // 4. Manifest
114
+ const manifestPath = path.join(tmpdir, '.power-claude', 'manifest.json');
115
+ let manifest = null;
116
+ try {
117
+ manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
118
+ } catch {
119
+ manifest = null;
120
+ }
121
+ check('.power-claude/manifest.json parseia', manifest !== null);
122
+ if (manifest) {
123
+ const expectedMin = countTemplateFiles(TEMPLATE_DIR);
124
+ check(
125
+ `manifest cobre >= ${expectedMin} arquivos (todos do template)`,
126
+ Array.isArray(manifest.files) && manifest.files.length >= expectedMin,
127
+ `manifest tem ${Array.isArray(manifest.files) ? manifest.files.length : 0}`
128
+ );
129
+ check('manifest tem version e installedAt', Boolean(manifest.version && manifest.installedAt));
130
+ }
131
+
132
+ // 5. .gitignore cobre .env
133
+ const gitignorePath = path.join(tmpdir, '.gitignore');
134
+ let coversEnv = false;
135
+ if (fs.existsSync(gitignorePath)) {
136
+ const lines = fs.readFileSync(gitignorePath, 'utf8').split(/\r?\n/).map((l) => l.trim());
137
+ coversEnv = lines.includes('.env');
138
+ }
139
+ check('.gitignore contém ".env"', coversEnv);
140
+
141
+ // 6. doctor passa no projeto recém-instalado
142
+ const doctor = spawnSync(process.execPath, [CLI, 'doctor'], { cwd: tmpdir, encoding: 'utf8', timeout: 120000 });
143
+ if (!check('doctor termina com exit 0 no projeto recém-instalado', doctor.status === 0, `exit ${doctor.status}`)) {
144
+ console.error('\n--- doctor stdout ---\n' + (doctor.stdout || ''));
145
+ }
146
+
147
+ // 7. Scripts do motor (ADR-007): os 14 presentes e byte-idênticos ao pacote.
148
+ const entries = motorEntries();
149
+ let allMotorPresent = true;
150
+ let allByteIdentical = true;
151
+ for (const { path: rel } of entries) {
152
+ const destPath = path.join(tmpdir, ...rel.split('/'));
153
+ const srcPath = path.join(REPO_ROOT, ...rel.split('/'));
154
+ if (!fs.existsSync(destPath)) {
155
+ allMotorPresent = false;
156
+ console.error(` ausente: ${rel}`);
157
+ } else if (fs.existsSync(srcPath) && sha256OfFile(destPath) !== sha256OfFile(srcPath)) {
158
+ allByteIdentical = false;
159
+ console.error(` difere do pacote: ${rel}`);
160
+ }
161
+ }
162
+ check(`${entries.length} scripts do motor presentes em scripts/<nome>`, allMotorPresent);
163
+ check('scripts do motor byte-idênticos ao pacote', allByteIdentical);
164
+
165
+ // 7b. Fixture negativa: scripts de teste/E2E NÃO vazam para o projeto.
166
+ const forbidden = [
167
+ 'scripts/validate-install.js',
168
+ 'scripts/validate-adopt.js',
169
+ 'scripts/pc-run-pod.test.cjs',
170
+ 'scripts/forge.test.cjs',
171
+ 'scripts/__tests__/motor-scripts.test.cjs',
172
+ ];
173
+ const leaked = forbidden.filter((rel) => fs.existsSync(path.join(tmpdir, ...rel.split('/'))));
174
+ check('scripts de teste/E2E NÃO foram copiados (fixture negativa)', leaked.length === 0, leaked.length ? `vazaram: ${leaked.join(', ')}` : undefined);
175
+ check('diretório scripts/__tests__ NÃO foi copiado', !fs.existsSync(path.join(tmpdir, 'scripts', '__tests__')));
176
+ check('diretório scripts/__fixtures__ NÃO foi copiado', !fs.existsSync(path.join(tmpdir, 'scripts', '__fixtures__')));
177
+
178
+ // 8. update preserva script do motor modificado pelo usuário; intacto atualiza.
179
+ const MOD_REL = 'scripts/watcher-audit.cjs';
180
+ const INTACT_REL = 'scripts/pod-memory.cjs';
181
+ const modPath = path.join(tmpdir, ...MOD_REL.split('/'));
182
+ const userContent = fs.readFileSync(modPath, 'utf8') + '\n// edição do usuário — não sobrescrever\n';
183
+ fs.writeFileSync(modPath, userContent, 'utf8');
184
+ const userHash = sha256OfFile(modPath);
185
+ const update = spawnSync(process.execPath, [CLI, 'update', '--yes'], { cwd: tmpdir, encoding: 'utf8', timeout: 120000 });
186
+ check('update --yes termina com exit 0', update.status === 0, `exit ${update.status}`);
187
+ check('update PRESERVOU o script do motor modificado', sha256OfFile(modPath) === userHash);
188
+ check(
189
+ 'update manteve script do motor intacto (byte-idêntico ao pacote)',
190
+ sha256OfFile(path.join(tmpdir, ...INTACT_REL.split('/'))) === sha256OfFile(path.join(REPO_ROOT, ...INTACT_REL.split('/')))
191
+ );
192
+
193
+ // 9. doctor: script do motor ausente com owner presente → FALHA (exit 1);
194
+ // sem o owner → check pulado (doctor não falha por causa dele).
195
+ const DEL_REL = 'scripts/forge-generate.cjs'; // owner pod:squad-forge
196
+ fs.rmSync(path.join(tmpdir, ...DEL_REL.split('/')), { force: true });
197
+ const doctorMissing = spawnSync(process.execPath, [CLI, 'doctor'], { cwd: tmpdir, encoding: 'utf8', timeout: 120000 });
198
+ check('doctor FALHA (exit 1) com script do motor ausente e owner presente', doctorMissing.status === 1, `exit ${doctorMissing.status}`);
199
+ check('doctor aponta o script ausente', /forge-generate\.cjs/.test(doctorMissing.stdout || ''));
200
+ // Remove o owner (pod squad-forge) → o check do script ausente é pulado.
201
+ fs.rmSync(path.join(tmpdir, 'pods', 'squad-forge'), { recursive: true, force: true });
202
+ const doctorSkipped = spawnSync(process.execPath, [CLI, 'doctor'], { cwd: tmpdir, encoding: 'utf8', timeout: 120000 });
203
+ check(
204
+ 'doctor PULA (warn) o script do motor quando o owner não está instalado',
205
+ /Motor: scripts\/forge-generate\.cjs.*pulada/i.test(doctorSkipped.stdout || ''),
206
+ undefined
207
+ );
208
+ } finally {
209
+ finish(tmpdir);
210
+ }
211
+ }
212
+
213
+ function finish(tmpdir) {
214
+ try {
215
+ fs.rmSync(tmpdir, { recursive: true, force: true });
216
+ } catch (error) {
217
+ console.warn(`Aviso: falha ao limpar tmpdir ${tmpdir}: ${error.message}`);
218
+ }
219
+ const failed = results.filter((r) => !r.ok);
220
+ console.log(`\n${results.length - failed.length}/${results.length} checagens passaram.`);
221
+ if (failed.length > 0) {
222
+ console.error('E2E FALHOU:');
223
+ for (const f of failed) console.error(` - ${f.label}${f.detail ? ` (${f.detail})` : ''}`);
224
+ process.exit(1);
225
+ }
226
+ console.log('E2E verde.');
227
+ process.exit(0);
228
+ }
229
+
230
+ main();