@intentius/chant 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/catalog.d.ts +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +20 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +14 -8
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/lineage.d.ts +10 -0
- package/dist/audit/lineage.d.ts.map +1 -0
- package/dist/audit/prior-art.d.ts +385 -0
- package/dist/audit/prior-art.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +16 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +8 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +3 -0
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +15 -0
- package/dist/audit/secrets.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +46 -0
- package/dist/audit/terraform-state.d.ts.map +1 -0
- package/dist/build.d.ts +17 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/claimed-fields.d.ts +161 -0
- package/dist/claimed-fields.d.ts.map +1 -0
- package/dist/cli/build-params-cli.d.ts +2 -2
- package/dist/cli/command-group.d.ts +14 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +17 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +2 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +82 -16
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -42
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts +28 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts +16 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resources/context.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +26 -9
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
- package/dist/components/auto-release.d.ts +10 -20
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +4 -4
- package/dist/components/cli-support.d.ts +20 -21
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +3 -3
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver-output.d.ts.map +1 -1
- package/dist/components/driver.d.ts +66 -36
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/manifest-persistence.d.ts +1 -9
- package/dist/components/manifest-persistence.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +6 -6
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts +9 -15
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts +2 -2
- package/dist/deep-observation.d.ts +17 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/files.d.ts +4 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +33 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +2 -2
- package/dist/fold/foldable-helpers.d.ts +5 -1
- package/dist/fold/foldable-helpers.d.ts.map +1 -1
- package/dist/fold-provenance.d.ts +236 -0
- package/dist/fold-provenance.d.ts.map +1 -0
- package/dist/graph-ops.d.ts +1 -1
- package/dist/held-elsewhere.d.ts +85 -0
- package/dist/held-elsewhere.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +3 -3
- package/dist/lexicon-config.d.ts +4 -4
- package/dist/lexicon-plugin-helpers.d.ts +1 -1
- package/dist/lexicon.d.ts +104 -14
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +13 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-diff.d.ts +133 -6
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +71 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +2 -2
- package/dist/lifecycle/run-ledger.d.ts +53 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +3 -3
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/config.d.ts +81 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +55 -2
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +1 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +16 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
- package/dist/lint/rules/op/support.d.ts +12 -0
- package/dist/lint/rules/op/support.d.ts.map +1 -0
- package/dist/lint/suppressions.d.ts +108 -0
- package/dist/lint/suppressions.d.ts.map +1 -0
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/op/activities/activity-contracts.d.ts +54 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -0
- package/dist/op/activities/apply.d.ts +262 -0
- package/dist/op/activities/apply.d.ts.map +1 -0
- package/dist/op/activities/build.d.ts +13 -0
- package/dist/op/activities/build.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +110 -0
- package/dist/op/activities/converge.d.ts.map +1 -0
- package/dist/op/activities/env-teardown.d.ts +76 -0
- package/dist/op/activities/env-teardown.d.ts.map +1 -0
- package/dist/op/activities/guard-validate.d.ts +64 -0
- package/dist/op/activities/guard-validate.d.ts.map +1 -0
- package/dist/op/activities/http-check.d.ts +34 -0
- package/dist/op/activities/http-check.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +31 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
- package/dist/op/activities/lifecycle.d.ts +42 -0
- package/dist/op/activities/lifecycle.d.ts.map +1 -0
- package/dist/op/activities/pipeline-audit.d.ts +84 -0
- package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
- package/dist/op/activities/policy.d.ts +17 -0
- package/dist/op/activities/policy.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +89 -0
- package/dist/op/activities/reconcile.d.ts.map +1 -0
- package/dist/op/activities/shell.d.ts +13 -0
- package/dist/op/activities/shell.d.ts.map +1 -0
- package/dist/op/activities/teardown.d.ts +10 -0
- package/dist/op/activities/teardown.d.ts.map +1 -0
- package/dist/op/activities/util.d.ts +7 -0
- package/dist/op/activities/util.d.ts.map +1 -0
- package/dist/op/activities/wait.d.ts +28 -0
- package/dist/op/activities/wait.d.ts.map +1 -0
- package/dist/op/activities/workflow-audit.d.ts +92 -0
- package/dist/op/activities/workflow-audit.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts +76 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +25 -18
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-failure.d.ts +28 -0
- package/dist/op/activity-failure.d.ts.map +1 -0
- package/dist/op/activity-profiles.d.ts +118 -0
- package/dist/op/activity-profiles.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +22 -38
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +3 -14
- package/dist/op/activity-runtime.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -29
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +113 -0
- package/dist/op/composites/apply-op.d.ts.map +1 -0
- package/dist/op/composites/converge-op.d.ts +120 -0
- package/dist/op/composites/converge-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +26 -0
- package/dist/op/composites/index.d.ts.map +1 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
- package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
- package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
- package/dist/op/composites/reconcile-op.d.ts +57 -0
- package/dist/op/composites/reconcile-op.d.ts.map +1 -0
- package/dist/op/composites/watch-op.d.ts +60 -0
- package/dist/op/composites/watch-op.d.ts.map +1 -0
- package/dist/op/composites/workflow-audit-op.d.ts +57 -0
- package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
- package/dist/op/converge-rule.d.ts +12 -10
- package/dist/op/converge-rule.d.ts.map +1 -1
- package/dist/op/cron.d.ts +51 -0
- package/dist/op/cron.d.ts.map +1 -0
- package/dist/op/discover.d.ts +9 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/duration.d.ts +11 -0
- package/dist/op/duration.d.ts.map +1 -0
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate.d.ts +90 -0
- package/dist/op/gate.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +9 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +23 -7
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +114 -28
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +11 -1
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +184 -0
- package/dist/op/op-ir.d.ts.map +1 -0
- package/dist/op/op-verb-class.d.ts +10 -2
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +30 -5
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/receipt-store.d.ts +1 -1
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/resource.d.ts +15 -1
- package/dist/op/resource.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +200 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtimes/local.d.ts +29 -0
- package/dist/op/runtimes/local.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +15 -13
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/types.d.ts +79 -27
- package/dist/op/types.d.ts.map +1 -1
- package/dist/ownership.d.ts +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/audit/catalog.test.ts +79 -3
- package/src/audit/catalog.ts +111 -13
- package/src/audit/core.test.ts +26 -0
- package/src/audit/core.ts +61 -17
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +91 -15
- package/src/audit/lineage.ts +100 -0
- package/src/audit/prior-art.ts +153 -0
- package/src/audit/proof.test.ts +67 -0
- package/src/audit/proof.ts +70 -0
- package/src/audit/report-html.ts +9 -4
- package/src/audit/report-model.ts +16 -2
- package/src/audit/report.test.ts +33 -0
- package/src/audit/report.ts +5 -3
- package/src/audit/rules-doc.test.ts +34 -2
- package/src/audit/rules-doc.ts +82 -9
- package/src/audit/secrets.ts +15 -3
- package/src/audit/terraform-state.test.ts +144 -0
- package/src/audit/terraform-state.ts +131 -0
- package/src/build.ts +31 -1
- package/src/claimed-fields.test.ts +145 -0
- package/src/claimed-fields.ts +208 -0
- package/src/cli/build-params-cli.ts +2 -2
- package/src/cli/command-group.ts +14 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
- package/src/cli/commands/audit.test.ts +189 -11
- package/src/cli/commands/audit.ts +73 -27
- package/src/cli/commands/build.test.ts +292 -9
- package/src/cli/commands/build.ts +156 -24
- package/src/cli/commands/check-lexicon-docs.ts +1 -1
- package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
- package/src/cli/commands/check-lexicon-examples.ts +42 -10
- package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
- package/src/cli/commands/init-lexicon.test.ts +97 -2
- package/src/cli/commands/init-lexicon.ts +4 -2
- package/src/cli/commands/init.test.ts +35 -1
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/lint.test.ts +48 -0
- package/src/cli/commands/lint.ts +123 -0
- package/src/cli/commands/onboard.test.ts +75 -0
- package/src/cli/commands/onboard.ts +54 -23
- package/src/cli/handlers/build.test.ts +81 -1
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/graph.test.ts +3 -3
- package/src/cli/handlers/lifecycle.test.ts +195 -12
- package/src/cli/handlers/lifecycle.ts +123 -6
- package/src/cli/handlers/misc.ts +1 -1
- package/src/cli/handlers/operator.test.ts +183 -11
- package/src/cli/handlers/operator.ts +260 -55
- package/src/cli/handlers/run.test.ts +431 -1287
- package/src/cli/handlers/run.ts +365 -915
- package/src/cli/main.test.ts +23 -15
- package/src/cli/main.ts +88 -33
- package/src/cli/mcp/op-tools.ts +147 -88
- package/src/cli/mcp/resource-handlers.ts +10 -37
- package/src/cli/mcp/resources/context.ts +11 -12
- package/src/cli/mcp/server.test.ts +25 -27
- package/src/cli/mcp/server.ts +2 -2
- package/src/cli/registry.ts +26 -9
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.ts +1 -1
- package/src/components/README.md +3 -2
- package/src/components/auto-release.test.ts +4 -20
- package/src/components/auto-release.ts +17 -42
- package/src/components/capability.ts +4 -4
- package/src/components/cli-support.test.ts +10 -5
- package/src/components/cli-support.ts +52 -44
- package/src/components/component.schema.json +2 -2
- package/src/components/component.ts +3 -3
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +92 -22
- package/src/components/driver.ts +193 -83
- package/src/components/index.ts +1 -6
- package/src/components/manifest-persistence.test.ts +2 -20
- package/src/components/manifest-persistence.ts +4 -24
- package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
- package/src/components/run-progress.ts +6 -6
- package/src/components/verbs/run-agent.ts +11 -17
- package/src/components/verbs/verify.ts +2 -2
- package/src/deep-observation.test.ts +75 -0
- package/src/deep-observation.ts +48 -0
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.ts +105 -2
- package/src/discovery/param-deps.test.ts +112 -1
- package/src/discovery/param-deps.ts +193 -0
- package/src/discovery/sandbox/bundle.ts +15 -1
- package/src/discovery/sandbox/config-wire.test.ts +2 -2
- package/src/discovery/sandbox/config-wire.ts +2 -2
- package/src/fold/fold.test.ts +56 -0
- package/src/fold/foldable-helpers.ts +81 -1
- package/src/fold-provenance-exclusion.test.ts +186 -0
- package/src/fold-provenance.test.ts +495 -0
- package/src/fold-provenance.ts +441 -0
- package/src/graph-ops.test.ts +2 -2
- package/src/graph-ops.ts +1 -1
- package/src/held-elsewhere.test.ts +102 -0
- package/src/held-elsewhere.ts +114 -0
- package/src/index.ts +3 -0
- package/src/kubectl-context.ts +3 -3
- package/src/lexicon-config.test.ts +5 -5
- package/src/lexicon-config.ts +4 -4
- package/src/lexicon-plugin-helpers.ts +1 -1
- package/src/lexicon.ts +120 -14
- package/src/lifecycle/change-set.test.ts +49 -0
- package/src/lifecycle/change-set.ts +67 -0
- package/src/lifecycle/converge-ledger.ts +1 -1
- package/src/lifecycle/deep-diff.test.ts +267 -14
- package/src/lifecycle/deep-diff.ts +217 -14
- package/src/lifecycle/deep-observe.test.ts +3 -1
- package/src/lifecycle/deep-observe.ts +16 -6
- package/src/lifecycle/gate-ledger.test.ts +74 -2
- package/src/lifecycle/gate-ledger.ts +143 -36
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/release-ledger.ts +2 -2
- package/src/lifecycle/run-ledger.test.ts +268 -0
- package/src/lifecycle/run-ledger.ts +201 -0
- package/src/lifecycle/symptoms.ts +3 -3
- package/src/lint/config.test.ts +82 -1
- package/src/lint/config.ts +112 -3
- package/src/lint/policy.test.ts +1 -1
- package/src/lint/post-synth.test.ts +31 -0
- package/src/lint/post-synth.ts +54 -1
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
- package/src/lint/rules/comp/comp.test.ts +1 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
- package/src/lint/rules/comp/index.ts +3 -3
- package/src/lint/rules/op/index.ts +23 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
- package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
- package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
- package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
- package/src/lint/rules/op/support.ts +11 -0
- package/src/lint/suppressions.test.ts +163 -0
- package/src/lint/suppressions.ts +204 -0
- package/src/live-endpoint.ts +1 -1
- package/src/op/activities/activity-contracts.ts +66 -0
- package/src/op/activities/apply.test.ts +567 -0
- package/src/op/activities/apply.ts +662 -0
- package/src/op/activities/build.ts +26 -0
- package/src/op/activities/converge.test.ts +330 -0
- package/src/op/activities/converge.ts +449 -0
- package/src/op/activities/env-teardown.test.ts +187 -0
- package/src/op/activities/env-teardown.ts +171 -0
- package/src/op/activities/guard-validate.test.ts +139 -0
- package/src/op/activities/guard-validate.ts +223 -0
- package/src/op/activities/http-check.test.ts +48 -0
- package/src/op/activities/http-check.ts +55 -0
- package/src/op/activities/index.ts +119 -0
- package/src/op/activities/lexicon-upgrade.test.ts +492 -0
- package/src/op/activities/lexicon-upgrade.ts +673 -0
- package/src/op/activities/lifecycle.ts +85 -0
- package/src/op/activities/pipeline-audit.test.ts +67 -0
- package/src/op/activities/pipeline-audit.ts +201 -0
- package/src/op/activities/policy.test.ts +19 -0
- package/src/op/activities/policy.ts +37 -0
- package/src/op/activities/reconcile.test.ts +81 -0
- package/src/op/activities/reconcile.ts +187 -0
- package/src/op/activities/shell.ts +26 -0
- package/src/op/activities/teardown.ts +22 -0
- package/src/op/activities/util.ts +6 -0
- package/src/op/activities/wait.ts +69 -0
- package/src/op/activities/workflow-audit.test.ts +90 -0
- package/src/op/activities/workflow-audit.ts +203 -0
- package/src/op/activity-contract-registry.test.ts +98 -0
- package/src/op/activity-contract-registry.ts +114 -0
- package/src/op/activity-contract.test.ts +1 -1
- package/src/op/activity-contract.ts +37 -33
- package/src/op/activity-failure.ts +34 -0
- package/src/op/activity-profiles.test.ts +56 -0
- package/src/op/activity-profiles.ts +116 -0
- package/src/op/activity-registry.test.ts +37 -28
- package/src/op/activity-registry.ts +27 -56
- package/src/op/activity-runtime.ts +3 -47
- package/src/op/builders-exports.test.ts +0 -49
- package/src/op/builders-typed.test.ts +164 -0
- package/src/op/builders.ts +123 -39
- package/src/op/composites/apply-op.ts +208 -0
- package/src/op/composites/composites.test.ts +366 -0
- package/src/op/composites/converge-op.test.ts +168 -0
- package/src/op/composites/converge-op.ts +214 -0
- package/src/op/composites/index.ts +26 -0
- package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
- package/src/op/composites/lexicon-upgrade-op.ts +134 -0
- package/src/op/composites/pipeline-audit-op.test.ts +25 -0
- package/src/op/composites/pipeline-audit-op.ts +78 -0
- package/src/op/composites/reconcile-op.test.ts +51 -0
- package/src/op/composites/reconcile-op.ts +107 -0
- package/src/op/composites/watch-op.ts +108 -0
- package/src/op/composites/workflow-audit-op.test.ts +25 -0
- package/src/op/composites/workflow-audit-op.ts +88 -0
- package/src/op/converge-rule.ts +12 -10
- package/src/op/cron.test.ts +128 -0
- package/src/op/cron.ts +149 -0
- package/src/op/discover-root.test.ts +105 -1
- package/src/op/discover.test.ts +6 -2
- package/src/op/discover.ts +84 -16
- package/src/op/duration.ts +21 -0
- package/src/op/effect-step.test.ts +26 -11
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate.ts +155 -0
- package/src/op/generate-pipeline.test.ts +65 -3
- package/src/op/generate-pipeline.ts +19 -2
- package/src/op/index.ts +39 -6
- package/src/op/local-executor.test.ts +163 -34
- package/src/op/local-executor.ts +370 -102
- package/src/op/local-output.test.ts +95 -11
- package/src/op/local-output.ts +33 -4
- package/src/op/op-ir.test.ts +281 -0
- package/src/op/op-ir.ts +417 -0
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +26 -5
- package/src/op/operator.test.ts +175 -6
- package/src/op/operator.ts +63 -16
- package/src/op/receipt-store.ts +1 -1
- package/src/op/resource.ts +20 -2
- package/src/op/runtime.ts +203 -0
- package/src/op/runtimes/local.test.ts +181 -0
- package/src/op/runtimes/local.ts +212 -0
- package/src/op/step-output-ref.ts +15 -13
- package/src/op/types.ts +84 -24
- package/src/ownership.ts +1 -1
- package/src/provenance.test.ts +6 -0
- package/src/provenance.ts +21 -1
- package/src/serializer-walker.test.ts +42 -0
- package/src/serializer-walker.ts +21 -2
- package/dist/cli/handlers/op-progress.d.ts +0 -90
- package/dist/cli/handlers/op-progress.d.ts.map +0 -1
- package/dist/cli/handlers/run-client.d.ts +0 -127
- package/dist/cli/handlers/run-client.d.ts.map +0 -1
- package/dist/cli/handlers/run-report.d.ts +0 -5
- package/dist/cli/handlers/run-report.d.ts.map +0 -1
- package/dist/components/temporal-codegen-loader.d.ts +0 -27
- package/dist/components/temporal-codegen-loader.d.ts.map +0 -1
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +0 -37
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +0 -1
- package/src/cli/handlers/op-progress.test.ts +0 -268
- package/src/cli/handlers/op-progress.ts +0 -248
- package/src/cli/handlers/run-client.test.ts +0 -82
- package/src/cli/handlers/run-client.ts +0 -219
- package/src/cli/handlers/run-report.test.ts +0 -62
- package/src/cli/handlers/run-report.ts +0 -122
- package/src/components/temporal-codegen-loader.ts +0 -41
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +0 -66
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* implementations are a separate later issue (#557). These tests exercise the
|
|
6
6
|
* driver's generic behavior only: dependency ordering + parallel-safe waves,
|
|
7
7
|
* sequential vs parallel step execution, `onFailure`/saga rollback in reverse
|
|
8
|
-
* order, output wiring (`@Phase.field` and `@<component>.publish.*`), and
|
|
9
|
-
*
|
|
8
|
+
* order, output wiring (`@Phase.field` and `@<component>.publish.*`), and
|
|
9
|
+
* gate-as-fact on the local executor (#2119). Several cases replay the real pilot
|
|
10
10
|
* declarations (../pilots/*.pilot.ts) as realistic inputs.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { describe, expect, it } from "vitest";
|
|
14
14
|
import { CapabilityRegistry, type DeployContext } from "./capability";
|
|
15
15
|
import { stubCapability } from "./verbs/stub";
|
|
16
|
+
import { memoryGateLedgerPort } from "../op/gate";
|
|
16
17
|
import {
|
|
17
18
|
DependencyCycleError,
|
|
18
|
-
DriverGateUnsupportedError,
|
|
19
19
|
DriverRunFailure,
|
|
20
20
|
UnknownDependencyError,
|
|
21
21
|
accumulateComponentOutputs,
|
|
@@ -210,32 +210,102 @@ describe("runComponentDeploy — sequential vs parallel", () => {
|
|
|
210
210
|
});
|
|
211
211
|
});
|
|
212
212
|
|
|
213
|
-
describe("runComponentDeploy — gate
|
|
214
|
-
|
|
213
|
+
describe("runComponentDeploy — gate as fact (#2119)", () => {
|
|
214
|
+
const NOW = "2026-09-05T12:00:00.000Z";
|
|
215
|
+
|
|
216
|
+
const gatedComponent = (): DriverComponent => ({
|
|
217
|
+
name: "neo4j-cluster",
|
|
218
|
+
deploy: [
|
|
219
|
+
{ phase: "Prepare", steps: [{ kind: "cfn-deploy" }] },
|
|
220
|
+
{ phase: "Node 1", steps: [{ kind: "gate", signalName: "approve-node-1" }, { kind: "code-deploy" }] },
|
|
221
|
+
],
|
|
222
|
+
rollback: [{ phase: "Undo", steps: [{ kind: "cfn-deploy" }] }],
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
function registryWithCalls() {
|
|
215
226
|
const registry = new CapabilityRegistry();
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
const calls: string[] = [];
|
|
228
|
+
const rolledBack: string[] = [];
|
|
229
|
+
for (const kind of ["cfn-deploy", "code-deploy"]) {
|
|
230
|
+
registry.register(
|
|
231
|
+
fakeCapability(kind, {
|
|
232
|
+
run: () => { calls.push(kind); return { ok: true }; },
|
|
233
|
+
rollback: () => { rolledBack.push(kind); },
|
|
234
|
+
}).capability,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return { registry, calls, rolledBack };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
it("ends gated, records the pending fact, and runs nothing after the gate", async () => {
|
|
241
|
+
const { registry, calls, rolledBack } = registryWithCalls();
|
|
242
|
+
const port = memoryGateLedgerPort();
|
|
243
|
+
const result = await runComponentDeploy(
|
|
244
|
+
gatedComponent(), { env: "dev", component: "neo4j-cluster" }, registry, {}, undefined,
|
|
245
|
+
{ port, now: NOW },
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
expect(result.status).toBe("gated");
|
|
249
|
+
expect(result.ok).toBe(false);
|
|
250
|
+
expect(result.gate).toMatchObject({ op: "neo4j-cluster", gate: "approve-node-1" });
|
|
251
|
+
expect(port.appended).toHaveLength(1);
|
|
252
|
+
expect(calls).toEqual(["cfn-deploy"]);
|
|
253
|
+
// A gate is not a failure: nothing unwinds and no `rollback` phase runs.
|
|
254
|
+
expect(rolledBack).toEqual([]);
|
|
255
|
+
expect(result.records.map((r) => [r.kind, r.status])).toEqual([
|
|
256
|
+
["cfn-deploy", "ok"],
|
|
257
|
+
["gate:approve-node-1", "skipped"],
|
|
258
|
+
["code-deploy", "skipped"],
|
|
259
|
+
]);
|
|
228
260
|
});
|
|
229
261
|
|
|
230
|
-
it("
|
|
262
|
+
it("a resolution newer than the pending fact passes the gate and carries the approver", async () => {
|
|
263
|
+
const { registry, calls } = registryWithCalls();
|
|
264
|
+
const port = memoryGateLedgerPort({
|
|
265
|
+
pending: [{
|
|
266
|
+
version: 1, kind: "pending", op: "neo4j-cluster", gate: "approve-node-1",
|
|
267
|
+
timestamp: "2026-09-05T10:00:00.000Z", expiresAt: "2026-09-07T10:00:00.000Z",
|
|
268
|
+
}],
|
|
269
|
+
resolutions: [{
|
|
270
|
+
version: 1, op: "neo4j-cluster", gate: "approve-node-1",
|
|
271
|
+
resolvedBy: "alex", timestamp: "2026-09-05T11:00:00.000Z",
|
|
272
|
+
}],
|
|
273
|
+
});
|
|
274
|
+
const result = await runComponentDeploy(
|
|
275
|
+
gatedComponent(), { env: "dev", component: "neo4j-cluster" }, registry, {}, undefined,
|
|
276
|
+
{ port, now: NOW },
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(result.status).toBe("ok");
|
|
280
|
+
expect(calls).toEqual(["cfn-deploy", "code-deploy"]);
|
|
281
|
+
expect(result.records.find((r) => r.kind === "gate:approve-node-1")?.approval)
|
|
282
|
+
.toEqual({ gate: "approve-node-1", resolvedBy: "alex", timestamp: "2026-09-05T11:00:00.000Z" });
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("the neo4j pilot's gated Node 1 phase ends the run pending approval", async () => {
|
|
231
286
|
const registry = new CapabilityRegistry();
|
|
232
287
|
for (const kind of ["cfn-deploy", "code-deploy", "wait-cluster-healthy"]) {
|
|
233
288
|
registry.register(fakeCapability(kind, { run: () => ({ ok: true }) }).capability);
|
|
234
289
|
}
|
|
235
290
|
const json = projectToJson(neo4jCluster) as unknown as DriverComponent;
|
|
236
|
-
|
|
237
|
-
|
|
291
|
+
const result = await runComponentDeploy(
|
|
292
|
+
json, { env: "dev", component: "neo4j-cluster" }, registry, {}, undefined,
|
|
293
|
+
{ port: memoryGateLedgerPort(), now: NOW },
|
|
238
294
|
);
|
|
295
|
+
expect(result.status).toBe("gated");
|
|
296
|
+
expect(result.gate?.gate).toBe("approve-neo4j-node-1");
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("a gated component stops the whole interpret run without throwing", async () => {
|
|
300
|
+
const { registry } = registryWithCalls();
|
|
301
|
+
const run = await runInterpretDriver([gatedComponent()], registry, {
|
|
302
|
+
env: "dev",
|
|
303
|
+
gates: memoryGateLedgerPort(),
|
|
304
|
+
now: NOW,
|
|
305
|
+
});
|
|
306
|
+
expect(run.status).toBe("gated");
|
|
307
|
+
expect(run.gatedComponent).toBe("neo4j-cluster");
|
|
308
|
+
expect(run.gate?.gate).toBe("approve-node-1");
|
|
239
309
|
});
|
|
240
310
|
});
|
|
241
311
|
|
|
@@ -415,7 +485,7 @@ describe("runComponentDeploy — output wiring", () => {
|
|
|
415
485
|
|
|
416
486
|
describe("collectComponentOutputs / accumulateComponentOutputs — the shared accumulator (#700)", () => {
|
|
417
487
|
// Exported so the durable path's `accumulateComponentOutputs` activity
|
|
418
|
-
// (
|
|
488
|
+
// (a hosting runtime's own step executor) captures outputs via
|
|
419
489
|
// the same function `runComponentDeploy` does — the accumulation twin of
|
|
420
490
|
// the already-shared `resolveStepInput`.
|
|
421
491
|
const phaseOutputs = {
|
|
@@ -446,7 +516,7 @@ describe("collectComponentOutputs / accumulateComponentOutputs — the shared ac
|
|
|
446
516
|
publish: { uri: "repo@sha256:abc", digest: "sha256:abc" },
|
|
447
517
|
});
|
|
448
518
|
expect(out.other).toEqual({ x: 1 });
|
|
449
|
-
// Plain data throughout — survives
|
|
519
|
+
// Plain data throughout — survives an out-of-process JSON boundary unchanged.
|
|
450
520
|
expect(JSON.parse(JSON.stringify(out))).toEqual(out);
|
|
451
521
|
});
|
|
452
522
|
|
package/src/components/driver.ts
CHANGED
|
@@ -11,11 +11,18 @@
|
|
|
11
11
|
* component.schema.json (`@Phase.field` prior-step references,
|
|
12
12
|
* `@<component>.publish.<field>` cross-component artifact references, and
|
|
13
13
|
* passes through `$env.*`/`stackOutput` values as opaque env config), runs
|
|
14
|
-
* `parallel` phases concurrently,
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* `parallel` phases concurrently, decides a `gate` against the gate ledger
|
|
15
|
+
* (#2119 — the same `../op/gate.ts` the Op executor uses, so `chant run
|
|
16
|
+
* --components` and `chant run <op>` can never disagree about what a gate
|
|
17
|
+
* means), and on terminal failure unwinds every executed step in reverse via
|
|
18
|
+
* that step's capability `rollback`, then runs `onFailure` phases in reverse
|
|
19
|
+
* order (best-effort), mirroring the Op local executor's saga semantics.
|
|
20
|
+
*
|
|
21
|
+
* A gate nobody has approved is not a failure: the component stops there with
|
|
22
|
+
* status `gated`, the pending fact is on the ledger, and no rollback and no
|
|
23
|
+
* `onFailure` phase runs — nothing failed and the steps that did run are the
|
|
24
|
+
* steps the author meant to run before the approval. The next run re-reads the
|
|
25
|
+
* ledger and continues past the gate if `chant approve` has answered it.
|
|
19
26
|
*
|
|
20
27
|
* A step whose capability declares no `rollback` is never silently passed
|
|
21
28
|
* over during unwind: it gets a `"rollback-opted-out"` record (see
|
|
@@ -25,15 +32,13 @@
|
|
|
25
32
|
* already requires at author time — the driver reports the same fact the
|
|
26
33
|
* lint rule already made a component author state.
|
|
27
34
|
*
|
|
28
|
-
* This unwind runs entirely in-process: if the process crashes mid-rollback
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* progress* is Temporal-only, the same boundary that already applies to
|
|
36
|
-
* forward `onFailure` compensation on an Op.
|
|
35
|
+
* This unwind runs entirely in-process: if the process crashes mid-rollback,
|
|
36
|
+
* nothing resumes it — there is no persisted run state to resume from (see
|
|
37
|
+
* docs/components/orchestration.mdx#rollback-comes-free). The *mechanism*
|
|
38
|
+
* (reverse-order unwind calling each capability's `rollback`) is
|
|
39
|
+
* capability-agnostic; *durable resume of a rollback already in progress*
|
|
40
|
+
* would be a hosting runtime's to offer, the same boundary that already
|
|
41
|
+
* applies to forward `onFailure` compensation on an Op.
|
|
37
42
|
*
|
|
38
43
|
* Contains zero per-component logic: nothing in this module branches on a
|
|
39
44
|
* component or capability `kind`/name. All behavior is either generic
|
|
@@ -42,8 +47,10 @@
|
|
|
42
47
|
*/
|
|
43
48
|
|
|
44
49
|
import { topoSort } from "../codegen/topo-sort";
|
|
50
|
+
import { evaluateGate, gitGateLedgerPort, type GateLedgerPort } from "../op/gate";
|
|
51
|
+
import type { PendingGateRecord } from "../lifecycle/gate-ledger";
|
|
45
52
|
import type { CapabilityRegistry, DeployContext } from "./capability";
|
|
46
|
-
import type { RunProgressEvent } from "./run-progress";
|
|
53
|
+
import type { RunProgressEvent, RunProgressStatus } from "./run-progress";
|
|
47
54
|
|
|
48
55
|
export type { RunProgressEvent } from "./run-progress";
|
|
49
56
|
|
|
@@ -61,7 +68,7 @@ export interface DriverStep {
|
|
|
61
68
|
[param: string]: unknown;
|
|
62
69
|
}
|
|
63
70
|
|
|
64
|
-
/** A gate step —
|
|
71
|
+
/** A gate step — a human approval, decided against the gate ledger when the run reaches it (schema `Gate`). */
|
|
65
72
|
export interface DriverGate {
|
|
66
73
|
kind: "gate";
|
|
67
74
|
signalName: string;
|
|
@@ -96,20 +103,6 @@ function isPhaseStep(step: DriverStep | DriverGate | DriverPhase): step is Drive
|
|
|
96
103
|
|
|
97
104
|
// ── Errors ────────────────────────────────────────────────────────────────
|
|
98
105
|
|
|
99
|
-
/** Thrown when a component's composition contains a `gate` — gates need a durable runtime, matching chant's Op local executor. */
|
|
100
|
-
export class DriverGateUnsupportedError extends Error {
|
|
101
|
-
constructor(
|
|
102
|
-
public readonly component: string,
|
|
103
|
-
public readonly signalName: string,
|
|
104
|
-
) {
|
|
105
|
-
super(
|
|
106
|
-
`component "${component}": gate "${signalName}" is not supported on the local executor — ` +
|
|
107
|
-
`gates need a durable runtime. Re-run with a durable (Temporal) backend.`,
|
|
108
|
-
);
|
|
109
|
-
this.name = "DriverGateUnsupportedError";
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
106
|
/** Thrown when a dependency cycle is found among `dependsOn` edges. */
|
|
114
107
|
export class DependencyCycleError extends Error {
|
|
115
108
|
constructor(public readonly cycle: string[]) {
|
|
@@ -147,12 +140,18 @@ export interface DriverStepRecord {
|
|
|
147
140
|
durationMs: number;
|
|
148
141
|
output?: unknown;
|
|
149
142
|
error?: string;
|
|
143
|
+
/** Set on a `gate` step that passed (#2119): who resolved it, when, and at what address. */
|
|
144
|
+
approval?: { gate: string; resolvedBy: string; timestamp: string; url?: string };
|
|
150
145
|
}
|
|
151
146
|
|
|
152
147
|
export interface DriverComponentResult {
|
|
153
148
|
component: string;
|
|
154
149
|
ok: boolean;
|
|
150
|
+
/** Three-state outcome (#2119) — `gated` is `ok: false` without being a failure: the component stopped at a gate awaiting `chant approve`, and nothing was rolled back. */
|
|
151
|
+
status: "ok" | "fail" | "gated";
|
|
155
152
|
records: DriverStepRecord[];
|
|
153
|
+
/** Present when `status === "gated"`: the pending fact this component stopped on. */
|
|
154
|
+
gate?: PendingGateRecord;
|
|
156
155
|
}
|
|
157
156
|
|
|
158
157
|
export interface DriverRunResult {
|
|
@@ -162,8 +161,14 @@ export interface DriverRunResult {
|
|
|
162
161
|
waves: string[][];
|
|
163
162
|
results: DriverComponentResult[];
|
|
164
163
|
ok: boolean;
|
|
164
|
+
/** Three-state outcome (#2119), matching `OpRunResult.status`. */
|
|
165
|
+
status: "ok" | "fail" | "gated";
|
|
165
166
|
/** Name of the component that terminated the run, if any. */
|
|
166
167
|
failedComponent?: string;
|
|
168
|
+
/** Name of the component the run stopped at for approval, when `status === "gated"`. */
|
|
169
|
+
gatedComponent?: string;
|
|
170
|
+
/** The pending fact the run stopped on, when `status === "gated"`. */
|
|
171
|
+
gate?: PendingGateRecord;
|
|
167
172
|
/**
|
|
168
173
|
* The accumulated cross-component/cross-stack outputs after the run — each
|
|
169
174
|
* component's `publish` output and, for an applied stack, its `cfn-deploy`
|
|
@@ -285,12 +290,11 @@ export function resolveWiring(
|
|
|
285
290
|
* Deep-walk an object, resolving every string/stackOutput wiring value found.
|
|
286
291
|
* Arrays and nested objects are walked too.
|
|
287
292
|
*
|
|
288
|
-
* Exported (not just used internally) so
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
-
*
|
|
293
|
-
* place so local and durable execution can never silently diverge.
|
|
293
|
+
* Exported (not just used internally) so a hosting runtime that runs steps
|
|
294
|
+
* out of process can resolve a step's wiring the same way — the orchestrator
|
|
295
|
+
* only accumulates `phaseOutputs`/`componentOutputs` and passes them through;
|
|
296
|
+
* resolution logic stays in one place so local and hosted execution can never
|
|
297
|
+
* silently diverge.
|
|
294
298
|
*/
|
|
295
299
|
export function resolveStepInput(
|
|
296
300
|
input: Record<string, unknown>,
|
|
@@ -338,6 +342,29 @@ class StepFailure extends Error {
|
|
|
338
342
|
}
|
|
339
343
|
}
|
|
340
344
|
|
|
345
|
+
/** Internal: a phase stopped at an unapproved gate (#2119). Carries what ran before it, so the records survive; not a failure, so no unwind follows. */
|
|
346
|
+
class GateStop extends Error {
|
|
347
|
+
constructor(
|
|
348
|
+
public readonly records: DriverStepRecord[],
|
|
349
|
+
public readonly executed: ExecutedStep[],
|
|
350
|
+
public readonly pending: PendingGateRecord,
|
|
351
|
+
) {
|
|
352
|
+
super(`gate "${pending.gate}" is pending approval`);
|
|
353
|
+
this.name = "GateStop";
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** A result status as the progress stream spells it — the only difference is `fail` vs `failed`, which the two vocabularies have always disagreed on. */
|
|
358
|
+
function progressStatus(status: DriverComponentResult["status"]): RunProgressStatus {
|
|
359
|
+
return status === "fail" ? "failed" : status;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** How a component's run reaches the gate ledger — see `../op/gate.ts`. */
|
|
363
|
+
export interface GateContext {
|
|
364
|
+
port: GateLedgerPort;
|
|
365
|
+
now?: string;
|
|
366
|
+
}
|
|
367
|
+
|
|
341
368
|
/** Run a single capability step. Never throws for a capability-run failure; returns a fail record instead. */
|
|
342
369
|
async function runCapabilityStep(
|
|
343
370
|
step: DriverStep,
|
|
@@ -381,11 +408,16 @@ async function runCapabilityStep(
|
|
|
381
408
|
}
|
|
382
409
|
|
|
383
410
|
/**
|
|
384
|
-
* Run a phase's steps. A step may be a `Gate` (
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
*
|
|
388
|
-
* `
|
|
411
|
+
* Run a phase's steps. A step may be a `Gate` (decided against the gate
|
|
412
|
+
* ledger, in authored order — a resolution newer than the gate's newest
|
|
413
|
+
* pending fact lets it pass carrying the approver, anything else records the
|
|
414
|
+
* fact and stops the component) or a nested `Phase` (a fan-out unit; recursed
|
|
415
|
+
* into, inheriting `parallel` from its own definition, not its parent's).
|
|
416
|
+
* Steps run sequentially unless `phase.parallel` is set, in which case they run
|
|
417
|
+
* via `Promise.all`, matching ../op/local-executor.ts's phase semantics — and
|
|
418
|
+
* as there, a gate in a parallel phase is decided before the fan-out, since a
|
|
419
|
+
* gate is a read of the ledger rather than work, and starting activities a
|
|
420
|
+
* pending gate is about to strand defeats the point of stopping at it.
|
|
389
421
|
*
|
|
390
422
|
* `onProgress`, when supplied, is called with a `phase-start` event before any
|
|
391
423
|
* step runs, a `step` event around each capability invocation (`"running"`
|
|
@@ -402,22 +434,60 @@ async function runPhase(
|
|
|
402
434
|
registry: CapabilityRegistry,
|
|
403
435
|
phaseOutputs: Record<string, Record<string, unknown>>,
|
|
404
436
|
componentOutputs: Record<string, Record<string, unknown>>,
|
|
437
|
+
gates: GateContext,
|
|
405
438
|
onProgress?: (event: RunProgressEvent) => void,
|
|
406
439
|
): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> {
|
|
407
|
-
|
|
408
|
-
|
|
440
|
+
/** 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. */
|
|
441
|
+
const decideGates = async (): Promise<DriverStepRecord[]> => {
|
|
442
|
+
const gateRecords: DriverStepRecord[] = [];
|
|
443
|
+
for (const gate of phaseDef.steps.filter(isGateStep)) {
|
|
444
|
+
const start = Date.now();
|
|
445
|
+
const check = await evaluateGate(gates.port, {
|
|
446
|
+
op: ctx.component,
|
|
447
|
+
gate: gate.signalName,
|
|
448
|
+
...(gate.description ? { description: gate.description } : {}),
|
|
449
|
+
...(gate.timeout ? { timeout: gate.timeout } : {}),
|
|
450
|
+
...(gates.now ? { now: gates.now } : {}),
|
|
451
|
+
});
|
|
452
|
+
const base = { component: ctx.component, phase: phaseDef.phase, kind: `gate:${gate.signalName}` };
|
|
453
|
+
if (!check.satisfied) {
|
|
454
|
+
gateRecords.push({ ...base, status: "skipped" as const, durationMs: Date.now() - start });
|
|
455
|
+
for (const skipped of phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s))) {
|
|
456
|
+
gateRecords.push(skippedRecord(skipped));
|
|
457
|
+
}
|
|
458
|
+
throw new GateStop(gateRecords, [], check.pending);
|
|
459
|
+
}
|
|
460
|
+
gateRecords.push({
|
|
461
|
+
...base,
|
|
462
|
+
status: "ok" as const,
|
|
463
|
+
durationMs: Date.now() - start,
|
|
464
|
+
approval: {
|
|
465
|
+
gate: gate.signalName,
|
|
466
|
+
resolvedBy: check.resolution.resolvedBy,
|
|
467
|
+
timestamp: check.resolution.timestamp,
|
|
468
|
+
...(check.resolution.url ? { url: check.resolution.url } : {}),
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
return gateRecords;
|
|
473
|
+
};
|
|
409
474
|
|
|
410
475
|
const entries = phaseDef.steps.filter((s): s is DriverStep | DriverPhase => !isGateStep(s));
|
|
411
476
|
|
|
412
477
|
const runEntry = async (
|
|
413
478
|
entry: DriverStep | DriverPhase,
|
|
414
|
-
): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[]; failed: boolean }> => {
|
|
479
|
+
): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[]; failed: boolean; pending?: PendingGateRecord }> => {
|
|
415
480
|
if (isPhaseStep(entry)) {
|
|
416
481
|
try {
|
|
417
|
-
const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs, onProgress);
|
|
482
|
+
const nested = await runPhase(entry, ctx, registry, phaseOutputs, componentOutputs, gates, onProgress);
|
|
418
483
|
return { ...nested, failed: false };
|
|
419
484
|
} catch (err) {
|
|
420
485
|
if (err instanceof StepFailure) return { records: err.records, executed: err.executed, failed: true };
|
|
486
|
+
// A nested fan-out phase's gate stops the whole component, but the
|
|
487
|
+
// records it produced before the gate still belong in the run.
|
|
488
|
+
if (err instanceof GateStop) {
|
|
489
|
+
return { records: err.records, executed: err.executed, failed: false, pending: err.pending };
|
|
490
|
+
}
|
|
421
491
|
throw err;
|
|
422
492
|
}
|
|
423
493
|
}
|
|
@@ -448,32 +518,38 @@ async function runPhase(
|
|
|
448
518
|
};
|
|
449
519
|
};
|
|
450
520
|
|
|
521
|
+
const skippedRecord = (entry: DriverStep | DriverPhase): DriverStepRecord => ({
|
|
522
|
+
component: ctx.component,
|
|
523
|
+
phase: phaseDef.phase,
|
|
524
|
+
kind: isPhaseStep(entry) ? entry.phase : (entry as DriverStep).kind,
|
|
525
|
+
status: "skipped",
|
|
526
|
+
durationMs: 0,
|
|
527
|
+
});
|
|
528
|
+
|
|
451
529
|
const runEntries = async (): Promise<{ records: DriverStepRecord[]; executed: ExecutedStep[] }> => {
|
|
530
|
+
const gateRecords = await decideGates();
|
|
452
531
|
if (phaseDef.parallel) {
|
|
453
532
|
const results = await Promise.all(entries.map(runEntry));
|
|
454
|
-
const records = results.flatMap((r) => r.records);
|
|
533
|
+
const records = gateRecords.concat(results.flatMap((r) => r.records));
|
|
455
534
|
const executed = results.flatMap((r) => r.executed);
|
|
535
|
+
const pending = results.find((r) => r.pending)?.pending;
|
|
536
|
+
if (pending) throw new GateStop(records, executed, pending);
|
|
456
537
|
if (results.some((r) => r.failed)) throw new StepFailure(records, executed);
|
|
457
538
|
return { records, executed };
|
|
458
539
|
}
|
|
459
540
|
|
|
460
|
-
const records: DriverStepRecord[] = [];
|
|
541
|
+
const records: DriverStepRecord[] = [...gateRecords];
|
|
461
542
|
const executed: ExecutedStep[] = [];
|
|
462
543
|
for (let i = 0; i < entries.length; i++) {
|
|
463
544
|
const result = await runEntry(entries[i]);
|
|
464
545
|
records.push(...result.records);
|
|
465
546
|
executed.push(...result.executed);
|
|
547
|
+
if (result.pending) {
|
|
548
|
+
for (const skipped of entries.slice(i + 1)) records.push(skippedRecord(skipped));
|
|
549
|
+
throw new GateStop(records, executed, result.pending);
|
|
550
|
+
}
|
|
466
551
|
if (result.failed) {
|
|
467
|
-
for (const skipped of entries.slice(i + 1))
|
|
468
|
-
const skippedKind = isPhaseStep(skipped) ? skipped.phase : (skipped as DriverStep).kind;
|
|
469
|
-
records.push({
|
|
470
|
-
component: ctx.component,
|
|
471
|
-
phase: phaseDef.phase,
|
|
472
|
-
kind: skippedKind,
|
|
473
|
-
status: "skipped",
|
|
474
|
-
durationMs: 0,
|
|
475
|
-
});
|
|
476
|
-
}
|
|
552
|
+
for (const skipped of entries.slice(i + 1)) records.push(skippedRecord(skipped));
|
|
477
553
|
throw new StepFailure(records, executed);
|
|
478
554
|
}
|
|
479
555
|
}
|
|
@@ -486,7 +562,9 @@ async function runPhase(
|
|
|
486
562
|
onProgress?.({ type: "phase-done", component: ctx.component, phase: phaseDef.phase, status: "ok" });
|
|
487
563
|
return result;
|
|
488
564
|
} catch (err) {
|
|
489
|
-
if (err instanceof
|
|
565
|
+
if (err instanceof GateStop) {
|
|
566
|
+
onProgress?.({ type: "phase-done", component: ctx.component, phase: phaseDef.phase, status: "gated" });
|
|
567
|
+
} else if (err instanceof StepFailure) {
|
|
490
568
|
onProgress?.({ type: "phase-done", component: ctx.component, phase: phaseDef.phase, status: "failed" });
|
|
491
569
|
}
|
|
492
570
|
throw err;
|
|
@@ -577,6 +655,7 @@ export async function runComponentDeploy(
|
|
|
577
655
|
registry: CapabilityRegistry,
|
|
578
656
|
componentOutputs: Record<string, Record<string, unknown>>,
|
|
579
657
|
onProgress?: (event: RunProgressEvent) => void,
|
|
658
|
+
gates: GateContext = { port: gitGateLedgerPort() },
|
|
580
659
|
): Promise<DriverComponentResult> {
|
|
581
660
|
const phaseOutputs: Record<string, Record<string, unknown>> = {};
|
|
582
661
|
const records: DriverStepRecord[] = [];
|
|
@@ -584,11 +663,20 @@ export async function runComponentDeploy(
|
|
|
584
663
|
|
|
585
664
|
try {
|
|
586
665
|
for (const phaseDef of component.deploy) {
|
|
587
|
-
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, onProgress);
|
|
666
|
+
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates, onProgress);
|
|
588
667
|
records.push(...result.records);
|
|
589
668
|
allExecuted.push(...result.executed);
|
|
590
669
|
}
|
|
591
670
|
} catch (err) {
|
|
671
|
+
// A pending gate stops the component where it stands (#2119): no saga
|
|
672
|
+
// unwind and no `rollback` phase. The steps before the gate ran on
|
|
673
|
+
// purpose and are meant to survive the wait for an approval — undoing
|
|
674
|
+
// them would make every gated run a no-op with a rollback attached.
|
|
675
|
+
if (err instanceof GateStop) {
|
|
676
|
+
records.push(...err.records);
|
|
677
|
+
return { component: component.name, ok: false, status: "gated", records, gate: err.pending };
|
|
678
|
+
}
|
|
679
|
+
|
|
592
680
|
if (err instanceof StepFailure) {
|
|
593
681
|
records.push(...err.records);
|
|
594
682
|
allExecuted.push(...err.executed);
|
|
@@ -600,24 +688,24 @@ export async function runComponentDeploy(
|
|
|
600
688
|
|
|
601
689
|
for (const phaseDef of [...(component.rollback ?? [])].reverse()) {
|
|
602
690
|
try {
|
|
603
|
-
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, onProgress);
|
|
691
|
+
const result = await runPhase(phaseDef, ctx, registry, phaseOutputs, componentOutputs, gates, onProgress);
|
|
604
692
|
records.push(...result.records);
|
|
605
693
|
} catch (compErr) {
|
|
606
|
-
if (compErr instanceof StepFailure) records.push(...compErr.records);
|
|
694
|
+
if (compErr instanceof StepFailure || compErr instanceof GateStop) records.push(...compErr.records);
|
|
607
695
|
else throw compErr;
|
|
608
696
|
}
|
|
609
697
|
}
|
|
610
698
|
|
|
611
|
-
return { component: component.name, ok: false, records };
|
|
699
|
+
return { component: component.name, ok: false, status: "fail", records };
|
|
612
700
|
}
|
|
613
701
|
|
|
614
702
|
// Publish-family and stack outputs become this component's entry in
|
|
615
|
-
// `componentOutputs` — through the same exported accumulator
|
|
616
|
-
//
|
|
617
|
-
//
|
|
703
|
+
// `componentOutputs` — through the same exported accumulator a hosted
|
|
704
|
+
// runtime calls (#700), so local and hosted runs can never diverge on what
|
|
705
|
+
// downstream references see.
|
|
618
706
|
accumulateComponentOutputs(componentOutputs, component.name, phaseOutputs);
|
|
619
707
|
|
|
620
|
-
return { component: component.name, ok: true, records };
|
|
708
|
+
return { component: component.name, ok: true, status: "ok", records };
|
|
621
709
|
}
|
|
622
710
|
|
|
623
711
|
/**
|
|
@@ -641,9 +729,8 @@ export async function runComponentDeploy(
|
|
|
641
729
|
* the driver free of per-capability branching. Returns `undefined` when the
|
|
642
730
|
* component exposed nothing.
|
|
643
731
|
*
|
|
644
|
-
* Exported, like `resolveStepInput`, so
|
|
645
|
-
* (
|
|
646
|
-
* this exact function rather than re-deriving it (#700). The resolver and the
|
|
732
|
+
* Exported, like `resolveStepInput`, so a hosting runtime accumulates outputs
|
|
733
|
+
* via this exact function rather than re-deriving it (#700). The resolver and the
|
|
647
734
|
* accumulator are the two halves of one contract; sharing only the resolver
|
|
648
735
|
* is how the cross-stack gap #699 closed locally could reopen durably.
|
|
649
736
|
*/
|
|
@@ -659,11 +746,11 @@ export function collectComponentOutputs(
|
|
|
659
746
|
/**
|
|
660
747
|
* Record a finished component's outputs (`collectComponentOutputs`) into the
|
|
661
748
|
* shared `componentOutputs` map under the component's own name, merging over
|
|
662
|
-
* any seeded entry (`--seed-outputs`, or a
|
|
663
|
-
* thread-through). Mutates and returns `componentOutputs`, so
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
749
|
+
* any seeded entry (`--seed-outputs`, or a parent orchestrator's
|
|
750
|
+
* thread-through). Mutates and returns `componentOutputs`, so an out-of-process
|
|
751
|
+
* step can hand the updated map back over a JSON boundary — every value in it
|
|
752
|
+
* is plain result data, so the map is serializable by construction. A no-op
|
|
753
|
+
* when the component exposed nothing.
|
|
667
754
|
*/
|
|
668
755
|
export function accumulateComponentOutputs(
|
|
669
756
|
componentOutputs: Record<string, Record<string, unknown>>,
|
|
@@ -736,6 +823,10 @@ export interface InterpretRunOptions {
|
|
|
736
823
|
* byte-for-byte the same as before this option existed.
|
|
737
824
|
*/
|
|
738
825
|
onProgress?: (event: RunProgressEvent) => void;
|
|
826
|
+
/** 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`. */
|
|
827
|
+
gates?: GateLedgerPort;
|
|
828
|
+
/** ISO-8601 "now" for gate decisions, so a test is deterministic. */
|
|
829
|
+
now?: string;
|
|
739
830
|
}
|
|
740
831
|
|
|
741
832
|
/**
|
|
@@ -748,6 +839,11 @@ export interface InterpretRunOptions {
|
|
|
748
839
|
* a result with `ok: false`; throws `DriverRunFailure` carrying that result
|
|
749
840
|
* so callers can choose to inspect or propagate it.
|
|
750
841
|
*
|
|
842
|
+
* A component that stops at an unapproved gate (#2119) ends the run the same
|
|
843
|
+
* way, with `status: "gated"` and the pending fact on `gate` — but it is
|
|
844
|
+
* *returned*, never thrown, because nothing failed. Downstream waves do not
|
|
845
|
+
* run: a gate is the author saying a human decides before the rest happens.
|
|
846
|
+
*
|
|
751
847
|
* Zero per-component logic: this function and everything it calls dispatches
|
|
752
848
|
* purely on the generic `Component`/`Phase`/`Step` shapes and the registry —
|
|
753
849
|
* no branch anywhere names a specific component or capability.
|
|
@@ -761,11 +857,16 @@ export async function runInterpretDriver(
|
|
|
761
857
|
const byName = new Map(components.map((c) => [c.name, c]));
|
|
762
858
|
const componentOutputs: Record<string, Record<string, unknown>> = { ...(options.componentOutputs ?? {}) };
|
|
763
859
|
const { onProgress } = options;
|
|
860
|
+
const gates: GateContext = {
|
|
861
|
+
port: options.gates ?? gitGateLedgerPort(),
|
|
862
|
+
...(options.now ? { now: options.now } : {}),
|
|
863
|
+
};
|
|
764
864
|
|
|
765
865
|
onProgress?.({ type: "run-start", waves });
|
|
766
866
|
|
|
767
867
|
const results: DriverComponentResult[] = [];
|
|
768
868
|
let failedComponent: string | undefined;
|
|
869
|
+
let gated: DriverComponentResult | undefined;
|
|
769
870
|
|
|
770
871
|
waveLoop: for (const [waveIndex, wave] of waves.entries()) {
|
|
771
872
|
const waveNum = waveIndex + 1;
|
|
@@ -780,23 +881,32 @@ export async function runInterpretDriver(
|
|
|
780
881
|
registry,
|
|
781
882
|
componentOutputs,
|
|
782
883
|
onProgress,
|
|
884
|
+
gates,
|
|
783
885
|
);
|
|
784
|
-
onProgress?.({ type: "component-done", wave: waveNum, component: component.name, status: result.
|
|
886
|
+
onProgress?.({ type: "component-done", wave: waveNum, component: component.name, status: progressStatus(result.status) });
|
|
785
887
|
return result;
|
|
786
888
|
}),
|
|
787
889
|
);
|
|
788
890
|
results.push(...waveResults);
|
|
789
|
-
const
|
|
790
|
-
onProgress?.({ type: "wave-done", wave: waveNum, status:
|
|
791
|
-
if (
|
|
792
|
-
|
|
891
|
+
const stopped = waveResults.find((r) => r.status !== "ok");
|
|
892
|
+
onProgress?.({ type: "wave-done", wave: waveNum, status: progressStatus(stopped?.status ?? "ok") });
|
|
893
|
+
if (stopped) {
|
|
894
|
+
// A failure in the same wave outranks a gate: something is actually
|
|
895
|
+
// broken, and reporting the run as "waiting on a human" would hide it.
|
|
896
|
+
const failure = waveResults.find((r) => r.status === "fail");
|
|
897
|
+
if (failure) failedComponent = failure.component;
|
|
898
|
+
else gated = stopped;
|
|
793
899
|
break waveLoop;
|
|
794
900
|
}
|
|
795
901
|
}
|
|
796
902
|
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
903
|
+
const status: DriverRunResult["status"] = failedComponent ? "fail" : gated ? "gated" : "ok";
|
|
904
|
+
const ok = status === "ok";
|
|
905
|
+
onProgress?.({ type: "run-done", status: progressStatus(status) });
|
|
906
|
+
const result: DriverRunResult = {
|
|
907
|
+
order, waves, results, ok, status, failedComponent, componentOutputs,
|
|
908
|
+
...(gated ? { gatedComponent: gated.component, gate: gated.gate } : {}),
|
|
909
|
+
};
|
|
910
|
+
if (status === "fail") throw new DriverRunFailure(result);
|
|
801
911
|
return result;
|
|
802
912
|
}
|