@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
package/src/op/local-executor.ts
CHANGED
|
@@ -1,20 +1,37 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Local Op executor — runs an Op's phases in
|
|
2
|
+
* Local Op executor — runs an Op's phases in this process.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Ops. Provides phase sequencing, parallel phases, per-step retry + timeout via
|
|
4
|
+
* The default runtime (#2121), and the one dev loops, CI, and drift/observation
|
|
5
|
+
* Ops use. Provides phase sequencing, parallel phases, per-step retry + timeout via
|
|
6
6
|
* activity profiles, `outcomeAttribute` capture, and `onFailure` compensation.
|
|
7
|
-
* Gates and schedules are unsupported and rejected before any phase runs.
|
|
8
7
|
*
|
|
9
|
-
*
|
|
8
|
+
* A gate is a fact, not a wait (#2119). Reaching one, the executor consults the
|
|
9
|
+
* gate ledger through `./gate.ts`: a resolution newer than the gate's newest
|
|
10
|
+
* pending fact lets the step pass, carrying the approver onto its record;
|
|
11
|
+
* anything else records the pending fact and ends the run with status `gated`.
|
|
12
|
+
* No later phase runs and `onFailure` does not run — a gate is not a failure,
|
|
13
|
+
* and there is nothing to compensate for. The next run re-evaluates from the
|
|
14
|
+
* ledger, so `chant approve <op> <gate>` followed by `chant run <op>` is the
|
|
15
|
+
* whole loop.
|
|
16
|
+
*
|
|
17
|
+
* The executor is deliberately decoupled from any lexicon: activity
|
|
10
18
|
* implementations and profiles are passed in (loaded dynamically by the CLI),
|
|
11
|
-
* so core never statically depends on
|
|
19
|
+
* so core never statically depends on one.
|
|
12
20
|
*/
|
|
13
21
|
|
|
22
|
+
import { outcomeAttributesOf } from "./types";
|
|
14
23
|
import type { OpConfig, PhaseDefinition, ActivityStep, GateStep, EffectStep, StepDefinition } from "./types";
|
|
15
24
|
import { resolveActivity, type ActivityFn, type ActivityProfile } from "./activity-registry";
|
|
16
25
|
import type { ReceiptReadResult } from "./receipt-store";
|
|
17
26
|
import { isStepOutputRef } from "./step-output-ref";
|
|
27
|
+
import { parseDuration } from "./duration";
|
|
28
|
+
import { evaluateGate, gitGateLedgerPort, type GateLedgerPort } from "./gate";
|
|
29
|
+
import type { PendingGateRecord } from "../lifecycle/gate-ledger";
|
|
30
|
+
import { appendRunRecord, buildRunRecord } from "../lifecycle/run-ledger";
|
|
31
|
+
import type { OpRunRecord } from "./runtime";
|
|
32
|
+
import { randomUUID } from "node:crypto";
|
|
33
|
+
|
|
34
|
+
export { parseDuration } from "./duration";
|
|
18
35
|
|
|
19
36
|
// ── Records ─────────────────────────────────────────────────────────────────
|
|
20
37
|
|
|
@@ -22,38 +39,53 @@ export interface StepRecord {
|
|
|
22
39
|
phase: string;
|
|
23
40
|
fn: string;
|
|
24
41
|
/**
|
|
25
|
-
* Present for a local-executor record.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* populated with a guess.
|
|
42
|
+
* Present for a local-executor record. A runtime that reconstructs records
|
|
43
|
+
* from its own run history may not have an activity's scheduled input to
|
|
44
|
+
* hand, and omits the field rather than guessing at it.
|
|
29
45
|
*/
|
|
30
46
|
args?: Record<string, unknown>;
|
|
31
47
|
status: "ok" | "fail" | "skipped";
|
|
32
48
|
durationMs: number;
|
|
49
|
+
/**
|
|
50
|
+
* The first outcome attribute the step published, kept singular because a
|
|
51
|
+
* step publishing one is the ordinary case and every reader of this field
|
|
52
|
+
* predates the plural form. {@link StepRecord.outcomes} is the whole list.
|
|
53
|
+
*/
|
|
33
54
|
outcome?: { name: string; value: unknown };
|
|
55
|
+
/** Every outcome attribute the step published, in authored order (#2105). Absent when it published none. */
|
|
56
|
+
outcomes?: Array<{ name: string; value: unknown }>;
|
|
34
57
|
error?: string;
|
|
58
|
+
/** Set on a `gate` step that passed (#2119): who resolved it, when, and at what address. */
|
|
59
|
+
approval?: { gate: string; resolvedBy: string; timestamp: string; url?: string };
|
|
35
60
|
}
|
|
36
61
|
|
|
37
62
|
export interface OpRunResult {
|
|
38
63
|
op: string;
|
|
39
64
|
records: StepRecord[];
|
|
40
65
|
totalMs: number;
|
|
41
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Three-state outcome (#2119). `gated` is neither success nor failure: the
|
|
68
|
+
* run reached a gate nobody has approved, recorded the fact, and stopped.
|
|
69
|
+
* The CLI exits 3 for it so CI can tell "waiting on a human" from "broken".
|
|
70
|
+
*
|
|
71
|
+
* Written to the run ledger as the record's own `status` (#2118).
|
|
72
|
+
*/
|
|
73
|
+
status: "ok" | "fail" | "gated";
|
|
74
|
+
/** ISO-8601 start of this run. */
|
|
75
|
+
startedAt: string;
|
|
76
|
+
/** Present when `status === "gated"`: the pending fact the run ended on. */
|
|
77
|
+
gate?: PendingGateRecord;
|
|
78
|
+
/**
|
|
79
|
+
* The run's ledger record (#2118) — always built, whether or not it was
|
|
80
|
+
* appended. `chant run <op> --json` prints exactly this, so what a caller
|
|
81
|
+
* reads on stdout and what a later `readRunLedger` reads back are the same
|
|
82
|
+
* document.
|
|
83
|
+
*/
|
|
84
|
+
record: OpRunRecord;
|
|
42
85
|
}
|
|
43
86
|
|
|
44
87
|
// ── Errors ────────────────────────────────────────────────────────────────��─
|
|
45
88
|
|
|
46
|
-
/** Thrown when an Op contains a gate (or schedule) that local mode cannot run. */
|
|
47
|
-
export class LocalGateUnsupportedError extends Error {
|
|
48
|
-
constructor(public readonly signalName: string) {
|
|
49
|
-
super(
|
|
50
|
-
`gate "${signalName}" is not supported in local mode — gates and schedules ` +
|
|
51
|
-
`need a durable runtime. Re-run with --temporal.`,
|
|
52
|
-
);
|
|
53
|
-
this.name = "LocalGateUnsupportedError";
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
89
|
/** Thrown on terminal Op failure; carries the partial run result for rendering. */
|
|
58
90
|
export class OpRunFailure extends Error {
|
|
59
91
|
constructor(public readonly result: OpRunResult) {
|
|
@@ -70,6 +102,18 @@ class PhaseFailure extends Error {
|
|
|
70
102
|
}
|
|
71
103
|
}
|
|
72
104
|
|
|
105
|
+
/** Internal: a phase stopped on an unapproved gate. Not a failure — no compensation follows it. */
|
|
106
|
+
class GateStop extends Error {
|
|
107
|
+
constructor(
|
|
108
|
+
public readonly records: StepRecord[],
|
|
109
|
+
public readonly pending: PendingGateRecord,
|
|
110
|
+
public readonly phase: string,
|
|
111
|
+
) {
|
|
112
|
+
super(`gate "${pending.gate}" is pending approval`);
|
|
113
|
+
this.name = "GateStop";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
73
117
|
// ── Helpers ───────────────────────────────────────────────────────────────��─
|
|
74
118
|
|
|
75
119
|
const DEFAULT_PROFILE = "fastIdempotent";
|
|
@@ -79,19 +123,6 @@ const isActivity = (s: StepDefinition): s is ActivityStep => s.kind === "activit
|
|
|
79
123
|
const isGate = (s: StepDefinition): s is GateStep => s.kind === "gate";
|
|
80
124
|
const isEffect = (s: StepDefinition): s is EffectStep => s.kind === "effect";
|
|
81
125
|
|
|
82
|
-
/** Parse a Temporal duration string ("5m", "30s", "1h30m", "100ms") to ms. */
|
|
83
|
-
export function parseDuration(s: string): number {
|
|
84
|
-
const units: Record<string, number> = { ms: 1, s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
85
|
-
let total = 0;
|
|
86
|
-
let matched = false;
|
|
87
|
-
for (const m of s.matchAll(/(\d+)(ms|s|m|h|d)/g)) {
|
|
88
|
-
total += Number(m[1]) * units[m[2]];
|
|
89
|
-
matched = true;
|
|
90
|
-
}
|
|
91
|
-
if (!matched) throw new Error(`unparseable duration: "${s}"`);
|
|
92
|
-
return total;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
126
|
/** Resolve a dot-path into a value; returns the whole value when path is absent. */
|
|
96
127
|
function resolvePath(value: unknown, path?: string): unknown {
|
|
97
128
|
if (!path) return value;
|
|
@@ -120,22 +151,6 @@ function resolveStepOutputRefs(value: unknown, resultsById: ReadonlyMap<string,
|
|
|
120
151
|
return value;
|
|
121
152
|
}
|
|
122
153
|
|
|
123
|
-
/** Find the first gate step anywhere in the Op (phases + onFailure, including
|
|
124
|
-
* gates nested inside effect steps), if any. */
|
|
125
|
-
export function findGate(config: OpConfig): GateStep | undefined {
|
|
126
|
-
const all = [...config.phases, ...(config.onFailure ?? [])];
|
|
127
|
-
for (const phase of all) {
|
|
128
|
-
for (const step of phase.steps) {
|
|
129
|
-
if (isGate(step)) return step;
|
|
130
|
-
if (isEffect(step)) {
|
|
131
|
-
const nested = step.steps.find(isGate);
|
|
132
|
-
if (nested) return nested;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
154
|
/**
|
|
140
155
|
* Find the first `policyGate` step anywhere in the Op (phases + `onFailure`,
|
|
141
156
|
* including steps nested inside effect steps), if any.
|
|
@@ -147,7 +162,8 @@ export function findGate(config: OpConfig): GateStep | undefined {
|
|
|
147
162
|
* shown to a user who passed a documented flag. The combination cannot be
|
|
148
163
|
* honoured until the gate can build and load policies inside the boundary
|
|
149
164
|
* (#1157), so both `chant run` paths pre-flight it with this and refuse before
|
|
150
|
-
* any phase executes
|
|
165
|
+
* any phase executes. This is the one pre-flight refusal left on the Op path —
|
|
166
|
+
* a `gate` no longer is one (#2119).
|
|
151
167
|
*/
|
|
152
168
|
export function findPolicyGateStep(config: OpConfig): ActivityStep | undefined {
|
|
153
169
|
const all = [...config.phases, ...(config.onFailure ?? [])];
|
|
@@ -217,9 +233,9 @@ interface RanStep {
|
|
|
217
233
|
/**
|
|
218
234
|
* Run one activity step with retry + timeout. Never throws — returns a
|
|
219
235
|
* record. Any {@link StepOutputRef} in `step.args` is resolved against
|
|
220
|
-
* `resultsById` before the activity is called (#1290) —
|
|
221
|
-
*
|
|
222
|
-
*
|
|
236
|
+
* `resultsById` before the activity is called (#1290) — an unresolved
|
|
237
|
+
* placeholder must never reach an activity function, on this runtime or any
|
|
238
|
+
* other; see `resolveStepOutputRefs`.
|
|
223
239
|
*/
|
|
224
240
|
async function runStep(
|
|
225
241
|
step: ActivityStep,
|
|
@@ -241,8 +257,8 @@ async function runStep(
|
|
|
241
257
|
}
|
|
242
258
|
|
|
243
259
|
const profile = profiles[step.profile ?? DEFAULT_PROFILE] ?? {};
|
|
244
|
-
const timeoutMs = profile.
|
|
245
|
-
? parseDuration(profile.
|
|
260
|
+
const timeoutMs = profile.timeout
|
|
261
|
+
? parseDuration(profile.timeout)
|
|
246
262
|
: FALLBACK_TIMEOUT_MS;
|
|
247
263
|
const maxAttempts =
|
|
248
264
|
profile.retry?.maximumAttempts && profile.retry.maximumAttempts > 0
|
|
@@ -261,11 +277,10 @@ async function runStep(
|
|
|
261
277
|
const result = await callWithTimeout(fn, args, timeoutMs, signal);
|
|
262
278
|
if (step.id) resultsById.set(step.id, result);
|
|
263
279
|
const record: StepRecord = { ...base, status: "ok", durationMs: Date.now() - start };
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
};
|
|
280
|
+
const attrs = outcomeAttributesOf(step);
|
|
281
|
+
if (attrs.length > 0) {
|
|
282
|
+
record.outcomes = attrs.map((a) => ({ name: a.name, value: resolvePath(result, a.from) }));
|
|
283
|
+
record.outcome = record.outcomes[0];
|
|
269
284
|
}
|
|
270
285
|
return { record, result };
|
|
271
286
|
} catch (err) {
|
|
@@ -307,6 +322,75 @@ function skippedRecord(phaseName: string, fn: string, args?: Record<string, unkn
|
|
|
307
322
|
return { phase: phaseName, fn, args: args ?? {}, status: "skipped", durationMs: 0 };
|
|
308
323
|
}
|
|
309
324
|
|
|
325
|
+
// ── Gate steps (#2119) ───────────────────────────────────────────────────────
|
|
326
|
+
|
|
327
|
+
/** What a run needs to decide a gate against the ledger — see `./gate.ts`. */
|
|
328
|
+
interface GateContext {
|
|
329
|
+
op: string;
|
|
330
|
+
port: GateLedgerPort;
|
|
331
|
+
now?: string;
|
|
332
|
+
runId?: string;
|
|
333
|
+
/** Called once per settled step, in production order (#2121) — what `--progress-json` streams from. */
|
|
334
|
+
onRecord?: (record: StepRecord) => void;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** Collect records and hand each to the caller's progress sink in one move. */
|
|
338
|
+
function pushRecord(sink: StepRecord[], ctx: GateContext, ...recs: StepRecord[]): void {
|
|
339
|
+
sink.push(...recs);
|
|
340
|
+
for (const r of recs) ctx.onRecord?.(r);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** The record name a gate step lands under, so a reader (and a JSON consumer) can pick it out of `records`. */
|
|
344
|
+
function gateFn(step: GateStep): string {
|
|
345
|
+
return `gate:${step.signalName}`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Decide one gate against the ledger. A resolution newer than the gate's
|
|
350
|
+
* newest pending fact passes it, and the approver lands on the step record; a
|
|
351
|
+
* pending fact is recorded (or left standing) otherwise, and the caller stops
|
|
352
|
+
* the run.
|
|
353
|
+
*/
|
|
354
|
+
async function runGateStep(
|
|
355
|
+
step: GateStep,
|
|
356
|
+
phaseName: string,
|
|
357
|
+
gates: GateContext,
|
|
358
|
+
): Promise<{ record: StepRecord; pending?: PendingGateRecord }> {
|
|
359
|
+
const start = Date.now();
|
|
360
|
+
const check = await evaluateGate(gates.port, {
|
|
361
|
+
op: gates.op,
|
|
362
|
+
gate: step.signalName,
|
|
363
|
+
...(step.description ? { description: step.description } : {}),
|
|
364
|
+
...(step.timeout ? { timeout: step.timeout } : {}),
|
|
365
|
+
...(gates.runId ? { runId: gates.runId } : {}),
|
|
366
|
+
...(gates.now ? { now: gates.now } : {}),
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
if (check.satisfied) {
|
|
370
|
+
const { resolution } = check;
|
|
371
|
+
return {
|
|
372
|
+
record: {
|
|
373
|
+
phase: phaseName,
|
|
374
|
+
fn: gateFn(step),
|
|
375
|
+
args: {},
|
|
376
|
+
status: "ok",
|
|
377
|
+
durationMs: Date.now() - start,
|
|
378
|
+
approval: {
|
|
379
|
+
gate: step.signalName,
|
|
380
|
+
resolvedBy: resolution.resolvedBy,
|
|
381
|
+
timestamp: resolution.timestamp,
|
|
382
|
+
...(resolution.url ? { url: resolution.url } : {}),
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
record: { phase: phaseName, fn: gateFn(step), args: {}, status: "skipped", durationMs: Date.now() - start },
|
|
390
|
+
pending: check.pending,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
310
394
|
/**
|
|
311
395
|
* Run one effect step: read-compare-run-write. On a match the nested steps are
|
|
312
396
|
* recorded as skipped ("effect already applied") and nothing is written. On a
|
|
@@ -314,6 +398,11 @@ function skippedRecord(phaseName: string, fn: string, args?: Record<string, unkn
|
|
|
314
398
|
* succeeds is the receipt written — last, once (the sole writer, #1703
|
|
315
399
|
* decision 3). Any failure leaves the receipt untouched (stale), so the next
|
|
316
400
|
* run re-proposes the effect.
|
|
401
|
+
*
|
|
402
|
+
* A nested `gate` is decided in authored order like any other nested step
|
|
403
|
+
* (#2119). Unapproved, it stops the effect before the receipt is written, so
|
|
404
|
+
* the next run re-reads the receipt, re-proposes the effect, and re-evaluates
|
|
405
|
+
* the gate — the receipt stays honest about what actually happened.
|
|
317
406
|
*/
|
|
318
407
|
async function runEffectStep(
|
|
319
408
|
step: EffectStep,
|
|
@@ -321,17 +410,18 @@ async function runEffectStep(
|
|
|
321
410
|
activities: Map<string, ActivityFn>,
|
|
322
411
|
profiles: Record<string, ActivityProfile>,
|
|
323
412
|
resultsById: Map<string, unknown>,
|
|
413
|
+
gates: GateContext,
|
|
324
414
|
signal?: AbortSignal,
|
|
325
|
-
): Promise<{ records: StepRecord[]; failed: boolean }> {
|
|
415
|
+
): Promise<{ records: StepRecord[]; failed: boolean; pending?: PendingGateRecord }> {
|
|
326
416
|
const records: StepRecord[] = [];
|
|
327
417
|
|
|
328
418
|
const read = await runStep(receiptReadStep(step), phaseName, activities, profiles, resultsById, signal);
|
|
329
|
-
records
|
|
419
|
+
pushRecord(records, gates, read.record);
|
|
330
420
|
if (read.record.status === "fail") return { records, failed: true };
|
|
331
421
|
|
|
332
422
|
const result = read.result as Partial<ReceiptReadResult> | undefined;
|
|
333
423
|
if (typeof result?.expectation !== "string") {
|
|
334
|
-
records
|
|
424
|
+
pushRecord(records, gates, {
|
|
335
425
|
phase: phaseName,
|
|
336
426
|
fn: `effect:${step.receipt.name}`,
|
|
337
427
|
args: {},
|
|
@@ -346,22 +436,44 @@ async function runEffectStep(
|
|
|
346
436
|
if (result.current === expectation) {
|
|
347
437
|
// Effect already applied — skip the nested steps, write nothing.
|
|
348
438
|
for (const nested of step.steps) {
|
|
349
|
-
|
|
439
|
+
pushRecord(records, gates,
|
|
440
|
+
nested.kind === "activity"
|
|
441
|
+
? skippedRecord(phaseName, nested.fn, nested.args)
|
|
442
|
+
: skippedRecord(phaseName, gateFn(nested)),
|
|
443
|
+
);
|
|
350
444
|
}
|
|
351
445
|
return { records, failed: false };
|
|
352
446
|
}
|
|
353
447
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
448
|
+
const skipRest = (from: number) => {
|
|
449
|
+
for (const skipped of step.steps.slice(from)) {
|
|
450
|
+
pushRecord(records, gates,
|
|
451
|
+
skipped.kind === "activity"
|
|
452
|
+
? skippedRecord(phaseName, skipped.fn, skipped.args)
|
|
453
|
+
: skippedRecord(phaseName, gateFn(skipped)),
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
pushRecord(records, gates, skippedRecord(phaseName, "receiptWrite"));
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
for (let i = 0; i < step.steps.length; i++) {
|
|
460
|
+
const nested = step.steps[i];
|
|
461
|
+
if (isGate(nested)) {
|
|
462
|
+
const { record, pending } = await runGateStep(nested, phaseName, gates);
|
|
463
|
+
pushRecord(records, gates, record);
|
|
464
|
+
if (pending) {
|
|
465
|
+
// Receipt left untouched — the next run re-proposes the effect and
|
|
466
|
+
// re-evaluates the gate against whatever the ledger says by then.
|
|
467
|
+
skipRest(i + 1);
|
|
468
|
+
return { records, failed: false, pending };
|
|
469
|
+
}
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const ran = await runStep(nested, phaseName, activities, profiles, resultsById, signal);
|
|
473
|
+
pushRecord(records, gates, ran.record);
|
|
359
474
|
if (ran.record.status === "fail") {
|
|
360
475
|
// Receipt left untouched (stale) — the next run re-proposes the effect.
|
|
361
|
-
|
|
362
|
-
records.push(skippedRecord(phaseName, skipped.fn, skipped.args));
|
|
363
|
-
}
|
|
364
|
-
records.push(skippedRecord(phaseName, "receiptWrite"));
|
|
476
|
+
skipRest(i + 1);
|
|
365
477
|
return { records, failed: true };
|
|
366
478
|
}
|
|
367
479
|
}
|
|
@@ -380,25 +492,22 @@ async function runEffectStep(
|
|
|
380
492
|
resultsById,
|
|
381
493
|
signal,
|
|
382
494
|
);
|
|
383
|
-
records
|
|
495
|
+
pushRecord(records, gates, wrote.record);
|
|
384
496
|
return { records, failed: wrote.record.status === "fail" };
|
|
385
497
|
}
|
|
386
498
|
|
|
387
|
-
/**
|
|
499
|
+
/**
|
|
500
|
+
* Run a phase. Throws `PhaseFailure` (with the records so far) if any step
|
|
501
|
+
* fails, or `GateStop` if a `gate` step in it is still pending approval.
|
|
502
|
+
*/
|
|
388
503
|
async function runPhase(
|
|
389
504
|
phase: PhaseDefinition,
|
|
390
505
|
activities: Map<string, ActivityFn>,
|
|
391
506
|
profiles: Record<string, ActivityProfile>,
|
|
392
507
|
resultsById: Map<string, unknown>,
|
|
508
|
+
gates: GateContext,
|
|
393
509
|
signal?: AbortSignal,
|
|
394
510
|
): Promise<StepRecord[]> {
|
|
395
|
-
// Defensive: gates are pre-flighted, but never execute one if it slips
|
|
396
|
-
// through — including a gate nested inside an effect step.
|
|
397
|
-
const gate =
|
|
398
|
-
phase.steps.find(isGate) ??
|
|
399
|
-
phase.steps.filter(isEffect).flatMap((e) => e.steps).find(isGate);
|
|
400
|
-
if (gate) throw new LocalGateUnsupportedError(gate.signalName);
|
|
401
|
-
|
|
402
511
|
if (phase.parallel) {
|
|
403
512
|
const eff = phase.steps.find(isEffect);
|
|
404
513
|
if (eff) {
|
|
@@ -406,32 +515,65 @@ async function runPhase(
|
|
|
406
515
|
`effect step "${eff.receipt.name}" cannot run in a parallel phase — read-compare-run-write is ordered`,
|
|
407
516
|
);
|
|
408
517
|
}
|
|
518
|
+
// Gates in a parallel phase are decided before the fan-out: a gate is a
|
|
519
|
+
// read of the ledger, not work, and starting activities that a pending
|
|
520
|
+
// gate is about to strand would defeat the point of stopping at it.
|
|
521
|
+
const gateRecords: StepRecord[] = [];
|
|
522
|
+
for (const step of phase.steps.filter(isGate)) {
|
|
523
|
+
const { record, pending } = await runGateStep(step, phase.name, gates);
|
|
524
|
+
pushRecord(gateRecords, gates, record);
|
|
525
|
+
if (pending) {
|
|
526
|
+
for (const skipped of phase.steps.filter(isActivity)) {
|
|
527
|
+
pushRecord(gateRecords, gates, skippedRecord(phase.name, skipped.fn, skipped.args));
|
|
528
|
+
}
|
|
529
|
+
throw new GateStop(gateRecords, pending, phase.name);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
409
532
|
const steps = phase.steps.filter(isActivity);
|
|
410
|
-
const
|
|
533
|
+
const ran = (
|
|
411
534
|
await Promise.all(steps.map((s) => runStep(s, phase.name, activities, profiles, resultsById, signal)))
|
|
412
535
|
).map((r) => r.record);
|
|
536
|
+
for (const r of ran) gates.onRecord?.(r);
|
|
537
|
+
const records = gateRecords.concat(ran);
|
|
413
538
|
if (records.some((r) => r.status === "fail")) throw new PhaseFailure(records);
|
|
414
539
|
return records;
|
|
415
540
|
}
|
|
416
541
|
|
|
417
|
-
const steps = phase.steps
|
|
542
|
+
const steps = phase.steps;
|
|
418
543
|
const records: StepRecord[] = [];
|
|
419
544
|
|
|
420
545
|
const skipRemaining = (from: number) => {
|
|
421
546
|
for (const skipped of steps.slice(from)) {
|
|
422
547
|
if (isEffect(skipped)) {
|
|
423
|
-
records
|
|
548
|
+
pushRecord(records, gates, skippedRecord(phase.name, `effect:${skipped.receipt.name}`));
|
|
549
|
+
} else if (isGate(skipped)) {
|
|
550
|
+
pushRecord(records, gates, skippedRecord(phase.name, gateFn(skipped)));
|
|
424
551
|
} else {
|
|
425
|
-
records
|
|
552
|
+
pushRecord(records, gates, skippedRecord(phase.name, skipped.fn, skipped.args));
|
|
426
553
|
}
|
|
427
554
|
}
|
|
428
555
|
};
|
|
429
556
|
|
|
430
557
|
for (let i = 0; i < steps.length; i++) {
|
|
431
558
|
const step = steps[i];
|
|
559
|
+
if (isGate(step)) {
|
|
560
|
+
const { record, pending } = await runGateStep(step, phase.name, gates);
|
|
561
|
+
pushRecord(records, gates, record);
|
|
562
|
+
if (pending) {
|
|
563
|
+
skipRemaining(i + 1);
|
|
564
|
+
throw new GateStop(records, pending, phase.name);
|
|
565
|
+
}
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
432
568
|
if (isEffect(step)) {
|
|
433
|
-
const { records: effRecords, failed } = await runEffectStep(
|
|
434
|
-
|
|
569
|
+
const { records: effRecords, failed, pending } = await runEffectStep(
|
|
570
|
+
step, phase.name, activities, profiles, resultsById, gates, signal,
|
|
571
|
+
);
|
|
572
|
+
records.push(...effRecords); // already emitted by runEffectStep
|
|
573
|
+
if (pending) {
|
|
574
|
+
skipRemaining(i + 1);
|
|
575
|
+
throw new GateStop(records, pending, phase.name);
|
|
576
|
+
}
|
|
435
577
|
if (failed) {
|
|
436
578
|
skipRemaining(i + 1);
|
|
437
579
|
throw new PhaseFailure(records);
|
|
@@ -439,7 +581,7 @@ async function runPhase(
|
|
|
439
581
|
continue;
|
|
440
582
|
}
|
|
441
583
|
const { record } = await runStep(step, phase.name, activities, profiles, resultsById, signal);
|
|
442
|
-
records
|
|
584
|
+
pushRecord(records, gates, record);
|
|
443
585
|
if (record.status === "fail") {
|
|
444
586
|
// Mark the remaining steps in this phase as skipped, then abort.
|
|
445
587
|
skipRemaining(i + 1);
|
|
@@ -451,20 +593,78 @@ async function runPhase(
|
|
|
451
593
|
|
|
452
594
|
// ── Public API ────────────────────────────────────────────────────────────��─
|
|
453
595
|
|
|
596
|
+
/** How a run reaches the gate ledger, and what it calls itself there (#2119). */
|
|
597
|
+
export interface RunOpOptions {
|
|
598
|
+
/**
|
|
599
|
+
* Where gate facts are read and written. Defaults to the `chant/lifecycle`
|
|
600
|
+
* orphan branch under `cwd`; a test (or a caller that already holds the
|
|
601
|
+
* ledger) passes `memoryGateLedgerPort()` from `./gate.ts` instead.
|
|
602
|
+
*/
|
|
603
|
+
gates?: GateLedgerPort;
|
|
604
|
+
/** Working directory for the default git-backed gate ledger. */
|
|
605
|
+
cwd?: string;
|
|
606
|
+
/** ISO-8601 "now", so a gate decision is deterministic under test. */
|
|
607
|
+
now?: string;
|
|
608
|
+
/** Identifies this run on any pending fact it records. */
|
|
609
|
+
runId?: string;
|
|
610
|
+
/**
|
|
611
|
+
* Called once per settled step, in the order the records are produced
|
|
612
|
+
* (#2121) — what the local op runtime (./runtimes/local.ts) feeds
|
|
613
|
+
* `--progress-json` from. Side-effect free when omitted.
|
|
614
|
+
*/
|
|
615
|
+
onRecord?: (record: StepRecord) => void;
|
|
616
|
+
/**
|
|
617
|
+
* Append the run's {@link OpRunRecord} to the run ledger
|
|
618
|
+
* (`../lifecycle/run-ledger.ts`) when the run settles (#2118). Opt-in on
|
|
619
|
+
* purpose: the executor is called from unit tests and in-process harnesses
|
|
620
|
+
* whose working directory is an ordinary checkout, and a run there must not
|
|
621
|
+
* write to the lifecycle branch. The local runtime (./runtimes/local.ts) and
|
|
622
|
+
* `chant operator` pass it; nothing else does.
|
|
623
|
+
*/
|
|
624
|
+
ledger?: { cwd?: string };
|
|
625
|
+
/**
|
|
626
|
+
* Called when the ledger append itself fails. The run's own outcome is
|
|
627
|
+
* already decided by then, so a ledger failure is reported to the caller,
|
|
628
|
+
* never promoted into a run failure.
|
|
629
|
+
*/
|
|
630
|
+
onLedgerError?: (err: unknown) => void;
|
|
631
|
+
}
|
|
632
|
+
|
|
454
633
|
/**
|
|
455
|
-
* Execute an Op locally.
|
|
456
|
-
*
|
|
457
|
-
*
|
|
458
|
-
*
|
|
634
|
+
* Execute an Op locally.
|
|
635
|
+
*
|
|
636
|
+
* Resolves with the run result when every phase succeeds (`status: "ok"`), and
|
|
637
|
+
* also when the run stopped at an unapproved gate (`status: "gated"`, with the
|
|
638
|
+
* pending fact on `result.gate`) — a gate is a fact, not an error, so it is not
|
|
639
|
+
* thrown. Rejects with `OpRunFailure` (carrying the partial result) on terminal
|
|
640
|
+
* failure, after running any `onFailure` phases in reverse order; a gated run
|
|
641
|
+
* runs no `onFailure` phase, because nothing failed and nothing was left
|
|
642
|
+
* half-applied to compensate for.
|
|
643
|
+
*
|
|
644
|
+
* Whichever of the three ways it settles, the run carries an
|
|
645
|
+
* {@link OpRunRecord} on `result.record` (#2118). With `options.ledger` that
|
|
646
|
+
* record is also appended to `<env>/runs__<op>.jsonl` on the lifecycle branch
|
|
647
|
+
* before this returns or rejects, so an Op's outcome outlives the process that
|
|
648
|
+
* produced it.
|
|
459
649
|
*/
|
|
460
650
|
export async function runOpLocally(
|
|
461
651
|
config: OpConfig,
|
|
462
652
|
activities: Map<string, ActivityFn>,
|
|
463
653
|
profiles: Record<string, ActivityProfile>,
|
|
464
654
|
signal?: AbortSignal,
|
|
655
|
+
options: RunOpOptions = {},
|
|
465
656
|
): Promise<OpRunResult> {
|
|
466
|
-
|
|
467
|
-
|
|
657
|
+
// The run id is the gate ledger's and the run ledger's alike: a pending gate
|
|
658
|
+
// fact and the record naming that gate carry the same string.
|
|
659
|
+
const runId = options.runId ?? randomUUID();
|
|
660
|
+
|
|
661
|
+
const gates: GateContext = {
|
|
662
|
+
op: config.name,
|
|
663
|
+
port: options.gates ?? gitGateLedgerPort(options.cwd ? { cwd: options.cwd } : undefined),
|
|
664
|
+
...(options.now ? { now: options.now } : {}),
|
|
665
|
+
runId,
|
|
666
|
+
...(options.onRecord ? { onRecord: options.onRecord } : {}),
|
|
667
|
+
};
|
|
468
668
|
|
|
469
669
|
// Effect steps are ordered (read-compare-run-write): refuse them in a
|
|
470
670
|
// parallel phase up front, with the phase named, rather than mid-run.
|
|
@@ -480,6 +680,33 @@ export async function runOpLocally(
|
|
|
480
680
|
|
|
481
681
|
const records: StepRecord[] = [];
|
|
482
682
|
const start = Date.now();
|
|
683
|
+
const startedAt = options.now ?? new Date(start).toISOString();
|
|
684
|
+
|
|
685
|
+
/**
|
|
686
|
+
* Build the run's ledger record, append it when asked, and hand it back for
|
|
687
|
+
* the result. Called once, at whichever of the three exits the run takes.
|
|
688
|
+
*/
|
|
689
|
+
const settle = async (
|
|
690
|
+
settled: StepRecord[],
|
|
691
|
+
status: OpRunResult["status"],
|
|
692
|
+
gate?: PendingGateRecord,
|
|
693
|
+
): Promise<OpRunRecord> => {
|
|
694
|
+
const input = buildRunRecord(config, settled, {
|
|
695
|
+
started: startedAt,
|
|
696
|
+
ended: options.now ?? new Date().toISOString(),
|
|
697
|
+
status,
|
|
698
|
+
id: runId,
|
|
699
|
+
...(gate ? { gate: { name: gate.gate, since: gate.timestamp } } : {}),
|
|
700
|
+
});
|
|
701
|
+
const record: OpRunRecord = { version: 1, ...input, id: runId };
|
|
702
|
+
if (!options.ledger) return record;
|
|
703
|
+
try {
|
|
704
|
+
return (await appendRunRecord(input, options.ledger)).record;
|
|
705
|
+
} catch (err) {
|
|
706
|
+
options.onLedgerError?.(err);
|
|
707
|
+
return record;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
483
710
|
|
|
484
711
|
// Completed step id → its activity result (#1290). Populated as main-phase
|
|
485
712
|
// steps finish, so a later step's step-output references resolve to real
|
|
@@ -489,9 +716,36 @@ export async function runOpLocally(
|
|
|
489
716
|
try {
|
|
490
717
|
for (const phase of config.phases) {
|
|
491
718
|
if (signal?.aborted) throw new PhaseFailure([]);
|
|
492
|
-
records.push(...(await runPhase(phase, activities, profiles, resultsById, signal)));
|
|
719
|
+
records.push(...(await runPhase(phase, activities, profiles, resultsById, gates, signal)));
|
|
493
720
|
}
|
|
494
721
|
} catch (err) {
|
|
722
|
+
// A pending gate ends the run where it stands: no later phase, and no
|
|
723
|
+
// `onFailure` compensation — nothing failed, so there is nothing to undo.
|
|
724
|
+
if (err instanceof GateStop) {
|
|
725
|
+
records.push(...err.records);
|
|
726
|
+
const stoppedAt = config.phases.findIndex((p) => p.name === err.phase);
|
|
727
|
+
for (const phase of config.phases.slice(stoppedAt + 1)) {
|
|
728
|
+
for (const step of phase.steps) {
|
|
729
|
+
records.push(
|
|
730
|
+
isEffect(step)
|
|
731
|
+
? skippedRecord(phase.name, `effect:${step.receipt.name}`)
|
|
732
|
+
: isGate(step)
|
|
733
|
+
? skippedRecord(phase.name, gateFn(step))
|
|
734
|
+
: skippedRecord(phase.name, step.fn, step.args),
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
op: config.name,
|
|
740
|
+
records,
|
|
741
|
+
totalMs: Date.now() - start,
|
|
742
|
+
status: "gated",
|
|
743
|
+
startedAt,
|
|
744
|
+
gate: err.pending,
|
|
745
|
+
record: await settle(records, "gated", err.pending),
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
|
|
495
749
|
if (err instanceof PhaseFailure) records.push(...err.records);
|
|
496
750
|
|
|
497
751
|
// Compensation: run onFailure phases in reverse order (best-effort). Skipped
|
|
@@ -499,17 +753,31 @@ export async function runOpLocally(
|
|
|
499
753
|
if (!signal?.aborted) {
|
|
500
754
|
for (const phase of [...(config.onFailure ?? [])].reverse()) {
|
|
501
755
|
try {
|
|
502
|
-
records.push(...(await runPhase(phase, activities, profiles, resultsById, signal)));
|
|
756
|
+
records.push(...(await runPhase(phase, activities, profiles, resultsById, gates, signal)));
|
|
503
757
|
} catch (compErr) {
|
|
504
|
-
if (compErr instanceof PhaseFailure) records.push(...compErr.records);
|
|
758
|
+
if (compErr instanceof PhaseFailure || compErr instanceof GateStop) records.push(...compErr.records);
|
|
505
759
|
}
|
|
506
760
|
}
|
|
507
761
|
}
|
|
508
762
|
|
|
509
|
-
throw new OpRunFailure({
|
|
763
|
+
throw new OpRunFailure({
|
|
764
|
+
op: config.name,
|
|
765
|
+
records,
|
|
766
|
+
totalMs: Date.now() - start,
|
|
767
|
+
status: "fail",
|
|
768
|
+
startedAt,
|
|
769
|
+
record: await settle(records, "fail"),
|
|
770
|
+
});
|
|
510
771
|
}
|
|
511
772
|
|
|
512
|
-
return {
|
|
773
|
+
return {
|
|
774
|
+
op: config.name,
|
|
775
|
+
records,
|
|
776
|
+
totalMs: Date.now() - start,
|
|
777
|
+
status: "ok",
|
|
778
|
+
startedAt,
|
|
779
|
+
record: await settle(records, "ok"),
|
|
780
|
+
};
|
|
513
781
|
}
|
|
514
782
|
|
|
515
783
|
function errMessage(err: unknown): string {
|