@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,745 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* enforce-ready-oracles.test.cjs — suíte da trava de `Ready` (story SF13.2,
|
|
5
|
+
* `ADR-032` com as Emendas 1 e 2).
|
|
6
|
+
*
|
|
7
|
+
* Cobre os blocos A-F da story (AC-1 a AC-19, incluindo AC-2b/AC-5b/AC-6b/
|
|
8
|
+
* AC-18b) e acrescenta 4 MUTANTES de classe. Os mutantes não são enfeite: o
|
|
9
|
+
* `*review` desta story mediu que, sem eles, arrancar a trava de `runMain`
|
|
10
|
+
* deixava **18 de 18** ACs verdes, remover a checagem de orçamento deixava
|
|
11
|
+
* **19 de 19**, e desembrulhar `main()` do `runWithFailSafe` deixava **18 de
|
|
12
|
+
* 18**. Um teste que sobrevive à remoção do mecanismo que ele afirma testar
|
|
13
|
+
* não é teste — é decoração.
|
|
14
|
+
*
|
|
15
|
+
* TÉCNICA NORMATIVA DE FIXTURE — "cópia instalada" (Dev Notes da SF13.2,
|
|
16
|
+
* Emenda 2 item 5): `template/.claude/hooks/` tem profundidade DIFERENTE da
|
|
17
|
+
* cópia instalada. No framework o hook mora 3 segmentos abaixo da raiz; numa
|
|
18
|
+
* instalação real (`template/` some na cópia) mora 2 — e a âncora do hook é
|
|
19
|
+
* `path.resolve(__dirname, '..', '..')`, correta para a instalação. Logo:
|
|
20
|
+
* nenhum teste que precise de `loadValidator`/`loadCycleAuthority` bem
|
|
21
|
+
* sucedidos pode rodar o hook "no lugar" dentro de `template/`; sempre copiar
|
|
22
|
+
* para `<tmp>/.claude/hooks/` (+ `<tmp>/scripts/`). Testes que NÃO alcançam
|
|
23
|
+
* os loaders (matcher, `file_path`, JSON ilegível) podem rodar no lugar.
|
|
24
|
+
*
|
|
25
|
+
* Todo mutante usa substituição de string sobre o FONTE REAL, com falha dura
|
|
26
|
+
* se o marcador não existir ("instrumento de mutação desalinhado") — nunca
|
|
27
|
+
* falso-passa quando o código muda de forma (precedente literal de
|
|
28
|
+
* `enforce-cycle-authority.test.cjs`).
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
const fs = require('node:fs');
|
|
32
|
+
const os = require('node:os');
|
|
33
|
+
const path = require('node:path');
|
|
34
|
+
const { spawnSync } = require('node:child_process');
|
|
35
|
+
|
|
36
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
37
|
+
const HOOK_SRC = path.join(REPO_ROOT, 'template', '.claude', 'hooks', 'enforce-ready-oracles.cjs');
|
|
38
|
+
const SIBLING_SRC = path.join(REPO_ROOT, 'template', '.claude', 'hooks', 'enforce-cycle-authority.cjs');
|
|
39
|
+
const VALIDATOR_SRC = path.join(REPO_ROOT, 'scripts', 'validate-story-acs.cjs');
|
|
40
|
+
|
|
41
|
+
const hook = require(HOOK_SRC);
|
|
42
|
+
const cycleAuthority = require(SIBLING_SRC);
|
|
43
|
+
const validator = require(VALIDATOR_SRC);
|
|
44
|
+
|
|
45
|
+
let failures = 0;
|
|
46
|
+
const tmpDirs = [];
|
|
47
|
+
|
|
48
|
+
function assert(ok, msg) {
|
|
49
|
+
if (ok) {
|
|
50
|
+
console.log(`[OK ] ${msg}`);
|
|
51
|
+
} else {
|
|
52
|
+
console.error(`[FALHA] ${msg}`);
|
|
53
|
+
failures++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Fixtures
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
function f(lines) {
|
|
62
|
+
return lines.join('\n');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function mkTmp(prefix) {
|
|
66
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
67
|
+
tmpDirs.push(dir);
|
|
68
|
+
return dir;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Cópia INSTALADA: `<tmp>/.claude/hooks/` (profundidade 2), como numa
|
|
73
|
+
* instalação real. `opts.hookSource` permite instalar um mutante.
|
|
74
|
+
*/
|
|
75
|
+
function mkInstalledCopy(opts) {
|
|
76
|
+
const o = opts || {};
|
|
77
|
+
const dir = mkTmp('ero-installed-');
|
|
78
|
+
fs.mkdirSync(path.join(dir, '.claude', 'hooks'), { recursive: true });
|
|
79
|
+
fs.writeFileSync(
|
|
80
|
+
path.join(dir, '.claude', 'hooks', 'enforce-ready-oracles.cjs'),
|
|
81
|
+
o.hookSource || fs.readFileSync(HOOK_SRC, 'utf8'),
|
|
82
|
+
);
|
|
83
|
+
if (o.sibling !== false) {
|
|
84
|
+
fs.copyFileSync(SIBLING_SRC, path.join(dir, '.claude', 'hooks', 'enforce-cycle-authority.cjs'));
|
|
85
|
+
}
|
|
86
|
+
if (o.validator !== false) {
|
|
87
|
+
fs.mkdirSync(path.join(dir, 'scripts'), { recursive: true });
|
|
88
|
+
fs.copyFileSync(VALIDATOR_SRC, path.join(dir, 'scripts', 'validate-story-acs.cjs'));
|
|
89
|
+
}
|
|
90
|
+
return dir;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function hookPathOf(dir) {
|
|
94
|
+
return path.join(dir, '.claude', 'hooks', 'enforce-ready-oracles.cjs');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function writeStory(dir, name, content) {
|
|
98
|
+
const p = path.join(dir, name);
|
|
99
|
+
fs.writeFileSync(p, content);
|
|
100
|
+
return p;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function runHook(hookPath, payload) {
|
|
104
|
+
const r = spawnSync(process.execPath, [hookPath], {
|
|
105
|
+
input: typeof payload === 'string' ? payload : JSON.stringify(payload),
|
|
106
|
+
encoding: 'utf8',
|
|
107
|
+
timeout: 15000,
|
|
108
|
+
});
|
|
109
|
+
let decision = null;
|
|
110
|
+
let reason = '';
|
|
111
|
+
try {
|
|
112
|
+
const j = JSON.parse(r.stdout);
|
|
113
|
+
decision = j.hookSpecificOutput.permissionDecision;
|
|
114
|
+
reason = j.hookSpecificOutput.permissionDecisionReason;
|
|
115
|
+
} catch {
|
|
116
|
+
// stdout vazio ou não-JSON — silêncio, ou crash
|
|
117
|
+
}
|
|
118
|
+
return { raw: r, stdout: String(r.stdout || '').trim(), decision, reason };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function editPayload(filePath, oldString, newString) {
|
|
122
|
+
return { tool_name: 'Edit', tool_input: { file_path: filePath, old_string: oldString, new_string: newString } };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const STORY_LIMPA = f([
|
|
126
|
+
'## Status', '', 'Draft', '', '## Acceptance Criteria', '',
|
|
127
|
+
'**AC-Y.** descricao', '```oraculo', 'test -f package.json', '```', '',
|
|
128
|
+
]);
|
|
129
|
+
|
|
130
|
+
const STORY_COM_P8 = f([
|
|
131
|
+
'## Status', '', 'Draft', '', '## Acceptance Criteria', '',
|
|
132
|
+
'**AC-X.** descricao', '```oraculo', 'grep -c zzz-termo-inexistente-xyz package.json', '```', '',
|
|
133
|
+
]);
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Instrumento de mutação — falha dura se o marcador sumir do fonte real.
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
function mutate(source, marcador, substituto, nome) {
|
|
140
|
+
if (!source.includes(marcador)) {
|
|
141
|
+
throw new Error(`instrumento de mutação desalinhado com o fonte real do hook (mutante ${nome}) — este teste não é prova válida`);
|
|
142
|
+
}
|
|
143
|
+
return source.split(marcador).join(substituto);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const MARCADOR_EMISSAO = " if (!hits || hits.length === 0) return;\n emitDecision('ask', buildHitsMessage(filePath, hits));";
|
|
147
|
+
const MARCADOR_ORCAMENTO = ' if (budgetExceeded(startedAtMs, now())) {';
|
|
148
|
+
const MARCADOR_WRAPPER = 'function main() {\n runWithFailSafe(() => {';
|
|
149
|
+
const MARCADOR_LOAD_VALIDATOR = [
|
|
150
|
+
'function loadValidator(repoRoot) {',
|
|
151
|
+
" const p = path.join(repoRoot, 'scripts', 'validate-story-acs.cjs');",
|
|
152
|
+
' try {',
|
|
153
|
+
' return { ok: true, mod: require(p), path: p };',
|
|
154
|
+
' } catch (err) {',
|
|
155
|
+
' return { ok: false, error: err, path: p };',
|
|
156
|
+
' }',
|
|
157
|
+
'}',
|
|
158
|
+
].join('\n');
|
|
159
|
+
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
// Bloco A — detecção da transição, sem eleição (AC-1, AC-2, AC-2b)
|
|
162
|
+
// ---------------------------------------------------------------------------
|
|
163
|
+
|
|
164
|
+
function testDeteccaoDaTransicao() {
|
|
165
|
+
console.log('\n--- Bloco A: detecção da transição (AC-1, AC-2, AC-2b) ---');
|
|
166
|
+
|
|
167
|
+
const dir = mkTmp('ero-ac1-');
|
|
168
|
+
const p = path.join(dir, 'x.story.md');
|
|
169
|
+
const mk = (s) => `## Status\n\n${s}\n\n## Acceptance Criteria\n`;
|
|
170
|
+
|
|
171
|
+
// AC-1 — 6 casos. Os 2 últimos (`Ready`→`Ready`, `InReview`→`Ready`) são os
|
|
172
|
+
// que o `*review` mediu faltando: sem eles, `after === 'ready'` (que ignora
|
|
173
|
+
// o "antes") passa, e o hook vira guard que interrompe TODA edição de uma
|
|
174
|
+
// story já `Ready`.
|
|
175
|
+
fs.writeFileSync(p, mk('Draft'));
|
|
176
|
+
const r1 = hook.detectDraftToReady(editPayload(p, 'Draft', 'Ready'), cycleAuthority);
|
|
177
|
+
const r2 = hook.detectDraftToReady(editPayload(p, 'Draft', 'Draft'), cycleAuthority);
|
|
178
|
+
fs.writeFileSync(p, mk('Ready'));
|
|
179
|
+
const r3 = hook.detectDraftToReady(editPayload(p, 'Ready', 'Done'), cycleAuthority);
|
|
180
|
+
fs.writeFileSync(p, mk('Draft'));
|
|
181
|
+
const r4 = hook.detectDraftToReady(editPayload(p, 'Draft', 'InReview'), cycleAuthority);
|
|
182
|
+
fs.writeFileSync(p, mk('Ready'));
|
|
183
|
+
const r5 = hook.detectDraftToReady(editPayload(p, 'nota qualquer', 'nota editada'), cycleAuthority);
|
|
184
|
+
fs.writeFileSync(p, mk('InReview'));
|
|
185
|
+
const r6 = hook.detectDraftToReady(editPayload(p, 'InReview', 'Ready'), cycleAuthority);
|
|
186
|
+
|
|
187
|
+
assert(r1 === true, 'AC-1: Draft→Ready ⇒ verdadeiro');
|
|
188
|
+
assert(r2 !== true, 'AC-1: Draft→Draft ⇒ falso');
|
|
189
|
+
assert(r3 !== true, 'AC-1: Ready→Done ⇒ falso');
|
|
190
|
+
assert(r4 !== true, 'AC-1: Draft→InReview ⇒ falso');
|
|
191
|
+
assert(r5 !== true, 'AC-1: edição numa story Ready que nem toca ## Status ⇒ falso');
|
|
192
|
+
assert(r6 !== true, 'AC-1: InReview→Ready ⇒ falso (o "antes" tem de ser Draft)');
|
|
193
|
+
|
|
194
|
+
// AC-2 — ordem: o gate por TRANSIÇÃO vem antes do gate por CONTEÚDO.
|
|
195
|
+
// Roda "no lugar" de propósito: esta rota sai antes de precisar do validador.
|
|
196
|
+
const dir2 = mkTmp('ero-ac2-');
|
|
197
|
+
const storyReady = writeStory(dir2, 'x.story.md', f([
|
|
198
|
+
'## Status', '', 'Ready', '', '## Acceptance Criteria', '',
|
|
199
|
+
'**AC-X.** d', '```oraculo', 'grep -c zzz-termo-inexistente-xyz package.json', '```',
|
|
200
|
+
]));
|
|
201
|
+
const rAc2 = runHook(HOOK_SRC, editPayload(storyReady, 'Ready', 'Done'));
|
|
202
|
+
assert(
|
|
203
|
+
rAc2.raw.status === 0 && rAc2.stdout === '',
|
|
204
|
+
'AC-2: Ready→Done sobre story com AC no-op ⇒ silêncio (predicados nem são avaliados)',
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
// AC-2b — a rota real, de ponta a ponta, com controle negativo pareado.
|
|
208
|
+
const inst = mkInstalledCopy();
|
|
209
|
+
const pSujo = writeStory(inst, 'dirty.story.md', STORY_COM_P8);
|
|
210
|
+
const rSujo = runHook(hookPathOf(inst), editPayload(pSujo, 'Draft', 'Ready'));
|
|
211
|
+
assert(rSujo.decision === 'ask', 'AC-2b: Draft→Ready sobre story com P8 ⇒ ask (a trava interrompe de fato)');
|
|
212
|
+
assert(/P8/.test(rSujo.reason), 'AC-2b: a mensagem do ask cita o predicado P8');
|
|
213
|
+
assert(/pode ser no-op/i.test(rSujo.reason), 'AC-2b: a mensagem reproduz o hedge do instrumento, não afirma mais que ele');
|
|
214
|
+
|
|
215
|
+
const pLimpo = writeStory(inst, 'clean.story.md', STORY_LIMPA);
|
|
216
|
+
const rLimpo = runHook(hookPathOf(inst), editPayload(pLimpo, 'Draft', 'Ready'));
|
|
217
|
+
assert(rLimpo.stdout === '', 'AC-2b (controle negativo): a MESMA rota sobre story limpa ⇒ silêncio (não é guard que sempre dispara)');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// Bloco B — silêncio o mais cedo possível (AC-3, AC-4)
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
|
|
224
|
+
function testSilencioCedo() {
|
|
225
|
+
console.log('\n--- Bloco B: silêncio cedo (AC-3, AC-4) ---');
|
|
226
|
+
|
|
227
|
+
const rBash = runHook(HOOK_SRC, { tool_name: 'Bash', tool_input: { command: 'echo oi' } });
|
|
228
|
+
assert(rBash.raw.status === 0 && rBash.stdout === '', 'AC-3: tool_name fora de Edit|Write ⇒ silêncio total');
|
|
229
|
+
|
|
230
|
+
const dir = mkTmp('ero-ac4-');
|
|
231
|
+
const p = writeStory(dir, 'nota.md', '## Status\n\nDraft\n');
|
|
232
|
+
const rNota = runHook(HOOK_SRC, editPayload(p, 'Draft', 'Ready'));
|
|
233
|
+
assert(rNota.raw.status === 0 && rNota.stdout === '', 'AC-4: file_path que não casa \\.story\\.md$ ⇒ silêncio total');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
// Bloco C — fonte única (AC-5, AC-5b, AC-6, AC-6b)
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
function testFonteUnica() {
|
|
241
|
+
console.log('\n--- Bloco C: fonte única e pureza (AC-5, AC-5b, AC-6, AC-6b) ---');
|
|
242
|
+
|
|
243
|
+
const src = fs.readFileSync(HOOK_SRC, 'utf8');
|
|
244
|
+
|
|
245
|
+
// AC-5/AC-6 — tripwires de contagem. Guardas de regressão, não provas
|
|
246
|
+
// sozinhas: só pegam cópia com o MESMO nome. AC-5b é a prova forte.
|
|
247
|
+
// SF13.9/N-5: `statusToken`/`doneEvidence` entraram na lista. Medido pelo
|
|
248
|
+
// revisor: sem elas, uma 2ª implementação DESSES nomes dentro deste hook
|
|
249
|
+
// passava despercebida (`npm test` continuava verde) — e são justamente as
|
|
250
|
+
// funções que a SF13.9 fez este hook consumir do irmão.
|
|
251
|
+
const redefineIrmao = /function\s+(simulateResult|extractStatusSection|normalizeStatusText|statusToken|doneEvidence)\b/.test(src);
|
|
252
|
+
assert(!redefineIrmao, 'AC-5: o hook não redefine simulateResult/extractStatusSection/normalizeStatusText/statusToken/doneEvidence');
|
|
253
|
+
const redefineValidador = /function\s+(parseAcs|wouldBeRefused|looksLikeNaiveCounting|parseEspera|extractAcSection)\b/.test(src);
|
|
254
|
+
assert(!redefineValidador, 'AC-6: o hook não redefine parseAcs/wouldBeRefused/looksLikeNaiveCounting/parseEspera/extractAcSection');
|
|
255
|
+
assert(/enforce-cycle-authority/.test(src), 'AC-5 (pareado): o hook referencia o irmão por path, isto é, o carrega em vez de copiá-lo');
|
|
256
|
+
assert(/validate-story-acs/.test(src), 'AC-6 (pareado): o hook referencia o validador por path');
|
|
257
|
+
|
|
258
|
+
// AC-6b — a pureza é ESTRUTURAL. Se algum predicado passar a executar, esta
|
|
259
|
+
// asserção reprova sozinha. Leitura honesta (não-bloqueante 2 do `*review`):
|
|
260
|
+
// o tripwire pega a forma ingênua; a prova real da pureza é o AC-2b.
|
|
261
|
+
const semSubprocesso = !/child_process|spawnSync|execSync|execFile|\.runOracle\(|\.validateStory\(/.test(src);
|
|
262
|
+
assert(semSubprocesso, 'AC-6b: o fonte do hook não contém nenhuma forma de subprocesso (hook PURO)');
|
|
263
|
+
// GATILHO DA PROMOÇÃO (`ADR-035` D6). Esta é a linha que quem promover a
|
|
264
|
+
// trava de `Ready` a `deny` é OBRIGADO a editar — por isso a condição de
|
|
265
|
+
// entrada mora aqui, na mensagem, e não numa disciplina que alguém precise
|
|
266
|
+
// lembrar. A CONDIÇÃO booleana abaixo é byte-idêntica à original: só o texto
|
|
267
|
+
// mudou (D6/D8 — o hook e a lógica do teste ficam intocados).
|
|
268
|
+
assert(!/'deny'/.test(src), "AC-6b: o fonte do hook não contém o literal 'deny' — negar é estruturalmente impossível. GATILHO DA PROMOÇÃO (ADR-035 D6): apagar ou afrouxar esta asserção É promover a trava de Ready de ask para deny, e a condição de entrada é D-Dono-2 (ADR-032), CONJUNTIVA — (i) 10 transições reais Draft→Ready registradas com a v1 E (ii) parseGuarda consertado. (ii) está PAGA (SF13.7). (i) se mede com: node scripts/derive-ready-transitions.cjs . --rev 6ef33f90 --since d6ae744 (instrumento offline, exit 0 sempre, ADR-035; leia total= e desde= e decida — nenhuma automação promove por você).");
|
|
269
|
+
|
|
270
|
+
// AC-5b — irmão ausente ⇒ falha DECLARADA, nunca silêncio nem crash mudo.
|
|
271
|
+
const semIrmao = mkInstalledCopy({ sibling: false });
|
|
272
|
+
const p1 = writeStory(semIrmao, 'x.story.md', '## Status\n\nDraft\n\n## Acceptance Criteria\n');
|
|
273
|
+
const rSemIrmao = runHook(hookPathOf(semIrmao), editPayload(p1, 'Draft', 'Ready'));
|
|
274
|
+
assert(rSemIrmao.decision === 'ask', 'AC-5b: irmão enforce-cycle-authority.cjs ausente ⇒ ask (5ª rota, Emenda 2)');
|
|
275
|
+
assert(
|
|
276
|
+
/enforce-cycle-authority/.test(rSemIrmao.reason),
|
|
277
|
+
'AC-5b: a mensagem NOMEIA a dependência ausente (falha declarada, não genérica)',
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// Controle negativo pareado, na MESMA rodada (fecha o não-bloqueante 3 do
|
|
281
|
+
// `*review`): a MESMA fixture COM o irmão presente e story limpa ⇒ silêncio.
|
|
282
|
+
const comIrmao = mkInstalledCopy();
|
|
283
|
+
const p2 = writeStory(comIrmao, 'x.story.md', STORY_LIMPA);
|
|
284
|
+
const rComIrmao = runHook(hookPathOf(comIrmao), editPayload(p2, 'Draft', 'Ready'));
|
|
285
|
+
assert(rComIrmao.stdout === '', 'AC-5b (controle negativo): irmão presente + story limpa ⇒ silêncio');
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ---------------------------------------------------------------------------
|
|
289
|
+
// Bloco D — os 5 predicados puros (AC-7 a AC-11)
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
function testPredicados() {
|
|
293
|
+
console.log('\n--- Bloco D: os 5 predicados puros (AC-7 a AC-11) ---');
|
|
294
|
+
|
|
295
|
+
// AC-7 / P1 — fence órfã.
|
|
296
|
+
const p1Pos = f([
|
|
297
|
+
'## Acceptance Criteria', '',
|
|
298
|
+
'**AC1.** header sem hifen (fora da convencao)', '```oraculo', 'test -f package.json', '```', '',
|
|
299
|
+
'**AC-1.** header correto, mesma fence', '```oraculo', 'test -f package.json', '```',
|
|
300
|
+
]);
|
|
301
|
+
const p1Neg = f(['## Acceptance Criteria', '', '**AC-1.** so a forma correta', '```oraculo', 'test -f package.json', '```']);
|
|
302
|
+
const orfaPos = hook.evaluateFenceOrfa(p1Pos, validator);
|
|
303
|
+
assert(orfaPos && orfaPos.predicate === 'P1', 'AC-7: 2 fences / 1 AC atribuído ⇒ hit P1');
|
|
304
|
+
assert(hook.evaluateFenceOrfa(p1Neg, validator) === null, 'AC-7 (negativo): 1 fence / 1 AC ⇒ sem hit');
|
|
305
|
+
|
|
306
|
+
const semFence = f(['## Acceptance Criteria', '', '**AC-1.** sem oráculo nenhum']);
|
|
307
|
+
assert(hook.evaluateFenceOrfa(semFence, validator) === null, 'AC-7 (fronteira): fences === 0 ⇒ silêncio declarado, fora do alcance');
|
|
308
|
+
|
|
309
|
+
const casos = [
|
|
310
|
+
{
|
|
311
|
+
ac: 'AC-8', pred: 'P4',
|
|
312
|
+
pos: ['# espera: stdout-vazia', 'test -f package.json'],
|
|
313
|
+
neg: ['test -f package.json'],
|
|
314
|
+
rotulo: '# espera: não reconhecido',
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
ac: 'AC-9', pred: 'P5',
|
|
318
|
+
pos: [],
|
|
319
|
+
neg: ['test -f package.json'],
|
|
320
|
+
rotulo: 'fence vazia',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
ac: 'AC-10', pred: 'P6',
|
|
324
|
+
pos: ['awk BEGIN{print 1}'],
|
|
325
|
+
neg: ['test -f package.json'],
|
|
326
|
+
rotulo: 'oráculo recusado pela allow-list',
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
ac: 'AC-11', pred: 'P8',
|
|
330
|
+
pos: ['grep -c zzz-termo-inexistente-xyz package.json'],
|
|
331
|
+
neg: ['# espera: stdout-nao-vazio', 'grep -c zzz-termo-inexistente-xyz package.json'],
|
|
332
|
+
rotulo: 'contagem ingênua sob exit0',
|
|
333
|
+
},
|
|
334
|
+
];
|
|
335
|
+
|
|
336
|
+
for (const c of casos) {
|
|
337
|
+
const conteudoPos = f(['## Acceptance Criteria', '', '**AC-X.** d', '```oraculo', ...c.pos, '```']);
|
|
338
|
+
const conteudoNeg = f(['## Acceptance Criteria', '', '**AC-X.** d', '```oraculo', ...c.neg, '```']);
|
|
339
|
+
const hitsPos = hook.evaluatePerAcPredicates(conteudoPos, validator);
|
|
340
|
+
const hitsNeg = hook.evaluatePerAcPredicates(conteudoNeg, validator);
|
|
341
|
+
const achado = hitsPos.find((h) => h.predicate === c.pred && h.id === 'AC-X');
|
|
342
|
+
assert(Boolean(achado), `${c.ac}: ${c.rotulo} ⇒ hit ${c.pred} no AC-X`);
|
|
343
|
+
assert(!hitsNeg.some((h) => h.predicate === c.pred), `${c.ac} (negativo): forma correta ⇒ sem hit ${c.pred}`);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// AC-11 — o hedge é normativo: a mensagem nunca afirma mais que o instrumento.
|
|
347
|
+
const p8 = hook.evaluatePerAcPredicates(
|
|
348
|
+
f(['## Acceptance Criteria', '', '**AC-X.** d', '```oraculo', 'grep -c zzz package.json', '```']),
|
|
349
|
+
validator,
|
|
350
|
+
).find((h) => h.predicate === 'P8');
|
|
351
|
+
assert(p8 && /pode ser no-op/i.test(p8.detail), 'AC-11: o detalhe do P8 reproduz o hedge do instrumento ("pode ser no-op")');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
// Bloco E — os corpus reais (AC-12 a AC-15)
|
|
356
|
+
// ---------------------------------------------------------------------------
|
|
357
|
+
|
|
358
|
+
function testCorpusReais() {
|
|
359
|
+
console.log('\n--- Bloco E: corpus reais (AC-12 a AC-15) ---');
|
|
360
|
+
|
|
361
|
+
const lerStory = (nome) => fs.readFileSync(path.join(REPO_ROOT, 'docs', 'stories', nome), 'utf8');
|
|
362
|
+
|
|
363
|
+
// Corpus A — `sf13.1.story.md` está `Done` em `main` e não deve mais mudar.
|
|
364
|
+
// Se algum fix futuro a editar, esta medição precisa ser refeita (mesma
|
|
365
|
+
// disciplina de "medição datada" do `ADR-032`).
|
|
366
|
+
const hitsA = hook.evaluateReadyOracles(lerStory('sf13.1.story.md'), validator);
|
|
367
|
+
const p8Ids = hitsA.filter((h) => h.predicate === 'P8').map((h) => h.id).sort();
|
|
368
|
+
const outros = hitsA.filter((h) => h.predicate !== 'P8');
|
|
369
|
+
assert(
|
|
370
|
+
JSON.stringify(p8Ids) === JSON.stringify(['AC-2', 'AC-23', 'AC-30', 'AC-31']),
|
|
371
|
+
'AC-12 (Corpus A): sf13.1 dispara exatamente os 4 P8 verdadeiros',
|
|
372
|
+
);
|
|
373
|
+
assert(outros.length === 0, 'AC-12 (Corpus A): nenhum P1/P4/P5/P6 e nenhum falso positivo da família [Guarda] (fora da v1)');
|
|
374
|
+
|
|
375
|
+
assert(hook.evaluateReadyOracles(lerStory('sf12.6.story.md'), validator).length === 0,
|
|
376
|
+
'AC-13 (Corpus B): sf12.6 (37 oráculos, convenção correta) ⇒ zero hits');
|
|
377
|
+
assert(hook.evaluateReadyOracles(lerStory('sf11.7.story.md'), validator).length === 0,
|
|
378
|
+
'AC-14 (Corpus C): sf11.7 (sem bloco oraculo) ⇒ zero hits, por fences === 0, não por leitura de relatório');
|
|
379
|
+
|
|
380
|
+
// AC-15 (Corpus D) — RECOMBINADO, com procedência: cabeçalho sem hífen real
|
|
381
|
+
// (`sf12.3.story.md:195`) + fence real (`sf13.1.story.md:159-161`). Nenhuma
|
|
382
|
+
// story real tem as duas coisas juntas hoje — é recombinação, não invenção.
|
|
383
|
+
const corpusD = f([
|
|
384
|
+
'## Acceptance Criteria', '',
|
|
385
|
+
'**AC1. [REESCRITO — fix round, *review 2026-08-06]** header da convencao antiga (sf12.3.story.md:195)',
|
|
386
|
+
'```oraculo', 'test -f docs/mecanismos/validador-de-acs-do-ciclo.md', '```', '',
|
|
387
|
+
'**AC-1.** header corrigido, mesma fence (sf13.1.story.md:159-161)',
|
|
388
|
+
'```oraculo', 'test -f docs/mecanismos/validador-de-acs-do-ciclo.md', '```',
|
|
389
|
+
]);
|
|
390
|
+
const corpusDNeg = f([
|
|
391
|
+
'## Acceptance Criteria', '', '**AC-1.** so a forma correta',
|
|
392
|
+
'```oraculo', 'test -f docs/mecanismos/validador-de-acs-do-ciclo.md', '```',
|
|
393
|
+
]);
|
|
394
|
+
const dHit = hook.evaluateFenceOrfa(corpusD, validator);
|
|
395
|
+
assert(dHit && dHit.predicate === 'P1', 'AC-15 (Corpus D): cabeçalho fora da convenção + fence real ⇒ P1');
|
|
396
|
+
assert(hook.evaluateFenceOrfa(corpusDNeg, validator) === null, 'AC-15 (Corpus D, negativo): a MESMA fence sob cabeçalho correto ⇒ sem hit');
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ---------------------------------------------------------------------------
|
|
400
|
+
// Bloco F — as rotas de fail-safe (AC-16 a AC-19)
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
|
|
403
|
+
function testFailSafe() {
|
|
404
|
+
console.log('\n--- Bloco F: rotas de fail-safe (AC-16 a AC-19) ---');
|
|
405
|
+
|
|
406
|
+
const src = fs.readFileSync(HOOK_SRC, 'utf8');
|
|
407
|
+
|
|
408
|
+
// AC-16 (Corpus E) — validador ausente, real.
|
|
409
|
+
const semValidador = mkInstalledCopy({ validator: false });
|
|
410
|
+
const pv = writeStory(semValidador, 'x.story.md', '## Status\n\nDraft\n\n## Acceptance Criteria\n');
|
|
411
|
+
const rReal = runHook(hookPathOf(semValidador), editPayload(pv, 'Draft', 'Ready'));
|
|
412
|
+
assert(rReal.decision === 'ask', 'AC-16: validador ausente ⇒ ask (nunca silêncio)');
|
|
413
|
+
assert(
|
|
414
|
+
/scripts\/validate-story-acs\.cjs ausente/.test(rReal.reason),
|
|
415
|
+
'AC-16: a mensagem é ACIONÁVEL — nomeia o arquivo ausente e o comando de conserto',
|
|
416
|
+
);
|
|
417
|
+
|
|
418
|
+
// AC-16, metade MUTANTE — `loadValidator` sem a captura própria. A defesa em
|
|
419
|
+
// profundidade (`runWithFailSafe` externo) ainda produz `ask`, mas a mensagem
|
|
420
|
+
// vira GENÉRICA: prova que a mensagem acionável depende da captura
|
|
421
|
+
// específica, e não é subproduto da rede externa.
|
|
422
|
+
const srcSemCaptura = mutate(
|
|
423
|
+
src,
|
|
424
|
+
MARCADOR_LOAD_VALIDATOR,
|
|
425
|
+
"function loadValidator(repoRoot) {\n const p = path.join(repoRoot, 'scripts', 'validate-story-acs.cjs');\n return { ok: true, mod: require(p), path: p }; // MUTANTE\n}",
|
|
426
|
+
'loadValidator sem captura',
|
|
427
|
+
);
|
|
428
|
+
const mutA = mkInstalledCopy({ validator: false, hookSource: srcSemCaptura });
|
|
429
|
+
const pmA = writeStory(mutA, 'x.story.md', '## Status\n\nDraft\n\n## Acceptance Criteria\n');
|
|
430
|
+
const rMutA = runHook(hookPathOf(mutA), editPayload(pmA, 'Draft', 'Ready'));
|
|
431
|
+
assert(rMutA.decision === 'ask', 'AC-16 (mutante): sem a captura de loadValidator, a rede externa ainda produz ask');
|
|
432
|
+
assert(
|
|
433
|
+
/lan[cç]ou uma exce[cç][aã]o/.test(rMutA.reason) && !/scripts\/validate-story-acs\.cjs ausente/.test(rMutA.reason),
|
|
434
|
+
'AC-16 (mutante): a mensagem degrada para GENÉRICA — a acionável depende da captura específica',
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// MUTANTE M2 — `main()` desembrulhado do `runWithFailSafe`, sobre o mesmo
|
|
438
|
+
// cenário do mutante acima: agora NADA é emitido. É a medição que prova qual
|
|
439
|
+
// camada produz o `ask` — e é a classe de defeito que esta story existe para
|
|
440
|
+
// matar (hook que lança não emite decisão e a ferramenta passa).
|
|
441
|
+
// A substituição troca `runWithFailSafe` por um executor IDENTIDADE — invoca
|
|
442
|
+
// a mesma função, sem try/catch. Trocar por `(() => {` sem invocar seria um
|
|
443
|
+
// mutante VAZIO (a arrow nunca rodaria, nada lançaria, e o teste passaria
|
|
444
|
+
// por não executar o código — falso conforto, não medição).
|
|
445
|
+
const srcSemRede = mutate(srcSemCaptura, MARCADOR_WRAPPER, 'function main() {\n ((fn) => fn())(() => {', 'main sem runWithFailSafe');
|
|
446
|
+
const mutB = mkInstalledCopy({ validator: false, hookSource: srcSemRede });
|
|
447
|
+
const pmB = writeStory(mutB, 'x.story.md', '## Status\n\nDraft\n\n## Acceptance Criteria\n');
|
|
448
|
+
const rMutB = runHook(hookPathOf(mutB), editPayload(pmB, 'Draft', 'Ready'));
|
|
449
|
+
assert(
|
|
450
|
+
rMutB.stdout === '' && rMutB.raw.status !== 0,
|
|
451
|
+
'MUTANTE M2: sem runWithFailSafe em main(), o hook morre MUDO — nenhuma decisão emitida (fail-open silencioso)',
|
|
452
|
+
);
|
|
453
|
+
|
|
454
|
+
// AC-17 — o fluxo REAL protegido, e o controle negativo pelo MESMO caminho.
|
|
455
|
+
const inst = mkInstalledCopy();
|
|
456
|
+
const p17 = writeStory(inst, 'x.story.md', '## Status\n\nDraft\n\n## Acceptance Criteria\n');
|
|
457
|
+
const h17 = require(hookPathOf(inst));
|
|
458
|
+
const input17 = editPayload(p17, 'Draft', 'Ready');
|
|
459
|
+
const explode = () => { throw new Error('boom-ac17'); };
|
|
460
|
+
|
|
461
|
+
let asked = null;
|
|
462
|
+
const origWrite = process.stdout.write.bind(process.stdout);
|
|
463
|
+
process.stdout.write = (s) => {
|
|
464
|
+
try {
|
|
465
|
+
asked = JSON.parse(s).hookSpecificOutput.permissionDecision;
|
|
466
|
+
} catch { /* saída não-JSON */ }
|
|
467
|
+
return true;
|
|
468
|
+
};
|
|
469
|
+
h17.runWithFailSafe(() => h17.runMain(input17, { evaluateReadyOracles: explode }));
|
|
470
|
+
process.stdout.write = origWrite;
|
|
471
|
+
|
|
472
|
+
let propagou = false;
|
|
473
|
+
try {
|
|
474
|
+
h17.runMain(input17, { evaluateReadyOracles: explode });
|
|
475
|
+
} catch {
|
|
476
|
+
propagou = true;
|
|
477
|
+
}
|
|
478
|
+
assert(asked === 'ask', 'AC-17: etapa genuína de runMain lançando ⇒ runWithFailSafe emite ask');
|
|
479
|
+
assert(propagou === true, 'AC-17 (controle negativo): o MESMO runMain sem o wrapper propaga a exceção — é o wrapper que protege');
|
|
480
|
+
|
|
481
|
+
// AC-18 — orçamento, função pura.
|
|
482
|
+
assert(hook.budgetExceeded(1000, 3001) === true, 'AC-18: 2001 ms > 2000 ⇒ estourado');
|
|
483
|
+
assert(hook.budgetExceeded(1000, 2999) === false, 'AC-18: 1999 ms ⇒ não estourado');
|
|
484
|
+
|
|
485
|
+
// AC-18b — `runMain` de fato USA o orçamento, e ele vence o silêncio.
|
|
486
|
+
const p18 = writeStory(inst, 'limpa.story.md', STORY_LIMPA);
|
|
487
|
+
const input18 = editPayload(p18, 'Draft', 'Ready');
|
|
488
|
+
let asked18 = null;
|
|
489
|
+
let msg18 = '';
|
|
490
|
+
let chamadas = 0;
|
|
491
|
+
const relogioQueSalta = () => {
|
|
492
|
+
chamadas += 1;
|
|
493
|
+
return chamadas === 1 ? 1000 : 5001;
|
|
494
|
+
};
|
|
495
|
+
process.stdout.write = (s) => {
|
|
496
|
+
try {
|
|
497
|
+
const j = JSON.parse(s);
|
|
498
|
+
asked18 = j.hookSpecificOutput.permissionDecision;
|
|
499
|
+
msg18 = j.hookSpecificOutput.permissionDecisionReason;
|
|
500
|
+
} catch { /* saída não-JSON */ }
|
|
501
|
+
return true;
|
|
502
|
+
};
|
|
503
|
+
h17.runMain(input18, { now: relogioQueSalta });
|
|
504
|
+
process.stdout.write = origWrite;
|
|
505
|
+
assert(asked18 === 'ask', 'AC-18b: orçamento estourado ⇒ runMain emite ask MESMO sobre story limpa');
|
|
506
|
+
assert(/2000/.test(msg18), 'AC-18b: a mensagem nomeia o orçamento de 2000 ms');
|
|
507
|
+
|
|
508
|
+
// Controle negativo do AC-18b: relógio parado ⇒ a MESMA story limpa cala.
|
|
509
|
+
let asked18neg = null;
|
|
510
|
+
process.stdout.write = (s) => {
|
|
511
|
+
try {
|
|
512
|
+
asked18neg = JSON.parse(s).hookSpecificOutput.permissionDecision;
|
|
513
|
+
} catch { /* saída não-JSON */ }
|
|
514
|
+
return true;
|
|
515
|
+
};
|
|
516
|
+
h17.runMain(input18, { now: () => 1000 });
|
|
517
|
+
process.stdout.write = origWrite;
|
|
518
|
+
assert(asked18neg === null, 'AC-18b (controle negativo): sem estouro, a MESMA story limpa ⇒ silêncio');
|
|
519
|
+
|
|
520
|
+
// AC-19 — JSON ilegível, fail-closed PROPORCIONAL.
|
|
521
|
+
const rRuidoRelevante = runHook(HOOK_SRC, '{ nao e json valido, mas fala de Status e Ready');
|
|
522
|
+
const rRuidoIrrelevante = runHook(HOOK_SRC, '{ nao e json valido, garbage total sem nada relevante');
|
|
523
|
+
assert(rRuidoRelevante.decision === 'ask', 'AC-19: texto bruto ilegível aparentando Status/Ready ⇒ ask');
|
|
524
|
+
assert(rRuidoIrrelevante.stdout === '', 'AC-19: texto bruto ilegível sem esses termos ⇒ silêncio total');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ---------------------------------------------------------------------------
|
|
528
|
+
// Mutantes da TRAVA e do ORÇAMENTO — os dois que sobreviviam a 18/18 e 19/19.
|
|
529
|
+
// ---------------------------------------------------------------------------
|
|
530
|
+
|
|
531
|
+
function testMutantesDaTrava() {
|
|
532
|
+
console.log('\n--- Mutantes de classe: a trava e o orçamento ---');
|
|
533
|
+
|
|
534
|
+
const src = fs.readFileSync(HOOK_SRC, 'utf8');
|
|
535
|
+
|
|
536
|
+
// M6 — a emissão por predicado arrancada de `runMain`. O `*review` mediu
|
|
537
|
+
// 18/18 ACs sobrevivendo a este mutante antes do AC-2b existir.
|
|
538
|
+
const srcSemTrava = mutate(
|
|
539
|
+
src,
|
|
540
|
+
MARCADOR_EMISSAO,
|
|
541
|
+
' if (!hits || hits.length === 0) return;\n return; // MUTANTE M6',
|
|
542
|
+
'M6 trava arrancada',
|
|
543
|
+
);
|
|
544
|
+
const m6 = mkInstalledCopy({ hookSource: srcSemTrava });
|
|
545
|
+
const p6 = writeStory(m6, 'dirty.story.md', STORY_COM_P8);
|
|
546
|
+
const r6 = runHook(hookPathOf(m6), editPayload(p6, 'Draft', 'Ready'));
|
|
547
|
+
assert(r6.stdout === '', 'MUTANTE M6: com a trava arrancada o hook fica mudo sobre story com P8 — logo o AC-2b o derruba');
|
|
548
|
+
|
|
549
|
+
// M1 — a checagem de orçamento removida. `*review`: 19/19 sobreviviam.
|
|
550
|
+
const srcSemOrcamento = mutate(src, MARCADOR_ORCAMENTO, ' if (false) { // MUTANTE M1', 'M1 orçamento removido');
|
|
551
|
+
const m1dir = mkInstalledCopy({ hookSource: srcSemOrcamento });
|
|
552
|
+
const p1story = writeStory(m1dir, 'limpa.story.md', STORY_LIMPA);
|
|
553
|
+
const h1 = require(hookPathOf(m1dir));
|
|
554
|
+
let asked = null;
|
|
555
|
+
const origWrite = process.stdout.write.bind(process.stdout);
|
|
556
|
+
let chamadas = 0;
|
|
557
|
+
process.stdout.write = (s) => {
|
|
558
|
+
try {
|
|
559
|
+
asked = JSON.parse(s).hookSpecificOutput.permissionDecision;
|
|
560
|
+
} catch { /* saída não-JSON */ }
|
|
561
|
+
return true;
|
|
562
|
+
};
|
|
563
|
+
h1.runMain(editPayload(p1story, 'Draft', 'Ready'), {
|
|
564
|
+
now: () => {
|
|
565
|
+
chamadas += 1;
|
|
566
|
+
return chamadas === 1 ? 1000 : 5001;
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
process.stdout.write = origWrite;
|
|
570
|
+
assert(asked === null, 'MUTANTE M1: sem a checagem de orçamento, o estouro passa em silêncio — logo o AC-18b o derruba');
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// ---------------------------------------------------------------------------
|
|
574
|
+
// SF13.9 — a transição passa a ser detectada por TOKEN; a superfície de export
|
|
575
|
+
// do irmão é CHECADA (não presumida); P2/P3/P7 entram na escada de 2 degraus.
|
|
576
|
+
// ---------------------------------------------------------------------------
|
|
577
|
+
|
|
578
|
+
function testTransicaoPorToken() {
|
|
579
|
+
console.log('\n--- SF13.9: detectDraftToReady por TOKEN, nos dois lados ---');
|
|
580
|
+
|
|
581
|
+
const dir = mkTmp('ero-sf139-tok-');
|
|
582
|
+
const p = path.join(dir, 'z.story.md');
|
|
583
|
+
function det(antes, depois) {
|
|
584
|
+
fs.writeFileSync(p, `## Status\n\n${antes}\n`);
|
|
585
|
+
return hook.detectDraftToReady(
|
|
586
|
+
{ tool_name: 'Write', tool_input: { file_path: p, content: `## Status\n\n${depois}\n` } },
|
|
587
|
+
cycleAuthority,
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// POSITIVO — o defeito de FINDING-8, nos DOIS lados de uma vez.
|
|
592
|
+
assert(det('Draft (aguardando PO)', 'Ready (validado pelo PO)') === true, 'SF13.9: "Draft (aguardando PO)"→"Ready (validado pelo PO)" DISPARA (a igualdade estrita ficava muda nos dois lados)');
|
|
593
|
+
assert(det('Draft', 'Ready — aguardando dev') === true, 'SF13.9: prosa com travessão no lado `depois` dispara');
|
|
594
|
+
assert(det('Draft', '`Ready`') === true, 'SF13.9: decoração de crase no lado `depois` dispara');
|
|
595
|
+
|
|
596
|
+
// NEGATIVOS pareados — os 3 primeiros são regressão que NÃO pode quebrar.
|
|
597
|
+
assert(det('Ready', 'Ready') === false, 'SF13.9/regressão: Ready→Ready NÃO dispara (senão o PO é interrompido a cada escrita numa story já Ready)');
|
|
598
|
+
assert(det('InReview (fix round 1)', 'Ready') === false, 'SF13.9/regressão: InReview→Ready NÃO dispara (não vem de Draft)');
|
|
599
|
+
assert(det('Draft', 'Not Ready') === false, 'SF13.9: Draft→"Not Ready" NÃO dispara — negação nunca dispara');
|
|
600
|
+
assert(det('Draft', '~~Ready~~') === false, 'SF13.9: Draft→"~~Ready~~" NÃO dispara — falso negativo DECLARADO e aceito (gatilho T-E3-RISCADO), preço de manter `~` fora da decoração por causa do deny do irmão');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function testSkewDoIrmao() {
|
|
604
|
+
console.log('\n--- SF13.9: skew da superfície de export do irmão (construído, não presumido) ---');
|
|
605
|
+
|
|
606
|
+
// O skew é CONSTRUÍDO: a cópia do irmão em tmp perde `statusToken` do bloco
|
|
607
|
+
// de exports. Copiar os arquivos ATUAIS do repo não constrói skew nenhum
|
|
608
|
+
// depois do fix — um AC assim só fica verde com implementação ERRADA
|
|
609
|
+
// (`ADR-032` Emenda 4, E4.3).
|
|
610
|
+
const dir = mkInstalledCopy({});
|
|
611
|
+
const irmao = path.join(dir, '.claude', 'hooks', 'enforce-cycle-authority.cjs');
|
|
612
|
+
const src = fs.readFileSync(irmao, 'utf8');
|
|
613
|
+
const semExport = src.replace(/^\s*statusToken,\s*$/m, '');
|
|
614
|
+
if (semExport === src) throw new Error('instrumento de skew desalinhado: `statusToken,` não está no bloco de exports do irmão — este teste não é prova válida');
|
|
615
|
+
fs.writeFileSync(irmao, semExport);
|
|
616
|
+
assert(typeof require(irmao).statusToken !== 'function', 'pré-condição: a cópia do irmão de fato PERDEU statusToken (skew real, não presumido)');
|
|
617
|
+
|
|
618
|
+
const sp = writeStory(dir, 'x.story.md', '## Status\n\nDraft\n');
|
|
619
|
+
const r = runHook(hookPathOf(dir), { tool_name: 'Write', tool_input: { file_path: sp, content: '## Status\n\nReady\n' }, cwd: dir });
|
|
620
|
+
assert(r.decision === 'ask', 'SF13.9: irmão em SKEW ⇒ ask (o caminho de degradação FALA; silêncio seria indistinguível de "não é a transição")');
|
|
621
|
+
assert(/statusToken/.test(r.reason), 'SF13.9: a razão NOMEIA a função ausente — `ask` genérico nasceria verde e não distinguiria skew de qualquer outra rota');
|
|
622
|
+
|
|
623
|
+
// CONTROLE DE SILÊNCIO, pareado (o par é o que mata a implementação que
|
|
624
|
+
// "fala em toda transição" para deixar o teste de skew verde): irmão
|
|
625
|
+
// ÍNTEGRO + story sem defeito de forma ⇒ stdout VAZIO, nas duas redações do
|
|
626
|
+
// Status. É a propriedade central da trava (`DF-SF13.2-1`).
|
|
627
|
+
const limpo = mkInstalledCopy({});
|
|
628
|
+
const base = f([
|
|
629
|
+
'## Status', '', 'Draft', '', '## Acceptance Criteria', '',
|
|
630
|
+
'**AC-1.** teste', '```oraculo', '# espera: exit0', 'test -f package.json', '```', '',
|
|
631
|
+
]);
|
|
632
|
+
const sp2 = writeStory(limpo, 'x.story.md', base);
|
|
633
|
+
const puro = runHook(hookPathOf(limpo), { tool_name: 'Write', tool_input: { file_path: sp2, content: base.replace('Draft', 'Ready') }, cwd: limpo });
|
|
634
|
+
const anotado = runHook(hookPathOf(limpo), { tool_name: 'Write', tool_input: { file_path: sp2, content: base.replace('Draft', 'Ready (validado pelo PO, 10/10)') }, cwd: limpo });
|
|
635
|
+
assert(puro.stdout === '', 'SF13.9/CONTROLE: irmão íntegro + story limpa, Draft→Ready puro ⇒ SILÊNCIO absoluto');
|
|
636
|
+
assert(anotado.stdout === '', 'SF13.9/CONTROLE: idem com Status anotado ⇒ SILÊNCIO absoluto (falar aqui seria pior que o defeito que a story conserta)');
|
|
637
|
+
|
|
638
|
+
// MUTANTE M12 — a checagem de superfície removida: o skew passa a seguir o
|
|
639
|
+
// caminho feliz e o hook CALA sobre uma story que ele não sabe ler.
|
|
640
|
+
const mutSrc = mutate(
|
|
641
|
+
fs.readFileSync(HOOK_SRC, 'utf8'),
|
|
642
|
+
' const faltando = REQUIRED_SIBLING_EXPORTS.filter((nome) => !mod || typeof mod[nome] !== \'function\');',
|
|
643
|
+
' const faltando = [];',
|
|
644
|
+
'M12',
|
|
645
|
+
);
|
|
646
|
+
const dirM12 = mkInstalledCopy({ hookSource: mutSrc });
|
|
647
|
+
fs.writeFileSync(path.join(dirM12, '.claude', 'hooks', 'enforce-cycle-authority.cjs'), semExport);
|
|
648
|
+
const spM12 = writeStory(dirM12, 'x.story.md', '## Status\n\nDraft\n');
|
|
649
|
+
const rM12 = runHook(hookPathOf(dirM12), { tool_name: 'Write', tool_input: { file_path: spM12, content: '## Status\n\nReady\n' }, cwd: dirM12 });
|
|
650
|
+
assert(rM12.stdout === '', 'MUTANTE M12 (checagem de superfície removida): o skew vira SILÊNCIO — logo o teste de skew acima é quem o mata');
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
function testGuardaP2P3P7() {
|
|
654
|
+
console.log('\n--- SF13.9: P2/P3/P7 ligados ao hook (escada de 2 degraus, ask-only) ---');
|
|
655
|
+
|
|
656
|
+
const comDefeitos = f([
|
|
657
|
+
'## Acceptance Criteria', '',
|
|
658
|
+
'**AC-1.** teste. [Guarda] (banana)', '```oraculo', 'test -f package.json', '```', '',
|
|
659
|
+
'**AC-2.** teste. [Guarda] (prova: AC-INEXISTENTE)', '```oraculo', 'test -f package.json', '```', '',
|
|
660
|
+
'**AC-3.** teste. [Guarda] (sem irmao)', '```oraculo', '# espera: exit0', 'test -f package.json', '```', '',
|
|
661
|
+
]);
|
|
662
|
+
const preds = new Set(hook.evaluateReadyOracles(comDefeitos, validator).map((h) => h.predicate));
|
|
663
|
+
assert(preds.has('P2'), 'SF13.9/P2: `[Guarda] (banana)` — forma não reconhecida — é VISTO pelo hook');
|
|
664
|
+
assert(preds.has('P3'), 'SF13.9/P3: `[Guarda] (prova: AC-INEXISTENTE)` — irmão que não existe — é VISTO pelo hook');
|
|
665
|
+
assert(preds.has('P7'), 'SF13.9/P7: `[Guarda] (sem irmao)` sem asserção de AUSÊNCIA é VISTO pelo hook');
|
|
666
|
+
|
|
667
|
+
// CONTROLE NEGATIVO pareado — as MESMAS 3 formas, aplicadas corretamente,
|
|
668
|
+
// não disparam. Sem este par, "sempre empurrar P2/P3/P7" passaria.
|
|
669
|
+
const semDefeitos = f([
|
|
670
|
+
'## Acceptance Criteria', '',
|
|
671
|
+
'**AC-1.** teste. [Guarda] (prova: AC-2)', '```oraculo', 'test -f package.json', '```', '',
|
|
672
|
+
'**AC-2.** teste', '```oraculo', 'test -f package.json', '```', '',
|
|
673
|
+
'**AC-3.** teste. [Guarda] (sem irmao — ausência declarada)', '```oraculo', '# espera: stdout-vazio', 'grep -w zzz-inexistente package.json', '```', '',
|
|
674
|
+
'**AC-4.** teste. [Guarda] (citação de fato pré-existente, não prova de trabalho)', '```oraculo', 'test -f package.json', '```', '',
|
|
675
|
+
]);
|
|
676
|
+
const limpos = hook.evaluateReadyOracles(semDefeitos, validator).filter((h) => ['P2', 'P3', 'P7'].includes(h.predicate));
|
|
677
|
+
assert(limpos.length === 0, `SF13.9/controle negativo: guardas BEM formados não disparam P2/P3/P7 (disparos: ${limpos.length})`);
|
|
678
|
+
|
|
679
|
+
// A escada continua a de 2 degraus: `ask` e nada mais. A negativa é
|
|
680
|
+
// ESTRUTURAL — o literal da decisão negativa não existe no arquivo (já
|
|
681
|
+
// coberto por AC-6b), e os 3 novos não abrem exceção a isso.
|
|
682
|
+
const dir = mkInstalledCopy({});
|
|
683
|
+
const sp = writeStory(dir, 'x.story.md', f(['## Status', '', 'Draft', '', ...comDefeitos.split('\n')]));
|
|
684
|
+
const r = runHook(hookPathOf(dir), { tool_name: 'Write', tool_input: { file_path: sp, content: f(['## Status', '', 'Ready', '', ...comDefeitos.split('\n')]) }, cwd: dir });
|
|
685
|
+
assert(r.decision === 'ask', 'SF13.9: os 3 predicados novos emitem `ask` pela rota real — nunca `deny` (D-Dono-2 não está pago)');
|
|
686
|
+
assert(/P2|P3|P7/.test(r.reason), 'SF13.9: a razão nomeia os predicados novos');
|
|
687
|
+
|
|
688
|
+
// MUTANTE N-6 — o bloco de `[Guarda]` deslocado para DEPOIS do `continue`
|
|
689
|
+
// de `!hasOraculo`: ACs sem oráculo, mas com guarda malformado, ficam
|
|
690
|
+
// invisíveis em silêncio. A fixture acima (3 ACs, todos COM oráculo) NÃO
|
|
691
|
+
// pegaria isso — por isso a fixture abaixo tem um AC SEM oráculo.
|
|
692
|
+
const semOraculo = f([
|
|
693
|
+
'## Acceptance Criteria', '',
|
|
694
|
+
'**AC-1.** teste sem oraculo nenhum. [Guarda] (banana)', '',
|
|
695
|
+
'**AC-2.** teste', '```oraculo', 'test -f package.json', '```', '',
|
|
696
|
+
]);
|
|
697
|
+
const realP2 = hook.evaluateReadyOracles(semOraculo, validator).filter((h) => h.predicate === 'P2');
|
|
698
|
+
assert(realP2.length === 1, 'SF13.9/N-6: AC SEM oráculo e COM guarda malformado é visto (o bloco roda ANTES do `continue`)');
|
|
699
|
+
|
|
700
|
+
const mutSrc = mutate(fs.readFileSync(HOOK_SRC, 'utf8'), ' if (ac.guarda) {', ' if (ac.guarda && ac.hasOraculo) {', 'N-6');
|
|
701
|
+
const mutPath = path.join(mkTmp('ero-n6-'), 'mutante.cjs');
|
|
702
|
+
fs.writeFileSync(mutPath, mutSrc);
|
|
703
|
+
const mutP2 = require(mutPath).evaluateReadyOracles(semOraculo, validator).filter((h) => h.predicate === 'P2');
|
|
704
|
+
assert(mutP2.length === 0, 'MUTANTE N-6 (bloco depois do `continue`): o mesmo AC fica INVISÍVEL — logo a asserção acima é quem o mata');
|
|
705
|
+
|
|
706
|
+
// Corpus REAL — zero disparo NOVO. Pareado com a prova de que estão ligados
|
|
707
|
+
// (acima): sem esse par, "zero disparo" passaria por vacuidade.
|
|
708
|
+
const dirStories = path.join(REPO_ROOT, 'docs', 'stories');
|
|
709
|
+
const files = fs.readdirSync(dirStories).filter((n) => n.endsWith('.story.md'));
|
|
710
|
+
let novos = 0;
|
|
711
|
+
for (const n of files) {
|
|
712
|
+
const c = fs.readFileSync(path.join(dirStories, n), 'utf8');
|
|
713
|
+
novos += hook.evaluateReadyOracles(c, validator).filter((h) => ['P2', 'P3', 'P7'].includes(h.predicate)).length;
|
|
714
|
+
}
|
|
715
|
+
assert(novos === 0, `SF13.9: zero disparo NOVO de P2/P3/P7 sobre as ${files.length} stories reais (disparos: ${novos})`);
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// ---------------------------------------------------------------------------
|
|
719
|
+
|
|
720
|
+
function main() {
|
|
721
|
+
console.log('POWER CLAUDE — suíte da trava de Ready (enforce-ready-oracles.cjs, SF13.2 + SF13.9)');
|
|
722
|
+
try {
|
|
723
|
+
testDeteccaoDaTransicao();
|
|
724
|
+
testSilencioCedo();
|
|
725
|
+
testFonteUnica();
|
|
726
|
+
testPredicados();
|
|
727
|
+
testCorpusReais();
|
|
728
|
+
testFailSafe();
|
|
729
|
+
testMutantesDaTrava();
|
|
730
|
+
testTransicaoPorToken();
|
|
731
|
+
testSkewDoIrmao();
|
|
732
|
+
testGuardaP2P3P7();
|
|
733
|
+
} finally {
|
|
734
|
+
for (const d of tmpDirs) fs.rmSync(d, { recursive: true, force: true });
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
if (failures > 0) {
|
|
738
|
+
console.error(`\nSuíte FALHOU: ${failures} violação(ões).`);
|
|
739
|
+
process.exit(1);
|
|
740
|
+
}
|
|
741
|
+
console.log('\nSuíte verde.');
|
|
742
|
+
process.exit(0);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
main();
|