@kontourai/flow-agents 0.1.1
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/.githooks/pre-push +11 -0
- package/.github/workflows/ci.yml +210 -0
- package/.github/workflows/docs-pages.yml +52 -0
- package/.github/workflows/publish-npm.yml +104 -0
- package/AGENTS.md +26 -0
- package/CHANGELOG.md +66 -0
- package/CODE_OF_CONDUCT.md +25 -0
- package/CONTEXT.md +300 -0
- package/CONTRIBUTING.md +44 -0
- package/LICENSE +201 -0
- package/README.md +129 -0
- package/SECURITY.md +33 -0
- package/agent-cards/dev.json +19 -0
- package/agents/dev.json +127 -0
- package/agents/tool-code-reviewer.json +61 -0
- package/agents/tool-dependencies-updater.json +118 -0
- package/agents/tool-explore-config.json +92 -0
- package/agents/tool-explore-deps.json +92 -0
- package/agents/tool-explore-entry.json +92 -0
- package/agents/tool-explore-patterns.json +92 -0
- package/agents/tool-explore-structure.json +92 -0
- package/agents/tool-explore-tests.json +92 -0
- package/agents/tool-planner.json +57 -0
- package/agents/tool-playwright.json +145 -0
- package/agents/tool-security-reviewer.json +56 -0
- package/agents/tool-verifier.json +61 -0
- package/agents/tool-worker.json +58 -0
- package/build/src/cli/console-learning-projection.js +123 -0
- package/build/src/cli/docs-preview.js +39 -0
- package/build/src/cli/effective-backlog-settings.js +102 -0
- package/build/src/cli/export-bookmarks.js +38 -0
- package/build/src/cli/fixture-retirement-audit.js +140 -0
- package/build/src/cli/flow-kit.js +138 -0
- package/build/src/cli/import-bookmarks.js +50 -0
- package/build/src/cli/init.js +239 -0
- package/build/src/cli/instinct-cli.js +93 -0
- package/build/src/cli/promote-workflow-artifact.js +63 -0
- package/build/src/cli/publish-change-helper.js +154 -0
- package/build/src/cli/pull-work-provider.js +469 -0
- package/build/src/cli/runtime-adapter.js +23 -0
- package/build/src/cli/telemetry-doctor.js +221 -0
- package/build/src/cli/usage-feedback.js +443 -0
- package/build/src/cli/validate-hook-influence.js +152 -0
- package/build/src/cli/validate-source-tree.js +31 -0
- package/build/src/cli/validate-workflow-artifacts.js +486 -0
- package/build/src/cli/veritas-governance.js +262 -0
- package/build/src/cli/workflow-artifact-cleanup-audit.js +272 -0
- package/build/src/cli/workflow-sidecar.js +816 -0
- package/build/src/cli.js +89 -0
- package/build/src/flow-kit/validate.js +75 -0
- package/build/src/lib/args.js +45 -0
- package/build/src/lib/fs.js +62 -0
- package/build/src/lib/workflow-learning-projection.js +334 -0
- package/build/src/runtime-adapters.js +146 -0
- package/build/src/tools/build-universal-bundles.js +397 -0
- package/build/src/tools/common.js +56 -0
- package/build/src/tools/filter-installed-packs.js +132 -0
- package/build/src/tools/generate-context-map.js +198 -0
- package/build/src/tools/validate-package.js +64 -0
- package/build/src/tools/validate-source-tree.js +622 -0
- package/console.telemetry.json +176 -0
- package/context/base-rules.md +17 -0
- package/context/code-review-standards.md +62 -0
- package/context/coding-standards.md +42 -0
- package/context/common/orchestrators.md +12 -0
- package/context/common/subagents.md +28 -0
- package/context/contracts/artifact-contract.md +182 -0
- package/context/contracts/builder-kit-workflow-state-contract.md +319 -0
- package/context/contracts/delivery-contract.md +69 -0
- package/context/contracts/execution-contract.md +53 -0
- package/context/contracts/governance-adapter-contract.md +67 -0
- package/context/contracts/planning-contract.md +85 -0
- package/context/contracts/review-contract.md +104 -0
- package/context/contracts/sandbox-policy.md +52 -0
- package/context/contracts/verification-contract.md +134 -0
- package/context/contracts/work-item-contract.md +215 -0
- package/context/deferred/demo-mode.md +33 -0
- package/context/deferred/languages/go.md +31 -0
- package/context/deferred/languages/python.md +31 -0
- package/context/deferred/languages/typescript.md +34 -0
- package/context/deferred/parallelization.md +35 -0
- package/context/deferred/worktree-isolation.md +24 -0
- package/context/development-workflow.md +50 -0
- package/context/scripts/context-budget/budget-scan.sh +166 -0
- package/context/scripts/detect-tools.sh +3 -0
- package/context/scripts/discover-agents.sh +28 -0
- package/context/scripts/git-status.sh +49 -0
- package/context/scripts/hooks/config-protection.js +79 -0
- package/context/scripts/hooks/desktop-notify.sh +39 -0
- package/context/scripts/hooks/governance-audit.sh +135 -0
- package/context/scripts/hooks/lib/audit-transport.sh +40 -0
- package/context/scripts/hooks/lib/hook-flags.js +49 -0
- package/context/scripts/hooks/lib/patterns.sh +57 -0
- package/context/scripts/hooks/lib/resolve-formatter.js +80 -0
- package/context/scripts/hooks/post-edit-accumulator.js +66 -0
- package/context/scripts/hooks/pre-commit-quality.js +194 -0
- package/context/scripts/hooks/quality-gate.js +93 -0
- package/context/scripts/hooks/report-only-guard.js +21 -0
- package/context/scripts/hooks/run-hook.js +136 -0
- package/context/scripts/hooks/stop-format-typecheck.js +141 -0
- package/context/scripts/hooks/stop-goal-fit.js +337 -0
- package/context/scripts/hooks/workflow-steering.js +250 -0
- package/context/scripts/telemetry/console-presets.sh +14 -0
- package/context/scripts/telemetry/install-console-config.sh +214 -0
- package/context/scripts/telemetry/lib/config.sh +85 -0
- package/context/scripts/telemetry/lib/enrich.sh +115 -0
- package/context/scripts/telemetry/lib/redact.sh +22 -0
- package/context/scripts/telemetry/lib/session.sh +63 -0
- package/context/scripts/telemetry/lib/transport.sh +183 -0
- package/context/scripts/telemetry/lib/usage.sh +29 -0
- package/context/scripts/telemetry/sync-agents.sh +173 -0
- package/context/scripts/telemetry/telemetry.conf +23 -0
- package/context/scripts/telemetry/telemetry.sh +387 -0
- package/context/scripts/validate-package.sh +89 -0
- package/context/settings/backlog-provider-settings.json +54 -0
- package/context/templates/core/identity.md +26 -0
- package/context/templates/core/user.md +15 -0
- package/docs/_config.yml +15 -0
- package/docs/_layouts/default.html +87 -0
- package/docs/adr/0001-flow-agents-consumes-flow.md +77 -0
- package/docs/adr/0002-flow-kits-as-extension-unit.md +13 -0
- package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +13 -0
- package/docs/adr/0004-gates-expect-surface-claims.md +15 -0
- package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +48 -0
- package/docs/adr/0006-typescript-first-source-policy.md +98 -0
- package/docs/agent-system-guidebook.md +391 -0
- package/docs/agent-usage-feedback-loop.md +351 -0
- package/docs/assets/favicon.svg +13 -0
- package/docs/assets/og-image.png +0 -0
- package/docs/assets/site.css +774 -0
- package/docs/assets/site.js +139 -0
- package/docs/configurable-workflow-routing.md +174 -0
- package/docs/context-map.md +145 -0
- package/docs/developer-architecture.md +145 -0
- package/docs/developer-hook-setup.md +61 -0
- package/docs/fixture-ownership.md +44 -0
- package/docs/flow-kit-repository-contract.md +180 -0
- package/docs/index.md +129 -0
- package/docs/kontour-resource-contract.md +358 -0
- package/docs/migrations.md +64 -0
- package/docs/north-star.md +322 -0
- package/docs/operating-layers.md +110 -0
- package/docs/repository-structure.md +132 -0
- package/docs/sandbox-policy.md +56 -0
- package/docs/skills-map.md +203 -0
- package/docs/standards-register.md +96 -0
- package/docs/veritas-integration.md +165 -0
- package/docs/work-item-adapters.md +72 -0
- package/docs/workflow-artifact-lifecycle.md +141 -0
- package/docs/workflow-eval-strategy.md +295 -0
- package/docs/workflow-shared-contracts.md +51 -0
- package/docs/workflow-usage-guide.md +443 -0
- package/evals/ARCHITECTURE.md +143 -0
- package/evals/CONVENTIONS.md +58 -0
- package/evals/README.md +128 -0
- package/evals/acceptance/run.sh +29 -0
- package/evals/acceptance/test_claude_harness.sh +242 -0
- package/evals/acceptance/test_codex_harness.sh +108 -0
- package/evals/acceptance/test_kiro_harness.sh +128 -0
- package/evals/cases/dev/404.html +97 -0
- package/evals/cases/dev/code-review.yaml +44 -0
- package/evals/cases/dev/dashboard.html +300 -0
- package/evals/cases/dev/deliver.yaml +66 -0
- package/evals/cases/dev/dependency-update.yaml +16 -0
- package/evals/cases/dev/explore.yaml +20 -0
- package/evals/cases/dev/index.html +370 -0
- package/evals/cases/dev/package-lock.json +28 -0
- package/evals/cases/dev/package.json +16 -0
- package/evals/cases/dev/plan-work.yaml +20 -0
- package/evals/cases/dev/promptfooconfig.yaml +666 -0
- package/evals/cases/dev/search-first.yaml +20 -0
- package/evals/cases/dev/tdd-workflow.yaml +48 -0
- package/evals/cases/dev/verify-work.yaml +44 -0
- package/evals/cases/dev/workflow.yaml +34 -0
- package/evals/ci/run-baseline.sh +283 -0
- package/evals/fixtures/backlog-provider-settings/global-default.json +44 -0
- package/evals/fixtures/backlog-provider-settings/project-override.json +53 -0
- package/evals/fixtures/builder-kit-workflow-state/baseline-freshness-resolution-hint.json +139 -0
- package/evals/fixtures/builder-kit-workflow-state/direct-primitive-stop.json +59 -0
- package/evals/fixtures/builder-kit-workflow-state/empty-board-route-shape.json +55 -0
- package/evals/fixtures/builder-kit-workflow-state/happy-path.json +71 -0
- package/evals/fixtures/builder-kit-workflow-state/mid-work-resume.json +80 -0
- package/evals/fixtures/builder-kit-workflow-state/missing-prestep-recovery.json +65 -0
- package/evals/fixtures/builder-kit-workflow-state/product-build-chaining.json +60 -0
- package/evals/fixtures/builder-kit-workflow-state/stale-continuation-requires-new-probe.json +57 -0
- package/evals/fixtures/console-learning-projection/artifacts/console-learning-correction/learning.json +50 -0
- package/evals/fixtures/console-learning-projection/artifacts/console-learning-open-route/learning.json +41 -0
- package/evals/fixtures/flow-kit-repository/invalid-absolute-path/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-asset-section/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-asset-section/kit.json +11 -0
- package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/kit.json +9 -0
- package/evals/fixtures/flow-kit-repository/invalid-id/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-id/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-malformed-json/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-flow/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-id/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-id/kit.json +7 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/kit.json +7 -0
- package/evals/fixtures/flow-kit-repository/invalid-name/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-name/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-schema-version/flows/review.flow.json +6 -0
- package/evals/fixtures/flow-kit-repository/invalid-schema-version/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/invalid-traversal/kit.json +8 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/adapters/example.json +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/assets/example.txt +1 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/docs/README.md +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/flows/runtime.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-evals/example.json +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-skills/mixed/SKILL.md +3 -0
- package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit.json +44 -0
- package/evals/fixtures/flow-kit-repository/valid-local-kit/docs/README.md +3 -0
- package/evals/fixtures/flow-kit-repository/valid-local-kit/flows/review.flow.json +26 -0
- package/evals/fixtures/flow-kit-repository/valid-local-kit/kit.json +20 -0
- package/evals/fixtures/hook-influence/cases.json +336 -0
- package/evals/fixtures/pull-work-provider/github-issues.json +170 -0
- package/evals/fixtures/pull-work-wip-shepherding/global-wip-informs.json +43 -0
- package/evals/fixtures/pull-work-wip-shepherding/personal-wip-blocks.json +42 -0
- package/evals/fixtures/surface-trust/accepted-claim-trust-report.json +31 -0
- package/evals/fixtures/surface-trust/artifact-absent.json +19 -0
- package/evals/fixtures/surface-trust/integrity-mismatch-trust-report.json +32 -0
- package/evals/fixtures/surface-trust/missing-authority-trust-report.json +27 -0
- package/evals/fixtures/surface-trust/provider-absent.json +19 -0
- package/evals/fixtures/surface-trust/rejected-claim-trust-report.json +30 -0
- package/evals/fixtures/surface-trust/stale-claim-trust-snapshot.json +31 -0
- package/evals/fixtures/usage-feedback/sample-full.jsonl +11 -0
- package/evals/fixtures/usage-feedback/sample-outcomes.jsonl +1 -0
- package/evals/fixtures/veritas-governance-adapter/fake-veritas-pass.sh +18 -0
- package/evals/fixtures/veritas-governance-adapter/fake-veritas-secret-fail.sh +10 -0
- package/evals/fixtures/veritas-governance-adapter/fake-veritas-unconfigured.sh +4 -0
- package/evals/integration/test_bundle_install.sh +541 -0
- package/evals/integration/test_console_learning_projection.sh +192 -0
- package/evals/integration/test_context_map.sh +65 -0
- package/evals/integration/test_effective_backlog_settings.sh +58 -0
- package/evals/integration/test_fixture_retirement_audit.sh +58 -0
- package/evals/integration/test_flow_agents_statusline.sh +93 -0
- package/evals/integration/test_flow_kit_repository.sh +90 -0
- package/evals/integration/test_goal_fit_hook.sh +482 -0
- package/evals/integration/test_hook_category_behaviors.sh +190 -0
- package/evals/integration/test_hook_influence_cases.sh +69 -0
- package/evals/integration/test_local_flow_kit_install.sh +145 -0
- package/evals/integration/test_publish_change_helper.sh +176 -0
- package/evals/integration/test_pull_work_provider.sh +140 -0
- package/evals/integration/test_runtime_adapter_activation.sh +106 -0
- package/evals/integration/test_telemetry.sh +485 -0
- package/evals/integration/test_telemetry_doctor.sh +193 -0
- package/evals/integration/test_usage_feedback_dashboard.sh +169 -0
- package/evals/integration/test_usage_feedback_global.sh +117 -0
- package/evals/integration/test_usage_feedback_import.sh +227 -0
- package/evals/integration/test_usage_feedback_outcomes.sh +165 -0
- package/evals/integration/test_usage_feedback_report.sh +263 -0
- package/evals/integration/test_veritas_governance_adapter.sh +235 -0
- package/evals/integration/test_workflow_artifact_cleanup_audit.sh +287 -0
- package/evals/integration/test_workflow_artifacts.sh +1247 -0
- package/evals/integration/test_workflow_sidecar_writer.sh +2112 -0
- package/evals/integration/test_workflow_steering_hook.sh +337 -0
- package/evals/lib/assertions/delegated-to.js +40 -0
- package/evals/lib/assertions/max-tool-calls.js +15 -0
- package/evals/lib/assertions/no-write-tools.js +27 -0
- package/evals/lib/assertions/pass-at-k.js +39 -0
- package/evals/lib/assertions/telemetry-utils.js +105 -0
- package/evals/lib/assertions/tool-called.js +39 -0
- package/evals/lib/assertions/verify-after-fix.js +61 -0
- package/evals/lib/claude-judge.sh +40 -0
- package/evals/lib/claude-provider.sh +74 -0
- package/evals/lib/codex-judge.sh +39 -0
- package/evals/lib/codex-provider.sh +81 -0
- package/evals/lib/eval-dev.sh +5 -0
- package/evals/lib/eval-judge.sh +22 -0
- package/evals/lib/eval-provider.sh +26 -0
- package/evals/lib/eval-report.sh +73 -0
- package/evals/lib/kiro-dev.sh +4 -0
- package/evals/lib/kiro-judge.sh +17 -0
- package/evals/lib/kiro-provider.sh +62 -0
- package/evals/lib/node.sh +111 -0
- package/evals/promptfooconfig.yaml +70 -0
- package/evals/run.sh +309 -0
- package/evals/static/test_evidence_refs.sh +141 -0
- package/evals/static/test_package.sh +407 -0
- package/evals/static/test_repo_hooks.sh +68 -0
- package/evals/static/test_universal_bundles.sh +274 -0
- package/evals/static/test_workflow_skills.sh +1207 -0
- package/install.sh +64 -0
- package/integrations/veritas/flow-agents.adapter.json +138 -0
- package/integrations/veritas/flow-agents.authority-settings.json +26 -0
- package/integrations/veritas/flow-agents.repo-standards.json +82 -0
- package/kits/builder/flows/build.flow.json +218 -0
- package/kits/builder/flows/shape.flow.json +127 -0
- package/kits/builder/kit.json +19 -0
- package/kits/catalog.json +11 -0
- package/package.json +130 -0
- package/packaging/README.md +60 -0
- package/packaging/manifest.json +173 -0
- package/packaging/packs.json +69 -0
- package/powers/dependency-checker/POWER.md +20 -0
- package/powers/dependency-checker/mcp.json +20 -0
- package/powers/playwright/POWER.md +25 -0
- package/powers/playwright/mcp.json +12 -0
- package/prompts/code-audit.md +123 -0
- package/prompts/kcommit.md +88 -0
- package/schemas/backlog-provider-settings.schema.json +138 -0
- package/schemas/workflow-acceptance.schema.json +216 -0
- package/schemas/workflow-critique.schema.json +113 -0
- package/schemas/workflow-evidence.schema.json +357 -0
- package/schemas/workflow-handoff.schema.json +52 -0
- package/schemas/workflow-learning.schema.json +223 -0
- package/schemas/workflow-release.schema.json +172 -0
- package/schemas/workflow-state.schema.json +80 -0
- package/scripts/README.md +111 -0
- package/scripts/build-universal-bundles.js +3 -0
- package/scripts/check-content-boundary.cjs +99 -0
- package/scripts/context-budget/budget-scan.sh +166 -0
- package/scripts/detect-tools.sh +3 -0
- package/scripts/discover-agents.sh +28 -0
- package/scripts/effective-backlog-settings.js +2 -0
- package/scripts/filter-installed-packs.js +2 -0
- package/scripts/flow-kit.js +2 -0
- package/scripts/generate-context-map.js +2 -0
- package/scripts/git-status.sh +49 -0
- package/scripts/hooks/claude-hook-adapter.js +174 -0
- package/scripts/hooks/claude-telemetry-hook.js +115 -0
- package/scripts/hooks/codex-hook-adapter.js +176 -0
- package/scripts/hooks/codex-telemetry-hook.js +95 -0
- package/scripts/hooks/config-protection.js +79 -0
- package/scripts/hooks/desktop-notify.sh +39 -0
- package/scripts/hooks/governance-audit.sh +135 -0
- package/scripts/hooks/lib/audit-transport.sh +40 -0
- package/scripts/hooks/lib/hook-flags.js +49 -0
- package/scripts/hooks/lib/patterns.sh +57 -0
- package/scripts/hooks/lib/resolve-formatter.js +80 -0
- package/scripts/hooks/post-edit-accumulator.js +66 -0
- package/scripts/hooks/pre-commit-quality.js +194 -0
- package/scripts/hooks/quality-gate.js +93 -0
- package/scripts/hooks/report-only-guard.js +21 -0
- package/scripts/hooks/run-hook.js +136 -0
- package/scripts/hooks/stop-format-typecheck.js +141 -0
- package/scripts/hooks/stop-goal-fit.js +337 -0
- package/scripts/hooks/workflow-steering.js +250 -0
- package/scripts/install-codex-home.sh +106 -0
- package/scripts/package.json +3 -0
- package/scripts/promote-workflow-artifact.js +2 -0
- package/scripts/publish-change-helper.js +2 -0
- package/scripts/pull-work-provider.js +2 -0
- package/scripts/setup-repo-hooks.sh +8 -0
- package/scripts/statusline/flow-agents-statusline.js +157 -0
- package/scripts/telemetry/console-presets.sh +14 -0
- package/scripts/telemetry/install-console-config.sh +214 -0
- package/scripts/telemetry/lib/config.sh +85 -0
- package/scripts/telemetry/lib/enrich.sh +115 -0
- package/scripts/telemetry/lib/redact.sh +22 -0
- package/scripts/telemetry/lib/session.sh +63 -0
- package/scripts/telemetry/lib/transport.sh +183 -0
- package/scripts/telemetry/lib/usage.sh +29 -0
- package/scripts/telemetry/sync-agents.sh +173 -0
- package/scripts/telemetry/telemetry.conf +23 -0
- package/scripts/telemetry/telemetry.sh +387 -0
- package/scripts/usage-feedback.js +2 -0
- package/scripts/validate-hook-influence-cases.js +2 -0
- package/scripts/validate-package.sh +89 -0
- package/scripts/validate-source-tree.js +9 -0
- package/skills/agentic-engineering/SKILL.md +62 -0
- package/skills/browser-test/SKILL.md +51 -0
- package/skills/builder-shape/SKILL.md +76 -0
- package/skills/context-budget/SKILL.md +40 -0
- package/skills/deliver/SKILL.md +241 -0
- package/skills/dependency-update/SKILL.md +68 -0
- package/skills/design-probe/SKILL.md +107 -0
- package/skills/eval-rebuild/SKILL.md +39 -0
- package/skills/evidence-gate/SKILL.md +186 -0
- package/skills/execute-plan/SKILL.md +110 -0
- package/skills/explore/SKILL.md +137 -0
- package/skills/feedback-loop/SKILL.md +87 -0
- package/skills/fix-bug/SKILL.md +133 -0
- package/skills/frontend-design/SKILL.md +80 -0
- package/skills/github-cli/SKILL.md +63 -0
- package/skills/idea-to-backlog/SKILL.md +267 -0
- package/skills/knowledge-capture/SKILL.md +55 -0
- package/skills/learning-review/SKILL.md +115 -0
- package/skills/pickup-probe/SKILL.md +114 -0
- package/skills/plan-work/SKILL.md +176 -0
- package/skills/pull-work/SKILL.md +309 -0
- package/skills/release-readiness/SKILL.md +121 -0
- package/skills/review-work/SKILL.md +161 -0
- package/skills/search-first/SKILL.md +66 -0
- package/skills/tdd-workflow/SKILL.md +140 -0
- package/skills/verify-work/SKILL.md +109 -0
- package/src/cli/console-learning-projection.ts +140 -0
- package/src/cli/effective-backlog-settings.ts +99 -0
- package/src/cli/fixture-retirement-audit.ts +154 -0
- package/src/cli/flow-kit.ts +139 -0
- package/src/cli/init.ts +248 -0
- package/src/cli/promote-workflow-artifact.ts +64 -0
- package/src/cli/publish-change-helper.ts +143 -0
- package/src/cli/pull-work-provider.ts +481 -0
- package/src/cli/runtime-adapter.ts +24 -0
- package/src/cli/telemetry-doctor.ts +243 -0
- package/src/cli/usage-feedback.ts +418 -0
- package/src/cli/validate-hook-influence.ts +119 -0
- package/src/cli/validate-source-tree.ts +30 -0
- package/src/cli/validate-workflow-artifacts.ts +411 -0
- package/src/cli/veritas-governance.ts +322 -0
- package/src/cli/workflow-artifact-cleanup-audit.ts +281 -0
- package/src/cli/workflow-sidecar.ts +676 -0
- package/src/cli.ts +95 -0
- package/src/flow-kit/validate.ts +74 -0
- package/src/lib/args.ts +43 -0
- package/src/lib/fs.ts +62 -0
- package/src/lib/workflow-learning-projection.ts +491 -0
- package/src/runtime-adapters.ts +154 -0
- package/src/tools/build-universal-bundles.ts +366 -0
- package/src/tools/common.ts +61 -0
- package/src/tools/filter-installed-packs.ts +129 -0
- package/src/tools/generate-context-map.ts +199 -0
- package/src/tools/validate-package.ts +57 -0
- package/src/tools/validate-source-tree.ts +488 -0
- package/tsconfig.json +19 -0
- package/veritas.claims.json +6 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
const root = path.resolve(".");
|
|
5
|
+
const statusRe = /\[(PASS|FAIL|NOT_VERIFIED|SKIP|PARTIAL)\]/i;
|
|
6
|
+
const dateTimeRe = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
7
|
+
const sidecarSchemas = {
|
|
8
|
+
"state.json": "schemas/workflow-state.schema.json",
|
|
9
|
+
"acceptance.json": "schemas/workflow-acceptance.schema.json",
|
|
10
|
+
"evidence.json": "schemas/workflow-evidence.schema.json",
|
|
11
|
+
"handoff.json": "schemas/workflow-handoff.schema.json",
|
|
12
|
+
"critique.json": "schemas/workflow-critique.schema.json",
|
|
13
|
+
"release.json": "schemas/workflow-release.schema.json",
|
|
14
|
+
"learning.json": "schemas/workflow-learning.schema.json",
|
|
15
|
+
};
|
|
16
|
+
function readText(file) {
|
|
17
|
+
return fs.readFileSync(file, "utf8");
|
|
18
|
+
}
|
|
19
|
+
function section(text, heading) {
|
|
20
|
+
const re = new RegExp(`^(?<marks>##+)\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*$`, "m");
|
|
21
|
+
const match = re.exec(text);
|
|
22
|
+
if (!match?.groups)
|
|
23
|
+
return "";
|
|
24
|
+
const level = match.groups.marks.length;
|
|
25
|
+
const start = match.index + match[0].length;
|
|
26
|
+
const rest = text.slice(start);
|
|
27
|
+
const next = new RegExp(`^#{2,${level}}\\s+`, "m").exec(rest);
|
|
28
|
+
return rest.slice(0, next ? next.index : undefined).trim();
|
|
29
|
+
}
|
|
30
|
+
function field(text, name) {
|
|
31
|
+
const re = new RegExp(`^${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*(.+)$`, "m");
|
|
32
|
+
return re.exec(text)?.[1]?.trim() ?? "";
|
|
33
|
+
}
|
|
34
|
+
function frontmatterField(text, name) {
|
|
35
|
+
if (!text.startsWith("---"))
|
|
36
|
+
return "";
|
|
37
|
+
const end = text.indexOf("\n---", 3);
|
|
38
|
+
if (end < 0)
|
|
39
|
+
return "";
|
|
40
|
+
return field(text.slice(0, end), name);
|
|
41
|
+
}
|
|
42
|
+
function checkboxes(text) {
|
|
43
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => /^-\s+\[[ xX]\]/.test(line));
|
|
44
|
+
}
|
|
45
|
+
function unchecked(text) {
|
|
46
|
+
return checkboxes(text).filter((line) => /^-\s+\[\s\]/.test(line));
|
|
47
|
+
}
|
|
48
|
+
function statusLines(text) {
|
|
49
|
+
return text.split(/\r?\n/).map((line) => line.trim()).filter((line) => statusRe.test(line));
|
|
50
|
+
}
|
|
51
|
+
function acceptanceLines(text) {
|
|
52
|
+
const body = section(text, "Acceptance Criteria") || section(text, "Definition Of Done");
|
|
53
|
+
return body.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.startsWith("-"));
|
|
54
|
+
}
|
|
55
|
+
function definitionAcceptanceCriteria(text) {
|
|
56
|
+
const body = section(text, "Definition Of Done");
|
|
57
|
+
const found = [];
|
|
58
|
+
let inAcceptance = false;
|
|
59
|
+
for (const raw of body.split(/\r?\n/)) {
|
|
60
|
+
const line = raw.trim();
|
|
61
|
+
if (/^-\s+\*\*Acceptance criteria:\*\*/i.test(line)) {
|
|
62
|
+
inAcceptance = true;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (inAcceptance && /^-\s+\*\*(Usefulness checks|Stop-short risks|Durable docs target|Scope|User outcome):\*\*/i.test(line))
|
|
66
|
+
break;
|
|
67
|
+
if (inAcceptance && /^-\s+\[[ xX]\]/.test(line))
|
|
68
|
+
found.push(line);
|
|
69
|
+
}
|
|
70
|
+
return found;
|
|
71
|
+
}
|
|
72
|
+
function hasEvidence(text) {
|
|
73
|
+
return /\bEvidence:\s*\S|\bevidence\b.+\S/i.test(text);
|
|
74
|
+
}
|
|
75
|
+
function hasExplicitAcceptance(text) {
|
|
76
|
+
return /explicitly accepted|accepted by user|user accepted|accepted gap/i.test(text);
|
|
77
|
+
}
|
|
78
|
+
function walk(dir) {
|
|
79
|
+
const out = [];
|
|
80
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
81
|
+
const p = path.join(dir, entry.name);
|
|
82
|
+
if (entry.isDirectory())
|
|
83
|
+
out.push(...walk(p));
|
|
84
|
+
else
|
|
85
|
+
out.push(p);
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
function artifactPaths(pathsIn) {
|
|
90
|
+
const found = new Set();
|
|
91
|
+
for (const p of pathsIn) {
|
|
92
|
+
if (fs.existsSync(p) && fs.statSync(p).isFile() && p.endsWith(".md"))
|
|
93
|
+
found.add(path.resolve(p));
|
|
94
|
+
else if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
|
|
95
|
+
for (const f of walk(p))
|
|
96
|
+
if (f.endsWith(".md"))
|
|
97
|
+
found.add(path.resolve(f));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return [...found].sort();
|
|
101
|
+
}
|
|
102
|
+
function sidecarPaths(pathsIn) {
|
|
103
|
+
const names = new Set(Object.keys(sidecarSchemas));
|
|
104
|
+
const found = new Set();
|
|
105
|
+
for (const p of pathsIn) {
|
|
106
|
+
if (fs.existsSync(p) && fs.statSync(p).isFile() && names.has(path.basename(p)))
|
|
107
|
+
found.add(path.resolve(p));
|
|
108
|
+
else if (fs.existsSync(p) && fs.statSync(p).isDirectory()) {
|
|
109
|
+
for (const f of walk(p))
|
|
110
|
+
if (names.has(path.basename(f)))
|
|
111
|
+
found.add(path.resolve(f));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return [...found].sort();
|
|
115
|
+
}
|
|
116
|
+
function schemaMatches(value, schema, rootSchema) {
|
|
117
|
+
const issues = [];
|
|
118
|
+
validateSchemaValue("<schema-match>", value, schema, "<value>", issues, rootSchema);
|
|
119
|
+
return issues.length === 0;
|
|
120
|
+
}
|
|
121
|
+
function validateSchemaCondition(value, schema, rootSchema) {
|
|
122
|
+
const issues = [];
|
|
123
|
+
validateSchemaValue("<schema-condition>", value, schema, "<value>", issues, rootSchema);
|
|
124
|
+
return issues.length === 0;
|
|
125
|
+
}
|
|
126
|
+
function resolveSchemaRef(ref, rootSchema) {
|
|
127
|
+
if (!ref.startsWith("#/$defs/"))
|
|
128
|
+
return undefined;
|
|
129
|
+
return rootSchema?.$defs?.[ref.slice("#/$defs/".length)];
|
|
130
|
+
}
|
|
131
|
+
function validateSchemaValue(file, value, schema, loc, issues, rootSchema = schema) {
|
|
132
|
+
if (schema.anyOf) {
|
|
133
|
+
if (!schema.anyOf.some((sub) => schemaMatches(value, sub, rootSchema))) {
|
|
134
|
+
issues.push({ path: file, message: `${loc} must match at least one allowed schema` });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (schema.oneOf) {
|
|
138
|
+
const matches = schema.oneOf.filter((sub) => schemaMatches(value, sub, rootSchema)).length;
|
|
139
|
+
if (matches !== 1)
|
|
140
|
+
issues.push({ path: file, message: `${loc} must match exactly one allowed schema` });
|
|
141
|
+
}
|
|
142
|
+
if (schema.$ref) {
|
|
143
|
+
const resolved = resolveSchemaRef(schema.$ref, rootSchema);
|
|
144
|
+
if (!resolved)
|
|
145
|
+
issues.push({ path: file, message: `${loc} has unsupported schema ref ${schema.$ref}` });
|
|
146
|
+
else
|
|
147
|
+
validateSchemaValue(file, value, resolved, loc, issues, rootSchema);
|
|
148
|
+
}
|
|
149
|
+
for (const sub of schema.allOf ?? [])
|
|
150
|
+
validateSchemaValue(file, value, sub, loc, issues, rootSchema);
|
|
151
|
+
if (schema.if && schema.then && validateSchemaCondition(value, schema.if, rootSchema))
|
|
152
|
+
validateSchemaValue(file, value, schema.then, loc, issues, rootSchema);
|
|
153
|
+
if (schema.const !== undefined && value !== schema.const)
|
|
154
|
+
issues.push({ path: file, message: `${loc} must be ${schema.const}` });
|
|
155
|
+
if (schema.enum && !schema.enum.includes(value))
|
|
156
|
+
issues.push({ path: file, message: `${loc} must be one of: ${schema.enum.join(", ")}` });
|
|
157
|
+
const t = schema.type;
|
|
158
|
+
if (!t && (schema.required || schema.properties)) {
|
|
159
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
160
|
+
issues.push({ path: file, message: `${loc} must be object` });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
const obj = value;
|
|
164
|
+
for (const key of schema.required ?? [])
|
|
165
|
+
if (!(key in obj))
|
|
166
|
+
issues.push({ path: file, message: `${loc}.${key} is required` });
|
|
167
|
+
for (const [key, sub] of Object.entries(schema.properties ?? {}))
|
|
168
|
+
if (key in obj)
|
|
169
|
+
validateSchemaValue(file, obj[key], sub, `${loc}.${key}`, issues, rootSchema);
|
|
170
|
+
}
|
|
171
|
+
if (t === "string") {
|
|
172
|
+
if (typeof value !== "string") {
|
|
173
|
+
issues.push({ path: file, message: `${loc} must be string` });
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (typeof schema.minLength === "number" && value.length < schema.minLength)
|
|
177
|
+
issues.push({ path: file, message: `${loc} must not be empty` });
|
|
178
|
+
if (schema.format === "date-time") {
|
|
179
|
+
const d = Date.parse(value);
|
|
180
|
+
if (!dateTimeRe.test(value) || Number.isNaN(d))
|
|
181
|
+
issues.push({ path: file, message: `${loc} must be date-time` });
|
|
182
|
+
}
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (t === "boolean" && typeof value !== "boolean") {
|
|
186
|
+
issues.push({ path: file, message: `${loc} must be boolean` });
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (t === "integer") {
|
|
190
|
+
if (!Number.isInteger(value)) {
|
|
191
|
+
issues.push({ path: file, message: `${loc} must be integer` });
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (typeof schema.minimum === "number" && value < schema.minimum)
|
|
195
|
+
issues.push({ path: file, message: `${loc} must be at least ${schema.minimum}` });
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (t === "number" && typeof value !== "number") {
|
|
199
|
+
issues.push({ path: file, message: `${loc} must be number` });
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (t === "array") {
|
|
203
|
+
if (!Array.isArray(value)) {
|
|
204
|
+
issues.push({ path: file, message: `${loc} must be array` });
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (typeof schema.minItems === "number" && value.length < schema.minItems)
|
|
208
|
+
issues.push({ path: file, message: `${loc} must contain at least ${schema.minItems} item(s)` });
|
|
209
|
+
if (schema.uniqueItems && new Set(value.map((v) => JSON.stringify(v))).size !== value.length)
|
|
210
|
+
issues.push({ path: file, message: `${loc} must contain unique items` });
|
|
211
|
+
if (schema.items)
|
|
212
|
+
value.forEach((item, i) => validateSchemaValue(file, item, schema.items, `${loc}[${i}]`, issues, rootSchema));
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
if (t === "object") {
|
|
216
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
217
|
+
issues.push({ path: file, message: `${loc} must be object` });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const obj = value;
|
|
221
|
+
for (const key of schema.required ?? [])
|
|
222
|
+
if (!(key in obj))
|
|
223
|
+
issues.push({ path: file, message: `${loc}.${key} is required` });
|
|
224
|
+
const props = schema.properties ?? {};
|
|
225
|
+
if (schema.additionalProperties === false) {
|
|
226
|
+
for (const key of Object.keys(obj).filter((k) => !(k in props)).sort())
|
|
227
|
+
issues.push({ path: file, message: `${loc}.${key} is not allowed` });
|
|
228
|
+
}
|
|
229
|
+
for (const [key, sub] of Object.entries(props))
|
|
230
|
+
if (key in obj)
|
|
231
|
+
validateSchemaValue(file, obj[key], sub, `${loc}.${key}`, issues, rootSchema);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function artifactKind(file, text) {
|
|
235
|
+
const base = path.basename(file);
|
|
236
|
+
return (frontmatterField(text, "role") || field(text, "type") || (base.endsWith("-plan.md") ? "plan" : base.endsWith("-review.md") ? "review" : base.includes("deliver") ? "deliver" : "artifact")).toLowerCase();
|
|
237
|
+
}
|
|
238
|
+
function requireSection(file, text, heading, issues) {
|
|
239
|
+
const body = section(text, heading);
|
|
240
|
+
if (!body)
|
|
241
|
+
issues.push({ path: file, message: `missing section: ## ${heading}` });
|
|
242
|
+
return body;
|
|
243
|
+
}
|
|
244
|
+
function validateDefinitionOfDone(file, text, issues) {
|
|
245
|
+
const dod = requireSection(file, text, "Definition Of Done", issues);
|
|
246
|
+
if (!dod)
|
|
247
|
+
return;
|
|
248
|
+
const modes = new Set(["local-read-only", "local-edit", "worktree", "container", "cloud-sandbox", "privileged-integration"]);
|
|
249
|
+
for (const [label, pattern] of [
|
|
250
|
+
["User outcome", /User outcome/i],
|
|
251
|
+
["Acceptance criteria", /Acceptance criteria/i],
|
|
252
|
+
["Evidence", /Evidence:|\bevidence\b/i],
|
|
253
|
+
["Stop-short risks", /Stop-short risks/i],
|
|
254
|
+
["Durable docs target", /Durable docs target/i],
|
|
255
|
+
["Sandbox mode", /Sandbox mode:\*\*/i],
|
|
256
|
+
])
|
|
257
|
+
if (!pattern.test(dod))
|
|
258
|
+
issues.push({ path: file, message: `Definition Of Done missing ${label}` });
|
|
259
|
+
const match = /Sandbox mode:\*\*\s*`?([a-z-]+)`?/i.exec(dod);
|
|
260
|
+
if (match && !modes.has(match[1].toLowerCase()))
|
|
261
|
+
issues.push({ path: file, message: `Definition Of Done has invalid Sandbox mode; expected one of: ${[...modes].sort().join(", ")}` });
|
|
262
|
+
}
|
|
263
|
+
function validateArtifact(file) {
|
|
264
|
+
const issues = [];
|
|
265
|
+
const text = readText(file);
|
|
266
|
+
const kind = artifactKind(file, text);
|
|
267
|
+
if (kind === "plan") {
|
|
268
|
+
requireSection(file, text, "Plan", issues);
|
|
269
|
+
validateDefinitionOfDone(file, text, issues);
|
|
270
|
+
if (!/^###\s+Wave\s+\d+/m.test(text))
|
|
271
|
+
issues.push({ path: file, message: "plan artifact missing implementation waves" });
|
|
272
|
+
}
|
|
273
|
+
else if (kind === "review") {
|
|
274
|
+
requireSection(file, text, "Verification Report", issues);
|
|
275
|
+
const verdict = frontmatterField(text, "verdict") || /###\s+Verdict:\s*(\w+)/i.exec(text)?.[1] || "";
|
|
276
|
+
if (!verdict)
|
|
277
|
+
issues.push({ path: file, message: "review artifact missing verdict" });
|
|
278
|
+
if (/PASS/i.test(verdict) && statusLines(text).some((line) => /\[NOT_VERIFIED\]/i.test(line)) && !hasExplicitAcceptance(text))
|
|
279
|
+
issues.push({ path: file, message: "PASS review contains NOT_VERIFIED without explicit acceptance or routing" });
|
|
280
|
+
if (/PASS/i.test(verdict) && !hasEvidence(text))
|
|
281
|
+
issues.push({ path: file, message: "PASS review missing evidence" });
|
|
282
|
+
}
|
|
283
|
+
else if (kind === "deliver") {
|
|
284
|
+
requireSection(file, text, "Plan", issues);
|
|
285
|
+
validateDefinitionOfDone(file, text, issues);
|
|
286
|
+
requireSection(file, text, "Verification Report", issues);
|
|
287
|
+
requireSection(file, text, "Goal Fit Gate", issues);
|
|
288
|
+
requireSection(file, text, "Final Acceptance", issues);
|
|
289
|
+
const delivered = /status:\s*(delivered|accepted|archived)/i.test(text);
|
|
290
|
+
if (delivered && unchecked(section(text, "Goal Fit Gate")).length)
|
|
291
|
+
issues.push({ path: file, message: "Goal Fit Gate has unchecked items" });
|
|
292
|
+
if (delivered && unchecked(section(text, "Final Acceptance")).length)
|
|
293
|
+
issues.push({ path: file, message: "Final Acceptance has unchecked items" });
|
|
294
|
+
const verification = section(text, "Verification Report");
|
|
295
|
+
if (/Verdict:\s*PASS/i.test(verification) && !acceptanceLines(verification).some((line) => /\[PASS\]/i.test(line) && hasEvidence(line)))
|
|
296
|
+
issues.push({ path: file, message: "green build is not enough; acceptance evidence is missing" });
|
|
297
|
+
if (delivered && /NOT_VERIFIED/i.test(text) && !hasExplicitAcceptance(text))
|
|
298
|
+
issues.push({ path: file, message: "NOT_VERIFIED requires explicit acceptance or routing" });
|
|
299
|
+
if (statusLines(verification).length && !hasEvidence(verification))
|
|
300
|
+
issues.push({ path: file, message: "verification statuses require evidence" });
|
|
301
|
+
}
|
|
302
|
+
return issues;
|
|
303
|
+
}
|
|
304
|
+
function readJson(file) {
|
|
305
|
+
try {
|
|
306
|
+
return { value: JSON.parse(readText(file)), issues: [] };
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
return { value: undefined, issues: [{ path: file, message: `invalid JSON: ${error.message}` }] };
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function validateSidecar(file) {
|
|
313
|
+
const { value, issues } = readJson(file);
|
|
314
|
+
if (value === undefined)
|
|
315
|
+
return issues;
|
|
316
|
+
const schemaFile = sidecarSchemas[path.basename(file)];
|
|
317
|
+
if (schemaFile) {
|
|
318
|
+
const schema = JSON.parse(readText(path.join(root, schemaFile)));
|
|
319
|
+
validateSchemaValue(file, value, schema, path.basename(file), issues, schema);
|
|
320
|
+
}
|
|
321
|
+
if (path.basename(file) === "evidence.json") {
|
|
322
|
+
const checks = Array.isArray(value.checks) ? value.checks : [];
|
|
323
|
+
if (value.verdict === "pass" && checks.some((c) => c.status !== "pass" && c.status !== "skip"))
|
|
324
|
+
issues.push({ path: file, message: "pass verdict requires all non-skipped checks to pass" });
|
|
325
|
+
if (value.verdict === "pass" && checks.length === 0)
|
|
326
|
+
issues.push({ path: file, message: "checks must contain at least 1 item" });
|
|
327
|
+
}
|
|
328
|
+
if (path.basename(file) === "critique.json") {
|
|
329
|
+
const critiques = Array.isArray(value.critiques) ? value.critiques : [];
|
|
330
|
+
if (value.status === "pass" && critiques.some((c) => Array.isArray(c.findings) && c.findings.some((f) => f.status === "open")))
|
|
331
|
+
issues.push({ path: file, message: "critique pass cannot have open findings" });
|
|
332
|
+
if (value.required && value.status !== "pass")
|
|
333
|
+
issues.push({ path: file, message: "required critique must pass" });
|
|
334
|
+
}
|
|
335
|
+
if (path.basename(file) === "learning.json") {
|
|
336
|
+
const records = Array.isArray(value.records) ? value.records : [];
|
|
337
|
+
if (value.status === "learned" && records.length === 0)
|
|
338
|
+
issues.push({ path: file, message: "records must contain at least 1 item" });
|
|
339
|
+
if (value.status === "learned" && records.some((r) => Array.isArray(r.routing) && r.routing.some((x) => x.status === "open")))
|
|
340
|
+
issues.push({ path: file, message: "learning status learned cannot have open routing" });
|
|
341
|
+
validateLearningCorrections(file, value.status, records, issues);
|
|
342
|
+
}
|
|
343
|
+
if (path.basename(file) === "release.json") {
|
|
344
|
+
const gates = Array.isArray(value.gates) ? value.gates : [];
|
|
345
|
+
if (["merge", "release", "deploy"].includes(value.decision)) {
|
|
346
|
+
if (gates.some((g) => g.status !== "pass" && g.status !== "not_required"))
|
|
347
|
+
issues.push({ path: file, message: "positive release decision requires all required gates to pass" });
|
|
348
|
+
if (!gates.some((g) => g.name === value.decision && g.status === "pass"))
|
|
349
|
+
issues.push({ path: file, message: `positive release decision requires ${value.decision} gate to pass` });
|
|
350
|
+
}
|
|
351
|
+
if (value.decision === "deploy") {
|
|
352
|
+
if (value.rollback_plan?.status !== "ready")
|
|
353
|
+
issues.push({ path: file, message: "deploy decision requires rollback_plan status ready" });
|
|
354
|
+
if (value.observability_plan?.status !== "ready")
|
|
355
|
+
issues.push({ path: file, message: "deploy decision requires observability_plan status ready" });
|
|
356
|
+
if (!Array.isArray(value.post_deploy_checks) || value.post_deploy_checks.length === 0)
|
|
357
|
+
issues.push({ path: file, message: "deploy decision requires post_deploy_checks" });
|
|
358
|
+
if (Array.isArray(value.post_deploy_checks) && value.post_deploy_checks.some((c) => !["planned", "pass"].includes(c.status)))
|
|
359
|
+
issues.push({ path: file, message: "deploy decision requires post_deploy_checks to be planned or pass" });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return issues;
|
|
363
|
+
}
|
|
364
|
+
function validateLearningCorrections(file, status, records, issues) {
|
|
365
|
+
records.forEach((record, index) => {
|
|
366
|
+
const correction = record?.correction;
|
|
367
|
+
const loc = `learning.json.records[${index}].correction`;
|
|
368
|
+
if (correction === undefined) {
|
|
369
|
+
if (status === "learned")
|
|
370
|
+
issues.push({ path: file, message: `${loc}.needed is required when learning status is learned` });
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (correction?.needed === false) {
|
|
374
|
+
if (typeof correction.evidence !== "string" || correction.evidence.length === 0) {
|
|
375
|
+
issues.push({ path: file, message: `${loc}.evidence is required when correction.needed is false` });
|
|
376
|
+
}
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (correction?.needed !== true)
|
|
380
|
+
return;
|
|
381
|
+
for (const key of ["type", "recurrence_key", "intended_behavior", "observed_behavior", "gap"]) {
|
|
382
|
+
if (typeof correction[key] !== "string" || correction[key].length === 0)
|
|
383
|
+
issues.push({ path: file, message: `${loc}.${key} is required when correction.needed is true` });
|
|
384
|
+
}
|
|
385
|
+
const prevention = correction.prevention;
|
|
386
|
+
if (prevention !== undefined)
|
|
387
|
+
validateLearningPrevention(file, `${loc}.prevention`, prevention, issues);
|
|
388
|
+
const hasPrevention = prevention !== undefined;
|
|
389
|
+
const hasNoChange = typeof correction.no_change_rationale === "string" && correction.no_change_rationale.length > 0;
|
|
390
|
+
if (!hasPrevention && !hasNoChange) {
|
|
391
|
+
issues.push({ path: file, message: `${loc} requires prevention route or no_change_rationale when correction.needed is true` });
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
function validateLearningPrevention(file, loc, prevention, issues) {
|
|
396
|
+
if (!prevention || typeof prevention !== "object" || Array.isArray(prevention)) {
|
|
397
|
+
issues.push({ path: file, message: `${loc} must be an object` });
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (typeof prevention.target !== "string" || prevention.target.length === 0) {
|
|
401
|
+
issues.push({ path: file, message: `${loc}.target is required` });
|
|
402
|
+
}
|
|
403
|
+
else if (!["rule", "skill", "power", "agent", "eval", "doc", "backlog", "knowledge", "none"].includes(prevention.target)) {
|
|
404
|
+
issues.push({ path: file, message: `${loc}.target must be one of: rule, skill, power, agent, eval, doc, backlog, knowledge, none` });
|
|
405
|
+
}
|
|
406
|
+
if (typeof prevention.action !== "string" || prevention.action.length === 0)
|
|
407
|
+
issues.push({ path: file, message: `${loc}.action is required` });
|
|
408
|
+
if (typeof prevention.status !== "string" || prevention.status.length === 0) {
|
|
409
|
+
issues.push({ path: file, message: `${loc}.status is required` });
|
|
410
|
+
}
|
|
411
|
+
else if (!["open", "completed", "accepted", "deferred", "rejected"].includes(prevention.status)) {
|
|
412
|
+
issues.push({ path: file, message: `${loc}.status must be one of: open, completed, accepted, deferred, rejected` });
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
function validateSidecarGroup(inputs, markdown, requireSidecars, requireCritique) {
|
|
416
|
+
const issues = [];
|
|
417
|
+
const sidecars = sidecarPaths(inputs);
|
|
418
|
+
const byDir = new Map();
|
|
419
|
+
for (const file of sidecars) {
|
|
420
|
+
const { value } = readJson(file);
|
|
421
|
+
if (value !== undefined) {
|
|
422
|
+
const dir = path.dirname(file);
|
|
423
|
+
if (!byDir.has(dir))
|
|
424
|
+
byDir.set(dir, new Map());
|
|
425
|
+
byDir.get(dir).set(path.basename(file), value);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
for (const [dir, payloads] of byDir) {
|
|
429
|
+
const slugs = new Set([...payloads.values()].map((p) => p.task_slug).filter(Boolean));
|
|
430
|
+
if (slugs.size > 1)
|
|
431
|
+
issues.push({ path: dir, message: "sidecar task_slug mismatch" });
|
|
432
|
+
const evidence = payloads.get("evidence.json");
|
|
433
|
+
if (evidence?.verdict === "pass" && Array.isArray(evidence.checks) && evidence.checks.some((c) => c.status !== "pass" && c.status !== "skip"))
|
|
434
|
+
issues.push({ path: path.join(dir, "evidence.json"), message: "pass verdict requires all non-skipped checks to pass" });
|
|
435
|
+
}
|
|
436
|
+
if (requireSidecars) {
|
|
437
|
+
const dirs = new Set(markdown.map((p) => path.dirname(p)));
|
|
438
|
+
for (const dir of dirs) {
|
|
439
|
+
const deliver = markdown.find((p) => path.dirname(p) === dir && p.includes("deliver") && !p.includes("plan") && !p.includes("review"));
|
|
440
|
+
const delivered = deliver ? /status:\s*(delivered|accepted|archived)/i.test(readText(deliver)) : true;
|
|
441
|
+
for (const name of ["state.json", "acceptance.json", ...(delivered ? ["evidence.json"] : []), "handoff.json"]) {
|
|
442
|
+
if (!fs.existsSync(path.join(dir, name)))
|
|
443
|
+
issues.push({ path: path.join(dir, name), message: "required sidecar is missing" });
|
|
444
|
+
}
|
|
445
|
+
if (requireCritique && !fs.existsSync(path.join(dir, "critique.json")))
|
|
446
|
+
issues.push({ path: path.join(dir, "critique.json"), message: "required sidecar is missing" });
|
|
447
|
+
const acceptance = path.join(dir, "acceptance.json");
|
|
448
|
+
if (deliver && fs.existsSync(acceptance)) {
|
|
449
|
+
const expected = definitionAcceptanceCriteria(readText(deliver)).length;
|
|
450
|
+
const payload = JSON.parse(readText(acceptance));
|
|
451
|
+
const actual = Array.isArray(payload.criteria) ? payload.criteria.length : 0;
|
|
452
|
+
if (expected && actual !== expected)
|
|
453
|
+
issues.push({ path: acceptance, message: `acceptance.json has ${actual} criteria but Markdown defines ${expected}` });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return issues;
|
|
458
|
+
}
|
|
459
|
+
function main() {
|
|
460
|
+
const args = process.argv.slice(2);
|
|
461
|
+
const requireSidecars = args.includes("--require-sidecars");
|
|
462
|
+
const requireCritique = args.includes("--require-critique");
|
|
463
|
+
const skipMarkdown = args.includes("--skip-markdown-validation");
|
|
464
|
+
const pathsIn = args.filter((a) => !a.startsWith("--"));
|
|
465
|
+
if (!pathsIn.length) {
|
|
466
|
+
console.error("usage: validate-workflow-artifacts [--require-sidecars] [--require-critique] [--skip-markdown-validation] paths...");
|
|
467
|
+
return 2;
|
|
468
|
+
}
|
|
469
|
+
const markdown = artifactPaths(pathsIn);
|
|
470
|
+
const sidecars = sidecarPaths(pathsIn);
|
|
471
|
+
const issues = [];
|
|
472
|
+
if (!skipMarkdown)
|
|
473
|
+
for (const file of markdown)
|
|
474
|
+
issues.push(...validateArtifact(file));
|
|
475
|
+
for (const file of sidecars)
|
|
476
|
+
issues.push(...validateSidecar(file));
|
|
477
|
+
issues.push(...validateSidecarGroup(pathsIn, markdown, requireSidecars, requireCritique));
|
|
478
|
+
if (issues.length) {
|
|
479
|
+
for (const issue of issues)
|
|
480
|
+
console.error(`${issue.path}: ${issue.message}`);
|
|
481
|
+
return 1;
|
|
482
|
+
}
|
|
483
|
+
console.log(`Validated ${markdown.length} artifact(s) and ${sidecars.length} sidecar(s).`);
|
|
484
|
+
return 0;
|
|
485
|
+
}
|
|
486
|
+
process.exit(main());
|