@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,878 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
/**
|
|
4
|
+
* validate-pod-workflow.cjs — validador estático do contrato `wf-*.yaml` de pod.
|
|
5
|
+
*
|
|
6
|
+
* Prova, SEM EXECUTAR NADA, que um workflow de pod está BEM-FORMADO (não que
|
|
7
|
+
* funciona — isso é o smoke dinâmico, SF1.3; fronteira do ADR-002). Porta o
|
|
8
|
+
* padrão --strict --fail-on-warnings / stop-on-first-failure. Exit 0 = contrato
|
|
9
|
+
* bem-formado; exit != 0 = primeira violação nomeada (arquivo, etapa, campo).
|
|
10
|
+
*
|
|
11
|
+
* Uso:
|
|
12
|
+
* node scripts/validate-pod-workflow.cjs <wf.yaml> [<wf.yaml> ...] [--pod-root <dir>]
|
|
13
|
+
* node scripts/validate-pod-workflow.cjs --glob template/pods (varre **\/workflows/*.yaml)
|
|
14
|
+
*
|
|
15
|
+
* Verificações (Escopo IN c da Story SF1.1; SPEC §B2a; ADR-002), em ordem,
|
|
16
|
+
* stop-on-first-failure por arquivo:
|
|
17
|
+
* 1. Schema — state_machine (com `failed` terminal) + steps[] tipados.
|
|
18
|
+
* 2. Etapa completa — toda etapa tem agent + template + outputs + veto.
|
|
19
|
+
* 3. Veto de máquina — veto.assertions[] reduz a uma das 5 formas (ADR-002).
|
|
20
|
+
* 4. DAG acíclico — depends_on referencia ids válidos e não forma ciclo.
|
|
21
|
+
* 5. Fix-loop — `fix_cycle` (opcional) referencia etapa A MONTANTE; é a
|
|
22
|
+
* ÚNICA aresta para trás permitida (SPEC §B2a "exceto o
|
|
23
|
+
* fix-loop explícito"). Aresta para trás via `depends_on`
|
|
24
|
+
* continua sendo ciclo → FALHA (débito herdado da SF1.1).
|
|
25
|
+
* 6. Templates — todo template: existe em disco (path resolvido do pod).
|
|
26
|
+
* 7. Handoffs fechados — todo output é consumido a jusante ou é artefato final.
|
|
27
|
+
* 8. Checkpoint — (SF11.10/ADR-024) forma de `checkpoint` (blocking/criteria/skip_modes,
|
|
28
|
+
* chaves fechadas) + INVARIANTE: nenhum modo do conjunto fechado fica sem
|
|
29
|
+
* um gate bloqueante não-pulável. Primeiro enforcement de MÁQUINA da
|
|
30
|
+
* proibição do ADR-020 ("o modo autônomo JAMAIS aprova").
|
|
31
|
+
*
|
|
32
|
+
* Node >= 18, sem dependências externas (parser YAML-lite embutido — ver abaixo).
|
|
33
|
+
* Exporta `parseYaml`, `validateWorkflow`, `podRootFor`, `topoOrder`, `VETO_TYPES`,
|
|
34
|
+
* `Fail` para reuso pelo runner `pc-run-pod.cjs` (fonte única do contrato — Risco 2
|
|
35
|
+
* da Story SF1.2: preflight do runner não reimplementa o schema/DAG).
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const fs = require('node:fs');
|
|
39
|
+
const path = require('node:path');
|
|
40
|
+
|
|
41
|
+
const REPO_ROOT = path.resolve(__dirname, '..');
|
|
42
|
+
const VETO_TYPES = {
|
|
43
|
+
section_exists: ['section'],
|
|
44
|
+
field_matches: ['field', 'pattern'],
|
|
45
|
+
file_referenced_exists: ['field'],
|
|
46
|
+
count_gte: ['field', 'n'],
|
|
47
|
+
json_schema_valid: ['schema'],
|
|
48
|
+
// SF7.2/ADR-011 — 6ª forma: executa um script BLESSED (só sob scripts/veto-checks/).
|
|
49
|
+
command_check: ['command'],
|
|
50
|
+
};
|
|
51
|
+
// Raiz BLESSED de execução (ADR-011 Decisão 2): o runner NUNCA executa código de pod —
|
|
52
|
+
// só scripts autorados/code-reviewed do framework sob este diretório.
|
|
53
|
+
const VETO_CHECKS_ROOT = path.join(REPO_ROOT, 'scripts', 'veto-checks');
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* SF11.10/ADR-024 `DF-SF11.10-5` — CONJUNTO FECHADO de ritmos de run. Emenda explícita ao
|
|
57
|
+
* ADR-020 `DF-SF10.21-12` ("`--mode` é rótulo inerte"): a partir daqui o modo PRODUZ
|
|
58
|
+
* diferença estrutural (`checkpoint.skip_modes`), e um rótulo não pode fazer isso. Mora
|
|
59
|
+
* AQUI, e não no motor, porque este arquivo é a FONTE ÚNICA do contrato (o motor o importa,
|
|
60
|
+
* Risco 2 da SF1.2): o validador estático e o runtime têm que concordar sobre o conjunto por
|
|
61
|
+
* construção, nunca por duas listas que alguém sincroniza à mão.
|
|
62
|
+
*/
|
|
63
|
+
const RUN_MODES = ['interactive', 'yolo'];
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* SF11.10/ADR-024 `DF-SF11.10-2`/`-6` — chaves CONHECIDAS sob `checkpoint` de uma etapa.
|
|
67
|
+
* Lista fechada: chave fora dela é contrato de um motor mais novo (ou typo) e vira falha
|
|
68
|
+
* estática aqui + gap nomeado em `capabilityGaps` (pc-run-pod.cjs) — nunca degradação muda.
|
|
69
|
+
*/
|
|
70
|
+
const CHECKPOINT_KEYS = ['blocking', 'criteria', 'skip_modes'];
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Validação ESTÁTICA de segurança de um `command_check` (ADR-011 Decisão 2), ANTES de
|
|
74
|
+
* qualquer execução: (1) `command` é nome de arquivo simples (sem `/`, sem `..`) → sem
|
|
75
|
+
* path traversal; (2) resolve DENTRO de scripts/veto-checks/; (3) extensão .cjs/.js;
|
|
76
|
+
* (4) existe em disco; (5) consta no manifesto ADR-007; (6) `args` (se houver) é array de
|
|
77
|
+
* strings LITERAIS, nunca template/interpolação. Qualquer violação → Fail (exit 1).
|
|
78
|
+
*/
|
|
79
|
+
function checkCommandCheckSafety(assertion) {
|
|
80
|
+
const cmd = assertion.command;
|
|
81
|
+
if (typeof cmd !== 'string' || cmd.trim() === '') fail('command_check: "command" ausente ou não-string');
|
|
82
|
+
if (/[\\/]/.test(cmd) || cmd.includes('..')) {
|
|
83
|
+
fail(`command_check: "command" deve ser um nome de arquivo simples sob scripts/veto-checks/ (sem caminho nem traversal): "${cmd}"`);
|
|
84
|
+
}
|
|
85
|
+
if (!/\.(cjs|js)$/.test(cmd)) fail(`command_check: extensão não permitida em "${cmd}" (esperado .cjs/.js)`);
|
|
86
|
+
const resolved = path.resolve(VETO_CHECKS_ROOT, cmd);
|
|
87
|
+
if (path.dirname(resolved) !== VETO_CHECKS_ROOT) {
|
|
88
|
+
fail(`command_check: "${cmd}" resolve fora da raiz blessed scripts/veto-checks/ — recusado`);
|
|
89
|
+
}
|
|
90
|
+
if (!fs.existsSync(resolved)) fail(`command_check: script blessed inexistente: scripts/veto-checks/${cmd}`);
|
|
91
|
+
const rel = `scripts/veto-checks/${cmd}`;
|
|
92
|
+
let inManifest = true;
|
|
93
|
+
try {
|
|
94
|
+
const { motorRels } = require(path.join(REPO_ROOT, 'installer', 'motor-scripts.js'));
|
|
95
|
+
inManifest = motorRels().includes(rel);
|
|
96
|
+
} catch { /* manifesto indisponível na instalação — a raiz blessed + existência já barram */ }
|
|
97
|
+
if (!inManifest) fail(`command_check: "${rel}" não está classificado em installer/motor-scripts.json (ADR-007)`);
|
|
98
|
+
if ('args' in assertion) {
|
|
99
|
+
if (!Array.isArray(assertion.args)) fail('command_check: "args" deve ser um array de strings literais');
|
|
100
|
+
for (const a of assertion.args) {
|
|
101
|
+
if (typeof a !== 'string') fail(`command_check: "args" só aceita strings literais (recebeu ${typeof a})`);
|
|
102
|
+
if (/[`$]|\{\{|\$\(/.test(a)) fail(`command_check: "args" não pode conter template/interpolação/shell: "${a}"`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if ('timeout_ms' in assertion && (!Number.isInteger(assertion.timeout_ms) || assertion.timeout_ms <= 0)) {
|
|
106
|
+
fail(`command_check: "timeout_ms" deve ser inteiro > 0 (recebeu ${JSON.stringify(assertion.timeout_ms)})`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Valida o shape do `extract:{section,fence}` opcional do json_schema_valid (ADR-011 Decisão 1). */
|
|
111
|
+
function checkJsonSchemaExtract(assertion) {
|
|
112
|
+
if (!('extract' in assertion) || assertion.extract == null) return;
|
|
113
|
+
const ex = assertion.extract;
|
|
114
|
+
if (typeof ex !== 'object' || Array.isArray(ex)) fail('json_schema_valid.extract deve ser um mapa {section, fence?}');
|
|
115
|
+
if (typeof ex.section !== 'string' || ex.section.trim() === '') fail('json_schema_valid.extract.section ausente (string)');
|
|
116
|
+
if ('fence' in ex && (typeof ex.fence !== 'string' || ex.fence.trim() === '')) fail('json_schema_valid.extract.fence deve ser string não-vazia');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// YAML-lite: parser de indentação para o subconjunto do contrato wf-*.yaml.
|
|
121
|
+
// Decisão do @dev (Story SF1.1): o repo tem ZERO dependências (sem node_modules,
|
|
122
|
+
// sem js-yaml declarado) e `scripts/` é publicado no pacote (files[]). Adicionar
|
|
123
|
+
// js-yaml como dep de produção pesa no npm pack sem ganho — o contrato é
|
|
124
|
+
// estruturalmente simples (mapas/listas/escalares). Parser próprio, determinístico.
|
|
125
|
+
// Suporta: mapas aninhados por indentação, listas de bloco (`- `), listas/mapas
|
|
126
|
+
// em flow (`[a, b]`, `{k: v}`), escalares (string/int/bool/null), comentários.
|
|
127
|
+
// NÃO suporta: âncoras, tags, escalares multi-linha (não usados pelo contrato).
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
function stripComment(line) {
|
|
131
|
+
let inS = false;
|
|
132
|
+
let inD = false;
|
|
133
|
+
for (let i = 0; i < line.length; i++) {
|
|
134
|
+
const c = line[i];
|
|
135
|
+
if (c === "'" && !inD) inS = !inS;
|
|
136
|
+
else if (c === '"' && !inS) inD = !inD;
|
|
137
|
+
else if (c === '#' && !inS && !inD && (i === 0 || /\s/.test(line[i - 1]))) {
|
|
138
|
+
return line.slice(0, i);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return line;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function splitTopLevel(str, sep) {
|
|
145
|
+
const out = [];
|
|
146
|
+
let depth = 0;
|
|
147
|
+
let inS = false;
|
|
148
|
+
let inD = false;
|
|
149
|
+
let cur = '';
|
|
150
|
+
for (let i = 0; i < str.length; i++) {
|
|
151
|
+
const c = str[i];
|
|
152
|
+
if (c === "'" && !inD) inS = !inS;
|
|
153
|
+
else if (c === '"' && !inS) inD = !inD;
|
|
154
|
+
if (!inS && !inD) {
|
|
155
|
+
if (c === '[' || c === '{') depth++;
|
|
156
|
+
else if (c === ']' || c === '}') depth--;
|
|
157
|
+
else if (c === sep && depth === 0) {
|
|
158
|
+
out.push(cur);
|
|
159
|
+
cur = '';
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
cur += c;
|
|
164
|
+
}
|
|
165
|
+
out.push(cur);
|
|
166
|
+
return out;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function parseScalar(raw) {
|
|
170
|
+
const s = raw.trim();
|
|
171
|
+
if (s === '' || s === '~' || s === 'null') return null;
|
|
172
|
+
if (s === 'true') return true;
|
|
173
|
+
if (s === 'false') return false;
|
|
174
|
+
if (/^-?\d+$/.test(s)) return Number.parseInt(s, 10);
|
|
175
|
+
if (/^-?\d+\.\d+$/.test(s)) return Number.parseFloat(s);
|
|
176
|
+
if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
|
|
177
|
+
return s.slice(1, -1);
|
|
178
|
+
}
|
|
179
|
+
if (s.startsWith('[') && s.endsWith(']')) {
|
|
180
|
+
const body = s.slice(1, -1).trim();
|
|
181
|
+
if (body === '') return [];
|
|
182
|
+
return splitTopLevel(body, ',').map((e) => parseScalar(e));
|
|
183
|
+
}
|
|
184
|
+
if (s.startsWith('{') && s.endsWith('}')) {
|
|
185
|
+
const body = s.slice(1, -1).trim();
|
|
186
|
+
const obj = {};
|
|
187
|
+
if (body === '') return obj;
|
|
188
|
+
for (const entry of splitTopLevel(body, ',')) {
|
|
189
|
+
const [k, ...rest] = splitTopLevel(entry, ':');
|
|
190
|
+
obj[parseScalar(k)] = parseScalar(rest.join(':'));
|
|
191
|
+
}
|
|
192
|
+
return obj;
|
|
193
|
+
}
|
|
194
|
+
return s;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function isMapEntry(content) {
|
|
198
|
+
// Uma entrada de mapa tem um ':' de topo seguido de espaço ou fim de linha,
|
|
199
|
+
// e não é um item de sequência de bloco, nem um flow list/map inteiro.
|
|
200
|
+
if (content.startsWith('- ') || content === '-') return false;
|
|
201
|
+
if ((content.startsWith('[') && content.endsWith(']')) ||
|
|
202
|
+
(content.startsWith('{') && content.endsWith('}'))) return false;
|
|
203
|
+
let inS = false;
|
|
204
|
+
let inD = false;
|
|
205
|
+
let depth = 0;
|
|
206
|
+
for (let i = 0; i < content.length; i++) {
|
|
207
|
+
const c = content[i];
|
|
208
|
+
if (c === "'" && !inD) inS = !inS;
|
|
209
|
+
else if (c === '"' && !inS) inD = !inD;
|
|
210
|
+
else if (!inS && !inD) {
|
|
211
|
+
if (c === '[' || c === '{') depth++;
|
|
212
|
+
else if (c === ']' || c === '}') depth--;
|
|
213
|
+
else if (c === ':' && depth === 0 && (i === content.length - 1 || content[i + 1] === ' ')) {
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function splitEntry(content) {
|
|
222
|
+
let inS = false;
|
|
223
|
+
let inD = false;
|
|
224
|
+
let depth = 0;
|
|
225
|
+
for (let i = 0; i < content.length; i++) {
|
|
226
|
+
const c = content[i];
|
|
227
|
+
if (c === "'" && !inD) inS = !inS;
|
|
228
|
+
else if (c === '"' && !inS) inD = !inD;
|
|
229
|
+
else if (!inS && !inD) {
|
|
230
|
+
if (c === '[' || c === '{') depth++;
|
|
231
|
+
else if (c === ']' || c === '}') depth--;
|
|
232
|
+
else if (c === ':' && depth === 0 && (i === content.length - 1 || content[i + 1] === ' ')) {
|
|
233
|
+
return { key: content.slice(0, i).trim(), val: content.slice(i + 1).trim() };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return { key: content.trim(), val: '' };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function parseYaml(text) {
|
|
241
|
+
const lines = [];
|
|
242
|
+
text.split(/\r?\n/).forEach((rawLine, idx) => {
|
|
243
|
+
const noComment = stripComment(rawLine);
|
|
244
|
+
if (noComment.trim() === '') return;
|
|
245
|
+
lines.push({
|
|
246
|
+
indent: noComment.length - noComment.trimStart().length,
|
|
247
|
+
content: noComment.trim(),
|
|
248
|
+
n: idx + 1,
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
let i = 0;
|
|
253
|
+
|
|
254
|
+
function parseValue(indent) {
|
|
255
|
+
if (i >= lines.length) return null;
|
|
256
|
+
const c = lines[i].content;
|
|
257
|
+
if (c.startsWith('- ') || c === '-') return parseSeq(indent);
|
|
258
|
+
return parseMap(indent);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function parseSeq(indent) {
|
|
262
|
+
const arr = [];
|
|
263
|
+
while (i < lines.length && lines[i].indent === indent &&
|
|
264
|
+
(lines[i].content.startsWith('- ') || lines[i].content === '-')) {
|
|
265
|
+
const line = lines[i];
|
|
266
|
+
const rest = line.content === '-' ? '' : line.content.slice(2).trim();
|
|
267
|
+
if (rest === '') {
|
|
268
|
+
i++;
|
|
269
|
+
if (i < lines.length && lines[i].indent > indent) arr.push(parseValue(lines[i].indent));
|
|
270
|
+
else arr.push(null);
|
|
271
|
+
} else if (isMapEntry(rest)) {
|
|
272
|
+
const childIndent = indent + 2;
|
|
273
|
+
// Reescreve a linha corrente como entrada de mapa em childIndent e
|
|
274
|
+
// deixa parseMap consumir a semente + entradas irmãs alinhadas.
|
|
275
|
+
lines[i] = { indent: childIndent, content: rest, n: line.n };
|
|
276
|
+
arr.push(parseMap(childIndent));
|
|
277
|
+
} else {
|
|
278
|
+
arr.push(parseScalar(rest));
|
|
279
|
+
i++;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return arr;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function parseMap(indent) {
|
|
286
|
+
const obj = {};
|
|
287
|
+
while (i < lines.length && lines[i].indent === indent && isMapEntry(lines[i].content)) {
|
|
288
|
+
const { key, val } = splitEntry(lines[i].content);
|
|
289
|
+
if (val === '') {
|
|
290
|
+
i++;
|
|
291
|
+
if (i < lines.length && lines[i].indent > indent) {
|
|
292
|
+
obj[key] = parseValue(lines[i].indent);
|
|
293
|
+
} else if (i < lines.length && lines[i].indent === indent &&
|
|
294
|
+
(lines[i].content.startsWith('- ') || lines[i].content === '-')) {
|
|
295
|
+
obj[key] = parseSeq(indent);
|
|
296
|
+
} else {
|
|
297
|
+
obj[key] = null;
|
|
298
|
+
}
|
|
299
|
+
} else {
|
|
300
|
+
obj[key] = parseScalar(val);
|
|
301
|
+
i++;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return obj;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return parseValue(lines.length ? lines[0].indent : 0);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ---------------------------------------------------------------------------
|
|
311
|
+
// Validação do contrato.
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
|
|
314
|
+
class Fail extends Error {}
|
|
315
|
+
function fail(msg) {
|
|
316
|
+
throw new Fail(msg);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function podRootFor(workflowPath, override) {
|
|
320
|
+
if (override) return path.resolve(override);
|
|
321
|
+
const dir = path.dirname(workflowPath);
|
|
322
|
+
// Convenção: <pod>/workflows/wf-*.yaml → raiz do pod é o pai de workflows/.
|
|
323
|
+
if (path.basename(dir) === 'workflows') return path.dirname(dir);
|
|
324
|
+
return dir;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function checkSchema(doc) {
|
|
328
|
+
if (doc === null || typeof doc !== 'object' || Array.isArray(doc)) {
|
|
329
|
+
fail('raiz do documento não é um mapa YAML');
|
|
330
|
+
}
|
|
331
|
+
const sm = doc.state_machine;
|
|
332
|
+
if (!sm || typeof sm !== 'object') fail('campo obrigatório ausente: state_machine');
|
|
333
|
+
if (!Array.isArray(sm.states) || sm.states.length === 0) {
|
|
334
|
+
fail('state_machine.states ausente ou vazio (esperado lista de estados)');
|
|
335
|
+
}
|
|
336
|
+
for (const req of ['init', 'complete', 'failed']) {
|
|
337
|
+
if (!sm.states.includes(req)) fail(`state_machine.states não contém o estado obrigatório "${req}"`);
|
|
338
|
+
}
|
|
339
|
+
const terminal = Array.isArray(sm.terminal) ? sm.terminal : [];
|
|
340
|
+
if (!terminal.includes('failed')) fail('state_machine.terminal deve incluir "failed" (estado terminal)');
|
|
341
|
+
if (!Array.isArray(doc.steps) || doc.steps.length === 0) {
|
|
342
|
+
fail('campo obrigatório ausente: steps[] (esperado lista não-vazia)');
|
|
343
|
+
}
|
|
344
|
+
doc.steps.forEach((step, idx) => {
|
|
345
|
+
if (!step || typeof step !== 'object' || Array.isArray(step)) {
|
|
346
|
+
fail(`steps[${idx}] não é um mapa`);
|
|
347
|
+
}
|
|
348
|
+
if (typeof step.id !== 'string' || step.id.trim() === '') {
|
|
349
|
+
fail(`steps[${idx}] sem campo obrigatório "id"`);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
const seen = new Set();
|
|
353
|
+
for (const step of doc.steps) {
|
|
354
|
+
if (seen.has(step.id)) fail(`id de etapa duplicado: "${step.id}"`);
|
|
355
|
+
seen.add(step.id);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function checkStepCompleteness(doc) {
|
|
360
|
+
// AC2/AC4: toda etapa tem agent + template + outputs (não-vazio) + veto.
|
|
361
|
+
for (const step of doc.steps) {
|
|
362
|
+
const id = step.id;
|
|
363
|
+
if (typeof step.agent !== 'string' || step.agent.trim() === '') {
|
|
364
|
+
fail(`etapa "${id}": campo obrigatório ausente: agent`);
|
|
365
|
+
}
|
|
366
|
+
if (typeof step.template !== 'string' || step.template.trim() === '') {
|
|
367
|
+
fail(`etapa "${id}": campo obrigatório ausente: template`);
|
|
368
|
+
}
|
|
369
|
+
if (!Array.isArray(step.outputs) || step.outputs.length === 0) {
|
|
370
|
+
fail(`etapa "${id}": campo obrigatório ausente ou vazio: outputs (artefato declarado)`);
|
|
371
|
+
}
|
|
372
|
+
if (!step.veto || typeof step.veto !== 'object') {
|
|
373
|
+
fail(`etapa "${id}": campo obrigatório ausente: veto`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function checkVetoMachineEvaluable(doc) {
|
|
379
|
+
// AC7 / ADR-002: veto.assertions[] reduz a uma das 5 formas estruturadas.
|
|
380
|
+
for (const step of doc.steps) {
|
|
381
|
+
const id = step.id;
|
|
382
|
+
const assertions = step.veto.assertions;
|
|
383
|
+
if (!Array.isArray(assertions) || assertions.length === 0) {
|
|
384
|
+
fail(`etapa "${id}": veto.assertions ausente ou vazio (ADR-002: veto deve ser asserção de máquina, não prosa)`);
|
|
385
|
+
}
|
|
386
|
+
assertions.forEach((a, ai) => {
|
|
387
|
+
if (typeof a === 'string' || a === null || typeof a !== 'object' || Array.isArray(a)) {
|
|
388
|
+
fail(`etapa "${id}": veto.assertions[${ai}] é prosa/valor livre, não asserção estruturada — proibido por ADR-002 (formas válidas: ${Object.keys(VETO_TYPES).join(', ')})`);
|
|
389
|
+
}
|
|
390
|
+
if (typeof a.type !== 'string' || !(a.type in VETO_TYPES)) {
|
|
391
|
+
fail(`etapa "${id}": veto.assertions[${ai}].type inválido ("${a.type}") — não redutível a asserção de máquina (ADR-002; formas válidas: ${Object.keys(VETO_TYPES).join(', ')})`);
|
|
392
|
+
}
|
|
393
|
+
for (const req of VETO_TYPES[a.type]) {
|
|
394
|
+
if (!(req in a) || a[req] === null || a[req] === '') {
|
|
395
|
+
fail(`etapa "${id}": veto.assertions[${ai}] (type=${a.type}) sem campo obrigatório "${req}"`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (a.type === 'field_matches') {
|
|
399
|
+
try {
|
|
400
|
+
new RegExp(String(a.pattern));
|
|
401
|
+
} catch (e) {
|
|
402
|
+
fail(`etapa "${id}": veto.assertions[${ai}] field_matches com regex inválida: ${e.message}`);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (a.type === 'command_check') {
|
|
406
|
+
try {
|
|
407
|
+
checkCommandCheckSafety(a);
|
|
408
|
+
} catch (e) {
|
|
409
|
+
fail(`etapa "${id}": veto.assertions[${ai}] ${e.message}`);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
if (a.type === 'json_schema_valid') {
|
|
413
|
+
try {
|
|
414
|
+
checkJsonSchemaExtract(a);
|
|
415
|
+
} catch (e) {
|
|
416
|
+
fail(`etapa "${id}": veto.assertions[${ai}] ${e.message}`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* SF7.3/ADR-016 — valida a FORMA do campo OPCIONAL `anti_path` por etapa (instrução
|
|
425
|
+
* negativa escopo-de-etapa). Ausente = no-op (retrocompat byte-idêntica). Quando presente:
|
|
426
|
+
* array de `{ rule: string não-vazio, mechanized_by: null | <id de uma veto.assertion da
|
|
427
|
+
* MESMA etapa> }`. `mechanized_by` que não referencia um id real de asserção da etapa =
|
|
428
|
+
* FAIL estático (deriva vira erro, não passa a runtime). A enforcement dos itens mecanizados
|
|
429
|
+
* é a da própria asserção pareada (runner genérico); a injeção do não-mecanizado é advisory.
|
|
430
|
+
*/
|
|
431
|
+
function checkAntiPath(doc) {
|
|
432
|
+
for (const step of doc.steps) {
|
|
433
|
+
if (step.anti_path == null) continue; // OPCIONAL — só valida quando presente
|
|
434
|
+
const ap = step.anti_path;
|
|
435
|
+
if (!Array.isArray(ap)) fail(`etapa "${step.id}": anti_path deve ser um array (ou ausente)`);
|
|
436
|
+
const assertions = (step.veto && Array.isArray(step.veto.assertions)) ? step.veto.assertions : [];
|
|
437
|
+
const ids = new Set(assertions.filter((a) => a && typeof a.id === 'string' && a.id.trim()).map((a) => a.id));
|
|
438
|
+
ap.forEach((item, i) => {
|
|
439
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
440
|
+
fail(`etapa "${step.id}": anti_path[${i}] deve ser um mapa { rule, mechanized_by }`);
|
|
441
|
+
}
|
|
442
|
+
if (typeof item.rule !== 'string' || item.rule.trim() === '') {
|
|
443
|
+
fail(`etapa "${step.id}": anti_path[${i}].rule ausente ou vazio (instrução negativa obrigatória)`);
|
|
444
|
+
}
|
|
445
|
+
if ('mechanized_by' in item && item.mechanized_by !== null) {
|
|
446
|
+
if (typeof item.mechanized_by !== 'string') {
|
|
447
|
+
fail(`etapa "${step.id}": anti_path[${i}].mechanized_by deve ser null ou o id (string) de uma veto.assertion da etapa`);
|
|
448
|
+
}
|
|
449
|
+
if (!ids.has(item.mechanized_by)) {
|
|
450
|
+
fail(`etapa "${step.id}": anti_path[${i}].mechanized_by "${item.mechanized_by}" não referencia nenhuma veto.assertion com esse id nesta etapa (item mecanizável sem veto que o enforça)`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* SF10.1/ADR-018 D2 — valida a FORMA do campo OPCIONAL `knowledge:` por etapa (lista ordenada
|
|
459
|
+
* de knowledge/*.md citados pela fase) e do `knowledge_exclude:` opcional no topo. Ausente =
|
|
460
|
+
* no-op (retrocompat byte-idêntica — precedente `anti_path`). Aqui só a FORMA: QUANDO declarado,
|
|
461
|
+
* o campo é uma lista de strings não-vazias. [SF11.2/DF-SF11.2-4] A EXISTÊNCIA em disco passou a
|
|
462
|
+
* ser checada por `checkKnowledgeExists` (paridade com `pod-export-compile.cjs:236`); as
|
|
463
|
+
* demais semânticas (superconjunto das citações, órfãos) seguem só no COMPILADOR de export.
|
|
464
|
+
*/
|
|
465
|
+
function checkKnowledge(doc) {
|
|
466
|
+
if (doc.knowledge_exclude != null) {
|
|
467
|
+
if (!Array.isArray(doc.knowledge_exclude)) fail('knowledge_exclude (topo) deve ser um array de strings (ou ausente)');
|
|
468
|
+
doc.knowledge_exclude.forEach((k, i) => {
|
|
469
|
+
if (typeof k !== 'string' || k.trim() === '') fail(`knowledge_exclude[${i}] deve ser string não-vazia`);
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
for (const step of doc.steps) {
|
|
473
|
+
if (step.knowledge == null) continue; // OPCIONAL — só valida quando presente
|
|
474
|
+
if (!Array.isArray(step.knowledge)) fail(`etapa "${step.id}": knowledge deve ser um array de strings (ou ausente)`);
|
|
475
|
+
step.knowledge.forEach((k, i) => {
|
|
476
|
+
if (typeof k !== 'string' || k.trim() === '') fail(`etapa "${step.id}": knowledge[${i}] deve ser string não-vazia (nome de arquivo knowledge/*.md)`);
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* SF11.10/ADR-024 `DF-SF11.10-3` — valida o `checkpoint` por etapa: FORMA + o INVARIANTE do
|
|
483
|
+
* gate humano. Até esta story o validador não olhava NADA sob `checkpoint` (zero ocorrências
|
|
484
|
+
* da palavra no arquivo) — a proibição do ADR-020 ("o modo autônomo JAMAIS aprova") era 100%
|
|
485
|
+
* prosa. Aqui ela vira máquina pela primeira vez.
|
|
486
|
+
*
|
|
487
|
+
* FORMA (ausente/`null` = no-op, retrocompat byte-idêntica — precedente `anti_path`/`knowledge`):
|
|
488
|
+
* - `blocking` booleano obrigatório quando o mapa existe;
|
|
489
|
+
* - chave desconhecida sob `checkpoint` = FALHA (o campo é contrato, não saco de extras);
|
|
490
|
+
* - `criteria` string não-vazia quando `blocking: true` (gate sem critério é gate mudo), e
|
|
491
|
+
* NUNCA um sigilo de escalar de bloco (`DF-SF11.10-17`: o parser descartaria o texto);
|
|
492
|
+
* - `skip_modes` (opcional) = array de modos do conjunto fechado, e só faz sentido com
|
|
493
|
+
* `blocking: true`. AUSENTE ⇒ bloqueia em TODO modo; modo NÃO listado ⇒ bloqueia
|
|
494
|
+
* (enumeração NEGATIVA de propósito: `blocking_in:` falharia ABERTO num modo com typo).
|
|
495
|
+
*
|
|
496
|
+
* INVARIANTE: se o `wf` declara ao menos um checkpoint bloqueante, então para CADA modo do
|
|
497
|
+
* conjunto fechado existe ≥1 bloqueante que NÃO o pula. Um `wf` cujo único gate é pulável em
|
|
498
|
+
* algum modo faria aquele modo rodar SEM NENHUM gate humano — o oposto do que a `skip_modes`
|
|
499
|
+
* existe para permitir (pular UM gate, nunca ficar sem gate).
|
|
500
|
+
*
|
|
501
|
+
* Escopo declarado, não escondido: um `wf` com ZERO checkpoint bloqueante NÃO é reprovado
|
|
502
|
+
* aqui. Ele já existia antes desta story (`wf-criacao-redteam`, `wf-materiais-revisao` usam
|
|
503
|
+
* `blocking: false`), e "todo pod precisa de gate humano" é uma decisão de produto que este
|
|
504
|
+
* ADR não tomou. O invariante desta story é mais estreito e verdadeiro: **`skip_modes` nunca
|
|
505
|
+
* pode ser o motivo de um modo perder o gate**.
|
|
506
|
+
*/
|
|
507
|
+
function checkCheckpoint(doc) {
|
|
508
|
+
const blockingPerMode = new Map(RUN_MODES.map((m) => [m, 0]));
|
|
509
|
+
let anyBlocking = false;
|
|
510
|
+
|
|
511
|
+
for (const step of doc.steps) {
|
|
512
|
+
const cp = step.checkpoint;
|
|
513
|
+
if (cp == null) continue; // OPCIONAL — só valida quando presente.
|
|
514
|
+
if (typeof cp !== 'object' || Array.isArray(cp)) {
|
|
515
|
+
fail(`etapa "${step.id}": checkpoint deve ser um mapa {blocking, criteria?, skip_modes?} (ou null/ausente)`);
|
|
516
|
+
}
|
|
517
|
+
for (const k of Object.keys(cp)) {
|
|
518
|
+
if (!CHECKPOINT_KEYS.includes(k)) {
|
|
519
|
+
fail(`etapa "${step.id}": checkpoint.${k} é chave desconhecida — chaves válidas: ${CHECKPOINT_KEYS.join(', ')} ` +
|
|
520
|
+
`(chave de um motor mais novo? rode "update --scope motor")`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
if (typeof cp.blocking !== 'boolean') {
|
|
524
|
+
fail(`etapa "${step.id}": checkpoint.blocking deve ser booleano (recebeu ${JSON.stringify(cp.blocking)})`);
|
|
525
|
+
}
|
|
526
|
+
if (cp.skip_modes != null) {
|
|
527
|
+
if (!Array.isArray(cp.skip_modes)) {
|
|
528
|
+
fail(`etapa "${step.id}": checkpoint.skip_modes deve ser um array de modos (ou ausente) — modos válidos: ${RUN_MODES.join(', ')}`);
|
|
529
|
+
}
|
|
530
|
+
cp.skip_modes.forEach((m, i) => {
|
|
531
|
+
if (typeof m !== 'string' || !RUN_MODES.includes(m)) {
|
|
532
|
+
fail(`etapa "${step.id}": checkpoint.skip_modes[${i}] = ${JSON.stringify(m)} não é um modo de run válido ` +
|
|
533
|
+
`(conjunto fechado: ${RUN_MODES.join(', ')})`);
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
if (cp.blocking !== true) {
|
|
537
|
+
fail(`etapa "${step.id}": checkpoint.skip_modes só faz sentido com blocking: true ` +
|
|
538
|
+
`(um checkpoint não-bloqueante não para em modo nenhum — nada a pular)`);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (cp.blocking !== true) continue;
|
|
542
|
+
if (typeof cp.criteria !== 'string' || cp.criteria.trim() === '') {
|
|
543
|
+
fail(`etapa "${step.id}": checkpoint.criteria ausente ou vazio com blocking: true — ` +
|
|
544
|
+
`um gate humano sem critério escrito não é gate, é pausa (ADR-002: o critério é o que o humano julga)`);
|
|
545
|
+
}
|
|
546
|
+
// SF11.10/ADR-024 `DF-SF11.10-17` — o parser YAML-lite NÃO suporta escalar de bloco e falha
|
|
547
|
+
// em SILÊNCIO: `criteria: |` vira a string "|" e as linhas seguintes são DESCARTADAS. Sem
|
|
548
|
+
// esta recusa, a checagem de "não-vazia" acima aceita ("|" é não-vazia) e o gate nasce mudo
|
|
549
|
+
// pelo idioma YAML mais natural para texto longo. Recusa com causa nomeada > aceite mudo.
|
|
550
|
+
// (Causa-raiz — o parser ignorar o sigilo em QUALQUER campo — é do épico do motor.)
|
|
551
|
+
if (/^[|>][0-9+-]*$/.test(cp.criteria.trim())) {
|
|
552
|
+
fail(`etapa "${step.id}": checkpoint.criteria = ${JSON.stringify(cp.criteria.trim())} é sigilo de escalar ` +
|
|
553
|
+
`de bloco YAML (| |- |+ > >- >+), e o parser do POWER CLAUDE NÃO suporta essa forma: as linhas ` +
|
|
554
|
+
`indentadas abaixo dele são DESCARTADAS em silêncio e o gate nasceria mudo. Escreva o critério ` +
|
|
555
|
+
`em UMA linha física, entre aspas — ex.: criteria: "o humano confirma X; rejeita se Y" ` +
|
|
556
|
+
`(ADR-024 DF-SF11.10-17)`);
|
|
557
|
+
}
|
|
558
|
+
anyBlocking = true;
|
|
559
|
+
const skip = new Set(Array.isArray(cp.skip_modes) ? cp.skip_modes : []);
|
|
560
|
+
for (const m of RUN_MODES) {
|
|
561
|
+
if (!skip.has(m)) blockingPerMode.set(m, blockingPerMode.get(m) + 1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (!anyBlocking) return; // wf sem gate humano nenhum: pré-existente, fora deste invariante.
|
|
566
|
+
for (const m of RUN_MODES) {
|
|
567
|
+
if (blockingPerMode.get(m) === 0) {
|
|
568
|
+
fail(`invariante do gate humano violado (ADR-024 DF-SF11.10-3): o modo "${m}" ficaria SEM ` +
|
|
569
|
+
`nenhum checkpoint bloqueante — todo checkpoint com blocking: true deste workflow o lista ` +
|
|
570
|
+
`em skip_modes. Cada modo do conjunto fechado (${RUN_MODES.join(', ')}) precisa de ao menos ` +
|
|
571
|
+
`um gate NÃO-pulável; pular um gate é permitido, ficar sem gate não é (ADR-020: o modo ` +
|
|
572
|
+
`autônomo JAMAIS aprova — se não há gate, não há o que ele espere)`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
function checkDag(doc) {
|
|
578
|
+
// AC3: depends_on referencia ids válidos; grafo acíclico (fluxo unidirecional).
|
|
579
|
+
const ids = new Set(doc.steps.map((s) => s.id));
|
|
580
|
+
const edges = new Map(); // step -> [deps]
|
|
581
|
+
for (const step of doc.steps) {
|
|
582
|
+
const deps = Array.isArray(step.depends_on) ? step.depends_on : [];
|
|
583
|
+
for (const d of deps) {
|
|
584
|
+
if (!ids.has(d)) fail(`etapa "${step.id}": depends_on referencia etapa inexistente "${d}"`);
|
|
585
|
+
}
|
|
586
|
+
edges.set(step.id, deps);
|
|
587
|
+
}
|
|
588
|
+
// DFS com detecção de ciclo, reportando o ciclo.
|
|
589
|
+
const WHITE = 0;
|
|
590
|
+
const GRAY = 1;
|
|
591
|
+
const BLACK = 2;
|
|
592
|
+
const color = new Map(doc.steps.map((s) => [s.id, WHITE]));
|
|
593
|
+
const stack = [];
|
|
594
|
+
function visit(node) {
|
|
595
|
+
color.set(node, GRAY);
|
|
596
|
+
stack.push(node);
|
|
597
|
+
for (const dep of edges.get(node)) {
|
|
598
|
+
if (color.get(dep) === GRAY) {
|
|
599
|
+
const cycleStart = stack.indexOf(dep);
|
|
600
|
+
const cycle = stack.slice(cycleStart).concat(dep);
|
|
601
|
+
fail(`ciclo no DAG (depends_on não é acíclico): ${cycle.join(' -> ')}`);
|
|
602
|
+
}
|
|
603
|
+
if (color.get(dep) === WHITE) visit(dep);
|
|
604
|
+
}
|
|
605
|
+
stack.pop();
|
|
606
|
+
color.set(node, BLACK);
|
|
607
|
+
}
|
|
608
|
+
for (const step of doc.steps) {
|
|
609
|
+
if (color.get(step.id) === WHITE) visit(step.id);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function ancestorsOf(doc, id) {
|
|
614
|
+
// Conjunto de todas as etapas alcançáveis para trás por `depends_on` a partir de `id`.
|
|
615
|
+
const dep = new Map(doc.steps.map((s) => [s.id, Array.isArray(s.depends_on) ? s.depends_on : []]));
|
|
616
|
+
const seen = new Set();
|
|
617
|
+
const stack = [...(dep.get(id) || [])];
|
|
618
|
+
while (stack.length > 0) {
|
|
619
|
+
const n = stack.pop();
|
|
620
|
+
if (seen.has(n)) continue;
|
|
621
|
+
seen.add(n);
|
|
622
|
+
for (const d of (dep.get(n) || [])) stack.push(d);
|
|
623
|
+
}
|
|
624
|
+
return seen;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function checkFixCycle(doc) {
|
|
628
|
+
// Débito herdado da SF1.1 (gate sf1.1 finding #1; SPEC §B2a linha 72 / linha 85):
|
|
629
|
+
// o fix-loop explícito é a ÚNICA aresta para trás permitida. Ele NÃO é `depends_on`
|
|
630
|
+
// (isso seria ciclo, reprovado por checkDag) — é um campo dedicado `fix_cycle` que
|
|
631
|
+
// aponta para uma etapa a MONTANTE e é isento da checagem de aciclicidade. Uma
|
|
632
|
+
// aresta para trás disfarçada de `depends_on` continua sendo ciclo → FALHA.
|
|
633
|
+
const ids = new Set(doc.steps.map((s) => s.id));
|
|
634
|
+
for (const step of doc.steps) {
|
|
635
|
+
if (step.fix_cycle == null) continue;
|
|
636
|
+
const fc = step.fix_cycle;
|
|
637
|
+
if (typeof fc !== 'object' || Array.isArray(fc)) {
|
|
638
|
+
fail(`etapa "${step.id}": fix_cycle deve ser um mapa {to, max_iterations}`);
|
|
639
|
+
}
|
|
640
|
+
if (typeof fc.to !== 'string' || !ids.has(fc.to)) {
|
|
641
|
+
fail(`etapa "${step.id}": fix_cycle.to referencia etapa inexistente "${fc.to}"`);
|
|
642
|
+
}
|
|
643
|
+
if (fc.to === step.id) {
|
|
644
|
+
fail(`etapa "${step.id}": fix_cycle.to não pode apontar para a própria etapa`);
|
|
645
|
+
}
|
|
646
|
+
if ('max_iterations' in fc && fc.max_iterations != null &&
|
|
647
|
+
(!Number.isInteger(fc.max_iterations) || fc.max_iterations < 1)) {
|
|
648
|
+
fail(`etapa "${step.id}": fix_cycle.max_iterations deve ser inteiro >= 1`);
|
|
649
|
+
}
|
|
650
|
+
if (!ancestorsOf(doc, step.id).has(fc.to)) {
|
|
651
|
+
fail(`etapa "${step.id}": fix_cycle.to "${fc.to}" deve ser uma etapa A MONTANTE ` +
|
|
652
|
+
`(ancestral via depends_on) — o fix-loop volta para trás, nunca para frente; ` +
|
|
653
|
+
`aresta para trás disfarçada de depends_on é ciclo (proibida)`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function topoOrder(doc) {
|
|
659
|
+
// Ordenação topológica determinística: a cada passo, escolhe a PRIMEIRA etapa (na
|
|
660
|
+
// ordem de declaração) cujas dependências já foram emitidas. Empates resolvidos pela
|
|
661
|
+
// ordem do arquivo → saída reprodutível. Assume DAG acíclico (checkDag rodou antes).
|
|
662
|
+
const ids = doc.steps.map((s) => s.id);
|
|
663
|
+
const dep = new Map(doc.steps.map((s) => [s.id, Array.isArray(s.depends_on) ? s.depends_on : []]));
|
|
664
|
+
const order = [];
|
|
665
|
+
const done = new Set();
|
|
666
|
+
while (order.length < ids.length) {
|
|
667
|
+
const next = ids.find((id) => !done.has(id) && dep.get(id).every((d) => done.has(d)));
|
|
668
|
+
if (!next) fail('topoOrder: ciclo residual (checkDag deveria ter reprovado antes)');
|
|
669
|
+
order.push(next);
|
|
670
|
+
done.add(next);
|
|
671
|
+
}
|
|
672
|
+
return order;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function checkTemplatesExist(doc, podRoot) {
|
|
676
|
+
// AC5: todo template: existe em disco (path resolvido a partir da raiz do pod).
|
|
677
|
+
for (const step of doc.steps) {
|
|
678
|
+
const resolved = path.resolve(podRoot, step.template);
|
|
679
|
+
if (!fs.existsSync(resolved)) {
|
|
680
|
+
fail(`etapa "${step.id}": template inexistente em disco — ${path.relative(REPO_ROOT, resolved)}`);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* SF11.2/ADR-021 `DF-SF11.2-4` (item 2) — paridade estática de EXISTÊNCIA do `knowledge:`
|
|
687
|
+
* declarado por etapa. Antes, aqui só a FORMA do campo era checada (`checkKnowledge`),
|
|
688
|
+
* enquanto o compilador de export já falhava pela existência (`pod-export-compile.cjs:236`)
|
|
689
|
+
* sobre o MESMO `wf`: o driver de dev era mais frouxo que o export. Agora o runner também
|
|
690
|
+
* BLOQUEIA o despacho pela mesma causa (`pc-run-pod.cjs`, `requiredReading`); este check é a
|
|
691
|
+
* versão estática, para o defeito aparecer no `validate` e não só na hora de rodar.
|
|
692
|
+
*
|
|
693
|
+
* `knowledge:` segue OPCIONAL aqui (o export o EXIGE, D2) — etapa sem o campo é no-op.
|
|
694
|
+
* Retrocompat, `DF-SF11.2-10`.
|
|
695
|
+
*
|
|
696
|
+
* [AUTO-DECISION SF11.2] A existência do AGENTE (`agents/<step.agent>.md`) NÃO é checada
|
|
697
|
+
* aqui, embora o runner a exija antes de despachar e o export a exija para compilar
|
|
698
|
+
* (`pod-export-compile.cjs:214`). Motivo MEDIDO, não preferência: `forge-generate.cjs`
|
|
699
|
+
* escreve `workflows/wf-*.yaml`, `templates/` e `squad-io.yaml`, mas NÃO escreve
|
|
700
|
+
* `agents/*.md` — os agentes são autorados depois, pela skill do forge. Um `wf` recém-gerado
|
|
701
|
+
* é um estado intermediário LEGÍTIMO em que o agente ainda não existe, e o próprio contrato
|
|
702
|
+
* do forge exige que esse `wf` passe neste validador (`scripts/forge.test.cjs`, AC4).
|
|
703
|
+
* Exigir o agente aqui reprovaria todo pod no instante seguinte à geração. O gate de agente
|
|
704
|
+
* vive onde a premissa "o pod está pronto" é verdadeira: no DESPACHO (runner) e no EXPORT.
|
|
705
|
+
*/
|
|
706
|
+
function checkKnowledgeExists(doc, podRoot) {
|
|
707
|
+
for (const step of doc.steps) {
|
|
708
|
+
if (step.knowledge == null) continue; // campo OPCIONAL — retrocompat
|
|
709
|
+
for (const k of step.knowledge) {
|
|
710
|
+
const kRel = path.join('knowledge', String(k));
|
|
711
|
+
const abs = path.resolve(podRoot, kRel);
|
|
712
|
+
const root = path.resolve(podRoot);
|
|
713
|
+
// Confinamento: mesma régua do runner (`pc-run-pod.cjs`, `requiredReading`) — knowledge
|
|
714
|
+
// que escapa da raiz do pod é recusado ainda que o arquivo exista lá fora.
|
|
715
|
+
if (!(abs === root || abs.startsWith(root + path.sep))) {
|
|
716
|
+
fail(`etapa "${step.id}": knowledge declarado escapa da raiz do pod — ${kRel} ` +
|
|
717
|
+
`(leitura obrigatória da etapa: só arquivos sob a raiz do pod podem ser declarados)`);
|
|
718
|
+
}
|
|
719
|
+
if (!fs.existsSync(abs)) {
|
|
720
|
+
fail(`etapa "${step.id}": knowledge declarado inexistente em disco — ${kRel} ` +
|
|
721
|
+
`(leitura obrigatória da etapa: o runner recusa despachar e o export recusa compilar)`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function checkVetoOrthogonality(doc, podRoot) {
|
|
728
|
+
// ADR-005 Decisão A (enforcement): um veto `section_exists` cuja seção coincide com
|
|
729
|
+
// um heading `##+` do template DA ETAPA é logicamente IMPLICADO pela conformidade de
|
|
730
|
+
// template (o runner roda checkTemplateConformance antes das asserções) — não existe
|
|
731
|
+
// artefato que viole só o veto sem violar a forma, tornando a Prova 3b do smoke
|
|
732
|
+
// inconstruível. Essa classe degenerada vira ERRO ESTÁTICO aqui. Régua dos headings
|
|
733
|
+
// idêntica à de checkTemplateConformance no runner: linha trimada casando /^#{2,6}\s+\S/.
|
|
734
|
+
const templateHeadings = (t) =>
|
|
735
|
+
new Set(t.split(/\r?\n/).map((l) => l.trim()).filter((l) => /^#{2,6}\s+\S/.test(l)));
|
|
736
|
+
for (const step of doc.steps) {
|
|
737
|
+
const assertions = (step.veto && Array.isArray(step.veto.assertions)) ? step.veto.assertions : [];
|
|
738
|
+
const hasSectionExists = assertions.some((a) => a && a.type === 'section_exists');
|
|
739
|
+
if (!hasSectionExists) continue;
|
|
740
|
+
const tmplPath = path.resolve(podRoot, step.template);
|
|
741
|
+
const wanted = templateHeadings(fs.readFileSync(tmplPath, 'utf8'));
|
|
742
|
+
for (const a of assertions) {
|
|
743
|
+
if (a && a.type === 'section_exists' && wanted.has(String(a.section).trim())) {
|
|
744
|
+
fail(`etapa "${step.id}": veto não-ortogonal — section_exists "${String(a.section).trim()}" ` +
|
|
745
|
+
`duplica a conformidade de template (ADR-005); use asserção de conteúdo ` +
|
|
746
|
+
`(field_matches/count_gte/file_referenced_exists/json_schema_valid)`);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function checkClosedHandoffs(doc) {
|
|
753
|
+
// AC6: todo output é consumido por um input a jusante OU é artefato final
|
|
754
|
+
// (produzido por um passo-sumidouro — etapa da qual nenhuma outra depende).
|
|
755
|
+
const consumed = new Set();
|
|
756
|
+
for (const step of doc.steps) {
|
|
757
|
+
const inputs = Array.isArray(step.inputs) ? step.inputs : [];
|
|
758
|
+
for (const inp of inputs) consumed.add(inp);
|
|
759
|
+
}
|
|
760
|
+
const hasDependent = new Set();
|
|
761
|
+
for (const step of doc.steps) {
|
|
762
|
+
for (const d of (Array.isArray(step.depends_on) ? step.depends_on : [])) hasDependent.add(d);
|
|
763
|
+
}
|
|
764
|
+
for (const step of doc.steps) {
|
|
765
|
+
const isSink = !hasDependent.has(step.id);
|
|
766
|
+
const outputs = Array.isArray(step.outputs) ? step.outputs : [];
|
|
767
|
+
for (const out of outputs) {
|
|
768
|
+
if (!consumed.has(out) && !isSink) {
|
|
769
|
+
fail(`handoff aberto: output "${out}" da etapa "${step.id}" não é consumido por nenhuma etapa a jusante nem é artefato final`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
function validateWorkflow(workflowPath, podRootOverride) {
|
|
776
|
+
let doc;
|
|
777
|
+
try {
|
|
778
|
+
doc = parseYaml(fs.readFileSync(workflowPath, 'utf8'));
|
|
779
|
+
} catch (e) {
|
|
780
|
+
fail(`YAML malformado: ${e.message}`);
|
|
781
|
+
}
|
|
782
|
+
checkSchema(doc);
|
|
783
|
+
checkStepCompleteness(doc);
|
|
784
|
+
checkVetoMachineEvaluable(doc);
|
|
785
|
+
checkAntiPath(doc); // SF7.3 — no-op quando nenhuma etapa declara anti_path (retrocompat)
|
|
786
|
+
checkKnowledge(doc); // SF10.1/D2 — no-op quando nenhuma etapa declara knowledge (retrocompat)
|
|
787
|
+
checkCheckpoint(doc); // SF11.10/DF-SF11.10-3 — forma do checkpoint + invariante do gate humano
|
|
788
|
+
checkDag(doc);
|
|
789
|
+
checkFixCycle(doc);
|
|
790
|
+
const podRoot = podRootFor(workflowPath, podRootOverride);
|
|
791
|
+
checkTemplatesExist(doc, podRoot);
|
|
792
|
+
checkKnowledgeExists(doc, podRoot); // SF11.2/DF-SF11.2-4: paridade de existência com o export.
|
|
793
|
+
checkVetoOrthogonality(doc, podRoot); // ADR-005: depois de garantir template em disco.
|
|
794
|
+
checkClosedHandoffs(doc);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function collectGlob(root) {
|
|
798
|
+
const out = [];
|
|
799
|
+
const stack = [path.resolve(root)];
|
|
800
|
+
while (stack.length > 0) {
|
|
801
|
+
const dir = stack.pop();
|
|
802
|
+
if (!fs.existsSync(dir)) continue;
|
|
803
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
804
|
+
const p = path.join(dir, entry.name);
|
|
805
|
+
if (entry.isDirectory()) stack.push(p);
|
|
806
|
+
else if (entry.isFile() && path.basename(dir) === 'workflows' &&
|
|
807
|
+
/^wf-.*\.ya?ml$/.test(entry.name)) {
|
|
808
|
+
out.push(p);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return out.sort();
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function main() {
|
|
816
|
+
const args = process.argv.slice(2);
|
|
817
|
+
let podRootOverride = null;
|
|
818
|
+
const targets = [];
|
|
819
|
+
for (let a = 0; a < args.length; a++) {
|
|
820
|
+
if (args[a] === '--pod-root') {
|
|
821
|
+
podRootOverride = args[++a];
|
|
822
|
+
} else if (args[a] === '--glob') {
|
|
823
|
+
targets.push(...collectGlob(args[++a]));
|
|
824
|
+
} else {
|
|
825
|
+
targets.push(args[a]);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
if (targets.length === 0) {
|
|
830
|
+
console.error('Uso: node scripts/validate-pod-workflow.cjs <wf.yaml> [...] [--pod-root <dir>] [--glob <dir>]');
|
|
831
|
+
process.exit(2);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
let ok = 0;
|
|
835
|
+
for (const t of targets) {
|
|
836
|
+
const rel = path.relative(REPO_ROOT, path.resolve(t));
|
|
837
|
+
if (!fs.existsSync(t)) {
|
|
838
|
+
console.error(`[FALHA] ${rel}: arquivo não encontrado`);
|
|
839
|
+
process.exit(1);
|
|
840
|
+
}
|
|
841
|
+
try {
|
|
842
|
+
validateWorkflow(t, podRootOverride);
|
|
843
|
+
console.log(`[OK ] ${rel}`);
|
|
844
|
+
ok++;
|
|
845
|
+
} catch (e) {
|
|
846
|
+
if (e instanceof Fail) {
|
|
847
|
+
console.error(`[FALHA] ${rel}: ${e.message}`);
|
|
848
|
+
process.exit(1);
|
|
849
|
+
}
|
|
850
|
+
throw e;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
console.log(`\n${ok} workflow(s) validado(s). Contrato bem-formado.`);
|
|
854
|
+
process.exit(0);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
if (require.main === module) {
|
|
858
|
+
main();
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
module.exports = {
|
|
862
|
+
parseYaml,
|
|
863
|
+
validateWorkflow,
|
|
864
|
+
podRootFor,
|
|
865
|
+
topoOrder,
|
|
866
|
+
ancestorsOf,
|
|
867
|
+
VETO_TYPES,
|
|
868
|
+
VETO_CHECKS_ROOT,
|
|
869
|
+
RUN_MODES, // SF11.10/DF-SF11.10-5 — conjunto fechado de ritmos (fonte única).
|
|
870
|
+
CHECKPOINT_KEYS, // SF11.10/DF-SF11.10-6 — chaves conhecidas sob `checkpoint`.
|
|
871
|
+
checkCommandCheckSafety,
|
|
872
|
+
checkJsonSchemaExtract,
|
|
873
|
+
checkAntiPath,
|
|
874
|
+
checkKnowledge,
|
|
875
|
+
checkCheckpoint,
|
|
876
|
+
checkKnowledgeExists,
|
|
877
|
+
Fail,
|
|
878
|
+
};
|