@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,421 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * validate-update-scope-motor.js — Story SF10.20 / ADR-019 (`DF-SF10.20-4`, `DF-SF10.20-5`).
5
+ *
6
+ * Dois mecanismos, um arquivo:
7
+ *
8
+ * A) `power-claude update --scope motor`. O teste que carrega o peso é o da ARMADILHA:
9
+ * `adopt --yes && update --yes` PRESERVA o script de motor velho (adopt.js marca
10
+ * divergência como `origin:'modified'`; update.js preserva) e ainda assim reporta
11
+ * sucesso — foi o que teria acontecido se a SF10.20 tivesse seguido a presunção
12
+ * original. O escopo motor tem que corrigir o mesmo fixture.
13
+ *
14
+ * B) Atribuição de skew de capacidade em `loadWorkflow` (`capabilityGaps` /
15
+ * `annotateCapabilitySkew`): a falha do preflight passa a dizer que a culpada é a
16
+ * INSTALAÇÃO, com as duas causas possíveis e o comando de correção.
17
+ *
18
+ * NOTA DE AUTORIDADE (`DF-SF10.20-1`): este arquivo é prova de NÃO-REGRESSÃO do
19
+ * mecanismo. Ele NÃO é o enforcement — o enforcement é o preflight do motor na árvore
20
+ * instalada do usuário. Nada aqui deve virar a única evidência de que o gate funciona:
21
+ * a evidência que conta está no Dev Record da story, colhida no `second-brain`.
22
+ *
23
+ * Node >= 18, sem dependências externas.
24
+ */
25
+
26
+ const fs = require('node:fs');
27
+ const os = require('node:os');
28
+ const path = require('node:path');
29
+ const { spawnSync } = require('node:child_process');
30
+
31
+ const REPO_ROOT = path.resolve(__dirname, '..');
32
+ const CLI = path.join(REPO_ROOT, 'bin', 'cli.js');
33
+ const TEMPLATE_DIR = path.join(REPO_ROOT, 'template');
34
+
35
+ const results = [];
36
+ function check(label, ok, detail) {
37
+ results.push({ label, ok, detail });
38
+ console.log(`[${ok ? 'OK ' : 'FALHA'}] ${label}${detail ? ` — ${detail}` : ''}`);
39
+ return ok;
40
+ }
41
+
42
+ function cp(src, dest) {
43
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
44
+ fs.copyFileSync(src, dest);
45
+ }
46
+
47
+ function cli(args, cwd) {
48
+ return spawnSync(process.execPath, [CLI, ...args], { cwd, encoding: 'utf8', timeout: 120000 });
49
+ }
50
+
51
+ /**
52
+ * Instalação-fixture montada À MÃO (sem manifest) — a mesma forma do defeito real:
53
+ * `scripts/pc-run-pod.cjs` presente porém DEFASADO, `scripts/veto-checks/` ausente.
54
+ */
55
+ function makeFixture() {
56
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-scope-motor-'));
57
+ // Owner das entradas do motor (ADR-007 Decisão 3): `pods/squad-forge` é o owner de
58
+ // validate-pod-workflow.cjs/smoke-pod-workflow.cjs e `skill:pc-run-pod` o de
59
+ // pc-run-pod.cjs — sem eles o escopo PULA justamente os arquivos sob teste.
60
+ fs.mkdirSync(path.join(dir, 'pods', 'demo', 'workflows'), { recursive: true });
61
+ fs.mkdirSync(path.join(dir, 'pods', 'squad-forge'), { recursive: true });
62
+ cp(path.join(TEMPLATE_DIR, '.claude', 'CLAUDE.md'), path.join(dir, '.claude', 'CLAUDE.md'));
63
+ for (const skill of fs.readdirSync(path.join(TEMPLATE_DIR, '.claude', 'skills'))) {
64
+ fs.mkdirSync(path.join(dir, '.claude', 'skills', skill), { recursive: true });
65
+ }
66
+ // Motor "sincronizado à mão em algum momento do passado": copiado e depois derivado.
67
+ cp(path.join(REPO_ROOT, 'scripts', 'pc-run-pod.cjs'), path.join(dir, 'scripts', 'pc-run-pod.cjs'));
68
+ fs.appendFileSync(path.join(dir, 'scripts', 'pc-run-pod.cjs'), '\n// motor defasado (fixture)\n');
69
+ cp(path.join(REPO_ROOT, 'scripts', 'validate-budgets.js'), path.join(dir, 'scripts', 'validate-budgets.js'));
70
+ return dir;
71
+ }
72
+
73
+ function hash(p) {
74
+ return require('../installer/manifest').sha256OfFile(p);
75
+ }
76
+
77
+ function main() {
78
+ console.log('POWER CLAUDE — validate-update-scope-motor (SF10.20)\n');
79
+ const tmpdirs = [];
80
+ try {
81
+ // ---------------------------------------------------------------------
82
+ // A1. A ARMADILHA: adopt --yes && update --yes PRESERVA o motor velho.
83
+ // ---------------------------------------------------------------------
84
+ const trap = makeFixture();
85
+ tmpdirs.push(trap);
86
+ const trapRunner = path.join(trap, 'scripts', 'pc-run-pod.cjs');
87
+ const staleHash = hash(trapRunner);
88
+ const freshHash = hash(path.join(REPO_ROOT, 'scripts', 'pc-run-pod.cjs'));
89
+ check('fixture: motor instalado diverge do framework', staleHash !== freshHash);
90
+
91
+ const adoptTrap = cli(['adopt', '--yes'], trap);
92
+ check('adopt --yes sai 0 na instalação sem manifest', adoptTrap.status === 0, `exit ${adoptTrap.status}`);
93
+ check('adopt --yes NÃO escreve arquivo de motor (só o manifest)', hash(trapRunner) === staleHash);
94
+
95
+ const updateTrap = cli(['update', '--yes'], trap);
96
+ check('update --yes (sem escopo) sai 0 — reporta SUCESSO', updateTrap.status === 0, `exit ${updateTrap.status}`);
97
+ check(
98
+ 'ARMADILHA confirmada: update --yes sem escopo PRESERVA o motor defasado',
99
+ hash(trapRunner) === staleHash,
100
+ 'é por isso que --scope motor existe (ADR-019 DF-SF10.20-5)'
101
+ );
102
+
103
+ // Mesmo fixture, agora com escopo: tem que corrigir o que a armadilha preservou.
104
+ const scopedTrap = cli(['update', '--scope', 'motor', '--yes'], trap);
105
+ check('update --scope motor sai 0', scopedTrap.status === 0, `exit ${scopedTrap.status}`);
106
+ check(
107
+ 'update --scope motor SOBRESCREVE o motor defasado (divergência = defasagem)',
108
+ hash(trapRunner) === freshHash
109
+ );
110
+ check(
111
+ 'update --scope motor instala os blessed ausentes (scripts/veto-checks/)',
112
+ fs.existsSync(path.join(trap, 'scripts', 'veto-checks', 'phash-gate.cjs'))
113
+ );
114
+
115
+ // ---------------------------------------------------------------------
116
+ // A2. Auditabilidade e contenção do escopo.
117
+ // ---------------------------------------------------------------------
118
+ const fresh = makeFixture();
119
+ tmpdirs.push(fresh);
120
+ cli(['adopt', '--yes'], fresh);
121
+ const beforeTemplateMiss = fs.existsSync(path.join(fresh, '.claude', 'settings.json'));
122
+ const scoped = cli(['update', '--scope', 'motor', '--yes'], fresh);
123
+ const out = scoped.stdout || '';
124
+ check('tabela de hashes é impressa', out.includes('sha256 instalado → sha256 framework'));
125
+ check('tabela marca o defasado ANTES de escrever', /DEFASADO\s+scripts\/pc-run-pod\.cjs/.test(out));
126
+ check('relatório distingue sobrescritos de instalados', out.includes('Sobrescritos (defasados):') && out.includes('Instalados (ausentes):'));
127
+ check('avisa sobre o landmine do update sem escopo', out.includes('SEM --scope motor') && out.includes('docs/mecanismos/update-scope-motor.md'));
128
+ check(
129
+ 'escopo NÃO instala arquivo de template (settings.json continua ausente)',
130
+ fs.existsSync(path.join(fresh, '.claude', 'settings.json')) === beforeTemplateMiss &&
131
+ !fs.existsSync(path.join(fresh, '.claude', 'settings.json'))
132
+ );
133
+ const manifestAfter = JSON.parse(fs.readFileSync(path.join(fresh, '.power-claude', 'manifest.json'), 'utf8'));
134
+ const runnerEntry = manifestAfter.files.find((f) => f.path === 'scripts/pc-run-pod.cjs');
135
+ check('manifest passa a registrar o hash NOVO do motor (sem origin:modified)', Boolean(runnerEntry) && runnerEntry.sha256 === freshHash && !runnerEntry.origin);
136
+ const noise = out.includes('não faz mais parte do template');
137
+ check('escopo não emite ruído de "saiu do template" sobre entradas fora do escopo', !noise);
138
+
139
+ // Idempotência.
140
+ const again = cli(['update', '--scope', 'motor', '--yes'], fresh);
141
+ check('segundo update --scope motor é no-op', again.status === 0 && (again.stdout || '').includes('Motor já sincronizado'));
142
+
143
+ // ---------------------------------------------------------------------
144
+ // A3. SF11.1 — REGRESSÃO: no-op recarimba o REGISTRO (frameworkRoot/updatedAt).
145
+ //
146
+ // O defeito que faltava cobrir: `updateScopedMotor` retornava em
147
+ // `toWrite.length === 0` ANTES de `writeManifest` — único lugar que grava
148
+ // `frameworkRoot`. Numa instalação já sincronizada (o caso comum depois do
149
+ // primeiro sync) NENHUM comando suportado reancorava o campo: `adopt` recusa com
150
+ // manifest presente, e o `update` escopado saía cedo. O campo apontando para um
151
+ // checkout efêmero já apagado degrada silenciosamente o comando de correção da
152
+ // SF10.20 para placeholder. Aqui o cenário é montado explicitamente: manifest
153
+ // apontando para caminho inexistente + motor 100% sincronizado.
154
+ // ---------------------------------------------------------------------
155
+ const staleMfPath = path.join(fresh, '.power-claude', 'manifest.json');
156
+ const beforeMf = JSON.parse(fs.readFileSync(staleMfPath, 'utf8'));
157
+ const orphanRoot = path.join(os.tmpdir(), 'pc-wt-efemero-que-sumiu');
158
+ fs.writeFileSync(
159
+ staleMfPath,
160
+ JSON.stringify({ ...beforeMf, frameworkRoot: orphanRoot, updatedAt: '2000-01-01T00:00:00.000Z' }, null, 2) + '\n'
161
+ );
162
+ const reanchor = cli(['update', '--scope', 'motor', '--yes'], fresh);
163
+ const reanchorOut = reanchor.stdout || '';
164
+ const afterMf = JSON.parse(fs.readFileSync(staleMfPath, 'utf8'));
165
+ check('no-op (nada a escrever) ainda sai 0', reanchor.status === 0 && reanchorOut.includes('Motor já sincronizado'), `exit ${reanchor.status}`);
166
+ check(
167
+ 'SF11.1: no-op RECARIMBA frameworkRoot órfão para o repo que rodou o comando',
168
+ afterMf.frameworkRoot === REPO_ROOT,
169
+ `${orphanRoot} → ${afterMf.frameworkRoot}`
170
+ );
171
+ check('SF11.1: no-op atualiza updatedAt', afterMf.updatedAt !== '2000-01-01T00:00:00.000Z' && !Number.isNaN(Date.parse(afterMf.updatedAt)), afterMf.updatedAt);
172
+ check(
173
+ 'SF11.1: no-op NÃO altera nenhuma entrada de files (nada perdido, nada reclassificado)',
174
+ JSON.stringify(afterMf.files) === JSON.stringify(beforeMf.files),
175
+ `${beforeMf.files.length} → ${afterMf.files.length} entradas`
176
+ );
177
+ check('SF11.1: no-op NÃO bumpa version nem mexe em installedAt', afterMf.version === beforeMf.version && afterMf.installedAt === beforeMf.installedAt);
178
+ check(
179
+ 'SF11.1: o relatório do no-op não alega escrita de arquivo',
180
+ /0 arquivos escritos/.test(reanchorOut) && !/Sobrescritos \(defasados\)/.test(reanchorOut)
181
+ );
182
+ // O campo recarimbado é utilizável, não só bonito: o CLI que ele aponta executa.
183
+ const reanchoredCli = path.join(afterMf.frameworkRoot, 'bin', 'cli.js');
184
+ const reanchoredProbe = fs.existsSync(reanchoredCli)
185
+ ? spawnSync(process.execPath, [reanchoredCli, '--version'], { encoding: 'utf8', timeout: 60000 })
186
+ : { status: 1, stdout: '' };
187
+ check(
188
+ 'SF11.1: ACIONABILIDADE — o frameworkRoot recarimbado hospeda um CLI que executa',
189
+ reanchoredProbe.status === 0 && (reanchoredProbe.stdout || '').trim() === require('../package.json').version,
190
+ `exit ${reanchoredProbe.status}, stdout "${(reanchoredProbe.stdout || '').trim()}"`
191
+ );
192
+
193
+ // A4. SF11.1 — guard proativo do doctor sobre frameworkRoot (AVISO, nunca FALHA).
194
+ fs.writeFileSync(staleMfPath, JSON.stringify({ ...afterMf, frameworkRoot: orphanRoot }, null, 2) + '\n');
195
+ const docOrphan = cli(['doctor'], fresh);
196
+ const docOrphanOut = docOrphan.stdout || '';
197
+ check(
198
+ 'SF11.1: doctor AVISA quando frameworkRoot não hospeda bin/cli.js',
199
+ /\[AVISO\].*frameworkRoot do manifest/.test(docOrphanOut),
200
+ (docOrphanOut.split('\n').find((l) => /frameworkRoot do manifest/.test(l)) || '').trim()
201
+ );
202
+ check('SF11.1: o aviso cita o caminho problemático', docOrphanOut.includes(orphanRoot));
203
+ check('SF11.1: o aviso cita o comando de correção', /frameworkRoot do manifest.*update --scope motor/.test(docOrphanOut));
204
+ check('SF11.1: o guard de frameworkRoot NUNCA é FALHA (registro, não oráculo)', !/\[FALHA\].*frameworkRoot do manifest/.test(docOrphanOut));
205
+ // Positivo: com o campo válido, o mesmo check passa como OK — nenhum falso-positivo
206
+ // numa instalação sadia (é o caso dos checkouts/worktrees reais em uso).
207
+ cli(['update', '--scope', 'motor', '--yes'], fresh);
208
+ const docHealthy = cli(['doctor'], fresh);
209
+ check(
210
+ 'SF11.1: frameworkRoot válido → check passa OK (sem falso-positivo)',
211
+ /\[OK\s*\].*frameworkRoot do manifest/.test(docHealthy.stdout || ''),
212
+ (String(docHealthy.stdout || '').split('\n').find((l) => /frameworkRoot do manifest/.test(l)) || '').trim()
213
+ );
214
+
215
+ // Escopo inválido e uso fora do update.
216
+ const badScope = cli(['update', '--scope', 'tudo', '--yes'], fresh);
217
+ check('escopo desconhecido é recusado (exit 2)', badScope.status === 2, `exit ${badScope.status}`);
218
+ const scopeOnInstall = cli(['doctor', '--scope', 'motor'], fresh);
219
+ check('--scope fora do update é recusado (exit 2)', scopeOnInstall.status === 2, `exit ${scopeOnInstall.status}`);
220
+
221
+ // planMotorSync é puro: chamá-lo não muda disco.
222
+ const { planMotorSync } = require('../installer/update');
223
+ const beforeRunner = hash(path.join(fresh, 'scripts', 'pc-run-pod.cjs'));
224
+ const plan = planMotorSync({ pkgRoot: REPO_ROOT, destDir: fresh });
225
+ check('planMotorSync não escreve nada', hash(path.join(fresh, 'scripts', 'pc-run-pod.cjs')) === beforeRunner);
226
+ check('planMotorSync classifica todos os estados conhecidos', plan.every((r) => ['same', 'stale', 'missing', 'skipped-owner'].includes(r.state)));
227
+
228
+ // ---------------------------------------------------------------------
229
+ // B. Atribuição de skew de capacidade (DF-SF10.20-4).
230
+ // ---------------------------------------------------------------------
231
+ const runner = require('../scripts/pc-run-pod.cjs');
232
+ const wfDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-skew-wf-'));
233
+ tmpdirs.push(wfDir);
234
+
235
+ // (i) tipo de veto que este motor não conhece.
236
+ const wfUnknownType = path.join(wfDir, 'wf-unknown-type.yaml');
237
+ fs.writeFileSync(wfUnknownType, [
238
+ 'name: skew-fixture', 'version: 0.1', 'steps:',
239
+ ' - id: alfa', ' agent: a', ' output: out/a.md',
240
+ ' veto:', ' assertions:',
241
+ ' - type: capacidade_do_futuro', ' section: "## X"',
242
+ '',
243
+ ].join('\n'));
244
+ const gapsA = runner.capabilityGaps(wfUnknownType);
245
+ check('capabilityGaps detecta tipo de veto desconhecido', gapsA.length === 1 && gapsA[0].includes('capacidade_do_futuro'), gapsA[0]);
246
+
247
+ // (ii) command_check apontando para blessed não instalado neste motor.
248
+ const wfMissingBlessed = path.join(wfDir, 'wf-missing-blessed.yaml');
249
+ fs.writeFileSync(wfMissingBlessed, [
250
+ 'name: skew-fixture', 'version: 0.1', 'steps:',
251
+ ' - id: alfa', ' agent: a', ' output: out/a.md',
252
+ ' veto:', ' assertions:',
253
+ ' - type: command_check', ' command: gate-que-nao-existe.cjs',
254
+ '',
255
+ ].join('\n'));
256
+ const gapsB = runner.capabilityGaps(wfMissingBlessed);
257
+ check('capabilityGaps detecta blessed ausente', gapsB.length === 1 && gapsB[0].includes('gate-que-nao-existe.cjs'), gapsB[0]);
258
+
259
+ // (iii) wf sadio: zero gap → nenhuma atribuição espúria.
260
+ const wfSane = path.join(wfDir, 'wf-sane.yaml');
261
+ fs.writeFileSync(wfSane, [
262
+ 'name: skew-fixture', 'version: 0.1', 'steps:',
263
+ ' - id: alfa', ' agent: a', ' output: out/a.md',
264
+ ' veto:', ' assertions:',
265
+ ' - type: section_exists', ' section: "## X"',
266
+ '',
267
+ ].join('\n'));
268
+ check('wf sadio não produz gap (sem atribuição espúria)', runner.capabilityGaps(wfSane).length === 0);
269
+
270
+ const plain = new Error('etapa "alfa": algum outro erro de contrato');
271
+ const kept = runner.annotateCapabilitySkew(plain, wfSane);
272
+ check('falha de contrato comum mantém a mensagem intacta', kept.message === 'etapa "alfa": algum outro erro de contrato');
273
+
274
+ // (iv) contrato da mensagem enriquecida.
275
+ const annotated = runner.annotateCapabilitySkew(new Error('etapa "alfa": veto.assertions[0].type inválido'), wfUnknownType);
276
+ const msg = annotated.message;
277
+ const required = [
278
+ ['preserva a mensagem original', 'veto.assertions[0].type inválido'],
279
+ ['atribui a falha à INSTALAÇÃO', 'ATRIBUIÇÃO: esta falha é da INSTALAÇÃO'],
280
+ ['nomeia o motor por path absoluto', path.join(REPO_ROOT, 'scripts', 'pc-run-pod.cjs')],
281
+ ['declara o estado da instalação', 'Instalação:'],
282
+ ['nomeia a capacidade que falta', 'capacidade_do_futuro'],
283
+ ['declara a causa 1 (instalação defasada)', 'INSTALAÇÃO DEFASADA'],
284
+ ['declara a causa 2 (wf inválido em qualquer versão)', 'não existe em nenhuma versão do motor'],
285
+ ];
286
+ for (const [label, needle] of required) check(`mensagem: ${label}`, msg.includes(needle), needle);
287
+ check('exceção mantém classe e identidade (só texto muda)', annotated instanceof Error && Array.isArray(annotated.capabilityGaps));
288
+
289
+ // (v) prova de execução: loadWorkflow falha ANTES de qualquer estado, com atribuição.
290
+ let threw = null;
291
+ try { runner.loadWorkflow(wfMissingBlessed, wfDir); } catch (e) { threw = e; }
292
+ check('loadWorkflow lança no preflight (antes de criar estado)', threw !== null);
293
+ check('mensagem do loadWorkflow carrega a atribuição', Boolean(threw) && threw.message.includes('ATRIBUIÇÃO: esta falha é da INSTALAÇÃO'));
294
+
295
+ // ---------------------------------------------------------------------
296
+ // C. Detector fora-de-banda do `doctor` (DF-SF10.20-4, item 2). A assimetria
297
+ // que importa: o doctor roda do FRAMEWORK mas executa o validador do PROJETO.
298
+ // ---------------------------------------------------------------------
299
+ const docDir = makeFixture();
300
+ tmpdirs.push(docDir);
301
+ cli(['adopt', '--yes'], docDir);
302
+ cli(['update', '--scope', 'motor', '--yes'], docDir);
303
+ // Pod-canário COMPLETO e válido em pods/demo (wf + templates) — para que a única
304
+ // diferença entre o caso positivo e o negativo seja a CAPACIDADE exigida, e não
305
+ // um wf malformado por outro motivo.
306
+ const canaryDir = path.join(REPO_ROOT, 'scripts', '__fixtures__', 'smoke-canary');
307
+ // SF11.2/DF-SF11.2-4: os `agents/*.md` entram na cópia porque a leitura obrigatória da
308
+ // etapa passou a ser verificada em disco (runner e validador). Sem eles o wf seria
309
+ // inválido por OUTRO motivo, contaminando o contraste positivo×negativo deste bloco.
310
+ for (const rel of ['workflows/wf-canary.yaml', 'templates/research-tmpl.md', 'templates/report-tmpl.md', 'templates/synth-tmpl.md',
311
+ 'agents/canary-researcher.md', 'agents/canary-synth.md', 'agents/canary-reporter.md']) {
312
+ cp(path.join(canaryDir, ...rel.split('/')), path.join(docDir, 'pods', 'demo', ...rel.split('/')));
313
+ }
314
+ const demoWf = path.join(docDir, 'pods', 'demo', 'workflows', 'wf-canary.yaml');
315
+ const canaryText = fs.readFileSync(demoWf, 'utf8');
316
+
317
+ // Negativa: o MESMO wf, com um único `type` trocado por capacidade que este motor
318
+ // não tem → o doctor FALHA e nomeia o wf.
319
+ fs.writeFileSync(demoWf, canaryText.replace('type: field_matches', 'type: phash_similarity'));
320
+ const docBad = cli(['doctor'], docDir);
321
+ const badOut = docBad.stdout || '';
322
+ check('doctor FALHA quando um wf instalado não é carregável pelo motor instalado', /\[FALHA\].*wf executável pelo motor instalado: pods\/demo\/workflows\/wf-canary\.yaml/.test(badOut));
323
+ check('doctor sai não-zero nesse caso', docBad.status !== 0, `exit ${docBad.status}`);
324
+ // Acionabilidade também na saída do doctor — mesma régua da seção D.
325
+ const docCli = [...badOut.matchAll(/node\s+(\S+bin\/cli\.js)/g)].map((m) => m[1]);
326
+ check('doctor dá comando de correção com caminho concreto', docCli.length > 0, docCli[0]);
327
+ check('ACIONABILIDADE: caminho de CLI impresso pelo doctor EXISTE', docCli.length > 0 && docCli.every((p) => fs.existsSync(p)));
328
+ check('doctor NÃO manda rodar npx power-claude', !/\bnpx\s+power-claude\b/.test(badOut));
329
+
330
+ // Positiva: o MESMO wf, íntegro → o mesmo check passa. (Prova que o FALHA acima é
331
+ // da capacidade, não de um efeito colateral do fixture.)
332
+ fs.writeFileSync(demoWf, canaryText);
333
+ const docGood = cli(['doctor'], docDir);
334
+ check('doctor passa no check quando o wf é carregável', /\[OK\s*\].*wf executável pelo motor instalado: pods\/demo\/workflows\/wf-canary\.yaml/.test(docGood.stdout || ''));
335
+
336
+ // ---------------------------------------------------------------------
337
+ // D. ACIONABILIDADE do comando de correção (finding 1 do gate SF10.20).
338
+ //
339
+ // Este bloco NÃO assere a string do comando. Asserir texto que o próprio dev
340
+ // escreveu é auto-atestação (lição SF10.8) — foi exatamente assim que passou a
341
+ // receita `npx power-claude`, que numa instalação sem package.json/node_modules/
342
+ // PATH resolve pelo REGISTRY PÚBLICO, onde o nome é de um pacote de TERCEIRO.
343
+ // Aqui se verifica que o comando IMPRESSO é executável no contexto em que é
344
+ // impresso: o path existe e responde como o CLI deste framework.
345
+ //
346
+ // Determinístico e OFFLINE: o único processo executado é `bin/cli.js --version`
347
+ // deste repositório. Nada é resolvido por rede, nada de terceiro é baixado.
348
+ // ---------------------------------------------------------------------
349
+
350
+ // O motor da INSTALAÇÃO (não o do framework) é quem produz a mensagem real.
351
+ const installedRunner = require(path.join(docDir, 'scripts', 'pc-run-pod.cjs'));
352
+ const wfGapInstalled = path.join(docDir, 'wf-gap.yaml');
353
+ fs.writeFileSync(wfGapInstalled, fs.readFileSync(wfUnknownType, 'utf8'));
354
+ const installedMsg = installedRunner.annotateCapabilitySkew(new Error('falha de contrato'), wfGapInstalled).message;
355
+
356
+ check('mensagem da instalação NÃO manda rodar npx (supply-chain do registry público)', !/\bnpx\s+power-claude\b/.test(installedMsg.replace('NÃO use "npx power-claude"', '')));
357
+
358
+ const cliMatches = [...installedMsg.matchAll(/node\s+(\S+bin\/cli\.js)/g)].map((m) => m[1]).filter((p) => !/[<>]/.test(p));
359
+ check('mensagem imprime um comando concreto com caminho para o CLI', cliMatches.length > 0, cliMatches[0]);
360
+ const allExist = cliMatches.length > 0 && cliMatches.every((p) => fs.existsSync(p));
361
+ check('ACIONABILIDADE: todo caminho de CLI impresso EXISTE em disco', allExist, cliMatches.join(' '));
362
+
363
+ if (allExist) {
364
+ const probe = spawnSync(process.execPath, [cliMatches[0], '--version'], { encoding: 'utf8', timeout: 60000 });
365
+ const version = require('../package.json').version;
366
+ check('ACIONABILIDADE: o CLI impresso executa e é ESTE framework', probe.status === 0 && (probe.stdout || '').trim() === version, `exit ${probe.status}, stdout "${(probe.stdout || '').trim()}" (esperado ${version})`);
367
+ const helpProbe = spawnSync(process.execPath, [cliMatches[0], '--help'], { encoding: 'utf8', timeout: 60000 });
368
+ check('ACIONABILIDADE: o CLI impresso reconhece os subcomandos citados', probe.status === 0 && /\badopt\b/.test(helpProbe.stdout || '') && /\bupdate\b/.test(helpProbe.stdout || ''));
369
+ }
370
+
371
+ // Degradação honesta: manifest apontando para framework inexistente.
372
+ const mfP = path.join(docDir, '.power-claude', 'manifest.json');
373
+ const mfOrig = fs.readFileSync(mfP, 'utf8');
374
+ const mfBroken = JSON.parse(mfOrig);
375
+ mfBroken.frameworkRoot = path.join(os.tmpdir(), 'power-claude-que-nao-existe');
376
+ fs.writeFileSync(mfP, JSON.stringify(mfBroken, null, 2));
377
+ delete require.cache[require.resolve(path.join(docDir, 'scripts', 'pc-run-pod.cjs'))];
378
+ const brokenRunner = require(path.join(docDir, 'scripts', 'pc-run-pod.cjs'));
379
+ const brokenMsg = brokenRunner.annotateCapabilitySkew(new Error('falha de contrato'), wfGapInstalled).message;
380
+ // Invariante: todo caminho CONCRETO impresso existe. Placeholder (`<...>`) é
381
+ // legítimo — anuncia que o caminho é desconhecido em vez de inventar um.
382
+ const brokenConcrete = [...brokenMsg.matchAll(/node\s+(\S+bin\/cli\.js)/g)].map((m) => m[1]).filter((p) => !/[<>]/.test(p));
383
+ check('framework movido/apagado: NÃO imprime comando concreto falso', brokenConcrete.length === 0, `concretos impressos: ${JSON.stringify(brokenConcrete)}`);
384
+ check('framework movido/apagado: degrada para placeholder explícito', brokenMsg.includes('<repo-power-claude>'));
385
+ check('framework movido/apagado: avisa contra npx', brokenMsg.includes('NÃO use "npx power-claude"'));
386
+ fs.writeFileSync(mfP, mfOrig);
387
+
388
+ // O manifest é quem carrega o caminho — carimbado pelo instalador, não adivinhado.
389
+ check('manifest registra frameworkRoot absoluto', JSON.parse(mfOrig).frameworkRoot === REPO_ROOT, JSON.parse(mfOrig).frameworkRoot);
390
+
391
+ // AVISO de versão: instalação atrás do framework.
392
+ const { compareVersions } = require('../installer/doctor');
393
+ check('compareVersions ordena versões corretamente', compareVersions('0.14.9', '0.15.0') < 0 && compareVersions('0.15.0', '0.15.0') === 0 && compareVersions('0.16.0', '0.15.0') > 0);
394
+ const mfPath = path.join(docDir, '.power-claude', 'manifest.json');
395
+ const mf = JSON.parse(fs.readFileSync(mfPath, 'utf8'));
396
+ mf.version = '0.0.1';
397
+ fs.writeFileSync(mfPath, JSON.stringify(mf, null, 2));
398
+ const docOld = cli(['doctor'], docDir);
399
+ check('doctor AVISA quando a instalação está atrás do framework', /\[AVISO\].*Versão da instalação vs\. framework/.test(docOld.stdout || ''));
400
+ check('o AVISO de versão NÃO é bloqueante (é o gate de capacidade que bloqueia)', !/\[FALHA\].*Versão da instalação/.test(docOld.stdout || ''));
401
+ } finally {
402
+ finish(tmpdirs);
403
+ }
404
+ }
405
+
406
+ function finish(tmpdirs) {
407
+ for (const d of tmpdirs) {
408
+ try { fs.rmSync(d, { recursive: true, force: true }); } catch { /* best-effort */ }
409
+ }
410
+ const failed = results.filter((r) => !r.ok);
411
+ console.log(`\n${results.length - failed.length}/${results.length} checagens passaram.`);
412
+ if (failed.length > 0) {
413
+ console.error('validate-update-scope-motor FALHOU:');
414
+ for (const f of failed) console.error(` - ${f.label}${f.detail ? ` (${f.detail})` : ''}`);
415
+ process.exit(1);
416
+ }
417
+ console.log('validate-update-scope-motor verde.');
418
+ process.exit(0);
419
+ }
420
+
421
+ main();
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * html-valid.cjs — veto BLESSED (forma command_check, ADR-011). Confere que o HTML do
5
+ * artefato tem tags BALANCEADAS (parser de balanceamento mínimo, zero-dep — não htmlhint,
6
+ * por decisão do ADR-011 Decisão 3) e, opcionalmente, que tags exigidas estão presentes.
7
+ * Caso motivador (d): o `<title>` levou 3 rodadas (SF6.2/6.3/6.4) para fechar só com
8
+ * heurística de memória — `--require title` mecaniza a exigência.
9
+ *
10
+ * Uso: node scripts/veto-checks/html-valid.cjs <artefato> [--require <tag>]...
11
+ * Exit 0 = balanceado (e requeridos presentes); exit 1 = desbalanceado/faltando; exit 2 = uso.
12
+ *
13
+ * LIMITES HONESTOS (documentados): é um balanceador de tags, NÃO um validador HTML5
14
+ * completo — não checa aninhamento semântico (ex.: <li> fora de <ul>), atributos, nem
15
+ * DOCTYPE. Prova a CLASSE "tag aberta sem fechar / fechamento órfão / tag exigida ausente",
16
+ * que é o defeito de campo do caso (d). Void elements e self-closing são tratados.
17
+ */
18
+
19
+ const fs = require('node:fs');
20
+
21
+ const VOID_ELEMENTS = new Set([
22
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link',
23
+ 'meta', 'param', 'source', 'track', 'wbr',
24
+ ]);
25
+
26
+ /** Balanceia tags. Retorna { ok, error }. Pura, testável. */
27
+ function checkBalance(html) {
28
+ // Remove comentários, <script>/<style> (conteúdo não é markup), e DOCTYPE.
29
+ const cleaned = html
30
+ .replace(/<!--[\s\S]*?-->/g, '')
31
+ .replace(/<script\b[\s\S]*?<\/script>/gi, '')
32
+ .replace(/<style\b[\s\S]*?<\/style>/gi, '')
33
+ .replace(/<!doctype[^>]*>/gi, '');
34
+ const tagRe = /<\/?([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*?(\/?)>/g;
35
+ const stack = [];
36
+ let m;
37
+ while ((m = tagRe.exec(cleaned)) !== null) {
38
+ const raw = m[0];
39
+ const name = m[1].toLowerCase();
40
+ const selfClosing = m[2] === '/';
41
+ const isClose = raw.startsWith('</');
42
+ if (isClose) {
43
+ if (VOID_ELEMENTS.has(name)) continue; // </br> etc. é ruído inofensivo
44
+ if (stack.length === 0 || stack[stack.length - 1] !== name) {
45
+ const top = stack.length ? stack[stack.length - 1] : '(nenhuma)';
46
+ return { ok: false, error: `fechamento </${name}> sem abertura correspondente (topo da pilha: <${top}>)` };
47
+ }
48
+ stack.pop();
49
+ } else if (!selfClosing && !VOID_ELEMENTS.has(name)) {
50
+ stack.push(name);
51
+ }
52
+ }
53
+ if (stack.length > 0) {
54
+ return { ok: false, error: `tag(s) aberta(s) sem fechar: ${stack.map((t) => `<${t}>`).join(', ')}` };
55
+ }
56
+ return { ok: true };
57
+ }
58
+
59
+ /** Presença de uma tag (abertura) no HTML. */
60
+ function hasTag(html, tag) {
61
+ return new RegExp(`<${tag}\\b`, 'i').test(html);
62
+ }
63
+
64
+ function main() {
65
+ const artifact = process.argv[2];
66
+ if (!artifact || !fs.existsSync(artifact)) {
67
+ console.error(`html-valid: artefato não encontrado: ${artifact}`);
68
+ process.exit(2);
69
+ }
70
+ const required = [];
71
+ const rest = process.argv.slice(3);
72
+ for (let i = 0; i < rest.length; i++) {
73
+ if (rest[i] === '--require') required.push(String(rest[++i]).toLowerCase());
74
+ }
75
+ const html = fs.readFileSync(artifact, 'utf8');
76
+ const bal = checkBalance(html);
77
+ if (!bal.ok) {
78
+ console.error(`html-valid BLOQUEOU: ${bal.error}`);
79
+ process.exit(1);
80
+ }
81
+ const missing = required.filter((t) => !hasTag(html, t));
82
+ if (missing.length > 0) {
83
+ console.error(`html-valid BLOQUEOU: tag(s) exigida(s) ausente(s): ${missing.map((t) => `<${t}>`).join(', ')}`);
84
+ process.exit(1);
85
+ }
86
+ console.log(`html-valid OK: tags balanceadas${required.length ? `; requeridas presentes (${required.join(', ')})` : ''}`);
87
+ process.exit(0);
88
+ }
89
+
90
+ if (require.main === module) main();
91
+
92
+ module.exports = { checkBalance, hasTag, VOID_ELEMENTS };
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * lexical-check.cjs — veto BLESSED (forma command_check, ADR-011). Conta ocorrências de
5
+ * um padrão lexical no artefato e BLOQUEIA se acima de um limiar. Caso motivador (b): a
6
+ * regressão de travessões (em/en-dash) na R5 do benchmark passou por toda heurística de
7
+ * memória — a máquina agora conta de verdade. Node puro, zero-dep, determinístico e síncrono
8
+ * (fronteira ADR-002: forma avaliável por máquina, nunca julgamento de LLM).
9
+ *
10
+ * Uso (invocado pelo runner via execFileSync, sem shell):
11
+ * node scripts/veto-checks/lexical-check.cjs <artefato> [--max N] [--pattern <regex-literal>]
12
+ * default: --pattern "[—–]" (em-dash U+2014 / en-dash U+2013 — o "tell" de IA) --max 0
13
+ * Exit 0 = dentro do limiar; exit 1 = acima (bloqueia); exit 2 = erro de uso.
14
+ */
15
+
16
+ const fs = require('node:fs');
17
+
18
+ /** Conta ocorrências de `pattern` (regex source) em `text`. Pura, testável. */
19
+ function countMatches(text, patternSource) {
20
+ const re = new RegExp(patternSource, 'gu');
21
+ const m = text.match(re);
22
+ return m ? m.length : 0;
23
+ }
24
+
25
+ function parseArgs(argv) {
26
+ const opts = { max: 0, pattern: '[\\u2014\\u2013]' };
27
+ for (let i = 0; i < argv.length; i++) {
28
+ if (argv[i] === '--max') opts.max = Number.parseInt(argv[++i], 10);
29
+ else if (argv[i] === '--pattern') opts.pattern = argv[++i];
30
+ }
31
+ return opts;
32
+ }
33
+
34
+ function main() {
35
+ const artifact = process.argv[2];
36
+ if (!artifact || !fs.existsSync(artifact)) {
37
+ console.error(`lexical-check: artefato não encontrado: ${artifact}`);
38
+ process.exit(2);
39
+ }
40
+ const opts = parseArgs(process.argv.slice(3));
41
+ if (!Number.isInteger(opts.max) || opts.max < 0) {
42
+ console.error(`lexical-check: --max inválido: ${opts.max}`);
43
+ process.exit(2);
44
+ }
45
+ let count;
46
+ try {
47
+ count = countMatches(fs.readFileSync(artifact, 'utf8'), opts.pattern);
48
+ } catch (e) {
49
+ console.error(`lexical-check: regex inválida /${opts.pattern}/: ${e.message}`);
50
+ process.exit(2);
51
+ }
52
+ if (count > opts.max) {
53
+ console.error(`lexical-check BLOQUEOU: ${count} ocorrência(s) de /${opts.pattern}/ > limiar ${opts.max}`);
54
+ process.exit(1);
55
+ }
56
+ console.log(`lexical-check OK: ${count} ocorrência(s) de /${opts.pattern}/ <= ${opts.max}`);
57
+ process.exit(0);
58
+ }
59
+
60
+ if (require.main === module) main();
61
+
62
+ module.exports = { countMatches, parseArgs };