@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
package/src/workspace/status.ts
CHANGED
|
@@ -39,7 +39,10 @@ import type { CommandContext } from "../cli/registry";
|
|
|
39
39
|
import { GATES_DIR } from "../lifecycle/gate-ledger";
|
|
40
40
|
import { readPathSha } from "../lifecycle/git";
|
|
41
41
|
import { latestPerComponent, readReleaseLedger, type ReleaseRecord } from "../lifecycle/release-ledger";
|
|
42
|
+
import { describeRevocation, revokedCommitsAt, revokedReleaseLookup, type ReleaseRevocation, type RevokedRelease } from "../lifecycle/release-revocation";
|
|
42
43
|
import { readReleasePlan, type ReleasePlan } from "../lifecycle/plan-ledger";
|
|
44
|
+
import { readDeployOutcomes, type DeployOutcome } from "../lifecycle/deploy-outcome";
|
|
45
|
+
import { mixedEnvironment, releaseRollbackLine, rollbackTarget, type MixedEnvironment, type ReleaseCheck } from "../lifecycle/mixed-environment";
|
|
43
46
|
import { listWorkLeases, type WorkLeaseState } from "../lifecycle/work-lease";
|
|
44
47
|
import { readCheckoutHead, worktreeChanges, type CheckoutHead } from "./records-checkout";
|
|
45
48
|
import { policyView, replicationState, type ReplicationState } from "./wip";
|
|
@@ -50,7 +53,12 @@ import { loadKindRegistry, resolveMemberFields, type MemberFields } from "./kind
|
|
|
50
53
|
import { factoryView, listingView, plantability, treeBytes, type FactoryView, type ListingView, type Plantable } from "./box-factory";
|
|
51
54
|
import { declaredRecordKinds, extensionsAt, readDeclaration, readerVersion, WorkspaceReadError, type Declaration, type ErrorLocation, type Member } from "./declaration";
|
|
52
55
|
import type { ReasonCode } from "./reason-codes";
|
|
53
|
-
import {
|
|
56
|
+
import type { FlagDecision, FlagValue } from "./flags";
|
|
57
|
+
import type { OverlapChangeSetRef, OverlapReport } from "./overlap";
|
|
58
|
+
import type { WorkLink } from "./work";
|
|
59
|
+
import { readMemberPromotionOrder, type PromotionStep } from "./promotion-order";
|
|
60
|
+
import { gateAdmissionFrom, releaseRuleFrom } from "./identity";
|
|
61
|
+
import { checkReleaseSignature, type GateAdmissionRule, type ReleaseSignatureCheck } from "./identity-gate-rule";
|
|
54
62
|
import { scopeSource } from "./write-scope";
|
|
55
63
|
import { GATE_REASON_CODES, readMemberGates, type GateLedgerReader, type StatusGate, type StatusGateLedger } from "./status-gates";
|
|
56
64
|
import { STEWARD_REASON_CODES, readMemberStewards, type StatusSteward, type StewardReasonCode } from "./status-stewards";
|
|
@@ -120,6 +128,14 @@ export interface StatusRelease {
|
|
|
120
128
|
runId: string;
|
|
121
129
|
timestamp: string;
|
|
122
130
|
actor: string;
|
|
131
|
+
/** Who approved the change at its gate (#1035), when it had one. Null for an ungated release. */
|
|
132
|
+
approver: string | null;
|
|
133
|
+
/** The gate resolution that admitted the release (#4020): the gate and when it was resolved. Null for an ungated release and for one recorded before chant 0.124.28. */
|
|
134
|
+
admission: { op: string; gate: string; resolvedAt: string } | null;
|
|
135
|
+
/** The release this deploy was made from (#4043): the `releaseDigest` of `_releases/<releaseDigest>.json`. Null for a deploy that named no release and for one recorded before chant 0.124.29. */
|
|
136
|
+
releaseDigest: string | null;
|
|
137
|
+
/** The label that release was cut with (#4043), or null when it has none, when no release is named, or when the cut can't be read. Chant 0.124.29 and newer write it. */
|
|
138
|
+
releaseLabel: string | null;
|
|
123
139
|
/** Read flags, such as `legacy-digest` (#2514). */
|
|
124
140
|
flags: string[];
|
|
125
141
|
/**
|
|
@@ -131,6 +147,25 @@ export interface StatusRelease {
|
|
|
131
147
|
* hasn't fetched yet.
|
|
132
148
|
*/
|
|
133
149
|
plan: ReleasePlan | null;
|
|
150
|
+
/**
|
|
151
|
+
* Set when the deploy names a release (`releaseDigest` on its record) that
|
|
152
|
+
* was cut from a revoked commit (#4033, ws-105): the revoked tag on that
|
|
153
|
+
* commit, the release and its label, and the line that rolls the component
|
|
154
|
+
* back. Absent otherwise, and from chant older than 0.124.29.
|
|
155
|
+
*/
|
|
156
|
+
revoked?: StatusRevokedRelease;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** A release an environment runs that was cut from a revoked commit (#4033). */
|
|
160
|
+
export interface StatusRevokedRelease extends RevokedRelease {
|
|
161
|
+
/**
|
|
162
|
+
* The command that rolls the environment back, run in the member's
|
|
163
|
+
* directory: `chant components rollback <env> --release <label|digest>`
|
|
164
|
+
* naming the newest release the environment ran before this one that is
|
|
165
|
+
* still kept and not revoked (#4035), or `chant components rollback <env>
|
|
166
|
+
* --component <component>` when there is none.
|
|
167
|
+
*/
|
|
168
|
+
rollback: string;
|
|
134
169
|
}
|
|
135
170
|
|
|
136
171
|
export interface StatusLedger {
|
|
@@ -148,6 +183,14 @@ export interface StatusEnvironment {
|
|
|
148
183
|
/** The latest release of each component in the ledger, sorted by component. */
|
|
149
184
|
releases: StatusRelease[];
|
|
150
185
|
reason: { code: StatusReasonCode; message: string } | null;
|
|
186
|
+
/**
|
|
187
|
+
* Set when the environment runs a mix (#4035): a promote, rollback or
|
|
188
|
+
* redeploy left a component failed or gated and nothing has been recorded
|
|
189
|
+
* for it since, or its components' latest records name more than one
|
|
190
|
+
* release. Each component with what it runs, and the lines that settle it.
|
|
191
|
+
* Absent otherwise, and from chant older than 0.124.29.
|
|
192
|
+
*/
|
|
193
|
+
mixed?: MixedEnvironment;
|
|
151
194
|
}
|
|
152
195
|
|
|
153
196
|
export type CompareState = "same" | "differs" | "only-env" | "only-compare";
|
|
@@ -185,6 +228,63 @@ export interface StatusMember {
|
|
|
185
228
|
stewards: StatusSteward[];
|
|
186
229
|
/** Why a steward or a last run may be missing from `stewards`. */
|
|
187
230
|
stewardReasons: { code: StewardReasonCode; message: string }[];
|
|
231
|
+
/**
|
|
232
|
+
* The member's promotion order (#4030): the literal entries of the
|
|
233
|
+
* `environments` array in its chant.config.ts, in order, each with whether
|
|
234
|
+
* it is marked canary. A release moves from each step to the next. `[]`
|
|
235
|
+
* when the config declares no environments, null when it could not be read.
|
|
236
|
+
* Added in contract 1 by chant 0.124.28.
|
|
237
|
+
*/
|
|
238
|
+
promotionOrder: PromotionStep[] | null;
|
|
239
|
+
/**
|
|
240
|
+
* The member's release cuts (#4026, ws-104), oldest first, each with
|
|
241
|
+
* whether its signature verifies for its actor against the signers at
|
|
242
|
+
* base and, when the declaration names a `release` gate with a class,
|
|
243
|
+
* whether the actor is in it (#4031). `[]` when none is cut, null when the
|
|
244
|
+
* cuts could not be read. Added in contract 1 by chant 0.124.28.
|
|
245
|
+
*/
|
|
246
|
+
releaseCuts: StatusReleaseCut[] | null;
|
|
247
|
+
/**
|
|
248
|
+
* The member's feature flags (#4024): each record of a declared flag kind
|
|
249
|
+
* that names this member, with its value per environment and the work item
|
|
250
|
+
* it hides. Sorted by id. `[]` when none names it, null when the flag
|
|
251
|
+
* records could not be read. Called `featureFlags` because a release
|
|
252
|
+
* record's `flags` already means something else. Added in contract 1 by
|
|
253
|
+
* chant 0.124.29.
|
|
254
|
+
*/
|
|
255
|
+
featureFlags: StatusFeatureFlag[] | null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** One feature flag of a member (#4024), as `status --json` lists it. */
|
|
259
|
+
export interface StatusFeatureFlag {
|
|
260
|
+
/** The flag's name, its record's id. */
|
|
261
|
+
id: string;
|
|
262
|
+
/** The build parameter of the member the flag sets. */
|
|
263
|
+
param: string;
|
|
264
|
+
owner: string;
|
|
265
|
+
/** The value per environment, as the record writes it. An environment it leaves out builds with the parameter's default. */
|
|
266
|
+
values: Record<string, FlagValue>;
|
|
267
|
+
/** The work item the flag hides, with its state (null when no record has it), or null when the record names none. */
|
|
268
|
+
work: WorkLink | null;
|
|
269
|
+
/** Each decision the work item implements, with its state and whether it is confirmed. */
|
|
270
|
+
implements: FlagDecision[];
|
|
271
|
+
/** True when every decision the work item implements is confirmed: the flag is due for removal (check WSP118). */
|
|
272
|
+
stale: boolean;
|
|
273
|
+
/** The kind file and the record file, from the workspace root. */
|
|
274
|
+
kind: string;
|
|
275
|
+
path: string;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/** One cut of a member's release (#4031), as `status --json` lists it. */
|
|
279
|
+
export interface StatusReleaseCut {
|
|
280
|
+
releaseDigest: string;
|
|
281
|
+
label: string | null;
|
|
282
|
+
/** The release's commit, or null when its release file can't be read. */
|
|
283
|
+
commit: string | null;
|
|
284
|
+
actor: string;
|
|
285
|
+
timestamp: string;
|
|
286
|
+
signature: string | null;
|
|
287
|
+
signatureCheck: ReleaseSignatureCheck;
|
|
188
288
|
}
|
|
189
289
|
|
|
190
290
|
/** The most paths `ship.pending.paths` lists; `files` still counts them all. */
|
|
@@ -294,6 +394,14 @@ export type StatusDocument =
|
|
|
294
394
|
acceptance: StatusAcceptance[];
|
|
295
395
|
/** How far each ref the box's replicate policy names is from its remote (#3172), read locally; null when no box declares one. */
|
|
296
396
|
replication: ReplicationState | null;
|
|
397
|
+
/**
|
|
398
|
+
* Overlap between in-flight work items (#4025): pairs of items whose
|
|
399
|
+
* linked commits touch the same files, or that both reach one entry of
|
|
400
|
+
* a change set a pending or approved gate binds. A report, never a
|
|
401
|
+
* lock. Null when no work kind is declared or the work records or
|
|
402
|
+
* history can't be read. Added in contract 1 by chant 0.124.29.
|
|
403
|
+
*/
|
|
404
|
+
overlap: OverlapReport | null;
|
|
297
405
|
summary: { members: number; released: number; unreadable: number; differing: number | null };
|
|
298
406
|
}
|
|
299
407
|
| {
|
|
@@ -312,12 +420,16 @@ export interface StatusQuery {
|
|
|
312
420
|
compareTo?: string;
|
|
313
421
|
/** Reads one ledger; the lifecycle reader unless a test swaps it. */
|
|
314
422
|
readLedger?: LedgerReader;
|
|
423
|
+
/** Reads the deploy outcomes beside one ledger (#4035), given the ledger's path; the lifecycle reader unless a test swaps it. */
|
|
424
|
+
readOutcomes?: OutcomeReader;
|
|
315
425
|
/** Reads one member's gate ledger directory; git unless a test swaps it. */
|
|
316
426
|
readGates?: GateLedgerReader;
|
|
317
427
|
/** The instant a gate's expiry is measured against; now by default. */
|
|
318
428
|
now?: string;
|
|
319
429
|
/** Reads one member's stewards; the declaration and ledger reader unless a test swaps it. */
|
|
320
430
|
readStewards?: typeof readMemberStewards;
|
|
431
|
+
/** Reads one member's promotion order from its directory; its chant.config.ts unless a test swaps it. */
|
|
432
|
+
readPromotionOrder?: (dir: string) => Promise<PromotionStep[] | null>;
|
|
321
433
|
}
|
|
322
434
|
|
|
323
435
|
/** A factory view with the declaration's x- keys on it and on its check, publish and tiers (#3595). */
|
|
@@ -412,7 +524,27 @@ async function shipView(member: Member, cwd: string, read: LedgerReader): Promis
|
|
|
412
524
|
|
|
413
525
|
const defaultReader: LedgerReader = (path, cwd) => readReleaseLedger(path.slice(0, -"/releases.jsonl".length), { cwd });
|
|
414
526
|
|
|
415
|
-
|
|
527
|
+
/** Reads the deploy outcomes (#4035) in the directory of the ledger at `path`. */
|
|
528
|
+
type OutcomeReader = (path: string, cwd: string) => Promise<DeployOutcome[]>;
|
|
529
|
+
|
|
530
|
+
const defaultOutcomeReader: OutcomeReader = (path, cwd) => readDeployOutcomes(path.slice(0, -"/releases.jsonl".length), { cwd }).catch(() => []);
|
|
531
|
+
|
|
532
|
+
/** A release digest's revocation, for one member, or null (#4033). */
|
|
533
|
+
type RevokedOf = (releaseDigest: string) => Promise<RevokedRelease | null>;
|
|
534
|
+
|
|
535
|
+
/** The per-component rollback line, for a component an environment runs from a revoked release when no earlier release can be named (#4033). */
|
|
536
|
+
export function rollbackLine(env: string, component: string): string {
|
|
537
|
+
return `chant components rollback ${env} --component ${component}`;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
async function readEnvironment(
|
|
541
|
+
member: Member,
|
|
542
|
+
env: string,
|
|
543
|
+
cwd: string,
|
|
544
|
+
read: LedgerReader,
|
|
545
|
+
revokedOf: RevokedOf = async () => null,
|
|
546
|
+
readOutcomes: OutcomeReader = defaultOutcomeReader,
|
|
547
|
+
): Promise<StatusEnvironment> {
|
|
416
548
|
const ledger = await ledgerFor(member, env, cwd);
|
|
417
549
|
let records: ReleaseRecord[];
|
|
418
550
|
let malformed: number;
|
|
@@ -434,6 +566,37 @@ async function readEnvironment(member: Member, env: string, cwd: string, read: L
|
|
|
434
566
|
// member (see plan-ledger.ts's readReleasePlan doc).
|
|
435
567
|
const planPrefix = ledger.layout === "members" ? `${MEMBERS_DIR}/${member.name}/` : "";
|
|
436
568
|
const latest = [...latestPerComponent(records).values()].sort((a, b) => (a.component < b.component ? -1 : a.component > b.component ? 1 : 0));
|
|
569
|
+
// #4043: a release item names the label of the release it was deployed from.
|
|
570
|
+
const memberDir = resolve(cwd, member.dir);
|
|
571
|
+
let labels = new Map<string, string>();
|
|
572
|
+
let labelsRead = false;
|
|
573
|
+
const readLabels = async (): Promise<Map<string, string>> => {
|
|
574
|
+
if (labelsRead) return labels;
|
|
575
|
+
labelsRead = true;
|
|
576
|
+
try {
|
|
577
|
+
const { readCuts, labelsByDigest } = await import("../lifecycle/release-store");
|
|
578
|
+
labels = labelsByDigest((await readCuts({ cwd: memberDir })).cuts);
|
|
579
|
+
} catch {
|
|
580
|
+
// The label is a convenience; the digest is the join key.
|
|
581
|
+
}
|
|
582
|
+
return labels;
|
|
583
|
+
};
|
|
584
|
+
if (latest.some((r) => r.releaseDigest !== undefined)) await readLabels();
|
|
585
|
+
// #4035: whether a release a rollback could go back to is kept and not revoked.
|
|
586
|
+
const checks = new Map<string, Promise<ReleaseCheck>>();
|
|
587
|
+
const check = (digest: string): Promise<ReleaseCheck> => {
|
|
588
|
+
let found = checks.get(digest);
|
|
589
|
+
if (!found) {
|
|
590
|
+
found = (async () => {
|
|
591
|
+
const { readRelease } = await import("../lifecycle/release-store");
|
|
592
|
+
const kept = (await readRelease(digest, { cwd: memberDir }).catch(() => null)) !== null;
|
|
593
|
+
return { kept, revoked: kept && (await revokedOf(digest)) !== null };
|
|
594
|
+
})();
|
|
595
|
+
checks.set(digest, found);
|
|
596
|
+
}
|
|
597
|
+
return found;
|
|
598
|
+
};
|
|
599
|
+
const back = { records, check, label: async (digest: string) => (await readLabels()).get(digest) };
|
|
437
600
|
const releases: StatusRelease[] = await Promise.all(
|
|
438
601
|
latest.map(async (r) => ({
|
|
439
602
|
component: r.component,
|
|
@@ -443,10 +606,16 @@ async function readEnvironment(member: Member, env: string, cwd: string, read: L
|
|
|
443
606
|
runId: r.runId,
|
|
444
607
|
timestamp: r.timestamp,
|
|
445
608
|
actor: r.actor,
|
|
609
|
+
approver: r.approver ?? null,
|
|
610
|
+
admission: r.admission ? { op: r.admission.op, gate: r.admission.gate, resolvedAt: r.admission.resolvedAt } : null,
|
|
611
|
+
releaseDigest: r.releaseDigest ?? null,
|
|
612
|
+
releaseLabel: (r.releaseDigest !== undefined ? labels.get(r.releaseDigest) : undefined) ?? null,
|
|
446
613
|
flags: [...(r.flags ?? [])],
|
|
447
614
|
plan: await readReleasePlan(r.digest, { cwd, prefix: planPrefix }),
|
|
615
|
+
...(await revokedItem(r, env, revokedOf, back)),
|
|
448
616
|
})),
|
|
449
617
|
);
|
|
618
|
+
const mixed = await mixedEnvironment({ env, records, outcomes: await readOutcomes(ledger.path, cwd), label: back.label, check });
|
|
450
619
|
return {
|
|
451
620
|
env,
|
|
452
621
|
ledger: { ...ledger, shared: false },
|
|
@@ -455,9 +624,47 @@ async function readEnvironment(member: Member, env: string, cwd: string, read: L
|
|
|
455
624
|
malformed > 0
|
|
456
625
|
? { code: "ledger-malformed", message: `${ledger.path}: ${malformed} line${malformed === 1 ? " is" : "s are"} not a release record and ${malformed === 1 ? "was" : "were"} skipped` }
|
|
457
626
|
: null,
|
|
627
|
+
...(mixed ? { mixed } : {}),
|
|
458
628
|
};
|
|
459
629
|
}
|
|
460
630
|
|
|
631
|
+
/**
|
|
632
|
+
* `{ revoked }` when the record names a release cut from a revoked commit
|
|
633
|
+
* (#4033), else nothing. Its rollback line names the newest release the
|
|
634
|
+
* environment ran before this one that is kept and not revoked (#4035).
|
|
635
|
+
*/
|
|
636
|
+
async function revokedItem(
|
|
637
|
+
r: ReleaseRecord,
|
|
638
|
+
env: string,
|
|
639
|
+
revokedOf: RevokedOf,
|
|
640
|
+
back: { records: readonly ReleaseRecord[]; check: (digest: string) => Promise<ReleaseCheck>; label: (digest: string) => Promise<string | undefined> },
|
|
641
|
+
): Promise<{ revoked?: StatusRevokedRelease }> {
|
|
642
|
+
if (r.releaseDigest === undefined) return {};
|
|
643
|
+
const revoked = await revokedOf(r.releaseDigest);
|
|
644
|
+
if (!revoked) return {};
|
|
645
|
+
const target = await rollbackTarget({ records: back.records, exclude: new Set([r.releaseDigest]), before: r.timestamp, check: back.check });
|
|
646
|
+
const rollback = target ? releaseRollbackLine(env, target, await back.label(target)) : rollbackLine(env, r.component);
|
|
647
|
+
return { revoked: { ...revoked, rollback } };
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* A release gate whose release was cut from a revoked commit is superseded
|
|
652
|
+
* (#4033): `chant approve` refuses it and the promote refuses the release, so
|
|
653
|
+
* neither an approval nor a pending fact will pass it. An expired gate stays
|
|
654
|
+
* expired. The gate carries the revocation.
|
|
655
|
+
*/
|
|
656
|
+
async function supersedeRevokedGates(gates: StatusGate[], revokedOf: RevokedOf): Promise<StatusGate[]> {
|
|
657
|
+
return Promise.all(
|
|
658
|
+
gates.map(async (g) => {
|
|
659
|
+
if (g.releaseDigest === undefined) return g;
|
|
660
|
+
const revoked = await revokedOf(g.releaseDigest);
|
|
661
|
+
if (!revoked) return g;
|
|
662
|
+
const { releaseDigest: _d, label: _l, ...revocation } = revoked;
|
|
663
|
+
return { ...g, state: g.state === "expired" ? g.state : "superseded", revoked: revocation };
|
|
664
|
+
}),
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
|
|
461
668
|
/** The digest two environments are compared on: the input side when there is one. */
|
|
462
669
|
const compareKey = (r: StatusRelease): string => r.inputDigest ?? r.digest;
|
|
463
670
|
|
|
@@ -506,10 +713,18 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
506
713
|
// #3163: the declaration's identity.gates at base, read once for every member's gates.
|
|
507
714
|
const ruleSource = scopeSource(found.dir);
|
|
508
715
|
const rules = (gate: string) => gateAdmissionFrom(ruleSource, gate);
|
|
716
|
+
const releaseRule = releaseRuleFrom(ruleSource).rule;
|
|
717
|
+
// #4033 (ws-105): revoked commits, read from local tags once and only when a release is met.
|
|
718
|
+
// Tags that can't be judged read as none revoked; workspace check reports the declaration.
|
|
719
|
+
let revokedCommits: Promise<ReadonlyMap<string, ReleaseRevocation>> | undefined;
|
|
720
|
+
const revoked = () => (revokedCommits ??= revokedCommitsAt(top, ruleSource.declaration?.ci?.green ?? null).catch(() => new Map()));
|
|
721
|
+
// #4024: the flag records, read once for every member. Unreadable records list as null.
|
|
722
|
+
const flags = await readStatusFlags(declaration, found.dir);
|
|
509
723
|
const members: StatusMember[] = [];
|
|
510
724
|
for (const m of declaration.members) {
|
|
725
|
+
const revokedOf = revokedReleaseLookup(resolve(found.dir, m.dir), revoked);
|
|
511
726
|
const environments: StatusEnvironment[] = [];
|
|
512
|
-
for (const env of envs) environments.push(await readEnvironment(m, env, found.dir, read));
|
|
727
|
+
for (const env of envs) environments.push(await readEnvironment(m, env, found.dir, read, revokedOf, query.readOutcomes));
|
|
513
728
|
const own = await hasMemberLedger(m, found.dir);
|
|
514
729
|
const gates = await readMemberGates(own ? `${MEMBERS_DIR}/${m.name}/${GATES_DIR}` : GATES_DIR, own ? "members" : "flat", commit, envs, found.dir, now, query.readGates, rules);
|
|
515
730
|
const stewards = await (query.readStewards ?? readMemberStewards)(resolve(found.dir, m.dir), query.env, now, m.kind, m.box);
|
|
@@ -526,7 +741,7 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
526
741
|
compare: query.compareTo === undefined ? null : compareEnvironments(environments[0], environments[environments.length - 1]),
|
|
527
742
|
readable: environments.every((e) => e.reason === null),
|
|
528
743
|
gateLedger: gates.ledger,
|
|
529
|
-
gates: gates.gates,
|
|
744
|
+
gates: await supersedeRevokedGates(gates.gates, revokedOf),
|
|
530
745
|
box:
|
|
531
746
|
m.box === null
|
|
532
747
|
? null
|
|
@@ -553,6 +768,9 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
553
768
|
},
|
|
554
769
|
stewards: stewards.stewards,
|
|
555
770
|
stewardReasons: stewards.reasons,
|
|
771
|
+
promotionOrder: await (query.readPromotionOrder ?? readMemberPromotionOrder)(resolve(found.dir, m.dir)),
|
|
772
|
+
releaseCuts: await readReleaseCuts(resolve(found.dir, m.dir), releaseRule),
|
|
773
|
+
featureFlags: flags === null ? null : flags.filter((f) => f.member === m.name).map(({ member: _member, ...f }) => f),
|
|
556
774
|
...x(m.pointer),
|
|
557
775
|
});
|
|
558
776
|
}
|
|
@@ -565,6 +783,16 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
565
783
|
|
|
566
784
|
const leases = await readWorkspaceLeases(declaration.members, found.dir, new Date(now));
|
|
567
785
|
const acceptance = await readWorkspaceAcceptance(declaration, found.dir);
|
|
786
|
+
// #4025: the change sets still waiting to apply, the ones a pending or approved gate binds.
|
|
787
|
+
const pendingChangeSets: OverlapChangeSetRef[] = [];
|
|
788
|
+
for (const m of members) {
|
|
789
|
+
const own = m.gateLedger.layout === "members";
|
|
790
|
+
for (const g of m.gates) {
|
|
791
|
+
if (g.changeSet === undefined || (g.state !== "pending" && g.state !== "approved")) continue;
|
|
792
|
+
pendingChangeSets.push({ digest: g.changeSet, cwd: resolve(found.dir, m.dir), ledgerMember: own ? m.name : null, gitSha: g.gitSha ?? null });
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
const overlap = await readStatusOverlap(declaration, found.dir, top, pendingChangeSets);
|
|
568
796
|
|
|
569
797
|
return {
|
|
570
798
|
...head,
|
|
@@ -578,6 +806,7 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
578
806
|
leases,
|
|
579
807
|
acceptance,
|
|
580
808
|
replication: replicationOf(declaration, top),
|
|
809
|
+
overlap,
|
|
581
810
|
summary: {
|
|
582
811
|
members: members.length,
|
|
583
812
|
released: members.filter((m) => m.environments[0].releases.length > 0).length,
|
|
@@ -595,6 +824,35 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
595
824
|
}
|
|
596
825
|
}
|
|
597
826
|
|
|
827
|
+
/**
|
|
828
|
+
* A member's release cuts with their signatures judged (#4031), read from
|
|
829
|
+
* its ledger on the local `chant/lifecycle`. Null when they can't be read.
|
|
830
|
+
*/
|
|
831
|
+
async function readReleaseCuts(memberDir: string, rule: GateAdmissionRule): Promise<StatusReleaseCut[] | null> {
|
|
832
|
+
try {
|
|
833
|
+
const { readCuts, readRelease } = await import("../lifecycle/release-store");
|
|
834
|
+
const loc = { cwd: memberDir };
|
|
835
|
+
const { cuts } = await readCuts(loc);
|
|
836
|
+
const commits = new Map<string, string | null>();
|
|
837
|
+
const out: StatusReleaseCut[] = [];
|
|
838
|
+
for (const c of cuts) {
|
|
839
|
+
if (!commits.has(c.releaseDigest)) commits.set(c.releaseDigest, (await readRelease(c.releaseDigest, loc))?.commit ?? null);
|
|
840
|
+
out.push({
|
|
841
|
+
releaseDigest: c.releaseDigest,
|
|
842
|
+
label: c.label,
|
|
843
|
+
commit: commits.get(c.releaseDigest) ?? null,
|
|
844
|
+
actor: c.actor,
|
|
845
|
+
timestamp: c.timestamp,
|
|
846
|
+
signature: c.signature,
|
|
847
|
+
signatureCheck: checkReleaseSignature(rule, c),
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
return out;
|
|
851
|
+
} catch {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
598
856
|
/**
|
|
599
857
|
* The work leases of the flat ledger and of each member's (#2732). Leases are
|
|
600
858
|
* env-independent, so the environment asked for doesn't narrow them. Never
|
|
@@ -610,6 +868,31 @@ async function readWorkspaceLeases(members: Member[], cwd: string, now: Date): P
|
|
|
610
868
|
return out;
|
|
611
869
|
}
|
|
612
870
|
|
|
871
|
+
/** Every flag of the declared flag kinds (#4024), read in the working tree, sorted by id; null when they can't be read. */
|
|
872
|
+
async function readStatusFlags(declaration: Declaration, root: string): Promise<Array<StatusFeatureFlag & { member: string }> | null> {
|
|
873
|
+
if (declaredRecordKinds(declaration).length === 0) return [];
|
|
874
|
+
try {
|
|
875
|
+
const { readWorkspaceFlags } = await import("./flags-read");
|
|
876
|
+
const { flags } = await readWorkspaceFlags(root);
|
|
877
|
+
return flags
|
|
878
|
+
.map((f) => ({ id: f.id, member: f.member, param: f.param, owner: f.owner, values: f.values, work: f.work, implements: f.implements, stale: f.stale, kind: f.kind, path: f.path }))
|
|
879
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
880
|
+
} catch {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
/** The overlap report (#4025); null when no work kind is declared or it can't be read. */
|
|
886
|
+
async function readStatusOverlap(declaration: Declaration, root: string, top: string, changeSets: OverlapChangeSetRef[]): Promise<OverlapReport | null> {
|
|
887
|
+
if (declaredRecordKinds(declaration).length === 0) return null;
|
|
888
|
+
try {
|
|
889
|
+
const { readOverlap } = await import("./overlap");
|
|
890
|
+
return await readOverlap({ declaration, root, top, changeSets });
|
|
891
|
+
} catch {
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
613
896
|
/** The acceptance counts of every declared work kind with criteria (#2772), read in the working tree. */
|
|
614
897
|
async function readWorkspaceAcceptance(declaration: Declaration, root: string): Promise<StatusAcceptance[]> {
|
|
615
898
|
if (declaredRecordKinds(declaration).length === 0) return [];
|
|
@@ -695,6 +978,22 @@ export function formatStatus(doc: Extract<StatusDocument, { members: unknown }>)
|
|
|
695
978
|
for (const e of m.environments) {
|
|
696
979
|
if (e.reason) memberNotes.push(` ${e.reason.code}: ${e.reason.message}`);
|
|
697
980
|
else if (e.ledger.shared && e.releases.length > 0) memberNotes.push(` ${e.env} is read from the shared flat ledger ${e.ledger.path}`);
|
|
981
|
+
// #4033: a component running a release cut from a revoked commit, and the way back.
|
|
982
|
+
for (const r of e.releases) {
|
|
983
|
+
if (!r.revoked) continue;
|
|
984
|
+
const name = r.revoked.label ?? r.revoked.releaseDigest;
|
|
985
|
+
memberNotes.push(` ${e.env}: ${r.component} runs release ${name}, revoked by ${r.revoked.tag} (${describeRevocation(r.revoked)}); roll back: ${r.revoked.rollback}`);
|
|
986
|
+
}
|
|
987
|
+
// #4035: an environment running a mix, what each component runs, and the lines that settle it.
|
|
988
|
+
if (e.mixed) {
|
|
989
|
+
const what = e.mixed.components.map((c) => {
|
|
990
|
+
const runs = c.releaseDigest ? `release ${c.releaseLabel ?? c.releaseDigest.slice(0, 24)}` : c.digest ? "no release" : "nothing recorded";
|
|
991
|
+
return c.state === "settled" ? `${c.component} runs ${runs}` : `${c.component} ${c.state} (${c.attempt!.verb} ${c.attempt!.runId}), recorded ${runs}`;
|
|
992
|
+
});
|
|
993
|
+
memberNotes.push(` ${e.env} is mixed: ${what.join("; ")}`);
|
|
994
|
+
for (const line of e.mixed.redeploy) memberNotes.push(` redeploy: ${line}`);
|
|
995
|
+
if (e.mixed.rollback) memberNotes.push(` roll back: ${e.mixed.rollback}`);
|
|
996
|
+
}
|
|
698
997
|
}
|
|
699
998
|
if (memberNotes.length > 0) notes.set(rows.length - 1, memberNotes);
|
|
700
999
|
}
|
|
@@ -715,6 +1014,37 @@ export function formatStatus(doc: Extract<StatusDocument, { members: unknown }>)
|
|
|
715
1014
|
for (const a of doc.acceptance) rows.push([a.member ? `${a.member}/${a.item}` : a.item, a.state ?? "-", `${a.met}/${a.total}`]);
|
|
716
1015
|
lines.push(...table(rows));
|
|
717
1016
|
}
|
|
1017
|
+
const cut = doc.members.filter((m) => (m.releaseCuts ?? []).length > 0);
|
|
1018
|
+
if (cut.length > 0) {
|
|
1019
|
+
lines.push("");
|
|
1020
|
+
const rows: string[][] = [["MEMBER", "RELEASE", "COMMIT", "CUT BY", "SIGNATURE"]];
|
|
1021
|
+
for (const m of cut) {
|
|
1022
|
+
for (const c of m.releaseCuts!) rows.push([m.name, c.label ?? c.releaseDigest.slice(0, 24), c.commit?.slice(0, 12) ?? "-", c.actor, c.signatureCheck.status]);
|
|
1023
|
+
}
|
|
1024
|
+
lines.push(...table(rows));
|
|
1025
|
+
}
|
|
1026
|
+
const flagged = doc.members.filter((m) => (m.featureFlags ?? []).length > 0);
|
|
1027
|
+
if (flagged.length > 0) {
|
|
1028
|
+
lines.push("");
|
|
1029
|
+
const rows: string[][] = [["FLAG", "PARAM", doc.env.toUpperCase(), "WORK ITEM", ""]];
|
|
1030
|
+
for (const m of flagged) {
|
|
1031
|
+
for (const f of m.featureFlags!) {
|
|
1032
|
+
const value = f.values[doc.env];
|
|
1033
|
+
rows.push([`${m.name}/${f.id}`, f.param, value === undefined ? "default" : JSON.stringify(value), f.work ? `${f.work.id} (${f.work.state ?? "unknown"})` : "-", f.stale ? "due for removal" : ""]);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
lines.push(...table(rows));
|
|
1037
|
+
}
|
|
1038
|
+
// #4025: in-flight work items that changed the same thing.
|
|
1039
|
+
if (doc.overlap && doc.overlap.pairs.length > 0) {
|
|
1040
|
+
lines.push("");
|
|
1041
|
+
const rows: string[][] = [["OVERLAP", "FILES", "ADDRESSES"]];
|
|
1042
|
+
for (const p of doc.overlap.pairs) {
|
|
1043
|
+
const list = (xs: string[]) => (xs.length === 0 ? "-" : xs.length <= 2 ? xs.join(", ") : `${xs.slice(0, 2).join(", ")} and ${xs.length - 2} more`);
|
|
1044
|
+
rows.push([`${p.items[0]} and ${p.items[1]}`, list(p.files), list([...new Set(p.addresses.map((x) => x.node ?? x.address))])]);
|
|
1045
|
+
}
|
|
1046
|
+
lines.push(...table(rows));
|
|
1047
|
+
}
|
|
718
1048
|
const boxes = doc.members.filter((m) => m.box?.isolation);
|
|
719
1049
|
if (boxes.length > 0) {
|
|
720
1050
|
lines.push("");
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The statement a ci tag carries (#4031): a runner's signed word that a
|
|
3
|
+
* commit went green, or went red after it was green.
|
|
4
|
+
*
|
|
5
|
+
* `ci/green/<sha>` and `ci/revoked/<sha>` are plain git tags (ws-103), so
|
|
6
|
+
* anyone who may push a tag could make one by hand and go around CI. When the
|
|
7
|
+
* declaration sets `ci.green.signedSince` to a commit, a tag on any commit
|
|
8
|
+
* that is not a strict first-parent ancestor of it counts only when its
|
|
9
|
+
* annotation holds a statement signed by a runner key, and readers ignore
|
|
10
|
+
* and report a tag without a valid one.
|
|
11
|
+
*
|
|
12
|
+
* The statement reuses what runner evidence (#2553, ws-069) and the agent-run
|
|
13
|
+
* statement (#3192, ws-090) already use: an in-toto Statement v1 in a DSSE
|
|
14
|
+
* envelope (./dsse.ts), signed with an Ed25519 key that `.chant/trust.json`
|
|
15
|
+
* at base lists under `runners`. The key's principal must also hold the
|
|
16
|
+
* `runner` role there, since a tag says CI ran. A key the signers file lists
|
|
17
|
+
* is never a runner key (./policy.ts).
|
|
18
|
+
*
|
|
19
|
+
* - `subject`: the tagged commit, by `digest.gitCommit`.
|
|
20
|
+
* - `predicate`: the signer's principal and the tag's JSON record as the
|
|
21
|
+
* annotation holds it (`tag`, `commit`, `branch`, `at`, and the phases'
|
|
22
|
+
* runs for a green tag or the failed phase and run for a revoked one).
|
|
23
|
+
*
|
|
24
|
+
* The envelope sits in the annotation's JSON record as `statement`, beside
|
|
25
|
+
* the fields it signs, so a reader that does not know it reads the record as
|
|
26
|
+
* before. Verifying needs the tag, the envelope and the runner keys at base,
|
|
27
|
+
* so it works offline, and every read verifies again.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { z } from "zod";
|
|
31
|
+
import { canonicalJson } from "../../effect-receipt";
|
|
32
|
+
import { IN_TOTO_PAYLOAD_TYPE, signEnvelope, verifyEnvelope, type DsseEnvelope } from "./dsse";
|
|
33
|
+
import { STATEMENT_TYPE } from "./evidence";
|
|
34
|
+
import type { KeyObject } from "node:crypto";
|
|
35
|
+
import type { RunnerKey } from "./policy";
|
|
36
|
+
|
|
37
|
+
export const CI_TAG_PREDICATE = "https://intentius.io/chant/ci-tag/v1";
|
|
38
|
+
|
|
39
|
+
/** The role a tag's signer must hold in `.chant/trust.json` (#4031): the core class `runner`. */
|
|
40
|
+
export const CI_TAG_SIGNER_ROLE = "runner";
|
|
41
|
+
|
|
42
|
+
const fullCommit = z.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/);
|
|
43
|
+
const text = z.string().min(1);
|
|
44
|
+
|
|
45
|
+
/** The statement a ci tag carries. Strict at the top, so a verifier never acts on a field it does not know. */
|
|
46
|
+
export const ciTagStatementSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
_type: z.literal(STATEMENT_TYPE),
|
|
49
|
+
subject: z
|
|
50
|
+
.array(
|
|
51
|
+
z
|
|
52
|
+
.object({ name: fullCommit, digest: z.object({ gitCommit: fullCommit }).strict() })
|
|
53
|
+
.strict()
|
|
54
|
+
.refine((s) => s.name === s.digest.gitCommit, "a commit subject is named by its own id"),
|
|
55
|
+
)
|
|
56
|
+
.length(1),
|
|
57
|
+
predicateType: z.literal(CI_TAG_PREDICATE),
|
|
58
|
+
predicate: z
|
|
59
|
+
.object({
|
|
60
|
+
signer: text,
|
|
61
|
+
record: z
|
|
62
|
+
.object({ schema: z.literal(1), tag: z.enum(["green", "revoked"]), commit: fullCommit, branch: text, at: text })
|
|
63
|
+
.passthrough(),
|
|
64
|
+
})
|
|
65
|
+
.strict(),
|
|
66
|
+
})
|
|
67
|
+
.strict();
|
|
68
|
+
export type CiTagStatement = z.infer<typeof ciTagStatementSchema>;
|
|
69
|
+
|
|
70
|
+
/** A tag's JSON record, without the statement. */
|
|
71
|
+
export type CiTagRecord = Record<string, unknown> & { tag: "green" | "revoked"; commit: string; branch: string };
|
|
72
|
+
|
|
73
|
+
/** Whoever signs the tags a tick makes: a runner key and the principal `.chant/trust.json` lists it for. */
|
|
74
|
+
export interface CiTagSigner {
|
|
75
|
+
principal: string;
|
|
76
|
+
key: KeyObject;
|
|
77
|
+
publicKey: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function buildCiTagStatement(record: CiTagRecord, signer: string): CiTagStatement {
|
|
81
|
+
return {
|
|
82
|
+
_type: STATEMENT_TYPE,
|
|
83
|
+
subject: [{ name: record.commit, digest: { gitCommit: record.commit } }],
|
|
84
|
+
predicateType: CI_TAG_PREDICATE,
|
|
85
|
+
predicate: { signer, record: record as CiTagStatement["predicate"]["record"] },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The envelope a tag's record carries as `statement`, signed by `signer`. */
|
|
90
|
+
export function signCiTagRecord(record: CiTagRecord, signer: CiTagSigner): DsseEnvelope {
|
|
91
|
+
const statement = buildCiTagStatement(record, signer.principal);
|
|
92
|
+
return signEnvelope(IN_TOTO_PAYLOAD_TYPE, Buffer.from(canonicalJson(statement), "utf8"), signer.key, signer.publicKey);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Why a tag is ignored: it has no statement, the statement is not one, no
|
|
97
|
+
* runner key at base signed it, or it signs another tag's facts. The same
|
|
98
|
+
* words a run's attestation uses (`./run-statement.ts`).
|
|
99
|
+
*/
|
|
100
|
+
export type CiTagFailure = "unsigned" | "invalid" | "untrusted" | "mismatch";
|
|
101
|
+
|
|
102
|
+
export type CiTagVerdict =
|
|
103
|
+
| { status: "signed"; signer: string; keyid: string; reason: string }
|
|
104
|
+
| { status: CiTagFailure; reason: string };
|
|
105
|
+
|
|
106
|
+
const normalise = (name: string): string => name.normalize("NFKC").trim().toLowerCase();
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Verify the statement in a tag's record. `expect` is what the tag itself
|
|
110
|
+
* says: the commit its name gives, its kind and the declared branch. The
|
|
111
|
+
* signer's key must be a runner key at base and its principal must hold the
|
|
112
|
+
* `runner` role in `roles`. Never throws.
|
|
113
|
+
*/
|
|
114
|
+
export function verifyCiTagStatement(
|
|
115
|
+
record: Record<string, unknown> | null,
|
|
116
|
+
expect: { sha: string; kind: "green" | "revoked"; branch: string },
|
|
117
|
+
runners: readonly RunnerKey[],
|
|
118
|
+
roles: Readonly<Record<string, readonly string[]>>,
|
|
119
|
+
): CiTagVerdict {
|
|
120
|
+
const tag = `ci/${expect.kind}/${expect.sha}`;
|
|
121
|
+
if (record === null) return { status: "unsigned", reason: `${tag} has no JSON record in its annotation, so it carries no statement (a lightweight tag, or one made by hand)` };
|
|
122
|
+
const { statement: envelope, ...fields } = record;
|
|
123
|
+
if (envelope === undefined) return { status: "unsigned", reason: `${tag} carries no statement signed by a runner key` };
|
|
124
|
+
const v = verifyEnvelope(envelope, runners);
|
|
125
|
+
if (!v.ok) return { status: v.code === "envelope-untrusted" ? "untrusted" : "invalid", reason: `${tag}: ${v.reason}` };
|
|
126
|
+
if (!(roles[CI_TAG_SIGNER_ROLE] ?? []).some((p) => normalise(p) === normalise(v.principal))) {
|
|
127
|
+
return { status: "untrusted", reason: `${tag} is signed by ${v.principal}'s runner key, and ${v.principal} holds no ${CI_TAG_SIGNER_ROLE} role in .chant/trust.json at base` };
|
|
128
|
+
}
|
|
129
|
+
if (v.payloadType !== IN_TOTO_PAYLOAD_TYPE) return { status: "invalid", reason: `${tag}: payload type ${v.payloadType} is not ${IN_TOTO_PAYLOAD_TYPE}` };
|
|
130
|
+
let raw: unknown;
|
|
131
|
+
try {
|
|
132
|
+
raw = JSON.parse(v.payload.toString("utf8"));
|
|
133
|
+
} catch {
|
|
134
|
+
return { status: "invalid", reason: `${tag}: the statement's payload is not JSON` };
|
|
135
|
+
}
|
|
136
|
+
const parsed = ciTagStatementSchema.safeParse(raw);
|
|
137
|
+
if (!parsed.success) {
|
|
138
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join(".") || "/"}: ${i.message}`).join("; ");
|
|
139
|
+
return { status: "invalid", reason: `${tag}: the payload is not a ${CI_TAG_PREDICATE} statement: ${detail}` };
|
|
140
|
+
}
|
|
141
|
+
const p = parsed.data.predicate;
|
|
142
|
+
if (p.signer !== v.principal) return { status: "invalid", reason: `${tag}: the statement names signer ${JSON.stringify(p.signer)}, but ${v.principal}'s key signed it` };
|
|
143
|
+
const differs: string[] = [];
|
|
144
|
+
if (parsed.data.subject[0]!.name !== expect.sha) differs.push(`it is for commit ${parsed.data.subject[0]!.name.slice(0, 12)}`);
|
|
145
|
+
if (p.record.commit !== expect.sha) differs.push(`its record names commit ${p.record.commit.slice(0, 12)}`);
|
|
146
|
+
if (p.record.tag !== expect.kind) differs.push(`it signs a ${p.record.tag} tag`);
|
|
147
|
+
if (p.record.branch !== expect.branch) differs.push(`it is for branch ${p.record.branch}, and ci.green names ${expect.branch}`);
|
|
148
|
+
if (canonicalJson(p.record) !== canonicalJson(fields)) differs.push("the annotation's record is not the one signed");
|
|
149
|
+
if (differs.length > 0) return { status: "mismatch", reason: `${tag} is signed by ${v.principal}, but the statement does not match the tag: ${differs.join("; ")}` };
|
|
150
|
+
return { status: "signed", signer: v.principal, keyid: v.keyid, reason: `signed by ${v.principal} (${CI_TAG_SIGNER_ROLE})` };
|
|
151
|
+
}
|