@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,755 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* smoke-pod-workflow.cjs — SMOKE DINÂMICO de seguibilidade de workflow (Story SF1.3).
|
|
5
|
+
*
|
|
6
|
+
* Prova que um `wf-*.yaml` "realmente trava o caminho errado" — não só que "está
|
|
7
|
+
* presente e bem-formado" (isso é o validador estático da SF1.1). Fronteira do
|
|
8
|
+
* ADR-002: o estático prova FORMA; este smoke prova FUNCIONAMENTO, DIRIGINDO o
|
|
9
|
+
* runner real `pc-run-pod` (SF1.2) sobre uma tarefa-canário e medindo 4/4:
|
|
10
|
+
*
|
|
11
|
+
* Prova 1 Ativação — cada agente do workflow é despachado e recebe um brief
|
|
12
|
+
* que aponta para um template EXISTENTE (não pede arquivo
|
|
13
|
+
* inexistente); o run percorre todos os passos.
|
|
14
|
+
* Prova 2 Caminho feliz — DAG completo com artefatos conformes → todos os passos
|
|
15
|
+
* `done`, artefato final conforme ao template.
|
|
16
|
+
* Prova 3a Bloqueio template— injeta artefato que viola a FORMA (seção ausente) na
|
|
17
|
+
* etapa-alvo; o runner NÃO avança, `state.json` para no
|
|
18
|
+
* passo, a etapa seguinte nunca roda.
|
|
19
|
+
* Prova 3b Bloqueio veto — injeta artefato conforme à forma mas que viola uma
|
|
20
|
+
* `veto.assertions`; MESMA garantia, caminho de código
|
|
21
|
+
* DISTINTO do 3a (evalVeto vs checkTemplateConformance).
|
|
22
|
+
*
|
|
23
|
+
* 4/4 é ATÔMICO (AC6): qualquer saída errada passando (ex.: um 3a/3b "verde" quando
|
|
24
|
+
* deveria travar) faz o smoke FALHAR por inteiro (exit != 0), nomeando a prova.
|
|
25
|
+
*
|
|
26
|
+
* "Execução real" = runner real (não mock). O papel do SUBAGENTE é plantado
|
|
27
|
+
* deterministicamente em disco pelo harness (mesma técnica da suíte da SF1.2) —
|
|
28
|
+
* logo o smoke do canário custa milissegundos, não tokens (ver Dev Notes da story:
|
|
29
|
+
* roda a cada `npm test`; pods reais SF1.4/SF1.5 rodam no gate G6, ADR-002 dívida).
|
|
30
|
+
*
|
|
31
|
+
* Reuso (AC7 / SF1.4-SF1.5): genérico para QUALQUER wf-*.yaml acompanhado de um
|
|
32
|
+
* cenário `.smoke.json` (sidecar) declarando artefatos conformes por etapa + a
|
|
33
|
+
* etapa-alvo + as duas injeções. A fábrica gera wf + cenário juntos e reusa este
|
|
34
|
+
* harness sem reescrever.
|
|
35
|
+
*
|
|
36
|
+
* SIDECAR — chave OPCIONAL `aux_files` (Story SF11.11 / ADR-025 `DF-SF11.11-6`):
|
|
37
|
+
* "aux_files": { "<step>": { "<caminho relativo ao runDir>": "<conteúdo>" } }
|
|
38
|
+
* Arquivos plantados no runDir no despacho da etapa, ALÉM do artefato. Existe para que um
|
|
39
|
+
* `command_check` que verifica o PRODUTO da etapa em disco (e não o texto do artefato) seja
|
|
40
|
+
* EXERCITADO pelo smoke em vez de ser contornado por ele. Cenário sem a chave = comportamento
|
|
41
|
+
* byte-idêntico ao anterior.
|
|
42
|
+
*
|
|
43
|
+
* Uso:
|
|
44
|
+
* node scripts/smoke-pod-workflow.cjs <wf.yaml> [--scenario <json>] [--pod-root <dir>]
|
|
45
|
+
* node scripts/smoke-pod-workflow.cjs <wf.yaml> --inject-template-violation (só 3a)
|
|
46
|
+
* node scripts/smoke-pod-workflow.cjs <wf.yaml> --inject-veto-violation (só 3b)
|
|
47
|
+
* (sem flag de injeção: roda as 4 provas, critério atômico 4/4)
|
|
48
|
+
*
|
|
49
|
+
* Node >= 18, sem dependências externas. Exit 0 = 4/4 (ou a prova pedida) PASS.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
const fs = require('node:fs');
|
|
53
|
+
const os = require('node:os');
|
|
54
|
+
const path = require('node:path');
|
|
55
|
+
|
|
56
|
+
const R = require('./pc-run-pod.cjs');
|
|
57
|
+
const { parseYaml, podRootFor } = require('./validate-pod-workflow.cjs');
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// Utilidades do harness.
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
/** Planta o artefato de uma etapa despachada (papel do subagente). */
|
|
64
|
+
function plant(ctx, stepId, content) {
|
|
65
|
+
const rel = ctx.state.steps[stepId].artifact;
|
|
66
|
+
if (!rel) throw new Error(`etapa "${stepId}" sem artifact atribuído (não foi despachada?)`);
|
|
67
|
+
const abs = path.resolve(ctx.runDir, rel);
|
|
68
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
69
|
+
fs.writeFileSync(abs, content, 'utf8');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Planta ARQUIVOS AUXILIARES da etapa despachada — o que o papel do subagente produziria em
|
|
74
|
+
* disco ALÉM do artefato (Story SF11.11 / ADR-025 `DF-SF11.11-6`).
|
|
75
|
+
*
|
|
76
|
+
* POR QUE ISTO EXISTE: `plant()` escreve SÓ `runDir/artifacts/<step>.md`. Enquanto todo veto
|
|
77
|
+
* olhava para dentro do artefato, isso bastava. Um `command_check` que verifica o PRODUTO da
|
|
78
|
+
* etapa em disco (ex.: renderizar os HTMLs que a etapa gerou) achava zero arquivo e derrubava a
|
|
79
|
+
* prova 2 — e as duas saídas fáceis foram RECUSADAS com motivo no ADR: fazer o script passar
|
|
80
|
+
* quando não há insumo é a degradação silenciosa da SF11.4b; desligar o veto no teste é o escape
|
|
81
|
+
* hatch que o `DF-SF11.11-4` proíbe. A saída certa é o smoke aprender a plantar o produto, e
|
|
82
|
+
* assim EXERCITAR o veto em vez de ficar cego a ele.
|
|
83
|
+
*
|
|
84
|
+
* RETROCOMPATÍVEL POR CONSTRUÇÃO: cenário sem `aux_files` (ou sem a chave da etapa) é no-op —
|
|
85
|
+
* o comportamento é byte-idêntico ao de antes desta função. Mesma regra do `skip_modes`.
|
|
86
|
+
* Contenção: todo caminho é resolvido sob o `runDir` e recusado fora dele (fail-loud).
|
|
87
|
+
* Retorna quantos arquivos plantou (0 = cenário não declara nada para esta etapa).
|
|
88
|
+
*/
|
|
89
|
+
function plantAux(ctx, stepId, auxFiles) {
|
|
90
|
+
if (!auxFiles || typeof auxFiles !== 'object') return 0;
|
|
91
|
+
const forStep = auxFiles[stepId];
|
|
92
|
+
if (forStep == null) return 0;
|
|
93
|
+
if (typeof forStep !== 'object' || Array.isArray(forStep)) {
|
|
94
|
+
throw new Error(`aux_files["${stepId}"] deve ser um mapa { "caminho/relativo": "conteudo" }`);
|
|
95
|
+
}
|
|
96
|
+
const root = path.resolve(ctx.runDir);
|
|
97
|
+
let n = 0;
|
|
98
|
+
for (const [rel, content] of Object.entries(forStep)) {
|
|
99
|
+
const abs = path.resolve(root, rel);
|
|
100
|
+
if (abs !== root && !abs.startsWith(root + path.sep)) {
|
|
101
|
+
throw new Error(`aux_files["${stepId}"]["${rel}"] resolve FORA do runDir (${abs}) — recusado`);
|
|
102
|
+
}
|
|
103
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
104
|
+
fs.writeFileSync(abs, String(content), 'utf8');
|
|
105
|
+
n++;
|
|
106
|
+
}
|
|
107
|
+
return n;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Ativação: o brief da etapa aponta para um template que EXISTE em disco. */
|
|
111
|
+
function briefReferencesExistingTemplate(brief, ctx, stepId) {
|
|
112
|
+
const step = R.stepById(ctx.doc, stepId);
|
|
113
|
+
const tmplAbs = path.resolve(ctx.podRoot, step.template);
|
|
114
|
+
return fs.existsSync(tmplAbs) && typeof brief === 'string' && brief.includes(tmplAbs);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Dirige UM run do canário até terminar. Planta o artefato conforme do cenário em
|
|
119
|
+
* cada etapa despachada, EXCETO na etapa `inject.step`, onde planta `inject.content`.
|
|
120
|
+
* Auto-aprova checkpoints bloqueantes (yolo). Retorna o estado final + trilha de
|
|
121
|
+
* ativação + o state.json RELIDO DO DISCO (prova de persistência).
|
|
122
|
+
*/
|
|
123
|
+
function driveRun({ workflowPath, podRoot, pod, workflow, scenario, inject, memoryPodDir }) {
|
|
124
|
+
const runsRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-smoke-'));
|
|
125
|
+
const { ctx, result } = R.startRun({
|
|
126
|
+
workflowPath,
|
|
127
|
+
podRootOverride: podRoot,
|
|
128
|
+
pod,
|
|
129
|
+
workflow,
|
|
130
|
+
mode: 'yolo',
|
|
131
|
+
taskRef: 'smoke-canary',
|
|
132
|
+
runsRoot,
|
|
133
|
+
// SF3.2 (caveat SF2.4 → requisito): no --sweep, memoryPodDir aponta para um dir
|
|
134
|
+
// ISOLADO (temp) para NÃO appendar learnings sintéticos na memória de produção do
|
|
135
|
+
// pod. Sem esta flag (modo 4-provas legado), permanece undefined ⇒ default podRoot.
|
|
136
|
+
memoryPodDir,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const activations = [];
|
|
140
|
+
let res = result;
|
|
141
|
+
let guard = 0;
|
|
142
|
+
while (true) {
|
|
143
|
+
if (++guard > 1000) throw new Error('loop guard: o workflow não terminou (possível ciclo no harness)');
|
|
144
|
+
if (res.action === 'dispatch') {
|
|
145
|
+
const stepId = res.step;
|
|
146
|
+
activations.push({ step: stepId, ok: briefReferencesExistingTemplate(res.brief, ctx, stepId) });
|
|
147
|
+
const content = (inject && inject.step === stepId)
|
|
148
|
+
? inject.content
|
|
149
|
+
: scenario.artifacts[stepId];
|
|
150
|
+
if (content == null) {
|
|
151
|
+
throw new Error(`cenário sem artefato conforme para a etapa "${stepId}" (chave artifacts.${stepId})`);
|
|
152
|
+
}
|
|
153
|
+
// SF11.11: auxiliares ANTES do artefato — o veto da etapa roda no `advance` logo abaixo e
|
|
154
|
+
// precisa achar o produto em disco. Cenário sem `aux_files` = no-op (retrocompatível).
|
|
155
|
+
plantAux(ctx, stepId, scenario.aux_files);
|
|
156
|
+
plant(ctx, stepId, content);
|
|
157
|
+
res = R.advance(ctx);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (res.action === 'awaiting_approval') {
|
|
161
|
+
// checkpoint.blocking: auto-approve determinístico (escopo OUT: sem gate humano real)
|
|
162
|
+
const ap = R.approve(ctx);
|
|
163
|
+
res = ap.next;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
break; // complete | blocked | halt | ...
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const disk = R.readState(ctx.statePath);
|
|
170
|
+
return { ctx, order: ctx.order, final: res, activations, disk, runsRoot };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function cleanup(runsRoot) {
|
|
174
|
+
try {
|
|
175
|
+
fs.rmSync(runsRoot, { recursive: true, force: true });
|
|
176
|
+
} catch (_) { /* best-effort */ }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// ---------------------------------------------------------------------------
|
|
180
|
+
// As 4 provas. Cada uma roda seu PRÓPRIO run isolado (não reaproveita state entre
|
|
181
|
+
// provas — Testing da story: uma violação nunca é mascarada pelo resultado anterior).
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
const TEMPLATE_FAIL_RE = /seção obrigatória do template/;
|
|
185
|
+
const VETO_FAIL_RE = /veto\.assertions/;
|
|
186
|
+
|
|
187
|
+
/** Prova 1 — Ativação de todo agente. */
|
|
188
|
+
function proveActivation(cfg) {
|
|
189
|
+
const run = driveRun({ ...cfg, inject: null });
|
|
190
|
+
cleanup(run.runsRoot);
|
|
191
|
+
const nSteps = run.order.length;
|
|
192
|
+
const allActivated = run.activations.length === nSteps && run.activations.every((a) => a.ok);
|
|
193
|
+
const reachedEnd = run.final.action === 'complete';
|
|
194
|
+
const failed = run.activations.filter((a) => !a.ok).map((a) => a.step);
|
|
195
|
+
return {
|
|
196
|
+
pass: allActivated && reachedEnd,
|
|
197
|
+
detail: allActivated && reachedEnd
|
|
198
|
+
? `${nSteps} agente(s) ativados, cada brief aponta template existente; run completou`
|
|
199
|
+
: `ativação falhou (${failed.join(', ') || 'run não completou: ' + run.final.action})`,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** Prova 2 — Caminho feliz ponta a ponta; todos `done`, final conforme. */
|
|
204
|
+
function proveHappyPath(cfg) {
|
|
205
|
+
const run = driveRun({ ...cfg, inject: null });
|
|
206
|
+
cleanup(run.runsRoot);
|
|
207
|
+
const complete = run.final.action === 'complete';
|
|
208
|
+
const allDone = Object.values(run.disk.steps).every((s) => s.status === 'done');
|
|
209
|
+
return {
|
|
210
|
+
pass: complete && allDone,
|
|
211
|
+
detail: complete && allDone
|
|
212
|
+
? `DAG completo: ${run.order.map((id) => `${id}=done`).join(', ')}`
|
|
213
|
+
: `caminho feliz falhou (final=${run.final.action}; state=${run.order.map((id) => `${id}:${run.disk.steps[id].status}`).join(', ')})`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Núcleo comum de 3a/3b: injeta na etapa-alvo e exige BLOQUEIO no passo. */
|
|
218
|
+
function proveBlock(cfg, kind) {
|
|
219
|
+
const target = cfg.scenario.target_step;
|
|
220
|
+
const content = kind === 'template' ? cfg.scenario.template_violation : cfg.scenario.veto_violation;
|
|
221
|
+
if (content == null) throw new Error(`cenário sem "${kind === 'template' ? 'template_violation' : 'veto_violation'}"`);
|
|
222
|
+
|
|
223
|
+
const run = driveRun({ ...cfg, inject: { step: target, content } });
|
|
224
|
+
cleanup(run.runsRoot);
|
|
225
|
+
|
|
226
|
+
const { order, disk, final } = run;
|
|
227
|
+
const idx = order.indexOf(target);
|
|
228
|
+
const failures = Array.isArray(final.failures) ? final.failures : [];
|
|
229
|
+
|
|
230
|
+
const blocked = final.action === 'blocked' && final.reason === 'verification_failed';
|
|
231
|
+
const hasTemplateFail = failures.some((f) => TEMPLATE_FAIL_RE.test(f));
|
|
232
|
+
const hasVetoFail = failures.some((f) => VETO_FAIL_RE.test(f));
|
|
233
|
+
|
|
234
|
+
// Classe de falha DISTINTA por caminho de código:
|
|
235
|
+
// 3a → falha de forma presente, veto AUSENTE (isola checkTemplateConformance);
|
|
236
|
+
// 3b → falha de veto presente, forma AUSENTE (isola evalVeto).
|
|
237
|
+
const classOk = kind === 'template'
|
|
238
|
+
? (hasTemplateFail && !hasVetoFail)
|
|
239
|
+
: (hasVetoFail && !hasTemplateFail);
|
|
240
|
+
|
|
241
|
+
// state.json parou EXATAMENTE no passo violado: anteriores done, alvo não-done,
|
|
242
|
+
// posteriores pending (a etapa seguinte nunca rodou).
|
|
243
|
+
const beforeDone = order.slice(0, idx).every((id) => disk.steps[id].status === 'done');
|
|
244
|
+
const targetNotDone = disk.steps[target].status !== 'done';
|
|
245
|
+
const afterPending = order.slice(idx + 1).every((id) => disk.steps[id].status === 'pending');
|
|
246
|
+
const stopped = beforeDone && targetNotDone && afterPending;
|
|
247
|
+
|
|
248
|
+
const pass = blocked && classOk && stopped;
|
|
249
|
+
return {
|
|
250
|
+
pass,
|
|
251
|
+
detail: pass
|
|
252
|
+
? `bloqueou em "${target}" (${disk.steps[target].status}); classe=${kind}; posteriores=pending; falha=${kind === 'template' ? 'forma' : 'veto'}`
|
|
253
|
+
: `esperado BLOQUEIO ${kind} em "${target}" — blocked=${blocked} classe_isolada=${classOk} parou_no_passo=${stopped} | failures=[${failures.join(' | ')}] | state=${order.map((id) => `${id}:${disk.steps[id].status}`).join(', ')}`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
// SWEEP DE INJEÇÃO EXAUSTIVO (SF3.2 — Decisão D.G3).
|
|
259
|
+
//
|
|
260
|
+
// 3a por TODA etapa (1 seção obrigatória do template removida) + 3b por TODA
|
|
261
|
+
// assertion, derivando a violação MAIS FORTE da semântica POR FORMA (nunca a mais
|
|
262
|
+
// conveniente). A UNDERIVABILIDADE é uma REGRA GERAL de controle de fluxo, não uma
|
|
263
|
+
// lista fechada: cada deriver TENTA construir "o artefato que viola SÓ esta
|
|
264
|
+
// assertion"; se não consegue, devolve {underivable} e o sweep termina exit 1
|
|
265
|
+
// fail-loud. É a underivabilidade que detecta estaticamente a assertion degenerada.
|
|
266
|
+
// ---------------------------------------------------------------------------
|
|
267
|
+
|
|
268
|
+
class SweepUnderivable extends Error {
|
|
269
|
+
constructor(reason) { super(reason); this.name = 'SweepUnderivable'; this.underivable = true; }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** Substitui (ou insere) a linha `field: valor`, preservando prefixo `- `/`**`. */
|
|
273
|
+
function replaceOrSetField(text, field, value) {
|
|
274
|
+
const esc = String(field).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
275
|
+
const re = new RegExp(`^(\\s*(?:[-*]\\s+)?(?:\\*\\*)?${esc}(?:\\*\\*)?\\s*[:=]\\s*).*$`, 'i');
|
|
276
|
+
const lines = text.split(/\r?\n/);
|
|
277
|
+
for (let i = 0; i < lines.length; i++) {
|
|
278
|
+
if (re.test(lines[i])) { lines[i] = lines[i].replace(re, `$1${value}`); return lines.join('\n'); }
|
|
279
|
+
}
|
|
280
|
+
return `${text.replace(/\s*$/, '')}\n${field}: ${value}\n`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** Remove toda linha cujo conteúdo trimado seja EXATAMENTE `target`. */
|
|
284
|
+
function removeExactLine(text, target) {
|
|
285
|
+
const want = String(target).trim();
|
|
286
|
+
return text.split(/\r?\n/).filter((l) => l.trim() !== want).join('\n');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** Reconstrói a seção cujo heading CONTÉM `field` para ter EXATAMENTE k itens de lista. */
|
|
290
|
+
function rebuildCountSection(text, field, k) {
|
|
291
|
+
const lines = text.split(/\r?\n/);
|
|
292
|
+
const items = Array.from({ length: k }, (_, i) => `- item ${i + 1}`);
|
|
293
|
+
const isHeading = (l) => /^#{1,6}\s+\S/.test(l.trim());
|
|
294
|
+
const out = [];
|
|
295
|
+
let i = 0;
|
|
296
|
+
let done = false;
|
|
297
|
+
while (i < lines.length) {
|
|
298
|
+
const line = lines[i];
|
|
299
|
+
if (!done && isHeading(line) && line.toLowerCase().includes(String(field).toLowerCase())) {
|
|
300
|
+
out.push(line); // mantém o heading (forma preservada)
|
|
301
|
+
out.push(...items); // k itens reais de lista
|
|
302
|
+
i++;
|
|
303
|
+
while (i < lines.length && !isHeading(lines[i])) i++; // descarta corpo antigo
|
|
304
|
+
done = true;
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
out.push(line);
|
|
308
|
+
i++;
|
|
309
|
+
}
|
|
310
|
+
if (!done) { out.push(`## ${field}`, ...items); } // heading ausente: cria com k itens
|
|
311
|
+
return out.join('\n');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Substitui o corpo do 1º bloco cercado ```<fence>``` sob a seção declarada por
|
|
316
|
+
* `newBody`, preservando TODO o resto do artefato (SF7.8 — espelho de escrita do
|
|
317
|
+
* `extractFencedBlock` do runner, mesma varredura: âncora na seção, não atravessa
|
|
318
|
+
* heading, exige fence de fechamento). Retorna o texto mutado ou null se o bloco
|
|
319
|
+
* não for encontrado/fechado.
|
|
320
|
+
*/
|
|
321
|
+
function replaceFencedBlock(text, section, fence, newBody) {
|
|
322
|
+
const lines = text.split(/\r?\n/);
|
|
323
|
+
const want = String(section).trim();
|
|
324
|
+
let i = 0;
|
|
325
|
+
if (want) {
|
|
326
|
+
i = lines.findIndex((l) => l.trim() === want);
|
|
327
|
+
if (i < 0) return null;
|
|
328
|
+
i += 1;
|
|
329
|
+
}
|
|
330
|
+
const open = new RegExp('^\\s*```' + (fence ? fence : '[A-Za-z0-9_-]*') + '\\s*$');
|
|
331
|
+
let start = -1;
|
|
332
|
+
for (; i < lines.length; i++) {
|
|
333
|
+
if (open.test(lines[i])) { start = i + 1; break; }
|
|
334
|
+
if (want && /^#{1,6}\s+\S/.test(lines[i].trim())) return null;
|
|
335
|
+
}
|
|
336
|
+
if (start < 0) return null;
|
|
337
|
+
for (let j = start; j < lines.length; j++) {
|
|
338
|
+
if (/^\s*```\s*$/.test(lines[j])) {
|
|
339
|
+
return [...lines.slice(0, start), ...String(newBody).split('\n'), ...lines.slice(j)].join('\n');
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
return null; // sem fechamento
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Régua idêntica ao runner: chaves `required` de um json_schema_valid.schema. */
|
|
346
|
+
function requiredKeysOf(schema, podRoot) {
|
|
347
|
+
if (Array.isArray(schema)) return schema;
|
|
348
|
+
if (schema && typeof schema === 'object' && Array.isArray(schema.required)) return schema.required;
|
|
349
|
+
if (typeof schema === 'string') {
|
|
350
|
+
const scPath = path.resolve(podRoot, schema);
|
|
351
|
+
if (fs.existsSync(scPath)) {
|
|
352
|
+
try {
|
|
353
|
+
const parsed = JSON.parse(fs.readFileSync(scPath, 'utf8'));
|
|
354
|
+
return Array.isArray(parsed) ? parsed : (parsed.required || []);
|
|
355
|
+
} catch { return []; }
|
|
356
|
+
}
|
|
357
|
+
return String(schema).split(',').map((s) => s.trim()).filter(Boolean);
|
|
358
|
+
}
|
|
359
|
+
return [];
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Conjunto de sondagem para achar UM valor que NÃO casa o pattern. Se NENHUM candidato
|
|
363
|
+
// viola (pattern universal como `.*`/`[\s\S]*`), o campo é underivável — a regra geral,
|
|
364
|
+
// não uma verificação hardcoded de "pattern === '.*'".
|
|
365
|
+
const PROBE_VALUES = ['', 'x', '0', 'zzz', 'INVALID', ' ', '9', 'a-b_c.d', '{}', 'null', 'FALSE', '@@@', '—', 'valor livre 123'];
|
|
366
|
+
function findNonMatchingValue(pattern) {
|
|
367
|
+
let re;
|
|
368
|
+
try { re = new RegExp(String(pattern)); } catch { return { value: 'regex-invalida', ok: true }; }
|
|
369
|
+
for (const v of PROBE_VALUES) if (!re.test(v)) return { value: v, ok: true };
|
|
370
|
+
for (let i = 0; i < 128; i++) {
|
|
371
|
+
const v = `Z${i.toString(36)}_${(i * 2654435761 >>> 0).toString(36)}`;
|
|
372
|
+
if (!re.test(v)) return { value: v, ok: true };
|
|
373
|
+
}
|
|
374
|
+
return { ok: false };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Deriva a violação MAIS FORTE de UMA assertion (regra exata do design §D.G3) a
|
|
379
|
+
* partir do artefato conforme da etapa. Retorna {artifact} OU lança SweepUnderivable.
|
|
380
|
+
*/
|
|
381
|
+
function deriveViolation(assertion, conforme, template, podRoot) {
|
|
382
|
+
switch (assertion.type) {
|
|
383
|
+
case 'count_gte': {
|
|
384
|
+
// n−1 itens REAIS de lista sob o heading (nunca omissão do heading). n:0 é
|
|
385
|
+
// degenerada — 0 itens já satisfaz 0>=0, nenhum artefato viola.
|
|
386
|
+
const n = Number(assertion.n);
|
|
387
|
+
if (!(n >= 1)) {
|
|
388
|
+
throw new SweepUnderivable(`count_gte n:${assertion.n} para "${assertion.field}" — 0 itens satisfaz ${n}>=${n}; nenhum artefato viola (assertion degenerada)`);
|
|
389
|
+
}
|
|
390
|
+
return { artifact: rebuildCountSection(conforme, assertion.field, n - 1) };
|
|
391
|
+
}
|
|
392
|
+
case 'field_matches': {
|
|
393
|
+
// Campo PRESENTE com valor fora do pattern (nunca campo omitido — omissão
|
|
394
|
+
// exercita val===null, não o pattern). Pattern universal ⇒ underivável.
|
|
395
|
+
const nm = findNonMatchingValue(assertion.pattern);
|
|
396
|
+
if (!nm.ok) {
|
|
397
|
+
throw new SweepUnderivable(`field_matches "${assertion.field}" /${assertion.pattern}/ — pattern universal: nenhum valor presente o viola (assertion sempre-true)`);
|
|
398
|
+
}
|
|
399
|
+
return { artifact: replaceOrSetField(conforme, assertion.field, nm.value) };
|
|
400
|
+
}
|
|
401
|
+
case 'section_exists': {
|
|
402
|
+
// Seção omitida — SE ortogonal. Se a seção já é forçada pelo template (heading
|
|
403
|
+
// ##+), violá-la é violar a forma ⇒ não-ortogonal ⇒ underivável.
|
|
404
|
+
const want = String(assertion.section).trim();
|
|
405
|
+
const tmplHeadings = new Set(
|
|
406
|
+
template.split(/\r?\n/).map((l) => l.trim()).filter((l) => /^#{2,6}\s+\S/.test(l)),
|
|
407
|
+
);
|
|
408
|
+
if (tmplHeadings.has(want)) {
|
|
409
|
+
throw new SweepUnderivable(`section_exists "${want}" já é forçada pelo template da etapa — não-ortogonal (ADR-005); nenhum artefato viola só o veto sem violar a forma`);
|
|
410
|
+
}
|
|
411
|
+
return { artifact: removeExactLine(conforme, want) };
|
|
412
|
+
}
|
|
413
|
+
case 'file_referenced_exists': {
|
|
414
|
+
const missing = `__inexistente__/${assertion.field}-${Date.now()}.missing`;
|
|
415
|
+
return { artifact: replaceOrSetField(conforme, assertion.field, missing) };
|
|
416
|
+
}
|
|
417
|
+
case 'json_schema_valid': {
|
|
418
|
+
// JSON BEM-FORMADO que viola um `required` (nunca só parse-error). Schema sem
|
|
419
|
+
// `required` ⇒ só parse-error exercitável ⇒ underivável.
|
|
420
|
+
const required = requiredKeysOf(assertion.schema, podRoot);
|
|
421
|
+
if (!required.length) {
|
|
422
|
+
throw new SweepUnderivable(`json_schema_valid sem chaves 'required' — só parse-error é exercitável; o schema nunca é provado discriminante (assertion degenerada)`);
|
|
423
|
+
}
|
|
424
|
+
// SF7.8 (ADR-011 Decisão 1): com `extract:{section,fence}`, a violação é derivada
|
|
425
|
+
// SOBRE O BLOCO fenced (mesma régua do evalVeto do runner) — extrai, remove a 1ª
|
|
426
|
+
// chave `required`, e REINSERE o JSON mutado na mesma posição; o resto do artefato
|
|
427
|
+
// (headings do template, campo `confianca`) permanece intacto, isolando a classe
|
|
428
|
+
// veto. Sem `extract`, caminho legado bit-a-bit idêntico (artefato inteiro = JSON).
|
|
429
|
+
if (assertion.extract && typeof assertion.extract === 'object') {
|
|
430
|
+
const section = assertion.extract.section;
|
|
431
|
+
const fence = assertion.extract.fence || 'json';
|
|
432
|
+
const block = R.extractFencedBlock(conforme, section, fence);
|
|
433
|
+
if (block === null) {
|
|
434
|
+
throw new SweepUnderivable(`json_schema_valid+extract: bloco \`\`\`${fence}\`\`\` não encontrado na seção "${section}" do artefato conforme — o cenário não exercita a assertion (sidecar incompleto)`);
|
|
435
|
+
}
|
|
436
|
+
let obj;
|
|
437
|
+
try { obj = JSON.parse(block); } catch (e) {
|
|
438
|
+
throw new SweepUnderivable(`json_schema_valid+extract: bloco extraído do conforme não é JSON parseável (${e.message}) — impossível derivar violação de required bem-formada`);
|
|
439
|
+
}
|
|
440
|
+
const drop = required[0];
|
|
441
|
+
const violated = { ...obj };
|
|
442
|
+
delete violated[drop];
|
|
443
|
+
const mutated = replaceFencedBlock(conforme, section, fence, JSON.stringify(violated, null, 2));
|
|
444
|
+
if (mutated === null) {
|
|
445
|
+
throw new SweepUnderivable(`json_schema_valid+extract: falha ao reinserir o bloco mutado na seção "${section}" (bloco sem fechamento?)`);
|
|
446
|
+
}
|
|
447
|
+
return { artifact: mutated };
|
|
448
|
+
}
|
|
449
|
+
let obj;
|
|
450
|
+
try { obj = JSON.parse(conforme); } catch (e) {
|
|
451
|
+
throw new SweepUnderivable(`json_schema_valid: artefato conforme da etapa não é JSON parseável (${e.message}) — impossível derivar violação de required bem-formada`);
|
|
452
|
+
}
|
|
453
|
+
const drop = required[0];
|
|
454
|
+
const violated = { ...obj };
|
|
455
|
+
delete violated[drop];
|
|
456
|
+
return { artifact: JSON.stringify(violated, null, 2) };
|
|
457
|
+
}
|
|
458
|
+
case 'command_check':
|
|
459
|
+
// SF7.2/ADR-011 Decisão 4: command_check não tem derivação TEXTUAL (validade definida
|
|
460
|
+
// por processo externo). A violação vem de uma fixture DECLARADA no sidecar
|
|
461
|
+
// (scenario.violations[step]) — tratada em runSweep antes desta função. Chegar aqui =
|
|
462
|
+
// sem fixture ⇒ underivável fail-loud (nunca pulado).
|
|
463
|
+
throw new SweepUnderivable(`command_check exige fixture de violação declarada no sidecar (scenario.violations[step]) — não há derivação textual; sweep não pode provar bloqueio sem ela`);
|
|
464
|
+
default:
|
|
465
|
+
throw new SweepUnderivable(`tipo de assertion desconhecido: ${assertion.type} — sem deriver definido (Constitution Art. IV: PARA e escala, não inventa 6ª regra)`);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Deriva o artefato 3a de uma etapa: o conforme com UMA seção obrigatória do template
|
|
471
|
+
* removida, escolhida para NÃO colidir com nenhum veto da etapa (isola a classe FORMA).
|
|
472
|
+
*/
|
|
473
|
+
function deriveTemplateViolation(conforme, template, step) {
|
|
474
|
+
const sections = template.split(/\r?\n/).map((l) => l.trim()).filter((l) => /^#{2,6}\s+\S/.test(l));
|
|
475
|
+
if (!sections.length) return null; // template sem seções ## → 3a não aplicável a esta etapa
|
|
476
|
+
const assertions = (step.veto && Array.isArray(step.veto.assertions)) ? step.veto.assertions : [];
|
|
477
|
+
const loadBearing = new Set();
|
|
478
|
+
for (const a of assertions) {
|
|
479
|
+
if (a.type === 'section_exists') loadBearing.add(String(a.section).trim());
|
|
480
|
+
if (a.type === 'count_gte') {
|
|
481
|
+
for (const h of sections) if (h.toLowerCase().includes(String(a.field).toLowerCase())) loadBearing.add(h);
|
|
482
|
+
}
|
|
483
|
+
// SF7.8: a seção-âncora de um json_schema_valid+extract é load-bearing — removê-la
|
|
484
|
+
// no 3a também quebraria o veto (bloco não encontrado), impedindo o isolamento de classe.
|
|
485
|
+
if (a.type === 'json_schema_valid' && a.extract && a.extract.section) {
|
|
486
|
+
loadBearing.add(String(a.extract.section).trim());
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const inert = sections.find((h) => !loadBearing.has(h));
|
|
490
|
+
const chosen = inert || sections[0]; // sem inerte: remove a 1ª (bloqueia, sem isolar)
|
|
491
|
+
return { artifact: removeExactLine(conforme, chosen), section: chosen, isolated: !!inert };
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Executa o sweep exaustivo sobre um workflow. Reusável por close-line/F3 (que
|
|
496
|
+
* RE-EXECUTAM, nunca releem o report). Retorna { ok, report, underivable }.
|
|
497
|
+
* - underivable ≠ null ⇒ ok=false, exit 1 (assertion degenerada — conserte a spec)
|
|
498
|
+
* - qualquer proof.blocked=false ⇒ ok=false, exit 1
|
|
499
|
+
*/
|
|
500
|
+
function runSweep({ workflowPath, podRoot, scenario, specPath, blockChecker }) {
|
|
501
|
+
// blockChecker é uma costura de teste (default = injectAndCheckBlock, o runner real).
|
|
502
|
+
// Existe SÓ para exercitar a agregação fail-loud "blocked:false ⇒ exit 1" de forma
|
|
503
|
+
// determinística — a ENFORCEMENT real é provada pelo sweep verde das 5 formas + as
|
|
504
|
+
// suítes SF1.x do runner. Nunca é passada em produção.
|
|
505
|
+
const check = blockChecker || injectAndCheckBlock;
|
|
506
|
+
const doc = parseYaml(fs.readFileSync(workflowPath, 'utf8'));
|
|
507
|
+
const pod = path.basename(podRoot);
|
|
508
|
+
const workflow = path.basename(workflowPath).replace(/\.ya?ml$/, '');
|
|
509
|
+
const specHash = specPath && fs.existsSync(specPath)
|
|
510
|
+
? R_sha256File(specPath)
|
|
511
|
+
: R_sha256File(workflowPath); // fallback documentado: auto-binding ao wf quando não há spec
|
|
512
|
+
// memoryPodDir ISOLADO: nenhum learning sintético entra na memória de produção.
|
|
513
|
+
const memoryPodDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-sweep-mem-'));
|
|
514
|
+
|
|
515
|
+
const cfgBase = { workflowPath, podRoot, pod, workflow, scenario, memoryPodDir };
|
|
516
|
+
|
|
517
|
+
// FASE 1 — DERIVAÇÃO (pura, estática, SEM runner). A underivabilidade é detectada
|
|
518
|
+
// AQUI, antes de qualquer run: é o detector estático da assertion degenerada (D.G3).
|
|
519
|
+
// Um wf com assertion underivável (ex.: section_exists não-ortogonal) nem chega a ser
|
|
520
|
+
// carregado pelo runner — a derivação já falha fail-loud.
|
|
521
|
+
const injections = [];
|
|
522
|
+
try {
|
|
523
|
+
for (const step of doc.steps) {
|
|
524
|
+
const stepId = step.id;
|
|
525
|
+
const conforme = scenario.artifacts[stepId];
|
|
526
|
+
if (conforme == null) throw new Error(`cenário sem artefato conforme para a etapa "${stepId}"`);
|
|
527
|
+
const tmplPath = path.resolve(podRoot, step.template);
|
|
528
|
+
const template = fs.existsSync(tmplPath) ? fs.readFileSync(tmplPath, 'utf8') : '';
|
|
529
|
+
|
|
530
|
+
// 3a — remoção de seção obrigatória (por etapa). Isola a CLASSE template quando
|
|
531
|
+
// a seção removida é inerte ao veto (senão exige só bloqueio — sobreposição legítima).
|
|
532
|
+
const tv = deriveTemplateViolation(conforme, template, step);
|
|
533
|
+
if (tv) injections.push({ step: stepId, assertion: `__template__(${tv.section})`, artifact: tv.artifact, expectClass: tv.isolated ? 'template' : null });
|
|
534
|
+
|
|
535
|
+
// 3b — por assertion, derivação por forma; underivável ⇒ SweepUnderivable.
|
|
536
|
+
// Isola a CLASSE veto: a violação derivada preserva a forma; só o veto falha.
|
|
537
|
+
const assertions = (step.veto && Array.isArray(step.veto.assertions)) ? step.veto.assertions : [];
|
|
538
|
+
assertions.forEach((a, i) => {
|
|
539
|
+
if (a.type === 'command_check') {
|
|
540
|
+
// SF7.2/ADR-011 Decisão 4: violação vem de fixture DECLARADA no sidecar; ausente ⇒
|
|
541
|
+
// UNDERIVÁVEL fail-loud (nunca pulado em silêncio).
|
|
542
|
+
const viol = scenario.violations && scenario.violations[stepId];
|
|
543
|
+
if (viol == null) {
|
|
544
|
+
throw new SweepUnderivable(`etapa "${stepId}" tem command_check[${i}] sem fixture de violação (scenario.violations["${stepId}"]) — sweep não pode provar bloqueio; declare o par passa/viola no sidecar`);
|
|
545
|
+
}
|
|
546
|
+
injections.push({ step: stepId, assertion: `${i}:command_check`, artifact: viol, expectClass: 'veto' });
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const d = deriveViolation(a, conforme, template, podRoot); // pode lançar SweepUnderivable
|
|
550
|
+
injections.push({ step: stepId, assertion: `${i}:${a.type}`, artifact: d.artifact, expectClass: 'veto' });
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
} catch (e) {
|
|
554
|
+
cleanup(memoryPodDir);
|
|
555
|
+
if (e instanceof SweepUnderivable) {
|
|
556
|
+
return { ok: false, underivable: { reason: e.message }, report: { workflow, pod, spec_hash: specHash, proofs: [] } };
|
|
557
|
+
}
|
|
558
|
+
throw e;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// FASE 2 — DRIVE: injeta cada violação derivada e mede o bloqueio no runner real.
|
|
562
|
+
const proofs = [];
|
|
563
|
+
try {
|
|
564
|
+
for (const inj of injections) {
|
|
565
|
+
const blocked = check(cfgBase, inj.step, inj.artifact, inj.expectClass);
|
|
566
|
+
proofs.push({ step: inj.step, assertion: inj.assertion, blocked });
|
|
567
|
+
}
|
|
568
|
+
} finally {
|
|
569
|
+
cleanup(memoryPodDir);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const report = { workflow, pod, spec_hash: specHash, proofs };
|
|
573
|
+
const leaked = proofs.filter((p) => !p.blocked);
|
|
574
|
+
return { ok: leaked.length === 0, underivable: null, report, leaked };
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Injeta o artefato derivado na etapa-alvo e devolve se o runner BLOQUEOU no passo,
|
|
579
|
+
* COM ISOLAMENTO DE CLASSE (mesmo padrão sem-OR do smoke SF1.3 `proveBlock`): a
|
|
580
|
+
* violação 3a tem que bloquear POR template (forma presente, veto AUSENTE) e a 3b POR
|
|
581
|
+
* veto (veto presente, forma AUSENTE). `expectClass=null` ⇒ só exige bloqueio (3a
|
|
582
|
+
* não-isolável: forma e veto se sobrepõem legitimamente — ex.: `count_gte` cujo campo
|
|
583
|
+
* É o heading do template; removê-lo quebra as duas classes). Sem o isolamento, um
|
|
584
|
+
* deriver que regredisse emitindo a CLASSE ERRADA passaria green-falso (finding QA #1).
|
|
585
|
+
*/
|
|
586
|
+
function injectAndCheckBlock(cfg, stepId, content, expectClass) {
|
|
587
|
+
const run = driveRun({ ...cfg, inject: { step: stepId, content } });
|
|
588
|
+
cleanup(run.runsRoot);
|
|
589
|
+
const { order, disk, final } = run;
|
|
590
|
+
const idx = order.indexOf(stepId);
|
|
591
|
+
const blockedFinal = final.action === 'blocked' && final.reason === 'verification_failed';
|
|
592
|
+
const targetNotDone = disk.steps[stepId].status !== 'done';
|
|
593
|
+
const afterPending = order.slice(idx + 1).every((id) => disk.steps[id].status === 'pending');
|
|
594
|
+
const failures = Array.isArray(final.failures) ? final.failures : [];
|
|
595
|
+
const hasTemplateFail = failures.some((f) => TEMPLATE_FAIL_RE.test(f));
|
|
596
|
+
const hasVetoFail = failures.some((f) => VETO_FAIL_RE.test(f));
|
|
597
|
+
let classOk = true;
|
|
598
|
+
if (expectClass === 'template') classOk = hasTemplateFail && !hasVetoFail;
|
|
599
|
+
else if (expectClass === 'veto') classOk = hasVetoFail && !hasTemplateFail;
|
|
600
|
+
return blockedFinal && targetNotDone && afterPending && classOk;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const R_sha256File = (p) => require('node:crypto').createHash('sha256').update(fs.readFileSync(p)).digest('hex');
|
|
604
|
+
|
|
605
|
+
// ---------------------------------------------------------------------------
|
|
606
|
+
// CLI.
|
|
607
|
+
// ---------------------------------------------------------------------------
|
|
608
|
+
|
|
609
|
+
function argVal(args, flag) {
|
|
610
|
+
const i = args.indexOf(flag);
|
|
611
|
+
return i >= 0 ? args[i + 1] : null;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function main() {
|
|
615
|
+
const args = process.argv.slice(2);
|
|
616
|
+
const workflowPath = args[0];
|
|
617
|
+
if (!workflowPath || workflowPath.startsWith('--')) {
|
|
618
|
+
console.error('Uso: node scripts/smoke-pod-workflow.cjs <wf.yaml> [--scenario <json>] [--pod-root <dir>] [--inject-template-violation|--inject-veto-violation]');
|
|
619
|
+
process.exit(2);
|
|
620
|
+
}
|
|
621
|
+
if (!fs.existsSync(workflowPath)) {
|
|
622
|
+
console.error(`[erro] workflow não encontrado: ${workflowPath}`);
|
|
623
|
+
process.exit(2);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
const podRootOverride = argVal(args, '--pod-root');
|
|
627
|
+
const podRoot = podRootFor(workflowPath, podRootOverride);
|
|
628
|
+
const scenarioPath = argVal(args, '--scenario') ||
|
|
629
|
+
workflowPath.replace(/\.ya?ml$/, '.smoke.json');
|
|
630
|
+
if (!fs.existsSync(scenarioPath)) {
|
|
631
|
+
console.error(`[erro] cenário de smoke não encontrado: ${scenarioPath} (esperado sidecar .smoke.json ao lado do wf, ou --scenario)`);
|
|
632
|
+
process.exit(2);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
let scenario;
|
|
636
|
+
try {
|
|
637
|
+
scenario = JSON.parse(fs.readFileSync(scenarioPath, 'utf8'));
|
|
638
|
+
} catch (e) {
|
|
639
|
+
console.error(`[erro] cenário .smoke.json inválido (${scenarioPath}): ${e.message}`);
|
|
640
|
+
process.exit(2);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const doc = parseYaml(fs.readFileSync(workflowPath, 'utf8'));
|
|
644
|
+
const pod = path.basename(podRoot);
|
|
645
|
+
const workflow = path.basename(workflowPath).replace(/\.ya?ml$/, '');
|
|
646
|
+
const cfg = { workflowPath, podRoot, pod, workflow, scenario };
|
|
647
|
+
|
|
648
|
+
// ---- Modo --sweep (SF3.2): prova de bloqueio EXAUSTIVA (3a×etapas + 3b×assertions).
|
|
649
|
+
if (args.includes('--sweep')) {
|
|
650
|
+
const specPath = argVal(args, '--spec');
|
|
651
|
+
const reportPath = argVal(args, '--report');
|
|
652
|
+
console.log(`# smoke --sweep — ${path.relative(process.cwd(), workflowPath)} (pod=${pod}, workflow=${workflow})`);
|
|
653
|
+
let sweep;
|
|
654
|
+
try {
|
|
655
|
+
sweep = runSweep({ workflowPath, podRoot, scenario, specPath });
|
|
656
|
+
} catch (e) {
|
|
657
|
+
console.error(`[erro] sweep abortou: ${e.message}`);
|
|
658
|
+
process.exit(1);
|
|
659
|
+
}
|
|
660
|
+
if (reportPath) fs.writeFileSync(reportPath, JSON.stringify(sweep.report, null, 2));
|
|
661
|
+
for (const p of sweep.report.proofs) {
|
|
662
|
+
console.log(`[${p.blocked ? 'BLOQUEOU' : 'VAZOU '}] ${p.step} · ${p.assertion}`);
|
|
663
|
+
}
|
|
664
|
+
if (sweep.underivable) {
|
|
665
|
+
console.error(`\nSWEEP FALHOU (underivabilidade) — assertion sem violação derivável = degenerada ou não-ortogonal — conserte a spec:\n ${sweep.underivable.reason}`);
|
|
666
|
+
process.exit(1);
|
|
667
|
+
}
|
|
668
|
+
if (!sweep.ok) {
|
|
669
|
+
console.error(`\nSWEEP FALHOU — ${sweep.leaked.length} prova(s) VAZARAM (blocked:false): ${sweep.leaked.map((l) => `${l.step}/${l.assertion}`).join(', ')}`);
|
|
670
|
+
process.exit(1);
|
|
671
|
+
}
|
|
672
|
+
console.log(`\nSWEEP VERDE — ${sweep.report.proofs.length} prova(s), TODAS bloquearam (spec_hash=${sweep.report.spec_hash.slice(0, 12)}…).`);
|
|
673
|
+
process.exit(0);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const onlyTemplate = args.includes('--inject-template-violation');
|
|
677
|
+
const onlyVeto = args.includes('--inject-veto-violation');
|
|
678
|
+
|
|
679
|
+
// SF7.7 — ISOLAMENTO DE MEMÓRIA INCONDICIONAL: toda execução do smoke (não só --sweep)
|
|
680
|
+
// grava learnings sintéticos numa cópia mkdtemp descartável, NUNCA na memória real do pod.
|
|
681
|
+
// O runner escreve em `memoryPodDir || podRoot` (pc-run-pod.cjs); omitir o parâmetro (como
|
|
682
|
+
// este caminho fazia, escrito antes do padrão de isolamento do --sweep/SF3.2) contaminava
|
|
683
|
+
// `memory/learnings.jsonl`. Mesmo padrão de runSweep()/harness-g5-lite.cjs. Cleanup é via
|
|
684
|
+
// try/finally — process.exit NÃO roda finally, por isso o exit code é COMPUTADO e aplicado
|
|
685
|
+
// só DEPOIS do finally.
|
|
686
|
+
cfg.memoryPodDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-smoke-mem-'));
|
|
687
|
+
|
|
688
|
+
console.log(`# smoke-pod-workflow — ${path.relative(process.cwd(), workflowPath)} (pod=${pod}, workflow=${workflow})`);
|
|
689
|
+
console.log(`# etapa-alvo de injeção: ${scenario.target_step}\n`);
|
|
690
|
+
|
|
691
|
+
let exitCode = 0;
|
|
692
|
+
let proofs = null;
|
|
693
|
+
try {
|
|
694
|
+
try {
|
|
695
|
+
if (onlyTemplate) {
|
|
696
|
+
proofs = [['Prova 3a (bloqueio template)', proveBlock(cfg, 'template')]];
|
|
697
|
+
} else if (onlyVeto) {
|
|
698
|
+
proofs = [['Prova 3b (bloqueio veto)', proveBlock(cfg, 'veto')]];
|
|
699
|
+
} else {
|
|
700
|
+
proofs = [
|
|
701
|
+
['Prova 1 (ativação)', proveActivation(cfg)],
|
|
702
|
+
['Prova 2 (caminho feliz)', proveHappyPath(cfg)],
|
|
703
|
+
['Prova 3a (bloqueio template)', proveBlock(cfg, 'template')],
|
|
704
|
+
['Prova 3b (bloqueio veto)', proveBlock(cfg, 'veto')],
|
|
705
|
+
];
|
|
706
|
+
}
|
|
707
|
+
} catch (e) {
|
|
708
|
+
console.error(`[erro] harness abortou: ${e.message}`);
|
|
709
|
+
exitCode = 1;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
if (proofs) {
|
|
713
|
+
let passed = 0;
|
|
714
|
+
for (const [label, r] of proofs) {
|
|
715
|
+
console.log(`[${r.pass ? 'OK ' : 'FALHA'}] ${label} — ${r.detail}`);
|
|
716
|
+
if (r.pass) passed++;
|
|
717
|
+
}
|
|
718
|
+
const total = proofs.length;
|
|
719
|
+
console.log(`\n${passed}/${total} provas passaram.`);
|
|
720
|
+
if (passed !== total) {
|
|
721
|
+
const failedNames = proofs.filter(([, r]) => !r.pass).map(([l]) => l);
|
|
722
|
+
console.error(`\nSMOKE FALHOU (não-seguível): ${failedNames.join('; ')}`);
|
|
723
|
+
exitCode = 1;
|
|
724
|
+
} else {
|
|
725
|
+
console.log(total === 4
|
|
726
|
+
? 'Smoke verde 4/4 — workflow SEGUÍVEL (ativa, percorre, e trava as DUAS classes de caminho errado).'
|
|
727
|
+
: 'Prova solicitada verde.');
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
} finally {
|
|
731
|
+
cleanup(cfg.memoryPodDir); // higiene: remove o dir temporário em TODOS os pontos de saída
|
|
732
|
+
}
|
|
733
|
+
process.exit(exitCode);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (require.main === module) {
|
|
737
|
+
main();
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
module.exports = {
|
|
741
|
+
driveRun,
|
|
742
|
+
proveActivation,
|
|
743
|
+
proveHappyPath,
|
|
744
|
+
proveBlock,
|
|
745
|
+
plant,
|
|
746
|
+
plantAux,
|
|
747
|
+
briefReferencesExistingTemplate,
|
|
748
|
+
// SF3.2 — sweep exaustivo (reusável por migration-manifest close-line/verify e pelo harness G5-LITE).
|
|
749
|
+
runSweep,
|
|
750
|
+
deriveViolation,
|
|
751
|
+
deriveTemplateViolation,
|
|
752
|
+
findNonMatchingValue,
|
|
753
|
+
injectAndCheckBlock,
|
|
754
|
+
SweepUnderivable,
|
|
755
|
+
};
|