@intentius/chant 0.124.27 → 0.124.29
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/README.md +1 -1
- package/dist/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/dist/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/dist/__golden__/wave-loop/fan-out-only.json +6 -0
- package/dist/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/dist/__golden__/wave-loop/op-cost-member.json +3 -0
- package/dist/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/dist/__golden__/wave-loop/op-held-on.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/dist/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/dist/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/dist/__golden__/wave-loop/op-per-run.json +7 -0
- package/dist/__golden__/wave-loop/op-pr-review.json +7 -0
- package/dist/__golden__/wave-loop/op-sealed.json +3 -0
- package/dist/__golden__/wave-loop/op-spend.json +3 -0
- package/dist/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/dist/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/dist/build-params.d.ts +34 -1
- package/dist/build-params.d.ts.map +1 -1
- package/dist/build-params.js +31 -1
- package/dist/change-set-graph.d.ts +63 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +130 -0
- package/dist/change-set.d.ts +62 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +36 -4
- package/dist/chant-version.d.ts +9 -0
- package/dist/chant-version.d.ts.map +1 -0
- package/dist/chant-version.js +16 -0
- package/dist/cli/build-params-cli.d.ts +7 -1
- package/dist/cli/build-params-cli.d.ts.map +1 -1
- package/dist/cli/build-params-cli.js +17 -8
- package/dist/cli/commands/build.d.ts +8 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +15 -11
- package/dist/cli/commands/check-lexicon-docs.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.js +30 -2
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.js +13 -2
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +4 -1
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/docs.js +6 -6
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.js +17 -8
- package/dist/cli/handlers/approve.d.ts.map +1 -1
- package/dist/cli/handlers/approve.js +64 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +43 -4
- package/dist/cli/handlers/change-set.d.ts +10 -0
- package/dist/cli/handlers/change-set.d.ts.map +1 -1
- package/dist/cli/handlers/change-set.js +51 -1
- package/dist/cli/handlers/fan-out.d.ts +10 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +54 -6
- package/dist/cli/handlers/lifecycle-gc.d.ts +19 -0
- package/dist/cli/handlers/lifecycle-gc.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle-gc.js +143 -0
- package/dist/cli/handlers/pr.d.ts +16 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +269 -37
- package/dist/cli/handlers/promote.d.ts +7 -0
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +440 -28
- package/dist/cli/handlers/release.d.ts +91 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +589 -0
- package/dist/cli/handlers/run.d.ts +38 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +234 -16
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +143 -17
- package/dist/cli/mcp/workspace-tools.js +1 -1
- package/dist/cli/parse-args.d.ts.map +1 -1
- package/dist/cli/parse-args.js +73 -5
- package/dist/cli/registry.d.ts +43 -5
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +30 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +49 -0
- package/dist/cli/version.d.ts +3 -5
- package/dist/cli/version.d.ts.map +1 -1
- package/dist/cli/version.js +3 -13
- package/dist/codegen/docs-in-site.d.ts +71 -0
- package/dist/codegen/docs-in-site.d.ts.map +1 -0
- package/dist/codegen/docs-in-site.js +177 -0
- package/dist/codegen/docs-types.d.ts +14 -1
- package/dist/codegen/docs-types.d.ts.map +1 -1
- package/dist/codegen/docs.d.ts +18 -1
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/docs.js +40 -7
- package/dist/codegen/rehype-base-url.mjs +15 -9
- package/dist/components/auto-release.d.ts +6 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +3 -0
- package/dist/components/capability.d.ts +9 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/cli-support.d.ts +6 -0
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/cli-support.js +10 -9
- package/dist/components/driver.d.ts +50 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +116 -54
- package/dist/components/fan-out-inputs.d.ts +69 -0
- package/dist/components/fan-out-inputs.d.ts.map +1 -0
- package/dist/components/fan-out-inputs.js +169 -0
- package/dist/components/fan-out-record.d.ts +13 -0
- package/dist/components/fan-out-record.d.ts.map +1 -1
- package/dist/components/fan-out-record.js +16 -0
- package/dist/components/fan-out-run.d.ts +7 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-run.js +85 -26
- package/dist/components/fan-out.d.ts +4 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/pr-pipeline.d.ts +25 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -1
- package/dist/components/pr-pipeline.js +22 -4
- package/dist/components/pr-run.d.ts +11 -0
- package/dist/components/pr-run.d.ts.map +1 -1
- package/dist/components/pr-run.js +20 -1
- package/dist/components/promote-order.d.ts +38 -0
- package/dist/components/promote-order.d.ts.map +1 -0
- package/dist/components/promote-order.js +62 -0
- package/dist/components/promote.d.ts +38 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +77 -2
- package/dist/components/release-compile.d.ts +142 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +382 -0
- package/dist/components/release-gate.d.ts +135 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +288 -0
- package/dist/components/release-promote.d.ts +96 -0
- package/dist/components/release-promote.d.ts.map +1 -0
- package/dist/components/release-promote.js +204 -0
- package/dist/components/run-progress.d.ts +7 -0
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/wave-plan.d.ts +8 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +11 -3
- package/dist/config.d.ts +45 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/flag-params.d.ts +54 -0
- package/dist/flag-params.d.ts.map +1 -0
- package/dist/flag-params.js +130 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts +120 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.d.ts.map +1 -0
- package/dist/lifecycle/__fixtures__/publish-fakes.js +304 -0
- package/dist/lifecycle/change-set-store.d.ts +68 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +97 -0
- package/dist/lifecycle/commit-sha.d.ts +13 -0
- package/dist/lifecycle/commit-sha.d.ts.map +1 -0
- package/dist/lifecycle/commit-sha.js +21 -0
- package/dist/lifecycle/delivery-metrics.d.ts +139 -0
- package/dist/lifecycle/delivery-metrics.d.ts.map +1 -0
- package/dist/lifecycle/delivery-metrics.js +145 -0
- package/dist/lifecycle/deploy-outcome.d.ts +81 -0
- package/dist/lifecycle/deploy-outcome.d.ts.map +1 -0
- package/dist/lifecycle/deploy-outcome.js +101 -0
- package/dist/lifecycle/gate-change-set.d.ts +78 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +138 -0
- package/dist/lifecycle/gate-ledger.d.ts +51 -4
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/git.d.ts +30 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/git.js +31 -5
- package/dist/lifecycle/index.d.ts +4 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/index.js +4 -0
- package/dist/lifecycle/mixed-environment.d.ts +95 -0
- package/dist/lifecycle/mixed-environment.d.ts.map +1 -0
- package/dist/lifecycle/mixed-environment.js +154 -0
- package/dist/lifecycle/publish-attachments.d.ts +79 -0
- package/dist/lifecycle/publish-attachments.d.ts.map +1 -0
- package/dist/lifecycle/publish-attachments.js +179 -0
- package/dist/lifecycle/publish-credentials.d.ts +37 -0
- package/dist/lifecycle/publish-credentials.d.ts.map +1 -0
- package/dist/lifecycle/publish-credentials.js +69 -0
- package/dist/lifecycle/publish-declaration.d.ts +97 -0
- package/dist/lifecycle/publish-declaration.d.ts.map +1 -0
- package/dist/lifecycle/publish-declaration.js +83 -0
- package/dist/lifecycle/publish-forge.d.ts +53 -0
- package/dist/lifecycle/publish-forge.d.ts.map +1 -0
- package/dist/lifecycle/publish-forge.js +298 -0
- package/dist/lifecycle/publish-npm.d.ts +61 -0
- package/dist/lifecycle/publish-npm.d.ts.map +1 -0
- package/dist/lifecycle/publish-npm.js +242 -0
- package/dist/lifecycle/publish-oci.d.ts +64 -0
- package/dist/lifecycle/publish-oci.d.ts.map +1 -0
- package/dist/lifecycle/publish-oci.js +383 -0
- package/dist/lifecycle/publish-record.d.ts +66 -0
- package/dist/lifecycle/publish-record.d.ts.map +1 -0
- package/dist/lifecycle/publish-record.js +84 -0
- package/dist/lifecycle/publish-targets.d.ts +45 -0
- package/dist/lifecycle/publish-targets.d.ts.map +1 -0
- package/dist/lifecycle/publish-targets.js +85 -0
- package/dist/lifecycle/release-cut.d.ts +154 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +366 -0
- package/dist/lifecycle/release-join.d.ts +95 -0
- package/dist/lifecycle/release-join.d.ts.map +1 -0
- package/dist/lifecycle/release-join.js +139 -0
- package/dist/lifecycle/release-ledger.d.ts +54 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.js +53 -20
- package/dist/lifecycle/release-publish.d.ts +312 -0
- package/dist/lifecycle/release-publish.d.ts.map +1 -0
- package/dist/lifecycle/release-publish.js +335 -0
- package/dist/lifecycle/release-revocation.d.ts +80 -0
- package/dist/lifecycle/release-revocation.d.ts.map +1 -0
- package/dist/lifecycle/release-revocation.js +123 -0
- package/dist/lifecycle/release-store.d.ts +200 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +320 -0
- package/dist/lifecycle/release-verify.d.ts +61 -0
- package/dist/lifecycle/release-verify.d.ts.map +1 -0
- package/dist/lifecycle/release-verify.js +92 -0
- package/dist/lifecycle/release-workstation.d.ts +91 -0
- package/dist/lifecycle/release-workstation.d.ts.map +1 -0
- package/dist/lifecycle/release-workstation.js +180 -0
- package/dist/lifecycle/retention-policy.d.ts +45 -0
- package/dist/lifecycle/retention-policy.d.ts.map +1 -0
- package/dist/lifecycle/retention-policy.js +43 -0
- package/dist/lifecycle/retention.d.ts +185 -0
- package/dist/lifecycle/retention.d.ts.map +1 -0
- package/dist/lifecycle/retention.js +400 -0
- package/dist/lifecycle/run-ledger.d.ts +4 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/run-ledger.js +2 -0
- package/dist/op/activities/change-coverage.d.ts.map +1 -1
- package/dist/op/activities/change-coverage.js +4 -2
- package/dist/op/activities/source-release.d.ts.map +1 -1
- package/dist/op/activities/source-release.js +7 -3
- package/dist/op/activities/source-rollback.d.ts.map +1 -1
- package/dist/op/activities/source-rollback.js +7 -3
- package/dist/op/gate-name.d.ts +11 -2
- package/dist/op/gate-name.d.ts.map +1 -1
- package/dist/op/gate-name.js +12 -3
- package/dist/op/gate-run-locator.d.ts +22 -0
- package/dist/op/gate-run-locator.d.ts.map +1 -1
- package/dist/op/gate-run-locator.js +31 -0
- package/dist/op/gate.d.ts +27 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/gate.js +57 -3
- package/dist/op/local-executor.d.ts +36 -1
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-executor.js +158 -52
- package/dist/op/op-waves-run.d.ts.map +1 -1
- package/dist/op/op-waves-run.js +4 -0
- package/dist/op/run-live.d.ts +2 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/run-live.js +1 -1
- package/dist/op/runtime.d.ts +46 -0
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/runtimes/local.js +8 -0
- package/dist/plan-summary.d.ts.map +1 -1
- package/dist/plan-summary.js +8 -2
- package/dist/point-decision.d.ts +2 -0
- package/dist/point-decision.d.ts.map +1 -1
- package/dist/point-decision.js +9 -2
- package/dist/pr-loop.d.ts +7 -0
- package/dist/pr-loop.d.ts.map +1 -1
- package/dist/pr-loop.js +23 -4
- package/dist/pr-report-url.d.ts +82 -0
- package/dist/pr-report-url.d.ts.map +1 -0
- package/dist/pr-report-url.js +174 -0
- package/dist/provenance.d.ts +2 -2
- package/dist/provenance.d.ts.map +1 -1
- package/dist/run-event-store.d.ts +104 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +251 -0
- package/dist/run-event-stream.d.ts +133 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +242 -0
- package/dist/run-event.d.ts +285 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +209 -0
- package/dist/wave-runtime.mjs +262 -226
- package/dist/workspace/__fixtures__/flag-workspace.d.ts +31 -0
- package/dist/workspace/__fixtures__/flag-workspace.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/flag-workspace.js +114 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts +19 -0
- package/dist/workspace/__fixtures__/run-event-check.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/run-event-check.js +59 -0
- package/dist/workspace/change-set-show.d.ts +56 -0
- package/dist/workspace/change-set-show.d.ts.map +1 -0
- package/dist/workspace/change-set-show.js +54 -0
- package/dist/workspace/change-set-show.schema.json +102 -0
- package/dist/workspace/change-set.schema.json +32 -1
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes-cli.js +10 -1
- package/dist/workspace/changes.d.ts +37 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/changes.js +49 -2
- package/dist/workspace/changes.schema.json +135 -0
- package/dist/workspace/check.schema.json +4 -2
- package/dist/workspace/checks/records.d.ts +9 -1
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks/records.js +35 -1
- package/dist/workspace/checks.d.ts +1 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/checks.js +2 -1
- package/dist/workspace/ci-green-cli.d.ts +19 -4
- package/dist/workspace/ci-green-cli.d.ts.map +1 -1
- package/dist/workspace/ci-green-cli.js +75 -12
- package/dist/workspace/ci-green-forge.d.ts +20 -3
- package/dist/workspace/ci-green-forge.d.ts.map +1 -1
- package/dist/workspace/ci-green-forge.js +6 -4
- package/dist/workspace/ci-green-workflow.d.ts +11 -0
- package/dist/workspace/ci-green-workflow.d.ts.map +1 -1
- package/dist/workspace/ci-green-workflow.js +6 -0
- package/dist/workspace/ci-green.d.ts +281 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +456 -38
- package/dist/workspace/ci-last-green.schema.json +32 -2
- package/dist/workspace/ci-list.schema.json +525 -0
- package/dist/workspace/commit-links.d.ts +85 -0
- package/dist/workspace/commit-links.d.ts.map +1 -0
- package/dist/workspace/commit-links.js +163 -0
- package/dist/workspace/declaration.d.ts +35 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +27 -1
- package/dist/workspace/declaration.schema.json +76 -0
- package/dist/workspace/declared-kinds.d.ts +16 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/declared-kinds.js +25 -0
- package/dist/workspace/delivery-metrics.d.ts +91 -0
- package/dist/workspace/delivery-metrics.d.ts.map +1 -0
- package/dist/workspace/delivery-metrics.js +217 -0
- package/dist/workspace/delivery-metrics.schema.json +519 -0
- package/dist/workspace/flags-read.d.ts +37 -0
- package/dist/workspace/flags-read.d.ts.map +1 -0
- package/dist/workspace/flags-read.js +103 -0
- package/dist/workspace/flags.d.ts +100 -0
- package/dist/workspace/flags.d.ts.map +1 -0
- package/dist/workspace/flags.js +98 -0
- package/dist/workspace/graph-cli.js +1 -1
- package/dist/workspace/identity-gate-rule.d.ts +37 -0
- package/dist/workspace/identity-gate-rule.d.ts.map +1 -1
- package/dist/workspace/identity-gate-rule.js +72 -7
- package/dist/workspace/identity.d.ts +12 -0
- package/dist/workspace/identity.d.ts.map +1 -1
- package/dist/workspace/identity.js +13 -1
- package/dist/workspace/intent-cli.d.ts +11 -2
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-cli.js +54 -5
- package/dist/workspace/intent-trace.d.ts +219 -0
- package/dist/workspace/intent-trace.d.ts.map +1 -0
- package/dist/workspace/intent-trace.js +314 -0
- package/dist/workspace/intent.d.ts +36 -7
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +192 -22
- package/dist/workspace/intent.schema.json +702 -6
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/ls.js +2 -0
- package/dist/workspace/ls.schema.json +3 -1
- package/dist/workspace/member-pipeline.d.ts +6 -0
- package/dist/workspace/member-pipeline.d.ts.map +1 -1
- package/dist/workspace/member-pipeline.js +6 -0
- package/dist/workspace/overlap.d.ts +146 -0
- package/dist/workspace/overlap.d.ts.map +1 -0
- package/dist/workspace/overlap.js +276 -0
- package/dist/workspace/promotion-order.d.ts +41 -0
- package/dist/workspace/promotion-order.d.ts.map +1 -0
- package/dist/workspace/promotion-order.js +95 -0
- package/dist/workspace/reason-codes.d.ts +4 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +7 -1
- package/dist/workspace/records.d.ts +15 -2
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/records.js +32 -0
- package/dist/workspace/records.schema.json +21 -2
- package/dist/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/dist/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/dist/workspace/releases.d.ts +81 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +103 -0
- package/dist/workspace/releases.schema.json +513 -0
- package/dist/workspace/run-event.schema.json +295 -0
- package/dist/workspace/status-gates.d.ts +34 -1
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-gates.js +31 -2
- package/dist/workspace/status-stewards.d.ts +4 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status-stewards.js +2 -0
- package/dist/workspace/status.d.ts +117 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +218 -4
- package/dist/workspace/status.schema.json +268 -2
- package/dist/workspace/trust/ci-tag-statement.d.ts +102 -0
- package/dist/workspace/trust/ci-tag-statement.d.ts.map +1 -0
- package/dist/workspace/trust/ci-tag-statement.js +123 -0
- package/dist/workspace/trust/op-run-statement.d.ts +91 -0
- package/dist/workspace/trust/op-run-statement.d.ts.map +1 -0
- package/dist/workspace/trust/op-run-statement.js +143 -0
- package/dist/workspace/trust/rotation.d.ts +18 -2
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.js +65 -16
- package/dist/workspace/trust/seal.d.ts +11 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/seal.js +9 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/ssh-commit.js +10 -10
- package/dist/workspace/wip.d.ts.map +1 -1
- package/dist/workspace/wip.js +11 -3
- package/package.json +1 -1
- package/src/__golden__/wave-loop/fan-out-gated.json +8 -0
- package/src/__golden__/wave-loop/fan-out-no-members.json +4 -0
- package/src/__golden__/wave-loop/fan-out-only.json +6 -0
- package/src/__golden__/wave-loop/op-by-apply-hold.json +2 -0
- package/src/__golden__/wave-loop/op-cost-member.json +3 -0
- package/src/__golden__/wave-loop/op-held-on-before-apply.json +1 -0
- package/src/__golden__/wave-loop/op-held-on.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-held.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-hold-before-apply.json +2 -0
- package/src/__golden__/wave-loop/op-hooks-refused-at-base.json +3 -0
- package/src/__golden__/wave-loop/op-hooks-share.json +3 -0
- package/src/__golden__/wave-loop/op-per-run.json +7 -0
- package/src/__golden__/wave-loop/op-pr-review.json +7 -0
- package/src/__golden__/wave-loop/op-sealed.json +3 -0
- package/src/__golden__/wave-loop/op-spend.json +3 -0
- package/src/__golden__/wave-loop/op-wave-commands.json +7 -0
- package/src/__golden__/wave-loop/op-wide-waiting.json +4 -0
- package/src/build-params.test.ts +46 -0
- package/src/build-params.ts +55 -1
- package/src/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +141 -0
- package/src/change-set.ts +87 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/build-params-cli.ts +22 -9
- package/src/cli/commands/build.ts +25 -11
- package/src/cli/commands/check-lexicon-docs.test.ts +51 -0
- package/src/cli/commands/check-lexicon-docs.ts +39 -2
- package/src/cli/commands/check-lexicon.ts +14 -2
- package/src/cli/commands/init-lexicon/templates/docs.ts +7 -6
- package/src/cli/commands/init-lexicon.test.ts +25 -0
- package/src/cli/commands/init-lexicon.ts +18 -8
- package/src/cli/handlers/approve-release.test.ts +84 -0
- package/src/cli/handlers/approve-revoked.test.ts +155 -0
- package/src/cli/handlers/approve.ts +65 -2
- package/src/cli/handlers/build.test.ts +67 -0
- package/src/cli/handlers/build.ts +46 -4
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +228 -1
- package/src/cli/handlers/fan-out.ts +53 -6
- package/src/cli/handlers/intent-trace.e2e.test.ts +201 -0
- package/src/cli/handlers/lifecycle-gc.test.ts +126 -0
- package/src/cli/handlers/lifecycle-gc.ts +146 -0
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +341 -0
- package/src/cli/handlers/pr.ts +292 -47
- package/src/cli/handlers/promote.test.ts +669 -7
- package/src/cli/handlers/promote.ts +464 -29
- package/src/cli/handlers/release-implicit.e2e.test.ts +161 -0
- package/src/cli/handlers/release-join.e2e.test.ts +185 -0
- package/src/cli/handlers/release-publish-targets.e2e.test.ts +295 -0
- package/src/cli/handlers/release-verify.e2e.test.ts +117 -0
- package/src/cli/handlers/release-workstation-ci.e2e.test.ts +135 -0
- package/src/cli/handlers/release.e2e.test.ts +199 -0
- package/src/cli/handlers/release.ts +593 -0
- package/src/cli/handlers/run-auto-release.test.ts +80 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run-release.test.ts +55 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +243 -18
- package/src/cli/main.test.ts +8 -0
- package/src/cli/main.ts +143 -17
- package/src/cli/mcp/never-over-mcp.test.ts +2 -0
- package/src/cli/mcp/workspace-tools.ts +1 -1
- package/src/cli/parse-args.ts +56 -5
- package/src/cli/registry.ts +43 -5
- package/src/cli/run-events.ts +57 -0
- package/src/cli/version.ts +3 -13
- package/src/codegen/docs-in-site.test.ts +166 -0
- package/src/codegen/docs-in-site.ts +182 -0
- package/src/codegen/docs-site/AgentPrompt.astro +26 -1
- package/src/codegen/docs-types.ts +14 -1
- package/src/codegen/docs.ts +60 -7
- package/src/codegen/rehype-base-url.d.mts +3 -1
- package/src/codegen/rehype-base-url.mjs +15 -9
- package/src/codegen/rehype-base-url.test.ts +34 -0
- package/src/components/auto-release.test.ts +30 -0
- package/src/components/auto-release.ts +9 -1
- package/src/components/capability.ts +9 -0
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.test.ts +24 -0
- package/src/components/driver.ts +165 -56
- package/src/components/fan-out-inputs.test.ts +85 -0
- package/src/components/fan-out-inputs.ts +192 -0
- package/src/components/fan-out-record.test.ts +10 -0
- package/src/components/fan-out-record.ts +19 -0
- package/src/components/fan-out-run.ts +104 -35
- package/src/components/fan-out.ts +4 -0
- package/src/components/pr-pipeline.ts +35 -4
- package/src/components/pr-run.ts +30 -1
- package/src/components/promote-order.test.ts +90 -0
- package/src/components/promote-order.ts +75 -0
- package/src/components/promote.test.ts +10 -1
- package/src/components/promote.ts +94 -3
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +459 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +355 -0
- package/src/components/release-promote.test.ts +159 -0
- package/src/components/release-promote.ts +232 -0
- package/src/components/run-progress.ts +7 -0
- package/src/components/wave-plan.ts +16 -3
- package/src/config.ts +15 -1
- package/src/flag-params.test.ts +144 -0
- package/src/flag-params.ts +134 -0
- package/src/index.ts +2 -0
- package/src/lifecycle/__fixtures__/publish-fakes.ts +351 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +113 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/delivery-metrics.ts +263 -0
- package/src/lifecycle/deploy-outcome.ts +147 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +164 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +68 -3
- package/src/lifecycle/git.ts +61 -8
- package/src/lifecycle/index.ts +4 -0
- package/src/lifecycle/mixed-environment.test.ts +97 -0
- package/src/lifecycle/mixed-environment.ts +204 -0
- package/src/lifecycle/publish-attachments.ts +196 -0
- package/src/lifecycle/publish-credentials.ts +84 -0
- package/src/lifecycle/publish-declaration.ts +95 -0
- package/src/lifecycle/publish-forge.ts +325 -0
- package/src/lifecycle/publish-npm.ts +259 -0
- package/src/lifecycle/publish-oci.ts +396 -0
- package/src/lifecycle/publish-record.ts +113 -0
- package/src/lifecycle/publish-resume.test.ts +168 -0
- package/src/lifecycle/publish-targets.test.ts +494 -0
- package/src/lifecycle/publish-targets.ts +100 -0
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +483 -0
- package/src/lifecycle/release-cut.ts +503 -0
- package/src/lifecycle/release-flag.test.ts +112 -0
- package/src/lifecycle/release-join.test.ts +68 -0
- package/src/lifecycle/release-join.ts +182 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-publish.test.ts +225 -0
- package/src/lifecycle/release-publish.ts +568 -0
- package/src/lifecycle/release-revocation.ts +147 -0
- package/src/lifecycle/release-store.ts +434 -0
- package/src/lifecycle/release-verify.test.ts +110 -0
- package/src/lifecycle/release-verify.ts +120 -0
- package/src/lifecycle/release-workstation.test.ts +204 -0
- package/src/lifecycle/release-workstation.ts +216 -0
- package/src/lifecycle/retention-growth.e2e.test.ts +279 -0
- package/src/lifecycle/retention-policy.ts +63 -0
- package/src/lifecycle/retention.test.ts +363 -0
- package/src/lifecycle/retention.ts +508 -0
- package/src/lifecycle/run-ledger.test.ts +10 -0
- package/src/lifecycle/run-ledger.ts +6 -0
- package/src/op/activities/change-coverage.ts +4 -1
- package/src/op/activities/source-release.ts +8 -3
- package/src/op/activities/source-rollback.ts +8 -4
- package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
- package/src/op/gate-name.ts +13 -3
- package/src/op/gate-release.test.ts +126 -0
- package/src/op/gate-run-locator.test.ts +110 -0
- package/src/op/gate-run-locator.ts +35 -0
- package/src/op/gate.ts +88 -6
- package/src/op/local-executor.ts +207 -52
- package/src/op/op-waves-run.ts +4 -0
- package/src/op/run-events.test.ts +166 -0
- package/src/op/run-live.ts +1 -1
- package/src/op/runtime.ts +39 -0
- package/src/op/runtimes/local.test.ts +16 -0
- package/src/op/runtimes/local.ts +9 -0
- package/src/plan-summary.test.ts +20 -0
- package/src/plan-summary.ts +8 -1
- package/src/point-decision.ts +12 -2
- package/src/pr-loop.ts +25 -4
- package/src/pr-report-url.test.ts +131 -0
- package/src/pr-report-url.ts +209 -0
- package/src/provenance.ts +2 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +273 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +309 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +471 -0
- package/src/wave-loop-golden.test.ts +9 -0
- package/src/workspace/__fixtures__/flag-workspace.ts +132 -0
- package/src/workspace/__fixtures__/older-records/change-set-984534ea9.json +100 -0
- package/src/workspace/__fixtures__/older-records/older-records.json +326 -0
- package/src/workspace/__fixtures__/run-event-check.ts +58 -0
- package/src/workspace/ancestry-index.test.ts +93 -0
- package/src/workspace/change-set-show.schema.json +102 -0
- package/src/workspace/change-set-show.ts +72 -0
- package/src/workspace/change-set.schema.json +32 -1
- package/src/workspace/changes-cli.ts +10 -1
- package/src/workspace/changes.schema.json +135 -0
- package/src/workspace/changes.test.ts +1 -1
- package/src/workspace/changes.ts +84 -2
- package/src/workspace/check.schema.json +4 -2
- package/src/workspace/checks/records.ts +35 -1
- package/src/workspace/checks.ts +2 -1
- package/src/workspace/ci-green-cli.ts +75 -13
- package/src/workspace/ci-green-forge.ts +24 -4
- package/src/workspace/ci-green-runs.test.ts +242 -0
- package/src/workspace/ci-green-signed.test.ts +302 -0
- package/src/workspace/ci-green-workflow.test.ts +16 -0
- package/src/workspace/ci-green-workflow.ts +12 -0
- package/src/workspace/ci-green.test.ts +131 -2
- package/src/workspace/ci-green.ts +635 -42
- package/src/workspace/ci-last-green.schema.json +32 -2
- package/src/workspace/ci-list.schema.json +525 -0
- package/src/workspace/commit-links.test.ts +279 -0
- package/src/workspace/commit-links.ts +183 -0
- package/src/workspace/declaration.schema.json +76 -0
- package/src/workspace/declaration.test.ts +28 -0
- package/src/workspace/declaration.ts +60 -2
- package/src/workspace/declared-kinds.ts +40 -1
- package/src/workspace/delivery-metrics.schema.json +519 -0
- package/src/workspace/delivery-metrics.test.ts +236 -0
- package/src/workspace/delivery-metrics.ts +286 -0
- package/src/workspace/flags-read.ts +121 -0
- package/src/workspace/flags.test.ts +141 -0
- package/src/workspace/flags.ts +157 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent-cli.ts +46 -5
- package/src/workspace/intent-trace.test.ts +173 -0
- package/src/workspace/intent-trace.ts +484 -0
- package/src/workspace/intent.schema.json +702 -6
- package/src/workspace/intent.ts +196 -26
- package/src/workspace/ls.schema.json +3 -1
- package/src/workspace/ls.ts +6 -0
- package/src/workspace/member-pipeline.test.ts +17 -0
- package/src/workspace/member-pipeline.ts +9 -0
- package/src/workspace/older-records.test.ts +412 -0
- package/src/workspace/overlap.test.ts +165 -0
- package/src/workspace/overlap.ts +340 -0
- package/src/workspace/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +5 -1
- package/src/workspace/reason-codes.test.ts +7 -1
- package/src/workspace/reason-codes.ts +7 -1
- package/src/workspace/record-assets.test.ts +2 -1
- package/src/workspace/records-contract.e2e.test.ts +2 -1
- package/src/workspace/records.schema.json +21 -2
- package/src/workspace/records.ts +36 -2
- package/src/workspace/reference-kinds/flag/flag.kind.mjs +32 -0
- package/src/workspace/reference-kinds/flag/flag.schema.json +53 -0
- package/src/workspace/releases.schema.json +513 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +133 -0
- package/src/workspace/run-event.schema.json +295 -0
- package/src/workspace/status-contract.test.ts +81 -1
- package/src/workspace/status-gates.ts +64 -1
- package/src/workspace/status-mixed.test.ts +152 -0
- package/src/workspace/status-revoked.test.ts +153 -0
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +268 -2
- package/src/workspace/status.ts +334 -4
- package/src/workspace/trust/ci-tag-statement.ts +151 -0
- package/src/workspace/trust/op-run-statement.ts +148 -0
- package/src/workspace/trust/rotation.ts +61 -14
- package/src/workspace/trust/seal.ts +10 -0
- package/src/workspace/trust/ssh-commit.ts +10 -9
- package/src/workspace/wip.ts +11 -2
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +0 -14
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +0 -16
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +0 -8
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +0 -15
- package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +0 -10
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which work item a commit belongs to (#4029): the one judgement, and where
|
|
3
|
+
* `workspace check --changes`, `ci list` and `ci tick` report it.
|
|
4
|
+
*
|
|
5
|
+
* - c0 declares the workspace, the decision s-001 and the work items W-001
|
|
6
|
+
* and W-002, and a ci.green block.
|
|
7
|
+
* - c1 carries Chant-Record: work:W-001.
|
|
8
|
+
* - c2 carries only a Chant-Lease, whose token the lease history of W-002
|
|
9
|
+
* on chant/lifecycle names.
|
|
10
|
+
* - c3 carries nothing.
|
|
11
|
+
* - c4 names work:W-999, which no record has.
|
|
12
|
+
* - c5 names decision:s-001, a record of a kind that is not the work kind.
|
|
13
|
+
* - c6 carries a Chant-Lease no lease history names.
|
|
14
|
+
* - c7 carries a Chant-Record that is not <kind>:<id>.
|
|
15
|
+
* - c8 names decision:s-001 and work:W-002, so the decision beside the work
|
|
16
|
+
* item changes nothing.
|
|
17
|
+
* - m is a merge of a branch whose commit is not on the first-parent line.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
22
|
+
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
23
|
+
import { writeBlobToPath } from "../lifecycle/git";
|
|
24
|
+
import { cleanScratch, contract, git, REPO, repo, scratchDir, writeFiles } from "./__fixtures__/contract-repo";
|
|
25
|
+
import { checkChanges, type ChangesDocument } from "./changes";
|
|
26
|
+
import { formatChanges } from "./changes-cli";
|
|
27
|
+
import changesSchema from "./changes.schema.json";
|
|
28
|
+
import { judgeCommitLink, type WorkIndex } from "./commit-links";
|
|
29
|
+
import { ciList, ciTick, formatList, formatTick, whyWorkUnknown, type CiListDocument } from "./ci-green";
|
|
30
|
+
import type { CiForge } from "./ci-green-forge";
|
|
31
|
+
import ciListSchema from "./ci-list.schema.json";
|
|
32
|
+
import { parseFrontMatter } from "./records";
|
|
33
|
+
|
|
34
|
+
const REF = join(REPO, "reference-workspace");
|
|
35
|
+
const DECISION = (() => {
|
|
36
|
+
const fm = parseFrontMatter(readFileSync(join(REF, "decisions", "ref-001-how-the-app-is-deployed.md"), "utf-8"));
|
|
37
|
+
if (!fm.ok) throw new Error(fm.message);
|
|
38
|
+
return fm.value;
|
|
39
|
+
})();
|
|
40
|
+
|
|
41
|
+
const decision = (id: string) => `---\n${JSON.stringify({ ...DECISION, id, title: `Decision ${id}`, state: "decided", supersedes: [], evidence: [], constrains: ["path:app"] }, null, 2)}\n---\n\n# ${id}\n`;
|
|
42
|
+
const work = (id: string) => `---\n${JSON.stringify({ schema: 1, id, title: `Work ${id}`, state: "open", implements: [], needs: [], constrains: ["path:app"], evidence: [], opened_on: "2026-10-01", source: { kind: "workspace", member: "app" }, supersedes: [] }, null, 2)}\n---\n\n# ${id}\n`;
|
|
43
|
+
|
|
44
|
+
const TOKEN = "7a1e5c90-lease-token";
|
|
45
|
+
const NOW = new Date("2026-10-01T13:00:00Z");
|
|
46
|
+
const changes = contract(changesSchema);
|
|
47
|
+
const list = contract(ciListSchema);
|
|
48
|
+
type Result = Exclude<ChangesDocument, { error: unknown }>;
|
|
49
|
+
type Listed = Extract<CiListDocument, { branch: string }>;
|
|
50
|
+
|
|
51
|
+
let root: string;
|
|
52
|
+
let origin: string;
|
|
53
|
+
const sha: Record<string, string> = {};
|
|
54
|
+
let tick = 0;
|
|
55
|
+
|
|
56
|
+
function commit(message: string[]): string {
|
|
57
|
+
const date = `2026-10-01T12:00:${String(tick++).padStart(2, "0")}Z`;
|
|
58
|
+
process.env.GIT_AUTHOR_DATE = date;
|
|
59
|
+
process.env.GIT_COMMITTER_DATE = date;
|
|
60
|
+
try {
|
|
61
|
+
git(root, "add", "-A");
|
|
62
|
+
git(root, "commit", "-q", ...message.flatMap((m) => ["-m", m]));
|
|
63
|
+
} finally {
|
|
64
|
+
delete process.env.GIT_AUTHOR_DATE;
|
|
65
|
+
delete process.env.GIT_COMMITTER_DATE;
|
|
66
|
+
}
|
|
67
|
+
return git(root, "rev-parse", "HEAD");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function edit(name: string, message: string[]): string {
|
|
71
|
+
writeFiles(root, { "app/main.js": `export const n = ${tick};\n` });
|
|
72
|
+
return (sha[name] = commit(message));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
beforeAll(async () => {
|
|
76
|
+
root = repo({
|
|
77
|
+
"chant.workspace.json": JSON.stringify({
|
|
78
|
+
name: "links",
|
|
79
|
+
schema: 1,
|
|
80
|
+
members: [{ name: "app", dir: "app", kind: "other", because: "the app" }],
|
|
81
|
+
records: [{ kind: "decisions/decision.kind.mjs" }, { kind: "work/work.kind.mjs" }],
|
|
82
|
+
ci: { green: { branch: "main", phases: { lint: ["lint"] }, require: ["lint"] } },
|
|
83
|
+
}),
|
|
84
|
+
"decisions/decision.kind.mjs": readFileSync(join(REF, "decisions", "decision.kind.mjs"), "utf-8"),
|
|
85
|
+
"decisions/decision.schema.json": readFileSync(join(REF, "decisions", "decision.schema.json"), "utf-8"),
|
|
86
|
+
"decisions/s-001-app.md": decision("s-001"),
|
|
87
|
+
"work/work.kind.mjs": readFileSync(join(REF, "work", "work.kind.mjs"), "utf-8"),
|
|
88
|
+
"work/work.schema.json": readFileSync(join(REF, "work", "work.schema.json"), "utf-8"),
|
|
89
|
+
"work/W-001-one.md": work("W-001"),
|
|
90
|
+
"work/W-002-two.md": work("W-002"),
|
|
91
|
+
"app/main.js": "export const n = 0;\n",
|
|
92
|
+
});
|
|
93
|
+
git(root, "symbolic-ref", "HEAD", "refs/heads/main");
|
|
94
|
+
sha.c0 = commit(["the workspace"]);
|
|
95
|
+
edit("c1", ["one", "Chant-Record: work:W-001"]);
|
|
96
|
+
edit("c2", ["two", `Chant-Lease: ${TOKEN}`]);
|
|
97
|
+
edit("c3", ["three"]);
|
|
98
|
+
edit("c4", ["four", "Chant-Record: work:W-999"]);
|
|
99
|
+
edit("c5", ["five", "Chant-Record: decision:s-001"]);
|
|
100
|
+
edit("c6", ["six", "Chant-Lease: no-such-token"]);
|
|
101
|
+
edit("c7", ["seven", "Chant-Record: nonsense"]);
|
|
102
|
+
edit("c8", ["eight", "Chant-Record: decision:s-001\nChant-Record: work:W-002"]);
|
|
103
|
+
git(root, "checkout", "-q", "-b", "side");
|
|
104
|
+
writeFiles(root, { "app/side.js": "export const side = 1;\n" });
|
|
105
|
+
commit(["on a side branch"]);
|
|
106
|
+
git(root, "checkout", "-q", "main");
|
|
107
|
+
process.env.GIT_AUTHOR_DATE = process.env.GIT_COMMITTER_DATE = "2026-10-01T12:30:00Z";
|
|
108
|
+
try {
|
|
109
|
+
git(root, "merge", "-q", "--no-ff", "-m", "Merge side", "side");
|
|
110
|
+
} finally {
|
|
111
|
+
delete process.env.GIT_AUTHOR_DATE;
|
|
112
|
+
delete process.env.GIT_COMMITTER_DATE;
|
|
113
|
+
}
|
|
114
|
+
sha.m = git(root, "rev-parse", "HEAD");
|
|
115
|
+
const line = { version: 1, event: "claim", item: "W-002", holder: "factory", by: "factory", token: TOKEN, acquiredAt: "2026-10-01T12:00:00Z", expiresAt: "2026-10-01T12:10:00Z", timestamp: "2026-10-01T12:00:00Z" };
|
|
116
|
+
await writeBlobToPath("_leases", "W-002.jsonl", JSON.stringify(line), "Work lease claim: W-002", { cwd: root });
|
|
117
|
+
});
|
|
118
|
+
afterAll(cleanScratch);
|
|
119
|
+
|
|
120
|
+
const RANGE = () => `${sha.c0}..${sha.m}`;
|
|
121
|
+
|
|
122
|
+
async function check(severity?: "off" | "warn" | "fail"): Promise<{ doc: Result; failed: boolean }> {
|
|
123
|
+
const { doc, failed } = await checkChanges({ cwd: root, range: RANGE(), ...(severity ? { severity } : {}) });
|
|
124
|
+
changes.expectValid(doc);
|
|
125
|
+
if ("error" in doc) throw new Error(`${doc.error.code}: ${doc.error.message}`);
|
|
126
|
+
return { doc, failed };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
describe("judging one commit's trailers", () => {
|
|
130
|
+
const index: WorkIndex = { kinds: new Map([["work", new Set(["W-1", "W-2"])]]), ledger: true };
|
|
131
|
+
const none = { records: [], lease: null };
|
|
132
|
+
|
|
133
|
+
test("a Chant-Record of a work kind with that record links, whatever else the commit names", () => {
|
|
134
|
+
expect(judgeCommitLink({ "Chant-Record": ["work:W-1"] }, { records: [{ kind: "work", id: "W-1" }], lease: null }, index)).toEqual({ state: "linked", ids: ["work:W-1"], problems: [] });
|
|
135
|
+
const both = judgeCommitLink({ "chant-record": ["decision:d-1", "work:W-2", "work:W-404"] }, { records: [{ kind: "decision", id: "d-1" }, { kind: "work", id: "W-2" }, { kind: "work", id: "W-404" }], lease: null }, index);
|
|
136
|
+
expect(both).toEqual({ state: "linked", ids: ["work:W-2"], problems: [] });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("a Chant-Lease links through the work item its history names", () => {
|
|
140
|
+
expect(judgeCommitLink({ "Chant-Lease": ["t"] }, { records: [], lease: { token: "t", item: "W-2" } }, index)).toMatchObject({ state: "linked", ids: ["work:W-2"] });
|
|
141
|
+
expect(judgeCommitLink({ "Chant-Lease": ["t"] }, { records: [], lease: { token: "t", item: "W-404" } }, index)).toMatchObject({ state: "wrong" });
|
|
142
|
+
expect(judgeCommitLink({ "Chant-Lease": ["t"] }, { records: [], lease: { token: "t", item: null } }, index)).toMatchObject({ state: "wrong" });
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("a lease is not judged where the lease histories are not there to read", () => {
|
|
146
|
+
const link = judgeCommitLink({ "Chant-Lease": ["t"] }, { records: [], lease: { token: "t", item: null } }, { ...index, ledger: false });
|
|
147
|
+
expect(link).toEqual({ state: "linked", ids: [], problems: [] });
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("a commit with no trailer is unlinked, and a window match is not a trailer", () => {
|
|
151
|
+
expect(judgeCommitLink({}, none, index)).toEqual({ state: "unlinked", ids: [], problems: [] });
|
|
152
|
+
expect(judgeCommitLink({ "Chant-Agent": ["factory"], "Chant-Run": ["r-1"] }, none, index).state).toBe("unlinked");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("each wrongly linked case says which trailer and why", () => {
|
|
156
|
+
const wrong = (value: string, ref: { kind: string; id: string } | null) => judgeCommitLink({ "Chant-Record": [value] }, { records: ref ? [ref] : [], lease: null }, index);
|
|
157
|
+
expect(wrong("work:W-9", { kind: "work", id: "W-9" })).toEqual({ state: "wrong", ids: [], problems: [{ trailer: "Chant-Record: work:W-9", message: "the work kind has no record W-9" }] });
|
|
158
|
+
expect(wrong("decision:d-1", { kind: "decision", id: "d-1" }).problems[0].message).toBe("decision is not a work kind (the work kinds are work)");
|
|
159
|
+
expect(wrong("nonsense", null).problems[0]).toEqual({ trailer: "Chant-Record: nonsense", message: "nonsense is not <kind>:<id>" });
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("workspace check --changes (#4029)", () => {
|
|
164
|
+
test("judges the first-parent commits of the range, merges left out", async () => {
|
|
165
|
+
const { doc, failed } = await check();
|
|
166
|
+
expect(failed).toBe(false);
|
|
167
|
+
expect(doc.links!.kinds).toEqual(["work"]);
|
|
168
|
+
expect(doc.links!.commits.map((c) => [c.commit, c.state, c.ids])).toEqual([
|
|
169
|
+
[sha.c1, "linked", ["work:W-001"]],
|
|
170
|
+
[sha.c2, "linked", ["work:W-002"]],
|
|
171
|
+
[sha.c3, "unlinked", []],
|
|
172
|
+
[sha.c4, "wrong", []],
|
|
173
|
+
[sha.c5, "wrong", []],
|
|
174
|
+
[sha.c6, "wrong", []],
|
|
175
|
+
[sha.c7, "wrong", []],
|
|
176
|
+
[sha.c8, "linked", ["work:W-002"]],
|
|
177
|
+
]);
|
|
178
|
+
expect(doc.links!.commits.find((c) => c.commit === sha.c4)!.problems).toEqual([{ trailer: "Chant-Record: work:W-999", message: "the work kind has no record W-999" }]);
|
|
179
|
+
expect(doc.links!.commits.find((c) => c.commit === sha.c5)!.problems[0].message).toContain("decision is not a work kind");
|
|
180
|
+
expect(doc.links!.commits.find((c) => c.commit === sha.c6)!.problems).toEqual([{ trailer: "Chant-Lease: no-such-token", message: "no lease history on chant/lifecycle names this token" }]);
|
|
181
|
+
expect(doc.links!.commits.find((c) => c.commit === sha.c7)!.problems[0].trailer).toBe("Chant-Record: nonsense");
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("reports an unlinked commit and each wrongly linked one with a fix line, at warn by default", async () => {
|
|
185
|
+
const { doc } = await check();
|
|
186
|
+
expect(doc.links!.findings.map((f) => [f.code, f.commit, f.severity])).toEqual([
|
|
187
|
+
["commit-unlinked", sha.c3, "warn"],
|
|
188
|
+
["commit-wrongly-linked", sha.c4, "warn"],
|
|
189
|
+
["commit-wrongly-linked", sha.c5, "warn"],
|
|
190
|
+
["commit-wrongly-linked", sha.c6, "warn"],
|
|
191
|
+
["commit-wrongly-linked", sha.c7, "warn"],
|
|
192
|
+
]);
|
|
193
|
+
expect(doc.links!.findings[0].id).toBe(`finding:commit-unlinked:${sha.c3}`);
|
|
194
|
+
expect(doc.links!.findings.every((f) => f.fix.includes("Chant-Record"))).toBe(true);
|
|
195
|
+
expect(doc.ok).toBe(true);
|
|
196
|
+
const text = formatChanges(doc);
|
|
197
|
+
expect(text).toContain("links 8 commits: 3 name a work item, 1 do not, 4 name one that is not there; work kinds: work");
|
|
198
|
+
expect(text).toContain(`warning commit-unlinked: ${sha.c3.slice(0, 8)} "three" carries no Chant-Record trailer of a work kind and no Chant-Lease trailer`);
|
|
199
|
+
expect(text).toContain(" fix: add a `Chant-Record: work:<id>` trailer");
|
|
200
|
+
expect(text).toContain(`commit-wrongly-linked: ${sha.c4.slice(0, 8)} "four" names no work item that exists: Chant-Record: work:W-999 (the work kind has no record W-999)`);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("--severity sets the level: fail fails the check, off reports no finding", async () => {
|
|
204
|
+
const fail = await check("fail");
|
|
205
|
+
expect(fail.failed).toBe(true);
|
|
206
|
+
expect(fail.doc.ok).toBe(false);
|
|
207
|
+
expect(fail.doc.links!.findings.every((f) => f.severity === "fail")).toBe(true);
|
|
208
|
+
expect(formatChanges(fail.doc)).toContain("error commit-unlinked");
|
|
209
|
+
const off = await check("off");
|
|
210
|
+
expect(off.failed).toBe(false);
|
|
211
|
+
expect(off.doc.links!.findings).toEqual([]);
|
|
212
|
+
expect(off.doc.links!.commits).toHaveLength(8);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("a range whose commits all name a work item has no finding", async () => {
|
|
216
|
+
const { doc } = await checkChanges({ cwd: root, range: `${sha.c0}..${sha.c2}` });
|
|
217
|
+
changes.expectValid(doc);
|
|
218
|
+
expect((doc as Result).links!.findings).toEqual([]);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("ci list and ci tick (#4029)", () => {
|
|
223
|
+
test("ci list --json marks each commit with its link, and merges as merge", async () => {
|
|
224
|
+
const doc = (await ciList({ cwd: root, now: NOW })) as Listed;
|
|
225
|
+
list.expectValid(doc);
|
|
226
|
+
const byCommit = Object.fromEntries(doc.commits.map((c) => [c.sha, c.work]));
|
|
227
|
+
expect(byCommit[sha.c1]).toEqual({ state: "linked", ids: ["work:W-001"], problems: [] });
|
|
228
|
+
expect(byCommit[sha.c2]).toMatchObject({ state: "linked", ids: ["work:W-002"] });
|
|
229
|
+
expect(byCommit[sha.c3]).toEqual({ state: "unlinked", ids: [], problems: [] });
|
|
230
|
+
expect(byCommit[sha.c4]).toMatchObject({ state: "wrong" });
|
|
231
|
+
expect(byCommit[sha.m]).toEqual({ state: "merge", ids: [], problems: [] });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
test("ci list text gives the reason a commit's work item is unknown", async () => {
|
|
235
|
+
const doc = (await ciList({ cwd: root, now: NOW })) as Listed;
|
|
236
|
+
const text = formatList(doc).split("\n");
|
|
237
|
+
const line = (s: string) => text.find((l) => l.includes(s.slice(0, 12)))!;
|
|
238
|
+
expect(line(sha.c3)).toContain("; why unknown: no Chant-Record or Chant-Lease trailer names a work item");
|
|
239
|
+
expect(line(sha.c4)).toContain("; why unknown: the work item named is not there: Chant-Record: work:W-999");
|
|
240
|
+
expect(line(sha.c1)).not.toContain("why unknown");
|
|
241
|
+
expect(line(sha.m)).not.toContain("why unknown");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("the field is null where the workspace declares no work kind", async () => {
|
|
245
|
+
const plain = scratchDir("chant-links-plain-");
|
|
246
|
+
git(plain, "init", "-q");
|
|
247
|
+
git(plain, "symbolic-ref", "HEAD", "refs/heads/main");
|
|
248
|
+
writeFiles(plain, {
|
|
249
|
+
"chant.workspace.json": JSON.stringify({ name: "plain", schema: 1, members: [{ name: "app", dir: "app", kind: "other", because: "x" }], ci: { green: { branch: "main", phases: { lint: ["lint"] }, require: ["lint"] } } }),
|
|
250
|
+
"app/a.txt": "a\n",
|
|
251
|
+
});
|
|
252
|
+
git(plain, "add", "-A");
|
|
253
|
+
git(plain, "-c", "user.name=t", "-c", "user.email=t@example.com", "commit", "-q", "-m", "one");
|
|
254
|
+
const doc = (await ciList({ cwd: plain })) as Listed;
|
|
255
|
+
list.expectValid(doc);
|
|
256
|
+
expect(doc.commits.map((c) => c.work)).toEqual([null]);
|
|
257
|
+
expect(formatList(doc)).not.toContain("why unknown");
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
test("ci tick shows an unlinked commit with why unknown", async () => {
|
|
261
|
+
origin = scratchDir("chant-links-origin-");
|
|
262
|
+
git(origin, "init", "-q", "--bare");
|
|
263
|
+
git(root, "remote", "add", "origin", origin);
|
|
264
|
+
git(root, "push", "-q", "origin", "main");
|
|
265
|
+
const forge: CiForge = { kind: "github", checkRuns: async () => [] };
|
|
266
|
+
const result = await ciTick({ cwd: root, forge, now: NOW, dryRun: true });
|
|
267
|
+
const text = formatTick(result, true);
|
|
268
|
+
const line = (s: string) => text.split("\n").find((l) => l.includes(s.slice(0, 12)))!;
|
|
269
|
+
expect(line(sha.c3)).toMatch(/undecided: lint; why unknown: no Chant-Record or Chant-Lease trailer names a work item/);
|
|
270
|
+
expect(line(sha.c1)).not.toContain("why unknown");
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test("whyWorkUnknown names nothing for a linked commit, a merge or no field", () => {
|
|
274
|
+
expect(whyWorkUnknown(null)).toBeUndefined();
|
|
275
|
+
expect(whyWorkUnknown(undefined)).toBeUndefined();
|
|
276
|
+
expect(whyWorkUnknown({ state: "linked", ids: ["work:W-1"], problems: [] })).toBeUndefined();
|
|
277
|
+
expect(whyWorkUnknown({ state: "merge", ids: [], problems: [] })).toBeUndefined();
|
|
278
|
+
});
|
|
279
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which work item a commit belongs to (#4029).
|
|
3
|
+
*
|
|
4
|
+
* A work item implements a decision, and a trunk commit says which work item
|
|
5
|
+
* it carries out in one of two ways:
|
|
6
|
+
*
|
|
7
|
+
* - a `Chant-Record: <kind>:<id>` trailer, where `<kind>` is a work kind the
|
|
8
|
+
* declaration names (conventionally `work`) and the kind has a record `<id>`;
|
|
9
|
+
* - a `Chant-Lease` trailer whose token a lease history on `chant/lifecycle`
|
|
10
|
+
* names a work item for, and that item is a record of a work kind.
|
|
11
|
+
*
|
|
12
|
+
* A window match (the intent graph's `worked` bucket) does not link a commit:
|
|
13
|
+
* the commit never said so itself. A commit is wrongly linked when it names a
|
|
14
|
+
* work record that does not exist, a record of a kind that is not a work
|
|
15
|
+
* kind, a lease that resolves to nothing, or a `Chant-Record` value that is
|
|
16
|
+
* not `<kind>:<id>`, and no trailer of it links to a work item.
|
|
17
|
+
*
|
|
18
|
+
* This is the one place that judgement is made. `workspace check --changes`
|
|
19
|
+
* reports it, `ci list` and `ci tick` show it, and later readers share
|
|
20
|
+
* {@link judgeCommitLink}. It reads trailers through `readTrailerJoins`,
|
|
21
|
+
* which `graph --intent` joins commits with, so both resolve a Chant-Record
|
|
22
|
+
* and a Chant-Lease the same way. Everything here reads local git.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { execFileSync } from "node:child_process";
|
|
26
|
+
import { declaredRecordKinds, readDeclaration, WorkspaceReadError } from "./declaration";
|
|
27
|
+
import { declaredKindFile } from "./declared-kinds";
|
|
28
|
+
import { trailerValues } from "./intent-joins";
|
|
29
|
+
import { commitDetails } from "./intent";
|
|
30
|
+
import { loadRecordKind, RecordReadError } from "./records";
|
|
31
|
+
import { readRecordsFor } from "./records-cli";
|
|
32
|
+
import { readTrailerJoins } from "./trailer-joins";
|
|
33
|
+
import { parseRecordRef, RECORD_TRAILER, LEASE_TRAILER } from "./trailers";
|
|
34
|
+
import { locateWorkspace } from "./which-chant";
|
|
35
|
+
|
|
36
|
+
/** `linked`, `unlinked` (no trailer names a work item), `wrong` (trailers name one that is not there), or `merge` (a merge commit carries no work of its own). */
|
|
37
|
+
export type CommitLinkState = "linked" | "unlinked" | "wrong" | "merge";
|
|
38
|
+
|
|
39
|
+
/** One trailer that does not link the commit, with why. */
|
|
40
|
+
export interface CommitLinkProblem {
|
|
41
|
+
/** The trailer as written, such as `Chant-Record: decision:ws-075`. */
|
|
42
|
+
trailer: string;
|
|
43
|
+
message: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** What {@link judgeCommitLink} says about one commit. */
|
|
47
|
+
export interface CommitLink {
|
|
48
|
+
state: CommitLinkState;
|
|
49
|
+
/** The work records the commit's trailers name, as `<kind>:<id>`. Empty unless `linked`; also empty for a lease that could not be checked. */
|
|
50
|
+
ids: string[];
|
|
51
|
+
/** The trailers that do not link. Empty unless `wrong`. */
|
|
52
|
+
problems: CommitLinkProblem[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** The work records a revision has, by work kind. */
|
|
56
|
+
export interface WorkIndex {
|
|
57
|
+
/** Each work kind's name, with the ids of its records. */
|
|
58
|
+
kinds: Map<string, Set<string>>;
|
|
59
|
+
/** Whether lease histories can be read: the local `chant/lifecycle` branch exists. Without it a lease is not judged. */
|
|
60
|
+
ledger: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The fix line for each state a check reports. */
|
|
64
|
+
export const COMMIT_UNLINKED_FIX = "add a `Chant-Record: work:<id>` trailer when the commit is made (git commit --trailer \"Chant-Record: work:<id>\"), naming the work item the commit carries out";
|
|
65
|
+
|
|
66
|
+
function workKindList(index: WorkIndex): string {
|
|
67
|
+
return [...index.kinds.keys()].join(", ") || "none";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Judge one commit from its trailers.
|
|
72
|
+
*
|
|
73
|
+
* `trailers` are the commit's parsed trailers, `joins` what
|
|
74
|
+
* `readTrailerJoins` made of them, and `index` the work records of the
|
|
75
|
+
* revision judged against. A commit is `linked` when any trailer names a
|
|
76
|
+
* work record; another trailer that names a decision or a typo beside it
|
|
77
|
+
* does not make it wrong.
|
|
78
|
+
*/
|
|
79
|
+
export function judgeCommitLink(trailers: Record<string, string[]>, joins: { records: { kind: string; id: string }[]; lease: { token: string; item: string | null } | null }, index: WorkIndex): CommitLink {
|
|
80
|
+
const ids: string[] = [];
|
|
81
|
+
const problems: CommitLinkProblem[] = [];
|
|
82
|
+
let unchecked = false;
|
|
83
|
+
const add = (id: string) => {
|
|
84
|
+
if (!ids.includes(id)) ids.push(id);
|
|
85
|
+
};
|
|
86
|
+
for (const value of trailerValues(trailers, RECORD_TRAILER)) {
|
|
87
|
+
const trailer = `${RECORD_TRAILER}: ${value}`;
|
|
88
|
+
const ref = parseRecordRef(value);
|
|
89
|
+
if (!ref) {
|
|
90
|
+
problems.push({ trailer, message: `${value} is not <kind>:<id>` });
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const known = index.kinds.get(ref.kind);
|
|
94
|
+
if (!known) problems.push({ trailer, message: `${ref.kind} is not a work kind (the work kinds are ${workKindList(index)})` });
|
|
95
|
+
else if (!known.has(ref.id)) problems.push({ trailer, message: `the ${ref.kind} kind has no record ${ref.id}` });
|
|
96
|
+
else add(`${ref.kind}:${ref.id}`);
|
|
97
|
+
}
|
|
98
|
+
if (joins.lease) {
|
|
99
|
+
const trailer = `${LEASE_TRAILER}: ${joins.lease.token}`;
|
|
100
|
+
const item = joins.lease.item;
|
|
101
|
+
if (item === null) {
|
|
102
|
+
if (index.ledger) problems.push({ trailer, message: "no lease history on chant/lifecycle names this token" });
|
|
103
|
+
else unchecked = true;
|
|
104
|
+
} else {
|
|
105
|
+
const hits = [...index.kinds].filter(([, found]) => found.has(item)).map(([kind]) => kind);
|
|
106
|
+
if (hits.length === 1) add(`${hits[0]}:${item}`);
|
|
107
|
+
else problems.push({ trailer, message: hits.length === 0 ? `the lease was taken on ${item}, which no work kind has a record for` : `the lease was taken on ${item}, which ${hits.join(" and ")} both have, so it names no one work item` });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ids.length > 0 || unchecked) return { state: "linked", ids, problems: [] };
|
|
111
|
+
return problems.length > 0 ? { state: "wrong", ids: [], problems } : { state: "unlinked", ids: [], problems: [] };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Judge each commit by its own trailers. A commit in `merges` is `merge`:
|
|
116
|
+
* its work is the commits it brings. Reads the lease histories on the local
|
|
117
|
+
* `chant/lifecycle` branch once, and never fetches.
|
|
118
|
+
*/
|
|
119
|
+
export function judgeCommits(top: string, shas: string[], index: WorkIndex, merges: ReadonlySet<string> = new Set()): Map<string, CommitLink> {
|
|
120
|
+
const details = commitDetails(top, shas);
|
|
121
|
+
const joined = readTrailerJoins(
|
|
122
|
+
top,
|
|
123
|
+
shas.map((sha) => ({ sha, trailers: details.get(sha)?.trailers ?? {} })),
|
|
124
|
+
);
|
|
125
|
+
const out = new Map<string, CommitLink>();
|
|
126
|
+
for (const sha of shas) {
|
|
127
|
+
if (merges.has(sha)) {
|
|
128
|
+
out.set(sha, { state: "merge", ids: [], problems: [] });
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
out.set(sha, judgeCommitLink(details.get(sha)?.trailers ?? {}, joined.get(sha)!.joins, index));
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** Whether the lease histories can be read from this repository: the local `chant/lifecycle` branch exists. */
|
|
137
|
+
export function hasLeaseLedger(top: string): boolean {
|
|
138
|
+
try {
|
|
139
|
+
execFileSync("git", ["rev-parse", "--verify", "--quiet", "refs/heads/chant/lifecycle"], { cwd: top, stdio: "ignore" });
|
|
140
|
+
return true;
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** Which of `shas` are merge commits. */
|
|
147
|
+
export function mergeCommits(top: string, shas: string[]): Set<string> {
|
|
148
|
+
const out = new Set<string>();
|
|
149
|
+
if (shas.length === 0) return out;
|
|
150
|
+
const text = execFileSync("git", ["log", "--no-walk=unsorted", "--stdin", "--format=%H %P"], { cwd: top, encoding: "utf-8", input: `${shas.join("\n")}\n`, maxBuffer: 256 * 1024 * 1024 });
|
|
151
|
+
for (const line of text.split("\n")) {
|
|
152
|
+
const [sha, ...parents] = line.trim().split(" ");
|
|
153
|
+
if (sha && parents.length > 1) out.add(sha);
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The work records the declaration's work kinds hold at `at` (a revision, or
|
|
160
|
+
* the working tree without one). Null when `cwd` is in no workspace, the
|
|
161
|
+
* declaration names no work kind, or a kind can't be read: the question
|
|
162
|
+
* "which work item" has no answer there.
|
|
163
|
+
*/
|
|
164
|
+
export async function readWorkIndex(cwd: string, top: string, at?: string): Promise<WorkIndex | null> {
|
|
165
|
+
try {
|
|
166
|
+
const located = locateWorkspace(cwd, at);
|
|
167
|
+
const declaration = readDeclaration(located.tree);
|
|
168
|
+
const kinds = new Map<string, Set<string>>();
|
|
169
|
+
for (const d of declaredRecordKinds(declaration)) {
|
|
170
|
+
const file = declaredKindFile(d, located.rootOnDisk);
|
|
171
|
+
const loaded = await loadRecordKind(file, cwd);
|
|
172
|
+
if (!loaded.kind.work) continue;
|
|
173
|
+
const read = await readRecordsFor({ kind: file, cwd, ...(at !== undefined ? { at } : {}) });
|
|
174
|
+
const ids = (kinds.get(loaded.kind.name) ?? new Set<string>());
|
|
175
|
+
for (const r of read.result.records) if (r.id !== null) ids.add(r.id);
|
|
176
|
+
kinds.set(loaded.kind.name, ids);
|
|
177
|
+
}
|
|
178
|
+
return kinds.size === 0 ? null : { kinds, ledger: hasLeaseLedger(top) };
|
|
179
|
+
} catch (err) {
|
|
180
|
+
if (err instanceof WorkspaceReadError || err instanceof RecordReadError) return null;
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
@@ -179,6 +179,58 @@
|
|
|
179
179
|
"$ref": "#/$defs/ciGreen"
|
|
180
180
|
}
|
|
181
181
|
}
|
|
182
|
+
},
|
|
183
|
+
"retention": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"additionalProperties": false,
|
|
186
|
+
"patternProperties": {
|
|
187
|
+
"^x-": true
|
|
188
|
+
},
|
|
189
|
+
"description": "How long chant lifecycle gc keeps each kind of stored record (#4036). A kind or field left out keeps its default: changeSets 14 days; releases 14 days and the newest 10 of each member; runEvents 14 days and the newest 100. Whatever the policy, gc never prunes a release an environment runs, what a pending gate binds, or a change set a gate names with a kept release. Read from the declaration at base. Added in schema 1 by chant 0.124.29, so a declaration that uses it sets minReader to 0.124.29 or newer.",
|
|
190
|
+
"properties": {
|
|
191
|
+
"changeSets": {
|
|
192
|
+
"type": "object",
|
|
193
|
+
"additionalProperties": false,
|
|
194
|
+
"description": "Stored change sets, _change-sets/<digest>.json on chant/lifecycle.",
|
|
195
|
+
"properties": {
|
|
196
|
+
"days": { "$ref": "#/$defs/retentionDays" }
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
"releases": {
|
|
200
|
+
"type": "object",
|
|
201
|
+
"additionalProperties": false,
|
|
202
|
+
"description": "Releases, _releases/<digest>.json on chant/lifecycle, with the template bytes only they name. A release stays promotable for days after its newest cut, and the newest keep releases of each member stay promotable at any age, unless their commit is revoked.",
|
|
203
|
+
"properties": {
|
|
204
|
+
"days": { "$ref": "#/$defs/retentionDays" },
|
|
205
|
+
"keep": { "$ref": "#/$defs/retentionKeep" }
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
"runEvents": {
|
|
209
|
+
"type": "object",
|
|
210
|
+
"additionalProperties": false,
|
|
211
|
+
"description": "Run event files, <git-common-dir>/chant/run-events/<run>.jsonl on each host. A file is kept for days after its last write, and the newest keep files at any age.",
|
|
212
|
+
"properties": {
|
|
213
|
+
"days": { "$ref": "#/$defs/retentionDays" },
|
|
214
|
+
"keep": { "$ref": "#/$defs/retentionKeep" }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"metrics": {
|
|
220
|
+
"type": "object",
|
|
221
|
+
"additionalProperties": false,
|
|
222
|
+
"patternProperties": {
|
|
223
|
+
"^x-": true
|
|
224
|
+
},
|
|
225
|
+
"description": "How chant workspace metrics counts the DORA metrics (#4039). Added in schema 1 by chant 0.124.29, so a declaration that uses it sets minReader to 0.124.29 or newer.",
|
|
226
|
+
"properties": {
|
|
227
|
+
"failureWindowDays": {
|
|
228
|
+
"type": "integer",
|
|
229
|
+
"minimum": 1,
|
|
230
|
+
"maximum": 365,
|
|
231
|
+
"description": "A deploy counts as failed when, within this many days after it, the commit it deployed is revoked or the environment is rolled back from it. The default is 7."
|
|
232
|
+
}
|
|
233
|
+
}
|
|
182
234
|
}
|
|
183
235
|
},
|
|
184
236
|
"patternProperties": {
|
|
@@ -186,6 +238,18 @@
|
|
|
186
238
|
},
|
|
187
239
|
"additionalProperties": false,
|
|
188
240
|
"$defs": {
|
|
241
|
+
"retentionDays": {
|
|
242
|
+
"type": "integer",
|
|
243
|
+
"minimum": 0,
|
|
244
|
+
"maximum": 36500,
|
|
245
|
+
"description": "Days a record is kept past its last write or use. 0 lets every record go that no never-prune rule keeps."
|
|
246
|
+
},
|
|
247
|
+
"retentionKeep": {
|
|
248
|
+
"type": "integer",
|
|
249
|
+
"minimum": 0,
|
|
250
|
+
"maximum": 100000,
|
|
251
|
+
"description": "How many of the newest are kept at any age."
|
|
252
|
+
},
|
|
189
253
|
"name": {
|
|
190
254
|
"type": "string",
|
|
191
255
|
"pattern": "^[a-z0-9][a-z0-9-]{0,39}$",
|
|
@@ -1450,6 +1514,18 @@
|
|
|
1450
1514
|
"items": {
|
|
1451
1515
|
"$ref": "#/$defs/ciPhaseName"
|
|
1452
1516
|
}
|
|
1517
|
+
},
|
|
1518
|
+
"signedSince": {
|
|
1519
|
+
"type": "string",
|
|
1520
|
+
"pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$",
|
|
1521
|
+
"description": "Added by #4031, read by chant 0.124.28 and newer. The full sha of the first commit on branch whose ci/green and ci/revoked tags must carry a DSSE statement in their annotation, signed by a key .chant/trust.json at base lists under runners for a principal holding the runner role. A tag on a strict first-parent ancestor of this commit counts unsigned, so existing tags keep working; a tag on any other commit, this one included, needs a statement. Position in history decides, never a date, which whoever makes a commit or a tag sets. A reader refuses a sha that names no commit in its clone with declaration-invalid. Absent, no tag needs a statement."
|
|
1522
|
+
},
|
|
1523
|
+
"source": {
|
|
1524
|
+
"enum": [
|
|
1525
|
+
"checks",
|
|
1526
|
+
"runs"
|
|
1527
|
+
],
|
|
1528
|
+
"description": "Added by #4044, read by chant 0.124.28 and newer, so a declaration that sets it sets minReader to 0.124.28 or newer. Where chant ci tick reads each phase's results. checks (the default): the forge's check runs, whose names the phases' patterns match. runs: chant's own Op runs, read from the run ledger on chant/lifecycle, for a project whose pipelines run as chant Ops, such as one on GitLab or Forgejo. A phase's patterns then match Op names, a run counts for the commit its record names as gitSha, and the latest run of each Op decides. A run counts only when its record carries a statement signed by a key .chant/trust.json at base lists under runners for a principal holding the runner role; the tick ignores and reports any other. A tick from runs signs every tag it makes, so it needs a runner key."
|
|
1453
1529
|
}
|
|
1454
1530
|
},
|
|
1455
1531
|
"patternProperties": {
|
|
@@ -421,3 +421,31 @@ describe("diagram artifacts (#2764)", () => {
|
|
|
421
421
|
expect(failure(base([member([diagram({ render: "../out.svg" })])])).code).toBe("declaration-invalid");
|
|
422
422
|
});
|
|
423
423
|
});
|
|
424
|
+
|
|
425
|
+
describe("retention (#4036)", () => {
|
|
426
|
+
test("the block is read as written, without x- keys, and is null when absent", () => {
|
|
427
|
+
expect(parse(base([])).retention).toBeNull();
|
|
428
|
+
const d = parse(base([], { minReader: "0.80.0", retention: { changeSets: { days: 30 }, releases: { keep: 3 }, runEvents: { days: 2, keep: 5 }, "x-why": "audits" } }));
|
|
429
|
+
expect(d.retention).toEqual({ changeSets: { days: 30 }, releases: { keep: 3 }, runEvents: { days: 2, keep: 5 } });
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test("an unknown kind or field, a negative or fractional number, and keep on changeSets are refused", () => {
|
|
433
|
+
for (const retention of [{ plans: { days: 1 } }, { releases: { days: -1 } }, { releases: { days: 1.5 } }, { changeSets: { keep: 2 } }, { runEvents: { hours: 2 } }]) {
|
|
434
|
+
expect(failure(base([], { retention })).code).toBe("declaration-invalid");
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
describe("metrics (#4039)", () => {
|
|
440
|
+
test("failureWindowDays is read as written, without x- keys, and the block is null when absent", () => {
|
|
441
|
+
expect(parse(base([])).metrics).toBeNull();
|
|
442
|
+
expect(parse(base([], { minReader: "0.80.0", metrics: { failureWindowDays: 14, "x-why": "weekly" } })).metrics).toEqual({ failureWindowDays: 14 });
|
|
443
|
+
expect(parse(base([], { metrics: {} })).metrics).toEqual({});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
test("an unknown field, 0, a fraction and more than a year are refused", () => {
|
|
447
|
+
for (const metrics of [{ window: 7 }, { failureWindowDays: 0 }, { failureWindowDays: 1.5 }, { failureWindowDays: 366 }]) {
|
|
448
|
+
expect(failure(base([], { metrics })).code).toBe("declaration-invalid");
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
});
|