@intentius/chant 0.124.26 → 0.124.28
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/change-set-graph.d.ts +56 -0
- package/dist/change-set-graph.d.ts.map +1 -0
- package/dist/change-set-graph.js +128 -0
- package/dist/change-set.d.ts +52 -3
- package/dist/change-set.d.ts.map +1 -1
- package/dist/change-set.js +35 -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/commands/build.d.ts +8 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +9 -8
- 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 +19 -4
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/build.js +28 -0
- 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 +1 -1
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.js +14 -3
- package/dist/cli/handlers/pr.d.ts +8 -2
- package/dist/cli/handlers/pr.d.ts.map +1 -1
- package/dist/cli/handlers/pr.js +148 -24
- package/dist/cli/handlers/promote.d.ts.map +1 -1
- package/dist/cli/handlers/promote.js +256 -21
- package/dist/cli/handlers/release.d.ts +34 -0
- package/dist/cli/handlers/release.d.ts.map +1 -0
- package/dist/cli/handlers/release.js +203 -0
- package/dist/cli/handlers/run.d.ts +28 -2
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/handlers/run.js +170 -13
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +88 -13
- 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 +49 -3
- package/dist/cli/registry.d.ts +29 -3
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/run-events.d.ts +29 -0
- package/dist/cli/run-events.d.ts.map +1 -0
- package/dist/cli/run-events.js +47 -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 +4 -1
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/auto-release.js +2 -0
- 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 +42 -9
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/driver.js +81 -54
- 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 +35 -26
- 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 +15 -2
- package/dist/components/promote.d.ts.map +1 -1
- package/dist/components/promote.js +17 -0
- package/dist/components/release-compile.d.ts +133 -0
- package/dist/components/release-compile.d.ts.map +1 -0
- package/dist/components/release-compile.js +376 -0
- package/dist/components/release-gate.d.ts +125 -0
- package/dist/components/release-gate.d.ts.map +1 -0
- package/dist/components/release-gate.js +282 -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 +3 -1
- package/dist/components/wave-plan.d.ts.map +1 -1
- package/dist/components/wave-plan.js +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -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/change-set-store.d.ts +67 -0
- package/dist/lifecycle/change-set-store.d.ts.map +1 -0
- package/dist/lifecycle/change-set-store.js +96 -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/gate-change-set.d.ts +69 -0
- package/dist/lifecycle/gate-change-set.d.ts.map +1 -0
- package/dist/lifecycle/gate-change-set.js +106 -0
- package/dist/lifecycle/gate-ledger.d.ts +49 -1
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.js +18 -0
- package/dist/lifecycle/release-cut.d.ts +107 -0
- package/dist/lifecycle/release-cut.d.ts.map +1 -0
- package/dist/lifecycle/release-cut.js +273 -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-store.d.ts +190 -0
- package/dist/lifecycle/release-store.d.ts.map +1 -0
- package/dist/lifecycle/release-store.js +318 -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/activity-registry.d.ts +6 -3
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-registry.js +7 -5
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/discover.js +14 -7
- 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 +12 -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/run-event-store.d.ts +77 -0
- package/dist/run-event-store.d.ts.map +1 -0
- package/dist/run-event-store.js +204 -0
- package/dist/run-event-stream.d.ts +132 -0
- package/dist/run-event-stream.d.ts.map +1 -0
- package/dist/run-event-stream.js +238 -0
- package/dist/run-event.d.ts +267 -0
- package/dist/run-event.d.ts.map +1 -0
- package/dist/run-event.js +207 -0
- package/dist/wave-runtime.mjs +225 -242
- 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 +28 -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/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 +273 -13
- package/dist/workspace/ci-green.d.ts.map +1 -1
- package/dist/workspace/ci-green.js +449 -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 +19 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.js +1 -1
- package/dist/workspace/declaration.schema.json +12 -0
- 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.d.ts +6 -1
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/intent.js +51 -1
- 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/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 +2 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/reason-codes.js +4 -1
- package/dist/workspace/releases.d.ts +72 -0
- package/dist/workspace/releases.d.ts.map +1 -0
- package/dist/workspace/releases.js +94 -0
- package/dist/workspace/releases.schema.json +391 -0
- package/dist/workspace/run-event.schema.json +288 -0
- package/dist/workspace/status-gates.d.ts +26 -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 +39 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/status.js +48 -1
- package/dist/workspace/status.schema.json +70 -1
- 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/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/change-set-graph.test.ts +72 -0
- package/src/change-set-graph.ts +133 -0
- package/src/change-set.ts +76 -6
- package/src/chant-version.ts +16 -0
- package/src/cli/commands/build.ts +19 -8
- 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/compiled-cli.e2e.test.ts +31 -6
- package/src/cli/handlers/approve-release.test.ts +80 -0
- package/src/cli/handlers/approve.ts +18 -2
- package/src/cli/handlers/build.test.ts +52 -0
- package/src/cli/handlers/build.ts +29 -0
- package/src/cli/handlers/change-set.ts +49 -1
- package/src/cli/handlers/fan-out.test.ts +48 -0
- package/src/cli/handlers/fan-out.ts +15 -3
- package/src/cli/handlers/operator.test.ts +37 -0
- package/src/cli/handlers/pr.test.ts +194 -0
- package/src/cli/handlers/pr.ts +168 -34
- package/src/cli/handlers/promote.test.ts +389 -5
- package/src/cli/handlers/promote.ts +279 -20
- package/src/cli/handlers/release-implicit.e2e.test.ts +153 -0
- package/src/cli/handlers/release.e2e.test.ts +85 -0
- package/src/cli/handlers/release.ts +206 -0
- package/src/cli/handlers/run-auto-release.test.ts +71 -0
- package/src/cli/handlers/run-events-cmd.test.ts +92 -0
- package/src/cli/handlers/run.test.ts +30 -0
- package/src/cli/handlers/run.ts +176 -15
- package/src/cli/main.test.ts +7 -0
- package/src/cli/main.ts +88 -13
- 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 +37 -3
- package/src/cli/registry.ts +29 -3
- package/src/cli/run-events.ts +54 -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 +21 -0
- package/src/components/auto-release.ts +6 -1
- package/src/components/cli-support.test.ts +82 -0
- package/src/components/cli-support.ts +16 -9
- package/src/components/driver.ts +129 -56
- package/src/components/fan-out-run.ts +56 -35
- 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 +26 -1
- package/src/components/release-compile.test.ts +147 -0
- package/src/components/release-compile.ts +445 -0
- package/src/components/release-gate.test.ts +215 -0
- package/src/components/release-gate.ts +341 -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 +1 -1
- package/src/config.ts +7 -1
- package/src/index.ts +2 -0
- package/src/lifecycle/change-set-store.test.ts +217 -0
- package/src/lifecycle/change-set-store.ts +112 -0
- package/src/lifecycle/commit-sha.ts +23 -0
- package/src/lifecycle/gate-change-set.e2e.test.ts +122 -0
- package/src/lifecycle/gate-change-set.ts +133 -0
- package/src/lifecycle/gate-ledger.test.ts +33 -0
- package/src/lifecycle/gate-ledger.ts +66 -1
- package/src/lifecycle/release-cut-signed.test.ts +180 -0
- package/src/lifecycle/release-cut.test.ts +411 -0
- package/src/lifecycle/release-cut.ts +367 -0
- package/src/lifecycle/release-ledger.test.ts +14 -0
- package/src/lifecycle/release-ledger.ts +85 -17
- package/src/lifecycle/release-store.ts +423 -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/activity-registry.ts +7 -5
- package/src/op/commit-run-gate-deploy-join.test.ts +117 -0
- package/src/op/discover-root.test.ts +39 -0
- package/src/op/discover.ts +15 -8
- 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 +13 -2
- package/src/point-decision.ts +12 -2
- package/src/run-event-store.test.ts +144 -0
- package/src/run-event-store.ts +213 -0
- package/src/run-event-stream.test.ts +175 -0
- package/src/run-event-stream.ts +306 -0
- package/src/run-event.test.ts +277 -0
- package/src/run-event.ts +451 -0
- package/src/wave-loop-golden.test.ts +9 -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 +28 -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/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 +624 -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 +12 -0
- package/src/workspace/declaration.ts +22 -2
- package/src/workspace/identity-gate-rule.ts +91 -7
- package/src/workspace/identity.ts +14 -1
- package/src/workspace/intent.ts +48 -1
- 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/promotion-order.ts +97 -0
- package/src/workspace/read-contract.e2e.test.ts +4 -1
- package/src/workspace/reason-codes.test.ts +2 -1
- package/src/workspace/reason-codes.ts +4 -1
- package/src/workspace/releases.schema.json +391 -0
- package/src/workspace/releases.test.ts +108 -0
- package/src/workspace/releases.ts +122 -0
- package/src/workspace/run-event.schema.json +288 -0
- package/src/workspace/status-contract.test.ts +52 -1
- package/src/workspace/status-gates.ts +56 -1
- package/src/workspace/status-stewards.ts +6 -0
- package/src/workspace/status.schema.json +70 -1
- package/src/workspace/status.ts +80 -1
- 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/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/components/driver.ts
CHANGED
|
@@ -56,7 +56,9 @@ import type { PendingGateRecord } from "../lifecycle/gate-ledger";
|
|
|
56
56
|
import type { CapabilityRegistry, DeployContext } from "./capability";
|
|
57
57
|
import { isPromotedArtifact } from "./auto-release";
|
|
58
58
|
import type { ReleaseIdentity } from "../telemetry-attribution";
|
|
59
|
-
import type { RunProgressEvent
|
|
59
|
+
import type { RunProgressEvent } from "./run-progress";
|
|
60
|
+
import type { RunEventSettleStatus } from "../run-event";
|
|
61
|
+
import { gateEventsOf, joinRunEventSinks, progressSinkOf, type RunEventSink } from "../run-event-stream";
|
|
60
62
|
|
|
61
63
|
export type { RunProgressEvent } from "./run-progress";
|
|
62
64
|
|
|
@@ -153,7 +155,16 @@ export interface DriverStepRecord {
|
|
|
153
155
|
output?: unknown;
|
|
154
156
|
error?: string;
|
|
155
157
|
/** Set on a `gate` step that passed (#2119): who resolved it, when, and at what address. */
|
|
156
|
-
approval?: {
|
|
158
|
+
approval?: {
|
|
159
|
+
gate: string;
|
|
160
|
+
resolvedBy: string;
|
|
161
|
+
timestamp: string;
|
|
162
|
+
url?: string;
|
|
163
|
+
/** The gate ledger the approval is in, when it isn't the component's own: a release gate's (#4045). */
|
|
164
|
+
op?: string;
|
|
165
|
+
/** Set when the gate passed over an empty change set (#4045) rather than on a person's approval. */
|
|
166
|
+
empty?: true;
|
|
167
|
+
};
|
|
157
168
|
}
|
|
158
169
|
|
|
159
170
|
export interface DriverComponentResult {
|
|
@@ -378,8 +389,8 @@ class GateStop extends Error {
|
|
|
378
389
|
}
|
|
379
390
|
}
|
|
380
391
|
|
|
381
|
-
/** A result status as the
|
|
382
|
-
function progressStatus(status: DriverComponentResult["status"]):
|
|
392
|
+
/** A result status as the event stream spells it — the only difference is `fail` vs `failed`, which the two vocabularies have always disagreed on. */
|
|
393
|
+
function progressStatus(status: DriverComponentResult["status"]): RunEventSettleStatus {
|
|
383
394
|
return status === "fail" ? "failed" : status;
|
|
384
395
|
}
|
|
385
396
|
|
|
@@ -395,6 +406,27 @@ export interface GateContext {
|
|
|
395
406
|
releases?: ReadonlyMap<string, string>;
|
|
396
407
|
/** This component's plan digest. Set by {@link runComponentDeploy}, never by a caller. */
|
|
397
408
|
planDigest?: string;
|
|
409
|
+
/**
|
|
410
|
+
* The release gate binding (#4045): set by a promote under a release.
|
|
411
|
+
* Every gate the run reaches is then decided in `op`'s ledger against the
|
|
412
|
+
* change-set document's digest, the release and the environment, in place
|
|
413
|
+
* of the component's own ledger and composition digest.
|
|
414
|
+
*/
|
|
415
|
+
releaseGate?: ReleaseGateBinding;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* What a release gate binds (#4045, ws-104 question 2): the change-set
|
|
420
|
+
* document's digest, the release, and (from the run) the environment.
|
|
421
|
+
*/
|
|
422
|
+
export interface ReleaseGateBinding {
|
|
423
|
+
/** The gate ledger the release gate is recorded in, for every component of the promote. */
|
|
424
|
+
op: string;
|
|
425
|
+
/** The digest of the change-set document planned for this release and environment. */
|
|
426
|
+
planDigest: string;
|
|
427
|
+
releaseDigest: string;
|
|
428
|
+
/** Reads the document stored under a digest and says whether it is empty (`evaluateGate`'s `verifyEmpty`). */
|
|
429
|
+
verifyEmpty?: (planDigest: string) => Promise<boolean>;
|
|
398
430
|
}
|
|
399
431
|
|
|
400
432
|
/** Run a single capability step. Never throws for a capability-run failure; returns a fail record instead. */
|
|
@@ -451,14 +483,16 @@ async function runCapabilityStep(
|
|
|
451
483
|
* gate is a read of the ledger rather than work, and starting activities a
|
|
452
484
|
* pending gate is about to strand defeats the point of stopping at it.
|
|
453
485
|
*
|
|
454
|
-
* `
|
|
455
|
-
* step runs, a `step`
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
*
|
|
486
|
+
* `emit`, when supplied, gets the phase's run events (#4041): `phase-start`
|
|
487
|
+
* before any step runs, a `step-start` and a `step-settle` around each gate
|
|
488
|
+
* and each capability invocation, the gate's `gate-resolved` or
|
|
489
|
+
* `gate-waiting`, a `skipped` settle with `skip: "not-run"` for each step a
|
|
490
|
+
* failure or a gate stopped, and `phase-settle` once the phase settles. It
|
|
491
|
+
* is observation only, never consulted for control flow. Recursion into a
|
|
492
|
+
* nested fan-out phase passes the same sink through, so a nested phase's
|
|
493
|
+
* events use its own name. `--progress-json` reads the same events through
|
|
494
|
+
* `progressOfRunEvent` (../run-event-stream.ts), which keeps the older
|
|
495
|
+
* stream's shape.
|
|
462
496
|
*/
|
|
463
497
|
async function runPhase(
|
|
464
498
|
phaseDef: DriverPhase,
|
|
@@ -467,22 +501,34 @@ async function runPhase(
|
|
|
467
501
|
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
468
502
|
componentOutputs: Record<string, Record<string, unknown>>,
|
|
469
503
|
gates: GateContext,
|
|
470
|
-
|
|
504
|
+
emit?: RunEventSink,
|
|
471
505
|
): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> {
|
|
506
|
+
/** Where a step of this phase sits, for its events. */
|
|
507
|
+
const stepAt = (entry: DriverStep | DriverGate | DriverPhase, step: string) => ({
|
|
508
|
+
component: ctx.component,
|
|
509
|
+
phase: phaseDef.phase,
|
|
510
|
+
step,
|
|
511
|
+
index: phaseDef.steps.indexOf(entry),
|
|
512
|
+
});
|
|
472
513
|
/** Decide this phase's gates against the ledger, in authored order, before any of its work starts. Throws `GateStop` at the first one nobody has approved. */
|
|
473
514
|
const decideGates = async (): Promise<DriverStepRecord[]> => {
|
|
474
515
|
const gateRecords: DriverStepRecord[] = [];
|
|
475
516
|
for (const gate of phaseDef.steps.filter(isGateStep)) {
|
|
476
517
|
const start = Date.now();
|
|
477
518
|
const base = { component: ctx.component, phase: phaseDef.phase, kind: `gate:${gateName(gate)}` };
|
|
519
|
+
const at = stepAt(gate, base.kind);
|
|
520
|
+
emit?.({ type: "step-start", ...at });
|
|
478
521
|
let check: Awaited<ReturnType<typeof evaluateGate>>;
|
|
479
522
|
try {
|
|
480
523
|
check = await evaluateGate(gates.port, {
|
|
481
|
-
op: ctx.component,
|
|
524
|
+
op: gates.releaseGate?.op ?? ctx.component,
|
|
482
525
|
gate: gateName(gate),
|
|
483
526
|
// #2574: an approval counts only for this environment and this plan.
|
|
484
527
|
environment: ctx.env,
|
|
485
528
|
...(gates.planDigest !== undefined ? { planDigest: gates.planDigest } : {}),
|
|
529
|
+
// #4045: and under a release, only for that release.
|
|
530
|
+
...(gates.releaseGate ? { releaseDigest: gates.releaseGate.releaseDigest } : {}),
|
|
531
|
+
...(gates.releaseGate?.verifyEmpty ? { verifyEmpty: gates.releaseGate.verifyEmpty } : {}),
|
|
486
532
|
...(gate.description ? { description: gate.description } : {}),
|
|
487
533
|
...(gate.timeout ? { timeout: gate.timeout } : {}),
|
|
488
534
|
...(gates.now ? { now: gates.now } : {}),
|
|
@@ -492,11 +538,22 @@ async function runPhase(
|
|
|
492
538
|
// executor's gate step does, and the component unwinds like any failure.
|
|
493
539
|
if (!(err instanceof GateNotRecordableError)) throw err;
|
|
494
540
|
gateRecords.push({ ...base, status: "fail" as const, durationMs: Date.now() - start, error: err.message });
|
|
541
|
+
emit?.({ type: "step-settle", ...at, status: "failed", durationMs: Date.now() - start, error: err.message });
|
|
495
542
|
for (const skipped of phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s))) {
|
|
496
543
|
gateRecords.push(skippedRecord(skipped));
|
|
497
544
|
}
|
|
498
545
|
throw new StepFailure(gateRecords, []);
|
|
499
546
|
}
|
|
547
|
+
for (const body of gateEventsOf(
|
|
548
|
+
check,
|
|
549
|
+
{ ...at, op: gates.releaseGate?.op ?? ctx.component, gate: gateName(gate) },
|
|
550
|
+
{
|
|
551
|
+
durationMs: Date.now() - start,
|
|
552
|
+
...(!check.satisfied && check.mismatch ? { message: "an approval stands for this gate, but for another environment or plan" } : {}),
|
|
553
|
+
},
|
|
554
|
+
)) {
|
|
555
|
+
emit?.(body);
|
|
556
|
+
}
|
|
500
557
|
if (!check.satisfied) {
|
|
501
558
|
gateRecords.push({ ...base, status: "skipped" as const, durationMs: Date.now() - start });
|
|
502
559
|
for (const skipped of phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s))) {
|
|
@@ -513,6 +570,8 @@ async function runPhase(
|
|
|
513
570
|
resolvedBy: check.resolution.resolvedBy,
|
|
514
571
|
timestamp: check.resolution.timestamp,
|
|
515
572
|
...(check.resolution.url ? { url: check.resolution.url } : {}),
|
|
573
|
+
...(gates.releaseGate ? { op: gates.releaseGate.op } : {}),
|
|
574
|
+
...(check.via === "empty" ? { empty: true as const } : {}),
|
|
516
575
|
},
|
|
517
576
|
});
|
|
518
577
|
}
|
|
@@ -534,7 +593,7 @@ async function runPhase(
|
|
|
534
593
|
}> => {
|
|
535
594
|
if (isPhaseStep(entry)) {
|
|
536
595
|
try {
|
|
537
|
-
const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs, gates,
|
|
596
|
+
const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs, gates, emit);
|
|
538
597
|
return { ...nested, failed: false };
|
|
539
598
|
} catch (err) {
|
|
540
599
|
if (err instanceof StepFailure) return { records: err.records, executed: err.executed, failed: true };
|
|
@@ -554,7 +613,8 @@ async function runPhase(
|
|
|
554
613
|
throw err;
|
|
555
614
|
}
|
|
556
615
|
}
|
|
557
|
-
|
|
616
|
+
const at = stepAt(entry, entry.kind);
|
|
617
|
+
emit?.({ type: "step-start", ...at });
|
|
558
618
|
const { record, resolvedInput, output } = await runCapabilityStep(
|
|
559
619
|
entry,
|
|
560
620
|
phaseDef.phase,
|
|
@@ -563,12 +623,11 @@ async function runPhase(
|
|
|
563
623
|
phaseOutputs,
|
|
564
624
|
componentOutputs,
|
|
565
625
|
);
|
|
566
|
-
|
|
567
|
-
type: "step",
|
|
568
|
-
|
|
569
|
-
phase: phaseDef.phase,
|
|
570
|
-
step: entry.kind,
|
|
626
|
+
emit?.({
|
|
627
|
+
type: "step-settle",
|
|
628
|
+
...at,
|
|
571
629
|
status: record.status === "ok" ? "ok" : "failed",
|
|
630
|
+
durationMs: record.durationMs,
|
|
572
631
|
...(record.error !== undefined ? { error: record.error } : {}),
|
|
573
632
|
});
|
|
574
633
|
if (record.status === "ok") {
|
|
@@ -586,13 +645,12 @@ async function runPhase(
|
|
|
586
645
|
};
|
|
587
646
|
};
|
|
588
647
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
phase:
|
|
592
|
-
|
|
593
|
-
status: "skipped",
|
|
594
|
-
|
|
595
|
-
});
|
|
648
|
+
/** A step a failure or a gate stopped before it started: recorded, and settled `skipped` with `skip: "not-run"`. */
|
|
649
|
+
const skippedRecord = (entry: DriverStep | DriverPhase): DriverStepRecord => {
|
|
650
|
+
const kind = isPhaseStep(entry) ? entry.phase : (entry as DriverStep).kind;
|
|
651
|
+
emit?.({ type: "step-settle", ...stepAt(entry, kind), status: "skipped", skip: "not-run", durationMs: null });
|
|
652
|
+
return { component: ctx.component, phase: phaseDef.phase, kind, status: "skipped", durationMs: 0 };
|
|
653
|
+
};
|
|
596
654
|
|
|
597
655
|
const runEntries = async (): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> => {
|
|
598
656
|
const gateRecords = await decideGates();
|
|
@@ -626,16 +684,16 @@ async function runPhase(
|
|
|
626
684
|
return { records, executed };
|
|
627
685
|
};
|
|
628
686
|
|
|
629
|
-
|
|
687
|
+
emit?.({ type: "phase-start", component: ctx.component, phase: phaseDef.phase });
|
|
630
688
|
try {
|
|
631
689
|
const result = await runEntries();
|
|
632
|
-
|
|
690
|
+
emit?.({ type: "phase-settle", component: ctx.component, phase: phaseDef.phase, status: "ok" });
|
|
633
691
|
return result;
|
|
634
692
|
} catch (err) {
|
|
635
693
|
if (err instanceof GateStop) {
|
|
636
|
-
|
|
694
|
+
emit?.({ type: "phase-settle", component: ctx.component, phase: phaseDef.phase, status: "gated" });
|
|
637
695
|
} else if (err instanceof StepFailure) {
|
|
638
|
-
|
|
696
|
+
emit?.({ type: "phase-settle", component: ctx.component, phase: phaseDef.phase, status: "failed" });
|
|
639
697
|
}
|
|
640
698
|
throw err;
|
|
641
699
|
}
|
|
@@ -710,14 +768,14 @@ async function rollbackExecuted(
|
|
|
710
768
|
* any) are recorded into `componentOutputs` under its own name so downstream
|
|
711
769
|
* components can reference `@<name>.publish.*`.
|
|
712
770
|
*
|
|
713
|
-
* `onProgress
|
|
714
|
-
*
|
|
715
|
-
* `
|
|
716
|
-
* `
|
|
717
|
-
* unwind step-by-step compensation
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
771
|
+
* `onProgress` (the older `RunProgressEvent` stream) and `onRunEvent` (run
|
|
772
|
+
* events, #4041), when supplied, are forwarded to every `runPhase` call (both
|
|
773
|
+
* the forward `deploy` phases and, on failure, the component's own authored
|
|
774
|
+
* `rollback` phases), so a consumer sees the phase and step events of
|
|
775
|
+
* whichever phases actually ran. The saga unwind step-by-step compensation
|
|
776
|
+
* (`rollbackExecuted` below) stays silent: it isn't a `deploy` phase, and
|
|
777
|
+
* its record statuses (`rolled-back`/`rollback-opted-out`) are not step
|
|
778
|
+
* statuses either stream has.
|
|
721
779
|
*/
|
|
722
780
|
export async function runComponentDeploy(
|
|
723
781
|
component: DriverComponent,
|
|
@@ -726,7 +784,9 @@ export async function runComponentDeploy(
|
|
|
726
784
|
componentOutputs: Record<string, Record<string, unknown>>,
|
|
727
785
|
onProgress?: (event: RunProgressEvent) => void,
|
|
728
786
|
gateContext: GateContext = { port: gitGateLedgerPort() },
|
|
787
|
+
onRunEvent?: RunEventSink,
|
|
729
788
|
): Promise<DriverComponentResult> {
|
|
789
|
+
const emit = joinRunEventSinks(onRunEvent, progressSinkOf(onProgress));
|
|
730
790
|
// Each component tracks its own release: a digest one component promoted never stamps another's deploy.
|
|
731
791
|
if (ctx.release) ctx = { ...ctx, release: { ...ctx.release } };
|
|
732
792
|
const phaseOutputs: Record<string, Record<string, unknown>> = {};
|
|
@@ -738,19 +798,22 @@ export async function runComponentDeploy(
|
|
|
738
798
|
if (ctx.release && release !== undefined && ctx.release.version === undefined) ctx.release.version = release;
|
|
739
799
|
const gates: GateContext = {
|
|
740
800
|
...gateContext,
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
801
|
+
// #4045: a release gate binds the release's change set, not this composition.
|
|
802
|
+
planDigest:
|
|
803
|
+
gateContext.releaseGate?.planDigest ??
|
|
804
|
+
componentPlanDigest({
|
|
805
|
+
environment: ctx.env,
|
|
806
|
+
component,
|
|
807
|
+
...(ctx.vars ? { vars: ctx.vars } : {}),
|
|
808
|
+
...(release !== undefined ? { release } : {}),
|
|
809
|
+
}),
|
|
747
810
|
};
|
|
748
811
|
const records: DriverStepRecord[] = [];
|
|
749
812
|
const allExecuted: ExecutedStep[] = [];
|
|
750
813
|
|
|
751
814
|
try {
|
|
752
815
|
for (const phaseDef of component.deploy) {
|
|
753
|
-
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates,
|
|
816
|
+
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates, emit);
|
|
754
817
|
records.push(...result.records);
|
|
755
818
|
allExecuted.push(...result.executed);
|
|
756
819
|
}
|
|
@@ -784,7 +847,7 @@ export async function runComponentDeploy(
|
|
|
784
847
|
|
|
785
848
|
for (const phaseDef of [...(component.rollback ?? [])].reverse()) {
|
|
786
849
|
try {
|
|
787
|
-
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates,
|
|
850
|
+
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates, emit);
|
|
788
851
|
records.push(...result.records);
|
|
789
852
|
} catch (compErr) {
|
|
790
853
|
if (compErr instanceof StepFailure || compErr instanceof GateStop) records.push(...compErr.records);
|
|
@@ -919,12 +982,20 @@ export interface InterpretRunOptions {
|
|
|
919
982
|
* byte-for-byte the same as before this option existed.
|
|
920
983
|
*/
|
|
921
984
|
onProgress?: (event: RunProgressEvent) => void;
|
|
985
|
+
/**
|
|
986
|
+
* Where the run's events go (#4041, `chant run --components --events-json`):
|
|
987
|
+
* the run-event bodies the stream in ../run-event-stream.ts stamps. Like
|
|
988
|
+
* `onProgress`, never consulted for control flow.
|
|
989
|
+
*/
|
|
990
|
+
onRunEvent?: RunEventSink;
|
|
922
991
|
/** Where a `gate` step's facts are read and written (#2119). Defaults to the `chant/lifecycle` orphan branch; a test passes `memoryGateLedgerPort()` from `../op/gate.ts`. */
|
|
923
992
|
gates?: GateLedgerPort;
|
|
924
993
|
/** ISO-8601 "now" for gate decisions, so a test is deterministic. */
|
|
925
994
|
now?: string;
|
|
926
995
|
/** The recorded digest a promote or rollback deploys, per component (#2574). Part of the plan a gate approval is bound to. */
|
|
927
996
|
releases?: ReadonlyMap<string, string>;
|
|
997
|
+
/** A promote under a release (#4045): what every gate the run reaches binds. See {@link GateContext.releaseGate}. */
|
|
998
|
+
releaseGate?: ReleaseGateBinding;
|
|
928
999
|
/**
|
|
929
1000
|
* The release each component deploys (#3061, ws-081), threaded into its
|
|
930
1001
|
* `DeployContext.release`. The driver fills in `version` when a publish
|
|
@@ -969,14 +1040,15 @@ export async function runInterpretDriver(
|
|
|
969
1040
|
const { order, waves } = resolveComponentGraph(components);
|
|
970
1041
|
const byName = new Map(components.map((c) => [c.name, c]));
|
|
971
1042
|
const componentOutputs: Record<string, Record<string, unknown>> = { ...(options.componentOutputs ?? {}) };
|
|
972
|
-
const
|
|
1043
|
+
const emit = joinRunEventSinks(options.onRunEvent, progressSinkOf(options.onProgress));
|
|
973
1044
|
const gates: GateContext = {
|
|
974
1045
|
port: options.gates ?? gitGateLedgerPort(),
|
|
975
1046
|
...(options.now ? { now: options.now } : {}),
|
|
976
1047
|
...(options.releases ? { releases: options.releases } : {}),
|
|
1048
|
+
...(options.releaseGate ? { releaseGate: options.releaseGate } : {}),
|
|
977
1049
|
};
|
|
978
1050
|
|
|
979
|
-
|
|
1051
|
+
emit?.({ type: "run-start", waves });
|
|
980
1052
|
|
|
981
1053
|
const results: DriverComponentResult[] = [];
|
|
982
1054
|
let failedComponent: string | undefined;
|
|
@@ -984,26 +1056,27 @@ export async function runInterpretDriver(
|
|
|
984
1056
|
|
|
985
1057
|
waveLoop: for (const [waveIndex, wave] of waves.entries()) {
|
|
986
1058
|
const waveNum = waveIndex + 1;
|
|
987
|
-
|
|
1059
|
+
emit?.({ type: "wave-start", wave: waveNum, components: wave });
|
|
988
1060
|
const waveComponents = wave.map((name) => byName.get(name)!);
|
|
989
1061
|
const waveResults = await Promise.all(
|
|
990
1062
|
waveComponents.map(async (component) => {
|
|
991
|
-
|
|
1063
|
+
emit?.({ type: "component-start", wave: waveNum, component: component.name });
|
|
992
1064
|
const result = await runComponentDeploy(
|
|
993
1065
|
component,
|
|
994
1066
|
deployContext(options, component.name),
|
|
995
1067
|
registry,
|
|
996
1068
|
componentOutputs,
|
|
997
|
-
|
|
1069
|
+
undefined,
|
|
998
1070
|
gates,
|
|
1071
|
+
emit,
|
|
999
1072
|
);
|
|
1000
|
-
|
|
1073
|
+
emit?.({ type: "component-settle", wave: waveNum, component: component.name, status: progressStatus(result.status) });
|
|
1001
1074
|
return result;
|
|
1002
1075
|
}),
|
|
1003
1076
|
);
|
|
1004
1077
|
results.push(...waveResults);
|
|
1005
1078
|
const stopped = waveResults.find((r) => r.status !== "ok");
|
|
1006
|
-
|
|
1079
|
+
emit?.({ type: "wave-settle", wave: waveNum, status: progressStatus(stopped?.status ?? "ok") });
|
|
1007
1080
|
if (stopped) {
|
|
1008
1081
|
// A failure in the same wave outranks a gate: something is actually
|
|
1009
1082
|
// broken, and reporting the run as "waiting on a human" would hide it.
|
|
@@ -1016,7 +1089,7 @@ export async function runInterpretDriver(
|
|
|
1016
1089
|
|
|
1017
1090
|
const status: DriverRunResult["status"] = failedComponent ? "fail" : gated ? "gated" : "ok";
|
|
1018
1091
|
const ok = status === "ok";
|
|
1019
|
-
|
|
1092
|
+
emit?.({ type: "run-settle", status: progressStatus(status), durationMs: null });
|
|
1020
1093
|
const result: DriverRunResult = {
|
|
1021
1094
|
order, waves, results, ok, status, failedComponent, componentOutputs,
|
|
1022
1095
|
...(gated
|
|
@@ -49,12 +49,18 @@ import type { ReleaseIdentity } from "../telemetry-attribution";
|
|
|
49
49
|
import { downstreamWithin, remainingFanOut, type FanOutPlan, type FanOutProgress, type FanOutSkip } from "./fan-out";
|
|
50
50
|
import { evaluateGate, gitGateLedgerPort, type GateCheck, type GateLedgerPort } from "../op/gate";
|
|
51
51
|
import type { RunProgressEvent } from "./run-progress";
|
|
52
|
+
import { emitGateStep, joinRunEventSinks, progressSinkOf, type RunEventSink } from "../run-event-stream";
|
|
52
53
|
import type { CapabilityRegistry } from "./capability";
|
|
53
54
|
import type { PendingGateRecord } from "../lifecycle/gate-ledger";
|
|
54
55
|
import type { DeployContext } from "./capability";
|
|
55
56
|
import { planWaveComponent, type WaveComponentPlan } from "./wave-plan";
|
|
56
57
|
import { decideWaveGate, runWaveLoop, waveGateName, waveSetDigest, type WaveMember, type WaveRecord } from "../wave-engine";
|
|
57
58
|
|
|
59
|
+
/** The sink a fan-out emits to: run events, and the older stream through them. */
|
|
60
|
+
function fanOutSink(options: FanOutRunOptions): RunEventSink | undefined {
|
|
61
|
+
return joinRunEventSinks(options.onRunEvent, progressSinkOf(options.onProgress));
|
|
62
|
+
}
|
|
63
|
+
|
|
58
64
|
/**
|
|
59
65
|
* One gate per wave (#3049), each bound to its wave's set digest. The other
|
|
60
66
|
* mode beside {@link FanOutGate}: that one approves the whole set once, this
|
|
@@ -115,6 +121,12 @@ export interface FanOutRunOptions {
|
|
|
115
121
|
/** Outputs of components this run is not running: the ones the plan lists in `seeds`. */
|
|
116
122
|
componentOutputs?: Record<string, Record<string, unknown>>;
|
|
117
123
|
onProgress?: (event: RunProgressEvent) => void;
|
|
124
|
+
/**
|
|
125
|
+
* Where the run's events go (#4041, `--events-json`). A gate over the set
|
|
126
|
+
* or over a wave is a `gate:<name>` step of a `gate` phase with no
|
|
127
|
+
* component, settled before the components it lets through start.
|
|
128
|
+
*/
|
|
129
|
+
onRunEvent?: RunEventSink;
|
|
118
130
|
gates?: GateLedgerPort;
|
|
119
131
|
now?: string;
|
|
120
132
|
/** One approval over the ordered set, bound to `plan.digest`. */
|
|
@@ -206,18 +218,25 @@ export async function runFanOut(
|
|
|
206
218
|
return runGatedWaves(plan, active, components, registry, options, options.waveGate, gates, componentOutputs);
|
|
207
219
|
}
|
|
208
220
|
|
|
221
|
+
const emit = fanOutSink(options);
|
|
222
|
+
emit?.({ type: "run-start", waves: active.waves });
|
|
223
|
+
|
|
209
224
|
if (options.gate) {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
225
|
+
const gate = options.gate;
|
|
226
|
+
const check = await emitGateStep(emit, gate, () =>
|
|
227
|
+
evaluateGate(gates.port, {
|
|
228
|
+
op: gate.op,
|
|
229
|
+
gate: gate.gate,
|
|
230
|
+
planDigest: active.digest,
|
|
231
|
+
...(gate.description ? { description: gate.description } : {}),
|
|
232
|
+
...(gate.timeout ? { timeout: gate.timeout } : {}),
|
|
233
|
+
...(options.now ? { now: options.now } : {}),
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
218
236
|
if (!check.satisfied) {
|
|
219
237
|
// Nothing ran, so nothing is half-applied and there is nothing to
|
|
220
238
|
// compensate. A gate is a fact, not a failure.
|
|
239
|
+
emit?.({ type: "run-settle", status: "gated", durationMs: null });
|
|
221
240
|
return {
|
|
222
241
|
plan: active,
|
|
223
242
|
status: "gated",
|
|
@@ -246,29 +265,28 @@ export async function runFanOut(
|
|
|
246
265
|
}
|
|
247
266
|
};
|
|
248
267
|
|
|
249
|
-
options.onProgress?.({ type: "run-start", waves: active.waves });
|
|
250
|
-
|
|
251
268
|
for (const [waveIndex, wave] of active.waves.entries()) {
|
|
252
269
|
const waveNum = waveIndex + 1;
|
|
253
270
|
// A component whose upstream failed in an earlier wave is not dispatched.
|
|
254
271
|
const runnable = wave.filter((name) => inPlan.has(name) && !blockedBy.has(name));
|
|
255
272
|
if (runnable.length === 0) continue;
|
|
256
273
|
|
|
257
|
-
|
|
274
|
+
emit?.({ type: "wave-start", wave: waveNum, components: runnable });
|
|
258
275
|
const waveResults = await Promise.all(
|
|
259
276
|
runnable.map(async (name) => {
|
|
260
|
-
|
|
277
|
+
emit?.({ type: "component-start", wave: waveNum, component: name });
|
|
261
278
|
const planned = options.plans?.[name];
|
|
262
279
|
const result = await runComponentDeploy(
|
|
263
280
|
byName.get(name)!,
|
|
264
281
|
{ ...fanOutContext(options, name), ...(planned && Object.keys(planned).length > 0 ? { plans: planned } : {}) },
|
|
265
282
|
registry,
|
|
266
283
|
componentOutputs,
|
|
267
|
-
|
|
284
|
+
undefined,
|
|
268
285
|
gates,
|
|
286
|
+
emit,
|
|
269
287
|
);
|
|
270
|
-
|
|
271
|
-
type: "component-
|
|
288
|
+
emit?.({
|
|
289
|
+
type: "component-settle",
|
|
272
290
|
wave: waveNum,
|
|
273
291
|
component: name,
|
|
274
292
|
status: result.status === "fail" ? "failed" : result.status,
|
|
@@ -297,8 +315,8 @@ export async function runFanOut(
|
|
|
297
315
|
}
|
|
298
316
|
|
|
299
317
|
const waveFailed = waveResults.some((r) => r.status === "fail");
|
|
300
|
-
|
|
301
|
-
type: "wave-
|
|
318
|
+
emit?.({
|
|
319
|
+
type: "wave-settle",
|
|
302
320
|
wave: waveNum,
|
|
303
321
|
status: waveFailed ? "failed" : waveResults.some((r) => r.status === "gated") ? "gated" : "ok",
|
|
304
322
|
});
|
|
@@ -311,7 +329,7 @@ export async function runFanOut(
|
|
|
311
329
|
// A failure outranks a gate: something is actually broken, and reporting the
|
|
312
330
|
// run as "waiting on a human" would hide it. Same precedence the driver uses.
|
|
313
331
|
const status: FanOutRunResult["status"] = failed.length > 0 ? "fail" : gatedFact ? "gated" : "ok";
|
|
314
|
-
|
|
332
|
+
emit?.({ type: "run-settle", status: status === "fail" ? "failed" : status, durationMs: null });
|
|
315
333
|
|
|
316
334
|
return {
|
|
317
335
|
plan: active,
|
|
@@ -391,7 +409,8 @@ async function runGatedWaves(
|
|
|
391
409
|
}
|
|
392
410
|
};
|
|
393
411
|
|
|
394
|
-
|
|
412
|
+
const emit = fanOutSink(options);
|
|
413
|
+
emit?.({ type: "run-start", waves: plan.waves });
|
|
395
414
|
|
|
396
415
|
/** What planning one wave gave. */
|
|
397
416
|
interface Planned {
|
|
@@ -477,14 +496,16 @@ async function runGatedWaves(
|
|
|
477
496
|
return { end: "next" };
|
|
478
497
|
}
|
|
479
498
|
|
|
480
|
-
const check = await
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
499
|
+
const check = await emitGateStep(emit, { op: waveGate.op, gate: gateName }, () =>
|
|
500
|
+
decideWaveGate(gates.port, {
|
|
501
|
+
op: waveGate.op,
|
|
502
|
+
gate: gateName,
|
|
503
|
+
digest,
|
|
504
|
+
description: waveGate.description ?? `wave ${waveNum} of ${plan.waves.length}: ${toRun.join(", ")}`,
|
|
505
|
+
...(waveGate.timeout ? { timeout: waveGate.timeout } : {}),
|
|
506
|
+
...(options.now ? { now: options.now } : {}),
|
|
507
|
+
}),
|
|
508
|
+
);
|
|
488
509
|
if (!check.satisfied) {
|
|
489
510
|
// Nothing in this wave ran, and no later wave is planned: its plan
|
|
490
511
|
// would read outputs this wave has not written.
|
|
@@ -502,15 +523,15 @@ async function runGatedWaves(
|
|
|
502
523
|
},
|
|
503
524
|
|
|
504
525
|
async apply({ k: waveNum }, { contexts, plansFor, waveFailures }, { check, record, toRun }) {
|
|
505
|
-
|
|
526
|
+
emit?.({ type: "wave-start", wave: waveNum, components: toRun });
|
|
506
527
|
const waveResults = await Promise.all(
|
|
507
528
|
toRun.map(async (name) => {
|
|
508
|
-
|
|
529
|
+
emit?.({ type: "component-start", wave: waveNum, component: name });
|
|
509
530
|
const plans = plansFor.get(name)!;
|
|
510
531
|
const ctx = { ...contexts.get(name)!, ...(Object.keys(plans).length > 0 ? { plans } : {}) };
|
|
511
|
-
const result = await runComponentDeploy(byName.get(name)!, ctx, registry, componentOutputs,
|
|
512
|
-
|
|
513
|
-
type: "component-
|
|
532
|
+
const result = await runComponentDeploy(byName.get(name)!, ctx, registry, componentOutputs, undefined, gates, emit);
|
|
533
|
+
emit?.({
|
|
534
|
+
type: "component-settle",
|
|
514
535
|
wave: waveNum,
|
|
515
536
|
component: name,
|
|
516
537
|
status: result.status === "fail" ? "failed" : result.status,
|
|
@@ -543,8 +564,8 @@ async function runGatedWaves(
|
|
|
543
564
|
};
|
|
544
565
|
waves.push(settled);
|
|
545
566
|
options.onWaveSettled?.(settled);
|
|
546
|
-
|
|
547
|
-
type: "wave-
|
|
567
|
+
emit?.({
|
|
568
|
+
type: "wave-settle",
|
|
548
569
|
wave: waveNum,
|
|
549
570
|
status: waveFailures.length > 0 ? "failed" : waveResults.some((r) => r.status === "gated") ? "gated" : "ok",
|
|
550
571
|
});
|
|
@@ -557,7 +578,7 @@ async function runGatedWaves(
|
|
|
557
578
|
.map(([component, by]) => ({ component, reason: "blocked" as const, blockedBy: by }));
|
|
558
579
|
|
|
559
580
|
const status: FanOutRunResult["status"] = failed.length > 0 ? "fail" : gatedFact ? "gated" : "ok";
|
|
560
|
-
|
|
581
|
+
emit?.({ type: "run-settle", status: status === "fail" ? "failed" : status, durationMs: null });
|
|
561
582
|
|
|
562
583
|
return {
|
|
563
584
|
// The derivation's waves, not the narrowed re-layering: their numbers name the gates.
|