@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,589 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ /**
4
+ * phash-gate.cjs — script BLESSED (forma command_check, ADR-011) de MEDIDA da não-cópia.
5
+ * Story SF10.8 (origem) → **Story SF10.19 / ADR-018 Adendo F6-b (DF-SF10.19-1/-2)**, que tirou
6
+ * daqui a metade bloqueante. / ADR-015 Decisão 3.
7
+ *
8
+ * ┌─ ESTE SCRIPT NÃO BLOQUEIA NADA POR CONTEÚDO — E NÃO PODE VOLTAR A BLOQUEAR ────────────┐
9
+ * │ Ele MEDE e REPORTA. Nenhum estado de insumo (referência ausente, `assets/` vazio, │
10
+ * │ ledger ausente/ilegível/de versão desconhecida, sha divergente, Chrome ausente) produz │
11
+ * │ exit não-zero: todos viram `measure.status` DECLARADO, com o motivo, no bloco do │
12
+ * │ artefato e no ledger quando ele existir. O único exit não-zero que resta é 2 — │
13
+ * │ uso/config inválida (artefato inexistente, `--refs-dir`/`--assets-dir` escapando do │
14
+ * │ runDir): erro de WIRING, idêntico nos dois drivers, nunca julgamento de conteúdo. │
15
+ * └───────────────────────────────────────────────────────────────────────────────────────┘
16
+ *
17
+ * ┌─ ONDE FOI PARAR O QUE BLOQUEAVA (o invariante NÃO foi afrouxado) ──────────────────────┐
18
+ * │ A PROVENIÊNCIA (`DF5-6/e1`) continua bloqueando, agora no `pod-export-executor.cjs` │
19
+ * │ (`tools.assertRunProvenance`, antes de `motor.advance`) — HALT do run, sem retentativa. │
20
+ * │ POR QUÊ: este arquivo é executado a partir de um `wf-*.yaml`, e o MESMO yaml é consumido│
21
+ * │ por dois drivers — `pc-run-pod.cjs` (dev: subagente com Bash/Write) e o executor (export│
22
+ * │ 4 tools confinadas). Proveniência é uma invariante de ORIGEM: ela só é verificável onde │
23
+ * │ a superfície de tools é confinada. No dev, o agente grava `assets/` por fora de qualquer│
24
+ * │ registro e alcança o ledger com shell — o mesmo verde teria significado DIFERENTE │
25
+ * │ conforme quem rodou, que é gate-teatro (`DF4-5`). Régua permanente (`DF-SF10.19-1`): │
26
+ * │ • propriedade do ARTEFATO/disco (verificável independentemente de quem produziu) │
27
+ * │ → PODE viver no wf, e nunca degrada em nenhum driver; │
28
+ * │ • invariante de ORIGEM/CONFINAMENTO → vive no componente que CONFINA, nunca no wf. │
29
+ * │ Esta mudança é INCONDICIONAL: não existe flag, env var, campo de modo nem ramo │
30
+ * │ "dev vs export" aqui — quem passa a bloquear é um componente que só existe no export. │
31
+ * └───────────────────────────────────────────────────────────────────────────────────────┘
32
+ *
33
+ * ┌─ O QUE ELE FAZ: SIMILARIDADE (medida REPORTADA, sem pass/fail) ────────────────────────┐
34
+ * │ A distância perceptual peça↔referência é calculada e GRAVADA (no ledger e num bloco │
35
+ * │ delimitado do artefato) para INSTRUMENTAR o checkpoint humano de `assembly`. │
36
+ * │ Não há limiar: a não-cópia por ADERÊNCIA ESTILÍSTICA é julgamento HUMANO. E o bloco │
37
+ * │ DECLARA o que não foi verificado — no driver de dev, a proveniência não é verificada │
38
+ * │ por ninguém, e omitir isso seria pior que não medir (`DF-SF10.19-3`). │
39
+ * └───────────────────────────────────────────────────────────────────────────────────────┘
40
+ *
41
+ * POR QUE MUDOU (registro da falsificação, não escondê-la — DF5-5): a versão anterior deste
42
+ * script bloqueava por "distância de Hamming <= 10/64 = cópia". O gate adversarial do QA
43
+ * (`docs/qa/gates/sf10.8.yaml`, CAL-1) e as medições próprias do architect mostraram que
44
+ * (a) NENHUM limiar separa as classes em conteúdo real (capa legítima independente = 10
45
+ * BLOQUEAVA; cópia fiel reenquadrada 4:5 = 12 PASSAVA; o cover-crop com âncora deslocada mede
46
+ * 32-36, dentro da massa legítima), em 5 primitivas medidas; e (b) mais decisivo: a classe
47
+ * "cópia pixel-a-pixel" contra a qual o limiar foi calibrado é INALCANÇÁVEL pela superfície de
48
+ * tools — `write_file` grava utf8 e a única escrita de bytes é `openrouter_image`, cujo
49
+ * conteúdo vem da API. Um limiar contra ameaça impossível é teatro (vedado por DF4-5).
50
+ *
51
+ * NO EXPORT, os dois lados são materializados POR CÓDIGO, nenhum atestado pelo agente (DF5-1):
52
+ * - geradas: `<runDir>/assets/**` — escritas pela tool `openrouter_image`, que REGISTRA
53
+ * o sha256 de cada gravação;
54
+ * - referência: `<runDir>/inputs/**` — copiada do `inputs_dir` do run pelo EXECUTOR
55
+ * (`materializeInputs`), que também REGISTRA o sha256; e as tools NEGAM
56
+ * escrita sob `inputs/` (camada 1 de INTEG-1).
57
+ * NO DEV nada disso existe (o motor é cego ao `inputs_dir` e o agente escreve com Bash/Write) —
58
+ * por isso a medida degrada declarada em vez de bloquear, e o bloco diz o que não foi verificado.
59
+ * Os diretórios chegam como ARGS LITERAIS FIXOS relativos ao diretório do artefato
60
+ * (`--refs-dir ../inputs`, `--assets-dir ../assets`) — nenhum dado de run é interpolado no
61
+ * wf.yaml (ADR-011 preservado); o `runDir` é derivado do `artifactPath` que o `command_check`
62
+ * já passa (invariante `runDir/artifacts/<step>.md`).
63
+ *
64
+ * ZERO-DEP npm: a MEDIDA reportada reusa o headless Chrome que o
65
+ * `render-gate.cjs` já tornou dependência v1 (mesmo `execFileSync` sem shell, mesmo
66
+ * `probeChrome`, mesmo marcador lido do `--dump-dom`): `<canvas>` + `getImageData` devolvem um
67
+ * grayscale 32×32 por imagem e o hash perceptual (DCT 2D → bloco 8×8 → mediana → 64 bits) é
68
+ * aritmética em JS PURO aqui dentro. Nenhuma lib npm, nenhuma ferramenta de ops nova.
69
+ *
70
+ * FAIL-LOUD sem virar gate: como este script não decide mais nada, "pular" não é um risco dele —
71
+ * o risco é ele MENTIR por omissão. Por isso toda degradação é DECLARADA (status + motivo) no
72
+ * artefato e no ledger; nada é silencioso, e nada vira exit não-zero (ADR-011 D3 vale para o
73
+ * componente que É gate, e esse componente agora é o executor).
74
+ *
75
+ * POR-ARQUIVO (DF5-3, VIGENTE): a asserção do executor é por-arquivo; aqui a medida é por-peça.
76
+ *
77
+ * LIMITES HONESTOS (o que este script NÃO faz):
78
+ * - NÃO mecaniza a não-cópia por ADERÊNCIA ESTILÍSTICA (ressíntese generativa que "adere
79
+ * demais" à referência): isso permanece julgamento HUMANO no checkpoint de `assembly`,
80
+ * agora instrumentado pela medida reportada. Mecanizar essa classe exige corpus rotulado
81
+ * por humano — é épico próprio (COMPLEX 19/25, DF5-8);
82
+ * - a medida perceptual mede layout grosseiro de luminância; NÃO é plágio jurídico, não é
83
+ * invariante a rotação/espelhamento, e não separa classes (por isso não é gate);
84
+ * - NÃO verifica proveniência (quem verifica é o executor de export; no driver de dev
85
+ * ninguém verifica — o bloco de medida diz isso por extenso, DF-SF10.19-3).
86
+ *
87
+ * Uso: node scripts/veto-checks/phash-gate.cjs <artefato> [--refs-dir ../inputs]
88
+ * [--assets-dir ../assets] (args de CLI INALTERADOS — bundles já compilados seguem válidos)
89
+ * Exit 0 = medida gravada ou declarada ausente/não-verificada (SEMPRE, em qualquer estado de
90
+ * insumo); 2 = uso/config inválida (artefato inexistente, dir escapando do runDir). Não há
91
+ * outro exit: 1 e 3 foram REMOVIDOS junto com a metade bloqueante (DF-SF10.19-2/e1).
92
+ */
93
+
94
+ const fs = require('node:fs');
95
+ const os = require('node:os');
96
+ const path = require('node:path');
97
+ const { execFileSync } = require('node:child_process');
98
+
99
+ const CHROME_CANDIDATES = [
100
+ 'google-chrome-stable', 'google-chrome', 'chromium', 'chromium-browser', 'chrome',
101
+ '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
102
+ '/Applications/Chromium.app/Contents/MacOS/Chromium',
103
+ ];
104
+ const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp']);
105
+ const SIDE = 32; // grayscale normalizado 32×32 (entrada da DCT)
106
+ const LOW = 8; // bloco 8×8 de baixa frequência → 64 bits
107
+ const MAX_TOTAL_BYTES = 48 * 1024 * 1024; // teto de material embutido no HTML temporário
108
+ const MAX_WALK_DEPTH = 3;
109
+
110
+ // PATH do registro de proveniência. Este script é BLESSED (fronteira `command_check`, ADR-011 D2)
111
+ // e não importa nada além de builtins — um require para fora de `veto-checks/` alargaria a
112
+ // superfície do blessed. Depois da SF10.19 o que sobrou aqui é só o ENDEREÇO do ledger (para
113
+ // gravar a MEDIDA nele quando ele existir); a VERIFICAÇÃO do contrato vive uma única vez em
114
+ // `pod-export-tools.cjs` (DF-SF10.19-4). A suíte assere que as duas pontas concordam no path.
115
+ const PROVENANCE_SUFFIX = '.provenance.json';
116
+ const PROVENANCE_VERSION = 1;
117
+
118
+ // Delimitadores do bloco de MEDIDA REPORTADA escrito no artefato (idempotente: reescrito, nunca
119
+ // duplicado). Comentário HTML: invisível no render, inerte para os vetos de texto do motor.
120
+ const MEASURE_BEGIN = '<!-- pc:medida-similaridade:inicio -->';
121
+ const MEASURE_END = '<!-- pc:medida-similaridade:fim -->';
122
+
123
+ /** Testa se um candidato é um Chrome utilizável (idem render-gate.cjs). */
124
+ function usable(cand) {
125
+ try {
126
+ if (cand.includes('/')) return fs.existsSync(cand) ? cand : null;
127
+ execFileSync(cand, ['--version'], { stdio: 'ignore', timeout: 8000 });
128
+ return cand;
129
+ } catch { return null; }
130
+ }
131
+
132
+ /**
133
+ * Resolve um binário de Chrome utilizável, ou null (→ fail-loud no chamador).
134
+ * CHROME_BIN setado é honrado ESTRITAMENTE: inválido → null, SEM fallback silencioso.
135
+ */
136
+ function probeChrome() {
137
+ if (process.env.CHROME_BIN) return usable(process.env.CHROME_BIN);
138
+ for (const cand of CHROME_CANDIDATES) {
139
+ const u = usable(cand);
140
+ if (u) return u;
141
+ }
142
+ return null;
143
+ }
144
+
145
+ // ---------------------------------------------------------------------------
146
+ // Hash perceptual (JS puro, testável sem Chrome).
147
+ // ---------------------------------------------------------------------------
148
+
149
+ const COS = (() => {
150
+ // COS[u][x] = cos((2x+1)·u·π / 2N) — pré-computado para a DCT-II separável.
151
+ const t = [];
152
+ for (let u = 0; u < SIDE; u++) {
153
+ const row = new Float64Array(SIDE);
154
+ for (let x = 0; x < SIDE; x++) row[x] = Math.cos(((2 * x + 1) * u * Math.PI) / (2 * SIDE));
155
+ t.push(row);
156
+ }
157
+ return t;
158
+ })();
159
+
160
+ /** DCT-II 2D (separável) de uma matriz SIDE×SIDE achatada. */
161
+ function dct2(gray) {
162
+ const tmp = new Float64Array(SIDE * SIDE);
163
+ for (let y = 0; y < SIDE; y++) {
164
+ for (let u = 0; u < LOW; u++) { // só as colunas de baixa frequência são necessárias
165
+ let s = 0;
166
+ const cu = COS[u];
167
+ for (let x = 0; x < SIDE; x++) s += gray[y * SIDE + x] * cu[x];
168
+ tmp[y * SIDE + u] = s;
169
+ }
170
+ }
171
+ const out = new Float64Array(LOW * LOW);
172
+ for (let u = 0; u < LOW; u++) {
173
+ for (let v = 0; v < LOW; v++) {
174
+ let s = 0;
175
+ const cv = COS[v];
176
+ for (let y = 0; y < SIDE; y++) s += tmp[y * SIDE + u] * cv[y];
177
+ out[v * LOW + u] = s;
178
+ }
179
+ }
180
+ return out;
181
+ }
182
+
183
+ /**
184
+ * Hash perceptual de 64 bits a partir do grayscale 32×32: DCT-II → bloco 8×8 de baixa
185
+ * frequência → bit = coeficiente > MEDIANA (mediana calculada SEM o termo DC, que carrega
186
+ * só o brilho médio). Invariante a offset de brilho e a ganho de contraste (o que dá a
187
+ * robustez a recolor/exposição); sensível à ESTRUTURA. Retorna hex de 16 chars.
188
+ */
189
+ function hashFromGray(gray) {
190
+ if (!gray || gray.length !== SIDE * SIDE) throw new Error(`grayscale deve ter ${SIDE * SIDE} amostras (recebeu ${gray ? gray.length : 'nada'})`);
191
+ const c = dct2(gray);
192
+ const vals = [];
193
+ for (let i = 1; i < LOW * LOW; i++) vals.push(c[i]); // exclui DC (i=0)
194
+ vals.sort((a, b) => a - b);
195
+ const mid = vals.length >> 1;
196
+ const median = vals.length % 2 ? vals[mid] : (vals[mid - 1] + vals[mid]) / 2;
197
+ let hex = '';
198
+ for (let nib = 0; nib < 16; nib++) {
199
+ let v = 0;
200
+ for (let b = 0; b < 4; b++) {
201
+ const i = nib * 4 + b;
202
+ v = (v << 1) | (c[i] > median ? 1 : 0);
203
+ }
204
+ hex += v.toString(16);
205
+ }
206
+ return hex;
207
+ }
208
+
209
+ const POPCOUNT = (() => { const t = new Uint8Array(256); for (let i = 0; i < 256; i++) t[i] = (i & 1) + t[i >> 1]; return t; })();
210
+
211
+ /** Distância de Hamming entre dois hashes hex de 64 bits. */
212
+ function hamming(a, b) {
213
+ if (typeof a !== 'string' || typeof b !== 'string' || a.length !== 16 || b.length !== 16) {
214
+ throw new Error('hashes devem ser hex de 16 chars (64 bits)');
215
+ }
216
+ let d = 0;
217
+ for (let i = 0; i < 16; i += 2) {
218
+ d += POPCOUNT[(parseInt(a.slice(i, i + 2), 16) ^ parseInt(b.slice(i, i + 2), 16)) & 0xFF];
219
+ }
220
+ return d;
221
+ }
222
+
223
+ // ---------------------------------------------------------------------------
224
+ // Decodificação via headless Chrome (canvas + getImageData).
225
+ // ---------------------------------------------------------------------------
226
+
227
+ /**
228
+ * Página de medição: decodifica cada imagem embutida (data: URL — NÃO tainta o canvas e
229
+ * dispensa flags de acesso a arquivo), reduz para SIDE×SIDE por downscale do próprio
230
+ * navegador, converte para luminância (Rec. 601) e publica tudo num marcador HEX no
231
+ * documentElement. Zera o corpo ao final para que o --dump-dom volte pequeno.
232
+ */
233
+ function measurePage(items) {
234
+ const payload = JSON.stringify(items.map((it) => ({ n: it.id, d: `data:${it.mediaType};base64,${it.base64}` })));
235
+ return `<!doctype html><html><head><meta charset="utf-8"></head><body>
236
+ <script id="pl" type="application/json">${payload}</script>
237
+ <script>(function(){
238
+ var SIDE=${SIDE};
239
+ function fail(msg){
240
+ try{ document.body.textContent=''; }catch(e){}
241
+ document.documentElement.setAttribute('data-phash-gate','ERR;'+String(msg).replace(/[^\\w .:\\/-]/g,' ').slice(0,300));
242
+ }
243
+ try{
244
+ var items=JSON.parse(document.getElementById('pl').textContent);
245
+ var cv=document.createElement('canvas'); cv.width=SIDE; cv.height=SIDE;
246
+ var cx=cv.getContext('2d',{willReadFrequently:true});
247
+ cx.imageSmoothingEnabled=true; cx.imageSmoothingQuality='high';
248
+ var out=[];
249
+ var pending=items.length, failed=false;
250
+ if(pending===0){ fail('nenhuma imagem recebida'); return; }
251
+ items.forEach(function(it,idx){
252
+ var img=new Image();
253
+ img.onload=function(){
254
+ try{
255
+ cx.clearRect(0,0,SIDE,SIDE);
256
+ cx.drawImage(img,0,0,SIDE,SIDE);
257
+ var d=cx.getImageData(0,0,SIDE,SIDE).data, hex='';
258
+ for(var p=0;p<SIDE*SIDE;p++){
259
+ var l=Math.round(0.299*d[p*4]+0.587*d[p*4+1]+0.114*d[p*4+2]);
260
+ if(l<0)l=0; if(l>255)l=255;
261
+ hex+=(l<16?'0':'')+l.toString(16);
262
+ }
263
+ out[idx]=it.n+':'+hex;
264
+ }catch(e){ failed=true; fail('getImageData falhou em '+it.n+': '+e.message); return; }
265
+ if(--pending===0 && !failed){
266
+ document.body.textContent='';
267
+ document.documentElement.setAttribute('data-phash-gate','OK;'+out.join('|'));
268
+ }
269
+ };
270
+ img.onerror=function(){ failed=true; fail('imagem ilegivel/nao decodificada: '+it.n); };
271
+ img.src=it.d;
272
+ });
273
+ }catch(e){ fail('medicao falhou: '+e.message); }
274
+ })();</script>
275
+ </body></html>`;
276
+ }
277
+
278
+ /** Lê o marcador do DOM renderizado e devolve { ok, gray: Map<id, Uint8Array>, error }. */
279
+ function parseMarker(domHtml) {
280
+ const m = String(domHtml).match(/data-phash-gate="([^"]*)"/);
281
+ if (!m) return { ok: false, error: 'marcador de medição ausente no DOM renderizado (a decodificação não concluiu?)' };
282
+ const raw = m[1];
283
+ if (raw.startsWith('ERR;')) return { ok: false, error: raw.slice(4) };
284
+ if (!raw.startsWith('OK;')) return { ok: false, error: `marcador inesperado: "${raw.slice(0, 80)}"` };
285
+ const gray = new Map();
286
+ for (const part of raw.slice(3).split('|')) {
287
+ const i = part.indexOf(':');
288
+ if (i < 0) return { ok: false, error: `entrada de medição malformada: "${part.slice(0, 40)}"` };
289
+ const id = part.slice(0, i);
290
+ const hex = part.slice(i + 1);
291
+ if (hex.length !== SIDE * SIDE * 2) return { ok: false, error: `grayscale de "${id}" com tamanho inesperado (${hex.length} chars)` };
292
+ const arr = new Uint8Array(SIDE * SIDE);
293
+ for (let p = 0; p < arr.length; p++) arr[p] = parseInt(hex.slice(p * 2, p * 2 + 2), 16);
294
+ gray.set(id, arr);
295
+ }
296
+ return { ok: true, gray };
297
+ }
298
+
299
+ function mediaTypeOf(p) {
300
+ const e = path.extname(p).slice(1).toLowerCase();
301
+ if (e === 'jpg' || e === 'jpeg') return 'image/jpeg';
302
+ if (e === 'svg') return 'image/svg+xml';
303
+ return `image/${e || 'png'}`;
304
+ }
305
+
306
+ /**
307
+ * Decodifica TODAS as imagens numa ÚNICA invocação de Chrome (uma etapa = poucas peças).
308
+ * Retorna Map<id, Uint8Array(1024)> ou lança Error (→ fail-loud no chamador).
309
+ */
310
+ function grayscalesViaChrome(chromeBin, files) {
311
+ let total = 0;
312
+ const items = files.map((f) => {
313
+ const buf = fs.readFileSync(f.abs);
314
+ total += buf.length;
315
+ return { id: f.id, mediaType: mediaTypeOf(f.abs), base64: buf.toString('base64') };
316
+ });
317
+ if (total > MAX_TOTAL_BYTES) {
318
+ throw new Error(`material de imagem excede ${Math.round(MAX_TOTAL_BYTES / 1048576)}MB (${Math.round(total / 1048576)}MB) — recuse em vez de arriscar medição parcial`);
319
+ }
320
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-phash-gate-'));
321
+ const tmpHtml = path.join(tmpDir, 'measure.html');
322
+ try {
323
+ fs.writeFileSync(tmpHtml, measurePage(items), 'utf8');
324
+ const dom = execFileSync(chromeBin, [
325
+ '--headless=new', '--disable-gpu', '--no-sandbox', '--hide-scrollbars',
326
+ '--window-size=64,64', '--virtual-time-budget=20000',
327
+ '--dump-dom', `file://${tmpHtml}`,
328
+ ], { encoding: 'utf8', timeout: 60000, maxBuffer: 64 * 1024 * 1024, stdio: ['ignore', 'pipe', 'ignore'] });
329
+ const r = parseMarker(dom);
330
+ if (!r.ok) throw new Error(r.error);
331
+ for (const f of files) if (!r.gray.has(f.id)) throw new Error(`medição não retornou a imagem "${f.id}"`);
332
+ return r.gray;
333
+ } finally {
334
+ try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* best-effort */ }
335
+ }
336
+ }
337
+
338
+ // ---------------------------------------------------------------------------
339
+ // Coleta de arquivos e veredito.
340
+ // ---------------------------------------------------------------------------
341
+
342
+ /** Lista imagens sob `dir` (recursivo, profundidade limitada), ordenadas — determinístico. */
343
+ function listImages(dir, depth = 0) {
344
+ if (!fs.existsSync(dir) || depth > MAX_WALK_DEPTH) return [];
345
+ const out = [];
346
+ for (const e of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))) {
347
+ if (e.name.startsWith('.')) continue;
348
+ const abs = path.join(dir, e.name);
349
+ if (e.isDirectory()) out.push(...listImages(abs, depth + 1));
350
+ else if (e.isFile() && IMAGE_EXT.has(path.extname(e.name).toLowerCase())) out.push(abs);
351
+ }
352
+ return out;
353
+ }
354
+
355
+ /**
356
+ * MEDIDA REPORTADA (e2 de DF5-6), NÃO veredito: para CADA peça gerada, a MENOR distância
357
+ * perceptual a QUALQUER referência. Sem limiar, sem pass/fail — quem julga "aderiu demais" é
358
+ * o humano no checkpoint de `assembly`. Pura e testável sem Chrome.
359
+ *
360
+ * A função ANTERIOR (`decide`, que bloqueava em distância <= N) foi REMOVIDA junto com o
361
+ * limiar: mantê-la exportada convidaria a reintroduzir por engano o veredito falsificado.
362
+ */
363
+ function measureDistances(generated, refs) {
364
+ return generated.map((g) => {
365
+ let best = { ref: null, distance: Infinity };
366
+ for (const r of refs) {
367
+ const d = hamming(g.hash, r.hash);
368
+ if (d < best.distance) best = { ref: r.name, distance: d };
369
+ }
370
+ return { name: g.name, nearestRef: best.ref, distance: best.distance };
371
+ });
372
+ }
373
+
374
+ function parseArgs(argv) {
375
+ const o = { refsDir: '../inputs', assetsDir: '../assets', revokedArgs: [] };
376
+ for (let i = 0; i < argv.length; i++) {
377
+ if (argv[i] === '--refs-dir') o.refsDir = String(argv[++i] || '');
378
+ else if (argv[i] === '--assets-dir') o.assetsDir = String(argv[++i] || '');
379
+ else if (argv[i] === '--max-distance') {
380
+ // Resíduo de R4 (limiar), FALSIFICADA e REVOGADA (DF5-5/DF5-6). Aceito e IGNORADO com
381
+ // aviso, para que um bundle compilado antes do fix round não quebre por um argumento
382
+ // que não afeta mais nenhuma decisão. Nunca silencioso: o aviso vai para stderr e o
383
+ // argumento é registrado na medida reportada.
384
+ o.revokedArgs.push(`--max-distance ${String(argv[++i])}`);
385
+ }
386
+ }
387
+ return o;
388
+ }
389
+
390
+ // ---------------------------------------------------------------------------
391
+ // Registro do run — LIDO E ESCRITO SÓ PARA A MEDIDA (nunca para decidir).
392
+ //
393
+ // A verificação de proveniência que MORAVA aqui foi REMOVIDA (não afrouxada): ela agora existe
394
+ // UMA ÚNICA VEZ, em `scripts/pod-export-tools.cjs` (`checkRunProvenance`/`assertRunProvenance`),
395
+ // ao lado de `recordProvenance`, e é consumida pelo `pod-export-executor.cjs` — DF-SF10.19-4.
396
+ // Com isso o contrato do ledger deixa de ter duas implementações: este script blessed (que não
397
+ // pode importar nada fora de `veto-checks/`) só precisa saber ONDE o ledger fica para gravar a
398
+ // medida quando ele existir. Ausência/ilegibilidade viram nota declarada, nunca exit.
399
+ // ---------------------------------------------------------------------------
400
+
401
+ /** `<runDir>.provenance.json` — IRMÃO do runDir, logo fora do alcance de escrita das tools. */
402
+ function provenancePathFor(runDir) {
403
+ return path.resolve(runDir) + PROVENANCE_SUFFIX;
404
+ }
405
+
406
+ // ---------------------------------------------------------------------------
407
+ // MEDIDA REPORTADA — nunca bloqueia (e2 de DF5-6).
408
+ // ---------------------------------------------------------------------------
409
+
410
+ /**
411
+ * Bloco humano-legível da medida, gravado no artefato que alimenta o checkpoint de `assembly`.
412
+ *
413
+ * O bloco DECLARA o que NÃO foi verificado (AC10 / DF-SF10.19-3). É o oposto de um selo: ele existe
414
+ * para que o humano que aprova o carrossel saiba exatamente qual garantia tem e qual não tem — no
415
+ * driver de dev, nenhuma garantia de máquina sobre a ORIGEM dos bytes.
416
+ */
417
+ function renderMeasureBlock(measure) {
418
+ const L = [MEASURE_BEGIN, '', '## Medida de similaridade (reportada por codigo, NAO e veto)', ''];
419
+ L.push('Calculada por `phash-gate.cjs`, que MEDE e nunca bloqueia. Nao ha limiar e nao ha pass/fail: julgar se a');
420
+ L.push('peca "aderiu demais" a referencia e JULGAMENTO HUMANO, no checkpoint de `assembly`.');
421
+ L.push('');
422
+ if (measure.status === 'ok') {
423
+ L.push('| Peca gerada | Referencia mais proxima | Distancia perceptual (Hamming /64) |');
424
+ L.push('|---|---|---|');
425
+ for (const r of measure.rows) L.push(`| ${r.name} | ${r.nearestRef} | ${r.distance} |`);
426
+ L.push('');
427
+ L.push('Escala: 0 = layout de luminancia identico; 64 = oposto. A medida NAO separa "copia" de "peca legitima"');
428
+ L.push('no conteudo real (medido em SF10.8, ver docs/mecanismos/veto-nao-copia-phash.md) - use-a como indicio, nunca como prova.');
429
+ } else {
430
+ const rotulo = measure.status === 'nao-verificada' ? 'Medida NAO CALCULADA' : 'Medida AUSENTE';
431
+ L.push(`${rotulo} (${measure.reason}). A ausencia NAO bloqueia a etapa e NAO foi substituida por nenhum valor.`);
432
+ }
433
+ L.push('');
434
+ L.push('### O que este bloco NAO garante (declarado, nao omitido)');
435
+ L.push('');
436
+ L.push('- Este script NAO verifica PROVENIENCIA. Que as imagens em `assets/` tenham sido gravadas pelas tools do run,');
437
+ L.push(' e que a referencia em `inputs/` esteja bit-a-bit como o codigo a materializou, e asserido pelo EXECUTOR de');
438
+ L.push(' export (`pod-export-executor.cjs`, antes de avancar a fase) - o unico componente que confina a superficie de');
439
+ L.push(' tools. Em run de DESENVOLVIMENTO (`pc-run-pod`, agente com Bash/Write) essa garantia NAO EXISTE: o unico');
440
+ L.push(' enforcement e humano, neste checkpoint. Ver docs/mecanismos/veto-nao-copia-phash.md.');
441
+ L.push('- A distancia acima e um indicio de layout de luminancia. NAO e prova de nao-copia nem de autoria.');
442
+ L.push('', MEASURE_END);
443
+ return L.join('\n');
444
+ }
445
+
446
+ /** Insere/SUBSTITUI o bloco no texto do artefato (idempotente entre retentativas da etapa). */
447
+ function upsertMeasureBlock(text, block) {
448
+ const b = text.indexOf(MEASURE_BEGIN);
449
+ const e = text.indexOf(MEASURE_END);
450
+ if (b >= 0 && e > b) return `${text.slice(0, b)}${block}${text.slice(e + MEASURE_END.length)}`;
451
+ return `${text.replace(/\s*$/, '')}\n\n${block}\n`;
452
+ }
453
+
454
+ /**
455
+ * Grava a medida no ledger QUANDO ele existe (no export ele existe; no dev, não). Best-effort
456
+ * DECLARADO: o chamador transforma qualquer falha daqui em nota no stderr — nunca em exit.
457
+ * LANÇA em ledger ausente/ilegível/de versão desconhecida em vez de criar ou sobrescrever um:
458
+ * este script não é dono do ledger e escrever um por conta própria fabricaria proveniência.
459
+ */
460
+ function recordMeasure(runDir, stepId, measure) {
461
+ const ledgerPath = provenancePathFor(runDir);
462
+ const parsed = JSON.parse(fs.readFileSync(ledgerPath, 'utf8'));
463
+ if (!parsed || typeof parsed !== 'object' || parsed.version !== PROVENANCE_VERSION) {
464
+ throw new Error(`registro de proveniência com formato/versão inesperados (${ledgerPath}) — recusando escrever a medida nele`);
465
+ }
466
+ parsed.measures = (parsed.measures && typeof parsed.measures === 'object') ? parsed.measures : {};
467
+ parsed.measures[stepId] = measure;
468
+ parsed.updated_at = new Date().toISOString();
469
+ const tmp = `${ledgerPath}.tmp`;
470
+ fs.writeFileSync(tmp, `${JSON.stringify(parsed, null, 2)}\n`, 'utf8');
471
+ fs.renameSync(tmp, ledgerPath);
472
+ }
473
+
474
+ /** Resolve um dir de arg LITERAL relativo ao dir do artefato, exigindo contenção no runDir. */
475
+ function resolveWithin(runDir, artDir, rel, label) {
476
+ const abs = path.resolve(artDir, rel);
477
+ const root = path.resolve(runDir);
478
+ if (abs !== root && !abs.startsWith(root + path.sep)) {
479
+ throw new Error(`${label} "${rel}" resolve FORA do runDir (${abs}) — recusado`);
480
+ }
481
+ return abs;
482
+ }
483
+
484
+ function main() {
485
+ const artifact = process.argv[2];
486
+ if (!artifact || !fs.existsSync(artifact)) {
487
+ console.error(`phash-gate: artefato não encontrado: ${artifact}`);
488
+ process.exit(2);
489
+ }
490
+ const { refsDir, assetsDir, revokedArgs } = parseArgs(process.argv.slice(3));
491
+ if (revokedArgs.length > 0) {
492
+ console.error(`phash-gate: AVISO — argumento(s) de limiar ignorado(s) [${revokedArgs.join(', ')}]: ` +
493
+ 'o veredito por distância perceptual foi FALSIFICADO e REVOGADO (ADR-018, Correção 1 do Adendo F5 / ' +
494
+ 'DF5-5, DF5-6). O que bloqueia agora é PROVENIÊNCIA (sha256), sem limiar; a distância virou medida ' +
495
+ 'reportada. Remova o argumento do wf do pod.');
496
+ }
497
+ const artDir = path.dirname(path.resolve(artifact));
498
+ const runDir = path.dirname(artDir); // invariante runDir/artifacts/<step>.md
499
+ let refsAbs; let assetsAbs;
500
+ try {
501
+ refsAbs = resolveWithin(runDir, artDir, refsDir, '--refs-dir');
502
+ assetsAbs = resolveWithin(runDir, artDir, assetsDir, '--assets-dir');
503
+ } catch (e) {
504
+ console.error(`phash-gate: ${e.message}`);
505
+ process.exit(2);
506
+ }
507
+
508
+ // ── MEDIDA (o único caminho que restou). NADA daqui em diante pode virar exit não-zero:
509
+ // insumo ausente, ledger ausente e Chrome ausente são DEGRADAÇÃO DECLARADA, não veredito
510
+ // (DF-SF10.19-2/e1). Quem BLOQUEIA proveniência é o executor de export, in-process.
511
+ // A VARREDURA dos diretórios entra no `try` (F1 do gate `pc-qa` da SF10.19): `listImages` faz IO
512
+ // e pode lançar (EACCES num diretório ilegível). Fora do `try`, a exceção escapava e o processo
513
+ // saía 1 — BLOQUEANDO, exatamente o que o comentário abaixo promete que não acontece. Comentário
514
+ // que o código falsifica é pior que ponto cego declarado (lição do gate da SF10.10).
515
+ let refFiles = []; let genFiles = [];
516
+ const stepId = path.basename(artifact).replace(/\.md$/i, '');
517
+ let measure;
518
+ try {
519
+ refFiles = listImages(refsAbs);
520
+ genFiles = listImages(assetsAbs);
521
+ if (refFiles.length === 0 || genFiles.length === 0) {
522
+ // Antes da SF10.19 estes dois estados eram exit 3. Eles não descrevem uma violação: descrevem
523
+ // um run cujo insumo de MEDIDA não está no lugar — o que é o estado NORMAL do driver de dev,
524
+ // que não materializa `inputs/`. Declarar é a resposta certa; bloquear travava o pod inteiro.
525
+ const faltas = [];
526
+ if (refFiles.length === 0) faltas.push(`nenhuma imagem de REFERENCIA em ${refsAbs} (no export ela e materializada por codigo a partir de inputs_dir; em run de dev ela costuma nao existir)`);
527
+ if (genFiles.length === 0) faltas.push(`nenhuma imagem GERADA em ${assetsAbs}`);
528
+ measure = { status: 'nao-verificada', reason: `insumo de medida ausente: ${faltas.join('; ')}` };
529
+ } else {
530
+ const chromeBin = probeChrome();
531
+ if (!chromeBin) {
532
+ measure = { status: 'ausente', reason: 'headless Chrome nao encontrado (defina CHROME_BIN) - a medida e opcional e nao bloqueia nada' };
533
+ } else {
534
+ const files = [
535
+ ...refFiles.map((abs, i) => ({ id: `r${i}`, abs, name: path.relative(runDir, abs), kind: 'ref' })),
536
+ ...genFiles.map((abs, i) => ({ id: `g${i}`, abs, name: path.relative(runDir, abs), kind: 'gen' })),
537
+ ];
538
+ const gray = grayscalesViaChrome(chromeBin, files);
539
+ const hashed = files.map((f) => ({ ...f, hash: hashFromGray(gray.get(f.id)) }));
540
+ measure = {
541
+ status: 'ok',
542
+ method: 'pHash DCT 64 bits sobre grayscale 32x32 (Chrome headless); distancia de Hamming',
543
+ blocking: false,
544
+ rows: measureDistances(hashed.filter((f) => f.kind === 'gen'), hashed.filter((f) => f.kind === 'ref')),
545
+ };
546
+ }
547
+ }
548
+ } catch (e) {
549
+ measure = { status: 'ausente', reason: `medicao falhou: ${e.message}` };
550
+ }
551
+ measure.at = new Date().toISOString();
552
+ // Declarado SEMPRE, em qualquer status: este script não verifica proveniência — nem no export,
553
+ // onde o executor a assere, nem no dev, onde ela não é verificável por ninguém (DF-SF10.19-3).
554
+ measure.provenance_checked_here = false;
555
+ measure.provenance_enforced_by = 'pod-export-executor.cjs (tools.assertRunProvenance, antes de motor.advance) — SO no driver de export';
556
+ if (revokedArgs.length > 0) measure.ignored_args = revokedArgs;
557
+
558
+ // Os dois destinos da medida são best-effort DECLARADO: a falha vira AVISO em stderr, nunca
559
+ // exit. Nada de `catch {}` cego — todo motivo é dito por extenso.
560
+ const notes = [];
561
+ try { recordMeasure(runDir, stepId, measure); } catch (e) { notes.push(`registro da medida no ledger falhou (esperado fora do export, onde nao ha ledger): ${e.message}`); }
562
+ try {
563
+ const raw = fs.readFileSync(path.resolve(artifact), 'utf8');
564
+ fs.writeFileSync(path.resolve(artifact), upsertMeasureBlock(raw, renderMeasureBlock(measure)), 'utf8');
565
+ } catch (e) { notes.push(`gravacao do bloco de medida no artefato falhou: ${e.message}`); }
566
+ for (const n of notes) console.error(`phash-gate: AVISO (nao-bloqueante) — ${n}`);
567
+
568
+ const detail = measure.status === 'ok'
569
+ ? measure.rows.map((r) => `${r.name} vs ${r.nearestRef}: ${r.distance}`).join('; ')
570
+ : `${measure.status} (${measure.reason})`;
571
+ console.log(`phash-gate: MEDIDA registrada (este script nao bloqueia) — ${genFiles.length} imagem(ns) em ` +
572
+ `assets/, ${refFiles.length} referencia(s) em inputs/. Similaridade: ${detail}. ` +
573
+ 'PROVENIENCIA nao e verificada aqui: ela e asserida pelo executor de export antes de avancar a fase ' +
574
+ '(ADR-018, DF-SF10.19-2); em run de desenvolvimento essa garantia nao existe.');
575
+ process.exit(0);
576
+ }
577
+
578
+ if (require.main === module) main();
579
+
580
+ module.exports = {
581
+ // medida reportada (o único caminho que este script tem)
582
+ probeChrome, hashFromGray, hamming, measureDistances, parseMarker, measurePage,
583
+ grayscalesViaChrome, renderMeasureBlock, upsertMeasureBlock, recordMeasure,
584
+ // endereço do ledger (para GRAVAR a medida; a VERIFICAÇÃO vive em pod-export-tools.cjs)
585
+ provenancePathFor,
586
+ // comuns
587
+ parseArgs, listImages, resolveWithin,
588
+ CHROME_CANDIDATES, SIDE, PROVENANCE_SUFFIX, PROVENANCE_VERSION, MEASURE_BEGIN, MEASURE_END,
589
+ };