@getarbiter/cli 0.5.0
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.
- package/CHANGELOG.md +713 -0
- package/LICENSE +202 -0
- package/NOTICE +31 -0
- package/PRIVACY.md +111 -0
- package/README.md +237 -0
- package/THIRD_PARTY_LICENSES.md +472 -0
- package/dist/.src-manifest.json +1 -0
- package/dist/capabilities/host-probe.js +3 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +87 -0
- package/dist/commands/adopt-policy.js +3 -0
- package/dist/commands/configure-interactive.js +3 -0
- package/dist/commands/configure.js +5 -0
- package/dist/commands/cross-model-review.js +3 -0
- package/dist/commands/diff.js +20 -0
- package/dist/commands/doc-set.js +3 -0
- package/dist/commands/doctor/clean.js +2 -0
- package/dist/commands/doctor/fail-open-census.js +12 -0
- package/dist/commands/doctor/health.js +11 -0
- package/dist/commands/doctor/prove-gates.js +13 -0
- package/dist/commands/doctor/recover-lock.js +12 -0
- package/dist/commands/doctor/repair-state.js +6 -0
- package/dist/commands/doctor/tool-pin-extract.js +4 -0
- package/dist/commands/doctor/tool-pins.js +9 -0
- package/dist/commands/doctor-interactive.js +2 -0
- package/dist/commands/doctor.js +2 -0
- package/dist/commands/explain.js +19 -0
- package/dist/commands/gate-exec.js +4 -0
- package/dist/commands/gold-audit.js +14 -0
- package/dist/commands/graph.js +5 -0
- package/dist/commands/init/build-arbiter-config.js +1 -0
- package/dist/commands/init/generate.js +42 -0
- package/dist/commands/init/github-setup.js +5 -0
- package/dist/commands/init/maturity-gates.js +10 -0
- package/dist/commands/init/resolve-config.js +3 -0
- package/dist/commands/init/toolchain.js +10 -0
- package/dist/commands/init/types.js +1 -0
- package/dist/commands/init.js +17 -0
- package/dist/commands/method-interactive.js +4 -0
- package/dist/commands/method.js +11 -0
- package/dist/commands/obsidian.js +2 -0
- package/dist/commands/plugin.js +8 -0
- package/dist/commands/pr-merged.js +2 -0
- package/dist/commands/retire-policy.js +3 -0
- package/dist/commands/review-diff.js +3 -0
- package/dist/commands/settings.js +7 -0
- package/dist/commands/ship-config.js +2 -0
- package/dist/commands/ship-profile.js +2 -0
- package/dist/commands/ship-review.js +1 -0
- package/dist/commands/ship-tier.js +2 -0
- package/dist/commands/ship-train.js +2 -0
- package/dist/commands/task-mark.js +2 -0
- package/dist/commands/task-note.js +3 -0
- package/dist/commands/task-record-red.js +2 -0
- package/dist/commands/task-record-tech-debt.js +4 -0
- package/dist/commands/task-ship.js +3 -0
- package/dist/commands/task-state.js +6 -0
- package/dist/commands/task.js +59 -0
- package/dist/commands/update.js +60 -0
- package/dist/commands/upgrade-level-interactive.js +2 -0
- package/dist/commands/upgrade-level.js +7 -0
- package/dist/commands/verify-plan.js +8 -0
- package/dist/commands/verify-tdd.js +2 -0
- package/dist/commands/verify.js +7 -0
- package/dist/commands/wizard/coherence.js +1 -0
- package/dist/commands/worktree-prune.js +4 -0
- package/dist/commands/worktree.js +39 -0
- package/dist/compatibility/cross-language-matrix.json +540 -0
- package/dist/compatibility/index.d.ts +4 -0
- package/dist/compatibility/index.js +2 -0
- package/dist/compatibility/matcher.js +1 -0
- package/dist/compatibility/matrix.json +27 -0
- package/dist/compatibility/parsers.d.ts +38 -0
- package/dist/compatibility/parsers.js +1 -0
- package/dist/compatibility/probe.d.ts +41 -0
- package/dist/compatibility/probe.js +4 -0
- package/dist/compatibility/report.js +2 -0
- package/dist/compatibility/schema.d.ts +80 -0
- package/dist/compatibility/schema.js +1 -0
- package/dist/compatibility/skills-matrix.json +98 -0
- package/dist/compatibility/skills-validator.d.ts +30 -0
- package/dist/compatibility/skills-validator.js +4 -0
- package/dist/compatibility/tooling-proposals.json +5 -0
- package/dist/config/arbiter-ignore.js +3 -0
- package/dist/config/collaboration-mode-defaults.js +1 -0
- package/dist/config/diff.js +1 -0
- package/dist/config/env-overrides.js +3 -0
- package/dist/config/env-registry.js +2 -0
- package/dist/config/levels.js +1 -0
- package/dist/config/migrations/index.js +2 -0
- package/dist/config/migrations/v0-to-v1.js +2 -0
- package/dist/config/migrations/v1-to-v2.js +2 -0
- package/dist/config/migrations/v2-to-v3.js +2 -0
- package/dist/config/migrations/v3-to-v4.js +2 -0
- package/dist/config/override-resolver.js +2 -0
- package/dist/config/resolve-project-config.js +2 -0
- package/dist/config/resolve-project-name.js +2 -0
- package/dist/config/schema.d.ts +480 -0
- package/dist/config/schema.js +1 -0
- package/dist/config/test-pyramid-profiles.js +1 -0
- package/dist/config/thresholds.js +2 -0
- package/dist/conformance/dimensions.js +3 -0
- package/dist/conformance/engine.js +7 -0
- package/dist/conformance/gate-proofs.js +4 -0
- package/dist/conformance/shared.js +2 -0
- package/dist/detectors/axis.js +1 -0
- package/dist/detectors/build.js +2 -0
- package/dist/detectors/existing.js +2 -0
- package/dist/detectors/external-model.js +2 -0
- package/dist/detectors/framework.js +2 -0
- package/dist/detectors/git.js +11 -0
- package/dist/detectors/github.js +2 -0
- package/dist/detectors/lanes.js +5 -0
- package/dist/detectors/language-hooks.js +117 -0
- package/dist/detectors/language.js +2 -0
- package/dist/detectors/package-manager.js +2 -0
- package/dist/detectors/package.js +2 -0
- package/dist/detectors/scaffold-wiring.js +2 -0
- package/dist/detectors/workflow-collision.js +4 -0
- package/dist/evidence/gate-binding.js +4 -0
- package/dist/evidence/git-checks.js +3 -0
- package/dist/evidence/load.js +2 -0
- package/dist/evidence/provenance.js +2 -0
- package/dist/evidence/summary.js +2 -0
- package/dist/evidence/tdd-reexecute.js +2 -0
- package/dist/evidence/tdd.js +4 -0
- package/dist/experimental/index.js +2 -0
- package/dist/experimental/parse-argv.js +2 -0
- package/dist/experimental/registry.js +2 -0
- package/dist/generators/agents-claude.js +2 -0
- package/dist/generators/agents-md.js +2 -0
- package/dist/generators/anti-drift-validators.js +3 -0
- package/dist/generators/anti-proforma.js +2 -0
- package/dist/generators/api-e2e.js +2 -0
- package/dist/generators/api-middleware.js +2 -0
- package/dist/generators/archunit.js +2 -0
- package/dist/generators/audit-toolchain.js +2 -0
- package/dist/generators/auth.js +2 -0
- package/dist/generators/behavioral-tests.js +2 -0
- package/dist/generators/boundaries.js +2 -0
- package/dist/generators/check-all.js +2 -0
- package/dist/generators/check-stack-conformity.js +2 -0
- package/dist/generators/ci-five-lane.js +2 -0
- package/dist/generators/ci-tier.js +2 -0
- package/dist/generators/claude.js +3 -0
- package/dist/generators/codex-hooks.js +2 -0
- package/dist/generators/codex-known-limitations.js +2 -0
- package/dist/generators/codex.js +15 -0
- package/dist/generators/commit-footer.js +2 -0
- package/dist/generators/compliance-menu.js +2 -0
- package/dist/generators/compliance.js +2 -0
- package/dist/generators/conformance.js +2 -0
- package/dist/generators/contract-testing.js +2 -0
- package/dist/generators/coverage.js +2 -0
- package/dist/generators/debt-gates.js +24 -0
- package/dist/generators/debt-ratchet.js +2 -0
- package/dist/generators/derived-class.js +2 -0
- package/dist/generators/doc-set.js +4 -0
- package/dist/generators/docs.js +2 -0
- package/dist/generators/duplication.js +2 -0
- package/dist/generators/e2e-constitution.js +2 -0
- package/dist/generators/env-template.js +2 -0
- package/dist/generators/evidence-retention.js +2 -0
- package/dist/generators/feature-matrix.js +2 -0
- package/dist/generators/frontend-governance.js +2 -0
- package/dist/generators/frontend-quality.js +2 -0
- package/dist/generators/gap.js +2 -0
- package/dist/generators/gdpr.js +2 -0
- package/dist/generators/githooks.js +2 -0
- package/dist/generators/github-setup.js +2 -0
- package/dist/generators/github.js +2 -0
- package/dist/generators/gitignore.js +2 -0
- package/dist/generators/global-invariants.js +3 -0
- package/dist/generators/go-boundaries.js +2 -0
- package/dist/generators/gold-kit.js +2 -0
- package/dist/generators/infra.js +2 -0
- package/dist/generators/integration-testing.js +7 -0
- package/dist/generators/iso27001.js +2 -0
- package/dist/generators/iso9001.js +2 -0
- package/dist/generators/labels.js +2 -0
- package/dist/generators/local-wrapper.js +2 -0
- package/dist/generators/modulith.js +2 -0
- package/dist/generators/mutation.js +2 -0
- package/dist/generators/observability.js +2 -0
- package/dist/generators/operations.js +2 -0
- package/dist/generators/perf-k6.js +2 -0
- package/dist/generators/pharma.js +2 -0
- package/dist/generators/playwright-python.js +2 -0
- package/dist/generators/playwright-ts.js +2 -0
- package/dist/generators/pr-tooling.js +2 -0
- package/dist/generators/python-boundaries.js +2 -0
- package/dist/generators/quality.js +2 -0
- package/dist/generators/registry.js +2 -0
- package/dist/generators/regulated.js +2 -0
- package/dist/generators/resilience.js +2 -0
- package/dist/generators/risk-register.js +2 -0
- package/dist/generators/root.js +2 -0
- package/dist/generators/rust-boundaries.js +2 -0
- package/dist/generators/safety-class.js +2 -0
- package/dist/generators/security.js +2 -0
- package/dist/generators/seed.js +2 -0
- package/dist/generators/self-validation.js +2 -0
- package/dist/generators/ship-driver.js +2 -0
- package/dist/generators/skill-names.json +17 -0
- package/dist/generators/skills.js +2 -0
- package/dist/generators/smoke-journeys.js +2 -0
- package/dist/generators/solo-exception.js +2 -0
- package/dist/generators/ssot.js +2 -0
- package/dist/generators/stride-enforcement.js +2 -0
- package/dist/generators/suppressions.js +2 -0
- package/dist/generators/test-pyramid-manifest.js +3 -0
- package/dist/generators/test-taxonomy.js +2 -0
- package/dist/generators/wiki.js +2 -0
- package/dist/github/branch-protection.js +2 -0
- package/dist/github/classify-gh-error.js +2 -0
- package/dist/github/labels.js +2 -0
- package/dist/github/project-board.js +2 -0
- package/dist/graph/builders/adr.js +4 -0
- package/dist/graph/builders/ast.js +3 -0
- package/dist/graph/builders/canon.js +2 -0
- package/dist/graph/builders/evidence.js +2 -0
- package/dist/graph/builders/inv.js +1 -0
- package/dist/graph/builders/req.js +3 -0
- package/dist/graph/builders/test-nodes.js +3 -0
- package/dist/graph/builders/utils.js +2 -0
- package/dist/graph/load.js +2 -0
- package/dist/graph/model.js +2 -0
- package/dist/graph/query.js +3 -0
- package/dist/graph/store.js +1 -0
- package/dist/i18n/en.json +293 -0
- package/dist/i18n/index.js +4 -0
- package/dist/i18n/types.js +2 -0
- package/dist/integrations/companions.js +2 -0
- package/dist/integrations/external-review.js +10 -0
- package/dist/integrations/plugin-schema.js +2 -0
- package/dist/integrations/skill-detector.js +7 -0
- package/dist/integrations/skills-matrix.js +2 -0
- package/dist/integrations/types.d.ts +23 -0
- package/dist/integrations/types.js +2 -0
- package/dist/internal/cli-deprecation-registry.js +2 -0
- package/dist/internal/deprecate.js +3 -0
- package/dist/internal/experimental-warn.js +3 -0
- package/dist/invariants/catalog.d.ts +2 -0
- package/dist/invariants/catalog.js +1 -0
- package/dist/invariants/filter.d.ts +28 -0
- package/dist/invariants/filter.js +1 -0
- package/dist/invariants/index.d.ts +4 -0
- package/dist/invariants/index.js +2 -0
- package/dist/invariants/tiers.js +1 -0
- package/dist/invariants/types.d.ts +1 -0
- package/dist/invariants/types.js +1 -0
- package/dist/kit/applicability.js +3 -0
- package/dist/kit/audit-report.js +3 -0
- package/dist/kit/brownfield-detect.js +4 -0
- package/dist/kit/canonical-mapping.json +1 -0
- package/dist/kit/catalog.js +2 -0
- package/dist/kit/catalog.json +1 -0
- package/dist/kit/derived.json +1 -0
- package/dist/kit/emit-issues.js +8 -0
- package/dist/kit/measure.js +5 -0
- package/dist/kit/redaction.js +3 -0
- package/dist/kit/schema.js +2 -0
- package/dist/kit/taxonomy.js +2 -0
- package/dist/kit/thresholds.d.ts +12 -0
- package/dist/kit/thresholds.js +2 -0
- package/dist/kit/wave-engine.js +1 -0
- package/dist/recipes/loader.js +6 -0
- package/dist/recipes/schema.js +2 -0
- package/dist/risk/classifier.js +2 -0
- package/dist/risk/sha-check.js +2 -0
- package/dist/state/backups.js +2 -0
- package/dist/state/envelope.js +2 -0
- package/dist/state/generated-manifest.js +3 -0
- package/dist/state/manifest-key.js +2 -0
- package/dist/state/migrations/index.js +1 -0
- package/dist/state/migrations/v0-to-v1.js +2 -0
- package/dist/state/retired-renders.js +2 -0
- package/dist/templates/.nvmrc.ejs +1 -0
- package/dist/templates/HANDOFF.template.md +45 -0
- package/dist/templates/__tests__/contract/error-shape.contract.test.ts.ejs +47 -0
- package/dist/templates/agents-md/AGENTS.md.ejs +517 -0
- package/dist/templates/api-e2e/README.md.ejs +33 -0
- package/dist/templates/api-e2e/go-httptest.go.ejs +43 -0
- package/dist/templates/api-e2e/java-restassured.java.ejs +28 -0
- package/dist/templates/api-e2e/kotlin-restassured.kt.ejs +30 -0
- package/dist/templates/api-e2e/manifest.json.ejs +8 -0
- package/dist/templates/api-e2e/postman.collection.json.ejs +30 -0
- package/dist/templates/api-e2e/python-httpx.py.ejs +17 -0
- package/dist/templates/api-e2e/run.sh.ejs +61 -0
- package/dist/templates/api-e2e/ts-supertest.test.ts.ejs +18 -0
- package/dist/templates/archunit/AntiCyclicTest.java.ejs +29 -0
- package/dist/templates/archunit/AntiProformaExempt.java.ejs +35 -0
- package/dist/templates/archunit/AntiProformaTest.java.ejs +104 -0
- package/dist/templates/archunit/ArchitectureTest.java.ejs +87 -0
- package/dist/templates/archunit/DependencyFlowTest.java.ejs +55 -0
- package/dist/templates/archunit/DomainPurityTest.java.ejs +47 -0
- package/dist/templates/archunit/NamingConventionsTest.java.ejs +54 -0
- package/dist/templates/archunit/NoH2ArchTest.java.ejs +30 -0
- package/dist/templates/archunit/NoMockMvcTest.java.ejs +71 -0
- package/dist/templates/archunit/PortsIndependenceTest.java.ejs +55 -0
- package/dist/templates/archunit/RestAssuredArchTest.java.ejs +32 -0
- package/dist/templates/archunit/RestAssuredBaseIT.java.ejs +66 -0
- package/dist/templates/archunit/TestCoverageArchTest.java.ejs +29 -0
- package/dist/templates/archunit/arch-test-deps-maven.md.ejs +42 -0
- package/dist/templates/archunit/arch-test-deps.gradle.ejs +14 -0
- package/dist/templates/audit/gdpr/check-gdpr-controls.mjs.ejs +102 -0
- package/dist/templates/audit/gdpr/controls-to-gates.md.ejs +42 -0
- package/dist/templates/audit/gdpr/data-flow-map.md.ejs +30 -0
- package/dist/templates/audit/gdpr/dpia.md.ejs +45 -0
- package/dist/templates/audit/generic/audit-gate-rules.md.ejs +24 -0
- package/dist/templates/audit/generic/audit-trail-policy.md.ejs +38 -0
- package/dist/templates/audit/iso27001/iso27001-controls-gate-map.md.ejs +60 -0
- package/dist/templates/auth/setup.md.ejs +661 -0
- package/dist/templates/behavioral-tests/ExampleBehavioralTest.java.ejs +67 -0
- package/dist/templates/behavioral-tests/TESTING_POLICY.md.ejs +116 -0
- package/dist/templates/behavioral-tests/bdd/ExampleBddIT.java.ejs +19 -0
- package/dist/templates/behavioral-tests/bdd/ExampleSteps.java.ejs +61 -0
- package/dist/templates/behavioral-tests/bdd/example.feature.ejs +14 -0
- package/dist/templates/behavioral-tests/bdd/example.steps.ts.ejs +37 -0
- package/dist/templates/behavioral-tests/bdd/example_bdd_test.rs.ejs +64 -0
- package/dist/templates/behavioral-tests/bdd/example_test.go.ejs +80 -0
- package/dist/templates/behavioral-tests/bdd/test_example_bdd.py.ejs +47 -0
- package/dist/templates/behavioral-tests/eslint-playwright.json.ejs +10 -0
- package/dist/templates/behavioral-tests/eslint.config.playwright.mjs.ejs +29 -0
- package/dist/templates/behavioral-tests/example.behavioral.test.ts.ejs +47 -0
- package/dist/templates/behavioral-tests/example_behavioral_test.go.ejs +51 -0
- package/dist/templates/behavioral-tests/example_behavioral_test.rs.ejs +47 -0
- package/dist/templates/behavioral-tests/test_example_behavioral.py.ejs +40 -0
- package/dist/templates/boundaries/.eslintrc-boundaries.cjs.ejs +81 -0
- package/dist/templates/boundaries/.eslintrc-frontend-spa.cjs.ejs +79 -0
- package/dist/templates/boundaries/.importlinter.ejs +48 -0
- package/dist/templates/boundaries/check-boundaries-go.mjs.ejs +94 -0
- package/dist/templates/boundaries/check-boundaries-python.mjs.ejs +118 -0
- package/dist/templates/boundaries/check-boundaries-rust.mjs.ejs +111 -0
- package/dist/templates/boundaries/check-boundaries.mjs.ejs +34 -0
- package/dist/templates/boundaries/clippy.toml.ejs +50 -0
- package/dist/templates/boundaries/deny.toml.ejs +56 -0
- package/dist/templates/boundaries/eslint.config.boundaries.mjs.ejs +112 -0
- package/dist/templates/boundaries/eslint.config.frontend-spa.mjs.ejs +93 -0
- package/dist/templates/boundaries/golangci-boundaries.yml.ejs +70 -0
- package/dist/templates/boundaries/ruff-boundaries.toml.ejs +32 -0
- package/dist/templates/claude/CLAUDE.md.ejs +78 -0
- package/dist/templates/claude/agents/agent-write-classes.json +30 -0
- package/dist/templates/claude/agents/bridge-reviewer.md.ejs +68 -0
- package/dist/templates/claude/agents/codebase-scanner.md.ejs +69 -0
- package/dist/templates/claude/agents/context-checker.md.ejs +73 -0
- package/dist/templates/claude/agents/red-team.md.ejs +134 -0
- package/dist/templates/claude/commands/close-gold-gap.md.ejs +43 -0
- package/dist/templates/claude/commands/drain.md.ejs +81 -0
- package/dist/templates/claude/commands/gold-audit.md.ejs +43 -0
- package/dist/templates/claude/commands/impact.md.ejs +39 -0
- package/dist/templates/claude/commands/levelup.md.ejs +61 -0
- package/dist/templates/claude/commands/ship.md.ejs +902 -0
- package/dist/templates/claude/commands/tabletop.md.ejs +46 -0
- package/dist/templates/claude/commands/task.md.ejs +87 -0
- package/dist/templates/claude/commands/wt-close.md.ejs +58 -0
- package/dist/templates/claude/commands/wt-list.md.ejs +48 -0
- package/dist/templates/claude/commands/wt-open.md.ejs +61 -0
- package/dist/templates/claude/commands/wt-prune.md.ejs +38 -0
- package/dist/templates/claude/hooks/check-circular-deps.mjs.ejs +56 -0
- package/dist/templates/claude/hooks/check-no-orphan-todo.mjs.ejs +45 -0
- package/dist/templates/claude/hooks/check-no-pii.mjs.ejs +121 -0
- package/dist/templates/claude/hooks/check-no-placeholders.mjs.ejs +71 -0
- package/dist/templates/claude/hooks/check-no-skipped-tests.mjs +80 -0
- package/dist/templates/claude/hooks/check-no-unused-exports.mjs +62 -0
- package/dist/templates/claude/hooks/closer-mode-guard.mjs.ejs +102 -0
- package/dist/templates/claude/hooks/debug-state-on-failure.mjs.ejs +98 -0
- package/dist/templates/claude/hooks/enforce-gate-before-pr.mjs +142 -0
- package/dist/templates/claude/hooks/enforce-read-only.mjs +28 -0
- package/dist/templates/claude/hooks/exitplanmode-banner.mjs.ejs +43 -0
- package/dist/templates/claude/hooks/guard-done-evidence.mjs.ejs +85 -0
- package/dist/templates/claude/hooks/guard-task-completion.mjs.ejs +56 -0
- package/dist/templates/claude/hooks/hooks.mjs.ejs +127 -0
- package/dist/templates/claude/hooks/lib.mjs.ejs +523 -0
- package/dist/templates/claude/hooks/post-brainstorm-stop.mjs +48 -0
- package/dist/templates/claude/hooks/post-commit-check.mjs.ejs +65 -0
- package/dist/templates/claude/hooks/post-commit-checklists/go/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/go/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/go/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/java/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/java/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/java/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/python/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/python/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/python/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/rust/backend.ejs +5 -0
- package/dist/templates/claude/hooks/post-commit-checklists/rust/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/rust/frontend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/ts/backend.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/ts/docs.ejs +4 -0
- package/dist/templates/claude/hooks/post-commit-checklists/ts/frontend.ejs +5 -0
- package/dist/templates/claude/hooks/post-edit-dispatch.mjs.ejs +73 -0
- package/dist/templates/claude/hooks/post-subagent-release.mjs +80 -0
- package/dist/templates/claude/hooks/pre-compact.mjs.ejs +79 -0
- package/dist/templates/claude/hooks/pre-edit-plan-anchor.mjs.ejs +225 -0
- package/dist/templates/claude/hooks/pre-edit-ssot-guard.mjs +204 -0
- package/dist/templates/claude/hooks/pre-spawn-worktree-guard.mjs +129 -0
- package/dist/templates/claude/hooks/skill-forced-eval.mjs.ejs +281 -0
- package/dist/templates/claude/hooks/stop-dangerous.mjs +86 -0
- package/dist/templates/claude/hooks/stop-evidence-guard.mjs.ejs +245 -0
- package/dist/templates/claude/hooks/stop-finding-loss.mjs.ejs +168 -0
- package/dist/templates/claude/hooks/wiki-on-commit.mjs.ejs +38 -0
- package/dist/templates/claude/knowledge-map.json.ejs +26 -0
- package/dist/templates/claude/rules/05-agent-lifecycle.md +7 -0
- package/dist/templates/claude/rules/25-todo-folder-policy.md +5 -0
- package/dist/templates/claude/rules/40-context-economy.md +32 -0
- package/dist/templates/claude/rules/45-mcp-fallback.md +25 -0
- package/dist/templates/claude/rules/50-batch-execution.md +75 -0
- package/dist/templates/claude/rules/55-brainstorm-terminal-state.md +44 -0
- package/dist/templates/claude/rules/60-incidental-capture.md +43 -0
- package/dist/templates/claude/rules/75-impact-vault-reading.md +43 -0
- package/dist/templates/claude/rules/90-exec-protocol.md.ejs +40 -0
- package/dist/templates/claude/rules/95-closer-mode.md +45 -0
- package/dist/templates/claude/settings.json.ejs +165 -0
- package/dist/templates/claude/skills/architect-review/SKILL.md.ejs +82 -0
- package/dist/templates/claude/skills/brainstorming/SKILL.md.ejs +86 -0
- package/dist/templates/claude/skills/clean-code/SKILL.md.ejs +39 -0
- package/dist/templates/claude/skills/close-gold-gap/SKILL.md.ejs +58 -0
- package/dist/templates/claude/skills/codebase-audit/SKILL.md.ejs +88 -0
- package/dist/templates/claude/skills/configure/SKILL.md.ejs +72 -0
- package/dist/templates/claude/skills/epic-decompose/SKILL.md.ejs +86 -0
- package/dist/templates/claude/skills/gold-audit/SKILL.md.ejs +91 -0
- package/dist/templates/claude/skills/impact/SKILL.md.ejs +104 -0
- package/dist/templates/claude/skills/levelup/SKILL.md.ejs +146 -0
- package/dist/templates/claude/skills/tabletop/SKILL.md.ejs +118 -0
- package/dist/templates/claude/skills/tdd/SKILL.md.ejs +157 -0
- package/dist/templates/claude/skills/understand-code/SKILL.md.ejs +93 -0
- package/dist/templates/claude/skills/verification/SKILL.md.ejs +68 -0
- package/dist/templates/claude/skills/wave-drain/SKILL.md.ejs +491 -0
- package/dist/templates/codex/CODEX.md.ejs +140 -0
- package/dist/templates/codex/codex-adapter.mjs +132 -0
- package/dist/templates/codex/config.toml.ejs +189 -0
- package/dist/templates/compliance/compliance-mapping.md.ejs +124 -0
- package/dist/templates/compliance/compliance-menu.md.ejs +89 -0
- package/dist/templates/contract-testing/CONTRACTS_POLICY.md.ejs +30 -0
- package/dist/templates/contract-testing/api-snapshots/config-keys-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/config-response-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/enum-values-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/error-shape-baseline.json.ejs +6 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-baseline.json.ejs +12 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-content-types-baseline.json.ejs +3 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-paths-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-required-fields-baseline.json.ejs +1 -0
- package/dist/templates/contract-testing/api-snapshots/openapi-response-status-baseline.json.ejs +3 -0
- package/dist/templates/contract-testing/api-snapshots/test-snapshot.json.ejs +4 -0
- package/dist/templates/contract-testing/env/.env.pact.ejs +4 -0
- package/dist/templates/contract-testing/graphql/GraphqlSchemaTest.java.ejs +29 -0
- package/dist/templates/contract-testing/graphql/graphql-inspector.test.ts.ejs +20 -0
- package/dist/templates/contract-testing/graphql/graphql_schema_test.go.ejs +27 -0
- package/dist/templates/contract-testing/graphql/graphql_schema_test.rs.ejs +27 -0
- package/dist/templates/contract-testing/graphql/test_graphql_schema.py.ejs +21 -0
- package/dist/templates/contract-testing/grpc/GrpcContractTest.java.ejs +23 -0
- package/dist/templates/contract-testing/grpc/buf-breaking.yml.ejs +6 -0
- package/dist/templates/contract-testing/grpc/buf.yaml.ejs +11 -0
- package/dist/templates/contract-testing/grpc/grpc-contract.test.ts.ejs +16 -0
- package/dist/templates/contract-testing/grpc/grpc_contract_test.go.ejs +22 -0
- package/dist/templates/contract-testing/grpc/grpc_contract_test.rs.ejs +22 -0
- package/dist/templates/contract-testing/grpc/test_grpc_contract.py.ejs +18 -0
- package/dist/templates/contract-testing/message-queue/SchemaRegistryCheckIT.java.ejs +44 -0
- package/dist/templates/contract-testing/message-queue/schema-registry-check.ts.ejs +42 -0
- package/dist/templates/contract-testing/message-queue/schema_registry_test.go.ejs +71 -0
- package/dist/templates/contract-testing/message-queue/schema_registry_test.rs.ejs +61 -0
- package/dist/templates/contract-testing/message-queue/test_schema_registry.py.ejs +31 -0
- package/dist/templates/contract-testing/pact-samples/assignment-response.json.ejs +7 -0
- package/dist/templates/contract-testing/pact-samples/availability-response.json.ejs +13 -0
- package/dist/templates/contract-testing/pact-samples/availability-rule-response.json.ejs +8 -0
- package/dist/templates/contract-testing/pact-samples/capacity-response.json.ejs +11 -0
- package/dist/templates/contract-testing/pact-samples/fully-booked-response.json.ejs +6 -0
- package/dist/templates/contract-testing/pact-samples/schedule-override-response.json.ejs +7 -0
- package/dist/templates/contract-testing/rest-owned/PactVerificationIT.java.ejs +31 -0
- package/dist/templates/contract-testing/rest-owned/pact-consumer.test.ts.ejs +36 -0
- package/dist/templates/contract-testing/rest-owned/pact-deps.gradle.ejs +15 -0
- package/dist/templates/contract-testing/rest-owned/pact_consumer_test.go.ejs +48 -0
- package/dist/templates/contract-testing/rest-owned/pact_consumer_test.rs.ejs +23 -0
- package/dist/templates/contract-testing/rest-owned/test_pact_consumer.py.ejs +35 -0
- package/dist/templates/contract-testing/rest-public/OpenApiDiffIT.java.ejs +44 -0
- package/dist/templates/contract-testing/rest-public/export-openapi-java.gradle.ejs +13 -0
- package/dist/templates/contract-testing/rest-public/export-openapi.mjs.ejs +21 -0
- package/dist/templates/contract-testing/rest-public/export_openapi.go.ejs +30 -0
- package/dist/templates/contract-testing/rest-public/export_openapi.py.ejs +27 -0
- package/dist/templates/contract-testing/rest-public/export_openapi.rs.ejs +32 -0
- package/dist/templates/contract-testing/rest-public/openapi-diff.ts.ejs +42 -0
- package/dist/templates/contract-testing/rest-public/openapi_diff_test.go.ejs +35 -0
- package/dist/templates/contract-testing/rest-public/openapi_diff_test.rs.ejs +36 -0
- package/dist/templates/contract-testing/rest-public/test_openapi_diff.py.ejs +32 -0
- package/dist/templates/coverage/.coveragerc.ejs +26 -0
- package/dist/templates/coverage/.tarpaulin.toml.ejs +30 -0
- package/dist/templates/coverage/Cargo.toml.profile.release.ejs +24 -0
- package/dist/templates/coverage/jacoco-maven-setup.md.ejs +72 -0
- package/dist/templates/coverage/jacoco.gradle.ejs +49 -0
- package/dist/templates/coverage/kover.gradle.ejs +35 -0
- package/dist/templates/coverage/vitest.browser.config.ts.ejs +47 -0
- package/dist/templates/coverage/vitest.config.ts.ejs +47 -0
- package/dist/templates/docs/CODING_STANDARDS.md.ejs +81 -0
- package/dist/templates/docs/FEATURE_MATRIX.md.ejs +84 -0
- package/dist/templates/docs/GAP.md.ejs +58 -0
- package/dist/templates/docs/ISO27001_ANNEX_A.md.ejs +144 -0
- package/dist/templates/docs/MASTER_TEST_PLAN.md.ejs +52 -0
- package/dist/templates/docs/POST_MERGE_REVIEW_TEMPLATE.md.ejs +63 -0
- package/dist/templates/docs/SECURE_CODING_CHECKLIST.md.ejs +61 -0
- package/dist/templates/docs/adr/ADR-000_template.md.ejs +28 -0
- package/dist/templates/docs/bugs/bug-analysis.md.ejs +47 -0
- package/dist/templates/docs/bugs/bug-report.md.ejs +51 -0
- package/dist/templates/docs/bugs/bug-verification.md.ejs +42 -0
- package/dist/templates/docs/runbooks/deployment.md.ejs +119 -0
- package/dist/templates/docs/runbooks/prod-checklist.md.ejs +90 -0
- package/dist/templates/docs/runbooks/rollback.md.ejs +124 -0
- package/dist/templates/docs/runbooks/troubleshooting.md.ejs +130 -0
- package/dist/templates/docs/skeletons/arc42-canvas.md.ejs +46 -0
- package/dist/templates/docs/skeletons/arc42-full.md.ejs +59 -0
- package/dist/templates/docs/skeletons/decision-registry.md.ejs +38 -0
- package/dist/templates/docs/skeletons/er-model.md.ejs +19 -0
- package/dist/templates/docs/skeletons/glossary.md.ejs +8 -0
- package/dist/templates/docs/skeletons/governance.md.ejs +13 -0
- package/dist/templates/docs/skeletons/slo.md.ejs +21 -0
- package/dist/templates/docs/skeletons/technical-debt.md.ejs +8 -0
- package/dist/templates/docs/skeletons/test-strategy.md.ejs +14 -0
- package/dist/templates/docs/skeletons/threat-model-4q.md.ejs +20 -0
- package/dist/templates/docs/skeletons/threat-model-stride.md.ejs +22 -0
- package/dist/templates/docs/specs/atomic-task-list.md.ejs +57 -0
- package/dist/templates/docs/steering/product.md.ejs +44 -0
- package/dist/templates/docs/steering/structure.md.ejs +49 -0
- package/dist/templates/docs/steering/tech.md.ejs +53 -0
- package/dist/templates/documentation/cli-catalog.md.ejs +21 -0
- package/dist/templates/e2e/E2E_CONSTITUTION.md.ejs +77 -0
- package/dist/templates/e2e/playwright-python/conftest.py.ejs +22 -0
- package/dist/templates/e2e/playwright-python/run_axe.py.ejs +99 -0
- package/dist/templates/e2e/playwright-python/test_a11y.py.ejs +38 -0
- package/dist/templates/e2e/playwright-python/test_smoke.py.ejs +28 -0
- package/dist/templates/e2e/playwright-ts/a11y.spec.ts.ejs +20 -0
- package/dist/templates/e2e/playwright-ts/render-smoke.spec.ts.ejs +90 -0
- package/dist/templates/e2e/playwright-ts/run-axe.ts.ejs +74 -0
- package/dist/templates/e2e/smoke-journeys/journeys.spec.ts.ejs +53 -0
- package/dist/templates/evidence-files.json.ejs +13 -0
- package/dist/templates/frontend/FE_DESIGN_PRINCIPLES.md.ejs +115 -0
- package/dist/templates/frontend/FRONTEND_CONSTITUTION.md.ejs +170 -0
- package/dist/templates/frontend/design-tokens.json.ejs +107 -0
- package/dist/templates/frontend/vrt-setup.md.ejs +83 -0
- package/dist/templates/githooks/commit-msg.ejs +60 -0
- package/dist/templates/githooks/pre-commit.ejs +95 -0
- package/dist/templates/githooks/pre-push.ejs +251 -0
- package/dist/templates/githooks/setup-hooks.sh.ejs +8 -0
- package/dist/templates/github/PULL_REQUEST_TEMPLATE.md +37 -0
- package/dist/templates/github/PULL_REQUEST_TEMPLATE.md.ejs +52 -0
- package/dist/templates/github/actions/build-cache/action.yml.ejs +84 -0
- package/dist/templates/github/actions/nas-ssh/action.yml.ejs +53 -0
- package/dist/templates/github/actions/setup-java-maven/action.yml.ejs +53 -0
- package/dist/templates/github/actions/setup-node-pnpm/action.yml.ejs +39 -0
- package/dist/templates/github/actions/sign-and-attest/action.yml.ejs +53 -0
- package/dist/templates/github/dependabot.yml.ejs +66 -0
- package/dist/templates/github/extended-ci-paths.txt.ejs +38 -0
- package/dist/templates/github/issue-templates/bug-report.yml +72 -0
- package/dist/templates/github/issue-templates/compliance-item.yml.ejs +75 -0
- package/dist/templates/github/issue-templates/config.yml +2 -0
- package/dist/templates/github/issue-templates/epic.yml +66 -0
- package/dist/templates/github/issue-templates/feature-request.yml +24 -0
- package/dist/templates/github/issue-templates/task-brief.yml.ejs +125 -0
- package/dist/templates/github/labels.yml.ejs +161 -0
- package/dist/templates/github/scripts/sticky-failure-issue.sh.ejs +61 -0
- package/dist/templates/github/workflows/01-pr-fast.yml.ejs +1089 -0
- package/dist/templates/github/workflows/02-pr-extended.yml.ejs +512 -0
- package/dist/templates/github/workflows/03-human-approval.yml.ejs +76 -0
- package/dist/templates/github/workflows/04-deploy-test.yml.ejs +425 -0
- package/dist/templates/github/workflows/05-release.yml.ejs +813 -0
- package/dist/templates/github/workflows/06-nightly-lite.yml.ejs +125 -0
- package/dist/templates/github/workflows/06-nightly.yml.ejs +25 -0
- package/dist/templates/github/workflows/07-weekly-lite.yml.ejs +221 -0
- package/dist/templates/github/workflows/07-weekly.yml.ejs +23 -0
- package/dist/templates/github/workflows/08-monthly.yml.ejs +23 -0
- package/dist/templates/github/workflows/09-heartbeat.yml.ejs +268 -0
- package/dist/templates/github/workflows/10-deploy-prod.yml.ejs +144 -0
- package/dist/templates/github/workflows/11-k6-on-demand.yml.ejs +43 -0
- package/dist/templates/github/workflows/12-mutation-scheduled.yml.ejs +100 -0
- package/dist/templates/github/workflows/13-archunit-extended.yml.ejs +33 -0
- package/dist/templates/github/workflows/14-license-scan.yml.ejs +45 -0
- package/dist/templates/github/workflows/15-codeql.yml.ejs +117 -0
- package/dist/templates/github/workflows/16-frontend-quality.yml.ejs +154 -0
- package/dist/templates/github/workflows/17-ossf-scorecard.yml.ejs +46 -0
- package/dist/templates/github/workflows/18-frontend-lane.yml.ejs +50 -0
- package/dist/templates/github/workflows/_ai-draft-check.yml.ejs +44 -0
- package/dist/templates/github/workflows/_contract-postman.yml.ejs +93 -0
- package/dist/templates/github/workflows/_cosign-copy/aws-ecs.ejs +90 -0
- package/dist/templates/github/workflows/_cosign-copy/azure-container-app.ejs +97 -0
- package/dist/templates/github/workflows/_cosign-copy/gcp-cloud-run.ejs +102 -0
- package/dist/templates/github/workflows/_cosign-copy/ghcr.ejs +76 -0
- package/dist/templates/github/workflows/_cosign-copy/nas-compose.ejs +69 -0
- package/dist/templates/github/workflows/_deploy/aws-ecs.ejs +78 -0
- package/dist/templates/github/workflows/_deploy/azure-container-app.ejs +59 -0
- package/dist/templates/github/workflows/_deploy/gcp-cloud-run.ejs +71 -0
- package/dist/templates/github/workflows/_deploy/ghcr.ejs +19 -0
- package/dist/templates/github/workflows/_deploy/nas-compose.ejs +45 -0
- package/dist/templates/github/workflows/_k6-runner.yml.ejs +107 -0
- package/dist/templates/github/workflows/_label-on-approve.yml.ejs +44 -0
- package/dist/templates/github/workflows/_label-sync.yml.ejs +58 -0
- package/dist/templates/github/workflows/_monthly.yml.ejs +343 -0
- package/dist/templates/github/workflows/_nightly.yml.ejs +826 -0
- package/dist/templates/github/workflows/_notify.yml.ejs +96 -0
- package/dist/templates/github/workflows/_partials/macos-keychain-bypass.ejs +6 -0
- package/dist/templates/github/workflows/_partials/macos-keychain-restore.ejs +6 -0
- package/dist/templates/github/workflows/_partials/scheduled-heavy-jobs.ejs +117 -0
- package/dist/templates/github/workflows/_partials/sigstore-preflight.ejs +8 -0
- package/dist/templates/github/workflows/_partials/trivy-fs-java.ejs +27 -0
- package/dist/templates/github/workflows/_post-merge-notify.yml.ejs +228 -0
- package/dist/templates/github/workflows/_pr-staleness.yml.ejs +85 -0
- package/dist/templates/github/workflows/_shared-security.yml.ejs +132 -0
- package/dist/templates/github/workflows/_sigstore-retry-sign.yml.ejs +102 -0
- package/dist/templates/github/workflows/_weekly.yml.ejs +313 -0
- package/dist/templates/github/workflows/drift-shadow.yml.ejs +51 -0
- package/dist/templates/github/workflows/five-lane/ci.yml.ejs +54 -0
- package/dist/templates/github/workflows/five-lane/nightly.yml.ejs +70 -0
- package/dist/templates/github/workflows/five-lane/release.yml.ejs +53 -0
- package/dist/templates/github/workflows/five-lane/weekly.yml.ejs +72 -0
- package/dist/templates/github/workflows/issue-state.yml.ejs +93 -0
- package/dist/templates/global-invariants/GLOBAL_INVARIANTS.md.ejs +34 -0
- package/dist/templates/governance/RACI.md.ejs +21 -0
- package/dist/templates/governance/ci-mental-model.md.ejs +268 -0
- package/dist/templates/governance/contract-integrity-policy.md.ejs +116 -0
- package/dist/templates/governance/enterprise-compliance-baseline.md.ejs +111 -0
- package/dist/templates/governance/evidence-retention.md.ejs +43 -0
- package/dist/templates/governance/gdpr-erasure-runbook.md.ejs +165 -0
- package/dist/templates/governance/qa-audit-phases.md.ejs +129 -0
- package/dist/templates/governance/risk-assessment-template.md.ejs +105 -0
- package/dist/templates/governance/risk-register.md.ejs +76 -0
- package/dist/templates/governance/solo-dev-exception.md.ejs +96 -0
- package/dist/templates/governance/validation-evidence.md.ejs +131 -0
- package/dist/templates/infra/azure/containerapp.tpl.yaml.ejs +74 -0
- package/dist/templates/infra/nas-compose/deploy.sh.ejs +114 -0
- package/dist/templates/integration-testing/AbstractIntegrationTest.java.ejs +36 -0
- package/dist/templates/integration-testing/NoH2ArchTest.java.ejs +24 -0
- package/dist/templates/integration-testing/conftest.py.ejs +20 -0
- package/dist/templates/integration-testing/db_fixture.rs.ejs +48 -0
- package/dist/templates/integration-testing/eslint-no-fake-db.json.ejs +19 -0
- package/dist/templates/integration-testing/eslint.config.no-fake-db.mjs.ejs +46 -0
- package/dist/templates/integration-testing/main_test.go.ejs +53 -0
- package/dist/templates/integration-testing/main_test_sqlite.go.ejs +37 -0
- package/dist/templates/integration-testing/test-setup.ts.ejs +26 -0
- package/dist/templates/integration-testing/testcontainers-deps.gradle.ejs +9 -0
- package/dist/templates/java/DeprecationInterceptor.java.ejs +50 -0
- package/dist/templates/java/archunit-hexagonal.ejs +69 -0
- package/dist/templates/java/modulith/ApplicationModulesTest.java.ejs +21 -0
- package/dist/templates/java/modulith/modulith-deps.gradle.ejs +13 -0
- package/dist/templates/java/modulith/modulith-maven-setup.md.ejs +21 -0
- package/dist/templates/java/pharma/AuditEvent.java.ejs +138 -0
- package/dist/templates/java/pharma/AuditMapper.java.ejs +60 -0
- package/dist/templates/java/pharma/archunit-pharma.ejs +128 -0
- package/dist/templates/kit/GLOBAL_KIT.md.ejs +11 -0
- package/dist/templates/kit/dim.md.ejs +33 -0
- package/dist/templates/llms-txt.config.json.ejs +22 -0
- package/dist/templates/local-wrapper/.env.example.ejs +15 -0
- package/dist/templates/local-wrapper/Makefile.ejs +44 -0
- package/dist/templates/local-wrapper/run.sh.ejs +5 -0
- package/dist/templates/middleware/410-gone-handler.ts.ejs +41 -0
- package/dist/templates/middleware/correlation-id.ts.ejs +32 -0
- package/dist/templates/middleware/deprecation.ts.ejs +45 -0
- package/dist/templates/middleware/error-handler.ts.ejs +73 -0
- package/dist/templates/middleware/payload-size-limit.ts.ejs +51 -0
- package/dist/templates/mutation/README.md.ejs +67 -0
- package/dist/templates/mutation/cargo-mutants.toml.ejs +9 -0
- package/dist/templates/mutation/mutmut-config.toml.ejs +7 -0
- package/dist/templates/mutation/parse-mutants.mjs.ejs +44 -0
- package/dist/templates/mutation/parse-mutmut.py.ejs +44 -0
- package/dist/templates/mutation/pitest-l2-setup.md.ejs +66 -0
- package/dist/templates/mutation/pitest-maven-setup.md.ejs +64 -0
- package/dist/templates/mutation/pitest.gradle.ejs +23 -0
- package/dist/templates/mutation/stryker.conf.json.ejs +17 -0
- package/dist/templates/observability/setup.md.ejs +495 -0
- package/dist/templates/operations/handbook.md.ejs +217 -0
- package/dist/templates/perf/bundle-budget.json.ejs +29 -0
- package/dist/templates/perf/k6/reports/csv-report.py.ejs +91 -0
- package/dist/templates/perf/k6/reports/html-report.py.ejs +114 -0
- package/dist/templates/perf/k6/reports/json-report.py.ejs +74 -0
- package/dist/templates/perf/k6/scenarios/breakpoint.js.ejs +26 -0
- package/dist/templates/perf/k6/scenarios/burst.js.ejs +43 -0
- package/dist/templates/perf/k6/scenarios/endurance.js.ejs +36 -0
- package/dist/templates/perf/k6/scenarios/load.js.ejs +26 -0
- package/dist/templates/perf/k6/scenarios/ramp-down.js.ejs +29 -0
- package/dist/templates/perf/k6/scenarios/ramp-up.js.ejs +32 -0
- package/dist/templates/perf/k6/scenarios/smoke.js.ejs +23 -0
- package/dist/templates/perf/k6/scenarios/soak.js.ejs +26 -0
- package/dist/templates/perf/k6/scenarios/spike.js.ejs +29 -0
- package/dist/templates/perf/k6/scenarios/steady-state.js.ejs +31 -0
- package/dist/templates/perf/k6/scenarios/stress.js.ejs +29 -0
- package/dist/templates/perf/k6/scenarios/volume.js.ejs +36 -0
- package/dist/templates/perf/k6/seed/test-data.sql.ejs +59 -0
- package/dist/templates/perf/lighthouserc.json.ejs +41 -0
- package/dist/templates/quality/iso9001/CAPA_LOG.md.ejs +36 -0
- package/dist/templates/quality/iso9001/DOCUMENT_CONTROL.md.ejs +36 -0
- package/dist/templates/quality/iso9001/REQUIREMENTS_TRACEABILITY.md.ejs +61 -0
- package/dist/templates/regulated/overlay.json.ejs +26 -0
- package/dist/templates/regulated/regulated-overlay.md.ejs +40 -0
- package/dist/templates/resilience/RESILIENCE.md.ejs +310 -0
- package/dist/templates/root/.gitignore.ejs +85 -0
- package/dist/templates/root/CODEOWNERS.ejs +18 -0
- package/dist/templates/root/CONTRIBUTING.md.ejs +62 -0
- package/dist/templates/root/SECURITY.md.ejs +19 -0
- package/dist/templates/root/TEST_TAXONOMY.md.ejs +100 -0
- package/dist/templates/root/commitlint.config.js.ejs +4 -0
- package/dist/templates/root/docs/METHOD/CANONICAL_PATHS.md.ejs +24 -0
- package/dist/templates/root/docs/METHOD/ENGINEERING_DEFAULTS.md.ejs +136 -0
- package/dist/templates/root/docs/METHOD/KNOWLEDGE_MAP.md.ejs +90 -0
- package/dist/templates/root/docs/METHOD/PATTERNS_CATALOG.md.ejs +51 -0
- package/dist/templates/root/docs/METHOD/REUSE_REGISTRY.md.ejs +26 -0
- package/dist/templates/root/docs/METHOD/REUSE_REGISTRY_SPEC.md.ejs +80 -0
- package/dist/templates/root/docs/METHOD/SSOT_CORE_SET.md.ejs +62 -0
- package/dist/templates/root/docs/METHOD/TRACK_ROUTER.md.ejs +39 -0
- package/dist/templates/root/docs/SYSTEM/CANON.md.ejs +359 -0
- package/dist/templates/root/docs/SYSTEM/FAIL_CLOSED.md.ejs +171 -0
- package/dist/templates/root/editorconfig.ejs +39 -0
- package/dist/templates/root/tsconfig.json.ejs +21 -0
- package/dist/templates/rust/clippy.toml.ejs +14 -0
- package/dist/templates/schemas/milestone.schema.json.ejs +164 -0
- package/dist/templates/schemas/rtm-verdict.schema.json.ejs +52 -0
- package/dist/templates/schemas/source-record.schema.json.ejs +91 -0
- package/dist/templates/schemas/tabletop-evidence.schema.json.ejs +53 -0
- package/dist/templates/schemas/use-case.schema.json.ejs +59 -0
- package/dist/templates/scripts/apply-branch-protection.mjs.ejs +70 -0
- package/dist/templates/scripts/audit-toolchain.mjs.ejs +112 -0
- package/dist/templates/scripts/bg-run.sh.ejs +59 -0
- package/dist/templates/scripts/capacity-probe.mjs.ejs +147 -0
- package/dist/templates/scripts/capture-debt-baseline.mjs.ejs +99 -0
- package/dist/templates/scripts/check-acceptance.mjs.ejs +298 -0
- package/dist/templates/scripts/check-action-pins.mjs.ejs +215 -0
- package/dist/templates/scripts/check-agent-return.mjs.ejs +177 -0
- package/dist/templates/scripts/check-all.mjs.ejs +1746 -0
- package/dist/templates/scripts/check-anti-fake-green.mjs.ejs +80 -0
- package/dist/templates/scripts/check-anti-proforma.mjs.ejs +547 -0
- package/dist/templates/scripts/check-api-e2e.mjs.ejs +106 -0
- package/dist/templates/scripts/check-arc42-slots.mjs.ejs +25 -0
- package/dist/templates/scripts/check-assertion-delta.mjs.ejs +287 -0
- package/dist/templates/scripts/check-audit-dry-pass.mjs.ejs +176 -0
- package/dist/templates/scripts/check-bundle-size.mjs.ejs +151 -0
- package/dist/templates/scripts/check-canonical-paths.mjs.ejs +39 -0
- package/dist/templates/scripts/check-ci-tiers.mjs.ejs +111 -0
- package/dist/templates/scripts/check-claude-md-lint.mjs.ejs +142 -0
- package/dist/templates/scripts/check-collab-mode-wired.mjs.ejs +60 -0
- package/dist/templates/scripts/check-commit-footer-rationale.mjs.ejs +170 -0
- package/dist/templates/scripts/check-constraint-scan.mjs.ejs +681 -0
- package/dist/templates/scripts/check-consumer-audit.mjs.ejs +414 -0
- package/dist/templates/scripts/check-continue-on-error.mjs.ejs +227 -0
- package/dist/templates/scripts/check-cross-model-review.mjs.ejs +458 -0
- package/dist/templates/scripts/check-decision-registry.mjs.ejs +101 -0
- package/dist/templates/scripts/check-doc-freshness.mjs.ejs +17 -0
- package/dist/templates/scripts/check-doc-links.mjs.ejs +98 -0
- package/dist/templates/scripts/check-doc-set.mjs.ejs +18 -0
- package/dist/templates/scripts/check-docs.mjs.ejs +71 -0
- package/dist/templates/scripts/check-domain-api-surface.mjs.ejs +80 -0
- package/dist/templates/scripts/check-drift.mjs.ejs +167 -0
- package/dist/templates/scripts/check-duplication.mjs.ejs +45 -0
- package/dist/templates/scripts/check-e2e-escalation.mjs.ejs +199 -0
- package/dist/templates/scripts/check-e2e-quarantine.mjs.ejs +68 -0
- package/dist/templates/scripts/check-emission-parity.mjs.ejs +91 -0
- package/dist/templates/scripts/check-exit-code-contract.mjs.ejs +138 -0
- package/dist/templates/scripts/check-fail-closed-audit.mjs.ejs +389 -0
- package/dist/templates/scripts/check-fe-boundaries.mjs.ejs +184 -0
- package/dist/templates/scripts/check-feature-matrix.mjs.ejs +835 -0
- package/dist/templates/scripts/check-frontend-lane.mjs.ejs +73 -0
- package/dist/templates/scripts/check-grace-window.mjs.ejs +102 -0
- package/dist/templates/scripts/check-handoff-doc.mjs.ejs +192 -0
- package/dist/templates/scripts/check-hook-routing.mjs.ejs +224 -0
- package/dist/templates/scripts/check-image-pins.mjs.ejs +90 -0
- package/dist/templates/scripts/check-inline-suppressions.mjs.ejs +153 -0
- package/dist/templates/scripts/check-iso9001.mjs.ejs +146 -0
- package/dist/templates/scripts/check-knowledge-map.mjs.ejs +49 -0
- package/dist/templates/scripts/check-local-ci-parity.mjs.ejs +382 -0
- package/dist/templates/scripts/check-m16-handoff.mjs.ejs +112 -0
- package/dist/templates/scripts/check-merge-method.mjs.ejs +92 -0
- package/dist/templates/scripts/check-milestones.mjs.ejs +678 -0
- package/dist/templates/scripts/check-min-test-execution.mjs.ejs +225 -0
- package/dist/templates/scripts/check-muted-test.mjs.ejs +270 -0
- package/dist/templates/scripts/check-no-stub-redirects.mjs.ejs +154 -0
- package/dist/templates/scripts/check-no-tracked-artifacts.mjs.ejs +134 -0
- package/dist/templates/scripts/check-oracle-discrimination.mjs.ejs +244 -0
- package/dist/templates/scripts/check-pii-scan.mjs.ejs +67 -0
- package/dist/templates/scripts/check-pipe-tee-hazard.mjs.ejs +85 -0
- package/dist/templates/scripts/check-pr-size-gate.mjs.ejs +85 -0
- package/dist/templates/scripts/check-refutation-verdicts.mjs.ejs +256 -0
- package/dist/templates/scripts/check-regulated-overlay.mjs.ejs +158 -0
- package/dist/templates/scripts/check-render-smoke.mjs.ejs +100 -0
- package/dist/templates/scripts/check-reuse-registry.mjs.ejs +44 -0
- package/dist/templates/scripts/check-review-completion.mjs.ejs +708 -0
- package/dist/templates/scripts/check-safety-adopt-ratchet.mjs.ejs +142 -0
- package/dist/templates/scripts/check-secret-presence.mjs.ejs +191 -0
- package/dist/templates/scripts/check-secret-scan.mjs.ejs +82 -0
- package/dist/templates/scripts/check-skip-critical-e2e.mjs.ejs +142 -0
- package/dist/templates/scripts/check-smoke-journeys.mjs.ejs +272 -0
- package/dist/templates/scripts/check-solo-reactivation.mjs.ejs +102 -0
- package/dist/templates/scripts/check-sources.mjs.ejs +245 -0
- package/dist/templates/scripts/check-ssot-core.mjs.ejs +45 -0
- package/dist/templates/scripts/check-stack-conformity.mjs.ejs +160 -0
- package/dist/templates/scripts/check-stride-traceability.mjs.ejs +101 -0
- package/dist/templates/scripts/check-suppression-expiry.mjs.ejs +79 -0
- package/dist/templates/scripts/check-suppression-rationale.mjs.ejs +76 -0
- package/dist/templates/scripts/check-suppressions.mjs.ejs +150 -0
- package/dist/templates/scripts/check-tabletop-evidence.mjs.ejs +499 -0
- package/dist/templates/scripts/check-tdd-evidence.mjs.ejs +381 -0
- package/dist/templates/scripts/check-test-naming.mjs.ejs +151 -0
- package/dist/templates/scripts/check-test-pyramid.mjs.ejs +170 -0
- package/dist/templates/scripts/check-test-scope-tier.mjs.ejs +208 -0
- package/dist/templates/scripts/check-tier-coverage.mjs.ejs +71 -0
- package/dist/templates/scripts/check-todo-max-age.mjs.ejs +205 -0
- package/dist/templates/scripts/check-touched-vs-manifest.mjs.ejs +179 -0
- package/dist/templates/scripts/check-unwired-guards.mjs.ejs +367 -0
- package/dist/templates/scripts/check-use-cases.mjs.ejs +345 -0
- package/dist/templates/scripts/check-validator-helptext.mjs.ejs +80 -0
- package/dist/templates/scripts/check-wiki-lint.mjs.ejs +162 -0
- package/dist/templates/scripts/check-workflow-docs-sync.mjs.ejs +91 -0
- package/dist/templates/scripts/check-workflow-job-naming.mjs.ejs +102 -0
- package/dist/templates/scripts/check-workflow-perms.mjs.ejs +54 -0
- package/dist/templates/scripts/check-workflow-runners.mjs.ejs +87 -0
- package/dist/templates/scripts/check-workflow-sha-pinning.mjs.ejs +89 -0
- package/dist/templates/scripts/check-workflow-test-integrity.mjs.ejs +235 -0
- package/dist/templates/scripts/checks/check-rust-no-unsafe.mjs.ejs +58 -0
- package/dist/templates/scripts/checks/check-rust-no-unwrap.mjs.ejs +61 -0
- package/dist/templates/scripts/ci-classify-changes.mjs.ejs +119 -0
- package/dist/templates/scripts/conformance.mjs.ejs +37 -0
- package/dist/templates/scripts/constraint-map.json.ejs +25 -0
- package/dist/templates/scripts/debt-lib.mjs.ejs +1015 -0
- package/dist/templates/scripts/debt-report.mjs.ejs +199 -0
- package/dist/templates/scripts/domain-api-surface.json.ejs +23 -0
- package/dist/templates/scripts/done-evidence.mjs.ejs +359 -0
- package/dist/templates/scripts/evidence-collect.mjs.ejs +264 -0
- package/dist/templates/scripts/evidence-prune.mjs.ejs +96 -0
- package/dist/templates/scripts/evidence-rotate.mjs.ejs +53 -0
- package/dist/templates/scripts/evidence-writer.sh.ejs +220 -0
- package/dist/templates/scripts/gate-registry.yml.ejs +286 -0
- package/dist/templates/scripts/gen-doc-index.mjs.ejs +19 -0
- package/dist/templates/scripts/gen-gap.mjs.ejs +153 -0
- package/dist/templates/scripts/gen-llms-txt.mjs.ejs +18 -0
- package/dist/templates/scripts/gen-wiki.mjs.ejs +196 -0
- package/dist/templates/scripts/gh-cleanup-expired-artifacts.mjs.ejs +134 -0
- package/dist/templates/scripts/gold-audit.mjs.ejs +19 -0
- package/dist/templates/scripts/i18n-literal-scanner.mjs.ejs +175 -0
- package/dist/templates/scripts/ingest-zap-report.mjs.ejs +107 -0
- package/dist/templates/scripts/inject-pact-samples.sh.ejs +96 -0
- package/dist/templates/scripts/issue-readiness.mjs.ejs +84 -0
- package/dist/templates/scripts/knowledge-map-update.mjs.ejs +32 -0
- package/dist/templates/scripts/lib/acceptance-criteria.mjs.ejs +213 -0
- package/dist/templates/scripts/lib/agent-return-validate.mjs.ejs +481 -0
- package/dist/templates/scripts/lib/coverage-gate.mjs.ejs +54 -0
- package/dist/templates/scripts/lib/e2e-reliability.mjs.ejs +207 -0
- package/dist/templates/scripts/lib/ephemeral-server.mjs.ejs +148 -0
- package/dist/templates/scripts/lib/evidence-binding.mjs.ejs +140 -0
- package/dist/templates/scripts/lib/exact-sha-policy.mjs.ejs +233 -0
- package/dist/templates/scripts/lib/gate-args.mjs.ejs +20 -0
- package/dist/templates/scripts/lib/gate-evidence.mjs.ejs +622 -0
- package/dist/templates/scripts/lib/gate-mutex.mjs.ejs +340 -0
- package/dist/templates/scripts/lib/glob-walk.mjs.ejs +148 -0
- package/dist/templates/scripts/lib/run-helpers.mjs.ejs +416 -0
- package/dist/templates/scripts/lib/seed-common.sh.ejs +105 -0
- package/dist/templates/scripts/lib/waiter-count.mjs.ejs +62 -0
- package/dist/templates/scripts/muted-tests-baseline.json.ejs +4 -0
- package/dist/templates/scripts/optional-emissions.json.ejs +117 -0
- package/dist/templates/scripts/oracle-discrimination-baseline.json.ejs +6 -0
- package/dist/templates/scripts/pid-watch.sh.ejs +78 -0
- package/dist/templates/scripts/pii-scan.mjs.ejs +125 -0
- package/dist/templates/scripts/pr-merge-watch.mjs.ejs +445 -0
- package/dist/templates/scripts/record-agent-return.mjs.ejs +279 -0
- package/dist/templates/scripts/record-journey-evidence.mjs.ejs +69 -0
- package/dist/templates/scripts/rework-log.mjs.ejs +172 -0
- package/dist/templates/scripts/run-postman-tests.sh.ejs +112 -0
- package/dist/templates/scripts/schemas/agent-return-external.schema.json +74 -0
- package/dist/templates/scripts/schemas/agent-return.schema.json.ejs +181 -0
- package/dist/templates/scripts/schemas/cross-model-dispatch.schema.json +95 -0
- package/dist/templates/scripts/seed-test-data.sh.ejs +99 -0
- package/dist/templates/scripts/self-validation.mjs.ejs +126 -0
- package/dist/templates/scripts/setup-repo.sh.ejs +78 -0
- package/dist/templates/scripts/spotbugs-baseline.json.ejs +4 -0
- package/dist/templates/scripts/test-gate-layering.mjs.ejs +92 -0
- package/dist/templates/scripts/validate-api-snapshots.mjs.ejs +86 -0
- package/dist/templates/scripts/validate-k6-scenarios.mjs.ejs +96 -0
- package/dist/templates/scripts/validate-openapi-field-types.mjs.ejs +124 -0
- package/dist/templates/scripts/validate-postman-collection.mjs.ejs +159 -0
- package/dist/templates/scripts/verify-fe-coverage.mjs.ejs +138 -0
- package/dist/templates/scripts/verify-i18n-parity.mjs.ejs +114 -0
- package/dist/templates/scripts/verify-module-coverage.mjs.ejs +207 -0
- package/dist/templates/scripts/verify-mutation-baseline.mjs.ejs +209 -0
- package/dist/templates/scripts/verify-spotbugs.mjs.ejs +216 -0
- package/dist/templates/scripts/verify-tokens.mjs.ejs +177 -0
- package/dist/templates/security/RISK_ASSESSMENT.md.ejs +35 -0
- package/dist/templates/security/STRIDE.md.ejs +24 -0
- package/dist/templates/security/gitleaks.toml.ejs +21 -0
- package/dist/templates/security/zap/baseline-auth.context.ejs +58 -0
- package/dist/templates/security/zap/rules.tsv.ejs +100 -0
- package/dist/templates/ship/TICK_PROMPT.md.ejs +55 -0
- package/dist/templates/ship/supervisor.sh.ejs +46 -0
- package/dist/templates/smoke-journeys/manifest.json.ejs +1 -0
- package/dist/templates/sonar-project.properties.ejs +25 -0
- package/dist/templates/standards/doc-profile.ejs +27 -0
- package/dist/templates/standards/gold-doc-set.yml.ejs +528 -0
- package/dist/templates/standards/gold-registry.java.yml.ejs +266 -0
- package/dist/templates/standards/gold-registry.typescript.yml.ejs +104 -0
- package/dist/templates/standards/gold-registry.yml.ejs +119 -0
- package/dist/templates/standards/thresholds.yml.ejs +86 -0
- package/dist/templates/static-analysis/.dependency-cruiser.cjs.ejs +78 -0
- package/dist/templates/static-analysis/.golangci.yml.ejs +70 -0
- package/dist/templates/static-analysis/.shellcheckrc.ejs +13 -0
- package/dist/templates/static-analysis/.yamllint.yml.ejs +41 -0
- package/dist/templates/static-analysis/checkstyle.xml.ejs +83 -0
- package/dist/templates/static-analysis/detekt.yml.ejs +26 -0
- package/dist/templates/static-analysis/eslint.config.mjs.ejs +54 -0
- package/dist/templates/static-analysis/eslint.config.static.mjs.ejs +38 -0
- package/dist/templates/static-analysis/eslintrc-static.json.ejs +30 -0
- package/dist/templates/static-analysis/jscpd.json.ejs +25 -0
- package/dist/templates/static-analysis/knip.json.ejs +26 -0
- package/dist/templates/static-analysis/pmd-ruleset.xml.ejs +92 -0
- package/dist/templates/static-analysis/prettierignore.ejs +59 -0
- package/dist/templates/static-analysis/prettierrc.json.ejs +10 -0
- package/dist/templates/static-analysis/requirements-dev.txt.ejs +11 -0
- package/dist/templates/static-analysis/ruff.toml.ejs +40 -0
- package/dist/templates/static-analysis/rustfmt.toml.ejs +3 -0
- package/dist/templates/static-analysis/spotbugs-exclude.xml.ejs +120 -0
- package/dist/templates/static-analysis/spotbugs.gradle.ejs +18 -0
- package/dist/templates/static-analysis/spotless.gradle.ejs +38 -0
- package/dist/templates/static-analysis/stylelintrc.json.ejs +16 -0
- package/dist/templates/suppressions/archunit-baseline.json.ejs +1 -0
- package/dist/templates/suppressions/consumer-audit-allowlist.json.ejs +1 -0
- package/dist/templates/suppressions/gitleaksignore.ejs +20 -0
- package/dist/templates/suppressions/pii-allowlist.json.ejs +1 -0
- package/dist/templates/suppressions/suppressions-schema.json.ejs +39 -0
- package/dist/templates/suppressions/trivyignore.ejs +17 -0
- package/dist/templates/testing/test-taxonomy.md.ejs +120 -0
- package/dist/test/example.behavioral.test.js +2 -0
- package/dist/types/memory.d.ts +19 -0
- package/dist/types/memory.js +1 -0
- package/dist/types/plan.d.ts +102 -0
- package/dist/types/plan.js +2 -0
- package/dist/types/plugin.d.ts +45 -0
- package/dist/types/plugin.js +1 -0
- package/dist/utils/canon-loader.js +2 -0
- package/dist/utils/channel.js +1 -0
- package/dist/utils/config.d.ts +79 -0
- package/dist/utils/config.js +5 -0
- package/dist/utils/confirm-downgrade.js +5 -0
- package/dist/utils/env.js +2 -0
- package/dist/utils/error-catalog.js +2 -0
- package/dist/utils/errors.js +2 -0
- package/dist/utils/evidence-log.js +3 -0
- package/dist/utils/file-lock.js +2 -0
- package/dist/utils/first-run.js +8 -0
- package/dist/utils/fs.js +3 -0
- package/dist/utils/github-issue-helper.js +4 -0
- package/dist/utils/gradle.js +21 -0
- package/dist/utils/json-output.js +2 -0
- package/dist/utils/logger-config.js +4 -0
- package/dist/utils/logger.js +3 -0
- package/dist/utils/maturity-check.js +2 -0
- package/dist/utils/pkg.js +3 -0
- package/dist/utils/platform.js +2 -0
- package/dist/utils/plugin-loader.js +2 -0
- package/dist/utils/plugin-worker.js +2 -0
- package/dist/utils/prettier-format.js +2 -0
- package/dist/utils/profiler.js +2 -0
- package/dist/utils/render.js +2 -0
- package/dist/utils/replay.js +3 -0
- package/dist/utils/run-cli.js +6 -0
- package/dist/utils/run-id.js +3 -0
- package/dist/utils/safe-read.js +2 -0
- package/dist/utils/task-id.js +2 -0
- package/dist/utils/today.js +2 -0
- package/dist/utils/tty.js +2 -0
- package/dist/utils/walk-dir.js +2 -0
- package/dist/verify/rules/drive-by-scope.js +1 -0
- package/dist/verify/rules/orphan-todos.js +1 -0
- package/dist/verify/rules/registry.js +1 -0
- package/dist/verify/rules/skip-patterns.js +1 -0
- package/dist/verify/rules/types.d.ts +15 -0
- package/dist/verify/rules/types.js +1 -0
- package/dist/verify/rules/ui-language.js +1 -0
- package/dist/verify/run.js +3 -0
- package/dist/wizard/archetype-defaults.js +1 -0
- package/dist/wizard/presets.js +1 -0
- package/dist/wizard/prompts.js +44 -0
- package/dist/wizard/types.d.ts +637 -0
- package/dist/wizard/types.js +1 -0
- package/dist/worktree/harvest.js +2 -0
- package/dist/worktree/links.js +2 -0
- package/dist/worktree/paths.js +2 -0
- package/dist/worktree/validate.js +3 -0
- package/package.json +124 -0
- package/scripts/check-arc42-slots.mjs +738 -0
- package/scripts/check-doc-freshness.mjs +236 -0
- package/scripts/check-doc-set.mjs +243 -0
- package/scripts/check-doc-style.mjs +228 -0
- package/scripts/gold-audit.mjs +319 -0
- package/scripts/lib/doc-gate-allowlist.mjs +123 -0
- package/scripts/lib/doc-set-resolve.mjs +274 -0
- package/scripts/lib/glob-walk.mjs +148 -0
- package/scripts/lib/gold-audit-lib.mjs +1260 -0
- package/scripts/lib/run-helpers.mjs +413 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
// <%= projectName %> — docs/MILESTONES.md gate (MS-NN, EP-NN, INV-146)
|
|
4
|
+
//
|
|
5
|
+
// Emitted twin of arbiter's scripts/check-milestones.mjs. Every RULE is identical — schema
|
|
6
|
+
// validity, unique ids, acyclic DAG, Now/Next/Later decay, fail-closed `done`, and the epic join
|
|
7
|
+
// checked in both directions. THREE things differ, and each is forced rather than chosen:
|
|
8
|
+
//
|
|
9
|
+
// 1. The SSOT is docs/MILESTONES.md carrying a fenced JSON block between sentinels, not YAML.
|
|
10
|
+
// A governed project has no YAML parser: `yaml` is a dependency of arbiter, no emitted script
|
|
11
|
+
// imports it, and a Go or Python project receives 77 .mjs gates and NO package.json at all —
|
|
12
|
+
// there is nowhere to declare one. Shipping `import YAML from 'yaml'` here would be a gate
|
|
13
|
+
// that dies on MODULE_NOT_FOUND the first time a project writes a milestone. The sentinel
|
|
14
|
+
// block is not a new invention either: it is what docs/SOURCES.md and docs/USE_CASES.md
|
|
15
|
+
// already use on this track, for exactly this reason.
|
|
16
|
+
// 2. The path is flat under docs/, as every other emitted document is.
|
|
17
|
+
// 3. An `INV-NN` evidence_ref resolves against AGENTS.md, which is where a governed project's
|
|
18
|
+
// invariants live; the TypeScript catalog is arbiter's own.
|
|
19
|
+
//
|
|
20
|
+
// Pinned in .dogfood-divergences.json. Nothing else is scoped down.
|
|
21
|
+
// CATALOG: validates docs/internal/PRODUCT/MILESTONES.yml — the milestone SSOT — against
|
|
22
|
+
// CATALOG: schemas/milestone.schema.json, then proves the properties a roadmap-as-code must hold
|
|
23
|
+
// CATALOG: for anything derived from it (forma's plan lens, the critical path) to be trustworthy:
|
|
24
|
+
// CATALOG: identifiers are unique, dependencies form a DAG, and a milestone cannot be called done
|
|
25
|
+
// CATALOG: without the evidence its own exit criteria demand.
|
|
26
|
+
// CATALOG: rejected fold-in into check-id-registry.mjs because that gate proves the MS *scheme* is
|
|
27
|
+
// CATALOG: registered and collision-free; this one reads the instances. A registry can be perfect
|
|
28
|
+
// CATALOG: while every milestone in it is a cycle.
|
|
29
|
+
// CATALOG: rejected fold-in into check-doc-set.mjs because doc-set gates a document's PRESENCE and
|
|
30
|
+
// CATALOG: freshness by mtime; a milestone set is a typed graph whose defects (a cycle, a dangling
|
|
31
|
+
// CATALOG: depends_on) are invisible to any presence check.
|
|
32
|
+
// CATALOG: EP-NN folds into THIS gate by design — an epic that targets no milestone is exactly the
|
|
33
|
+
// CATALOG: defect the shared reader exists to surface, so they must be read together (wave 8).
|
|
34
|
+
//
|
|
35
|
+
// scripts/check-milestones.mjs
|
|
36
|
+
// L1 gate: the milestone SSOT is well-formed, acyclic, and fail-closed on `done`.
|
|
37
|
+
//
|
|
38
|
+
// Asserts:
|
|
39
|
+
// 1. the sentinel block parses as JSON and satisfies schemas/milestone.schema.json — which is where
|
|
40
|
+
// the Now/Next/Later granularity decay lives (`due` required for now, forbidden for later),
|
|
41
|
+
// enforceable only since #2509 taught the shared validator if/then/not,
|
|
42
|
+
// 2. no two milestones share an id, and no milestone repeats an exit-criterion id,
|
|
43
|
+
// 3. every `depends_on` names a milestone that exists in this file,
|
|
44
|
+
// 4. the dependency graph is acyclic — reported as the actual cycle, not merely "a cycle exists",
|
|
45
|
+
// 5. `status: done` or `verified` requires EVERY exit criterion to carry an `evidence_ref`.
|
|
46
|
+
// This is the fail-closed rule: a milestone is not done because someone typed done,
|
|
47
|
+
// 6. `status: verified` additionally requires each `evidence_ref` to RESOLVE — a script or path
|
|
48
|
+
// on disk, or an INV id present in the catalog. A `github:` ref is accepted unresolved
|
|
49
|
+
// because this gate is offline by contract (INV-13: the CI runner may be unreachable).
|
|
50
|
+
//
|
|
51
|
+
// And over the epic index in the same file (EP-NN, wave 8):
|
|
52
|
+
// 7. epic ids are unique and no two epics claim the same issue,
|
|
53
|
+
// 8. the milestone join agrees IN BOTH DIRECTIONS — every `members.epics` entry names a declared
|
|
54
|
+
// epic, and every epic's `targets` names a milestone that lists it back. A one-sided link is
|
|
55
|
+
// how an epic detaches from the plan while both halves still read as plausible,
|
|
56
|
+
// 9. an issue is claimed once: a number may not appear both as an epic's `issue` and in some
|
|
57
|
+
// milestone's `members.issues`,
|
|
58
|
+
// 10. a terminal epic status (`drained`, `abandoned`) requires an `evidence_ref` — the same
|
|
59
|
+
// fail-closed rule the milestone statuses carry,
|
|
60
|
+
// 11. a milestone cannot be `done` or `verified` while an epic targeting it is still open.
|
|
61
|
+
//
|
|
62
|
+
// KNOWN LIMIT, stated rather than hidden: this gate cannot tell whether an epic's recorded status
|
|
63
|
+
// matches its GitHub issue, because it never reaches the network (INV-13). That gap is not
|
|
64
|
+
// theoretical — the first three epics recorded here were ALL closed (one completed, two
|
|
65
|
+
// not-planned) while the SSOT prose called them open, and no gate saw it. The detector for that
|
|
66
|
+
// class is the reconciliation pass on the consumer side, which reads a committed issues snapshot;
|
|
67
|
+
// it has never been run against arbiter's own file.
|
|
68
|
+
//
|
|
69
|
+
// A missing MILESTONES.md is a SKIP, not a violation: a project need not have codified a roadmap.
|
|
70
|
+
// The skip prints its reason and is visible in --json as `verdict: "skip"`, so it cannot be
|
|
71
|
+
// mistaken for a pass by a reader or by runCheck's marker grep.
|
|
72
|
+
//
|
|
73
|
+
// Usage: node scripts/check-milestones.mjs [--dir <repo>] [--json] [--emit <path>]
|
|
74
|
+
//
|
|
75
|
+
// --emit writes the machine projection a viewer consumes (schema arbiter-milestones-v1). It is
|
|
76
|
+
// written ONLY after every rule has passed, so an invalid milestone set cannot produce a
|
|
77
|
+
// projection — a property a separate generator could not offer.
|
|
78
|
+
//
|
|
79
|
+
// Exit: 0 pass or skip, 1 violation, 2 error (INV-53).
|
|
80
|
+
//
|
|
81
|
+
// Exports for unit tests: findDuplicateIds, findDanglingDeps, findCycle, findDoneWithoutEvidence,
|
|
82
|
+
// findUnresolvedEvidence, evidenceResolves, collectViolations,
|
|
83
|
+
// annotateSchemaViolations, milestoneProjection, findDuplicateEpicIds,
|
|
84
|
+
// findEpicJoinViolations, findDoubleClaimedIssues,
|
|
85
|
+
// findTerminalEpicsWithoutEvidence, findDoneWithOpenEpics
|
|
86
|
+
|
|
87
|
+
import { readRegularFileSync } from './lib/run-helpers.mjs'
|
|
88
|
+
import { existsSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
89
|
+
import { dirname, join, resolve } from 'node:path'
|
|
90
|
+
import { fileURLToPath } from 'node:url'
|
|
91
|
+
import { loadSchema, validateSchema } from './lib/agent-return-validate.mjs'
|
|
92
|
+
|
|
93
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url))
|
|
94
|
+
const MILESTONES_REL = join('docs', 'MILESTONES.md')
|
|
95
|
+
const SCHEMA_REL = join('schemas', 'milestone.schema.json')
|
|
96
|
+
const CATALOG_REL = 'AGENTS.md'
|
|
97
|
+
const START = '<!-- MILESTONES_START -->'
|
|
98
|
+
const END = '<!-- MILESTONES_END -->'
|
|
99
|
+
|
|
100
|
+
/** Statuses that oblige every exit criterion to carry evidence. */
|
|
101
|
+
const EVIDENCE_REQUIRED = new Set(['done', 'verified'])
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Duplicate milestone ids. The schema cannot see this: it validates each item alone.
|
|
105
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
106
|
+
* @returns {string[]}
|
|
107
|
+
*/
|
|
108
|
+
export function findDuplicateIds(milestones) {
|
|
109
|
+
const seen = new Set()
|
|
110
|
+
const violations = []
|
|
111
|
+
for (const m of milestones) {
|
|
112
|
+
const id = String(m['id'])
|
|
113
|
+
if (seen.has(id)) violations.push(`duplicate milestone id "${id}"`)
|
|
114
|
+
seen.add(id)
|
|
115
|
+
}
|
|
116
|
+
for (const m of milestones) {
|
|
117
|
+
const criteria = Array.isArray(m['exit_criteria']) ? m['exit_criteria'] : []
|
|
118
|
+
const ecSeen = new Set()
|
|
119
|
+
for (const ec of criteria) {
|
|
120
|
+
const ecId = String(/** @type {Record<string, unknown>} */ (ec)['id'])
|
|
121
|
+
if (ecSeen.has(ecId)) {
|
|
122
|
+
violations.push(`milestone ${String(m['id'])}: duplicate exit-criterion id "${ecId}"`)
|
|
123
|
+
}
|
|
124
|
+
ecSeen.add(ecId)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return violations
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* `depends_on` entries naming a milestone that is not in this file.
|
|
132
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
133
|
+
* @returns {string[]}
|
|
134
|
+
*/
|
|
135
|
+
export function findDanglingDeps(milestones) {
|
|
136
|
+
const known = new Set(milestones.map((m) => String(m['id'])))
|
|
137
|
+
const violations = []
|
|
138
|
+
for (const m of milestones) {
|
|
139
|
+
const deps = Array.isArray(m['depends_on']) ? m['depends_on'] : []
|
|
140
|
+
for (const dep of deps) {
|
|
141
|
+
if (!known.has(String(dep))) {
|
|
142
|
+
violations.push(`milestone ${String(m['id'])}: depends_on "${String(dep)}" does not exist`)
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return violations
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* First dependency cycle, as the path that closes it. Reporting the path rather than a bare
|
|
151
|
+
* "cycle detected" is the difference between a usable error and a puzzle.
|
|
152
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
153
|
+
* @returns {string[]}
|
|
154
|
+
*/
|
|
155
|
+
export function findCycle(milestones) {
|
|
156
|
+
const edges = new Map(
|
|
157
|
+
milestones.map((m) => [
|
|
158
|
+
String(m['id']),
|
|
159
|
+
(Array.isArray(m['depends_on']) ? m['depends_on'] : []).map(String),
|
|
160
|
+
]),
|
|
161
|
+
)
|
|
162
|
+
const state = new Map() // id -> 'open' | 'done'
|
|
163
|
+
/** @type {string[]} */
|
|
164
|
+
const stack = []
|
|
165
|
+
|
|
166
|
+
/** @param {string} id @returns {string[] | null} */
|
|
167
|
+
function visit(id) {
|
|
168
|
+
if (state.get(id) === 'done') return null
|
|
169
|
+
if (state.get(id) === 'open') {
|
|
170
|
+
const from = stack.indexOf(id)
|
|
171
|
+
return [...stack.slice(from), id]
|
|
172
|
+
}
|
|
173
|
+
state.set(id, 'open')
|
|
174
|
+
stack.push(id)
|
|
175
|
+
for (const dep of edges.get(id) ?? []) {
|
|
176
|
+
if (!edges.has(dep)) continue // dangling: reported separately, not a cycle
|
|
177
|
+
const cycle = visit(dep)
|
|
178
|
+
if (cycle) return cycle
|
|
179
|
+
}
|
|
180
|
+
stack.pop()
|
|
181
|
+
state.set(id, 'done')
|
|
182
|
+
return null
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
for (const id of edges.keys()) {
|
|
186
|
+
const cycle = visit(id)
|
|
187
|
+
if (cycle) return [`dependency cycle: ${cycle.join(' -> ')}`]
|
|
188
|
+
}
|
|
189
|
+
return []
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The fail-closed rule. `done` without evidence is the single defect this gate exists to stop.
|
|
194
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
195
|
+
* @returns {string[]}
|
|
196
|
+
*/
|
|
197
|
+
export function findDoneWithoutEvidence(milestones) {
|
|
198
|
+
const violations = []
|
|
199
|
+
for (const m of milestones) {
|
|
200
|
+
if (!EVIDENCE_REQUIRED.has(String(m['status']))) continue
|
|
201
|
+
const criteria = Array.isArray(m['exit_criteria']) ? m['exit_criteria'] : []
|
|
202
|
+
for (const ec of criteria) {
|
|
203
|
+
const rec = /** @type {Record<string, unknown>} */ (ec)
|
|
204
|
+
if (typeof rec['evidence_ref'] !== 'string' || rec['evidence_ref'].trim() === '') {
|
|
205
|
+
violations.push(
|
|
206
|
+
`milestone ${String(m['id'])} is "${String(m['status'])}" but exit criterion ` +
|
|
207
|
+
`${String(rec['id'])} carries no evidence_ref — status is not evidence`,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return violations
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Does an evidence_ref point at something real? A `github:` ref is accepted unresolved: this
|
|
217
|
+
* gate is offline by contract, and reaching for the network would make it fail when CI is down
|
|
218
|
+
* rather than when the evidence is missing.
|
|
219
|
+
* @param {string} ref
|
|
220
|
+
* @param {string} root
|
|
221
|
+
* @returns {boolean}
|
|
222
|
+
*/
|
|
223
|
+
export function evidenceResolves(ref, root) {
|
|
224
|
+
if (ref.startsWith('github:')) return true
|
|
225
|
+
if (/^INV-[0-9]+$/.test(ref)) {
|
|
226
|
+
const catalog = join(root, CATALOG_REL)
|
|
227
|
+
if (!existsSync(catalog)) return false
|
|
228
|
+
// Bounded on the trailing digit: a bare `includes("INV-10")` is also satisfied by INV-100,
|
|
229
|
+
// which would resolve a reference to an invariant this project does not carry.
|
|
230
|
+
return new RegExp(`${ref}(?![0-9])`).test(readRegularFileSync(catalog, 'utf-8'))
|
|
231
|
+
}
|
|
232
|
+
return existsSync(join(root, ref))
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* `verified` demands its refs actually resolve — the step beyond `done` that turns a recorded
|
|
237
|
+
* claim into a checked one.
|
|
238
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
239
|
+
* @param {string} root
|
|
240
|
+
* @returns {string[]}
|
|
241
|
+
*/
|
|
242
|
+
export function findUnresolvedEvidence(milestones, root) {
|
|
243
|
+
const violations = []
|
|
244
|
+
for (const m of milestones) {
|
|
245
|
+
if (String(m['status']) !== 'verified') continue
|
|
246
|
+
const criteria = Array.isArray(m['exit_criteria']) ? m['exit_criteria'] : []
|
|
247
|
+
for (const ec of criteria) {
|
|
248
|
+
const rec = /** @type {Record<string, unknown>} */ (ec)
|
|
249
|
+
const ref = rec['evidence_ref']
|
|
250
|
+
if (typeof ref !== 'string' || evidenceResolves(ref, root)) continue
|
|
251
|
+
violations.push(
|
|
252
|
+
`milestone ${String(m['id'])} is "verified" but exit criterion ${String(rec['id'])} ` +
|
|
253
|
+
`cites evidence_ref "${ref}", which does not resolve`,
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return violations
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Statuses that end an epic's life, in either direction. */
|
|
261
|
+
const EPIC_TERMINAL = new Set(['drained', 'abandoned'])
|
|
262
|
+
|
|
263
|
+
/** `members.epics` of one milestone, as strings. @param {Record<string, unknown>} m */
|
|
264
|
+
function claimedEpics(m) {
|
|
265
|
+
const members = /** @type {Record<string, unknown>} */ (m['members'])
|
|
266
|
+
const epics = members && typeof members === 'object' ? members['epics'] : null
|
|
267
|
+
return Array.isArray(epics) ? epics.map(String) : []
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** `members.issues` of one milestone, as numbers. @param {Record<string, unknown>} m */
|
|
271
|
+
function claimedIssues(m) {
|
|
272
|
+
const members = /** @type {Record<string, unknown>} */ (m['members'])
|
|
273
|
+
const issues = members && typeof members === 'object' ? members['issues'] : null
|
|
274
|
+
return Array.isArray(issues) ? issues.map(Number) : []
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Two epics with one id, or two epics claiming one issue. The schema validates each item alone
|
|
279
|
+
* and can see neither.
|
|
280
|
+
* @param {Array<Record<string, unknown>>} epics
|
|
281
|
+
* @returns {string[]}
|
|
282
|
+
*/
|
|
283
|
+
export function findDuplicateEpicIds(epics) {
|
|
284
|
+
const seenIds = new Set()
|
|
285
|
+
const issueOwner = new Map()
|
|
286
|
+
const violations = []
|
|
287
|
+
for (const e of epics) {
|
|
288
|
+
const id = String(e['id'])
|
|
289
|
+
if (seenIds.has(id)) violations.push(`duplicate epic id "${id}"`)
|
|
290
|
+
seenIds.add(id)
|
|
291
|
+
const issue = Number(e['issue'])
|
|
292
|
+
const owner = issueOwner.get(issue)
|
|
293
|
+
if (owner !== undefined) {
|
|
294
|
+
violations.push(`epics ${owner} and ${id} both claim issue #${issue}`)
|
|
295
|
+
} else {
|
|
296
|
+
issueOwner.set(issue, id)
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return violations
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* The join, both ways. A dangling `members.epics` entry and an epic whose milestone does not list
|
|
304
|
+
* it back are the same detachment seen from opposite ends, and each is invisible from its own end.
|
|
305
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
306
|
+
* @param {Array<Record<string, unknown>>} epics
|
|
307
|
+
* @returns {string[]}
|
|
308
|
+
*/
|
|
309
|
+
export function findEpicJoinViolations(milestones, epics) {
|
|
310
|
+
const knownEpics = new Set(epics.map((e) => String(e['id'])))
|
|
311
|
+
const knownMilestones = new Set(milestones.map((m) => String(m['id'])))
|
|
312
|
+
const violations = []
|
|
313
|
+
|
|
314
|
+
for (const m of milestones) {
|
|
315
|
+
for (const claim of claimedEpics(m)) {
|
|
316
|
+
if (!knownEpics.has(claim)) {
|
|
317
|
+
violations.push(
|
|
318
|
+
`milestone ${String(m['id'])}: members.epics names "${claim}", which is not declared in this file`,
|
|
319
|
+
)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
for (const e of epics) {
|
|
325
|
+
const id = String(e['id'])
|
|
326
|
+
const target = String(e['targets'])
|
|
327
|
+
if (!knownMilestones.has(target)) {
|
|
328
|
+
violations.push(`epic ${id}: targets "${target}", which does not exist`)
|
|
329
|
+
continue
|
|
330
|
+
}
|
|
331
|
+
const milestone = milestones.find((m) => String(m['id']) === target)
|
|
332
|
+
if (milestone && !claimedEpics(milestone).includes(id)) {
|
|
333
|
+
violations.push(
|
|
334
|
+
`epic ${id} targets ${target}, but ${target} does not list ${id} in members.epics — the link is one-sided`,
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return violations
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* One issue, one claim. An issue carried as an epic AND as a plain member is counted twice by
|
|
343
|
+
* anything that rolls the plan up, and there is no reading of the file that makes it right.
|
|
344
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
345
|
+
* @param {Array<Record<string, unknown>>} epics
|
|
346
|
+
* @returns {string[]}
|
|
347
|
+
*/
|
|
348
|
+
export function findDoubleClaimedIssues(milestones, epics) {
|
|
349
|
+
const epicIssues = new Map(epics.map((e) => [Number(e['issue']), String(e['id'])]))
|
|
350
|
+
const violations = []
|
|
351
|
+
for (const m of milestones) {
|
|
352
|
+
for (const issue of claimedIssues(m)) {
|
|
353
|
+
const owner = epicIssues.get(issue)
|
|
354
|
+
if (owner === undefined) continue
|
|
355
|
+
violations.push(
|
|
356
|
+
`milestone ${String(m['id'])}: members.issues carries #${issue}, which is already epic ${owner} — an issue is claimed once`,
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return violations
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Fail-closed on the epic ladder: an epic is not finished because someone typed finished. Applies
|
|
365
|
+
* to `abandoned` as much as to `drained` — dropping work is a decision, and a decision with no
|
|
366
|
+
* record is indistinguishable later from work that was quietly forgotten.
|
|
367
|
+
* @param {Array<Record<string, unknown>>} epics
|
|
368
|
+
* @returns {string[]}
|
|
369
|
+
*/
|
|
370
|
+
export function findTerminalEpicsWithoutEvidence(epics) {
|
|
371
|
+
const violations = []
|
|
372
|
+
for (const e of epics) {
|
|
373
|
+
const status = String(e['status'])
|
|
374
|
+
if (!EPIC_TERMINAL.has(status)) continue
|
|
375
|
+
const ref = e['evidence_ref']
|
|
376
|
+
if (typeof ref === 'string' && ref.trim() !== '') continue
|
|
377
|
+
violations.push(
|
|
378
|
+
`epic ${String(e['id'])} is "${status}" but carries no evidence_ref — status is not evidence`,
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
return violations
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* A milestone cannot be finished while work rolled up into it is open. This is the rule that makes
|
|
386
|
+
* the epic index load-bearing rather than decorative: without it a milestone can be marked done
|
|
387
|
+
* with every exit criterion evidenced and an open epic still pointing at it.
|
|
388
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
389
|
+
* @param {Array<Record<string, unknown>>} epics
|
|
390
|
+
* @returns {string[]}
|
|
391
|
+
*/
|
|
392
|
+
export function findDoneWithOpenEpics(milestones, epics) {
|
|
393
|
+
const violations = []
|
|
394
|
+
for (const m of milestones) {
|
|
395
|
+
if (!EVIDENCE_REQUIRED.has(String(m['status']))) continue
|
|
396
|
+
const id = String(m['id'])
|
|
397
|
+
for (const e of epics) {
|
|
398
|
+
if (String(e['targets']) !== id) continue
|
|
399
|
+
const status = String(e['status'])
|
|
400
|
+
if (EPIC_TERMINAL.has(status)) continue
|
|
401
|
+
violations.push(
|
|
402
|
+
`milestone ${id} is "${String(m['status'])}" but epic ${String(e['id'])} targeting it is "${status}"`,
|
|
403
|
+
)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return violations
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Every structural rule, once the document is known schema-valid.
|
|
411
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
412
|
+
* @param {string} root
|
|
413
|
+
* @param {Array<Record<string, unknown>>} [epics]
|
|
414
|
+
* @returns {string[]}
|
|
415
|
+
*/
|
|
416
|
+
export function collectViolations(milestones, root, epics = []) {
|
|
417
|
+
return [
|
|
418
|
+
...findDuplicateIds(milestones),
|
|
419
|
+
...findDanglingDeps(milestones),
|
|
420
|
+
...findCycle(milestones),
|
|
421
|
+
...findDoneWithoutEvidence(milestones),
|
|
422
|
+
...findUnresolvedEvidence(milestones, root),
|
|
423
|
+
...findDuplicateEpicIds(epics),
|
|
424
|
+
...findEpicJoinViolations(milestones, epics),
|
|
425
|
+
...findDoubleClaimedIssues(milestones, epics),
|
|
426
|
+
...findTerminalEpicsWithoutEvidence(epics),
|
|
427
|
+
...findDoneWithOpenEpics(milestones, epics),
|
|
428
|
+
]
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Rewrite `milestones.milestones[2]` as `MS-03` (and `milestones.epics[0]` as `EP-01`), and
|
|
433
|
+
* translate the one schema rule whose native wording is opaque. A `not` violation reads "value
|
|
434
|
+
* must NOT match the not schema, but does", which is true and useless; on this schema it can only
|
|
435
|
+
* mean the granularity-decay rule.
|
|
436
|
+
* @param {string[]} violations
|
|
437
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
438
|
+
* @param {Array<Record<string, unknown>>} [epics]
|
|
439
|
+
* @returns {string[]}
|
|
440
|
+
*/
|
|
441
|
+
export function annotateSchemaViolations(violations, milestones, epics = []) {
|
|
442
|
+
return violations.map((v) => {
|
|
443
|
+
const named = v
|
|
444
|
+
.replace(/^milestones\.milestones\[(\d+)\]/, (_match, index) => {
|
|
445
|
+
const id = milestones[Number(index)]?.['id']
|
|
446
|
+
return id ? `milestone ${String(id)}` : `milestone #${index}`
|
|
447
|
+
})
|
|
448
|
+
.replace(/^milestones\.epics\[(\d+)\]/, (_match, index) => {
|
|
449
|
+
const id = epics[Number(index)]?.['id']
|
|
450
|
+
return id ? `epic ${String(id)}` : `epic #${index}`
|
|
451
|
+
})
|
|
452
|
+
return named.includes('must NOT match the "not" schema')
|
|
453
|
+
? `${named.split(':')[0]}: a "later" milestone must not carry a "due" date (granularity decay)`
|
|
454
|
+
: named
|
|
455
|
+
})
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** @param {string[]} argv */
|
|
459
|
+
function parseArgs(argv) {
|
|
460
|
+
const dirIndex = argv.indexOf('--dir')
|
|
461
|
+
const emitIndex = argv.indexOf('--emit')
|
|
462
|
+
return {
|
|
463
|
+
root: dirIndex >= 0 && argv[dirIndex + 1] ? resolve(argv[dirIndex + 1]) : process.cwd(),
|
|
464
|
+
json: argv.includes('--json'),
|
|
465
|
+
emit: emitIndex >= 0 && argv[emitIndex + 1] ? resolve(argv[emitIndex + 1]) : null,
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* The machine projection a viewer consumes. The SSOT is a document a human edits; this is the
|
|
471
|
+
* derived form a tool reads, and keeping them separate is what stops a consumer reimplementing the
|
|
472
|
+
* reader and holding a second opinion about the same plan.
|
|
473
|
+
*
|
|
474
|
+
* Only what a scheduler needs. The GSN goal and the exit criteria are governance, not schedule,
|
|
475
|
+
* and shipping them here would invite a consumer to re-render arbiter's evidence rules.
|
|
476
|
+
*
|
|
477
|
+
* `estimate_days` and `due` are OMITTED when the SSOT has none rather than defaulted: a consumer
|
|
478
|
+
* must be able to tell "no estimate was given" from "the estimate is zero", which is exactly the
|
|
479
|
+
* distinction forma's own duration heuristic has to declare on screen.
|
|
480
|
+
* `epics` rides along for the same reason `members` does: it is the claimed side of a join whose
|
|
481
|
+
* other side the consumer already holds. Always present, empty when none are declared — an absent
|
|
482
|
+
* key would make "this arbiter is too old to emit epics" indistinguishable from "this plan has
|
|
483
|
+
* none", and the consumer would have to guess which silence it was reading.
|
|
484
|
+
* @param {Array<Record<string, unknown>>} milestones
|
|
485
|
+
* @param {Array<Record<string, unknown>>} [epics]
|
|
486
|
+
* @returns {{ schema: string, epics: Array<Record<string, unknown>>, milestones: Array<Record<string, unknown>> }}
|
|
487
|
+
*/
|
|
488
|
+
export function milestoneProjection(milestones, epics = []) {
|
|
489
|
+
return {
|
|
490
|
+
schema: 'arbiter-milestones-v1',
|
|
491
|
+
epics: [...epics]
|
|
492
|
+
.sort((a, b) => String(a['id']).localeCompare(String(b['id'])))
|
|
493
|
+
.map((e) => ({
|
|
494
|
+
id: e['id'],
|
|
495
|
+
title: e['title'],
|
|
496
|
+
issue: e['issue'],
|
|
497
|
+
targets: e['targets'],
|
|
498
|
+
status: e['status'],
|
|
499
|
+
})),
|
|
500
|
+
milestones: [...milestones]
|
|
501
|
+
.sort((a, b) => String(a['id']).localeCompare(String(b['id'])))
|
|
502
|
+
.map((m) => {
|
|
503
|
+
/** @type {Record<string, unknown>} */
|
|
504
|
+
const row = {
|
|
505
|
+
id: m['id'],
|
|
506
|
+
title: m['title'],
|
|
507
|
+
depends_on: Array.isArray(m['depends_on']) ? [...m['depends_on']].sort() : [],
|
|
508
|
+
horizon: m['horizon'],
|
|
509
|
+
status: m['status'],
|
|
510
|
+
}
|
|
511
|
+
if (typeof m['estimate_days'] === 'number') row['estimate_days'] = m['estimate_days']
|
|
512
|
+
if (typeof m['due'] === 'string') row['due'] = m['due']
|
|
513
|
+
// `members` is the CLAIM: which issues this milestone says it contains. It is not
|
|
514
|
+
// governance and it is not optional colour — it is the join key. The consumer already sees
|
|
515
|
+
// GitHub's side (each issue carries its milestone), and reconciliation drift is exactly the
|
|
516
|
+
// comparison of the two. Without the claimed side a consumer can spot a milestone with no
|
|
517
|
+
// GitHub counterpart but never an issue filed under the wrong one, which is the drift that
|
|
518
|
+
// actually happens. Omitted entirely when the SSOT declares none, so "no claim" stays
|
|
519
|
+
// distinguishable from "claims nothing".
|
|
520
|
+
if (m['members'] && typeof m['members'] === 'object') row['members'] = m['members']
|
|
521
|
+
return row
|
|
522
|
+
}),
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* @param {boolean} json
|
|
528
|
+
* @param {string} verdict
|
|
529
|
+
* @param {string} message
|
|
530
|
+
* @param {string[]} violations
|
|
531
|
+
* @returns {void}
|
|
532
|
+
*/
|
|
533
|
+
function report(json, verdict, message, violations) {
|
|
534
|
+
if (json) {
|
|
535
|
+
process.stdout.write(`${JSON.stringify({ verdict, message, violations }, null, 2)}\n`)
|
|
536
|
+
return
|
|
537
|
+
}
|
|
538
|
+
if (verdict === 'skip') {
|
|
539
|
+
process.stdout.write(`[SKIP] check-milestones: ${message}\n`)
|
|
540
|
+
return
|
|
541
|
+
}
|
|
542
|
+
for (const v of violations) process.stderr.write(`check-milestones: FAIL — ${v}\n`)
|
|
543
|
+
if (verdict === 'pass') process.stdout.write(`check-milestones: PASS — ${message}\n`)
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** @param {string[]} argv @returns {number} */
|
|
547
|
+
/**
|
|
548
|
+
* Pull the fenced JSON out of the sentinel block. THROWS on a malformed block rather than
|
|
549
|
+
* returning an empty set: "this project codified no milestones" and "the file is broken" must not
|
|
550
|
+
* produce the same green, which is the same rule the emitted sources gate holds itself to.
|
|
551
|
+
* @param {string} markdown
|
|
552
|
+
* @returns {unknown}
|
|
553
|
+
*/
|
|
554
|
+
export function extractBlock(markdown) {
|
|
555
|
+
const start = markdown.indexOf(START)
|
|
556
|
+
const end = markdown.indexOf(END)
|
|
557
|
+
if (start < 0 || end < 0 || end < start) {
|
|
558
|
+
throw new Error(`missing its ${START} / ${END} sentinel block`)
|
|
559
|
+
}
|
|
560
|
+
const fence = /```json\n([\s\S]*?)\n```/.exec(markdown.slice(start + START.length, end))
|
|
561
|
+
if (!fence) throw new Error(`has no \`\`\`json fence between its sentinels`)
|
|
562
|
+
try {
|
|
563
|
+
return JSON.parse(fence[1])
|
|
564
|
+
} catch (err) {
|
|
565
|
+
throw new Error(`block is not valid JSON — ${err.message}`)
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Read the SSOT and its schema (INV-53, #2553). A tracked file that cannot be parsed is the
|
|
571
|
+
* author's artifact — exit 1, filed as `unreadable SSOT` exactly as check-use-cases does. A file
|
|
572
|
+
* that cannot be READ (missing, not a regular file) or an unloadable schema means the gate could
|
|
573
|
+
* not tell — exit 2.
|
|
574
|
+
* @returns {{ code: number } | { doc: unknown, schema: unknown }}
|
|
575
|
+
*/
|
|
576
|
+
function loadInputs(path, json) {
|
|
577
|
+
let raw
|
|
578
|
+
try {
|
|
579
|
+
raw = readRegularFileSync(path, 'utf-8')
|
|
580
|
+
} catch (err) {
|
|
581
|
+
process.stderr.write(`check-milestones: cannot read ${MILESTONES_REL} — ${err.message}\n`)
|
|
582
|
+
return { code: 2 }
|
|
583
|
+
}
|
|
584
|
+
let doc
|
|
585
|
+
try {
|
|
586
|
+
doc = extractBlock(raw)
|
|
587
|
+
} catch (err) {
|
|
588
|
+
// #2553 (INV-53): a tracked SSOT that cannot be parsed is the author's artifact — 1, not 2.
|
|
589
|
+
report(json, 'fail', 'unreadable SSOT', [`unreadable SSOT — ${MILESTONES_REL} — ${err.message}`])
|
|
590
|
+
return { code: 1 }
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
return { doc, schema: loadSchema(resolve(scriptDir, '..', SCHEMA_REL)) }
|
|
594
|
+
} catch (err) {
|
|
595
|
+
process.stderr.write(`check-milestones: cannot load ${SCHEMA_REL} — ${err.message}\n`)
|
|
596
|
+
return { code: 2 }
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* Write the projection. Called only after every rule has passed, which is the property that keeps
|
|
602
|
+
* an invalid plan from producing a consumable artifact at all.
|
|
603
|
+
*/
|
|
604
|
+
/** A top-level array field, or an empty array — the document may be any shape when it is invalid. */
|
|
605
|
+
function listOf(doc, key) {
|
|
606
|
+
return Array.isArray(doc?.[key]) ? doc[key] : []
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Report schema violations with ids substituted for array indices, and say whether any were found.
|
|
611
|
+
* Reads the two lists defensively: this runs precisely when the document is known NOT to conform,
|
|
612
|
+
* so neither `milestones` nor `epics` can be assumed to be an array.
|
|
613
|
+
* @returns {boolean} true when the caller should stop
|
|
614
|
+
*/
|
|
615
|
+
function reportSchemaViolations(schemaViolations, doc, json) {
|
|
616
|
+
if (schemaViolations.length === 0) return false
|
|
617
|
+
const annotated = annotateSchemaViolations(
|
|
618
|
+
schemaViolations,
|
|
619
|
+
listOf(doc, 'milestones'),
|
|
620
|
+
listOf(doc, 'epics'),
|
|
621
|
+
)
|
|
622
|
+
report(json, 'fail', 'schema violations', annotated)
|
|
623
|
+
return true
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
function emitProjection(emit, milestones, epics, json) {
|
|
627
|
+
mkdirSync(dirname(emit), { recursive: true })
|
|
628
|
+
writeFileSync(
|
|
629
|
+
emit,
|
|
630
|
+
`${JSON.stringify(milestoneProjection(milestones, epics), null, 2)}\n`,
|
|
631
|
+
'utf-8',
|
|
632
|
+
)
|
|
633
|
+
if (!json) process.stdout.write(`check-milestones: projection written to ${emit}\n`)
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function main(argv) {
|
|
637
|
+
const { root, json, emit } = parseArgs(argv)
|
|
638
|
+
const path = join(root, MILESTONES_REL)
|
|
639
|
+
if (!existsSync(path)) {
|
|
640
|
+
report(json, 'skip', `${MILESTONES_REL} absent — no roadmap codified in this project`, [])
|
|
641
|
+
return 0
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const loaded = loadInputs(path, json)
|
|
645
|
+
if (loaded.code !== undefined) return loaded.code
|
|
646
|
+
const { doc, schema } = loaded
|
|
647
|
+
|
|
648
|
+
const schemaViolations = validateSchema(doc, schema, schema, 'milestones')
|
|
649
|
+
if (reportSchemaViolations(schemaViolations, doc, json)) return 1
|
|
650
|
+
|
|
651
|
+
const milestones = doc.milestones
|
|
652
|
+
const epics = listOf(doc, 'epics')
|
|
653
|
+
const violations = collectViolations(milestones, root, epics)
|
|
654
|
+
if (violations.length > 0) {
|
|
655
|
+
report(json, 'fail', 'structural violations', violations)
|
|
656
|
+
return 1
|
|
657
|
+
}
|
|
658
|
+
// Emitted only here — AFTER schema validation and every structural rule has passed. Putting the
|
|
659
|
+
// projection behind the gate rather than in a separate generator buys the property that an
|
|
660
|
+
// invalid milestone set cannot produce a projection at all.
|
|
661
|
+
if (emit) emitProjection(emit, milestones, epics, json)
|
|
662
|
+
report(
|
|
663
|
+
json,
|
|
664
|
+
'pass',
|
|
665
|
+
`${milestones.length} milestone(s) and ${epics.length} epic(s), acyclic, evidence-complete, join agrees both ways`,
|
|
666
|
+
[],
|
|
667
|
+
)
|
|
668
|
+
return 0
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
672
|
+
try {
|
|
673
|
+
process.exit(main(process.argv.slice(2)))
|
|
674
|
+
} catch (err) {
|
|
675
|
+
process.stderr.write(`check-milestones: ${err?.stack ?? err}\n`)
|
|
676
|
+
process.exit(2)
|
|
677
|
+
}
|
|
678
|
+
}
|