@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.
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/bin/cli.js +124 -0
- package/installer/__tests__/doctor-ready-oracles.test.cjs +140 -0
- package/installer/__tests__/merge-settings.test.cjs +330 -0
- package/installer/adopt.js +161 -0
- package/installer/copy.js +203 -0
- package/installer/doctor.js +266 -0
- package/installer/install.js +155 -0
- package/installer/manifest.js +102 -0
- package/installer/motor-scripts.js +50 -0
- package/installer/motor-scripts.json +39 -0
- package/installer/uninstall.js +99 -0
- package/installer/update.js +376 -0
- package/installer/util.js +113 -0
- package/installer/wizard.js +64 -0
- package/package.json +26 -0
- package/scripts/__fixtures__/anti-path-demo/agents/ap-writer.md +13 -0
- package/scripts/__fixtures__/anti-path-demo/templates/write-tmpl.md +7 -0
- package/scripts/__fixtures__/anti-path-demo/workflows/wf-ap.smoke.json +10 -0
- package/scripts/__fixtures__/anti-path-demo/workflows/wf-ap.yaml +25 -0
- package/scripts/__fixtures__/assess-tier/retrofit-leve/pod/agents/content-chief.md +1 -0
- package/scripts/__fixtures__/assess-tier/retrofit-leve/pod/workflows/wf-criacao-redteam.yaml +3 -0
- package/scripts/__fixtures__/assess-tier/retrofit-leve/pod-spec.json +13 -0
- package/scripts/__fixtures__/fidelity/fake-green-average-in-band.yaml +21 -0
- package/scripts/__fixtures__/fidelity/fake-green-average.yaml +18 -0
- package/scripts/__fixtures__/fidelity/fake-green-denominator.yaml +19 -0
- package/scripts/__fixtures__/fidelity/fake-green-missing-evidence.yaml +18 -0
- package/scripts/__fixtures__/fidelity/fake-green-threshold.yaml +18 -0
- package/scripts/__fixtures__/fidelity/fake-green-weight.yaml +18 -0
- package/scripts/__fixtures__/fidelity/golden.yaml +31 -0
- package/scripts/__fixtures__/fidelity/trindade-incoherent.yaml +15 -0
- package/scripts/__fixtures__/forge-canonical/pod-spec-example.json +37 -0
- package/scripts/__fixtures__/migrate-squad/authored/agents/offer-engineer.md +17 -0
- package/scripts/__fixtures__/migrate-squad/authored/knowledge/no-source.md +7 -0
- package/scripts/__fixtures__/migrate-squad/authored/knowledge/value-stack-method.md +8 -0
- package/scripts/__fixtures__/migrate-squad/authored/specs/wf-sample.smoke.json +24 -0
- package/scripts/__fixtures__/migrate-squad/authored/specs/wf-sample.spec.json +45 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/HEADLINE.md +3 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/README.md +4 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/agents/hormozi-offer.md +14 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/dead-redundant.md +4 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/method-guide.md +6 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/checklists/veto-screen.md +4 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/config/config.yaml +2 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/data/case-library.md +3 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/scripts/validate-mvn.py +16 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/sops/sop-sample.md +3 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/squad-io.yaml +14 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/tasks/gather-context.md +7 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/templates/output-tmpl.md +5 -0
- package/scripts/__fixtures__/migrate-squad/legacy-squad/workflows/wf-sample.yaml +24 -0
- package/scripts/__fixtures__/pipeline/pipelines/pipeline-checkpoint.yaml +21 -0
- package/scripts/__fixtures__/pipeline/pipelines/pipeline-demo.yaml +22 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-a/agents/maker.md +13 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-a/memory/learnings.consolidated.md +8 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-a/memory/learnings.jsonl +83 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-a/templates/make-tmpl.md +8 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-a/workflows/wf-a.yaml +16 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-b/agents/user.md +13 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-b/memory/learnings.consolidated.md +8 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-b/memory/learnings.jsonl +14 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-b/templates/use-tmpl.md +6 -0
- package/scripts/__fixtures__/pipeline/pods/pipe-b/workflows/wf-b.yaml +16 -0
- package/scripts/__fixtures__/pod-run/agents/forge-chief.md +13 -0
- package/scripts/__fixtures__/pod-run/agents/pod-critic.md +13 -0
- package/scripts/__fixtures__/pod-run/templates/build-tmpl.md +8 -0
- package/scripts/__fixtures__/pod-run/templates/gate-tmpl.md +6 -0
- package/scripts/__fixtures__/pod-run/workflows/wf-min.yaml +43 -0
- package/scripts/__fixtures__/pod-run/workflows/wf-skip-modes.yaml +50 -0
- package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-all-skippable.yaml +30 -0
- package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-criteria-escalar-de-bloco.yaml +40 -0
- package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-skip-mode-desconhecido.yaml +30 -0
- package/scripts/__fixtures__/pod-workflow/invalid-checkpoint-unknown-key.yaml +29 -0
- package/scripts/__fixtures__/pod-workflow/invalid-cycle.yaml +26 -0
- package/scripts/__fixtures__/pod-workflow/invalid-fix-loop-backedge.yaml +29 -0
- package/scripts/__fixtures__/pod-workflow/invalid-fix-loop-forward.yaml +29 -0
- package/scripts/__fixtures__/pod-workflow/invalid-handoff-open.yaml +27 -0
- package/scripts/__fixtures__/pod-workflow/invalid-knowledge-shape.yaml +18 -0
- package/scripts/__fixtures__/pod-workflow/invalid-missing-agent.yaml +15 -0
- package/scripts/__fixtures__/pod-workflow/invalid-missing-outputs.yaml +15 -0
- package/scripts/__fixtures__/pod-workflow/invalid-missing-template.yaml +15 -0
- package/scripts/__fixtures__/pod-workflow/invalid-missing-veto.yaml +13 -0
- package/scripts/__fixtures__/pod-workflow/invalid-template-not-on-disk.yaml +16 -0
- package/scripts/__fixtures__/pod-workflow/invalid-veto-prose.yaml +17 -0
- package/scripts/__fixtures__/pod-workflow/knowledge/a.md +4 -0
- package/scripts/__fixtures__/pod-workflow/knowledge/b.md +4 -0
- package/scripts/__fixtures__/pod-workflow/knowledge/c.md +4 -0
- package/scripts/__fixtures__/pod-workflow/templates/step-tmpl.md +7 -0
- package/scripts/__fixtures__/pod-workflow/valid-checkpoint-skip-modes.yaml +29 -0
- package/scripts/__fixtures__/pod-workflow/valid-fix-loop.yaml +30 -0
- package/scripts/__fixtures__/pod-workflow/valid-knowledge.yaml +30 -0
- package/scripts/__fixtures__/pod-workflow/valid-veto-always-true.yaml +20 -0
- package/scripts/__fixtures__/pod-workflow/valid.yaml +27 -0
- package/scripts/__fixtures__/smoke-canary/agents/canary-reporter.md +13 -0
- package/scripts/__fixtures__/smoke-canary/agents/canary-researcher.md +13 -0
- package/scripts/__fixtures__/smoke-canary/agents/canary-synth.md +13 -0
- package/scripts/__fixtures__/smoke-canary/memory/learnings.consolidated.md +10 -0
- package/scripts/__fixtures__/smoke-canary/memory/learnings.jsonl +448 -0
- package/scripts/__fixtures__/smoke-canary/templates/report-tmpl.md +9 -0
- package/scripts/__fixtures__/smoke-canary/templates/research-tmpl.md +7 -0
- package/scripts/__fixtures__/smoke-canary/templates/synth-tmpl.md +9 -0
- package/scripts/__fixtures__/smoke-canary/workflows/wf-canary.smoke.json +11 -0
- package/scripts/__fixtures__/smoke-canary/workflows/wf-canary.yaml +53 -0
- package/scripts/__fixtures__/source-audit/fake-green-bronze-unsignaled.yaml +10 -0
- package/scripts/__fixtures__/source-audit/fake-green-counts.yaml +9 -0
- package/scripts/__fixtures__/source-audit/fake-green-missing-evidence.yaml +9 -0
- package/scripts/__fixtures__/source-audit/fake-green-unclassified.yaml +10 -0
- package/scripts/__fixtures__/source-audit/fake-green-verdict.yaml +10 -0
- package/scripts/__fixtures__/source-audit/golden-blocked.yaml +10 -0
- package/scripts/__fixtures__/source-audit/golden.yaml +11 -0
- package/scripts/__fixtures__/sweep-forms/agents/forms-count.md +13 -0
- package/scripts/__fixtures__/sweep-forms/agents/forms-field.md +13 -0
- package/scripts/__fixtures__/sweep-forms/agents/forms-fileref.md +13 -0
- package/scripts/__fixtures__/sweep-forms/agents/forms-json.md +13 -0
- package/scripts/__fixtures__/sweep-forms/agents/forms-section.md +13 -0
- package/scripts/__fixtures__/sweep-forms/specs/wf-forms.spec.json +5 -0
- package/scripts/__fixtures__/sweep-forms/templates/t_count.md +5 -0
- package/scripts/__fixtures__/sweep-forms/templates/t_field.md +3 -0
- package/scripts/__fixtures__/sweep-forms/templates/t_fileref.md +3 -0
- package/scripts/__fixtures__/sweep-forms/templates/t_json.md +0 -0
- package/scripts/__fixtures__/sweep-forms/templates/t_section.md +3 -0
- package/scripts/__fixtures__/sweep-forms/workflows/wf-forms.smoke.json +13 -0
- package/scripts/__fixtures__/sweep-forms/workflows/wf-forms.yaml +67 -0
- package/scripts/__fixtures__/validate-story-acs-corpus/corpus-1-noop-state-json.fixture.md +18 -0
- package/scripts/__fixtures__/validate-story-acs-corpus/corpus-2-crash-buildworkflowyaml.fixture.md +26 -0
- package/scripts/__fixtures__/validate-story-acs-corpus/corpus-3-nasce-verde-skip-modes.fixture.md +29 -0
- package/scripts/__fixtures__/validate-story-acs-corpus/corpus-4-guarda-sem-irmao.fixture.md +17 -0
- package/scripts/__fixtures__/validate-story-acs-corpus/corpus-5a-diff-stat-sem-guarda.fixture.md +17 -0
- package/scripts/__fixtures__/validate-story-acs-corpus/corpus-5b-test-f-sobre-diretorio.fixture.md +20 -0
- package/scripts/__fixtures__/veto-checks/copy-clean.md +4 -0
- package/scripts/__fixtures__/veto-checks/copy-dashes.md +4 -0
- package/scripts/__fixtures__/veto-checks/n8n-bad.md +18 -0
- package/scripts/__fixtures__/veto-checks/n8n-ok.md +22 -0
- package/scripts/__fixtures__/veto-checks/page-bad.html +7 -0
- package/scripts/__fixtures__/veto-checks/phash/copy.png +0 -0
- package/scripts/__fixtures__/veto-checks/phash/distinct.png +0 -0
- package/scripts/__fixtures__/veto-checks/phash/distinct2.png +0 -0
- package/scripts/__fixtures__/veto-checks/phash/make-fixtures.cjs +203 -0
- package/scripts/__fixtures__/veto-checks/phash/ref.png +0 -0
- package/scripts/__fixtures__/veto-checks/slide-ok.html +15 -0
- package/scripts/__fixtures__/veto-checks/slide-overflow.html +14 -0
- package/scripts/__fixtures__/veto-checks/sweep-pod/agents/vc-checker.md +13 -0
- package/scripts/__fixtures__/veto-checks/sweep-pod/templates/check-tmpl.md +6 -0
- package/scripts/__fixtures__/veto-checks/sweep-pod/workflows/wf-vc.smoke.json +10 -0
- package/scripts/__fixtures__/veto-checks/sweep-pod/workflows/wf-vc.yaml +19 -0
- package/scripts/__fixtures__/voz-do-dono/README.md +23 -0
- package/scripts/__fixtures__/voz-do-dono/dono-escalar.yaml +9 -0
- package/scripts/__fixtures__/voz-do-dono/dono-guia.yaml +10 -0
- package/scripts/__fixtures__/voz-do-dono/peca-3a-pessoa.md +6 -0
- package/scripts/__fixtures__/voz-do-dono/peca-com-lexico-proibido.md +6 -0
- package/scripts/__fixtures__/voz-do-dono/peca-limpa-1a-pessoa.md +6 -0
- package/scripts/__tests__/derive-ready-transitions.test.cjs +269 -0
- package/scripts/__tests__/enforce-cycle-authority.test.cjs +869 -0
- package/scripts/__tests__/enforce-git-push-authority.test.cjs +701 -0
- package/scripts/__tests__/enforce-ready-oracles.test.cjs +745 -0
- package/scripts/__tests__/motor-scripts.test.cjs +339 -0
- package/scripts/__tests__/pc-run-pod-counter.test.cjs +58 -0
- package/scripts/__tests__/pc-run-pod.test.cjs +578 -0
- package/scripts/__tests__/validate-pod-workflow.test.cjs +90 -0
- package/scripts/__tests__/validate-story-acs.test.cjs +607 -0
- package/scripts/anti-path.test.cjs +163 -0
- package/scripts/arena.cjs +514 -0
- package/scripts/arena.test.cjs +213 -0
- package/scripts/brief-leitura-obrigatoria.test.cjs +364 -0
- package/scripts/consolidate-knowledge-runner.test.cjs +249 -0
- package/scripts/default-funcional.test.cjs +153 -0
- package/scripts/derive-ready-transitions.cjs +325 -0
- package/scripts/forge-additive.test.cjs +158 -0
- package/scripts/forge-assess-tier.cjs +205 -0
- package/scripts/forge-assess-tier.test.cjs +114 -0
- package/scripts/forge-canonical.test.cjs +624 -0
- package/scripts/forge-generate.cjs +1303 -0
- package/scripts/forge-harden-learning.cjs +921 -0
- package/scripts/forge-harden-learning.test.cjs +242 -0
- package/scripts/forge-ingest-memory.cjs +136 -0
- package/scripts/forge-upgrade-pod.cjs +368 -0
- package/scripts/forge.test.cjs +609 -0
- package/scripts/harden-knowledge.test.cjs +612 -0
- package/scripts/harness-g5-lite.cjs +243 -0
- package/scripts/harness-g5-lite.test.cjs +134 -0
- package/scripts/hn-carousel-contrato-entrada.test.cjs +96 -0
- package/scripts/migrate-squad-orchestration.test.cjs +146 -0
- package/scripts/migration-manifest-sf3.2.test.cjs +382 -0
- package/scripts/migration-manifest-sf3.3.test.cjs +170 -0
- package/scripts/migration-manifest-sf5.5.test.cjs +189 -0
- package/scripts/migration-manifest.cjs +1146 -0
- package/scripts/migration-manifest.test.cjs +186 -0
- package/scripts/npm-scope-invocation.test.cjs +194 -0
- package/scripts/owner-layer.cjs +442 -0
- package/scripts/owner-layer.test.cjs +358 -0
- package/scripts/pc-run-pipeline.cjs +289 -0
- package/scripts/pc-run-pipeline.test.cjs +256 -0
- package/scripts/pc-run-pod-countgte.test.cjs +66 -0
- package/scripts/pc-run-pod.cjs +1355 -0
- package/scripts/phash-gate.test.cjs +433 -0
- package/scripts/pod-export-compile.cjs +487 -0
- package/scripts/pod-export-compile.test.cjs +312 -0
- package/scripts/pod-export-executor.cjs +678 -0
- package/scripts/pod-export-executor.test.cjs +1341 -0
- package/scripts/pod-export-hardening.test.cjs +531 -0
- package/scripts/pod-export-postprocess.cjs +427 -0
- package/scripts/pod-export-postprocess.test.cjs +558 -0
- package/scripts/pod-export-queue-race.test.cjs +865 -0
- package/scripts/pod-export-release.cjs +473 -0
- package/scripts/pod-export-release.test.cjs +267 -0
- package/scripts/pod-export-service.cjs +1297 -0
- package/scripts/pod-export-service.test.cjs +489 -0
- package/scripts/pod-export-tools.cjs +470 -0
- package/scripts/pod-export-ui.cjs +805 -0
- package/scripts/pod-export-ui.test.cjs +301 -0
- package/scripts/pod-memory-budget.test.cjs +309 -0
- package/scripts/pod-memory-note.cjs +357 -0
- package/scripts/pod-memory-note.test.cjs +452 -0
- package/scripts/pod-memory.cjs +1252 -0
- package/scripts/pod-memory.test.cjs +951 -0
- package/scripts/render-png.test.cjs +307 -0
- package/scripts/smoke-isolation.test.cjs +100 -0
- package/scripts/smoke-pod-workflow.cjs +755 -0
- package/scripts/smoke-sweep.test.cjs +170 -0
- package/scripts/validate-adopt.js +235 -0
- package/scripts/validate-branding.js +84 -0
- package/scripts/validate-budgets.js +140 -0
- package/scripts/validate-fidelity-output.cjs +267 -0
- package/scripts/validate-fidelity-output.test.cjs +101 -0
- package/scripts/validate-install.js +230 -0
- package/scripts/validate-no-clone.cjs +220 -0
- package/scripts/validate-no-clone.test.cjs +77 -0
- package/scripts/validate-pipeline.cjs +145 -0
- package/scripts/validate-pod-workflow.cjs +878 -0
- package/scripts/validate-source-audit.cjs +170 -0
- package/scripts/validate-source-audit.test.cjs +90 -0
- package/scripts/validate-story-acs.cjs +862 -0
- package/scripts/validate-update-scope-motor.js +421 -0
- package/scripts/veto-checks/html-valid.cjs +92 -0
- package/scripts/veto-checks/lexical-check.cjs +62 -0
- package/scripts/veto-checks/phash-gate.cjs +589 -0
- package/scripts/veto-checks/render-gate.cjs +165 -0
- package/scripts/veto-checks/render-png.cjs +358 -0
- package/scripts/veto-checks.test.cjs +219 -0
- package/scripts/watcher-audit.cjs +178 -0
- package/template/.claude/CLAUDE.md +103 -0
- package/template/.claude/agents/pc-analyst.md +100 -0
- package/template/.claude/agents/pc-architect.md +32 -0
- package/template/.claude/agents/pc-dev.md +27 -0
- package/template/.claude/agents/pc-devops.md +45 -0
- package/template/.claude/agents/pc-planner.md +32 -0
- package/template/.claude/agents/pc-po.md +25 -0
- package/template/.claude/agents/pc-qa.md +25 -0
- package/template/.claude/commands/power/academy.md +9 -0
- package/template/.claude/commands/power/automation.md +9 -0
- package/template/.claude/commands/power/content-carousel.md +267 -0
- package/template/.claude/commands/power/content.md +9 -0
- package/template/.claude/commands/power/dev.md +9 -0
- package/template/.claude/commands/power/forge.md +224 -0
- package/template/.claude/commands/power/hormozi.md +221 -0
- package/template/.claude/commands/power/modo.md +57 -0
- package/template/.claude/hooks/README.md +114 -0
- package/template/.claude/hooks/context-budget.cjs +64 -0
- package/template/.claude/hooks/enforce-cycle-authority.cjs +457 -0
- package/template/.claude/hooks/enforce-git-push-authority.cjs +925 -0
- package/template/.claude/hooks/enforce-ready-oracles.cjs +502 -0
- package/template/.claude/rules/authority.md +76 -0
- package/template/.claude/rules/curadoria-de-conhecimento.md +30 -0
- package/template/.claude/rules/model-matrix.md +34 -0
- package/template/.claude/rules/orchestration.md +133 -0
- package/template/.claude/settings.json +50 -0
- package/template/.claude/skills/pc-apply-qa-fixes/SKILL.md +108 -0
- package/template/.claude/skills/pc-apply-qa-fixes/references/fix-loop.md +40 -0
- package/template/.claude/skills/pc-apply-qa-fixes/references/fix-request-template.md +70 -0
- package/template/.claude/skills/pc-arena/SKILL.md +96 -0
- package/template/.claude/skills/pc-close-story/SKILL.md +105 -0
- package/template/.claude/skills/pc-commit/SKILL.md +108 -0
- package/template/.claude/skills/pc-develop-story/SKILL.md +116 -0
- package/template/.claude/skills/pc-full-cycle/SKILL.md +150 -0
- package/template/.claude/skills/pc-pipeline/SKILL.md +63 -0
- package/template/.claude/skills/pc-pipeline/references/gate-templates.md +46 -0
- package/template/.claude/skills/pc-review-story/SKILL.md +129 -0
- package/template/.claude/skills/pc-review-story/references/false-positive-detection.md +50 -0
- package/template/.claude/skills/pc-review-story/references/nfr-assess.md +47 -0
- package/template/.claude/skills/pc-review-story/references/risk-profile.md +75 -0
- package/template/.claude/skills/pc-review-story/references/security-checklist.md +69 -0
- package/template/.claude/skills/pc-review-story/references/test-design.md +37 -0
- package/template/.claude/skills/pc-review-story/references/trace-requirements.md +46 -0
- package/template/.claude/skills/pc-run-pod/SKILL.md +123 -0
- package/template/.claude/skills/pc-validate-story/SKILL.md +114 -0
- package/template/.claude/skills/pc-wave/SKILL.md +133 -0
- package/template/.env.example +10 -0
- package/template/knowledge/analyst/brainstorming.md +40 -0
- package/template/knowledge/analyst/brief-skeleton.md +32 -0
- package/template/knowledge/analyst/competitor-skeleton.md +39 -0
- package/template/knowledge/analyst/elicitation-menu.md +49 -0
- package/template/knowledge/analyst/market-research-skeleton.md +36 -0
- package/template/knowledge/analyst/research-prompt-structure.md +48 -0
- package/template/knowledge/analyst/roi-skeleton.md +51 -0
- package/template/knowledge/voice-dna/README.md +99 -0
- package/template/knowledge/voice-dna/voice-dna.example.yaml +157 -0
- package/template/pods/README.md +64 -0
- package/template/pods/academy/README.md +37 -0
- package/template/pods/academy/agents/academy-chief.md +27 -0
- package/template/pods/academy/examples/palestra.example.md +142 -0
- package/template/pods/academy/pod-spec.json +44 -0
- package/template/pods/academy/templates/materiais-tmpl.md +17 -0
- package/template/pods/academy/templates/revisao-didatica-tmpl.md +20 -0
- package/template/pods/academy/workflows/wf-materiais-revisao.yaml +30 -0
- package/template/pods/authority-content/README.md +43 -0
- package/template/pods/authority-content/agents/content-chief.md +28 -0
- package/template/pods/authority-content/pod-spec.json +45 -0
- package/template/pods/authority-content/templates/criacao-na-voz-tmpl.md +17 -0
- package/template/pods/authority-content/templates/red-team-factual-tmpl.md +23 -0
- package/template/pods/authority-content/workflows/wf-criacao-redteam.yaml +30 -0
- package/template/pods/automation/README.md +37 -0
- package/template/pods/automation/agents/langgraph-architect.md +30 -0
- package/template/pods/automation/agents/n8n-architect.md +30 -0
- package/template/pods/automation/examples/README.md +46 -0
- package/template/pods/automation/examples/n8n-content-radar.example.json +380 -0
- package/template/pods/automation/knowledge/langgraph-patterns.md +126 -0
- package/template/pods/automation/knowledge/n8n-patterns.md +131 -0
- package/template/pods/automation/memory/README.md +16 -0
- package/template/pods/automation/memory/learnings.consolidated.md +8 -0
- package/template/pods/automation/memory/learnings.jsonl +0 -0
- package/template/pods/automation/pod-spec.json +109 -0
- package/template/pods/automation/specs/wf-langgraph-build.spec.json +107 -0
- package/template/pods/automation/squad-io.yaml +28 -0
- package/template/pods/automation/templates/lg-build-tmpl.md +17 -0
- package/template/pods/automation/templates/lg-design-tmpl.md +23 -0
- package/template/pods/automation/templates/lg-discover-tmpl.md +20 -0
- package/template/pods/automation/templates/lg-handoff-tmpl.md +20 -0
- package/template/pods/automation/templates/lg-test-tmpl.md +20 -0
- package/template/pods/automation/templates/n8n-build-tmpl.md +14 -0
- package/template/pods/automation/templates/n8n-design-tmpl.md +23 -0
- package/template/pods/automation/templates/n8n-discover-tmpl.md +20 -0
- package/template/pods/automation/templates/n8n-handoff-tmpl.md +20 -0
- package/template/pods/automation/templates/n8n-test-tmpl.md +20 -0
- package/template/pods/automation/workflows/wf-langgraph-build.smoke.json +11 -0
- package/template/pods/automation/workflows/wf-langgraph-build.yaml +64 -0
- package/template/pods/automation/workflows/wf-n8n-build.smoke.json +11 -0
- package/template/pods/automation/workflows/wf-n8n-build.yaml +65 -0
- package/template/pods/content-carousel/README.md +91 -0
- package/template/pods/content-carousel/agents/carousel-builder.md +28 -0
- package/template/pods/content-carousel/agents/carousel-visual.md +24 -0
- package/template/pods/content-carousel/agents/hn-curator.md +74 -0
- package/template/pods/content-carousel/config/heuristic-watcher-rubric.yaml +59 -0
- package/template/pods/content-carousel/knowledge/aquisicao-ancora.md +443 -0
- package/template/pods/content-carousel/knowledge/cta-brief.md +25 -0
- package/template/pods/content-carousel/knowledge/dono.example.yaml +45 -0
- package/template/pods/content-carousel/knowledge/identidade-visual-mascote.md +62 -0
- package/template/pods/content-carousel/knowledge/image-generation.md +62 -0
- package/template/pods/content-carousel/knowledge/selecao-de-ancora.md +60 -0
- package/template/pods/content-carousel/knowledge/slides-html.md +56 -0
- package/template/pods/content-carousel/memory/README.md +18 -0
- package/template/pods/content-carousel/memory/learnings.consolidated.md +6 -0
- package/template/pods/content-carousel/memory/learnings.jsonl +0 -0
- package/template/pods/content-carousel/pod-spec.json +100 -0
- package/template/pods/content-carousel/squad-io.yaml +10 -0
- package/template/pods/content-carousel/templates/assembly-tmpl.md +23 -0
- package/template/pods/content-carousel/templates/digest-tmpl.md +29 -0
- package/template/pods/content-carousel/templates/visuals-tmpl.md +20 -0
- package/template/pods/content-carousel/workflows/wf-content-carousel.smoke.json +23 -0
- package/template/pods/content-carousel/workflows/wf-content-carousel.yaml +80 -0
- package/template/pods/dev-ia/README.md +45 -0
- package/template/pods/dev-ia/agents/db-architect.md +33 -0
- package/template/pods/dev-ia/agents/ux-architect.md +29 -0
- package/template/pods/dev-ia/knowledge/db/migration-protocol.md +95 -0
- package/template/pods/dev-ia/knowledge/db/rls-patterns.md +101 -0
- package/template/pods/dev-ia/knowledge/db/schema-review-checklist.md +76 -0
- package/template/pods/dev-ia/memory/README.md +16 -0
- package/template/pods/dev-ia/memory/learnings.consolidated.md +8 -0
- package/template/pods/dev-ia/memory/learnings.jsonl +0 -0
- package/template/pods/dev-ia/pod-spec.json +34 -0
- package/template/pods/dev-ia/templates/schema-review-tmpl.md +23 -0
- package/template/pods/dev-ia/templates/ux-flow-review-tmpl.md +23 -0
- package/template/pods/dev-ia/tools/db-ops/backup-manager.sh +321 -0
- package/template/pods/dev-ia/tools/db-ops/best-practices-auditor.sh +534 -0
- package/template/pods/dev-ia/tools/db-ops/bootstrap-runner.sh +255 -0
- package/template/pods/dev-ia/tools/db-ops/data-loader.sh +616 -0
- package/template/pods/dev-ia/tools/db-ops/detect-database.sh +113 -0
- package/template/pods/dev-ia/tools/db-ops/explain-analyzer.sh +285 -0
- package/template/pods/dev-ia/tools/db-ops/health-checker.sh +323 -0
- package/template/pods/dev-ia/tools/db-ops/migration-runner.sh +308 -0
- package/template/pods/dev-ia/tools/db-ops/postgresql-schema-loader.sh +334 -0
- package/template/pods/dev-ia/tools/db-ops/query-runner.sh +171 -0
- package/template/pods/dev-ia/tools/db-ops/rls-policy-installer.sh +480 -0
- package/template/pods/dev-ia/tools/db-ops/rls-test-suite.sh +172 -0
- package/template/pods/dev-ia/tools/db-ops/schema-context-loader.sh +176 -0
- package/template/pods/hormozi-method/README.md +52 -0
- package/template/pods/hormozi-method/agents/acquisition-strategist.md +33 -0
- package/template/pods/hormozi-method/agents/conversion-copywriter.md +34 -0
- package/template/pods/hormozi-method/agents/monetization-architect.md +35 -0
- package/template/pods/hormozi-method/agents/offer-engineer.md +37 -0
- package/template/pods/hormozi-method/knowledge/core-four-leads.md +51 -0
- package/template/pods/hormozi-method/knowledge/grand-slam-offer.md +66 -0
- package/template/pods/hormozi-method/knowledge/growth-decision.md +38 -0
- package/template/pods/hormozi-method/knowledge/hooks-and-ads.md +56 -0
- package/template/pods/hormozi-method/knowledge/launch-email.md +31 -0
- package/template/pods/hormozi-method/knowledge/money-model.md +46 -0
- package/template/pods/hormozi-method/knowledge/opportunity-screening.md +44 -0
- package/template/pods/hormozi-method/knowledge/pages.md +25 -0
- package/template/pods/hormozi-method/knowledge/pos-lancamento-leads.md +56 -0
- package/template/pods/hormozi-method/knowledge/pricing.md +36 -0
- package/template/pods/hormozi-method/knowledge/retention-ltv.md +37 -0
- package/template/pods/hormozi-method/knowledge/scale-roadmap.md +35 -0
- package/template/pods/hormozi-method/knowledge/swipefiles-curados.md +44 -0
- package/template/pods/hormozi-method/knowledge/value-equation-diagnostic.md +77 -0
- package/template/pods/hormozi-method/memory/README.md +16 -0
- package/template/pods/hormozi-method/memory/learnings.consolidated.md +6 -0
- package/template/pods/hormozi-method/memory/learnings.jsonl +0 -0
- package/template/pods/hormozi-method/pod-spec.json +50 -0
- package/template/pods/hormozi-method/specs/wf-full-launch-sequence.spec.json +149 -0
- package/template/pods/hormozi-method/specs/wf-lead-magnet-pipeline.spec.json +125 -0
- package/template/pods/hormozi-method/specs/wf-money-model-design.spec.json +117 -0
- package/template/pods/hormozi-method/specs/wf-paid-ads-campaign.spec.json +132 -0
- package/template/pods/hormozi-method/specs/wf-scaling-diagnostic.spec.json +121 -0
- package/template/pods/hormozi-method/squad-io.yaml +43 -0
- package/template/pods/hormozi-method/templates/conversion-copywriter-tmpl.md +20 -0
- package/template/pods/hormozi-method/templates/launch-execucao-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/launch-fechamento-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/launch-pos-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/launch-preparacao-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/lm-copy-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/lm-distribuicao-tmpl.md +18 -0
- package/template/pods/hormozi-method/templates/lm-estrategia-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/mm-arquitetura-tmpl.md +18 -0
- package/template/pods/hormozi-method/templates/mm-copy-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/mm-diagnostico-tmpl.md +14 -0
- package/template/pods/hormozi-method/templates/monetization-architect-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/offer-engineer-tmpl.md +20 -0
- package/template/pods/hormozi-method/templates/paid-criativos-tmpl.md +19 -0
- package/template/pods/hormozi-method/templates/paid-fundacao-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/paid-teste-escala-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/scaling-diagnostico-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/scaling-priorizacao-tmpl.md +17 -0
- package/template/pods/hormozi-method/templates/scaling-roadmap-tmpl.md +17 -0
- package/template/pods/hormozi-method/workflows/wf-full-launch-sequence.smoke.json +12 -0
- package/template/pods/hormozi-method/workflows/wf-full-launch-sequence.yaml +56 -0
- package/template/pods/hormozi-method/workflows/wf-grand-slam-offer.smoke.json +11 -0
- package/template/pods/hormozi-method/workflows/wf-grand-slam-offer.yaml +45 -0
- package/template/pods/hormozi-method/workflows/wf-lead-magnet-pipeline.smoke.json +9 -0
- package/template/pods/hormozi-method/workflows/wf-lead-magnet-pipeline.yaml +45 -0
- package/template/pods/hormozi-method/workflows/wf-money-model-design.smoke.json +11 -0
- package/template/pods/hormozi-method/workflows/wf-money-model-design.yaml +45 -0
- package/template/pods/hormozi-method/workflows/wf-paid-ads-campaign.smoke.json +9 -0
- package/template/pods/hormozi-method/workflows/wf-paid-ads-campaign.yaml +46 -0
- package/template/pods/hormozi-method/workflows/wf-scaling-diagnostic.smoke.json +11 -0
- package/template/pods/hormozi-method/workflows/wf-scaling-diagnostic.yaml +44 -0
- package/template/pods/squad-forge/README.md +53 -0
- package/template/pods/squad-forge/agents/forge-chief.md +31 -0
- package/template/pods/squad-forge/agents/mind-extractor.md +29 -0
- package/template/pods/squad-forge/agents/pod-critic.md +26 -0
- package/template/pods/squad-forge/config/heuristic-watcher-rubric.yaml +74 -0
- package/template/pods/squad-forge/knowledge/fidelity-rubric.yaml +176 -0
- package/template/pods/squad-forge/knowledge/g6-memory-check.md +17 -0
- package/template/pods/squad-forge/knowledge/memory-ingest.md +16 -0
- package/template/pods/squad-forge/knowledge/source-audit-rubric.yaml +60 -0
- package/template/pods/squad-forge/memory/README.md +16 -0
- package/template/pods/squad-forge/memory/learnings.consolidated.md +8 -0
- package/template/pods/squad-forge/memory/learnings.jsonl +0 -0
- package/template/pods/squad-forge/pod-spec.json +166 -0
- package/template/pods/squad-forge/squad-io.yaml +13 -0
- package/template/pods/squad-forge/tasks/pipeline.md +158 -0
- package/template/pods/squad-forge/templates/discovery-tmpl.md +40 -0
- package/template/pods/squad-forge/templates/generation-tmpl.md +18 -0
- package/template/pods/squad-forge/templates/red-team-tmpl.md +17 -0
- package/template/pods/squad-forge/templates/register-tmpl.md +17 -0
- package/template/pods/squad-forge/templates/workflow-tmpl.yaml +101 -0
- package/template/pods/squad-forge/workflows/wf-forge.smoke.json +12 -0
- package/template/pods/squad-forge/workflows/wf-forge.yaml +60 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* render-gate.cjs — veto BLESSED (forma command_check, ADR-011). RENDERIZA o HTML do
|
|
5
|
+
* artefato em headless Chrome e detecta OVERFLOW de layout (conteúdo que estoura a
|
|
6
|
+
* viewport ou uma caixa) — o defeito de campo (a): 2 interfaces quebradas na R5 passaram
|
|
7
|
+
* por todo veto textual; "o braço que fazia render+inspeção peça a peça não quebrou".
|
|
8
|
+
*
|
|
9
|
+
* ZERO-DEP npm (ADR-011 Decisão 3): usa o binário de sistema headless Chrome via
|
|
10
|
+
* execFileSync (nunca uma lib npm). DEPENDÊNCIA AUSENTE = FAIL-LOUD (exit 3, mensagem
|
|
11
|
+
* acionável), NUNCA skip-silencioso — um veto que pula quando a ferramenta falta é
|
|
12
|
+
* green-fake (o artefato embarca não-verificado com o run parecendo limpo).
|
|
13
|
+
*
|
|
14
|
+
* MECANISMO: injeta (numa CÓPIA temporária, sem mutar o artefato) um <script> de medição
|
|
15
|
+
* que marca `document.documentElement[data-render-gate] = OVERFLOW|OK` conforme
|
|
16
|
+
* scrollWidth/scrollHeight excedam a viewport ou uma caixa sem overflow rolável; roda
|
|
17
|
+
* `chrome --headless --dump-dom` e lê o marcador do DOM renderizado.
|
|
18
|
+
*
|
|
19
|
+
* LIMITES HONESTOS (documentados — ADR-011 Decisão 3, render-gate portável só onde há
|
|
20
|
+
* headless browser): detecta OVERFLOW/CORTE por métrica de scroll; NÃO detecta colisão
|
|
21
|
+
* visual por z-index/sobreposição de cor, contraste, nem estética. Viewport é parâmetro
|
|
22
|
+
* (--width/--height; default 1080×1350, retrato Instagram). Onde não há Chrome, BLOQUEIA
|
|
23
|
+
* com mensagem (nunca falso-verde). Direção FAIL-CLOSED por design (na dúvida, bloqueia).
|
|
24
|
+
* CONTEÚDO DEVE SER PINADO À MOLDURA: o artefato precisa fixar `html,body{width:W;height:H}`
|
|
25
|
+
* casando a viewport (--width/--height); um elemento de largura EXATAMENTE igual à viewport
|
|
26
|
+
* num body NÃO-pinado toca a borda e é reportado como overflow de borda (falso-positivo de
|
|
27
|
+
* borda, fail-closed) — os slides de pod já são autorados pinados à moldura, então não ocorre
|
|
28
|
+
* no uso pretendido (MINOR-1 do gate SF7.2).
|
|
29
|
+
*
|
|
30
|
+
* Uso: node scripts/veto-checks/render-gate.cjs <artefato.html> [--width N] [--height N]
|
|
31
|
+
* Exit 0 = sem overflow; exit 1 = overflow detectado (bloqueia); exit 2 = uso;
|
|
32
|
+
* exit 3 = Chrome ausente (fail-loud) ou falha ao renderizar.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const os = require('node:os');
|
|
37
|
+
const path = require('node:path');
|
|
38
|
+
const { execFileSync } = require('node:child_process');
|
|
39
|
+
|
|
40
|
+
const CHROME_CANDIDATES = [
|
|
41
|
+
'google-chrome-stable', 'google-chrome', 'chromium', 'chromium-browser', 'chrome',
|
|
42
|
+
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
|
43
|
+
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
/** Testa se um candidato é um Chrome utilizável. */
|
|
47
|
+
function usable(cand) {
|
|
48
|
+
try {
|
|
49
|
+
if (cand.includes('/')) return fs.existsSync(cand) ? cand : null; // caminho absoluto
|
|
50
|
+
execFileSync(cand, ['--version'], { stdio: 'ignore', timeout: 8000 }); // nome no PATH
|
|
51
|
+
return cand;
|
|
52
|
+
} catch { return null; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Resolve um binário de Chrome utilizável, ou null (→ fail-loud no chamador).
|
|
57
|
+
* Se CHROME_BIN está SETADO, é honrado ESTRITAMENTE: inválido → null (fail-loud), SEM
|
|
58
|
+
* fallback silencioso para outro navegador (um veto que troca de ferramenta por baixo dos
|
|
59
|
+
* panos seria green-fake). Sem CHROME_BIN, varre os candidatos conhecidos.
|
|
60
|
+
*/
|
|
61
|
+
function probeChrome() {
|
|
62
|
+
if (process.env.CHROME_BIN) return usable(process.env.CHROME_BIN);
|
|
63
|
+
for (const cand of CHROME_CANDIDATES) {
|
|
64
|
+
const u = usable(cand);
|
|
65
|
+
if (u) return u;
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Medição VIEWPORT-INDEPENDENTE: compara cada elemento (body + descendentes) com a PRÓPRIA
|
|
71
|
+
// caixa (scrollWidth/Height vs clientWidth/Height). NÃO usa documentElement.clientHeight, que
|
|
72
|
+
// em headless retorna a viewport interna (menor que --window-size por causa do cromo da
|
|
73
|
+
// janela) e geraria falso-positivo. Um filho que estoura a caixa de overflow:hidden do pai
|
|
74
|
+
// aparece como scroll no PAI — é o corte real que queremos pegar.
|
|
75
|
+
const MEASURE_SCRIPT = `
|
|
76
|
+
<script>(function(){
|
|
77
|
+
function overflowing(){
|
|
78
|
+
// TOL=8px: ignora ruído de métricas de texto (line-height apertado faz scrollHeight
|
|
79
|
+
// exceder clientHeight por ~1-4px sem corte real). Os defeitos de campo (interface
|
|
80
|
+
// quebrada, caixa estourando a moldura) são GROSSEIROS (centenas de px). LIMITE HONESTO:
|
|
81
|
+
// este gate pega corte GROSSEIRO, não descender clipado por sub-pixel.
|
|
82
|
+
var TOL=8, root=document.body;
|
|
83
|
+
if (!root) return false;
|
|
84
|
+
var els=[root].concat([].slice.call(root.querySelectorAll('*')));
|
|
85
|
+
for (var i=0;i<els.length;i++){
|
|
86
|
+
var e=els[i], cs=getComputedStyle(e);
|
|
87
|
+
if (cs.display==='none' || cs.visibility==='hidden') continue;
|
|
88
|
+
var scrollable=/auto|scroll/.test((cs.overflow||'')+(cs.overflowX||'')+(cs.overflowY||''));
|
|
89
|
+
if (!scrollable && (e.scrollWidth>e.clientWidth+TOL || e.scrollHeight>e.clientHeight+TOL)) return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
try { document.documentElement.setAttribute('data-render-gate', overflowing()?'OVERFLOW':'OK'); }
|
|
94
|
+
catch(err){ document.documentElement.setAttribute('data-render-gate','ERROR'); }
|
|
95
|
+
})();</script>`;
|
|
96
|
+
|
|
97
|
+
/** Decide o veredito a partir do DOM renderizado (dump-dom). Pura, testável. */
|
|
98
|
+
function decideFromDom(domHtml) {
|
|
99
|
+
const m = domHtml.match(/data-render-gate="([^"]*)"/);
|
|
100
|
+
if (!m) return { ok: false, verdict: 'no-marker', reason: 'marcador de medição ausente no DOM renderizado (script não executou?)' };
|
|
101
|
+
if (m[1] === 'OK') return { ok: true, verdict: 'OK' };
|
|
102
|
+
if (m[1] === 'OVERFLOW') return { ok: false, verdict: 'OVERFLOW', reason: 'conteúdo estoura a viewport ou uma caixa não-rolável (overflow/corte)' };
|
|
103
|
+
return { ok: false, verdict: m[1], reason: `medição retornou "${m[1]}"` };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function renderDom(chromeBin, htmlPath, width, height) {
|
|
107
|
+
return execFileSync(chromeBin, [
|
|
108
|
+
'--headless=new', '--disable-gpu', '--no-sandbox', '--hide-scrollbars',
|
|
109
|
+
`--window-size=${width},${height}`, '--virtual-time-budget=4000',
|
|
110
|
+
'--dump-dom', `file://${htmlPath}`,
|
|
111
|
+
], { encoding: 'utf8', timeout: 30000, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function parseArgs(argv) {
|
|
115
|
+
const o = { width: 1080, height: 1350 };
|
|
116
|
+
for (let i = 0; i < argv.length; i++) {
|
|
117
|
+
if (argv[i] === '--width') o.width = Number.parseInt(argv[++i], 10);
|
|
118
|
+
else if (argv[i] === '--height') o.height = Number.parseInt(argv[++i], 10);
|
|
119
|
+
}
|
|
120
|
+
return o;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function main() {
|
|
124
|
+
const artifact = process.argv[2];
|
|
125
|
+
if (!artifact || !fs.existsSync(artifact)) {
|
|
126
|
+
console.error(`render-gate: artefato não encontrado: ${artifact}`);
|
|
127
|
+
process.exit(2);
|
|
128
|
+
}
|
|
129
|
+
const { width, height } = parseArgs(process.argv.slice(3));
|
|
130
|
+
|
|
131
|
+
const chromeBin = probeChrome();
|
|
132
|
+
if (!chromeBin) {
|
|
133
|
+
console.error('render-gate BLOQUEOU (fail-loud): headless Chrome não encontrado. ' +
|
|
134
|
+
'Defina CHROME_BIN ou instale google-chrome/chromium. Não é possível verificar a ' +
|
|
135
|
+
'renderização — BLOQUEANDO em vez de pular (skip = green-fake).');
|
|
136
|
+
process.exit(3);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Cópia temporária com o script de medição anexado (não muta o artefato; scripts não
|
|
140
|
+
// afetam o layout, então a medição reflete o render real).
|
|
141
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-render-gate-'));
|
|
142
|
+
const tmpHtml = path.join(tmpDir, 'measured.html');
|
|
143
|
+
let dom;
|
|
144
|
+
try {
|
|
145
|
+
fs.writeFileSync(tmpHtml, fs.readFileSync(artifact, 'utf8') + MEASURE_SCRIPT, 'utf8');
|
|
146
|
+
dom = renderDom(chromeBin, tmpHtml, width, height);
|
|
147
|
+
} catch (e) {
|
|
148
|
+
console.error(`render-gate BLOQUEOU (fail-loud): falha ao renderizar em ${chromeBin}: ${e.message}`);
|
|
149
|
+
process.exit(3);
|
|
150
|
+
} finally {
|
|
151
|
+
try { fs.rmSync(tmpDir, { recursive: true, force: true }); } catch { /* best-effort */ }
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const r = decideFromDom(dom);
|
|
155
|
+
if (r.ok) {
|
|
156
|
+
console.log(`render-gate OK: sem overflow em ${width}×${height} (${chromeBin.split('/').pop()}).`);
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
console.error(`render-gate BLOQUEOU: ${r.reason} (veredito ${r.verdict}).`);
|
|
160
|
+
process.exit(r.verdict === 'no-marker' ? 3 : 1);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (require.main === module) main();
|
|
164
|
+
|
|
165
|
+
module.exports = { probeChrome, decideFromDom, parseArgs, CHROME_CANDIDATES };
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* render-png.cjs — veto BLESSED (forma command_check, ADR-011) que PRODUZ o entregável de
|
|
5
|
+
* publicacao e o VERIFICA por maquina, ANTES do checkpoint humano da etapa.
|
|
6
|
+
*
|
|
7
|
+
* Story SF11.11 / ADR-025 (`DF-SF11.11-1`..`-4`). O problema que ele resolve: o pod entregava
|
|
8
|
+
* HTML e o dono publicava PNG renderizado a mao, fora do caminho governado — logo ele aprovava
|
|
9
|
+
* uma coisa e publicava outra. Como `verifyStep` (onde os vetos rodam) e chamado por
|
|
10
|
+
* `advance()` ANTES de `isBlocking`/`awaiting_approval`, declarar a renderizacao aqui faz os
|
|
11
|
+
* PNGs existirem, medidos e aprovados por maquina, no instante em que o humano e convocado —
|
|
12
|
+
* em QUALQUER modo (`verifyStep`/`evalVeto` nao leem `mode`), sem uma linha de motor.
|
|
13
|
+
*
|
|
14
|
+
* GENERICO POR CONTRATO (ADR-025 `DF-SF11.11-1`): nenhum nome de pod, de peca ou de dominio
|
|
15
|
+
* mora aqui. Tudo que e especifico do pod vem por argumento LITERAL do `wf` (ADR-011 proibe
|
|
16
|
+
* interpolacao): quantos arquivos, que dimensao, de onde e para onde.
|
|
17
|
+
*
|
|
18
|
+
* ZERO-DEP npm (ADR-011 Decisao 3): headless Chrome de sistema via execFileSync. DEPENDENCIA
|
|
19
|
+
* AUSENTE = FAIL-LOUD (exit 3), NUNCA skip-silencioso. NAO EXISTE escape hatch: nenhuma flag,
|
|
20
|
+
* nenhuma env var e nenhum ramo de codigo faz este script sair 0 sem ter renderizado e medido
|
|
21
|
+
* as `--expect` pecas (decisao do dono, 2026-08-03: uma rodada sem Chrome NAO FECHA, nem
|
|
22
|
+
* entregando HTML — a alternativa reintroduziria a degradacao silenciosa que a SF11.4b
|
|
23
|
+
* eliminou). `probeChrome` e REUSADO de `render-gate.cjs` (mesma recusa de fallback silencioso
|
|
24
|
+
* quando CHROME_BIN esta setado e invalido) — este script nao reimplementa a sonda.
|
|
25
|
+
*
|
|
26
|
+
* O QUE ELE PROVA: que existem EXATAMENTE `--expect` arquivos `.html` no dir declarado, que o
|
|
27
|
+
* Chrome conseguiu renderizar cada um, e que cada PNG gerado tem `--width`x`--height` EXATOS,
|
|
28
|
+
* lidos do IHDR do proprio arquivo (`readUInt32BE(16)`/`readUInt32BE(20)`), comparados com
|
|
29
|
+
* `===`. Igualdade de inteiros: sem tolerancia, sem `+/-`, sem limiar calibravel (ADR-018
|
|
30
|
+
* `DF4-5` — o oraculo e o formato de publicacao declarado no `wf`, nao uma amostragem).
|
|
31
|
+
*
|
|
32
|
+
* O QUE ELE NAO PROVA (dito antes que alguem descubra): que a peca esta bonita, que o texto
|
|
33
|
+
* coube, que o contraste presta ou que o encaixe copy x canvas ficou bom. Isso e JULGAMENTO
|
|
34
|
+
* HUMANO no checkpoint (fronteira ADR-002), agora com a evidencia certa na mao. Overflow de
|
|
35
|
+
* layout tem script proprio (`render-gate.cjs`) e NAO entra aqui (ADR-025 `DF-SF11.11-13`,
|
|
36
|
+
* divida 1).
|
|
37
|
+
*
|
|
38
|
+
* LIMITE HONESTO DA CHECAGEM DE DIMENSAO (MEDIDO na implementacao da SF11.11, corrigindo o que
|
|
39
|
+
* o ADR-025 supunha): a dimensao do PNG e ditada pelo `--window-size` do Chrome, NAO pelo CSS
|
|
40
|
+
* do documento. Medido: um HTML pinado a 1080x2000 e outro a 900x700, renderizados com
|
|
41
|
+
* `--window-size=1080,1350`, produzem os DOIS um PNG 1080x1350. Logo esta checagem NAO e um
|
|
42
|
+
* veto sobre o CONTEUDO do HTML — e uma POS-CONDICAO SOBRE O RENDERIZADOR: ela pega o
|
|
43
|
+
* renderizador que desobedece a moldura (escala de tela dobrada/HiDPI, PNG truncado ou
|
|
44
|
+
* corrompido, binario que sai 0 escrevendo outra coisa — a classe de green-fake que o
|
|
45
|
+
* chromium-snap produziu na VPS, achado 1 do provisionamento). Isso continua valendo a pena e
|
|
46
|
+
* continua sendo igualdade binaria sem limiar; o que NAO se pode afirmar e que "HTML com altura
|
|
47
|
+
* errada bloqueia aqui". Quem pega defeito de encaixe no canvas e o olho humano no checkpoint,
|
|
48
|
+
* e (divida 1) o `render-gate.cjs`.
|
|
49
|
+
*
|
|
50
|
+
* Uso (o motor passa o artefato da etapa como argv[2] — invariante runDir/artifacts/<step>.md):
|
|
51
|
+
* node scripts/veto-checks/render-png.cjs <artefato.md> \
|
|
52
|
+
* --html-dir <rel> --out-dir <rel> --width N --height N --expect N
|
|
53
|
+
*
|
|
54
|
+
* Exit 0 = `--expect` PNGs gerados, todos na dimensao exata.
|
|
55
|
+
* Exit 1 = VETO DE CONTEUDO: contagem de HTML != --expect, ou dimensao medida != pedida.
|
|
56
|
+
* Exit 2 = wiring/uso invalido: artefato inexistente, argumento ausente/invalido, dir
|
|
57
|
+
* resolvendo FORA do runDir, dir de HTML inexistente.
|
|
58
|
+
* Exit 3 = FAIL-LOUD de dependencia: Chrome ausente/invalido, ou falha ao renderizar/escrever.
|
|
59
|
+
* Qualquer exit != 0 vira `no()` no `evalVeto` => etapa BLOQUEADA => o checkpoint nao acontece.
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
const fs = require('node:fs');
|
|
63
|
+
const path = require('node:path');
|
|
64
|
+
const crypto = require('node:crypto');
|
|
65
|
+
const { execFileSync } = require('node:child_process');
|
|
66
|
+
|
|
67
|
+
// Reuso, nao extensao (Dev Notes da SF11.11): `render-gate.cjs` recebe UM html e mede overflow;
|
|
68
|
+
// este recebe o artefato da etapa e produz o entregavel. O que e comum e a SONDA de Chrome, e
|
|
69
|
+
// ela existe uma unica vez — duplicar seria arriscar duas doutrinas de fail-loud divergindo.
|
|
70
|
+
const { probeChrome } = require('./render-gate.cjs');
|
|
71
|
+
|
|
72
|
+
const BLOCK_BEGIN = '<!-- pc:entregavel-png:inicio -->';
|
|
73
|
+
const BLOCK_END = '<!-- pc:entregavel-png:fim -->';
|
|
74
|
+
|
|
75
|
+
const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
76
|
+
|
|
77
|
+
/** Erro de USO/WIRING (exit 2). Separado do fail-loud de dependencia (exit 3) por classe. */
|
|
78
|
+
class UsageError extends Error {}
|
|
79
|
+
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
// Funcoes puras (testaveis sem Chrome e sem disco).
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Le a dimensao do IHDR de um buffer PNG. NAO usa heuristica: exige a assinatura PNG de 8 bytes
|
|
86
|
+
* e le os dois uint32 big-endian do IHDR. Buffer que nao e PNG => { ok:false } (nunca um palpite).
|
|
87
|
+
*/
|
|
88
|
+
function readPngDimensions(buf) {
|
|
89
|
+
if (!Buffer.isBuffer(buf) || buf.length < 24) {
|
|
90
|
+
return { ok: false, reason: 'arquivo com menos de 24 bytes — nao ha IHDR para ler' };
|
|
91
|
+
}
|
|
92
|
+
if (!buf.subarray(0, 8).equals(PNG_SIGNATURE)) {
|
|
93
|
+
return { ok: false, reason: 'assinatura PNG ausente (os 8 primeiros bytes nao sao 89 50 4E 47 0D 0A 1A 0A)' };
|
|
94
|
+
}
|
|
95
|
+
if (buf.subarray(12, 16).toString('latin1') !== 'IHDR') {
|
|
96
|
+
return { ok: false, reason: 'primeiro chunk nao e IHDR — PNG malformado' };
|
|
97
|
+
}
|
|
98
|
+
return { ok: true, width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Veredito de dimensao: IGUALDADE DE INTEIROS, os dois lados. Nenhuma tolerancia, nenhum
|
|
103
|
+
* `Math.abs`, nenhum `>=`/`<=` — 1 pixel de diferenca BLOQUEIA (ADR-018 `DF4-5`).
|
|
104
|
+
*/
|
|
105
|
+
function dimensionMatches(measured, width, height) {
|
|
106
|
+
return measured.width === width && measured.height === height;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Veredito de cardinalidade: igualdade EXATA contra o conjunto declarado no `wf`. Nao e limiar
|
|
111
|
+
* (`--min`): pacote incompleto nao e pacote, e pacote com peca a mais tambem nao.
|
|
112
|
+
*/
|
|
113
|
+
function countMatches(found, expect) {
|
|
114
|
+
return found === expect;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Parse dos args LITERAIS. Argumento ausente/invalido => UsageError (exit 2), nunca default. */
|
|
118
|
+
function parseArgs(argv) {
|
|
119
|
+
const o = { htmlDir: null, outDir: null, width: null, height: null, expect: null };
|
|
120
|
+
const int = (raw, flag) => {
|
|
121
|
+
const n = Number.parseInt(raw, 10);
|
|
122
|
+
if (!Number.isInteger(n) || n <= 0 || String(n) !== String(raw)) {
|
|
123
|
+
throw new UsageError(`${flag} exige um inteiro positivo (recebido: ${JSON.stringify(raw)})`);
|
|
124
|
+
}
|
|
125
|
+
return n;
|
|
126
|
+
};
|
|
127
|
+
for (let i = 0; i < argv.length; i++) {
|
|
128
|
+
switch (argv[i]) {
|
|
129
|
+
case '--html-dir': o.htmlDir = argv[++i]; break;
|
|
130
|
+
case '--out-dir': o.outDir = argv[++i]; break;
|
|
131
|
+
case '--width': o.width = int(argv[++i], '--width'); break;
|
|
132
|
+
case '--height': o.height = int(argv[++i], '--height'); break;
|
|
133
|
+
case '--expect': o.expect = int(argv[++i], '--expect'); break;
|
|
134
|
+
default:
|
|
135
|
+
throw new UsageError(`argumento desconhecido: ${JSON.stringify(argv[i])}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
for (const [k, flag] of [['htmlDir', '--html-dir'], ['outDir', '--out-dir'], ['width', '--width'], ['height', '--height'], ['expect', '--expect']]) {
|
|
139
|
+
if (o[k] === null || o[k] === undefined) throw new UsageError(`argumento obrigatorio ausente: ${flag}`);
|
|
140
|
+
}
|
|
141
|
+
return o;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Resolve um dir de arg LITERAL relativo ao dir do artefato, exigindo contencao no runDir.
|
|
146
|
+
* Mesmo molde de `phash-gate.cjs:474-482` — um `wf` nao pode fazer este script ler/escrever
|
|
147
|
+
* fora do run que o invocou.
|
|
148
|
+
*/
|
|
149
|
+
function resolveWithin(runDir, artDir, rel, label) {
|
|
150
|
+
const abs = path.resolve(artDir, rel);
|
|
151
|
+
const root = path.resolve(runDir);
|
|
152
|
+
if (abs !== root && !abs.startsWith(root + path.sep)) {
|
|
153
|
+
throw new UsageError(`${label} "${rel}" resolve FORA do runDir (${abs}) — recusado`);
|
|
154
|
+
}
|
|
155
|
+
return abs;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Lista os `.html` do dir (nao-recursivo), em ordem estavel por nome. */
|
|
159
|
+
function listHtml(dir) {
|
|
160
|
+
return fs.readdirSync(dir, { withFileTypes: true })
|
|
161
|
+
.filter((e) => e.isFile() && /\.html?$/i.test(e.name))
|
|
162
|
+
.map((e) => e.name)
|
|
163
|
+
.sort((a, b) => a.localeCompare(b, 'en'));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Bloco delimitado, humano-legivel e grep-avel, gravado no artefato da etapa. */
|
|
167
|
+
function renderBlock({ rows, width, height, outRel, chromeLabel, failures }) {
|
|
168
|
+
// ATENCAO: o bloco e escrito DENTRO do artefato da etapa, que continua sendo lido por outros
|
|
169
|
+
// vetos na mesma verificacao e nas retentativas. Nada aqui pode introduzir um caractere que
|
|
170
|
+
// outro veto do mesmo `wf` bane (ex.: travessao em/en-dash sob `lexical-check`), nem um
|
|
171
|
+
// heading que colida com a conformidade de template. Ha teste para isso.
|
|
172
|
+
const L = [BLOCK_BEGIN, '', '## Entregavel de publicacao (PNG): verificado por maquina', ''];
|
|
173
|
+
L.push(`Gerado e medido por \`render-png.cjs\` ANTES do checkpoint desta etapa (ADR-025). Dimensao exigida: ${width}x${height};`);
|
|
174
|
+
L.push(`destino: \`${outRel}\`; renderizador: ${chromeLabel}.`);
|
|
175
|
+
L.push('');
|
|
176
|
+
L.push('| Peca (PNG) | Dimensao medida (IHDR) | sha256 | Veredito |');
|
|
177
|
+
L.push('|---|---|---|---|');
|
|
178
|
+
for (const r of rows) {
|
|
179
|
+
const dim = r.measured.ok ? `${r.measured.width}x${r.measured.height}` : `ILEGIVEL (${r.measured.reason})`;
|
|
180
|
+
L.push(`| ${r.png} | ${dim} | ${r.sha256 || '-'} | ${r.verdict} |`);
|
|
181
|
+
}
|
|
182
|
+
L.push('');
|
|
183
|
+
if (failures.length > 0) {
|
|
184
|
+
L.push('### BLOQUEIO');
|
|
185
|
+
L.push('');
|
|
186
|
+
for (const f of failures) L.push(`- ${f}`);
|
|
187
|
+
L.push('');
|
|
188
|
+
}
|
|
189
|
+
L.push('### O que este bloco garante, e o que NAO garante (declarado, nao omitido)');
|
|
190
|
+
L.push('');
|
|
191
|
+
L.push(`- GARANTE: existem exatamente as pecas listadas, o Chrome renderizou cada uma, e a dimensao de cada PNG foi lida do IHDR`);
|
|
192
|
+
L.push(' do proprio arquivo e comparada por IGUALDADE de inteiros com o formato declarado no `wf`. Sem tolerancia, sem limiar.');
|
|
193
|
+
L.push('- NAO GARANTE: que a peca esteja bonita, que o texto tenha cabido, que o contraste preste ou que o encaixe copy x canvas');
|
|
194
|
+
L.push(' esteja bom. Isso e JULGAMENTO HUMANO no checkpoint (fronteira ADR-002). Overflow de layout tem script proprio');
|
|
195
|
+
L.push(' (`render-gate.cjs`) e NAO roda aqui.');
|
|
196
|
+
L.push('', BLOCK_END);
|
|
197
|
+
return L.join('\n');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Insere/SUBSTITUI o bloco no texto do artefato (idempotente entre retentativas da etapa). */
|
|
201
|
+
function upsertBlock(text, block) {
|
|
202
|
+
const b = text.indexOf(BLOCK_BEGIN);
|
|
203
|
+
const e = text.indexOf(BLOCK_END);
|
|
204
|
+
if (b >= 0 && e > b) return `${text.slice(0, b)}${block}${text.slice(e + BLOCK_END.length)}`;
|
|
205
|
+
return `${text.replace(/\s*$/, '')}\n\n${block}\n`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Nome do PNG derivado do HTML (mesmo stem, extensao trocada). */
|
|
209
|
+
function pngNameFor(htmlName) {
|
|
210
|
+
return htmlName.replace(/\.html?$/i, '.png');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Render (unico ponto que toca processo externo).
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Renderiza UM html em PNG. Argumentos identicos aos medidos no `*assess` da SF11.11, cujo
|
|
219
|
+
* resultado foi byte-a-byte identico ao render manual de referencia (sha256 bf4e809d...9d01).
|
|
220
|
+
*/
|
|
221
|
+
function renderOne(chromeBin, htmlAbs, pngAbs, width, height, timeoutMs) {
|
|
222
|
+
execFileSync(chromeBin, [
|
|
223
|
+
'--headless=new', '--disable-gpu', '--no-sandbox', '--hide-scrollbars',
|
|
224
|
+
'--force-device-scale-factor=1', `--window-size=${width},${height}`,
|
|
225
|
+
'--virtual-time-budget=4000', `--screenshot=${pngAbs}`, `file://${htmlAbs}`,
|
|
226
|
+
], { encoding: 'utf8', timeout: timeoutMs, stdio: ['ignore', 'ignore', 'pipe'] });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const RENDER_TIMEOUT_MS = 60000;
|
|
230
|
+
|
|
231
|
+
function main() {
|
|
232
|
+
const artifact = process.argv[2];
|
|
233
|
+
if (!artifact || artifact.startsWith('--') || !fs.existsSync(artifact)) {
|
|
234
|
+
console.error(`render-png: artefato nao encontrado: ${artifact} (uso: render-png.cjs <artefato.md> ` +
|
|
235
|
+
'--html-dir <rel> --out-dir <rel> --width N --height N --expect N)');
|
|
236
|
+
process.exit(2);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let opts; let htmlAbsDir; let outAbsDir; let runDir;
|
|
240
|
+
try {
|
|
241
|
+
opts = parseArgs(process.argv.slice(3));
|
|
242
|
+
const artDir = path.dirname(path.resolve(artifact));
|
|
243
|
+
runDir = path.dirname(artDir); // invariante runDir/artifacts/<step>.md
|
|
244
|
+
htmlAbsDir = resolveWithin(runDir, artDir, opts.htmlDir, '--html-dir');
|
|
245
|
+
outAbsDir = resolveWithin(runDir, artDir, opts.outDir, '--out-dir');
|
|
246
|
+
if (!fs.existsSync(htmlAbsDir) || !fs.statSync(htmlAbsDir).isDirectory()) {
|
|
247
|
+
throw new UsageError(`--html-dir "${opts.htmlDir}" nao existe ou nao e diretorio (${htmlAbsDir})`);
|
|
248
|
+
}
|
|
249
|
+
} catch (e) {
|
|
250
|
+
if (e instanceof UsageError) {
|
|
251
|
+
console.error(`render-png: ${e.message}`);
|
|
252
|
+
process.exit(2);
|
|
253
|
+
}
|
|
254
|
+
throw e;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const { width, height, expect } = opts;
|
|
258
|
+
|
|
259
|
+
// ── (1) CARDINALIDADE — igualdade exata, antes de gastar Chrome.
|
|
260
|
+
let htmlFiles;
|
|
261
|
+
try {
|
|
262
|
+
htmlFiles = listHtml(htmlAbsDir);
|
|
263
|
+
} catch (e) {
|
|
264
|
+
console.error(`render-png: falha ao listar --html-dir (${htmlAbsDir}): ${e.message}`);
|
|
265
|
+
process.exit(2);
|
|
266
|
+
}
|
|
267
|
+
if (!countMatches(htmlFiles.length, expect)) {
|
|
268
|
+
console.error(`render-png BLOQUEOU: esperado EXATAMENTE ${expect} arquivo(s) .html em ${path.relative(runDir, htmlAbsDir) || '.'}, ` +
|
|
269
|
+
`encontrado(s) ${htmlFiles.length}${htmlFiles.length ? ` [${htmlFiles.join(', ')}]` : ''}. ` +
|
|
270
|
+
'Pacote incompleto (ou com peca a mais) nao e pacote — nada foi renderizado.');
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── (2) CHROME — fail-loud, sem fallback e sem escape hatch.
|
|
275
|
+
const chromeBin = probeChrome();
|
|
276
|
+
if (!chromeBin) {
|
|
277
|
+
console.error('render-png BLOQUEOU (fail-loud): headless Chrome nao encontrado. Instale google-chrome/chromium ' +
|
|
278
|
+
'ou defina CHROME_BIN apontando para um binario valido. NAO existe modo degradado: sem render nao ha entregavel ' +
|
|
279
|
+
'de publicacao, e entregar HTML no lugar seria aprovar um proxy do que se publica (ADR-025 DF-SF11.11-4).');
|
|
280
|
+
process.exit(3);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ── (3) RENDER + MEDIDA. Falha de render = exit 3 (dependencia/ferramenta); dimensao errada
|
|
284
|
+
// = exit 1 (veto de conteudo). As classes nao se misturam: a causa fica legivel no run.
|
|
285
|
+
try {
|
|
286
|
+
fs.mkdirSync(outAbsDir, { recursive: true });
|
|
287
|
+
} catch (e) {
|
|
288
|
+
console.error(`render-png BLOQUEOU (fail-loud): nao foi possivel criar --out-dir (${outAbsDir}): ${e.message}`);
|
|
289
|
+
process.exit(3);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const rows = [];
|
|
293
|
+
const dimFailures = [];
|
|
294
|
+
for (const htmlName of htmlFiles) {
|
|
295
|
+
const htmlAbs = path.join(htmlAbsDir, htmlName);
|
|
296
|
+
const pngName = pngNameFor(htmlName);
|
|
297
|
+
const pngAbs = path.join(outAbsDir, pngName);
|
|
298
|
+
try {
|
|
299
|
+
fs.rmSync(pngAbs, { force: true }); // nunca medir o PNG da tentativa anterior
|
|
300
|
+
renderOne(chromeBin, htmlAbs, pngAbs, width, height, RENDER_TIMEOUT_MS);
|
|
301
|
+
} catch (e) {
|
|
302
|
+
const err = `${(e.stderr || '').toString().trim().split('\n').pop() || e.message}`;
|
|
303
|
+
console.error(`render-png BLOQUEOU (fail-loud): falha ao renderizar "${htmlName}" em ${chromeBin}: ${err}`);
|
|
304
|
+
process.exit(3);
|
|
305
|
+
}
|
|
306
|
+
if (!fs.existsSync(pngAbs)) {
|
|
307
|
+
console.error(`render-png BLOQUEOU (fail-loud): o Chrome saiu sem erro mas NAO escreveu o PNG de "${htmlName}" ` +
|
|
308
|
+
`(esperado em ${pngAbs}). Sem arquivo nao ha entregavel — bloqueando em vez de seguir.`);
|
|
309
|
+
process.exit(3);
|
|
310
|
+
}
|
|
311
|
+
const buf = fs.readFileSync(pngAbs);
|
|
312
|
+
const measured = readPngDimensions(buf);
|
|
313
|
+
if (!measured.ok) {
|
|
314
|
+
console.error(`render-png BLOQUEOU (fail-loud): "${pngName}" nao e um PNG legivel: ${measured.reason}.`);
|
|
315
|
+
process.exit(3);
|
|
316
|
+
}
|
|
317
|
+
const sha256 = crypto.createHash('sha256').update(buf).digest('hex');
|
|
318
|
+
const okDim = dimensionMatches(measured, width, height);
|
|
319
|
+
if (!okDim) {
|
|
320
|
+
dimFailures.push(`${pngName}: medido ${measured.width}x${measured.height}, exigido ${width}x${height} (origem: ${htmlName})`);
|
|
321
|
+
}
|
|
322
|
+
rows.push({ html: htmlName, png: pngName, measured, sha256, verdict: okDim ? 'OK' : 'DIMENSAO ERRADA' });
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// ── (4) RASTRO no artefato (upsert idempotente), gravado tanto no verde quanto no bloqueio
|
|
326
|
+
// por dimensao: quem for consertar precisa ver o numero medido, nao so a mensagem do run.
|
|
327
|
+
const outRel = path.relative(runDir, outAbsDir) || '.';
|
|
328
|
+
const block = renderBlock({
|
|
329
|
+
rows, width, height, outRel,
|
|
330
|
+
chromeLabel: chromeBin.split('/').pop(),
|
|
331
|
+
failures: dimFailures,
|
|
332
|
+
});
|
|
333
|
+
try {
|
|
334
|
+
const raw = fs.readFileSync(path.resolve(artifact), 'utf8');
|
|
335
|
+
fs.writeFileSync(path.resolve(artifact), upsertBlock(raw, block), 'utf8');
|
|
336
|
+
} catch (e) {
|
|
337
|
+
console.error(`render-png: AVISO — gravacao do bloco no artefato falhou: ${e.message} ` +
|
|
338
|
+
'(o veredito abaixo NAO depende desta escrita)');
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (dimFailures.length > 0) {
|
|
342
|
+
console.error(`render-png BLOQUEOU: ${dimFailures.length} peca(s) fora de ${width}x${height} — ${dimFailures.join(' | ')}. ` +
|
|
343
|
+
'Dimensao e igualdade de inteiros lida do IHDR do PNG gerado: nao ha tolerancia a ajustar.');
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
console.log(`render-png OK: ${rows.length} PNG(s) gerados em ${outRel}, todos ${width}x${height} por IHDR ` +
|
|
348
|
+
`(${rows.map((r) => `${r.png}=${r.sha256.slice(0, 12)}`).join(', ')}). Renderizador: ${chromeBin.split('/').pop()}.`);
|
|
349
|
+
process.exit(0);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (require.main === module) main();
|
|
353
|
+
|
|
354
|
+
module.exports = {
|
|
355
|
+
readPngDimensions, dimensionMatches, countMatches, parseArgs, resolveWithin,
|
|
356
|
+
listHtml, renderBlock, upsertBlock, pngNameFor, UsageError,
|
|
357
|
+
BLOCK_BEGIN, BLOCK_END, PNG_SIGNATURE,
|
|
358
|
+
};
|