@intentius/chant 0.57.0 → 0.59.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 +36 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +26 -9
- 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/carve-bridge.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 +13 -0
- 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 +12 -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/plugins.d.ts +22 -0
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +40 -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 +24 -22
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +21 -6
- 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 +73 -37
- 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 +10 -22
- 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/detectLexicon.d.ts +13 -0
- package/dist/detectLexicon.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 +273 -16
- 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 +79 -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 +149 -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 +85 -30
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/change-signal.d.ts +91 -0
- package/dist/op/change-signal.d.ts.map +1 -0
- package/dist/op/composites/apply-op.d.ts +118 -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-name.d.ts +40 -0
- package/dist/op/gate-name.d.ts.map +1 -0
- package/dist/op/gate-summary.d.ts +43 -0
- package/dist/op/gate-summary.d.ts.map +1 -0
- 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 +29 -9
- 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 +185 -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 +93 -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 +96 -29
- 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/dist/terraform/bridge.d.ts +26 -7
- package/dist/terraform/bridge.d.ts.map +1 -1
- package/dist/terraform/carve-provider.d.ts +28 -2
- package/dist/terraform/carve-provider.d.ts.map +1 -1
- package/dist/terraform/data-source-shape.d.ts +77 -0
- package/dist/terraform/data-source-shape.d.ts.map +1 -0
- package/dist/terraform/graph.d.ts.map +1 -1
- package/dist/terraform/providers/kubernetes.d.ts +5 -0
- package/dist/terraform/providers/kubernetes.d.ts.map +1 -1
- package/dist/terraform/tier-map.d.ts +16 -4
- package/dist/terraform/tier-map.d.ts.map +1 -1
- package/dist/terraform/types.d.ts +8 -0
- package/dist/terraform/types.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 +84 -18
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +202 -18
- 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 +191 -11
- package/src/cli/commands/audit.ts +78 -28
- package/src/cli/commands/build.test.ts +330 -9
- package/src/cli/commands/build.ts +170 -24
- package/src/cli/commands/carve-bridge.test.ts +237 -14
- package/src/cli/commands/carve-bridge.ts +21 -17
- package/src/cli/commands/carve-emit-k8s.test.ts +17 -10
- 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 +194 -1
- package/src/cli/commands/lint.ts +205 -7
- 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 +341 -55
- package/src/cli/handlers/run.test.ts +568 -1295
- package/src/cli/handlers/run.ts +446 -912
- package/src/cli/main.test.ts +22 -16
- package/src/cli/main.ts +66 -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/plugins.test.ts +68 -2
- package/src/cli/plugins.ts +39 -0
- package/src/cli/registry.ts +40 -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 +4 -3
- package/src/components/__fixtures__/neo4j-fanout.json +1 -1
- 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 +28 -12
- package/src/components/cli-support.ts +60 -47
- package/src/components/component-schema.test.ts +18 -2
- package/src/components/component.schema.json +18 -5
- package/src/components/component.test.ts +2 -2
- package/src/components/component.ts +27 -7
- package/src/components/config-defaults.test.ts +2 -2
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +109 -20
- package/src/components/driver.ts +201 -84
- 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/neo4j-fanout.pilot.ts +3 -3
- 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.test.ts +19 -0
- package/src/components/verbs/run-agent.ts +12 -24
- 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/detectLexicon.ts +18 -1
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.test.ts +1 -1
- 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 +82 -2
- 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 +3 -3
- 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 +296 -16
- 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 +154 -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/pipeline-change-gate.test.ts +2 -2
- 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 +27 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +76 -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 +161 -0
- package/src/op/activities/reconcile.ts +354 -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 +125 -41
- package/src/op/change-signal.test.ts +117 -0
- package/src/op/change-signal.ts +169 -0
- package/src/op/composites/apply-op.ts +218 -0
- package/src/op/composites/composites.test.ts +379 -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 +110 -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 +29 -14
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate-name.test.ts +65 -0
- package/src/op/gate-name.ts +60 -0
- package/src/op/gate-summary.ts +84 -0
- 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 +48 -8
- package/src/op/local-executor.test.ts +178 -34
- package/src/op/local-executor.ts +371 -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 +292 -0
- package/src/op/op-ir.ts +419 -0
- package/src/op/op-verb-class.test.ts +2 -2
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +28 -7
- package/src/op/operator.test.ts +543 -6
- package/src/op/operator.ts +203 -20
- 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 +106 -26
- 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/src/terraform/aws-resources.test.ts +13 -4
- package/src/terraform/bridge.test.ts +22 -9
- package/src/terraform/bridge.ts +89 -28
- package/src/terraform/carve-provider.ts +38 -2
- package/src/terraform/data-source-shape.ts +95 -0
- package/src/terraform/graph.ts +35 -7
- package/src/terraform/providers/kubernetes.ts +48 -2
- package/src/terraform/tier-map.ts +21 -6
- package/src/terraform/types.ts +8 -0
- 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
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ApplyOp composite — the code → cloud workflow as an Op.
|
|
3
|
+
*
|
|
4
|
+
* Compute the plan, then apply via the target's native mechanism. Authority
|
|
5
|
+
* stays with the platform (the CloudFormation stack, the Kubernetes API
|
|
6
|
+
* server, the ARM resource group) — chant never hosts a state file.
|
|
7
|
+
*
|
|
8
|
+
* Phases: build → plan → [approve] → apply. Deletes ride the native
|
|
9
|
+
* prune path, whose bound is per target: the ownership marker on `kubectl`,
|
|
10
|
+
* the ownership tag on `arm` (chant #1448 — previously `--mode Complete`, which
|
|
11
|
+
* removed resources chant never applied), the ownership label on `gcp`
|
|
12
|
+
* (chant #1449), the metadata marker (machines) and managed app
|
|
13
|
+
* (volumes/ips/certs/secrets) on `fly` (chant #1449), and the stack itself on
|
|
14
|
+
* `cloudformation`, which a resource CFN did not create is not in. All of
|
|
15
|
+
* them are owned-only. See {@link DeleteMode}.
|
|
16
|
+
*
|
|
17
|
+
* A gate is a fact, not a wait (#2119): the local executor reads the gate
|
|
18
|
+
* ledger, and a run that finds no resolution ends `gated` with the `chant
|
|
19
|
+
* approve <op> <gate>` line to run. Approve, run again, and the same Op
|
|
20
|
+
* converges — no durable workflow engine is involved.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // additive, local executor
|
|
25
|
+
* export const { op } = ApplyOp({ name: "prod-apply", env: "prod", target: "kubectl" });
|
|
26
|
+
*
|
|
27
|
+
* // gated destructive apply — `chant approve prod-apply approve-apply`, then re-run
|
|
28
|
+
* export const { op } = ApplyOp({
|
|
29
|
+
* name: "prod-apply",
|
|
30
|
+
* env: "prod",
|
|
31
|
+
* target: "kubectl",
|
|
32
|
+
* delete: "gated",
|
|
33
|
+
* gate: { gate: "approve-apply", description: "Approve prod apply with deletes" },
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @see #112 — stateless-authoritative state model + live import
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { Op, phase, activity, gate } from "../builders";
|
|
41
|
+
import { gateName } from "../gate-name";
|
|
42
|
+
import type { OpResource } from "../resource";
|
|
43
|
+
import { defaultOutput, hasNativeRollback, type ApplyTarget, type DeleteMode } from "../activities/apply";
|
|
44
|
+
|
|
45
|
+
export interface ApplyOpConfig {
|
|
46
|
+
/** Op name (kebab-case). */
|
|
47
|
+
name: string;
|
|
48
|
+
/** Environment — CFN stack name / ARM resource group / kube context env;
|
|
49
|
+
* a log label on `gcp` and `fly`. */
|
|
50
|
+
env: string;
|
|
51
|
+
/** Native apply mechanism. Default: "kubectl". */
|
|
52
|
+
target?: ApplyTarget;
|
|
53
|
+
/** Built manifest/template path. Default per target: `dist` (dir) for kubectl,
|
|
54
|
+
* `template.json` (file) for CloudFormation/ARM, `dist/gcp.yaml` for gcp,
|
|
55
|
+
* `dist/fly.json` for fly. Must match your build output. */
|
|
56
|
+
output?: string;
|
|
57
|
+
/** Project directory to build. Default: ".". */
|
|
58
|
+
path?: string;
|
|
59
|
+
/** Delete handling. Default: "never". */
|
|
60
|
+
delete?: DeleteMode;
|
|
61
|
+
/**
|
|
62
|
+
* Effect handling (#1834, epic #1703 decision 6). `"gated"` routes the run
|
|
63
|
+
* through the approval gate whenever the plan proposes an effect, so the Plan
|
|
64
|
+
* phase's effect-will-fire rows (#1832) are reviewed before any effect
|
|
65
|
+
* runs. A run that reaches the gate unapproved ends `gated` and is answered
|
|
66
|
+
* by `chant approve`, the same shape `delete: "gated"` has. The generic
|
|
67
|
+
* apply itself never writes a receipt regardless (#1832); only an
|
|
68
|
+
* `effect()` step does, on success, last.
|
|
69
|
+
*/
|
|
70
|
+
effects?: "gated";
|
|
71
|
+
/**
|
|
72
|
+
* Approval gate before the apply. Implied when `delete: "gated"`; may also be
|
|
73
|
+
* set explicitly. Omit `gate` to default to `approve-<name>`. The gate
|
|
74
|
+
* is resolved by `chant approve` on the ledger (#2119), which the next run
|
|
75
|
+
* reads; nothing waits in the meantime.
|
|
76
|
+
*
|
|
77
|
+
* `signalName` is the key `gate` carried through 0.58.0 (#2202): still read,
|
|
78
|
+
* removed in 0.60.0.
|
|
79
|
+
*/
|
|
80
|
+
gate?: {
|
|
81
|
+
gate?: string;
|
|
82
|
+
/** @deprecated Renamed to `gate` in #2202. Accepted through 0.59.0, removed in 0.60.0. */
|
|
83
|
+
signalName?: string;
|
|
84
|
+
timeout?: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Saga-style rollback on partial apply failure, run as an `onFailure` phase.
|
|
89
|
+
*
|
|
90
|
+
* Compensation is total or refused (#1449): it only builds when the target
|
|
91
|
+
* has a rollback to run — a mapped native one (`cloudformation`, the aws
|
|
92
|
+
* lexicon's `rollbackStack`) or an explicit `{ command }`. Asking for it
|
|
93
|
+
* (`true`, or an object without a `command`) on any other target fails at
|
|
94
|
+
* build time rather than warning at rollback time.
|
|
95
|
+
*
|
|
96
|
+
* Defaults on for a destructive apply (`delete !== "never"`) when the target
|
|
97
|
+
* has a native rollback; a destructive apply on a target without one gets no
|
|
98
|
+
* compensation phase unless a `{ command }` supplies the rollback. Pass
|
|
99
|
+
* `false` to disable.
|
|
100
|
+
*/
|
|
101
|
+
compensate?: boolean | { command?: string };
|
|
102
|
+
/**
|
|
103
|
+
* kubectl target only (chant #1075). Let the server-side apply take
|
|
104
|
+
* ownership of fields another field manager already owns, instead of failing
|
|
105
|
+
* with a conflict naming them.
|
|
106
|
+
*
|
|
107
|
+
* Off unless set, and chant never turns it on by itself — a conflict means
|
|
108
|
+
* something else in the cluster believes it owns that field, and deciding
|
|
109
|
+
* otherwise is a deliberate act. Set it when chant is meant to be the
|
|
110
|
+
* authority for those fields (adopting objects a previous `kubectl apply`
|
|
111
|
+
* created is the usual case).
|
|
112
|
+
*/
|
|
113
|
+
forceConflicts?: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ApplyOpResources {
|
|
117
|
+
/** Op resource — the build→plan→[approve]→apply Op. */
|
|
118
|
+
op: InstanceType<typeof OpResource>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function ApplyOp(config: ApplyOpConfig): ApplyOpResources {
|
|
122
|
+
const target = config.target ?? "kubectl";
|
|
123
|
+
const output = config.output ?? defaultOutput(target);
|
|
124
|
+
const deleteMode = config.delete ?? "never";
|
|
125
|
+
const gated = deleteMode === "gated" || config.effects === "gated" || config.gate !== undefined;
|
|
126
|
+
|
|
127
|
+
const phases = [
|
|
128
|
+
phase("Build", [activity("chantBuild", { path: config.path ?? "." })]),
|
|
129
|
+
phase("Plan", [
|
|
130
|
+
{
|
|
131
|
+
kind: "activity" as const,
|
|
132
|
+
fn: "lifecycleDiff",
|
|
133
|
+
args: { env: config.env, live: true },
|
|
134
|
+
outcomeAttribute: { name: "Drift", from: "drifted" },
|
|
135
|
+
},
|
|
136
|
+
]),
|
|
137
|
+
];
|
|
138
|
+
|
|
139
|
+
if (gated) {
|
|
140
|
+
phases.push(
|
|
141
|
+
phase("Approve", [
|
|
142
|
+
gate(gateName(config.gate ?? {}) || `approve-${config.name}`, {
|
|
143
|
+
...(config.gate?.timeout ? { timeout: config.gate.timeout } : {}),
|
|
144
|
+
description:
|
|
145
|
+
config.gate?.description ??
|
|
146
|
+
`Approve apply to ${config.env} (delete mode: ${deleteMode}` +
|
|
147
|
+
`${config.effects === "gated" ? ", effects: gated" : ""})`,
|
|
148
|
+
}),
|
|
149
|
+
]),
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
phases.push(
|
|
154
|
+
phase("Apply", [
|
|
155
|
+
activity(
|
|
156
|
+
"nativeApply",
|
|
157
|
+
{
|
|
158
|
+
target,
|
|
159
|
+
env: config.env,
|
|
160
|
+
output,
|
|
161
|
+
deleteMode,
|
|
162
|
+
// Only present when the author asked for it, so an Op that does not
|
|
163
|
+
// mention conflicts serializes exactly as it always has.
|
|
164
|
+
...(config.forceConflicts !== undefined ? { forceConflicts: config.forceConflicts } : {}),
|
|
165
|
+
},
|
|
166
|
+
"longInfra",
|
|
167
|
+
),
|
|
168
|
+
]),
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// Compensation is total or refused (#1449). A rollback path is either the
|
|
172
|
+
// target's mapped native rollback (cloudformation's rollbackStack) or an
|
|
173
|
+
// explicit command; without one there is nothing a Rollback phase could run,
|
|
174
|
+
// so asking for one is refused here — at build time, with the op named —
|
|
175
|
+
// rather than surfacing as a warn when the rollback is already needed.
|
|
176
|
+
const compensateCommand =
|
|
177
|
+
typeof config.compensate === "object" ? config.compensate.command : undefined;
|
|
178
|
+
const rollbackAvailable = hasNativeRollback(target) || compensateCommand !== undefined;
|
|
179
|
+
if (config.compensate !== undefined && config.compensate !== false && !rollbackAvailable) {
|
|
180
|
+
throw new Error(
|
|
181
|
+
`ApplyOp "${config.name}": compensate is enabled, but target "${target}" has no automatic ` +
|
|
182
|
+
`rollback — the only mapped one today is cloudformation's rollbackStack. Either supply ` +
|
|
183
|
+
`compensate: { command: "..." } with a rollback of your own, or set compensate: false.`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Compensation defaults on for destructive applies — a partial failure should
|
|
188
|
+
// unwind rather than leave the cloud half-applied — but only where a rollback
|
|
189
|
+
// path exists; a destructive apply on a rollback-less target gets no
|
|
190
|
+
// compensation phase rather than one that could only warn.
|
|
191
|
+
const compensateDefault = deleteMode !== "never" && rollbackAvailable;
|
|
192
|
+
const compensateEnabled = config.compensate === undefined ? compensateDefault : config.compensate !== false;
|
|
193
|
+
|
|
194
|
+
const op = Op({
|
|
195
|
+
name: config.name,
|
|
196
|
+
overview: `Apply declared source to the ${config.env} environment (code → cloud)`,
|
|
197
|
+
labels: {
|
|
198
|
+
Apply: "true",
|
|
199
|
+
Env: config.env,
|
|
200
|
+
},
|
|
201
|
+
phases,
|
|
202
|
+
...(compensateEnabled
|
|
203
|
+
? {
|
|
204
|
+
onFailure: [
|
|
205
|
+
phase("Rollback", [
|
|
206
|
+
activity("compensateApply", {
|
|
207
|
+
target,
|
|
208
|
+
env: config.env,
|
|
209
|
+
...(compensateCommand ? { command: compensateCommand } : {}),
|
|
210
|
+
}),
|
|
211
|
+
]),
|
|
212
|
+
],
|
|
213
|
+
}
|
|
214
|
+
: {}),
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return { op };
|
|
218
|
+
}
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composite unit tests (#2120) — WatchOp, ReconcileOp, ApplyOp and the
|
|
3
|
+
* receipt-staleness watch, moved here with the composites themselves. The
|
|
4
|
+
* serialization half of this suite stays in
|
|
5
|
+
* a hosting lexicon's own composites suite: that one asserts on its
|
|
6
|
+
* serializer's output, which is the lexicon's business.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, test, expect } from "vitest";
|
|
10
|
+
import { WatchOp } from "./watch-op";
|
|
11
|
+
import { ReconcileOp } from "./reconcile-op";
|
|
12
|
+
import { ApplyOp } from "./apply-op";
|
|
13
|
+
import { DECLARABLE_MARKER } from "../../declarable";
|
|
14
|
+
import { EffectReceipt, receiptExpectation } from "../../effect-receipt";
|
|
15
|
+
|
|
16
|
+
function getProps(entity: unknown): Record<string, unknown> {
|
|
17
|
+
return (entity as { props: Record<string, unknown> }).props;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getEntityType(entity: unknown): string {
|
|
21
|
+
return (entity as Record<string, unknown>).entityType as string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ── WatchOp ──────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
describe("WatchOp: shape", () => {
|
|
27
|
+
test("returns { op } — the cadence is on the op, not a second resource (#2120)", () => {
|
|
28
|
+
const result = WatchOp({ name: "prod-watch", env: "prod", schedule: "*/15 * * * *" });
|
|
29
|
+
expect(result.op).toBeDefined();
|
|
30
|
+
expect(Object.keys(result)).toEqual(["op"]);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("op has entityType Chant::Op", () => {
|
|
34
|
+
const { op } = WatchOp({ name: "prod-watch", env: "prod", schedule: "*/15 * * * *" });
|
|
35
|
+
expect(getEntityType(op)).toBe("Chant::Op");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("the op is Declarable", () => {
|
|
39
|
+
const { op } = WatchOp({ name: "prod-watch", env: "prod", schedule: "*/15 * * * *" });
|
|
40
|
+
expect((op as unknown as Record<symbol, unknown>)[DECLARABLE_MARKER]).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("op.schedule carries the cron with the only overlap policy there is", () => {
|
|
44
|
+
const { op } = WatchOp({ name: "prod-watch", env: "prod", schedule: "*/15 * * * *" });
|
|
45
|
+
expect(getProps(op).schedule).toEqual({ cron: "*/15 * * * *", overlap: "skip" });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("no cron: a one-shot watch, no schedule on the op", () => {
|
|
49
|
+
const { op } = WatchOp({ name: "prod-watch", env: "prod" });
|
|
50
|
+
expect(getProps(op).schedule).toBeUndefined();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("an invalid cron is refused at construction with cronSyntaxMessage's wording", () => {
|
|
54
|
+
expect(() => WatchOp({ name: "prod-watch", env: "prod", schedule: "every 15 minutes" })).toThrow(
|
|
55
|
+
/does not look like valid 5- or 6-field cron syntax/,
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("WatchOp: configuration", () => {
|
|
61
|
+
test("op has Snapshot + Diff phases referencing the right activities", () => {
|
|
62
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "*/15 * * * *" });
|
|
63
|
+
const phases = (getProps(op).phases as Array<Record<string, unknown>>) ?? [];
|
|
64
|
+
expect(phases.map((p) => p.name)).toEqual(["Snapshot", "Diff"]);
|
|
65
|
+
const snapStep = (phases[0].steps as Array<Record<string, unknown>>)[0];
|
|
66
|
+
const diffStep = (phases[1].steps as Array<Record<string, unknown>>)[0];
|
|
67
|
+
expect(snapStep.fn).toBe("lifecycleSnapshot");
|
|
68
|
+
expect(diffStep.fn).toBe("lifecycleDiff");
|
|
69
|
+
expect(snapStep.args).toEqual({ env: "prod" });
|
|
70
|
+
expect(diffStep.args).toEqual({ env: "prod", live: true });
|
|
71
|
+
// Drift is surfaced as a run outcome via outcomeAttribute (#41)
|
|
72
|
+
expect(diffStep.outcomeAttribute).toEqual({ name: "Drift", from: "drifted" });
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("labels include Watch + Env", () => {
|
|
76
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "* * * * *" });
|
|
77
|
+
expect(getProps(op).labels).toEqual({ Watch: "true", Env: "prod" });
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
test("the configured cron reaches op.schedule verbatim", () => {
|
|
81
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "0 0 * * *" });
|
|
82
|
+
expect((getProps(op).schedule as Record<string, unknown>).cron).toBe("0 0 * * *");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("live: false produces a digest-only diff step", () => {
|
|
86
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "* * * * *", live: false });
|
|
87
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
88
|
+
const diffStep = (phases[1].steps as Array<Record<string, unknown>>)[0];
|
|
89
|
+
expect(diffStep.args).toEqual({ env: "prod", live: false });
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ── ReconcileOp ──────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
describe("ReconcileOp: shape", () => {
|
|
96
|
+
test("one-shot (no cron) returns an op with no cadence", () => {
|
|
97
|
+
const result = ReconcileOp({ name: "prod-reconcile", env: "prod" });
|
|
98
|
+
expect(result.op).toBeDefined();
|
|
99
|
+
expect(getProps(result.op).schedule).toBeUndefined();
|
|
100
|
+
expect(getEntityType(result.op)).toBe("Chant::Op");
|
|
101
|
+
expect((result.op as unknown as Record<symbol, unknown>)[DECLARABLE_MARKER]).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test("with a cron the op carries the schedule and nothing else is returned", () => {
|
|
105
|
+
const result = ReconcileOp({ name: "prod-reconcile", env: "prod", schedule: "0 * * * *" });
|
|
106
|
+
expect(Object.keys(result)).toEqual(["op"]);
|
|
107
|
+
expect(getProps(result.op).schedule).toEqual({ cron: "0 * * * *", overlap: "skip" });
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
describe("ReconcileOp: configuration", () => {
|
|
112
|
+
test("phases are Snapshot → Plan → Reconcile with the right activities", () => {
|
|
113
|
+
const { op } = ReconcileOp({ name: "p", env: "prod" });
|
|
114
|
+
const phases = (getProps(op).phases as Array<Record<string, unknown>>) ?? [];
|
|
115
|
+
expect(phases.map((p) => p.name)).toEqual(["Snapshot", "Plan", "Reconcile"]);
|
|
116
|
+
const reconcileStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
117
|
+
expect(reconcileStep.fn).toBe("reconcilePr");
|
|
118
|
+
expect(reconcileStep.args).toEqual({ env: "prod", mode: "pull-request", owned: false });
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("scope.owned + onDrift flow into the reconcilePr step", () => {
|
|
122
|
+
const { op } = ReconcileOp({ name: "p", env: "prod", onDrift: "issue", scope: { owned: true } });
|
|
123
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
124
|
+
const reconcileStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
125
|
+
expect(reconcileStep.args).toEqual({ env: "prod", mode: "issue", owned: true });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
test("labels include Reconcile + Env", () => {
|
|
129
|
+
const { op } = ReconcileOp({ name: "p", env: "prod" });
|
|
130
|
+
expect(getProps(op).labels).toEqual({ Reconcile: "true", Env: "prod" });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("Plan phase surfaces Drift as a run outcome", () => {
|
|
134
|
+
const { op } = ReconcileOp({ name: "p", env: "prod" });
|
|
135
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
136
|
+
const diffStep = (phases[1].steps as Array<Record<string, unknown>>)[0];
|
|
137
|
+
expect(diffStep.outcomeAttribute).toEqual({ name: "Drift", from: "drifted" });
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// ── ApplyOp ──────────────────────────────────────────────────────────
|
|
142
|
+
|
|
143
|
+
describe("ApplyOp: shape", () => {
|
|
144
|
+
test("returns an op (no schedule)", () => {
|
|
145
|
+
const result = ApplyOp({ name: "prod-apply", env: "prod" });
|
|
146
|
+
expect(result.op).toBeDefined();
|
|
147
|
+
expect(getEntityType(result.op)).toBe("Chant::Op");
|
|
148
|
+
expect((result.op as unknown as Record<symbol, unknown>)[DECLARABLE_MARKER]).toBe(true);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("ungated apply: Build → Plan → Apply (no Approve phase)", () => {
|
|
152
|
+
const { op } = ApplyOp({ name: "p", env: "prod", target: "kubectl" });
|
|
153
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
154
|
+
expect(phases.map((p) => p.name)).toEqual(["Build", "Plan", "Apply"]);
|
|
155
|
+
const applyStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
156
|
+
expect(applyStep.fn).toBe("nativeApply");
|
|
157
|
+
expect(applyStep.args).toEqual({ target: "kubectl", env: "prod", output: "dist", deleteMode: "never" });
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
describe("ApplyOp: gating + deletes", () => {
|
|
162
|
+
test("delete: gated inserts an Approve gate phase before Apply", () => {
|
|
163
|
+
const { op } = ApplyOp({ name: "p", env: "prod", delete: "gated" });
|
|
164
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
165
|
+
expect(phases.map((p) => p.name)).toEqual(["Build", "Plan", "Approve", "Apply"]);
|
|
166
|
+
const gateStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
167
|
+
expect(gateStep.kind).toBe("gate");
|
|
168
|
+
expect(gateStep.gate).toBe("approve-p");
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("explicit gate config is honored", () => {
|
|
172
|
+
const { op } = ApplyOp({
|
|
173
|
+
name: "p",
|
|
174
|
+
env: "prod",
|
|
175
|
+
gate: { gate: "go", description: "ship it" },
|
|
176
|
+
});
|
|
177
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
178
|
+
const gateStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
179
|
+
expect(gateStep.gate).toBe("go");
|
|
180
|
+
expect(gateStep.description).toBe("ship it");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// #2202: the gate config's key is `gate`; `signalName` is read through 0.59.0.
|
|
184
|
+
test("the deprecated `signalName` key on the gate config still names the gate", () => {
|
|
185
|
+
const { op } = ApplyOp({
|
|
186
|
+
name: "p",
|
|
187
|
+
env: "prod",
|
|
188
|
+
gate: { signalName: "go", description: "ship it" },
|
|
189
|
+
});
|
|
190
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
191
|
+
const gateStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
192
|
+
expect(gateStep.gate).toBe("go");
|
|
193
|
+
expect(gateStep.signalName).toBeUndefined();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("deleteMode flows into the nativeApply step", () => {
|
|
197
|
+
const { op } = ApplyOp({ name: "p", env: "prod", delete: "owned-only" });
|
|
198
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
199
|
+
const applyStep = (phases.find((p) => p.name === "Apply")!.steps as Array<Record<string, unknown>>)[0];
|
|
200
|
+
expect((applyStep.args as Record<string, unknown>).deleteMode).toBe("owned-only");
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
test("labels include Apply + Env", () => {
|
|
204
|
+
const { op } = ApplyOp({ name: "p", env: "prod" });
|
|
205
|
+
expect(getProps(op).labels).toEqual({ Apply: "true", Env: "prod" });
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("ApplyOp: compensation (#125, total-or-refused in #1449)", () => {
|
|
210
|
+
test("destructive apply on a target with a native rollback compensates by default", () => {
|
|
211
|
+
const { op } = ApplyOp({ name: "p", env: "prod", target: "cloudformation", delete: "owned-only" });
|
|
212
|
+
const onFailure = getProps(op).onFailure as Array<Record<string, unknown>> | undefined;
|
|
213
|
+
expect(onFailure?.map((p) => p.name)).toEqual(["Rollback"]);
|
|
214
|
+
const step = (onFailure![0].steps as Array<Record<string, unknown>>)[0];
|
|
215
|
+
expect(step.fn).toBe("compensateApply");
|
|
216
|
+
expect(step.args).toEqual({ target: "cloudformation", env: "prod" });
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("destructive apply on a rollback-less target has no compensation by default", () => {
|
|
220
|
+
// Compensation is total: without a rollback path there is nothing the
|
|
221
|
+
// phase could run, so none is wired — rather than one that could only
|
|
222
|
+
// warn at rollback time.
|
|
223
|
+
const { op } = ApplyOp({ name: "p", env: "prod", target: "kubectl", delete: "owned-only" });
|
|
224
|
+
expect(getProps(op).onFailure).toBeUndefined();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("additive apply has no compensation by default", () => {
|
|
228
|
+
const { op } = ApplyOp({ name: "p", env: "prod", delete: "never" });
|
|
229
|
+
expect(getProps(op).onFailure).toBeUndefined();
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("compensate: false disables rollback even when destructive", () => {
|
|
233
|
+
const { op } = ApplyOp({
|
|
234
|
+
name: "p",
|
|
235
|
+
env: "prod",
|
|
236
|
+
target: "cloudformation",
|
|
237
|
+
delete: "owned-only",
|
|
238
|
+
compensate: false,
|
|
239
|
+
});
|
|
240
|
+
expect(getProps(op).onFailure).toBeUndefined();
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("compensate.command supplies a rollback for targets without a native one", () => {
|
|
244
|
+
const { op } = ApplyOp({
|
|
245
|
+
name: "p",
|
|
246
|
+
env: "prod",
|
|
247
|
+
target: "kubectl",
|
|
248
|
+
compensate: { command: "kubectl rollout undo deployment/web" },
|
|
249
|
+
});
|
|
250
|
+
const onFailure = getProps(op).onFailure as Array<Record<string, unknown>>;
|
|
251
|
+
const step = (onFailure[0].steps as Array<Record<string, unknown>>)[0];
|
|
252
|
+
expect((step.args as Record<string, unknown>).command).toBe("kubectl rollout undo deployment/web");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test("compensate: true on cloudformation builds — the native rollbackStack is the path", () => {
|
|
256
|
+
const { op } = ApplyOp({ name: "p", env: "prod", target: "cloudformation", compensate: true });
|
|
257
|
+
const onFailure = getProps(op).onFailure as Array<Record<string, unknown>>;
|
|
258
|
+
const step = (onFailure[0].steps as Array<Record<string, unknown>>)[0];
|
|
259
|
+
expect(step.fn).toBe("compensateApply");
|
|
260
|
+
expect((step.args as Record<string, unknown>).command).toBeUndefined();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("compensate: true is refused at build time for every rollback-less target (#1449)", () => {
|
|
264
|
+
for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly"] as const) {
|
|
265
|
+
expect(() => ApplyOp({ name: "p", env: "prod", target, compensate: true })).toThrow(
|
|
266
|
+
new RegExp(`ApplyOp "p": compensate is enabled, but target "${target}" has no automatic rollback`),
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test("the refusal names the two ways out — compensate.command or compensate: false", () => {
|
|
272
|
+
const err = (() => {
|
|
273
|
+
try {
|
|
274
|
+
ApplyOp({ name: "web-apply", env: "prod", target: "fly", compensate: true });
|
|
275
|
+
return undefined;
|
|
276
|
+
} catch (e) {
|
|
277
|
+
return String(e);
|
|
278
|
+
}
|
|
279
|
+
})();
|
|
280
|
+
expect(err).toMatch(/compensate: \{ command/);
|
|
281
|
+
expect(err).toMatch(/compensate: false/);
|
|
282
|
+
expect(err).toMatch(/rollbackStack/);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("an object without a command is refused the same way as true", () => {
|
|
286
|
+
for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly"] as const) {
|
|
287
|
+
expect(() => ApplyOp({ name: "p", env: "prod", target, compensate: {} })).toThrow(
|
|
288
|
+
/has no automatic rollback/,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("a command lifts the refusal on every target", () => {
|
|
294
|
+
for (const target of ["kubectl", "kustomize", "arm", "gcp", "fly", "cloudformation"] as const) {
|
|
295
|
+
const { op } = ApplyOp({
|
|
296
|
+
name: "p",
|
|
297
|
+
env: "prod",
|
|
298
|
+
target,
|
|
299
|
+
compensate: { command: "echo rollback" },
|
|
300
|
+
});
|
|
301
|
+
const onFailure = getProps(op).onFailure as Array<Record<string, unknown>>;
|
|
302
|
+
const step = (onFailure[0].steps as Array<Record<string, unknown>>)[0];
|
|
303
|
+
expect((step.args as Record<string, unknown>).command).toBe("echo rollback");
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// ── ApplyOp: gated effects (#1834) ───────────────────────────────────
|
|
309
|
+
|
|
310
|
+
describe("ApplyOp: effects gated (#1834, #1703 decision 6)", () => {
|
|
311
|
+
test("effects: gated inserts the Approve gate phase before Apply, like delete: gated", () => {
|
|
312
|
+
const { op } = ApplyOp({ name: "p", env: "prod", effects: "gated" });
|
|
313
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
314
|
+
expect(phases.map((p) => p.name)).toEqual(["Build", "Plan", "Approve", "Apply"]);
|
|
315
|
+
const gateStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
316
|
+
expect(gateStep.kind).toBe("gate");
|
|
317
|
+
expect(gateStep.gate).toBe("approve-p");
|
|
318
|
+
expect(gateStep.description).toBe("Approve apply to prod (delete mode: never, effects: gated)");
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("effects: gated does not change the delete mode riding into nativeApply", () => {
|
|
322
|
+
const { op } = ApplyOp({ name: "p", env: "prod", effects: "gated" });
|
|
323
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
324
|
+
const applyStep = (phases.find((p) => p.name === "Apply")!.steps as Array<Record<string, unknown>>)[0];
|
|
325
|
+
expect((applyStep.args as Record<string, unknown>).deleteMode).toBe("never");
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
test("effects: gated composes with delete: gated in one Approve gate", () => {
|
|
329
|
+
const { op } = ApplyOp({ name: "p", env: "prod", delete: "gated", effects: "gated" });
|
|
330
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
331
|
+
expect(phases.map((p) => p.name)).toEqual(["Build", "Plan", "Approve", "Apply"]);
|
|
332
|
+
const gateStep = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
333
|
+
expect(gateStep.description).toBe("Approve apply to prod (delete mode: gated, effects: gated)");
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// ── WatchOp: receipt staleness (#1834) ───────────────────────────────
|
|
339
|
+
|
|
340
|
+
describe("WatchOp: receipt staleness (#1834)", () => {
|
|
341
|
+
const seeded = EffectReceipt("seeded", {
|
|
342
|
+
effect: "db-seed",
|
|
343
|
+
flavor: "hash",
|
|
344
|
+
inputs: { file: "seed.sql" },
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
test("without receipts the op is unchanged (no Receipts phase)", () => {
|
|
348
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "* * * * *" });
|
|
349
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
350
|
+
expect(phases.map((p) => p.name)).toEqual(["Snapshot", "Diff"]);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("receipts add a read-only Receipts phase carrying identity + expectation data", () => {
|
|
354
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "* * * * *", receipts: [seeded] });
|
|
355
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
356
|
+
expect(phases.map((p) => p.name)).toEqual(["Snapshot", "Diff", "Receipts"]);
|
|
357
|
+
const step = (phases[2].steps as Array<Record<string, unknown>>)[0];
|
|
358
|
+
expect(step.fn).toBe("receiptStaleness");
|
|
359
|
+
expect(step.args).toEqual({
|
|
360
|
+
receipts: [
|
|
361
|
+
{
|
|
362
|
+
receipt: { name: "seeded", effect: "db-seed", flavor: "hash", inputs: { file: "seed.sql" } },
|
|
363
|
+
expectation: receiptExpectation(seeded),
|
|
364
|
+
},
|
|
365
|
+
],
|
|
366
|
+
});
|
|
367
|
+
// Staleness surfaces as a run outcome, like Drift.
|
|
368
|
+
expect(step.outcomeAttribute).toEqual({ name: "StaleReceipts", from: "stale" });
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
test("the Receipts phase runs nothing: its only step is the staleness read", () => {
|
|
372
|
+
const { op } = WatchOp({ name: "p", env: "prod", schedule: "* * * * *", receipts: [seeded] });
|
|
373
|
+
const phases = getProps(op).phases as Array<Record<string, unknown>>;
|
|
374
|
+
const steps = phases[2].steps as Array<Record<string, unknown>>;
|
|
375
|
+
expect(steps).toHaveLength(1);
|
|
376
|
+
expect(steps.map((s) => s.fn)).toEqual(["receiptStaleness"]);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
});
|