@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,558 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* pod-export-postprocess.test.cjs - suite do FIXER determinístico pré-veto (Story SF10.11, F6).
|
|
5
|
+
*
|
|
6
|
+
* HERMÉTICA: zero rede, zero modelo, zero subprocesso próprio. Usa o CONTADOR REAL do veto
|
|
7
|
+
* (`lexical-check.countMatches`), o VALIDADOR REAL de HTML (`html-valid.checkBalance`), o
|
|
8
|
+
* GERADOR REAL do bloco de medida (`phash-gate.renderMeasureBlock`) e as guardas REAIS de
|
|
9
|
+
* blessed (`validate-pod-workflow.checkCommandCheckSafety` + `pc-run-pod.evalVeto`) - nada de
|
|
10
|
+
* reimplementar oráculo dentro do teste.
|
|
11
|
+
*
|
|
12
|
+
* Cobre: AC1 (domínio = toolTrail de write_file, peça HTML inclusa), AC2 (forma + tabela
|
|
13
|
+
* declarada + extensibilidade), AC3 (idempotência/determinismo), AC4 (relatório estruturado),
|
|
14
|
+
* AC5 (veto como última linha / não-regressão), AC8 (no-op sobre o bloco de MEDIDA do phash-gate).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('node:fs');
|
|
18
|
+
const os = require('node:os');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const crypto = require('node:crypto');
|
|
21
|
+
|
|
22
|
+
const pp = require('./pod-export-postprocess.cjs');
|
|
23
|
+
const lexical = require('./veto-checks/lexical-check.cjs');
|
|
24
|
+
const htmlValid = require('./veto-checks/html-valid.cjs');
|
|
25
|
+
const phash = require('./veto-checks/phash-gate.cjs');
|
|
26
|
+
const vp = require('./validate-pod-workflow.cjs');
|
|
27
|
+
const rp = require('./pc-run-pod.cjs');
|
|
28
|
+
const { motorRels } = require('../installer/motor-scripts.js');
|
|
29
|
+
|
|
30
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
31
|
+
const MODULE_PATH = path.join(__dirname, 'pod-export-postprocess.cjs');
|
|
32
|
+
const MODULE_SRC = fs.readFileSync(MODULE_PATH, 'utf8');
|
|
33
|
+
|
|
34
|
+
// Padrão do veto em produção (wf-hn-carousel / content-carousel): em-dash U+2014, en-dash U+2013.
|
|
35
|
+
const VETO_PATTERN = '[\\u2014\\u2013]';
|
|
36
|
+
const EM = '—';
|
|
37
|
+
const EN = '–';
|
|
38
|
+
|
|
39
|
+
let failures = 0;
|
|
40
|
+
function assert(ok, msg) {
|
|
41
|
+
if (ok) console.log(`[OK ] ${msg}`);
|
|
42
|
+
else { console.error(`[FALHA] ${msg}`); failures++; }
|
|
43
|
+
}
|
|
44
|
+
function tmpDir(label) { return fs.mkdtempSync(path.join(os.tmpdir(), `sf10.11-${label}-`)); }
|
|
45
|
+
function sha(p) { return crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex'); }
|
|
46
|
+
/** toolTrail no MESMO formato que `runPhase` produz (name/ok/arg). */
|
|
47
|
+
function trailWrite(...rels) { return rels.map((r) => ({ name: 'write_file', ok: true, arg: r })); }
|
|
48
|
+
function writeIn(runDir, rel, content) {
|
|
49
|
+
const abs = path.join(runDir, rel);
|
|
50
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
51
|
+
fs.writeFileSync(abs, content, 'utf8');
|
|
52
|
+
return abs;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ===========================================================================
|
|
56
|
+
// AC2 - FORMA: in-process, zero-dep, síncrono, NÃO blessed, NÃO declarável.
|
|
57
|
+
// ===========================================================================
|
|
58
|
+
|
|
59
|
+
function formaTests() {
|
|
60
|
+
console.log('\n--- AC2: forma do post-processor (DF-SF10.11-2) ---');
|
|
61
|
+
|
|
62
|
+
assert(!MODULE_PATH.includes(`${path.sep}veto-checks${path.sep}`), 'o módulo NÃO vive em scripts/veto-checks/ (raiz blessed intocada)');
|
|
63
|
+
|
|
64
|
+
// NÃO é blessed: a raiz blessed é derivada por PREFIXO scripts/veto-checks/ (blessedVetoRels,
|
|
65
|
+
// pc-run-pod.cjs). Estar no manifesto de DISTRIBUIÇÃO (ADR-007) não confere blessed nenhum.
|
|
66
|
+
const rels = motorRels();
|
|
67
|
+
const blessed = rels.filter((r) => r.startsWith('scripts/veto-checks/'));
|
|
68
|
+
// A contagem é PINADA de propósito: script novo sob scripts/veto-checks/ tem que passar por
|
|
69
|
+
// uma decisão consciente aqui. SF11.11 subiu de 4 para 5 (render-png.cjs, ADR-025).
|
|
70
|
+
assert(!blessed.includes('scripts/veto-checks/pod-export-postprocess.cjs') && blessed.length === 5,
|
|
71
|
+
`o fixer NÃO está na raiz blessed (blessed segue com os 5 veto-checks: ${blessed.map((b) => path.basename(b)).join(', ')})`);
|
|
72
|
+
assert(rels.includes('scripts/pod-export-postprocess.cjs'),
|
|
73
|
+
'o fixer ESTÁ no manifesto ADR-007 como require-dependency do executor (invariantes 1/3 do teste-guarda) - distribuição, não bênção');
|
|
74
|
+
|
|
75
|
+
// NÃO é declarável em wf-*.yaml: as DUAS guardas reais recusam, estática e em runtime.
|
|
76
|
+
let staticRefused = false;
|
|
77
|
+
try { vp.checkCommandCheckSafety({ command: 'pod-export-postprocess.cjs' }); } catch (e) { staticRefused = e instanceof vp.Fail; }
|
|
78
|
+
assert(staticRefused, 'ESTÁTICO: validate-pod-workflow RECUSA um command_check que nomeie o fixer (fora da raiz blessed)');
|
|
79
|
+
const rt = rp.evalVeto({ type: 'command_check', command: 'pod-export-postprocess.cjs' }, '', path.join(REPO_ROOT, 'package.json'), { podRoot: REPO_ROOT, runDir: REPO_ROOT });
|
|
80
|
+
assert(rt.ok === false && /blessed/.test(rt.reason || ''), `RUNTIME: evalVeto RECUSA o fixer como command_check sem executá-lo (${rt.reason})`);
|
|
81
|
+
|
|
82
|
+
// Nenhum wf do repo o declara.
|
|
83
|
+
const wfHits = [];
|
|
84
|
+
const stack = [path.join(REPO_ROOT, 'pods'), path.join(REPO_ROOT, 'template')];
|
|
85
|
+
while (stack.length > 0) {
|
|
86
|
+
const d = stack.pop();
|
|
87
|
+
if (!fs.existsSync(d)) continue;
|
|
88
|
+
for (const e of fs.readdirSync(d, { withFileTypes: true })) {
|
|
89
|
+
const p = path.join(d, e.name);
|
|
90
|
+
if (e.isDirectory()) stack.push(p);
|
|
91
|
+
else if (/\.ya?ml$/.test(e.name) && fs.readFileSync(p, 'utf8').includes('pod-export-postprocess')) wfHits.push(p);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
assert(wfHits.length === 0, `nenhum wf-*.yaml declara o fixer (encontrados: ${wfHits.length})`);
|
|
95
|
+
|
|
96
|
+
// Zero-dep, síncrono, sem shell, sem rede, sem catch cego.
|
|
97
|
+
const requires = [...MODULE_SRC.matchAll(/require\(\s*['"]([^'"]+)['"]\s*\)/g)].map((m) => m[1]);
|
|
98
|
+
assert(requires.every((r) => r === 'node:fs' || r === './pod-export-tools.cjs'),
|
|
99
|
+
`zero-dep: requires apenas builtins/irmão do export (${requires.join(', ')})`);
|
|
100
|
+
// A varredura de disciplina roda sobre o CÓDIGO, não sobre os comentários (o cabeçalho do módulo
|
|
101
|
+
// cita "child_process"/"rede" ao declarar o que NÃO faz; casar nisso seria falso-positivo).
|
|
102
|
+
const CODE = MODULE_SRC.replace(/\/\*[\s\S]*?\*\//g, '').split('\n').map((l) => l.replace(/(^|\s)\/\/.*$/, '')).join('\n');
|
|
103
|
+
assert(!/child_process|rede/.test(CODE) && /child_process/.test(MODULE_SRC),
|
|
104
|
+
'sanidade da varredura: os comentários foram removidos (o cabeçalho cita child_process; o código não)');
|
|
105
|
+
assert(!/execSync|execFileSync|\bspawn\w*\(|node:https?|node:net|node:dgram|\bfetch\(/.test(CODE),
|
|
106
|
+
'sem shell, sem child_process, sem rede no CÓDIGO: o fixer não pode chamar modelo nem processo (AC3)');
|
|
107
|
+
assert(!/\basync\b|\bawait\b|new Promise|\.then\(/.test(CODE), 'SÍNCRONO: nenhuma assincronia no módulo');
|
|
108
|
+
assert(!/catch\s*(\([^)]*\))?\s*\{\s*\}/.test(MODULE_SRC), 'nenhum `catch {}` cego sobrevive no fixer (fail-loud)');
|
|
109
|
+
assert((MODULE_SRC.match(/[—–]/g) || []).length === 0,
|
|
110
|
+
'o fonte do fixer não contém NENHUM literal em/en-dash (os padrões são escapes \\u2014/\\u2013)');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ===========================================================================
|
|
114
|
+
// AC2 - TABELA declarada + extensibilidade sem tocar o ponto de chamada.
|
|
115
|
+
// ===========================================================================
|
|
116
|
+
|
|
117
|
+
function tabelaTests() {
|
|
118
|
+
console.log('\n--- AC2: tabela declarada de substituição (DF-SF10.11-3) ---');
|
|
119
|
+
|
|
120
|
+
assert(pp.SUBSTITUTIONS.length === 2, `tabela tem exatamente as 2 entradas aprovadas pelo dono (tem ${pp.SUBSTITUTIONS.length})`);
|
|
121
|
+
assert(pp.SUBSTITUTIONS.every((r) => r.id && r.pattern instanceof RegExp && typeof r.replacement === 'string' && r.porque && r.veto_que_alivia),
|
|
122
|
+
'toda entrada declara { id, pattern, replacement, porque, veto_que_alivia } - tabela, não regex solta');
|
|
123
|
+
|
|
124
|
+
// As DUAS entradas do dono, na semântica exata aprovada.
|
|
125
|
+
assert(pp.fixText(`palavra ${EM} outra`).text === 'palavra, outra', 'entrada 1: em-dash ENTRE ESPAÇOS vira vírgula');
|
|
126
|
+
assert(pp.fixText(`de 2020${EN}2024`).text === 'de 2020-2024', 'entrada 2: en-dash ENTRE DÍGITOS vira hífen ASCII');
|
|
127
|
+
|
|
128
|
+
// Fronteira: o que NÃO está na tabela NÃO é tocado (o veto continua sendo quem bloqueia).
|
|
129
|
+
const foraDaTabela = `palavra${EM}colada e um travess${EM}o solto\n${EM} item de lista\ntexto ${EN} com en-dash em prosa`;
|
|
130
|
+
assert(pp.fixText(foraDaTabela).text === foraDaTabela,
|
|
131
|
+
'NADA fora da tabela é alterado (em-dash colado, início de linha, en-dash em prosa) - No Invention; o veto segue bloqueando');
|
|
132
|
+
|
|
133
|
+
// 2ª classe mecânica adicionável SEM mudar o ponto de chamada (a tabela é o parâmetro).
|
|
134
|
+
const run = tmpDir('tabela2');
|
|
135
|
+
writeIn(run, 'artifacts/x.md', 'reticencias… aqui');
|
|
136
|
+
const tabela2 = [...pp.SUBSTITUTIONS, { id: 'reticencias', classe: 'prosa', pattern: /…/gu, replacement: '...', porque: 'fixture de extensibilidade', veto_que_alivia: 'n/a' }];
|
|
137
|
+
const rep = pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/x.md'), table: tabela2 });
|
|
138
|
+
assert(rep.changed && fs.readFileSync(path.join(run, 'artifacts/x.md'), 'utf8') === 'reticencias... aqui'
|
|
139
|
+
&& rep.files[0].rules.some((r) => r.id === 'reticencias'),
|
|
140
|
+
'uma 2ª entrada de tabela é aplicada e reportada SEM nenhuma mudança no ponto de chamada (AC2)');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ===========================================================================
|
|
144
|
+
// AC1 - DOMÍNIO: toolTrail de write_file da tentativa (não o artefato verificado).
|
|
145
|
+
// ===========================================================================
|
|
146
|
+
|
|
147
|
+
function dominioTests() {
|
|
148
|
+
console.log('\n--- AC1: domínio = toolTrail de write_file da tentativa (DF-SF10.11-1) ---');
|
|
149
|
+
|
|
150
|
+
const trail = [
|
|
151
|
+
{ name: 'write_file', ok: true, arg: 'artifacts/assembly.md' },
|
|
152
|
+
{ name: 'write_file', ok: true, arg: 'pieces/01-capa.html' },
|
|
153
|
+
{ name: 'write_file', ok: true, arg: 'artifacts/assembly.md' }, // reescrita: 1 vez só
|
|
154
|
+
{ name: 'write_file', ok: false, arg: 'pieces/99-falhou.html' }, // escrita que FALHOU
|
|
155
|
+
{ name: 'openrouter_image', ok: true, arg: 'assets/cover.png' }, // binário: fora
|
|
156
|
+
{ name: 'read_image', ok: true, arg: 'inputs:ref.png' },
|
|
157
|
+
{ name: 'http_fetch', ok: true, arg: 'https://news.ycombinator.com/x' },
|
|
158
|
+
];
|
|
159
|
+
const rels = pp.writtenPathsFromTrail(trail);
|
|
160
|
+
assert(rels.length === 2 && rels[0] === 'artifacts/assembly.md' && rels[1] === 'pieces/01-capa.html',
|
|
161
|
+
`domínio = só write_file OK, sem duplicata (${rels.join(', ')}) - imagem/leitura/fetch ficam de fora por construção`);
|
|
162
|
+
|
|
163
|
+
// As 7 PEÇAS HTML entram; o binário de assets/ NÃO é tocado.
|
|
164
|
+
const run = tmpDir('dominio');
|
|
165
|
+
const pecas = [];
|
|
166
|
+
for (let i = 1; i <= 7; i++) {
|
|
167
|
+
const rel = `pieces/0${i}-peca.html`;
|
|
168
|
+
writeIn(run, rel, `<html><body><p>Slide ${i} ${EM} com travessao</p></body></html>`);
|
|
169
|
+
pecas.push(rel);
|
|
170
|
+
}
|
|
171
|
+
writeIn(run, 'artifacts/assembly.md', `## Pacote Final\n- resumo ${EM} do pacote\n`);
|
|
172
|
+
const pngBytes = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
|
|
173
|
+
fs.mkdirSync(path.join(run, 'assets'), { recursive: true });
|
|
174
|
+
fs.writeFileSync(path.join(run, 'assets/cover.png'), pngBytes);
|
|
175
|
+
const pngShaBefore = sha(path.join(run, 'assets/cover.png'));
|
|
176
|
+
|
|
177
|
+
const trail2 = [...trailWrite(...pecas, 'artifacts/assembly.md'), { name: 'openrouter_image', ok: true, arg: 'assets/cover.png' }];
|
|
178
|
+
const rep = pp.postProcessAttempt({ runDir: run, toolTrail: trail2 });
|
|
179
|
+
|
|
180
|
+
const pecasLimpas = pecas.filter((rel) => lexical.countMatches(fs.readFileSync(path.join(run, rel), 'utf8'), VETO_PATTERN) === 0);
|
|
181
|
+
assert(pecasLimpas.length === 7, `AC1(i): as 7 PEÇAS HTML foram corrigidas - a classe que NENHUM veto do wf inspeciona hoje (limpas: ${pecasLimpas.length}/7)`);
|
|
182
|
+
assert(rep.files.length === 8 && rep.scanned === 8, `relatório cobre as 8 escritas da tentativa (7 peças + sumário): ${rep.files.length}`);
|
|
183
|
+
assert(sha(path.join(run, 'assets/cover.png')) === pngShaBefore, 'imagem de assets/ (openrouter_image) NÃO é tocada pelo fixer - domínio é write_file');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ===========================================================================
|
|
187
|
+
// AC1 - CORREÇÃO REAL: o contador do veto passa a ver 0; o HTML segue válido.
|
|
188
|
+
// ===========================================================================
|
|
189
|
+
|
|
190
|
+
function correcaoTests() {
|
|
191
|
+
console.log('\n--- AC1: correção real medida pelo oráculo do veto ---');
|
|
192
|
+
|
|
193
|
+
// (ii) sumário assembly.md dentro do limiar do lexical-check (--max 0) SEM intervenção do modelo.
|
|
194
|
+
const assemblyAntes = [
|
|
195
|
+
'## Slides Internos',
|
|
196
|
+
`- slide 1 ${EM} o gancho`,
|
|
197
|
+
`- slide 2 ${EM} o conflito`,
|
|
198
|
+
`- slide 3 ${EM} a virada de 2020${EN}2024`,
|
|
199
|
+
'## Slide CTA',
|
|
200
|
+
`Chamada final ${EM} forte.`,
|
|
201
|
+
'',
|
|
202
|
+
'confianca: alta',
|
|
203
|
+
'',
|
|
204
|
+
].join('\n');
|
|
205
|
+
const run = tmpDir('correcao');
|
|
206
|
+
writeIn(run, 'artifacts/assembly.md', assemblyAntes);
|
|
207
|
+
assert(lexical.countMatches(assemblyAntes, VETO_PATTERN) === 5, `fixture: o veto contaria 5 ocorrências ANTES do fixer (${lexical.countMatches(assemblyAntes, VETO_PATTERN)})`);
|
|
208
|
+
|
|
209
|
+
pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/assembly.md') });
|
|
210
|
+
const depois = fs.readFileSync(path.join(run, 'artifacts/assembly.md'), 'utf8');
|
|
211
|
+
assert(lexical.countMatches(depois, VETO_PATTERN) === 0,
|
|
212
|
+
'AC1(ii): o CONTADOR REAL do lexical-check vê 0 ocorrência depois do fixer (limiar --max 0 satisfeito sem retry)');
|
|
213
|
+
assert(depois.includes('- slide 1, o gancho') && depois.includes('a virada de 2020-2024'),
|
|
214
|
+
'a substituição preserva a leitura (vírgula na prosa, hífen no intervalo)');
|
|
215
|
+
|
|
216
|
+
// Peça HTML adversarial: travessão em ATRIBUTO, em <style> e em texto. O HTML segue válido.
|
|
217
|
+
const htmlAntes = [
|
|
218
|
+
'<!doctype html>',
|
|
219
|
+
'<html><head><style>',
|
|
220
|
+
`/* tema ${EM} cores da marca */`,
|
|
221
|
+
'.t { font-family: "Inter", sans-serif; }',
|
|
222
|
+
'</style></head>',
|
|
223
|
+
'<body>',
|
|
224
|
+
`<p data-nota="fonte ${EM} HN" title="ano 2020${EN}2024">Texto ${EM} do slide</p>`,
|
|
225
|
+
'<img src="../assets/cover.png" alt="capa">',
|
|
226
|
+
'</body></html>',
|
|
227
|
+
].join('\n');
|
|
228
|
+
const run2 = tmpDir('html');
|
|
229
|
+
writeIn(run2, 'pieces/01-capa.html', htmlAntes);
|
|
230
|
+
assert(htmlValid.checkBalance(htmlAntes).ok, 'fixture HTML é válida ANTES do fixer (balanceamento real do html-valid)');
|
|
231
|
+
pp.postProcessAttempt({ runDir: run2, toolTrail: trailWrite('pieces/01-capa.html') });
|
|
232
|
+
const htmlDepois = fs.readFileSync(path.join(run2, 'pieces/01-capa.html'), 'utf8');
|
|
233
|
+
assert(lexical.countMatches(htmlDepois, VETO_PATTERN) === 0, 'peça HTML: 0 ocorrência depois (atributo, <style> e texto)');
|
|
234
|
+
assert(htmlValid.checkBalance(htmlDepois).ok, 'AC2/inércia sintática: o HTML continua VÁLIDO depois do fixer (html-valid real)');
|
|
235
|
+
assert(/data-nota="fonte, HN"/.test(htmlDepois) && /title="ano 2020-2024"/.test(htmlDepois),
|
|
236
|
+
'atributos seguem com aspas intactas: o caractere trocado é sintaticamente INERTE (nada de "normalizar aspas")');
|
|
237
|
+
assert(htmlDepois.includes('<img src="../assets/cover.png" alt="capa">'), 'markup não relacionado sai intacto');
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ===========================================================================
|
|
241
|
+
// AC3 - IDEMPOTÊNCIA e DETERMINISMO.
|
|
242
|
+
// ===========================================================================
|
|
243
|
+
|
|
244
|
+
function idempotenciaTests() {
|
|
245
|
+
console.log('\n--- AC3: idempotência (fix(fix(x)) === fix(x)) e determinismo ---');
|
|
246
|
+
|
|
247
|
+
const corpus = [
|
|
248
|
+
`prosa ${EM} simples`,
|
|
249
|
+
`cadeia ${EM} de ${EM} tres ${EM} pedacos`,
|
|
250
|
+
`intervalo 1${EN}2${EN}3 e 2020${EN}2024`,
|
|
251
|
+
`dash duplo ${EM} ${EM} colados`,
|
|
252
|
+
`misto ${EM} com 1990${EN}1999 no meio ${EM} fim`,
|
|
253
|
+
`sem nada para corrigir`,
|
|
254
|
+
`${EM} inicio de linha e fim ${EM}`,
|
|
255
|
+
`<p class="a ${EM} b">html ${EM} inline</p>`,
|
|
256
|
+
'',
|
|
257
|
+
];
|
|
258
|
+
let allIdem = true;
|
|
259
|
+
let allClean = true;
|
|
260
|
+
for (const s of corpus) {
|
|
261
|
+
const once = pp.fixText(s).text;
|
|
262
|
+
const twice = pp.fixText(once).text;
|
|
263
|
+
if (once !== twice) { allIdem = false; console.error(` nao-idempotente: ${JSON.stringify(s)} -> ${JSON.stringify(once)} -> ${JSON.stringify(twice)}`); }
|
|
264
|
+
if (pp.applyTableOnce(once, pp.SUBSTITUTIONS).text !== once) allClean = false;
|
|
265
|
+
}
|
|
266
|
+
assert(allIdem, `fix(fix(x)) === fix(x) em ${corpus.length} entradas, cadeias e dashes adjacentes inclusos`);
|
|
267
|
+
assert(allClean, 'a saída é PONTO FIXO da tabela: mais uma passada não muda nada (idempotência estrutural, não acidente de regex)');
|
|
268
|
+
|
|
269
|
+
// Determinismo: mesma entrada, mesma saída, 3 vezes.
|
|
270
|
+
const alvo = corpus[4];
|
|
271
|
+
const s1 = pp.fixText(alvo).text; const s2 = pp.fixText(alvo).text; const s3 = pp.fixText(alvo).text;
|
|
272
|
+
assert(s1 === s2 && s2 === s3, 'determinístico: 3 execuções da mesma entrada dão byte a byte o mesmo resultado');
|
|
273
|
+
|
|
274
|
+
// Idempotência EM DISCO: 2ª passada sobre os mesmos arquivos não muda byte nenhum.
|
|
275
|
+
const run = tmpDir('idem');
|
|
276
|
+
writeIn(run, 'artifacts/assembly.md', `sumario ${EM} do pacote de 2020${EN}2024`);
|
|
277
|
+
writeIn(run, 'pieces/01-capa.html', `<p>capa ${EM} titulo</p>`);
|
|
278
|
+
const trail = trailWrite('artifacts/assembly.md', 'pieces/01-capa.html');
|
|
279
|
+
const r1 = pp.postProcessAttempt({ runDir: run, toolTrail: trail });
|
|
280
|
+
const shas = [sha(path.join(run, 'artifacts/assembly.md')), sha(path.join(run, 'pieces/01-capa.html'))];
|
|
281
|
+
const r2 = pp.postProcessAttempt({ runDir: run, toolTrail: trail });
|
|
282
|
+
assert(r1.changed && !r2.changed, 'em disco: 1ª passada corrige, 2ª passada não tem NADA a corrigir');
|
|
283
|
+
assert(shas[0] === sha(path.join(run, 'artifacts/assembly.md')) && shas[1] === sha(path.join(run, 'pieces/01-capa.html')),
|
|
284
|
+
'em disco: os sha256 dos arquivos são idênticos depois da 2ª passada (idempotência provada por hash)');
|
|
285
|
+
|
|
286
|
+
// Tabela SEM ponto fixo (substituição que volta a casar e cresce) FALHA ALTO, em vez de devolver
|
|
287
|
+
// um resultado que depende de quantas passadas rodaram.
|
|
288
|
+
const divergente = [{ id: 'cresce', pattern: /A/gu, replacement: 'AA', porque: 'fixture', veto_que_alivia: 'n/a' }];
|
|
289
|
+
let threw = null;
|
|
290
|
+
try { pp.fixText('A', { table: divergente }); } catch (e) { threw = e; }
|
|
291
|
+
assert(threw && /ponto fixo/.test(threw.message), 'tabela SEM ponto fixo (`A` -> `AA`) FALHA ALTO em MAX_FIX_PASSES, nunca devolve saída dependente do número de passadas');
|
|
292
|
+
|
|
293
|
+
// Par que se desfaz DENTRO da passada (A->B, B->A) é IDENTIDADE: estável e determinístico - o
|
|
294
|
+
// fixer só deixa de ajudar, e o veto continua sendo quem bloqueia (deriva fail-safe).
|
|
295
|
+
const parInverso = [
|
|
296
|
+
{ id: 'a-para-b', pattern: /A/gu, replacement: 'B', porque: 'fixture', veto_que_alivia: 'n/a' },
|
|
297
|
+
{ id: 'b-para-a', pattern: /B/gu, replacement: 'A', porque: 'fixture', veto_que_alivia: 'n/a' },
|
|
298
|
+
];
|
|
299
|
+
const pi = pp.fixText('A', { table: parInverso });
|
|
300
|
+
assert(pi.text === 'A' && pi.changed === false && pp.fixText(pi.text, { table: parInverso }).text === 'A',
|
|
301
|
+
'par de entradas que se anulam na mesma passada é IDENTIDADE estável (não altera nada, não gira) - fail-safe: o veto segue bloqueando');
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ===========================================================================
|
|
305
|
+
// AC4 - RELATÓRIO estruturado: evidência de auditoria E insumo da SF10.12.
|
|
306
|
+
// ===========================================================================
|
|
307
|
+
|
|
308
|
+
function relatorioTests() {
|
|
309
|
+
console.log('\n--- AC4: relatório estruturado (evidência + insumo da SF10.12) ---');
|
|
310
|
+
|
|
311
|
+
const run = tmpDir('relatorio');
|
|
312
|
+
writeIn(run, 'artifacts/assembly.md', `linha limpa\nlinha ${EM} suja\noutra 2020${EN}2024\n`);
|
|
313
|
+
writeIn(run, 'pieces/01-capa.html', '<p>nada a corrigir</p>');
|
|
314
|
+
const logs = [];
|
|
315
|
+
const rep = pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/assembly.md', 'pieces/01-capa.html'), log: (m) => logs.push(m) });
|
|
316
|
+
|
|
317
|
+
assert(rep.changed === true && rep.scanned === 2 && rep.files.length === 1 && rep.totalReplacements === 2,
|
|
318
|
+
`relatório: scanned=2, 1 arquivo alterado, 2 substituições (${JSON.stringify({ s: rep.scanned, f: rep.files.length, t: rep.totalReplacements })})`);
|
|
319
|
+
const f = rep.files[0];
|
|
320
|
+
assert(f.path === 'artifacts/assembly.md' && f.rules.length === 2
|
|
321
|
+
&& f.rules.some((r) => r.id === 'em-dash-entre-espacos' && r.count === 1)
|
|
322
|
+
&& f.rules.some((r) => r.id === 'en-dash-entre-digitos' && r.count === 1),
|
|
323
|
+
'relatório diz QUAL entrada da tabela disparou e quantas vezes, por arquivo');
|
|
324
|
+
assert(f.diff.length === 2 && f.diff[0].line === 2 && /suja/.test(f.diff[0].before) && /, suja/.test(f.diff[0].after),
|
|
325
|
+
`diff MÍNIMO por linha, com número de linha e antes/depois (linhas no diff: ${f.diff.length})`);
|
|
326
|
+
assert(logs.some((l) => /\[fixer\]/.test(l) && /assembly\.md/.test(l)), 'AC4: NUNCA silencioso - a correção aparece no log do run');
|
|
327
|
+
|
|
328
|
+
// O diff é bounded: vira mensagem de modelo na SF10.12, não pode explodir o contexto.
|
|
329
|
+
const run2 = tmpDir('relatorio-cap');
|
|
330
|
+
const muitas = Array.from({ length: 40 }, (_, i) => `linha ${i} ${EM} suja`).join('\n');
|
|
331
|
+
writeIn(run2, 'artifacts/assembly.md', muitas);
|
|
332
|
+
const rep2 = pp.postProcessAttempt({ runDir: run2, toolTrail: trailWrite('artifacts/assembly.md') });
|
|
333
|
+
assert(rep2.files[0].diff.length === pp.MAX_DIFF_LINES_PER_FILE && rep2.files[0].diffTruncated === true,
|
|
334
|
+
`diff limitado a ${pp.MAX_DIFF_LINES_PER_FILE} linhas/arquivo com flag de truncamento (o relatório vira prompt na SF10.12)`);
|
|
335
|
+
assert(rep2.files[0].replacements === 40, 'a CONTAGEM permanece completa mesmo com o diff truncado (40 substituições)');
|
|
336
|
+
|
|
337
|
+
// Costura declarada para a SF10.12.
|
|
338
|
+
const msg = pp.renderFixMessage(rep);
|
|
339
|
+
assert(/artifacts\/assembly\.md/.test(msg) && /em-dash-entre-espacos/.test(msg) && /linha 2/.test(msg),
|
|
340
|
+
'renderFixMessage produz o bloco que a SF10.12 injeta na retomada da conversa (arquivo + regra + linha)');
|
|
341
|
+
assert(pp.renderFixMessage({ changed: false, files: [] }) === '', 'sem correção, não há mensagem (custo zero no caminho limpo)');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// ===========================================================================
|
|
345
|
+
// AC5 - VETO CONTINUA SENDO A ÚLTIMA LINHA (não-regressão).
|
|
346
|
+
// ===========================================================================
|
|
347
|
+
|
|
348
|
+
function naoRegressaoTests() {
|
|
349
|
+
console.log('\n--- AC5: violação NÃO-mecânica passa incólume pelo fixer e segue para o veto ---');
|
|
350
|
+
|
|
351
|
+
const run = tmpDir('nao-mecanico');
|
|
352
|
+
// Artefato INCOMPLETO (falta seção/campo que o veto exige) e SEM classe mecânica.
|
|
353
|
+
const incompleto = '## Slides Internos\n- so um slide\n\nconfianca: talvez\n';
|
|
354
|
+
writeIn(run, 'artifacts/assembly.md', incompleto);
|
|
355
|
+
const shaAntes = sha(path.join(run, 'artifacts/assembly.md'));
|
|
356
|
+
const rep = pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/assembly.md') });
|
|
357
|
+
assert(!rep.changed && rep.files.length === 0 && sha(path.join(run, 'artifacts/assembly.md')) === shaAntes,
|
|
358
|
+
'o fixer NÃO altera um artefato com violação não-mecânica (byte-idêntico) - nada de mascarar problema de conteúdo');
|
|
359
|
+
|
|
360
|
+
// E o veto REAL continua reprovando esse artefato (o fixer não é bypass).
|
|
361
|
+
const vetoCount = rp.evalVeto({ type: 'count_gte', field: 'Slides Internos', n: 5 }, incompleto, path.join(run, 'artifacts/assembly.md'), { podRoot: run, runDir: run });
|
|
362
|
+
const vetoField = rp.evalVeto({ type: 'field_matches', field: 'confianca', pattern: '^(alta|media|baixa)$' }, incompleto, path.join(run, 'artifacts/assembly.md'), { podRoot: run, runDir: run });
|
|
363
|
+
assert(vetoCount.ok === false && vetoField.ok === false,
|
|
364
|
+
'os vetos REAIS (count_gte, field_matches) seguem reprovando depois do fixer - o veto é a ÚLTIMA linha, sem regressão');
|
|
365
|
+
|
|
366
|
+
// Arquivo já limpo: incólume, e o fixer não inventa mudança nenhuma.
|
|
367
|
+
const run2 = tmpDir('limpo');
|
|
368
|
+
const limpo = '## Slides Internos\n- slide sem travessao, com virgula\n\nconfianca: alta\n';
|
|
369
|
+
writeIn(run2, 'artifacts/assembly.md', limpo);
|
|
370
|
+
const shaLimpo = sha(path.join(run2, 'artifacts/assembly.md'));
|
|
371
|
+
const rep2 = pp.postProcessAttempt({ runDir: run2, toolTrail: trailWrite('artifacts/assembly.md') });
|
|
372
|
+
assert(!rep2.changed && sha(path.join(run2, 'artifacts/assembly.md')) === shaLimpo, 'artefato SEM travessão nenhum passa incólume (nem reescrita, nem mtime falso de correção)');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// ===========================================================================
|
|
376
|
+
// AC8 - NO-OP sobre o bloco de MEDIDA do phash-gate.
|
|
377
|
+
// ===========================================================================
|
|
378
|
+
|
|
379
|
+
function phashNoOpTests() {
|
|
380
|
+
console.log('\n--- AC8: no-op sobre o bloco de MEDIDA do phash-gate ---');
|
|
381
|
+
|
|
382
|
+
// Anti-deriva PERMANENTE: os delimitadores declarados no fixer são os do veto REAL.
|
|
383
|
+
const região = pp.PROTECTED_REGIONS.find((r) => r.id === 'phash-medida-similaridade');
|
|
384
|
+
assert(região && região.begin === phash.MEASURE_BEGIN && região.end === phash.MEASURE_END,
|
|
385
|
+
'os delimitadores protegidos são byte a byte os de phash-gate.cjs (deriva futura fica VERMELHA aqui)');
|
|
386
|
+
|
|
387
|
+
// Bloco REAL gerado pelo veto, ENVENENADO com a classe mecânica (hoje o gerador não emite token
|
|
388
|
+
// banido - NOTE-4; o envenenamento prova que a proteção é ESTRUTURAL, não sorte do conteúdo).
|
|
389
|
+
const blocoReal = phash.renderMeasureBlock({ status: 'ok', rows: [{ name: 'cover.png', nearestRef: 'ref.png', distance: 31 }] });
|
|
390
|
+
const blocoEnvenenado = blocoReal.replace('## Medida de similaridade', `## Medida ${EM} de similaridade (faixa 2020${EN}2024)`);
|
|
391
|
+
const artefato = phash.upsertMeasureBlock(`## Conformidade de Referencia\nEstilo inspirado ${EM} composicao propria.\n\nreferencia_copiada: nao\nconfianca: alta\n`, blocoEnvenenado);
|
|
392
|
+
|
|
393
|
+
const run = tmpDir('phash');
|
|
394
|
+
writeIn(run, 'artifacts/visuals.md', artefato);
|
|
395
|
+
const rep = pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/visuals.md') });
|
|
396
|
+
const depois = fs.readFileSync(path.join(run, 'artifacts/visuals.md'), 'utf8');
|
|
397
|
+
|
|
398
|
+
const b = depois.indexOf(phash.MEASURE_BEGIN);
|
|
399
|
+
const e = depois.indexOf(phash.MEASURE_END);
|
|
400
|
+
const blocoDepois = depois.slice(b, e + phash.MEASURE_END.length);
|
|
401
|
+
assert(b >= 0 && e > b && blocoDepois === blocoEnvenenado,
|
|
402
|
+
'AC8: o bloco de MEDIDA sai BYTE-IDÊNTICO do fixer, mesmo contendo a classe mecânica que o fixer corrige fora dele');
|
|
403
|
+
assert(depois.includes('Estilo inspirado, composicao propria.') && rep.changed,
|
|
404
|
+
'a prosa FORA do bloco protegido continua sendo corrigida normalmente (a proteção é cirúrgica, não um opt-out do arquivo)');
|
|
405
|
+
|
|
406
|
+
// Marcador de INÍCIO sem FIM: protege até o fim do arquivo e AVISA (nunca silencioso). O veto
|
|
407
|
+
// segue verificando o arquivo inteiro, então isso não é caminho de evasão.
|
|
408
|
+
const run2 = tmpDir('phash-aberto');
|
|
409
|
+
writeIn(run2, 'artifacts/visuals.md', `prosa ${EM} corrigivel\n${phash.MEASURE_BEGIN}\nbloco ${EM} sem fim\n`);
|
|
410
|
+
const rep2 = pp.postProcessAttempt({ runDir: run2, toolTrail: trailWrite('artifacts/visuals.md') });
|
|
411
|
+
const d2 = fs.readFileSync(path.join(run2, 'artifacts/visuals.md'), 'utf8');
|
|
412
|
+
assert(d2.includes('prosa, corrigivel') && d2.includes(`bloco ${EM} sem fim`) && rep2.warnings.length === 1,
|
|
413
|
+
'marcador de início SEM fim: protege até o fim do arquivo e registra AVISO no relatório (nunca silencioso)');
|
|
414
|
+
assert(lexical.countMatches(d2, VETO_PATTERN) === 1, 'e o veto ainda VÊ o travessão escondido no bloco aberto (não há evasão: o veto lê o arquivo inteiro)');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ===========================================================================
|
|
418
|
+
// FAIL-LOUD e contenção herdada das tools.
|
|
419
|
+
// ===========================================================================
|
|
420
|
+
|
|
421
|
+
function failLoudTests() {
|
|
422
|
+
console.log('\n--- Fail-loud e contenção (DF-SF10.11-1) ---');
|
|
423
|
+
|
|
424
|
+
const run = tmpDir('failloud');
|
|
425
|
+
|
|
426
|
+
// Path do trail que sumiu do disco: ENOENT é o ÚNICO errno com tratamento sancionado, e ele PARA
|
|
427
|
+
// o run com mensagem acionável (nunca segue em frente).
|
|
428
|
+
let e1 = null;
|
|
429
|
+
try { pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/sumiu.md') }); } catch (e) { e1 = e; }
|
|
430
|
+
assert(e1 && /NÃO existe em disco/.test(e1.message) && /sumiu\.md/.test(e1.message),
|
|
431
|
+
'path do toolTrail ausente em disco: FALHA ALTA com mensagem acionável (ENOENT sancionado, run PARA)');
|
|
432
|
+
|
|
433
|
+
// Qualquer outro errno sobe CRU (nada de catch-all disfarçado): diretório no lugar de arquivo.
|
|
434
|
+
fs.mkdirSync(path.join(run, 'artifacts', 'sou-um-dir.md'), { recursive: true });
|
|
435
|
+
let e2 = null;
|
|
436
|
+
try { pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('artifacts/sou-um-dir.md') }); } catch (e) { e2 = e; }
|
|
437
|
+
assert(e2 && e2.code === 'EISDIR', `errno não sancionado sobe CRU, preservando .code (${e2 && e2.code})`);
|
|
438
|
+
|
|
439
|
+
// Contenção herdada de confinedResolveWritable: traversal, path absoluto e inputs/ negados.
|
|
440
|
+
for (const [rel, label] of [['../escape.md', 'traversal'], ['/etc/passwd', 'path absoluto']]) {
|
|
441
|
+
let err = null;
|
|
442
|
+
try { pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite(rel) }); } catch (e) { err = e; }
|
|
443
|
+
assert(err !== null, `${label} no trail é NEGADO antes de qualquer leitura/escrita`);
|
|
444
|
+
}
|
|
445
|
+
fs.mkdirSync(path.join(run, 'inputs'), { recursive: true });
|
|
446
|
+
fs.writeFileSync(path.join(run, 'inputs', 'ref.png'), Buffer.from([0x89, 0x50]));
|
|
447
|
+
const shaRef = sha(path.join(run, 'inputs', 'ref.png'));
|
|
448
|
+
let e3 = null;
|
|
449
|
+
try { pp.postProcessAttempt({ runDir: run, toolTrail: trailWrite('inputs/ref.png') }); } catch (e) { e3 = e; }
|
|
450
|
+
assert(e3 && /PROIBIDA/.test(e3.message) && sha(path.join(run, 'inputs', 'ref.png')) === shaRef,
|
|
451
|
+
'a REFERÊNCIA materializada em inputs/ é INALCANÇÁVEL pelo fixer (DF5-6 intacto, sem defesa nova)');
|
|
452
|
+
|
|
453
|
+
// runDir obrigatório.
|
|
454
|
+
let e4 = null;
|
|
455
|
+
try { pp.postProcessAttempt({ toolTrail: [] }); } catch (e) { e4 = e; }
|
|
456
|
+
assert(e4 && /runDir/.test(e4.message), 'runDir ausente: falha alta imediata');
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ===========================================================================
|
|
460
|
+
// FIND-1 (débito Low do gate de QA da SF10.11, PAGO na SF10.12) — um path de `write_file`
|
|
461
|
+
// SOBRESCRITO por `openrouter_image` na MESMA tentativa sai do domínio: binário jamais é lido
|
|
462
|
+
// como UTF-8 nem regravado. O QA mediu a corrupção antes do conserto: 16B -> 21B, sha alterado.
|
|
463
|
+
// ===========================================================================
|
|
464
|
+
|
|
465
|
+
function find1BinarioTests() {
|
|
466
|
+
console.log('\n--- FIND-1: binário sobrescrito por openrouter_image sai do domínio do fixer ---');
|
|
467
|
+
|
|
468
|
+
const run = tmpDir('find1');
|
|
469
|
+
// Bytes que NÃO são UTF-8 válido: 0x89 'PNG' + 0xC3 solto + 0xFF/0xFE. Ler como utf8 e regravar
|
|
470
|
+
// trocaria cada byte inválido por U+FFFD (3 bytes), que é exatamente a perda que o QA mediu.
|
|
471
|
+
const pngBytes = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0xc3, 0xff, 0xfe, 0x00, 0x01, 0x02, 0x03, 0x04]);
|
|
472
|
+
const abs = path.join(run, 'assets', 'cover.png');
|
|
473
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
474
|
+
fs.writeFileSync(abs, pngBytes);
|
|
475
|
+
const shaAntes = sha(abs);
|
|
476
|
+
const bytesAntes = fs.statSync(abs).size;
|
|
477
|
+
|
|
478
|
+
// A tentativa: o modelo escreveu o path como TEXTO (write_file) e DEPOIS a imagem o sobrescreveu.
|
|
479
|
+
// Os dois eventos estão no MESMO toolTrail — é o cenário exato do achado.
|
|
480
|
+
const trail = [
|
|
481
|
+
{ name: 'write_file', ok: true, arg: 'assets/cover.png' },
|
|
482
|
+
{ name: 'write_file', ok: true, arg: 'artifacts/visuals.md' },
|
|
483
|
+
{ name: 'openrouter_image', ok: true, arg: 'assets/cover.png' },
|
|
484
|
+
];
|
|
485
|
+
writeIn(run, 'artifacts/visuals.md', `Capa gerada ${EM} referência inspirada, de 2020${EN}2024.`);
|
|
486
|
+
|
|
487
|
+
const logs = [];
|
|
488
|
+
const rep = pp.postProcessAttempt({ runDir: run, toolTrail: trail, log: (m) => logs.push(m) });
|
|
489
|
+
|
|
490
|
+
assert(sha(abs) === shaAntes && fs.statSync(abs).size === bytesAntes,
|
|
491
|
+
`FIND-1: o BINÁRIO saiu INTACTO (${bytesAntes}B, sha inalterado) — nada de 16B -> 21B com U+FFFD`);
|
|
492
|
+
assert(fs.readFileSync(abs).equals(pngBytes), 'FIND-1: bytes idênticos ao original, byte a byte (comparação de Buffer, não de string)');
|
|
493
|
+
assert(rep.skippedBinary.length === 1 && rep.skippedBinary[0] === 'assets/cover.png',
|
|
494
|
+
`FIND-1: o path pulado é NOMEADO no relatório (${JSON.stringify(rep.skippedBinary)})`);
|
|
495
|
+
assert(logs.some((l) => /assets\/cover\.png/.test(l) && /PULADO/.test(l)), 'FIND-1: a subtração do domínio NUNCA é silenciosa (log do run)');
|
|
496
|
+
assert(rep.scanned === 1 && rep.files.length === 1 && rep.files[0].path === 'artifacts/visuals.md' && rep.totalReplacements === 2,
|
|
497
|
+
`FIND-1: o arquivo de TEXTO da mesma tentativa continua sendo corrigido (scanned=${rep.scanned}, ${rep.totalReplacements} substituições)`);
|
|
498
|
+
|
|
499
|
+
// Evasão por GRAFIA: './assets/cover.png' e 'assets/cover.png' são o MESMO arquivo. A subtração é
|
|
500
|
+
// por path absoluto resolvido — comparação literal de string deixaria este vetor aberto.
|
|
501
|
+
const run2 = tmpDir('find1-grafia');
|
|
502
|
+
const abs2 = path.join(run2, 'assets', 'cover.png');
|
|
503
|
+
fs.mkdirSync(path.dirname(abs2), { recursive: true });
|
|
504
|
+
fs.writeFileSync(abs2, pngBytes);
|
|
505
|
+
const rep2 = pp.postProcessAttempt({
|
|
506
|
+
runDir: run2,
|
|
507
|
+
toolTrail: [
|
|
508
|
+
{ name: 'write_file', ok: true, arg: './assets/cover.png' },
|
|
509
|
+
{ name: 'openrouter_image', ok: true, arg: 'assets/./cover.png' },
|
|
510
|
+
],
|
|
511
|
+
});
|
|
512
|
+
assert(fs.readFileSync(abs2).equals(pngBytes) && rep2.skippedBinary.length === 1,
|
|
513
|
+
'FIND-1: grafias diferentes do MESMO path não escapam da subtração (resolução absoluta, não string)');
|
|
514
|
+
|
|
515
|
+
// Contrapartida: imagem que FALHOU não gravou nada naquele path — subtrair por ela removeria do
|
|
516
|
+
// domínio um arquivo de TEXTO legítimo (o fixer deixaria de ajudar em silêncio).
|
|
517
|
+
const run3 = tmpDir('find1-falha');
|
|
518
|
+
writeIn(run3, 'artifacts/visuals.md', `texto ${EM} com travessão`);
|
|
519
|
+
const rep3 = pp.postProcessAttempt({
|
|
520
|
+
runDir: run3,
|
|
521
|
+
toolTrail: [
|
|
522
|
+
{ name: 'write_file', ok: true, arg: 'artifacts/visuals.md' },
|
|
523
|
+
{ name: 'openrouter_image', ok: false, arg: 'artifacts/visuals.md' },
|
|
524
|
+
],
|
|
525
|
+
});
|
|
526
|
+
assert(rep3.changed === true && rep3.skippedBinary.length === 0,
|
|
527
|
+
'FIND-1: openrouter_image que FALHOU não subtrai o path (nada foi gravado por ela; o texto continua no domínio)');
|
|
528
|
+
|
|
529
|
+
// A função de domínio isolada: só imagens BEM-SUCEDIDAS, sem duplicata, na ordem de escrita.
|
|
530
|
+
const imgs = pp.imagePathsFromTrail([
|
|
531
|
+
{ name: 'openrouter_image', ok: true, arg: 'a.png' },
|
|
532
|
+
{ name: 'openrouter_image', ok: true, arg: 'a.png' },
|
|
533
|
+
{ name: 'openrouter_image', ok: false, arg: 'b.png' },
|
|
534
|
+
{ name: 'write_file', ok: true, arg: 'c.md' },
|
|
535
|
+
]);
|
|
536
|
+
assert(imgs.length === 1 && imgs[0] === 'a.png', `imagePathsFromTrail: só imagens ok, deduplicadas (${JSON.stringify(imgs)})`);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// ---------------------------------------------------------------------------
|
|
540
|
+
|
|
541
|
+
function main() {
|
|
542
|
+
console.log('POWER CLAUDE — fixer determinístico pré-veto do pod-export (SF10.11)\n');
|
|
543
|
+
formaTests();
|
|
544
|
+
tabelaTests();
|
|
545
|
+
dominioTests();
|
|
546
|
+
correcaoTests();
|
|
547
|
+
idempotenciaTests();
|
|
548
|
+
relatorioTests();
|
|
549
|
+
naoRegressaoTests();
|
|
550
|
+
phashNoOpTests();
|
|
551
|
+
failLoudTests();
|
|
552
|
+
find1BinarioTests();
|
|
553
|
+
if (failures > 0) { console.error(`\nSuíte FALHOU: ${failures} violação(ões).`); process.exit(1); }
|
|
554
|
+
console.log('\nSuíte verde.');
|
|
555
|
+
process.exit(0);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
main();
|