@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,470 @@
1
+ 'use strict';
2
+ /**
3
+ * pod-export-tools.cjs — as 4 tools CONFINADAS do executor de fase de PRODUÇÃO (Story SF10.2,
4
+ * épico pod-export F2). Industrialização de `spike/pod-export-c3/tools.cjs` (PoC 7/7): mesma
5
+ * contenção que o spike PROVOU, agora como biblioteca de produção com suíte formal.
6
+ *
7
+ * ADR-018 Decisão 3 / Adendo F2 DF2-3 estende o modelo de ameaça de ADR-011 ao servidor: a
8
+ * superfície de execução nova (fetch/imagem/write/read) DEVE ser confinada — sem SSRF, sem
9
+ * traversal, sem shell. NENHUMA destas tools executa shell. Todas as escritas são confinadas
10
+ * ao runDir da fase.
11
+ *
12
+ * Rede INJETÁVEL (`transport`/`imageTransport`) para testes herméticos de CI (precedente
13
+ * `pod-export-compile.cjs` `fetchImpl`). Os caminhos de NEGATIVA de segurança (host bloqueado,
14
+ * fora da whitelist, traversal, path absoluto) fazem short-circuit ANTES de qualquer rede/IO —
15
+ * são hermeticamente testáveis sem mock.
16
+ *
17
+ * Node >= 18, zero dependências externas.
18
+ */
19
+
20
+ const fs = require('node:fs');
21
+ const path = require('node:path');
22
+ const https = require('node:https');
23
+ const http = require('node:http');
24
+ const crypto = require('node:crypto');
25
+
26
+ const MAX_FETCH_BYTES = 400 * 1024; // teto de corpo lido (anti-abuso)
27
+
28
+ // ── Proveniência do run (SF10.8 / ADR-018 Correção 1 do Adendo F5, DF5-6/e1) ──────
29
+ // O veto bloqueante da não-cópia deixou de ser um limiar perceptual (R4 FALSIFICADA) e
30
+ // passou a ser um INVARIANTE DE PROVENIÊNCIA: só passa a etapa cujas imagens em `assets/`
31
+ // foram gravadas pelas TOOLS deste run e cuja referência em `inputs/` está bit-a-bit como
32
+ // o CÓDIGO a materializou. Duas camadas, independentes por construção:
33
+ // (1) DENYLIST DE ESCRITA — nenhuma tool escreve sob `<runDir>/<RUN_INPUTS_DIRNAME>/`
34
+ // (`confinedResolveWritable`), fechando o vetor INTEG-1 do gate de QA;
35
+ // (2) REGISTRO sha256 — toda gravação de bytes por código é registrada num ledger fora
36
+ // da árvore gravável pelo agente; o veto RECUSA (fail-loud) qualquer divergência.
37
+ // Cada camada segura sozinha: (1) impede o vetor conhecido, (2) detecta a CLASSE inteira
38
+ // (inclusive um caminho de escrita que (1) não tenha antecipado).
39
+ const RUN_INPUTS_DIRNAME = 'inputs'; // convenção FIXA lida pelo veto (`--refs-dir ../inputs`)
40
+ const RUN_ASSETS_DIRNAME = 'assets'; // onde `openrouter_image` grava (única porta de bytes)
41
+ const PROVENANCE_SUFFIX = '.provenance.json'; // ledger = IRMÃO do runDir, nunca DENTRO dele
42
+ const PROVENANCE_VERSION = 1;
43
+ // Extensões consideradas IMAGEM na verificação do lado `assets/` (mesma lista que o veto de medida
44
+ // usa para varrer o diretório): arquivo de texto que uma fase escreva em `assets/` não é objeto
45
+ // deste invariante — só bytes de imagem entram por `openrouter_image`.
46
+ const PROVENANCE_IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif', '.bmp']);
47
+ const PROVENANCE_MAX_DEPTH = 3; // varredura determinística e limitada
48
+
49
+ /**
50
+ * Path do registro de proveniência de um run: `<runDir>.provenance.json`, IRMÃO do runDir.
51
+ * Fica fora da árvore gravável pelo agente por CONSTRUÇÃO — `confinedResolve` exige que todo
52
+ * destino de escrita esteja DENTRO do runDir, e o irmão não está. Não depende da denylist.
53
+ * Contrato duplicado (deliberadamente) em `scripts/veto-checks/phash-gate.cjs`, que é blessed
54
+ * e não pode importar este módulo; a suíte assere que as duas pontas concordam.
55
+ */
56
+ function provenancePathFor(runDir) {
57
+ return path.resolve(runDir) + PROVENANCE_SUFFIX;
58
+ }
59
+
60
+ function sha256File(abs) {
61
+ return crypto.createHash('sha256').update(fs.readFileSync(abs)).digest('hex');
62
+ }
63
+
64
+ /** Chave do ledger: path relativo ao runDir, sempre em separador POSIX (determinístico). */
65
+ function provenanceKey(runDir, abs) {
66
+ return path.relative(path.resolve(runDir), path.resolve(abs)).split(path.sep).join('/');
67
+ }
68
+
69
+ /**
70
+ * Registra (ou ATUALIZA) o sha256 de um arquivo gravado POR CÓDIGO no ledger do run.
71
+ * `section` = 'assets' (imagem gravada por `openrouter_image`) | 'inputs' (referência copiada
72
+ * por `materializeInputs`). Escrita atômica (tmp + rename).
73
+ *
74
+ * FAIL-LOUD: ledger existente ilegível/de versão desconhecida LANÇA em vez de sobrescrever —
75
+ * silenciar aqui reabriria a porta para "resetar" a proveniência de um run.
76
+ */
77
+ function recordProvenance(runDir, section, absFile, extra = {}) {
78
+ if (section !== 'assets' && section !== 'inputs') throw new Error(`seção de proveniência inválida: ${section}`);
79
+ const ledgerPath = provenancePathFor(runDir);
80
+ const now = new Date().toISOString();
81
+ let ledger = { version: PROVENANCE_VERSION, run_dir: path.resolve(runDir), created_at: now, updated_at: now, inputs: {}, assets: {}, measures: {} };
82
+ if (fs.existsSync(ledgerPath)) {
83
+ let parsed;
84
+ try { parsed = JSON.parse(fs.readFileSync(ledgerPath, 'utf8')); } catch (e) {
85
+ throw new Error(`registro de proveniência ilegível (${ledgerPath}): ${e.message} — recusando gravar por cima (fail-loud)`);
86
+ }
87
+ if (!parsed || typeof parsed !== 'object' || parsed.version !== PROVENANCE_VERSION) {
88
+ throw new Error(`registro de proveniência com versão inesperada (${ledgerPath}) — recusando gravar por cima (fail-loud)`);
89
+ }
90
+ ledger = {
91
+ ...ledger, ...parsed,
92
+ inputs: parsed.inputs && typeof parsed.inputs === 'object' ? parsed.inputs : {},
93
+ assets: parsed.assets && typeof parsed.assets === 'object' ? parsed.assets : {},
94
+ measures: parsed.measures && typeof parsed.measures === 'object' ? parsed.measures : {},
95
+ };
96
+ }
97
+ const key = provenanceKey(runDir, absFile);
98
+ const entry = { sha256: sha256File(absFile), bytes: fs.statSync(absFile).size, at: now, ...extra };
99
+ ledger[section][key] = entry;
100
+ ledger.updated_at = now;
101
+ const tmp = `${ledgerPath}.tmp`;
102
+ fs.writeFileSync(tmp, `${JSON.stringify(ledger, null, 2)}\n`, 'utf8');
103
+ fs.renameSync(tmp, ledgerPath);
104
+ return { ledgerPath, key, sha256: entry.sha256 };
105
+ }
106
+
107
+ // ── Verificação CANÔNICA da proveniência (SF10.19 / ADR-018 Adendo F6-b, DF-SF10.19-2/e2) ──
108
+ //
109
+ // POR QUE ELA VIVE AQUI, e não mais no blessed `phash-gate.cjs` (DF-SF10.19-1 / -4):
110
+ // um veto declarado em `wf-*.yaml` é compartilhado por TODOS os drivers do mesmo yaml — hoje
111
+ // `pc-run-pod.cjs` (dev, subagente com Bash/Write) e `pod-export-executor.cjs` (export, 4 tools
112
+ // confinadas). Uma invariante de ORIGEM só é verificável onde a superfície de tools é confinada:
113
+ // no dev o agente escreve `assets/` por fora de qualquer código de registro e alcança o ledger com
114
+ // shell, então o mesmo verde teria significados diferentes conforme quem rodou (gate-teatro, DF4-5).
115
+ // Logo a checagem passa a viver AO LADO de `recordProvenance` — uma única implementação do contrato
116
+ // do ledger — e é consumida pelo EXECUTOR, que é o componente que garante a premissa.
117
+ //
118
+ // O invariante NÃO muda de força, só de hospedeiro: as duas camadas de INTEG-1 continuam de pé —
119
+ // (1) `confinedResolveWritable` nega escrita sob `inputs/`; (2) esta verificação detecta a CLASSE
120
+ // inteira, inclusive um caminho de escrita que (1) não tenha antecipado.
121
+
122
+ /** Lista arquivos regulares sob `dir` (recursivo, profundidade limitada), ordenado — determinístico. */
123
+ function listProvenanceFiles(dir, { imagesOnly = false, depth = 0 } = {}) {
124
+ if (!fs.existsSync(dir) || depth > PROVENANCE_MAX_DEPTH) return [];
125
+ const out = [];
126
+ for (const e of fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => (a.name < b.name ? -1 : 1))) {
127
+ if (e.name.startsWith('.')) continue;
128
+ const abs = path.join(dir, e.name);
129
+ if (e.isDirectory()) out.push(...listProvenanceFiles(abs, { imagesOnly, depth: depth + 1 }));
130
+ else if (e.isFile() && (!imagesOnly || PROVENANCE_IMAGE_EXT.has(path.extname(e.name).toLowerCase()))) out.push(abs);
131
+ }
132
+ return out;
133
+ }
134
+
135
+ /**
136
+ * Carrega o ledger do run para VERIFICAÇÃO. Devolve `{ ok:false, error }` em ausência/ilegibilidade
137
+ * /versão desconhecida — NUNCA um registro vazio como se fosse válido (isso passaria a aprovar
138
+ * qualquer coisa). `exists` distingue "não há ledger" de "há e está quebrado".
139
+ */
140
+ function loadProvenanceLedger(runDir) {
141
+ const ledgerPath = provenancePathFor(runDir);
142
+ if (!fs.existsSync(ledgerPath)) return { ok: false, exists: false, ledgerPath, error: `registro de proveniência AUSENTE em ${ledgerPath}` };
143
+ let parsed;
144
+ try { parsed = JSON.parse(fs.readFileSync(ledgerPath, 'utf8')); } catch (e) {
145
+ return { ok: false, exists: true, ledgerPath, error: `registro de proveniência ILEGÍVEL (${ledgerPath}): ${e.message}` };
146
+ }
147
+ if (!parsed || typeof parsed !== 'object' || parsed.version !== PROVENANCE_VERSION) {
148
+ return { ok: false, exists: true, ledgerPath, error: `registro de proveniência com formato/versão inesperados (${ledgerPath}) — nunca interpretar schema estranho` };
149
+ }
150
+ const norm = (o) => (o && typeof o === 'object' ? o : {});
151
+ return { ok: true, exists: true, ledgerPath, ledger: { ...parsed, inputs: norm(parsed.inputs), assets: norm(parsed.assets), measures: norm(parsed.measures) } };
152
+ }
153
+
154
+ function isUnderDir(root, abs) {
155
+ const r = path.resolve(root);
156
+ const a = path.resolve(abs);
157
+ return a === r || a.startsWith(r + path.sep);
158
+ }
159
+
160
+ /**
161
+ * INVARIANTE DE PROVENIÊNCIA do run (DF5-6/e1, hospedado aqui por DF-SF10.19-2/e2). PURA quanto a
162
+ * decisão: lê disco e ledger, não escreve nada, não lança. Duas regras, ambas POR-ARQUIVO (DF5-3):
163
+ *
164
+ * 1. INTEGRIDADE (nos dois sentidos): todo arquivo sob `<runDir>/inputs/` e toda imagem sob
165
+ * `<runDir>/assets/` consta do ledger com sha256 idêntico, E toda entrada do ledger sob esses
166
+ * diretórios existe em disco. Nada nos dois diretórios e nada no ledger ⇒ nada a verificar
167
+ * (pod sem imagem não passa a exigir ledger — a mudança é INERTE para os outros pods).
168
+ * 2. NÃO-VACUIDADE (só quando `requireNonVacuous`): a fase que DECLARA o veto de não-cópia tem de
169
+ * ter ≥1 referência materializada e ≥1 imagem registrada. É a garantia que o `exit 3` do veto
170
+ * dava de graça e que, sem esta regra, sumiria ao script virar medida-only.
171
+ */
172
+ function checkRunProvenance({ runDir, requireNonVacuous = false, stepId = null }) {
173
+ const refsAbs = path.join(path.resolve(runDir), RUN_INPUTS_DIRNAME);
174
+ const assetsAbs = path.join(path.resolve(runDir), RUN_ASSETS_DIRNAME);
175
+ const refFiles = listProvenanceFiles(refsAbs);
176
+ const genFiles = listProvenanceFiles(assetsAbs, { imagesOnly: true });
177
+ const loaded = loadProvenanceLedger(runDir);
178
+ const violations = [];
179
+ const fase = stepId ? `a fase "${stepId}"` : 'a fase corrente';
180
+ // Regra 2 aplicada nos DOIS ramos (com e sem ledger): sem ela, um run sem ledger nenhum devolveria
181
+ // só "registro ausente" e esconderia que a fase também não produziu nada.
182
+ const checkNonVacuity = (registeredRefs, registeredAssets) => {
183
+ if (!requireNonVacuous) return;
184
+ if (registeredRefs === 0) {
185
+ violations.push(`${fase} declara o veto de não-cópia, mas NENHUMA referência materializada consta do registro (${refsAbs}) — a referência é copiada POR CÓDIGO pelo executor (materializeInputs a partir de "inputs_dir"); copiá-la à mão não produz proveniência`);
186
+ }
187
+ if (registeredAssets === 0) {
188
+ violations.push(`${fase} declara produzir imagem sob o veto de não-cópia, mas NENHUMA imagem registrada existe em ${assetsAbs} — sem peça gravada por openrouter_image não há proveniência a verificar (etapa não passa por vacuidade)`);
189
+ }
190
+ };
191
+
192
+ if (!loaded.ok) {
193
+ // Ledger inválido só é irrelevante quando NÃO há nada para provar e nada é exigido. Um ledger
194
+ // que EXISTE e está quebrado é sempre violação (fail-loud: nunca gravar/decidir por cima dele).
195
+ if (loaded.exists || refFiles.length > 0 || genFiles.length > 0 || requireNonVacuous) {
196
+ violations.push(`${loaded.error} — sem registro íntegro não há como provar que as imagens em ${RUN_ASSETS_DIRNAME}/ foram gravadas pelas tools deste run nem que a referência em ${RUN_INPUTS_DIRNAME}/ está bit-a-bit como o código a materializou`);
197
+ checkNonVacuity(0, 0);
198
+ return { ok: false, violations, ledgerPath: loaded.ledgerPath, refs: refFiles.length, assets: genFiles.length, registeredRefs: 0, registeredAssets: 0 };
199
+ }
200
+ return { ok: true, violations, ledgerPath: loaded.ledgerPath, refs: 0, assets: 0, registeredRefs: 0, registeredAssets: 0 };
201
+ }
202
+
203
+ const { ledger } = loaded;
204
+ const checkSide = (dirAbs, files, registry, label) => {
205
+ for (const abs of files) {
206
+ const key = provenanceKey(runDir, abs);
207
+ const entry = registry[key];
208
+ if (!entry || typeof entry.sha256 !== 'string') {
209
+ violations.push(`${label} "${key}" NÃO consta do registro de proveniência do run — não foi gravada pelas tools/pelo executor`);
210
+ continue;
211
+ }
212
+ const sha = sha256File(abs);
213
+ if (sha !== entry.sha256) {
214
+ violations.push(`${label} "${key}" DIVERGE do sha256 registrado (registro ${entry.sha256.slice(0, 12)}…, disco ${sha.slice(0, 12)}…) — os bytes mudaram depois da gravação por código`);
215
+ }
216
+ }
217
+ let registered = 0;
218
+ for (const [key, entry] of Object.entries(registry)) {
219
+ const abs = path.resolve(runDir, key);
220
+ if (!isUnderDir(dirAbs, abs)) continue; // registrada fora deste diretório: não é objeto desta checagem
221
+ registered++;
222
+ if (!fs.existsSync(abs)) violations.push(`${label} registrada "${key}" (sha ${String(entry.sha256).slice(0, 12)}…) AUSENTE em disco`);
223
+ }
224
+ return registered;
225
+ };
226
+ const registeredRefs = checkSide(refsAbs, refFiles, ledger.inputs, 'referência');
227
+ const registeredAssets = checkSide(assetsAbs, genFiles, ledger.assets, 'imagem gerada');
228
+
229
+ checkNonVacuity(registeredRefs, registeredAssets);
230
+ return { ok: violations.length === 0, violations, ledgerPath: loaded.ledgerPath, refs: refFiles.length, assets: genFiles.length, registeredRefs, registeredAssets };
231
+ }
232
+
233
+ /**
234
+ * A mesma verificação, em forma de ASSERÇÃO fail-loud: LANÇA com mensagem acionável. É o que o
235
+ * executor chama antes de `motor.advance` — e lançar é o HALT do run (DF-SF10.19-2/e3): violação de
236
+ * proveniência não é conteúdo que o modelo possa corrigir (sob as 4 tools ela é INALCANÇÁVEL), logo
237
+ * significa bug de tool, adulteração ou driver novo não-conformante. Consumir tentativa nisso
238
+ * mascararia a causa — por isso a asserção NÃO devolve um veredito retentável, ela interrompe.
239
+ */
240
+ function assertRunProvenance({ runDir, requireNonVacuous = false, stepId = null }) {
241
+ const r = checkRunProvenance({ runDir, requireNonVacuous, stepId });
242
+ if (r.ok) return r;
243
+ throw new Error(
244
+ `PROVENIÊNCIA VIOLADA${stepId ? ` na fase "${stepId}"` : ''} — run HALTADO (sem retentativa): ` +
245
+ r.violations.join('; ') +
246
+ `. Registro do run: ${r.ledgerPath}. Nenhuma imagem entra no run sem ter sido produzida pelas tools do run, ` +
247
+ 'e a referência comparada tem de ser bit-a-bit a que o código materializou: grave as peças geradas em ' +
248
+ `"${RUN_ASSETS_DIRNAME}/" com openrouter_image (escrever em "${RUN_INPUTS_DIRNAME}/" é PROIBIDO) e não altere ` +
249
+ 'arquivos do run por fora das tools. Esta classe de violação é inalcançável pela superfície de tools do ' +
250
+ 'export — se ela aparece, é bug de tool, adulteração ou driver não-conformante, e retentar apenas ' +
251
+ 'mascararia a causa (ADR-018, DF5-6/e1 hospedado no executor por DF-SF10.19-2/e2 e /e3).',
252
+ );
253
+ }
254
+
255
+ // ── Segurança de rede: bloqueia hosts privados/loopback/metadata (anti-SSRF) ──────
256
+ // DF2-3: barra INCONDICIONALMENTE, ACIMA da whitelist — mesmo que o host apareça na
257
+ // whitelist, um alvo privado/metadata é recusado (defesa em profundidade contra
258
+ // whitelist mal-configurada pelo operador).
259
+ function isBlockedHost(host) {
260
+ const h = String(host).toLowerCase().replace(/^\[|\]$/g, ''); // tira colchetes de IPv6 literal
261
+ if (!h) return true;
262
+ if (h === 'localhost' || h.endsWith('.local') || h.endsWith('.internal') || h.endsWith('.localhost')) return true;
263
+ // IPv6 loopback / unspecified / link-local / unique-local.
264
+ if (h === '::1' || h === '::' || h.startsWith('fe80:') || h.startsWith('fc') || h.startsWith('fd')) return true;
265
+ // IPv4 privado / loopback / link-local / metadata / unspecified.
266
+ if (/^(127\.|10\.|192\.168\.|169\.254\.|0\.0\.0\.0)/.test(h)) return true;
267
+ if (/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(h)) return true;
268
+ if (h === '169.254.169.254') return true; // cloud metadata (AWS/GCP/Azure)
269
+ return false;
270
+ }
271
+
272
+ /**
273
+ * Derivação da whitelist de fetch de um run (DF2-3 / AC7): a partir do PAYLOAD DECLARADO
274
+ * no início do run — NUNCA de uma URL descoberta/sugerida pelo modelo durante a execução.
275
+ * whitelist = `fetch_allow_hosts` ∪ { host de toda URL http(s) pinada no payload }.
276
+ * `isBlockedHost` barra por cima disto, incondicionalmente.
277
+ */
278
+ function computeWhitelist(payload) {
279
+ const hosts = new Set();
280
+ const declared = Array.isArray(payload && payload.fetch_allow_hosts) ? payload.fetch_allow_hosts : [];
281
+ for (const h of declared) if (typeof h === 'string' && h.trim()) hosts.add(h.trim().toLowerCase());
282
+ // Hosts das URLs pinadas no payload (ex.: article_url). Só top-level string values.
283
+ for (const [k, v] of Object.entries(payload || {})) {
284
+ if (k === 'fetch_allow_hosts') continue;
285
+ if (typeof v !== 'string') continue;
286
+ if (!/^https?:\/\//i.test(v)) continue;
287
+ try { hosts.add(new URL(v).hostname.toLowerCase()); } catch { /* URL malformada no payload: ignora (não alarga a whitelist) */ }
288
+ }
289
+ return [...hosts];
290
+ }
291
+
292
+ /** Fetch confinado por WHITELIST de hosts + bloqueio de hosts privados. Sem redirect cego. */
293
+ function httpFetch(url, { whitelistHosts = [], maxBytes = MAX_FETCH_BYTES, transport = null, _redirects = 0 } = {}) {
294
+ return new Promise((resolve) => {
295
+ let u;
296
+ try { u = new URL(url); } catch { return resolve({ ok: false, error: `URL inválida: ${url}` }); }
297
+ if (u.protocol !== 'https:' && u.protocol !== 'http:') return resolve({ ok: false, error: `protocolo não permitido: ${u.protocol}` });
298
+ // Ordem deliberada: bloqueio de host privado/metadata ANTES da whitelist (DF2-3).
299
+ if (isBlockedHost(u.hostname)) return resolve({ ok: false, error: `host bloqueado (privado/loopback/metadata): ${u.hostname}` });
300
+ const wl = whitelistHosts.map((h) => String(h).toLowerCase());
301
+ if (wl.length && !wl.includes(u.hostname.toLowerCase())) {
302
+ return resolve({ ok: false, error: `host fora da whitelist: ${u.hostname} (permitidos: ${wl.join(', ')})` });
303
+ }
304
+ // Rede injetável (CI): transport recebe a URL JÁ VALIDADA e devolve o corpo.
305
+ if (transport) {
306
+ Promise.resolve(transport(u.toString(), { whitelistHosts: wl, maxBytes })).then(resolve).catch((e) => resolve({ ok: false, error: String(e && e.message || e) }));
307
+ return;
308
+ }
309
+ if (_redirects > 5) return resolve({ ok: false, error: 'excesso de redirects' });
310
+ const lib = u.protocol === 'https:' ? https : http;
311
+ const req = lib.get(u, { timeout: 20000, headers: { 'user-agent': 'pod-export-executor/1 (power-claude)' } }, (res) => {
312
+ // redirect: só segue se o destino também passa a MESMA validação (nunca redirect cego → SSRF).
313
+ if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location) {
314
+ res.resume();
315
+ const next = new URL(res.headers.location, u).toString();
316
+ return resolve(httpFetch(next, { whitelistHosts, maxBytes, _redirects: _redirects + 1 }));
317
+ }
318
+ let buf = Buffer.alloc(0);
319
+ res.on('data', (d) => { if (buf.length < maxBytes) buf = Buffer.concat([buf, d]); });
320
+ res.on('end', () => resolve({ ok: true, status: res.statusCode, contentType: res.headers['content-type'] || '', body: buf.slice(0, maxBytes).toString('utf8'), bytes: buf.length }));
321
+ });
322
+ req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'timeout' }); });
323
+ req.on('error', (e) => resolve({ ok: false, error: e.message }));
324
+ });
325
+ }
326
+
327
+ // ── Escrita confinada ao runDir (anti-traversal) ─────────────────────────────────
328
+ function confinedResolve(runDir, relPath) {
329
+ if (typeof relPath !== 'string' || relPath.trim() === '') throw new Error('path vazio');
330
+ if (path.isAbsolute(relPath)) throw new Error(`path absoluto proibido: ${relPath}`);
331
+ const abs = path.resolve(runDir, relPath);
332
+ const root = path.resolve(runDir);
333
+ if (abs !== root && !abs.startsWith(root + path.sep)) throw new Error(`traversal fora do runDir: ${relPath}`);
334
+ return abs;
335
+ }
336
+
337
+ /**
338
+ * CAMADA 1 de INTEG-1 (SF10.8 / DF5-7 emenda a DF5-1): resolve um destino de ESCRITA.
339
+ * Tudo que `confinedResolve` já barra (path absoluto, traversal para fora do runDir) MAIS a
340
+ * DENYLIST de `<runDir>/<RUN_INPUTS_DIRNAME>/` — a árvore onde o EXECUTOR materializa a
341
+ * referência do run. Sem isso, `openrouter_image(out_path:"inputs/<referência>")` sobrescrevia
342
+ * a referência comparada com uma imagem recém-gerada e derrotava o veto inteiro (achado INTEG-1
343
+ * do gate de QA, `docs/qa/gates/sf10.8.yaml`).
344
+ *
345
+ * A comparação do primeiro segmento é CASE-INSENSITIVE de propósito: em filesystem
346
+ * case-insensitive (macOS por default) `INPUTS/ref.png` alcança o MESMO arquivo, e uma
347
+ * comparação sensível a caixa deixaria o vetor aberto no ambiente de desenvolvimento.
348
+ *
349
+ * O registro de proveniência não precisa de regra aqui: ele é IRMÃO do runDir, e `confinedResolve`
350
+ * já recusa qualquer destino fora do runDir.
351
+ */
352
+ function confinedResolveWritable(runDir, relPath) {
353
+ const abs = confinedResolve(runDir, relPath);
354
+ const rel = path.relative(path.resolve(runDir), abs);
355
+ const first = rel.split(path.sep)[0] || '';
356
+ if (first.toLowerCase() === RUN_INPUTS_DIRNAME.toLowerCase()) {
357
+ throw new Error(
358
+ `escrita PROIBIDA sob "${RUN_INPUTS_DIRNAME}/": ${relPath}. Esse diretório guarda a REFERÊNCIA ` +
359
+ 'do run, materializada por CÓDIGO (executor) e comparada pelo veto de não-cópia — nenhuma tool ' +
360
+ 'escreve nele. Grave imagens geradas em "assets/".',
361
+ );
362
+ }
363
+ return abs;
364
+ }
365
+
366
+ function writeFile(relPath, content, { runDir }) {
367
+ const abs = confinedResolveWritable(runDir, relPath);
368
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
369
+ fs.writeFileSync(abs, typeof content === 'string' ? content : String(content), 'utf8');
370
+ return { ok: true, path: path.relative(runDir, abs), bytes: Buffer.byteLength(content || '') };
371
+ }
372
+
373
+ /**
374
+ * read_image (AC5 / Achado 4 do spike): confirma/lê imagem confinada.
375
+ * - Referência PINADA (`inputs:<nome>`): devolve base64 para leitura MULTIMODAL (o modelo
376
+ * PRECISA ver a referência de estilo). Confinada a inputsDir.
377
+ * - Imagem GERADA (path relativo ao runDir): confirma por PATH + TAMANHO, NUNCA devolve o
378
+ * base64 completo de volta ao modelo (evita inflar o contexto — Achado 4). Mecaniza AC5.
379
+ */
380
+ function readImage(ref, { runDir, inputsDir }) {
381
+ if (typeof ref !== 'string' || ref.trim() === '') return { ok: false, error: 'ref vazia' };
382
+ if (ref.startsWith('inputs:')) {
383
+ if (!inputsDir) return { ok: false, error: 'inputsDir não configurado para este run (nenhum input pinado)' };
384
+ const rel = ref.slice('inputs:'.length);
385
+ if (path.isAbsolute(rel)) return { ok: false, error: `path absoluto proibido em inputs: ${rel}` };
386
+ const abs = path.resolve(inputsDir, rel);
387
+ const root = path.resolve(inputsDir);
388
+ if (abs !== root && !abs.startsWith(root + path.sep)) return { ok: false, error: `leitura fora de inputs/: ${rel}` };
389
+ if (!fs.existsSync(abs)) return { ok: false, error: `imagem de referência não encontrada: ${ref}` };
390
+ const buf = fs.readFileSync(abs);
391
+ const ext = path.extname(abs).slice(1).toLowerCase();
392
+ const mediaType = ext === 'jpg' ? 'image/jpeg' : `image/${ext || 'png'}`;
393
+ return { ok: true, kind: 'reference', base64: buf.toString('base64'), mediaType, bytes: buf.length };
394
+ }
395
+ // Imagem gerada/escrita no runDir: confirmação por path+tamanho, SEM base64 (Achado 4).
396
+ let abs;
397
+ try { abs = confinedResolve(runDir, ref); } catch (e) { return { ok: false, error: e.message }; }
398
+ if (!fs.existsSync(abs)) return { ok: false, error: `imagem gerada não encontrada: ${ref}` };
399
+ const bytes = fs.statSync(abs).size;
400
+ return { ok: true, kind: 'generated', path: path.relative(runDir, abs), bytes };
401
+ }
402
+
403
+ /**
404
+ * Geração de imagem via OpenRouter (POST chat/completions, image modality). Escreve PNG
405
+ * confinado ao runDir. Modelo PINADO passado pelo caller (do bundle). Rede injetável
406
+ * (`imageTransport`) para CI. Chave lida em runtime pelo caller, nunca gravada.
407
+ */
408
+ function openrouterImage(prompt, outRel, { runDir, model, apiKey, imageTransport = null }) {
409
+ return new Promise((resolve) => {
410
+ if (!model) return resolve({ ok: false, error: 'modelo de imagem ausente (deve vir PINADO do bundle)' });
411
+ // Resolve/valida o destino ANTES de qualquer rede (anti-traversal + denylist de inputs/).
412
+ let abs;
413
+ try { abs = confinedResolveWritable(runDir, outRel); } catch (e) { return resolve({ ok: false, error: e.message }); }
414
+ const finish = (raw) => {
415
+ try {
416
+ const j = typeof raw === 'string' ? JSON.parse(raw) : raw;
417
+ if (j.error) return resolve({ ok: false, error: `openrouter: ${j.error.message || JSON.stringify(j.error)}` });
418
+ const msg = j.choices && j.choices[0] && j.choices[0].message;
419
+ let dataUrl = null;
420
+ if (msg && Array.isArray(msg.images) && msg.images[0]) dataUrl = msg.images[0].image_url ? msg.images[0].image_url.url : msg.images[0].url;
421
+ if (!dataUrl && msg && typeof msg.content === 'string') { const m = msg.content.match(/data:image\/[^;]+;base64,[A-Za-z0-9+/=]+/); if (m) dataUrl = m[0]; }
422
+ if (!dataUrl) return resolve({ ok: false, error: 'resposta sem imagem (modelo não retornou modality image)' });
423
+ const b64 = dataUrl.split(',')[1];
424
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
425
+ fs.writeFileSync(abs, Buffer.from(b64, 'base64'));
426
+ // CAMADA 2 de INTEG-1 (DF5-6/e1): esta é a ÚNICA porta por onde bytes de imagem entram
427
+ // no run — registrar aqui o sha256 é o que torna "imagem sem proveniência" detectável.
428
+ // Falha de registro é FAIL-LOUD (a imagem existe mas não fica registrada ⇒ o veto vai
429
+ // bloquear; devolver ok:true aqui esconderia a causa do bloqueio adiante).
430
+ try {
431
+ recordProvenance(runDir, 'assets', abs, { source: 'openrouter_image', model });
432
+ } catch (e) {
433
+ return resolve({ ok: false, error: `imagem escrita em ${path.relative(runDir, abs)} mas NÃO registrada na proveniência do run: ${e.message}` });
434
+ }
435
+ // Confirmação por PATH + TAMANHO (Achado 4): nunca devolve o base64 ao modelo.
436
+ resolve({ ok: true, path: path.relative(runDir, abs), bytes: fs.statSync(abs).size, model });
437
+ } catch (e) { resolve({ ok: false, error: `parse: ${e.message}` }); }
438
+ };
439
+ const payload = { model, messages: [{ role: 'user', content: prompt }], modalities: ['image', 'text'] };
440
+ if (imageTransport) {
441
+ Promise.resolve(imageTransport(payload, { apiKey })).then(finish).catch((e) => resolve({ ok: false, error: String(e && e.message || e) }));
442
+ return;
443
+ }
444
+ if (!apiKey) return resolve({ ok: false, error: 'OPENROUTER_API_KEY ausente — parar, nunca substituir (regra de ouro do pod)' });
445
+ const body = JSON.stringify(payload);
446
+ const req = https.request('https://openrouter.ai/api/v1/chat/completions', {
447
+ method: 'POST', timeout: 90000,
448
+ headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}`, 'content-length': Buffer.byteLength(body) },
449
+ }, (res) => {
450
+ let s = '';
451
+ res.on('data', (d) => { s += d; });
452
+ res.on('end', () => finish(s));
453
+ });
454
+ req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'timeout openrouter' }); });
455
+ req.on('error', (e) => resolve({ ok: false, error: e.message }));
456
+ req.write(body); req.end();
457
+ });
458
+ }
459
+
460
+ module.exports = {
461
+ httpFetch, writeFile, readImage, openrouterImage,
462
+ confinedResolve, confinedResolveWritable,
463
+ isBlockedHost, computeWhitelist, MAX_FETCH_BYTES,
464
+ // Proveniência (SF10.8 / DF5-6 e1) — o par de máquina do veto de não-cópia.
465
+ provenancePathFor, recordProvenance, sha256File, provenanceKey,
466
+ // Verificação CANÔNICA (SF10.19 / DF-SF10.19-2 e2, -4): implementação ÚNICA do contrato do
467
+ // ledger, consumida pelo executor. O blessed `phash-gate.cjs` não a duplica mais.
468
+ loadProvenanceLedger, checkRunProvenance, assertRunProvenance, listProvenanceFiles,
469
+ RUN_INPUTS_DIRNAME, RUN_ASSETS_DIRNAME, PROVENANCE_SUFFIX, PROVENANCE_VERSION, PROVENANCE_IMAGE_EXT,
470
+ };