@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
|
@@ -15,26 +15,24 @@
|
|
|
15
15
|
* second non-env top-level directory.
|
|
16
16
|
*
|
|
17
17
|
* `chant approve <op> <gate>` (`../cli/handlers/operator.ts`) is what
|
|
18
|
-
* appends
|
|
19
|
-
* writes the counterpart fact". Per
|
|
18
|
+
* appends a resolution — issue #1485's "resolution is an out-of-band act
|
|
19
|
+
* that writes the counterpart fact". Per that issue's leaning on open
|
|
20
20
|
* question 3 ("local trust in v1, signature as an additive follow-up"),
|
|
21
21
|
* this record is *not* itself an authorization check — anyone who can run
|
|
22
22
|
* `chant approve` locally can write one, the same trust boundary a local
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* "this gate is cleared" that a human (or a future auto-resume path) reads;
|
|
37
|
-
* it is not itself the unblock.
|
|
23
|
+
* commit already has.
|
|
24
|
+
*
|
|
25
|
+
* Since #2119 the file carries both halves of the loop. A `gate` step the
|
|
26
|
+
* local executor reaches (`../op/local-executor.ts`) appends a
|
|
27
|
+
* {@link PendingGateRecord} and ends that run with status `gated`; `chant
|
|
28
|
+
* approve` appends the {@link GateResolutionRecord} that answers it; the next
|
|
29
|
+
* run reads both, finds a resolution newer than the pending fact, and walks
|
|
30
|
+
* through the gate carrying the approver. A resolution older than the newest
|
|
31
|
+
* pending fact is not an answer to it — {@link latestResolutionSince} applies
|
|
32
|
+
* that rule for the executor and for `chant operator status` alike. Both kinds
|
|
33
|
+
* of line share one file, told apart by {@link GateLedgerRecord}'s `kind`
|
|
34
|
+
* (absent on the resolution lines written before #2119, which is why
|
|
35
|
+
* `"resolution"` is the default reading).
|
|
38
36
|
*/
|
|
39
37
|
import { sortedJsonReplacer } from "../utils";
|
|
40
38
|
import { readBlobFromPath, readPathSha, readBlobBySha, writeBlobToPath, RefCASConflictError } from "./git";
|
|
@@ -98,11 +96,13 @@ export function isApprovalUrl(raw: string): boolean {
|
|
|
98
96
|
export interface GateResolutionRecord {
|
|
99
97
|
/** Schema version, so an incompatible future shape is detected before being misread. */
|
|
100
98
|
version: 1;
|
|
99
|
+
/** Discriminator against {@link PendingGateRecord}, which shares this file. Absent on every line written before #2119, so a reader must treat "no kind" as `"resolution"`. */
|
|
100
|
+
kind?: "resolution";
|
|
101
101
|
/** The dispatched op the gate belongs to. */
|
|
102
102
|
op: string;
|
|
103
103
|
/** The gate's signal name (matches `ConvergeRuleOutcome.gateName`). */
|
|
104
104
|
gate: string;
|
|
105
|
-
/** Who resolved it — an actor name, the same convention `components release --actor` and `run
|
|
105
|
+
/** Who resolved it — an actor name, the same convention `components release --actor` and `run approve --approver` use. */
|
|
106
106
|
resolvedBy: string;
|
|
107
107
|
/** ISO-8601 timestamp, caller-supplied (library code never calls `Date.now()` internally). */
|
|
108
108
|
timestamp: string;
|
|
@@ -121,7 +121,53 @@ export interface GateResolutionRecord {
|
|
|
121
121
|
url?: string;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
export type GateResolutionInput = Omit<GateResolutionRecord, "version">;
|
|
124
|
+
export type GateResolutionInput = Omit<GateResolutionRecord, "version" | "kind">;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* One immutable pending-gate record (#2119) — what an executor writes when a
|
|
128
|
+
* run reaches a `gate` step with no resolution standing against it. The run
|
|
129
|
+
* ends here with status `gated`; this line is the durable trace of that, and
|
|
130
|
+
* the anchor a later {@link GateResolutionRecord} has to be newer than to
|
|
131
|
+
* count as its answer.
|
|
132
|
+
*
|
|
133
|
+
* Idempotent by design: a run that finds a live (unexpired) pending fact for
|
|
134
|
+
* the same gate reuses it rather than appending a second one, so a converge
|
|
135
|
+
* loop ticking every minute against an unapproved gate does not grow the
|
|
136
|
+
* ledger by a line a minute. Once `expiresAt` passes, the fact is stale and
|
|
137
|
+
* the next run records a fresh one.
|
|
138
|
+
*/
|
|
139
|
+
export interface PendingGateRecord {
|
|
140
|
+
/** Schema version, so an incompatible future shape is detected before being misread. */
|
|
141
|
+
version: 1;
|
|
142
|
+
kind: "pending";
|
|
143
|
+
/** The op (or, on the component driver, the component) the gate belongs to. */
|
|
144
|
+
op: string;
|
|
145
|
+
/** The gate's signal name (matches `GateStep.signalName`). */
|
|
146
|
+
gate: string;
|
|
147
|
+
/** The gate's human-readable description, when it declared one — what `chant operator status` shows a reader who wasn't there for the run. */
|
|
148
|
+
description?: string;
|
|
149
|
+
/** The run that reached the gate, when the caller identifies its runs. */
|
|
150
|
+
runId?: string;
|
|
151
|
+
/** ISO-8601 timestamp, caller-supplied (library code never calls `Date.now()` internally). */
|
|
152
|
+
timestamp: string;
|
|
153
|
+
/** ISO-8601 expiry, from the gate's `timeout` (default {@link DEFAULT_GATE_EXPIRY}). Past it, the fact is stale and a run re-records it. */
|
|
154
|
+
expiresAt: string;
|
|
155
|
+
/** The address approval happens at, when the run knew one — see {@link resolveApprovalUrl}. */
|
|
156
|
+
url?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export type PendingGateInput = Omit<PendingGateRecord, "version" | "kind">;
|
|
160
|
+
|
|
161
|
+
/** Either kind of line in `_gates/<op>.jsonl`. */
|
|
162
|
+
export type GateLedgerRecord = GateResolutionRecord | PendingGateRecord;
|
|
163
|
+
|
|
164
|
+
/** The default a gate's pending fact expires after when its `GateStep` declares no `timeout` — the same 48h `GateStep.timeout` documents as its own default. */
|
|
165
|
+
export const DEFAULT_GATE_EXPIRY = "48h";
|
|
166
|
+
|
|
167
|
+
/** Whether a pending fact has aged out of relevance at `nowIso`. */
|
|
168
|
+
export function isPendingGateExpired(record: PendingGateRecord, nowIso: string): boolean {
|
|
169
|
+
return new Date(record.expiresAt).getTime() <= new Date(nowIso).getTime();
|
|
170
|
+
}
|
|
125
171
|
|
|
126
172
|
function filename(op: string): string {
|
|
127
173
|
return `${op}.jsonl`;
|
|
@@ -133,6 +179,30 @@ export async function appendGateResolution(
|
|
|
133
179
|
opts?: { cwd?: string },
|
|
134
180
|
): Promise<{ commit: string; record: GateResolutionRecord }> {
|
|
135
181
|
const record: GateResolutionRecord = { version: 1, ...input };
|
|
182
|
+
const commit = await appendGateLine(record, "Gate resolution record", opts);
|
|
183
|
+
return { commit, record };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Append one immutable pending-gate record (#2119) — the fact a run leaves
|
|
188
|
+
* behind when it reaches a gate nobody has approved. Same file, same
|
|
189
|
+
* append-and-retry discipline, same "does not push" contract as
|
|
190
|
+
* {@link appendGateResolution}.
|
|
191
|
+
*/
|
|
192
|
+
export async function appendPendingGate(
|
|
193
|
+
input: PendingGateInput,
|
|
194
|
+
opts?: { cwd?: string },
|
|
195
|
+
): Promise<{ commit: string; record: PendingGateRecord }> {
|
|
196
|
+
const record: PendingGateRecord = { version: 1, kind: "pending", ...input };
|
|
197
|
+
const commit = await appendGateLine(record, "Pending gate record", opts);
|
|
198
|
+
return { commit, record };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function appendGateLine(
|
|
202
|
+
record: GateLedgerRecord,
|
|
203
|
+
message: string,
|
|
204
|
+
opts?: { cwd?: string },
|
|
205
|
+
): Promise<string> {
|
|
136
206
|
const json = JSON.stringify(record, sortedJsonReplacer);
|
|
137
207
|
|
|
138
208
|
let lastErr: unknown;
|
|
@@ -141,11 +211,10 @@ export async function appendGateResolution(
|
|
|
141
211
|
const priorSha = await readPathSha(DIR, filename(record.op), opts);
|
|
142
212
|
const existing = priorSha ? await readBlobBySha(priorSha, opts) : null;
|
|
143
213
|
const content = existing ? `${existing.replace(/\n$/, "")}\n${json}` : json;
|
|
144
|
-
|
|
214
|
+
return await writeBlobToPath(DIR, filename(record.op), content, message, {
|
|
145
215
|
...opts,
|
|
146
216
|
expectPriorPathSha: priorSha,
|
|
147
217
|
});
|
|
148
|
-
return { commit, record };
|
|
149
218
|
} catch (err) {
|
|
150
219
|
if (!(err instanceof RefCASConflictError)) throw err;
|
|
151
220
|
lastErr = err;
|
|
@@ -154,36 +223,74 @@ export async function appendGateResolution(
|
|
|
154
223
|
throw lastErr;
|
|
155
224
|
}
|
|
156
225
|
|
|
157
|
-
/**
|
|
158
|
-
|
|
226
|
+
/**
|
|
227
|
+
* Read every line of `op`'s gate file, oldest first, split into the two kinds
|
|
228
|
+
* (#2119). Malformed lines are skipped and counted, not thrown on — the same
|
|
229
|
+
* graceful-degradation stance `readConvergeLedger` takes. Returns empty arrays
|
|
230
|
+
* (never throws) when `op` has nothing recorded yet.
|
|
231
|
+
*/
|
|
232
|
+
export async function readGateLedger(
|
|
159
233
|
op: string,
|
|
160
234
|
opts?: { cwd?: string },
|
|
161
|
-
): Promise<{
|
|
235
|
+
): Promise<{ resolutions: GateResolutionRecord[]; pending: PendingGateRecord[]; malformed: number }> {
|
|
162
236
|
const content = await readBlobFromPath(DIR, filename(op), opts);
|
|
163
|
-
if (!content) return {
|
|
237
|
+
if (!content) return { resolutions: [], pending: [], malformed: 0 };
|
|
164
238
|
|
|
165
239
|
const lines = content.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
166
|
-
const
|
|
240
|
+
const resolutions: GateResolutionRecord[] = [];
|
|
241
|
+
const pending: PendingGateRecord[] = [];
|
|
167
242
|
let malformed = 0;
|
|
168
243
|
for (const line of lines) {
|
|
169
244
|
try {
|
|
170
|
-
const parsed = JSON.parse(line) as Partial<GateResolutionRecord
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
typeof parsed.op
|
|
174
|
-
typeof parsed.gate
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
245
|
+
const parsed = JSON.parse(line) as Partial<Omit<GateResolutionRecord, "kind">> &
|
|
246
|
+
Partial<Omit<PendingGateRecord, "kind">> & { kind?: string };
|
|
247
|
+
const common =
|
|
248
|
+
parsed.version === 1 && typeof parsed.op === "string" &&
|
|
249
|
+
typeof parsed.gate === "string" && typeof parsed.timestamp === "string";
|
|
250
|
+
if (!common) {
|
|
251
|
+
malformed++;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (parsed.kind === "pending") {
|
|
255
|
+
if (typeof parsed.expiresAt !== "string") {
|
|
256
|
+
malformed++;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
pending.push(parsed as PendingGateRecord);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (typeof parsed.resolvedBy !== "string") {
|
|
178
263
|
malformed++;
|
|
179
264
|
continue;
|
|
180
265
|
}
|
|
181
|
-
|
|
266
|
+
resolutions.push(parsed as GateResolutionRecord);
|
|
182
267
|
} catch {
|
|
183
268
|
malformed++;
|
|
184
269
|
}
|
|
185
270
|
}
|
|
186
|
-
return {
|
|
271
|
+
return { resolutions, pending, malformed };
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Read every gate-*resolution* record for `op`, oldest first — {@link readGateLedger} narrowed to the half every caller before #2119 wanted. Pending facts are not malformed lines and are not counted as such. */
|
|
275
|
+
export async function readGateResolutions(
|
|
276
|
+
op: string,
|
|
277
|
+
opts?: { cwd?: string },
|
|
278
|
+
): Promise<{ records: GateResolutionRecord[]; malformed: number }> {
|
|
279
|
+
const { resolutions, malformed } = await readGateLedger(op, opts);
|
|
280
|
+
return { records: resolutions, malformed };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** The most recent pending fact for `gate`, expired or not — the anchor {@link latestResolutionSince} measures a resolution against. `undefined` when the gate has never been recorded pending. */
|
|
284
|
+
export function latestPendingGate(
|
|
285
|
+
records: PendingGateRecord[],
|
|
286
|
+
gate: string,
|
|
287
|
+
): PendingGateRecord | undefined {
|
|
288
|
+
let latest: PendingGateRecord | undefined;
|
|
289
|
+
for (const r of records) {
|
|
290
|
+
if (r.gate !== gate) continue;
|
|
291
|
+
if (!latest || new Date(r.timestamp).getTime() >= new Date(latest.timestamp).getTime()) latest = r;
|
|
292
|
+
}
|
|
293
|
+
return latest;
|
|
187
294
|
}
|
|
188
295
|
|
|
189
296
|
/** The most recent resolution for `gate` recorded after `sinceIso` (a gated tick's own timestamp) — what `chant operator status` uses to tell a resolved gate from a still-pending one. `undefined` when no such resolution exists. */
|
package/src/lifecycle/index.ts
CHANGED
|
@@ -98,8 +98,8 @@ export interface ReleaseRecord {
|
|
|
98
98
|
/**
|
|
99
99
|
* Who approved the change at its durable approval gate (#1035). Populated for
|
|
100
100
|
* a gated change — the same identity the approver supplied when they cleared
|
|
101
|
-
* the
|
|
102
|
-
*
|
|
101
|
+
* the gate (`chant approve <op> <gate> --approver`, recorded on the gate
|
|
102
|
+
* resolution fact). Optional and omitted for an
|
|
103
103
|
* ungated change (nothing to approve), so `actor` alone answers "who made
|
|
104
104
|
* it" while `approver` answers "who approved it" only where a gate exists.
|
|
105
105
|
* Distinct from `actor`: the approver is deliberately a different person than
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run ledger tests (#2118) — the same git fixture `converge-ledger.test.ts`
|
|
3
|
+
* uses, so the two ledgers are exercised against one real orphan branch and a
|
|
4
|
+
* change to the shared plumbing under them fails in both places.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, test, expect } from "vitest";
|
|
7
|
+
import { withTestDir } from "@intentius/chant-test-utils";
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { writeFileSync } from "node:fs";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
import {
|
|
12
|
+
appendRunRecord,
|
|
13
|
+
readRunLedger,
|
|
14
|
+
buildRunRecord,
|
|
15
|
+
runEnvOf,
|
|
16
|
+
DEFAULT_RUN_ENV,
|
|
17
|
+
} from "./run-ledger";
|
|
18
|
+
import type { OpRunRecordInput } from "../op/runtime";
|
|
19
|
+
import { appendConvergeRecord, readConvergeLedger } from "./converge-ledger";
|
|
20
|
+
import { runOpLocally, OpRunFailure } from "../op/local-executor";
|
|
21
|
+
import type { ActivityFn } from "../op/activity-registry";
|
|
22
|
+
import type { OpConfig } from "../op/types";
|
|
23
|
+
|
|
24
|
+
function git(args: string[], cwd: string): { stdout: string; exitCode: number } {
|
|
25
|
+
const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
|
|
26
|
+
return { stdout: r.stdout ?? "", exitCode: r.status ?? -1 };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function initRepo(dir: string): Promise<void> {
|
|
30
|
+
git(["init", "-q", "-b", "main"], dir);
|
|
31
|
+
git(["config", "user.email", "test@chant.dev"], dir);
|
|
32
|
+
git(["config", "user.name", "Test"], dir);
|
|
33
|
+
writeFileSync(join(dir, "README.md"), "fixture\n");
|
|
34
|
+
git(["add", "README.md"], dir);
|
|
35
|
+
git(["commit", "-q", "-m", "init"], dir);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function makeInput(overrides?: Partial<OpRunRecordInput>): OpRunRecordInput {
|
|
39
|
+
return {
|
|
40
|
+
op: "alb-deploy",
|
|
41
|
+
env: "staging",
|
|
42
|
+
started: "2026-01-01T00:00:00.000Z",
|
|
43
|
+
ended: "2026-01-01T00:00:12.000Z",
|
|
44
|
+
status: "ok",
|
|
45
|
+
labels: { Apply: "true", Env: "staging" },
|
|
46
|
+
outcomes: {},
|
|
47
|
+
phases: [{ name: "Apply", status: "ok", steps: [{ fn: "kubectlApply", status: "ok", durationMs: 12_000 }] }],
|
|
48
|
+
...overrides,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const NO_PROFILES = {};
|
|
53
|
+
|
|
54
|
+
function opConfig(overrides?: Partial<OpConfig>): OpConfig {
|
|
55
|
+
return {
|
|
56
|
+
name: "alb-deploy",
|
|
57
|
+
overview: "deploy the alb",
|
|
58
|
+
labels: { Env: "staging" },
|
|
59
|
+
phases: [{ name: "Apply", steps: [{ kind: "activity", fn: "ok", args: {} }] }],
|
|
60
|
+
...overrides,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe("runEnvOf", () => {
|
|
65
|
+
test("reads labels.Env", () => {
|
|
66
|
+
expect(runEnvOf({ labels: { Env: "prod" } })).toBe("prod");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("falls back to the default env when the Op declares no labels", () => {
|
|
70
|
+
expect(runEnvOf({})).toBe(DEFAULT_RUN_ENV);
|
|
71
|
+
expect(runEnvOf({ labels: { Apply: "true" } })).toBe(DEFAULT_RUN_ENV);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("buildRunRecord", () => {
|
|
76
|
+
test("groups step records into phases in execution order and folds outcomes", () => {
|
|
77
|
+
const input = buildRunRecord(
|
|
78
|
+
{ name: "watch", labels: { Watch: "true", Env: "prod" } },
|
|
79
|
+
[
|
|
80
|
+
{ phase: "Snapshot", fn: "lifecycleSnapshot", status: "ok", durationMs: 5 },
|
|
81
|
+
{ phase: "Diff", fn: "lifecycleDiff", status: "ok", durationMs: 7, outcome: { name: "Drift", value: true } },
|
|
82
|
+
{ phase: "Diff", fn: "report", status: "skipped", durationMs: 0 },
|
|
83
|
+
],
|
|
84
|
+
{ started: "2026-01-01T00:00:00.000Z", ended: "2026-01-01T00:00:01.000Z", status: "ok" },
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(input.env).toBe("prod");
|
|
88
|
+
expect(input.status).toBe("ok");
|
|
89
|
+
expect(input.labels).toEqual({ Watch: "true", Env: "prod" });
|
|
90
|
+
expect(input.outcomes).toEqual({ Drift: true });
|
|
91
|
+
expect(input.phases.map((p) => [p.name, p.status])).toEqual([["Snapshot", "ok"], ["Diff", "ok"]]);
|
|
92
|
+
expect(input.phases[1].steps.map((s) => s.status)).toEqual(["ok", "skipped"]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("a phase with a failed step is fail; a phase whose steps all skipped is skipped", () => {
|
|
96
|
+
const input = buildRunRecord(
|
|
97
|
+
{ name: "deploy" },
|
|
98
|
+
[
|
|
99
|
+
{ phase: "Apply", fn: "kubectlApply", status: "fail", durationMs: 3, error: "boom" },
|
|
100
|
+
{ phase: "Verify", fn: "httpCheck", status: "skipped", durationMs: 0 },
|
|
101
|
+
],
|
|
102
|
+
{ started: "2026-01-01T00:00:00.000Z", ended: "2026-01-01T00:00:01.000Z", status: "fail" },
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(input.status).toBe("fail");
|
|
106
|
+
expect(input.env).toBe(DEFAULT_RUN_ENV);
|
|
107
|
+
expect(input.phases.map((p) => [p.name, p.status])).toEqual([["Apply", "fail"], ["Verify", "skipped"]]);
|
|
108
|
+
expect(input.phases[0].steps[0].error).toBe("boom");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
describe("appendRunRecord / readRunLedger", () => {
|
|
113
|
+
test("round-trips a single record", async () => {
|
|
114
|
+
await withTestDir(async (dir) => {
|
|
115
|
+
await initRepo(dir);
|
|
116
|
+
const { record } = await appendRunRecord(makeInput(), { cwd: dir });
|
|
117
|
+
expect(record.version).toBe(1);
|
|
118
|
+
expect(record.id).toMatch(/^[0-9a-f-]{36}$/);
|
|
119
|
+
|
|
120
|
+
const { records, malformed } = await readRunLedger("staging", "alb-deploy", { cwd: dir });
|
|
121
|
+
expect(malformed).toBe(0);
|
|
122
|
+
expect(records).toEqual([record]);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("appends without clobbering prior records", async () => {
|
|
127
|
+
await withTestDir(async (dir) => {
|
|
128
|
+
await initRepo(dir);
|
|
129
|
+
await appendRunRecord(makeInput({ ended: "2026-01-01T00:00:12.000Z" }), { cwd: dir });
|
|
130
|
+
await appendRunRecord(makeInput({ ended: "2026-01-01T00:10:12.000Z" }), { cwd: dir });
|
|
131
|
+
await appendRunRecord(makeInput({ ended: "2026-01-01T00:20:12.000Z" }), { cwd: dir });
|
|
132
|
+
|
|
133
|
+
const { records } = await readRunLedger("staging", "alb-deploy", { cwd: dir });
|
|
134
|
+
expect(records.map((r) => r.ended)).toEqual([
|
|
135
|
+
"2026-01-01T00:00:12.000Z",
|
|
136
|
+
"2026-01-01T00:10:12.000Z",
|
|
137
|
+
"2026-01-01T00:20:12.000Z",
|
|
138
|
+
]);
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("one op's ledger is not another's, and neither disturbs the converge ledger in the same env", async () => {
|
|
143
|
+
await withTestDir(async (dir) => {
|
|
144
|
+
await initRepo(dir);
|
|
145
|
+
await appendRunRecord(makeInput({ op: "alb-deploy" }), { cwd: dir });
|
|
146
|
+
await appendRunRecord(makeInput({ op: "alb-teardown" }), { cwd: dir });
|
|
147
|
+
await appendConvergeRecord(
|
|
148
|
+
{
|
|
149
|
+
op: "staging-converge",
|
|
150
|
+
env: "staging",
|
|
151
|
+
timestamp: "2026-01-01T00:00:00.000Z",
|
|
152
|
+
firedRuleIds: [],
|
|
153
|
+
outcomes: [],
|
|
154
|
+
summary: { drifted: 0, remediated: 0, reported: 0, skippedBudget: 0, skippedFlap: 0, unobserved: 0, adopted: 0 },
|
|
155
|
+
log: "converge(staging): drifted=0",
|
|
156
|
+
},
|
|
157
|
+
{ cwd: dir },
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
expect((await readRunLedger("staging", "alb-deploy", { cwd: dir })).records).toHaveLength(1);
|
|
161
|
+
expect((await readRunLedger("staging", "alb-teardown", { cwd: dir })).records).toHaveLength(1);
|
|
162
|
+
expect((await readConvergeLedger("staging", { cwd: dir })).records).toHaveLength(1);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test("an unwritten ledger reads empty — never throws", async () => {
|
|
167
|
+
await withTestDir(async (dir) => {
|
|
168
|
+
await initRepo(dir);
|
|
169
|
+
const { records, malformed } = await readRunLedger("nowhere", "nothing", { cwd: dir });
|
|
170
|
+
expect(records).toEqual([]);
|
|
171
|
+
expect(malformed).toBe(0);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("skips malformed lines but reports the count", async () => {
|
|
176
|
+
await withTestDir(async (dir) => {
|
|
177
|
+
await initRepo(dir);
|
|
178
|
+
await appendRunRecord(makeInput(), { cwd: dir });
|
|
179
|
+
|
|
180
|
+
const { readBlobFromPath, writeBlobToPath } = await import("./git");
|
|
181
|
+
const existing = await readBlobFromPath("staging", "runs__alb-deploy.jsonl", { cwd: dir });
|
|
182
|
+
const corrupted = `${existing}\n${JSON.stringify({ version: 1, op: "alb-deploy", env: "staging" })}`;
|
|
183
|
+
await writeBlobToPath("staging", "runs__alb-deploy.jsonl", corrupted, "corrupt", { cwd: dir });
|
|
184
|
+
|
|
185
|
+
const { records, malformed } = await readRunLedger("staging", "alb-deploy", { cwd: dir });
|
|
186
|
+
expect(records).toHaveLength(1);
|
|
187
|
+
expect(malformed).toBe(1);
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("a gated record written by hand round-trips (#2119 produces these; the type carries the status now)", async () => {
|
|
192
|
+
await withTestDir(async (dir) => {
|
|
193
|
+
await initRepo(dir);
|
|
194
|
+
await appendRunRecord(
|
|
195
|
+
makeInput({
|
|
196
|
+
status: "gated",
|
|
197
|
+
phases: [
|
|
198
|
+
{ name: "Plan", status: "ok", steps: [{ fn: "terraformPlan", status: "ok", durationMs: 900 }] },
|
|
199
|
+
{ name: "Approve", status: "skipped", steps: [{ fn: "gate:approve-apply", status: "skipped", durationMs: 0 }] },
|
|
200
|
+
],
|
|
201
|
+
}),
|
|
202
|
+
{ cwd: dir },
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const { records } = await readRunLedger("staging", "alb-deploy", { cwd: dir });
|
|
206
|
+
expect(records[0].status).toBe("gated");
|
|
207
|
+
expect(records[0].phases[1]).toMatchObject({ name: "Approve", status: "skipped" });
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe("runOpLocally → the run ledger", () => {
|
|
213
|
+
test("a successful run appends exactly one record, and the result carries the same document", async () => {
|
|
214
|
+
await withTestDir(async (dir) => {
|
|
215
|
+
await initRepo(dir);
|
|
216
|
+
const activities = new Map<string, ActivityFn>([["ok", async () => ({ drifted: false })]]);
|
|
217
|
+
const config = opConfig({
|
|
218
|
+
phases: [
|
|
219
|
+
{
|
|
220
|
+
name: "Apply",
|
|
221
|
+
steps: [{ kind: "activity", fn: "ok", args: {}, outcomeAttribute: { name: "Drift", from: "drifted" } }],
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
const result = await runOpLocally(config, activities, NO_PROFILES, undefined, { ledger: { cwd: dir } });
|
|
227
|
+
|
|
228
|
+
const { records } = await readRunLedger("staging", "alb-deploy", { cwd: dir });
|
|
229
|
+
expect(records).toHaveLength(1);
|
|
230
|
+
expect(records[0]).toEqual(result.record);
|
|
231
|
+
expect(records[0].status).toBe("ok");
|
|
232
|
+
expect(records[0].outcomes).toEqual({ Drift: false });
|
|
233
|
+
expect(records[0].labels).toEqual({ Env: "staging" });
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("a failed run appends a fail record and still rejects with OpRunFailure", async () => {
|
|
238
|
+
await withTestDir(async (dir) => {
|
|
239
|
+
await initRepo(dir);
|
|
240
|
+
const activities = new Map<string, ActivityFn>([
|
|
241
|
+
["ok", async () => { throw new Error("boom"); }],
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
const err = await runOpLocally(opConfig(), activities, NO_PROFILES, undefined, { ledger: { cwd: dir } })
|
|
245
|
+
.catch((e: unknown) => e);
|
|
246
|
+
expect(err).toBeInstanceOf(OpRunFailure);
|
|
247
|
+
|
|
248
|
+
const { records } = await readRunLedger("staging", "alb-deploy", { cwd: dir });
|
|
249
|
+
expect(records).toHaveLength(1);
|
|
250
|
+
expect(records[0].status).toBe("fail");
|
|
251
|
+
expect(records[0].phases[0].steps[0].error).toContain("boom");
|
|
252
|
+
expect((err as OpRunFailure).result.record).toEqual(records[0]);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("without the ledger option a run writes nothing, but still carries its record", async () => {
|
|
257
|
+
await withTestDir(async (dir) => {
|
|
258
|
+
await initRepo(dir);
|
|
259
|
+
const activities = new Map<string, ActivityFn>([["ok", async () => ({})]]);
|
|
260
|
+
|
|
261
|
+
const result = await runOpLocally(opConfig(), activities, NO_PROFILES);
|
|
262
|
+
|
|
263
|
+
expect(result.record.op).toBe("alb-deploy");
|
|
264
|
+
expect(result.record.status).toBe("ok");
|
|
265
|
+
expect((await readRunLedger("staging", "alb-deploy", { cwd: dir })).records).toEqual([]);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
});
|