@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,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* render-png.test.cjs — suíte do script BLESSED que PRODUZ e VERIFICA o entregável de
|
|
5
|
+
* publicação antes do checkpoint. Story SF11.11 / ADR-025.
|
|
6
|
+
*
|
|
7
|
+
* O QUE ESTA SUÍTE PROVA (cada item é um AC com oráculo, nunca um relato):
|
|
8
|
+
* (AC1) GENERICIDADE: o fonte não contém nome de pod nem de peça — o que é do pod vem por
|
|
9
|
+
* argumento literal do `wf`.
|
|
10
|
+
* (AC3) DIMENSÃO É IGUALDADE, NÃO LIMIAR: `dimensionMatches` só aceita `===` nos dois lados
|
|
11
|
+
* (1 px de diferença bloqueia), e o fonte não tem `Math.abs`/`±`/`>=`/`<=` na comparação
|
|
12
|
+
* de dimensão. Estático + comportamental — um sem o outro é teatro.
|
|
13
|
+
* (AC4) CARDINALIDADE EXATA: `--expect N` bloqueia com N-1 e com N+1 (os dois lados).
|
|
14
|
+
* (AC5) AS TRÊS CLASSES DE FALHA, EXERCITADAS PELO PROCESSO REAL (execFileSync do script,
|
|
15
|
+
* não da função): (a) CHROME_BIN inválido ⇒ exit 3 sem fallback; (b) HTML que não
|
|
16
|
+
* rende / PNG não escrito ⇒ exit 3; (c) dir escapando do runDir ⇒ exit 2. E: NENHUM
|
|
17
|
+
* caminho faz o script sair 0 sem ter renderizado e medido.
|
|
18
|
+
* (AC9) BLOCO NO ARTEFATO: upsert idempotente (re-verificação não duplica).
|
|
19
|
+
*
|
|
20
|
+
* As provas que EXIGEM Chrome real registram SKIP HONESTO onde ele não existe (e o CI não tem
|
|
21
|
+
* Chrome). A aritmética de IHDR, a cardinalidade e as classes 2/3 rodam sempre.
|
|
22
|
+
*
|
|
23
|
+
* Node >= 18. Exit 0/1.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const fs = require('node:fs');
|
|
27
|
+
const os = require('node:os');
|
|
28
|
+
const path = require('node:path');
|
|
29
|
+
const { spawnSync } = require('node:child_process');
|
|
30
|
+
|
|
31
|
+
const rp = require('./veto-checks/render-png.cjs');
|
|
32
|
+
const { probeChrome } = require('./veto-checks/render-gate.cjs');
|
|
33
|
+
|
|
34
|
+
const SCRIPT = path.join(__dirname, 'veto-checks', 'render-png.cjs');
|
|
35
|
+
const SRC = fs.readFileSync(SCRIPT, 'utf8');
|
|
36
|
+
|
|
37
|
+
let passed = 0;
|
|
38
|
+
const failures = [];
|
|
39
|
+
function ok(cond, label) {
|
|
40
|
+
if (cond) { passed++; console.log(`[OK ] ${label}`); } else { failures.push(label); console.error(`[FALHA] ${label}`); }
|
|
41
|
+
}
|
|
42
|
+
function skip(label) { console.log(`[SKIP ] ${label}`); }
|
|
43
|
+
|
|
44
|
+
/** Monta um runDir sintético: <runDir>/artifacts/<step>.md + os HTMLs pedidos. */
|
|
45
|
+
function makeRun(htmls, artifactText) {
|
|
46
|
+
const runDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-render-png-'));
|
|
47
|
+
fs.mkdirSync(path.join(runDir, 'artifacts'), { recursive: true });
|
|
48
|
+
const artifact = path.join(runDir, 'artifacts', 'assembly.md');
|
|
49
|
+
fs.writeFileSync(artifact, artifactText || '## Pacote\nconteudo da etapa\n', 'utf8');
|
|
50
|
+
for (const [name, body] of Object.entries(htmls)) fs.writeFileSync(path.join(runDir, name), body, 'utf8');
|
|
51
|
+
return { runDir, artifact };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Roda o SCRIPT como processo (o mesmo caminho que o motor usa). Devolve {status, stderr}. */
|
|
55
|
+
function run(artifact, args, env) {
|
|
56
|
+
const r = spawnSync(process.execPath, [SCRIPT, artifact, ...args], {
|
|
57
|
+
encoding: 'utf8', timeout: 240000, env: { ...process.env, ...(env || {}) },
|
|
58
|
+
});
|
|
59
|
+
return { status: r.status, stderr: (r.stderr || '').trim(), stdout: (r.stdout || '').trim() };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const pinned = (w, h) => `<!doctype html><html><head><meta charset="utf-8"><style>html,body{width:${w}px;height:${h}px;margin:0;background:#101820}</style></head><body></body></html>`;
|
|
63
|
+
|
|
64
|
+
// ===========================================================================
|
|
65
|
+
// (AC1) Genericidade — nenhum nome de pod/peça no fonte.
|
|
66
|
+
// ===========================================================================
|
|
67
|
+
console.log('--- (AC1) genericidade do script blessed ---');
|
|
68
|
+
for (const termo of ['hn-carousel', 'capa', 'slide', 'cta', 'carousel', 'instagram']) {
|
|
69
|
+
const hits = (SRC.match(new RegExp(termo, 'gi')) || []).length;
|
|
70
|
+
ok(hits === 0, `fonte não cita "${termo}" (ocorrências: ${hits})`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ===========================================================================
|
|
74
|
+
// (AC3) Dimensão: igualdade de inteiros lida do IHDR — estático + comportamental.
|
|
75
|
+
// ===========================================================================
|
|
76
|
+
console.log('\n--- (AC3) dimensão é igualdade, não limiar ---');
|
|
77
|
+
// PNG mínimo real (1x1) para provar que a leitura é dos BYTES, não de metadado externo.
|
|
78
|
+
const PNG_1x1 = Buffer.from(
|
|
79
|
+
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
|
|
80
|
+
'base64',
|
|
81
|
+
);
|
|
82
|
+
const d1 = rp.readPngDimensions(PNG_1x1);
|
|
83
|
+
ok(d1.ok && d1.width === 1 && d1.height === 1, `readPngDimensions lê o IHDR real (${JSON.stringify(d1)})`);
|
|
84
|
+
ok(rp.readPngDimensions(Buffer.from('nao sou png, nem de longe, tenho mais de 24 bytes')).ok === false,
|
|
85
|
+
'buffer sem assinatura PNG NÃO vira palpite de dimensão (ok:false)');
|
|
86
|
+
ok(rp.readPngDimensions(Buffer.alloc(10)).ok === false, 'buffer curto demais para IHDR é recusado');
|
|
87
|
+
|
|
88
|
+
// Um pixel de diferença BLOQUEIA — os dois eixos, os dois sentidos.
|
|
89
|
+
ok(rp.dimensionMatches({ width: 1080, height: 1350 }, 1080, 1350) === true, 'dimensão exata PASSA');
|
|
90
|
+
ok(rp.dimensionMatches({ width: 1080, height: 1349 }, 1080, 1350) === false, '1349 de altura BLOQUEIA (1 px a menos)');
|
|
91
|
+
ok(rp.dimensionMatches({ width: 1080, height: 1351 }, 1080, 1350) === false, '1351 de altura BLOQUEIA (1 px a mais)');
|
|
92
|
+
ok(rp.dimensionMatches({ width: 1079, height: 1350 }, 1080, 1350) === false, '1079 de largura BLOQUEIA');
|
|
93
|
+
|
|
94
|
+
// Oráculo ESTÁTICO da ausência de limiar: a função de veredito de dimensão só usa `===`.
|
|
95
|
+
const fnSrc = SRC.slice(SRC.indexOf('function dimensionMatches'), SRC.indexOf('function countMatches'));
|
|
96
|
+
ok(!/Math\.abs|tolerance|toleranc|>=|<=|±|\bdelta\b/i.test(fnSrc),
|
|
97
|
+
`dimensionMatches não tem comparação frouxa (trecho: ${fnSrc.replace(/\s+/g, ' ').trim().slice(0, 120)})`);
|
|
98
|
+
ok((fnSrc.match(/===/g) || []).length === 2, 'dimensionMatches compara os DOIS eixos com === (2 ocorrências)');
|
|
99
|
+
|
|
100
|
+
// ===========================================================================
|
|
101
|
+
// (AC4) Cardinalidade exata — os dois lados.
|
|
102
|
+
// ===========================================================================
|
|
103
|
+
console.log('\n--- (AC4) contagem exata (--expect) ---');
|
|
104
|
+
ok(rp.countMatches(7, 7) === true, 'countMatches: 7 de 7 PASSA');
|
|
105
|
+
ok(rp.countMatches(6, 7) === false, 'countMatches: 6 de 7 BLOQUEIA');
|
|
106
|
+
ok(rp.countMatches(8, 7) === false, 'countMatches: 8 de 7 BLOQUEIA');
|
|
107
|
+
|
|
108
|
+
{
|
|
109
|
+
const six = Object.fromEntries(Array.from({ length: 6 }, (_, i) => [`0${i + 1}-p.html`, pinned(1080, 1350)]));
|
|
110
|
+
const { runDir, artifact } = makeRun(six);
|
|
111
|
+
const r = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '7']);
|
|
112
|
+
ok(r.status === 1 && /encontrado\(s\) 6/.test(r.stderr), `6 HTMLs com --expect 7 ⇒ exit 1 nomeando a contagem (exit=${r.status}) — ${r.stderr.slice(0, 110)}`);
|
|
113
|
+
ok(!fs.existsSync(path.join(runDir, 'png')), 'contagem errada bloqueia ANTES de gastar render (png/ nem é criado)');
|
|
114
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
115
|
+
}
|
|
116
|
+
{
|
|
117
|
+
const eight = Object.fromEntries(Array.from({ length: 8 }, (_, i) => [`0${i + 1}-p.html`, pinned(1080, 1350)]));
|
|
118
|
+
const { runDir, artifact } = makeRun(eight);
|
|
119
|
+
const r = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '7']);
|
|
120
|
+
ok(r.status === 1 && /encontrado\(s\) 8/.test(r.stderr), `8 HTMLs com --expect 7 ⇒ exit 1 nomeando a contagem (exit=${r.status})`);
|
|
121
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ===========================================================================
|
|
125
|
+
// (AC5c) Classe WIRING = exit 2 — uso inválido, dir fora do runDir, artefato ausente.
|
|
126
|
+
// ===========================================================================
|
|
127
|
+
console.log('\n--- (AC5c) classe wiring/uso ⇒ exit 2 ---');
|
|
128
|
+
{
|
|
129
|
+
const { runDir, artifact } = makeRun({ '01-p.html': pinned(1080, 1350) });
|
|
130
|
+
const escapes = run(artifact, ['--html-dir', '../../..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '1']);
|
|
131
|
+
ok(escapes.status === 2 && /FORA do runDir/.test(escapes.stderr), `--html-dir fora do runDir ⇒ exit 2 (exit=${escapes.status})`);
|
|
132
|
+
const escapesOut = run(artifact, ['--html-dir', '..', '--out-dir', '../../fuga', '--width', '1080', '--height', '1350', '--expect', '1']);
|
|
133
|
+
ok(escapesOut.status === 2 && /FORA do runDir/.test(escapesOut.stderr), `--out-dir fora do runDir ⇒ exit 2 (exit=${escapesOut.status})`);
|
|
134
|
+
const semArg = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350']);
|
|
135
|
+
ok(semArg.status === 2 && /--expect/.test(semArg.stderr), `--expect ausente ⇒ exit 2 (sem default silencioso) (exit=${semArg.status})`);
|
|
136
|
+
const argRuim = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '108o', '--height', '1350', '--expect', '1']);
|
|
137
|
+
ok(argRuim.status === 2 && /--width/.test(argRuim.stderr), `--width não-inteiro ⇒ exit 2 (exit=${argRuim.status})`);
|
|
138
|
+
const dirInexistente = run(artifact, ['--html-dir', 'nao-existe', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '1']);
|
|
139
|
+
ok(dirInexistente.status === 2 && /nao existe/.test(dirInexistente.stderr), `--html-dir inexistente ⇒ exit 2 (exit=${dirInexistente.status})`);
|
|
140
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
{
|
|
143
|
+
const r = run(path.join(os.tmpdir(), 'nao-ha-artefato-aqui-pc.md'), ['--html-dir', '..', '--out-dir', '../png', '--width', '1', '--height', '1', '--expect', '1']);
|
|
144
|
+
ok(r.status === 2 && /artefato nao encontrado/.test(r.stderr), `artefato inexistente ⇒ exit 2 (exit=${r.status})`);
|
|
145
|
+
}
|
|
146
|
+
ok(typeof rp.resolveWithin === 'function' && (() => {
|
|
147
|
+
try { rp.resolveWithin('/tmp/run', '/tmp/run/artifacts', '../../..', '--html-dir'); return false; } catch (e) { return e instanceof rp.UsageError; }
|
|
148
|
+
})(), 'resolveWithin lança UsageError (classe 2) quando o arg escapa do runDir');
|
|
149
|
+
|
|
150
|
+
// ===========================================================================
|
|
151
|
+
// (AC5a) Classe DEPENDÊNCIA = exit 3 — Chrome ausente/inválido, SEM fallback.
|
|
152
|
+
// ===========================================================================
|
|
153
|
+
console.log('\n--- (AC5a) Chrome inválido ⇒ exit 3, fail-loud sem fallback ---');
|
|
154
|
+
{
|
|
155
|
+
const { runDir, artifact } = makeRun({ '01-p.html': pinned(1080, 1350) });
|
|
156
|
+
const r = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '1'],
|
|
157
|
+
{ CHROME_BIN: '/nao/existe/chrome-inexistente' });
|
|
158
|
+
ok(r.status === 3 && /Chrome nao encontrado/.test(r.stderr),
|
|
159
|
+
`CHROME_BIN inválido ⇒ exit 3 com mensagem acionável (exit=${r.status}) — ${r.stderr.slice(0, 120)}`);
|
|
160
|
+
ok(!fs.existsSync(path.join(runDir, 'png', '01-p.png')),
|
|
161
|
+
'CHROME_BIN inválido NÃO cai em outro navegador (nenhum PNG produzido) — sem fallback silencioso');
|
|
162
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ===========================================================================
|
|
166
|
+
// (AC5b) Classe DEPENDÊNCIA = exit 3 — render que falha e render que não escreve o PNG.
|
|
167
|
+
// Exercitada DETERMINISTICAMENTE, sem depender de Chrome: `CHROME_BIN` aponta para um
|
|
168
|
+
// binário que existe (logo passa a sonda) e (i) sai != 0, ou (ii) sai 0 SEM escrever nada.
|
|
169
|
+
// O caso (ii) é o mais perigoso dos dois — é exatamente o green-fake que a SF11.4b ensinou
|
|
170
|
+
// a temer (o Chrome-snap na VPS saía 0 sem renderizar). Ele TEM que bloquear.
|
|
171
|
+
// ===========================================================================
|
|
172
|
+
console.log('\n--- (AC5b) render falho / PNG não escrito ⇒ exit 3 ---');
|
|
173
|
+
if (!fs.existsSync('/usr/bin/false') || !fs.existsSync('/usr/bin/true')) {
|
|
174
|
+
skip('/usr/bin/{true,false} ausentes nesta máquina: provas determinísticas da classe 3 não rodaram');
|
|
175
|
+
} else {
|
|
176
|
+
const a = makeRun({ '01-p.html': pinned(1080, 1350) });
|
|
177
|
+
const rFail = run(a.artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '1'],
|
|
178
|
+
{ CHROME_BIN: '/usr/bin/false' });
|
|
179
|
+
ok(rFail.status === 3 && /falha ao renderizar "01-p\.html"/.test(rFail.stderr),
|
|
180
|
+
`renderizador que sai != 0 ⇒ exit 3 nomeando o arquivo (exit=${rFail.status}) — ${rFail.stderr.slice(0, 130)}`);
|
|
181
|
+
fs.rmSync(a.runDir, { recursive: true, force: true });
|
|
182
|
+
|
|
183
|
+
const b = makeRun({ '01-p.html': pinned(1080, 1350) });
|
|
184
|
+
const rMudo = run(b.artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '1'],
|
|
185
|
+
{ CHROME_BIN: '/usr/bin/true' });
|
|
186
|
+
ok(rMudo.status === 3 && /NAO escreveu o PNG/.test(rMudo.stderr),
|
|
187
|
+
`renderizador que sai 0 SEM escrever o PNG ⇒ exit 3 (green-fake bloqueado) (exit=${rMudo.status}) — ${rMudo.stderr.slice(0, 130)}`);
|
|
188
|
+
fs.rmSync(b.runDir, { recursive: true, force: true });
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// ===========================================================================
|
|
192
|
+
// (AC5) Ausência de escape hatch — nenhum caminho de código sai 0 sem verificar.
|
|
193
|
+
// ===========================================================================
|
|
194
|
+
console.log('\n--- (AC5) sem escape hatch ---');
|
|
195
|
+
const exits0 = SRC.match(/process\.exit\(0\)/g) || [];
|
|
196
|
+
ok(exits0.length === 1, `existe UM único process.exit(0) no script (encontrados: ${exits0.length})`);
|
|
197
|
+
const tail = SRC.slice(SRC.lastIndexOf('process.exit(0)') - 900, SRC.lastIndexOf('process.exit(0)'));
|
|
198
|
+
ok(/render-png OK/.test(tail), 'o único exit 0 vem DEPOIS do laço de render+medida (linha de sucesso o precede)');
|
|
199
|
+
ok(!/--skip|--bypass|--allow-|--no-verify|SKIP_|BYPASS_|PC_RENDER_/.test(SRC),
|
|
200
|
+
'não há flag nem env var de bypass no fonte (grep por --skip/--bypass/--allow-/--no-verify/SKIP_/BYPASS_)');
|
|
201
|
+
// `skip`/`ignore` aparecem SÓ em contexto justificado: doutrina anti-skip e a opção `stdio` do
|
|
202
|
+
// Node. Este teste falha se aparecerem em qualquer outro lugar.
|
|
203
|
+
const suspeitas = SRC.split('\n').filter((l) => /\b(skip|bypass|ignore)\b/i.test(l))
|
|
204
|
+
.filter((l) => !/NUNCA skip-silencioso|degradacao silenciosa|stdio:/.test(l));
|
|
205
|
+
ok(suspeitas.length === 0, `nenhuma ocorrência não-justificada de skip/bypass/ignore (suspeitas: ${suspeitas.join(' // ').slice(0, 160)})`);
|
|
206
|
+
|
|
207
|
+
// ===========================================================================
|
|
208
|
+
// (AC9) Bloco no artefato: upsert IDEMPOTENTE.
|
|
209
|
+
// ===========================================================================
|
|
210
|
+
console.log('\n--- (AC9) bloco delimitado, upsert idempotente ---');
|
|
211
|
+
{
|
|
212
|
+
const bloco = rp.renderBlock({
|
|
213
|
+
rows: [{ png: '01-p.png', measured: { ok: true, width: 1080, height: 1350 }, sha256: 'abc123', verdict: 'OK' }],
|
|
214
|
+
width: 1080, height: 1350, outRel: 'png', chromeLabel: 'chrome-teste', failures: [],
|
|
215
|
+
});
|
|
216
|
+
const base = '## Pacote\nlinha original\n';
|
|
217
|
+
const um = rp.upsertBlock(base, bloco);
|
|
218
|
+
const dois = rp.upsertBlock(um, bloco);
|
|
219
|
+
const conta = (t, m) => (t.split(m).length - 1);
|
|
220
|
+
ok(conta(um, rp.BLOCK_BEGIN) === 1 && conta(dois, rp.BLOCK_BEGIN) === 1, 'o bloco não duplica na re-verificação (upsert)');
|
|
221
|
+
ok(dois === um, 'segunda escrita do MESMO bloco é byte-idêntica à primeira');
|
|
222
|
+
ok(um.includes('linha original'), 'o conteúdo original do artefato é preservado');
|
|
223
|
+
ok(!/[—–]/.test(bloco), 'o bloco não introduz travessão em/en-dash (não quebra o veto lexical de quem o consome)');
|
|
224
|
+
ok(/1080x1350/.test(bloco) && /abc123/.test(bloco), 'o bloco registra dimensão medida e sha256 por peça');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// ===========================================================================
|
|
228
|
+
// (AC2/AC5b/AC3) Provas com Chrome REAL — caminho feliz, dimensão errada, HTML que não rende.
|
|
229
|
+
// ===========================================================================
|
|
230
|
+
console.log('\n--- (Chrome real) caminho feliz, dimensão errada, render falho ---');
|
|
231
|
+
const chrome = probeChrome();
|
|
232
|
+
if (!chrome) {
|
|
233
|
+
skip('Chrome ausente nesta máquina: provas de render real NÃO rodaram (nada foi presumido verde)');
|
|
234
|
+
} else {
|
|
235
|
+
// Caminho feliz: 2 peças pinadas, dimensão exata ⇒ exit 0 + PNGs em disco + bloco no artefato.
|
|
236
|
+
const { runDir, artifact } = makeRun({ '01-p.html': pinned(1080, 1350), '02-p.html': pinned(1080, 1350) });
|
|
237
|
+
const r = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '2']);
|
|
238
|
+
const pngs = fs.existsSync(path.join(runDir, 'png')) ? fs.readdirSync(path.join(runDir, 'png')).sort() : [];
|
|
239
|
+
ok(r.status === 0, `caminho feliz ⇒ exit 0 (exit=${r.status}) — ${r.stderr.slice(0, 160)}`);
|
|
240
|
+
ok(pngs.join(',') === '01-p.png,02-p.png', `os PNGs existem em disco: [${pngs.join(', ')}]`);
|
|
241
|
+
const dims = pngs.map((f) => rp.readPngDimensions(fs.readFileSync(path.join(runDir, 'png', f))));
|
|
242
|
+
ok(dims.every((d) => d.ok && d.width === 1080 && d.height === 1350),
|
|
243
|
+
`todo PNG mede 1080x1350 por IHDR (${dims.map((d) => `${d.width}x${d.height}`).join(', ')})`);
|
|
244
|
+
const art = fs.readFileSync(artifact, 'utf8');
|
|
245
|
+
ok(art.includes(rp.BLOCK_BEGIN) && art.includes('01-p.png'), 'o artefato ganhou o bloco com as peças');
|
|
246
|
+
// Re-verificação (o motor reexecuta o veto a cada advance): NÃO duplica o bloco.
|
|
247
|
+
const r2 = run(artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '2']);
|
|
248
|
+
const art2 = fs.readFileSync(artifact, 'utf8');
|
|
249
|
+
ok(r2.status === 0 && (art2.split(rp.BLOCK_BEGIN).length - 1) === 1, 'reexecução mantém exit 0 e UM bloco só');
|
|
250
|
+
fs.rmSync(runDir, { recursive: true, force: true });
|
|
251
|
+
|
|
252
|
+
// MEDIÇÃO QUE FALSIFICA A PREMISSA DO ADR-025 (registrada aqui, no código, e não só em prosa):
|
|
253
|
+
// a dimensão do PNG é ditada pelo `--window-size`, NÃO pelo CSS do documento. Este par de
|
|
254
|
+
// renders prova em disco que "plantar um HTML de altura diferente" NÃO produz PNG de altura
|
|
255
|
+
// diferente — logo aquele oráculo é inalcançável, e um teste que fingisse alcançá-lo seria
|
|
256
|
+
// fixture fabricada para passar (a armadilha que a doutrina da casa chama de teatro).
|
|
257
|
+
const alt = makeRun({ '01-p.html': pinned(1080, 2000), '02-p.html': pinned(900, 700) });
|
|
258
|
+
const ra = run(alt.artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '2']);
|
|
259
|
+
const dAlt = ['01-p.png', '02-p.png'].map((f) => rp.readPngDimensions(fs.readFileSync(path.join(alt.runDir, 'png', f))));
|
|
260
|
+
ok(ra.status === 0 && dAlt.every((d) => d.width === 1080 && d.height === 1350),
|
|
261
|
+
`MEDIDO: HTML pinado a 1080x2000 e a 900x700 renderizam ambos ${dAlt.map((d) => `${d.width}x${d.height}`).join(' e ')} ` +
|
|
262
|
+
'— a moldura vem do --window-size, não do CSS (premissa do AC8 do ADR-025 FALSIFICADA)');
|
|
263
|
+
fs.rmSync(alt.runDir, { recursive: true, force: true });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// ===========================================================================
|
|
267
|
+
// (AC3, comportamental) Dimensão errada ⇒ exit 1 NOMEANDO peça e medida.
|
|
268
|
+
//
|
|
269
|
+
// Dado o achado acima, a classe que esta checagem de fato defende é o RENDERIZADOR QUE
|
|
270
|
+
// DESOBEDECE a moldura (escala dobrada/HiDPI, PNG truncado, binário que sai 0 escrevendo outra
|
|
271
|
+
// coisa — a classe green-fake do chromium-snap, achado 1 do provisionamento da VPS). É ESSA
|
|
272
|
+
// classe que o teste abaixo exercita, pelo processo real, com um renderizador de mentira que
|
|
273
|
+
// escreve um PNG válido de tamanho errado. Roda sem Chrome, deterministicamente.
|
|
274
|
+
// ===========================================================================
|
|
275
|
+
console.log('\n--- (AC3 comportamental) renderizador que produz dimensão errada ⇒ exit 1 ---');
|
|
276
|
+
{
|
|
277
|
+
const fake = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-fake-chrome-'));
|
|
278
|
+
const fakeBin = path.join(fake, 'chrome-mentiroso');
|
|
279
|
+
fs.writeFileSync(fakeBin, `#!/bin/sh
|
|
280
|
+
# Renderizador de mentira: escreve um PNG VALIDO, porem 1x1 - o tamanho que ninguem pediu.
|
|
281
|
+
for a in "$@"; do
|
|
282
|
+
case "$a" in
|
|
283
|
+
--screenshot=*) printf '%s' '${PNG_1x1.toString('base64')}' | base64 -d > "\${a#--screenshot=}" ;;
|
|
284
|
+
esac
|
|
285
|
+
done
|
|
286
|
+
exit 0
|
|
287
|
+
`, 'utf8');
|
|
288
|
+
fs.chmodSync(fakeBin, 0o755);
|
|
289
|
+
const bad = makeRun({ '01-p.html': pinned(1080, 1350), '02-p.html': pinned(1080, 1350) });
|
|
290
|
+
const rb = run(bad.artifact, ['--html-dir', '..', '--out-dir', '../png', '--width', '1080', '--height', '1350', '--expect', '2'],
|
|
291
|
+
{ CHROME_BIN: fakeBin });
|
|
292
|
+
ok(rb.status === 1 && /01-p\.png/.test(rb.stderr) && /medido 1x1, exigido 1080x1350/.test(rb.stderr),
|
|
293
|
+
`PNG fora da moldura ⇒ exit 1 nomeando peça e dimensão medida (exit=${rb.status}) — ${rb.stderr.slice(0, 160)}`);
|
|
294
|
+
const artBad = fs.readFileSync(bad.artifact, 'utf8');
|
|
295
|
+
ok(/DIMENSAO ERRADA/.test(artBad) && /1x1/.test(artBad),
|
|
296
|
+
'o bloqueio por dimensão deixa o número medido no artefato (quem for consertar vê o valor)');
|
|
297
|
+
fs.rmSync(bad.runDir, { recursive: true, force: true });
|
|
298
|
+
fs.rmSync(fake, { recursive: true, force: true });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ===========================================================================
|
|
302
|
+
console.log(`\n${passed} prova(s) passaram; ${failures.length} falha(s).`);
|
|
303
|
+
if (failures.length > 0) {
|
|
304
|
+
console.error(`RENDER-PNG SUITE FALHOU: ${failures.join(' | ')}`);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
}
|
|
307
|
+
process.exit(0);
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* smoke-isolation.test.cjs — regressão do fix SF7.7 (AC2): `smoke-pod-workflow.cjs` isola
|
|
5
|
+
* a memória do pod-alvo em TODA execução, não só `--sweep`. Prova que rodar o smoke
|
|
6
|
+
* NÃO-sweep contra um pod real deixa `memory/learnings.jsonl` (e `learnings.consolidated.md`)
|
|
7
|
+
* BYTE-INTOCADO — fecha os 2 incidentes reais (OBS-MOTOR-1 do gate SF7.5; contaminação do
|
|
8
|
+
* pre-push do pc-devops no hn-carousel), cuja causa-raiz era o `cfg` do `main()` sem
|
|
9
|
+
* `memoryPodDir` → o runner escrevia em `memoryPodDir || podRoot` = a memória REAL.
|
|
10
|
+
*
|
|
11
|
+
* Prova negativa (mesma classe de forge.test.cjs): sha256 + tamanho antes == depois.
|
|
12
|
+
* O pod-alvo é uma CÓPIA em tmpdir do fixture canary (memória semeada com conteúdo próprio),
|
|
13
|
+
* para não depender da memória gitignored do canary nem poluir nada versionado.
|
|
14
|
+
*
|
|
15
|
+
* Node >= 18. Exit 0/1.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
const os = require('node:os');
|
|
20
|
+
const path = require('node:path');
|
|
21
|
+
const crypto = require('node:crypto');
|
|
22
|
+
const { spawnSync } = require('node:child_process');
|
|
23
|
+
|
|
24
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
25
|
+
const CANARY = path.join(REPO_ROOT, 'scripts', '__fixtures__', 'smoke-canary');
|
|
26
|
+
const SMOKE = path.join(REPO_ROOT, 'scripts', 'smoke-pod-workflow.cjs');
|
|
27
|
+
|
|
28
|
+
let passed = 0;
|
|
29
|
+
const failures = [];
|
|
30
|
+
function ok(cond, label) {
|
|
31
|
+
if (cond) { passed++; console.log(`[OK ] ${label}`); }
|
|
32
|
+
else { failures.push(label); console.error(`[FALHA] ${label}`); }
|
|
33
|
+
}
|
|
34
|
+
const sha = (p) => crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex');
|
|
35
|
+
|
|
36
|
+
// --- Monta um pod-alvo REAL isolado (cópia do canary) com memória semeada própria. ---
|
|
37
|
+
const podDir = fs.mkdtempSync(path.join(os.tmpdir(), 'sf77-pod-'));
|
|
38
|
+
// SF11.2/DF-SF11.2-4: `agents/` entra na cópia — o runner resolve e VERIFICA
|
|
39
|
+
// `agents/<step.agent>.md` antes de despachar; pod sem eles é `leitura_obrigatoria_ausente`.
|
|
40
|
+
for (const sub of ['workflows', 'templates', 'agents']) {
|
|
41
|
+
fs.cpSync(path.join(CANARY, sub), path.join(podDir, sub), { recursive: true });
|
|
42
|
+
}
|
|
43
|
+
// SF8.3 (fix latente de checkout limpo): `__fixtures__/**/memory/` é gitignorado e,
|
|
44
|
+
// desde a SF7.7 (isolamento incondicional), o smoke NÃO recria esse dir — a cópia
|
|
45
|
+
// dele só funcionava em working dirs com resíduo pré-SF7.7. O baseline é SEMEADO
|
|
46
|
+
// logo abaixo de qualquer forma; basta criar o dir vazio (worktree/CI-safe).
|
|
47
|
+
fs.mkdirSync(path.join(podDir, 'memory'), { recursive: true });
|
|
48
|
+
const jsonl = path.join(podDir, 'memory', 'learnings.jsonl');
|
|
49
|
+
const consolidated = path.join(podDir, 'memory', 'learnings.consolidated.md');
|
|
50
|
+
// Semeia conteúdo conhecido (sobrescreve a cópia, garantindo baseline determinística).
|
|
51
|
+
fs.writeFileSync(jsonl, '{"seed":"baseline-sf7.7","ts":"fixo"}\n', 'utf8');
|
|
52
|
+
fs.writeFileSync(consolidated, '# consolidado semeado SF7.7\n- item fixo\n', 'utf8');
|
|
53
|
+
|
|
54
|
+
const before = { jsonl: sha(jsonl), consolidated: sha(consolidated), size: fs.statSync(jsonl).size };
|
|
55
|
+
|
|
56
|
+
// --- SNAPSHOT de higiene, ANTES de qualquer spawnSync (SF13.9). ---
|
|
57
|
+
// A asserção de resíduo é por DELTA: só reprova `pc-smoke-mem-*` que ESTA
|
|
58
|
+
// execução criou. Varrer o `os.tmpdir()` inteiro (o que estava aqui até a
|
|
59
|
+
// SF13.9) derrubava a suíte por resíduo de OUTRA execução que não chegou a
|
|
60
|
+
// limpar — leitura SUPERADA, não repetir: custou 4 rodadas de investigação num
|
|
61
|
+
// único dia, todas contornadas com `TMPDIR` isolado, por falso alarme.
|
|
62
|
+
// O que o delta NÃO afrouxa: vazamento genuíno desta execução continua
|
|
63
|
+
// reprovando (é a única coisa que esta asserção existe para pegar).
|
|
64
|
+
const tmpdirAntes = new Set(
|
|
65
|
+
fs.readdirSync(os.tmpdir()).filter((n) => n.startsWith('pc-smoke-mem-')),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// --- Roda o smoke NÃO-sweep (as 4 provas) via CLI real contra o pod copiado. ---
|
|
69
|
+
const wf = path.join(podDir, 'workflows', 'wf-canary.yaml');
|
|
70
|
+
const res = spawnSync('node', [SMOKE, wf, '--pod-root', podDir], { encoding: 'utf8' });
|
|
71
|
+
ok(res.status === 0, `smoke não-sweep roda verde (exit ${res.status}) — ${(res.stdout || '').trim().split('\n').pop()}`);
|
|
72
|
+
|
|
73
|
+
const after = { jsonl: sha(jsonl), consolidated: sha(consolidated), size: fs.statSync(jsonl).size };
|
|
74
|
+
|
|
75
|
+
// --- AC2: memória BYTE-INTOCADA (o coração do fix). ---
|
|
76
|
+
ok(before.jsonl === after.jsonl, `learnings.jsonl BYTE-INTOCADO após smoke não-sweep (sha ${before.jsonl.slice(0, 12)}… == ${after.jsonl.slice(0, 12)}…; ${after.size} bytes)`);
|
|
77
|
+
ok(before.consolidated === after.consolidated, 'learnings.consolidated.md BYTE-INTOCADO após smoke não-sweep');
|
|
78
|
+
ok(before.size === after.size, `tamanho de learnings.jsonl inalterado (${before.size} == ${after.size})`);
|
|
79
|
+
|
|
80
|
+
// --- Higiene: nenhum pc-smoke-mem-* NOVO (cleanup em finally). Delta contra o
|
|
81
|
+
// snapshot capturado antes do primeiro spawn — resíduo alheio é ignorado,
|
|
82
|
+
// vazamento desta execução reprova. ---
|
|
83
|
+
const tmpdirDepois = fs.readdirSync(os.tmpdir()).filter((n) => n.startsWith('pc-smoke-mem-'));
|
|
84
|
+
const leftover = tmpdirDepois.filter((n) => !tmpdirAntes.has(n));
|
|
85
|
+
ok(leftover.length === 0, `cleanup: zero tmpdirs pc-smoke-mem-* NOVOS nesta execução (encontrados: ${leftover.length}; preexistentes ignorados: ${tmpdirAntes.size})`);
|
|
86
|
+
|
|
87
|
+
// --- Também nos modos de injeção (compartilham o mesmo cfg do main). ---
|
|
88
|
+
const resInj = spawnSync('node', [SMOKE, wf, '--pod-root', podDir, '--inject-veto-violation'], { encoding: 'utf8' });
|
|
89
|
+
ok(resInj.status === 0 && sha(jsonl) === before.jsonl,
|
|
90
|
+
'modo --inject-veto-violation também isola a memória (byte-intocada)');
|
|
91
|
+
|
|
92
|
+
// limpeza best-effort do pod temporário do teste.
|
|
93
|
+
try { fs.rmSync(podDir, { recursive: true, force: true }); } catch { /* ok */ }
|
|
94
|
+
|
|
95
|
+
if (failures.length > 0) {
|
|
96
|
+
console.error(`\nSuíte de isolamento de memória do smoke FALHOU: ${failures.length} de ${passed + failures.length}.`);
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
console.log(`\n${passed} asserções verdes. Smoke isola a memória do pod incondicionalmente (SF7.7).`);
|
|
100
|
+
process.exit(0);
|