@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
|
@@ -1,17 +1,35 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import type { OpRunResult } from "./local-executor";
|
|
3
|
+
import { buildRunRecord } from "../lifecycle/run-ledger";
|
|
4
|
+
import type { OpRunRecord } from "./runtime";
|
|
3
5
|
import { renderHuman, renderJson } from "./local-output";
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Attach the ledger record the executor would have built (#2118), so a fixture
|
|
9
|
+
* result is shaped like a real one and `renderJson` has something to print.
|
|
10
|
+
*/
|
|
11
|
+
function withRecord(partial: Omit<OpRunResult, "record">): OpRunResult {
|
|
12
|
+
const input = buildRunRecord({ name: partial.op }, partial.records, {
|
|
13
|
+
started: partial.startedAt,
|
|
14
|
+
ended: "2026-09-05T12:00:01.000Z",
|
|
15
|
+
status: partial.status,
|
|
16
|
+
id: "11111111-2222-4333-8444-555555555555",
|
|
17
|
+
...(partial.gate ? { gate: { name: partial.gate.gate, since: partial.gate.timestamp } } : {}),
|
|
18
|
+
});
|
|
19
|
+
return { ...partial, record: { version: 1, ...input, id: input.id! } };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const RESULT: OpRunResult = withRecord({
|
|
6
23
|
op: "hello",
|
|
7
24
|
totalMs: 100,
|
|
8
|
-
|
|
25
|
+
status: "ok",
|
|
26
|
+
startedAt: "2026-09-05T12:00:00.000Z",
|
|
9
27
|
records: [
|
|
10
28
|
{ phase: "Greet", fn: "shellCmd", args: { cmd: "echo hello from chant" }, status: "ok", durationMs: 42 },
|
|
11
29
|
{ phase: "Check", fn: "lifecycleDiff", args: { env: "prod" }, status: "ok", durationMs: 1200,
|
|
12
30
|
outcome: { name: "Drift", value: false } },
|
|
13
31
|
],
|
|
14
|
-
};
|
|
32
|
+
});
|
|
15
33
|
|
|
16
34
|
describe("renderHuman", () => {
|
|
17
35
|
test("renders phase banners, step lines, outcomes, and a summary", () => {
|
|
@@ -27,10 +45,10 @@ describe("renderHuman", () => {
|
|
|
27
45
|
});
|
|
28
46
|
|
|
29
47
|
test("renders failures with ✗ and the error", () => {
|
|
30
|
-
const failed
|
|
31
|
-
op: "deploy", totalMs: 50,
|
|
48
|
+
const failed = withRecord({
|
|
49
|
+
op: "deploy", totalMs: 50, status: "fail", startedAt: "2026-09-05T12:00:00.000Z",
|
|
32
50
|
records: [{ phase: "Apply", fn: "kubectlApply", args: { manifest: "x.yaml" }, status: "fail", durationMs: 10, error: "boom" }],
|
|
33
|
-
};
|
|
51
|
+
});
|
|
34
52
|
const lines: string[] = [];
|
|
35
53
|
renderHuman(failed, (l) => lines.push(l));
|
|
36
54
|
const out = lines.join("\n");
|
|
@@ -40,15 +58,81 @@ describe("renderHuman", () => {
|
|
|
40
58
|
});
|
|
41
59
|
});
|
|
42
60
|
|
|
61
|
+
const GATED: OpRunResult = withRecord({
|
|
62
|
+
op: "prod-apply",
|
|
63
|
+
totalMs: 300,
|
|
64
|
+
status: "gated",
|
|
65
|
+
startedAt: "2026-09-05T12:00:00.000Z",
|
|
66
|
+
gate: {
|
|
67
|
+
version: 1, kind: "pending", op: "prod-apply", gate: "rollout-gate",
|
|
68
|
+
description: "release manager signs off",
|
|
69
|
+
timestamp: "2026-09-05T12:00:00.000Z",
|
|
70
|
+
expiresAt: "2026-09-07T12:00:00.000Z",
|
|
71
|
+
url: "https://github.com/org/repo/pull/7",
|
|
72
|
+
},
|
|
73
|
+
records: [
|
|
74
|
+
{ phase: "Apply", fn: "gate:rollout-gate", args: {}, status: "skipped", durationMs: 0 },
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("renderHuman — gated (#2119)", () => {
|
|
79
|
+
test("names the gate, the approve line and the expiry, and points at no other runtime", () => {
|
|
80
|
+
const lines: string[] = [];
|
|
81
|
+
renderHuman(GATED, (l) => lines.push(l));
|
|
82
|
+
const out = lines.join("\n");
|
|
83
|
+
expect(out).toContain('Op "prod-apply" is gated on "rollout-gate"');
|
|
84
|
+
expect(out).toContain("release manager signs off");
|
|
85
|
+
expect(out).toContain("chant approve prod-apply rollout-gate");
|
|
86
|
+
expect(out).toContain("https://github.com/org/repo/pull/7");
|
|
87
|
+
expect(out).toContain("expires : 2026-09-07T12:00:00.000Z");
|
|
88
|
+
expect(out).not.toContain("--on");
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("shows the approver on a gate that passed", () => {
|
|
92
|
+
const lines: string[] = [];
|
|
93
|
+
renderHuman(withRecord({
|
|
94
|
+
op: "prod-apply", totalMs: 10, status: "ok", startedAt: "2026-09-05T12:00:00.000Z",
|
|
95
|
+
records: [{
|
|
96
|
+
phase: "Apply", fn: "gate:rollout-gate", args: {}, status: "ok", durationMs: 1,
|
|
97
|
+
approval: { gate: "rollout-gate", resolvedBy: "alex", timestamp: "2026-09-05T11:00:00.000Z" },
|
|
98
|
+
}],
|
|
99
|
+
}), (l) => lines.push(l));
|
|
100
|
+
expect(lines.join("\n")).toContain("[approved] alex at 2026-09-05T11:00:00.000Z");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
43
104
|
describe("renderJson", () => {
|
|
44
|
-
test("prints
|
|
105
|
+
test("prints the run's ledger record, not the raw result (#2118)", () => {
|
|
45
106
|
const lines: string[] = [];
|
|
46
107
|
renderJson(RESULT, (l) => lines.push(l));
|
|
47
108
|
expect(lines).toHaveLength(1);
|
|
48
|
-
const parsed = JSON.parse(lines[0]) as
|
|
109
|
+
const parsed = JSON.parse(lines[0]) as OpRunRecord;
|
|
110
|
+
expect(parsed).toEqual(RESULT.record);
|
|
49
111
|
expect(parsed.op).toBe("hello");
|
|
50
|
-
expect(parsed.
|
|
51
|
-
expect(parsed.
|
|
52
|
-
expect(parsed.
|
|
112
|
+
expect(parsed.status).toBe("ok");
|
|
113
|
+
expect(parsed.phases.map((p) => p.name)).toEqual(["Greet", "Check"]);
|
|
114
|
+
expect(parsed.outcomes).toEqual({ Drift: false });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("a failed run prints status fail with the failing step's error", () => {
|
|
118
|
+
const failed = withRecord({
|
|
119
|
+
op: "deploy", totalMs: 50, status: "fail", startedAt: "2026-09-05T12:00:00.000Z",
|
|
120
|
+
records: [{ phase: "Apply", fn: "kubectlApply", args: {}, status: "fail", durationMs: 10, error: "boom" }],
|
|
121
|
+
});
|
|
122
|
+
const lines: string[] = [];
|
|
123
|
+
renderJson(failed, (l) => lines.push(l));
|
|
124
|
+
const parsed = JSON.parse(lines[0]) as OpRunRecord;
|
|
125
|
+
expect(parsed.status).toBe("fail");
|
|
126
|
+
expect(parsed.phases[0]).toMatchObject({ name: "Apply", status: "fail" });
|
|
127
|
+
expect(parsed.phases[0].steps[0].error).toBe("boom");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("a gated record names its gate, and the approve command rides alongside", () => {
|
|
131
|
+
const lines: string[] = [];
|
|
132
|
+
renderJson(GATED, (l) => lines.push(l));
|
|
133
|
+
const parsed = JSON.parse(lines[0]) as OpRunRecord & { approve: string };
|
|
134
|
+
expect(parsed.status).toBe("gated");
|
|
135
|
+
expect(parsed.gate).toEqual({ name: "rollout-gate", since: "2026-09-05T12:00:00.000Z" });
|
|
136
|
+
expect(parsed.approve).toBe("chant approve prod-apply rollout-gate");
|
|
53
137
|
});
|
|
54
138
|
});
|
package/src/op/local-output.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { OpRunResult, StepRecord } from "./local-executor";
|
|
8
|
+
import { approveCommand } from "./gate";
|
|
8
9
|
|
|
9
10
|
type Writer = (line: string) => void;
|
|
10
11
|
|
|
@@ -42,22 +43,50 @@ export function renderHuman(result: OpRunResult, write: Writer = stderr): void {
|
|
|
42
43
|
if (record.outcome) {
|
|
43
44
|
write(` [outcome] ${record.outcome.name}=${String(record.outcome.value)}`);
|
|
44
45
|
}
|
|
46
|
+
if (record.approval) {
|
|
47
|
+
write(` [approved] ${record.approval.resolvedBy} at ${record.approval.timestamp}` +
|
|
48
|
+
(record.approval.url ? ` (${record.approval.url})` : ""));
|
|
49
|
+
}
|
|
45
50
|
if (record.error) {
|
|
46
51
|
write(` ${record.error}`);
|
|
47
52
|
}
|
|
48
53
|
}
|
|
49
54
|
|
|
50
55
|
const total = `${(result.totalMs / 1000).toFixed(1)}s`;
|
|
51
|
-
if (result.ok) {
|
|
56
|
+
if (result.status === "ok") {
|
|
52
57
|
write(`Op "${result.op}" completed in ${total}`);
|
|
53
|
-
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (result.status === "fail" || !result.gate) {
|
|
54
61
|
write(`Op "${result.op}" failed after ${total}`);
|
|
62
|
+
return;
|
|
55
63
|
}
|
|
64
|
+
|
|
65
|
+
// Gated (#2119): a standing fact and the one command that clears it. No
|
|
66
|
+
// hint at another runtime — the run ended here on purpose, and the next one
|
|
67
|
+
// re-reads the ledger.
|
|
68
|
+
const { gate } = result;
|
|
69
|
+
write(`Op "${result.op}" is gated on "${gate.gate}" after ${total}`);
|
|
70
|
+
if (gate.description) write(` ${gate.description}`);
|
|
71
|
+
write(` approve : ${approveCommand(gate.op, gate.gate)}`);
|
|
72
|
+
if (gate.url) write(` approve at: ${gate.url}`);
|
|
73
|
+
write(` expires : ${gate.expiresAt}`);
|
|
56
74
|
}
|
|
57
75
|
|
|
58
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Render the run's ledger record as JSON on stdout (and nothing else on
|
|
78
|
+
* stdout).
|
|
79
|
+
*
|
|
80
|
+
* The record, not the whole `OpRunResult` (#2118): what `chant run <op> --json`
|
|
81
|
+
* emits is byte-for-byte what the run appended to `<env>/runs__<op>.jsonl`, so
|
|
82
|
+
* a consumer reading stdout and one reading the ledger back later agree on
|
|
83
|
+
* every field, the run id included. A gated run's record names the gate it
|
|
84
|
+
* stopped on; the `approve` command line rides alongside so a CI job reporting
|
|
85
|
+
* the gate doesn't have to reassemble it from the op and gate names.
|
|
86
|
+
*/
|
|
59
87
|
export function renderJson(result: OpRunResult, write: Writer = stdout): void {
|
|
60
|
-
|
|
88
|
+
const approve = result.gate ? { approve: approveCommand(result.gate.op, result.gate.gate) } : {};
|
|
89
|
+
write(JSON.stringify({ ...result.record, ...approve }));
|
|
61
90
|
}
|
|
62
91
|
|
|
63
92
|
export type { OpRunResult, StepRecord };
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* op.json IR tests — chant #1289, ported to core with #2118.
|
|
3
|
+
*
|
|
4
|
+
* The lexicon-flavoured half of the original suite (workflow.ts byte-identity,
|
|
5
|
+
* this-lexicon contract coverage) stays in
|
|
6
|
+
* a hosting lexicon's own op-ir suite, which now drives the same code
|
|
7
|
+
* through the shim. What is here is what the IR itself promises: the `2.0`
|
|
8
|
+
* shape, determinism, the injected registries, and the round trip.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
import { activityContract, type ActivityContract } from "./activity-contract";
|
|
14
|
+
import { ACTIVITY_PROFILES } from "./activity-profiles";
|
|
15
|
+
import { phase, gate, effect, shell, kubectlApply, httpCheck, activity } from "./builders";
|
|
16
|
+
import { stepOutput } from "./step-output-ref";
|
|
17
|
+
import type { OpConfig } from "./types";
|
|
18
|
+
import { EffectReceipt, receiptExpectation } from "../effect-receipt";
|
|
19
|
+
import {
|
|
20
|
+
buildOpIR,
|
|
21
|
+
serializeOpIR,
|
|
22
|
+
opConfigFromIR,
|
|
23
|
+
OP_IR_FORMAT_VERSION,
|
|
24
|
+
type OpIR,
|
|
25
|
+
type OpIRActivityStep,
|
|
26
|
+
} from "./op-ir";
|
|
27
|
+
|
|
28
|
+
const seeded = EffectReceipt("seeded", {
|
|
29
|
+
effect: "db-seed",
|
|
30
|
+
flavor: "hash",
|
|
31
|
+
inputs: { file: "seed.sql" },
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
/** Representative Op: phases, labels, a gate, an effect, and onFailure compensation. */
|
|
35
|
+
function representativeOp(): OpConfig {
|
|
36
|
+
return {
|
|
37
|
+
name: "full-deploy",
|
|
38
|
+
overview: "Deploy with approval, a seeded effect, and rollback on failure",
|
|
39
|
+
depends: [],
|
|
40
|
+
labels: { Team: "infra", Env: "staging" },
|
|
41
|
+
phases: [
|
|
42
|
+
phase("Build", [shell("npm run build")]),
|
|
43
|
+
phase("Approve", [gate("approve-deploy", { timeout: "24h", description: "Release manager sign-off" })]),
|
|
44
|
+
phase("Deploy", [kubectlApply("dist/infra.yaml", { profile: "longInfra" })]),
|
|
45
|
+
phase("Seed", [effect(seeded, [shell("npm run db:seed")])]),
|
|
46
|
+
phase("Verify", [httpCheck("https://app.example.com/healthz")]),
|
|
47
|
+
],
|
|
48
|
+
onFailure: [phase("Rollback", [shell("kubectl delete -f dist/infra.yaml --ignore-not-found")])],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function contracts(): Map<string, ActivityContract> {
|
|
53
|
+
return new Map<string, ActivityContract>([
|
|
54
|
+
["shellCmd", activityContract("shellCmd", z.strictObject({ cmd: z.string() }))],
|
|
55
|
+
[
|
|
56
|
+
"httpCheck",
|
|
57
|
+
activityContract("httpCheck", z.strictObject({ url: z.string() }), z.strictObject({ status: z.number() }), {
|
|
58
|
+
entities: ["url"],
|
|
59
|
+
}),
|
|
60
|
+
],
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
describe("op.json IR", () => {
|
|
65
|
+
it("carries formatVersion 2.0 — the shape without taskQueue, namespace or searchAttributes (#2118)", () => {
|
|
66
|
+
expect(OP_IR_FORMAT_VERSION).toBe("2.0");
|
|
67
|
+
|
|
68
|
+
const ir = buildOpIR(representativeOp());
|
|
69
|
+
expect(ir.formatVersion).toBe("2.0");
|
|
70
|
+
expect(ir.labels).toEqual({ Team: "infra", Env: "staging" });
|
|
71
|
+
expect(ir).not.toHaveProperty("taskQueue");
|
|
72
|
+
expect(ir).not.toHaveProperty("namespace");
|
|
73
|
+
expect(ir).not.toHaveProperty("searchAttributes");
|
|
74
|
+
|
|
75
|
+
const text = serializeOpIR(representativeOp());
|
|
76
|
+
expect(text).toContain('"labels"');
|
|
77
|
+
expect(text).not.toContain("taskQueue");
|
|
78
|
+
expect(text).not.toContain("namespace");
|
|
79
|
+
expect(text).not.toContain("searchAttributes");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("an Op with no labels gets an empty object, not a missing key", () => {
|
|
83
|
+
const ir = buildOpIR({ name: "bare", overview: "o", phases: [] });
|
|
84
|
+
expect(ir.labels).toEqual({});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("carries the Op's own cadence, and omits the key when it declares none (#2120)", () => {
|
|
88
|
+
const scheduled = buildOpIR({
|
|
89
|
+
name: "nightly",
|
|
90
|
+
overview: "o",
|
|
91
|
+
phases: [],
|
|
92
|
+
schedule: { cron: "0 3 * * *", overlap: "skip" },
|
|
93
|
+
});
|
|
94
|
+
expect(scheduled.schedule).toEqual({ cron: "0 3 * * *", overlap: "skip" });
|
|
95
|
+
expect(serializeOpIR({ name: "nightly", overview: "o", phases: [], schedule: { cron: "0 3 * * *" } }))
|
|
96
|
+
.toContain('"cron": "0 3 * * *"');
|
|
97
|
+
|
|
98
|
+
expect(buildOpIR({ name: "bare", overview: "o", phases: [] })).not.toHaveProperty("schedule");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("round-trips a scheduled Op byte-identically (#2120)", () => {
|
|
102
|
+
const original: OpConfig = {
|
|
103
|
+
name: "nightly",
|
|
104
|
+
overview: "o",
|
|
105
|
+
phases: [phase("Only", [shell("echo hi")])],
|
|
106
|
+
schedule: { cron: "0 3 * * *", overlap: "skip" },
|
|
107
|
+
};
|
|
108
|
+
const text = serializeOpIR(original);
|
|
109
|
+
const reconstructed = opConfigFromIR(JSON.parse(text) as OpIR);
|
|
110
|
+
|
|
111
|
+
expect(reconstructed.schedule).toEqual({ cron: "0 3 * * *", overlap: "skip" });
|
|
112
|
+
expect(serializeOpIR(reconstructed)).toBe(text);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("two serializations of the same config are byte-identical (determinism)", () => {
|
|
116
|
+
const config = representativeOp();
|
|
117
|
+
expect(serializeOpIR(config)).toBe(serializeOpIR(config));
|
|
118
|
+
expect(serializeOpIR(representativeOp())).toBe(serializeOpIR(representativeOp()));
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("captures the full step graph: phases, gate, effect, onFailure", () => {
|
|
122
|
+
const ir = buildOpIR(representativeOp());
|
|
123
|
+
|
|
124
|
+
expect(ir.name).toBe("full-deploy");
|
|
125
|
+
const [build, approve, deploy, seed, verify] = ir.phases;
|
|
126
|
+
expect(build.steps[0]).toMatchObject({ kind: "activity", fn: "shellCmd", profile: "fastIdempotent" });
|
|
127
|
+
expect(approve.steps[0]).toMatchObject({
|
|
128
|
+
kind: "gate",
|
|
129
|
+
gate: "approve-deploy",
|
|
130
|
+
timeout: "24h",
|
|
131
|
+
description: "Release manager sign-off",
|
|
132
|
+
});
|
|
133
|
+
expect(deploy.steps[0]).toMatchObject({ kind: "activity", fn: "kubectlApply", profile: "longInfra" });
|
|
134
|
+
expect(seed.steps[0]).toMatchObject({
|
|
135
|
+
kind: "effect",
|
|
136
|
+
receipt: { name: "seeded", effect: "db-seed", flavor: "hash", inputs: { file: "seed.sql" } },
|
|
137
|
+
expectation: receiptExpectation(seeded),
|
|
138
|
+
});
|
|
139
|
+
expect(verify.steps[0]).toMatchObject({ kind: "activity", fn: "httpCheck", profile: "fastIdempotent" });
|
|
140
|
+
expect(ir.onFailure).toHaveLength(1);
|
|
141
|
+
expect(ir.onFailure[0].name).toBe("Rollback");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// #2202: whichever key an authored gate step used, the IR writes `gate`, so
|
|
145
|
+
// a foreign consumer of op.json never sees the deprecated spelling.
|
|
146
|
+
it("normalizes a gate step's deprecated `signalName` key onto `gate`", () => {
|
|
147
|
+
const config = representativeOp();
|
|
148
|
+
const approve = config.phases.find((p) => p.name === "Approve")!;
|
|
149
|
+
approve.steps[0] = { kind: "gate", signalName: "approve-deploy", timeout: "24h" };
|
|
150
|
+
const ir = buildOpIR(config);
|
|
151
|
+
expect(ir.phases[1].steps[0]).toMatchObject({ kind: "gate", gate: "approve-deploy", timeout: "24h" });
|
|
152
|
+
expect(serializeOpIR(config)).not.toContain("signalName");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("contracts are the injected registry's: empty until a caller supplies one", () => {
|
|
156
|
+
const bare = buildOpIR(representativeOp());
|
|
157
|
+
expect(bare.activityContracts).toEqual({});
|
|
158
|
+
// The step graph is unaffected — `profile` still resolves to its effective value.
|
|
159
|
+
expect((bare.phases[2].steps[0] as OpIRActivityStep).profile).toBe("longInfra");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("embeds core's profile table for every referenced profile, and the caller's contracts", () => {
|
|
163
|
+
const ir = buildOpIR(representativeOp(), contracts());
|
|
164
|
+
|
|
165
|
+
expect(ir.activityProfiles.fastIdempotent).toEqual(ACTIVITY_PROFILES.fastIdempotent);
|
|
166
|
+
expect(ir.activityProfiles.longInfra).toEqual(ACTIVITY_PROFILES.longInfra);
|
|
167
|
+
// No step used k8sWait, so it does not ride along.
|
|
168
|
+
expect(ir.activityProfiles.k8sWait).toBeUndefined();
|
|
169
|
+
|
|
170
|
+
expect(ir.activityContracts.shellCmd.args).toMatchObject({ type: "object" });
|
|
171
|
+
expect(ir.activityContracts.httpCheck.returns).toMatchObject({ type: "object" });
|
|
172
|
+
// kubectlApply has no contract in the passed registry — no entry, not an error.
|
|
173
|
+
expect(ir.activityContracts.kubectlApply).toBeUndefined();
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("resolves entity-identifying args into a step's entities and echoes the key list (#2022)", () => {
|
|
177
|
+
const ir = buildOpIR(representativeOp(), contracts());
|
|
178
|
+
const verify = ir.phases.find((p) => p.name === "Verify")!;
|
|
179
|
+
const check = verify.steps.find((s): s is OpIRActivityStep => s.kind === "activity" && s.fn === "httpCheck")!;
|
|
180
|
+
expect(check.entities).toEqual(["https://app.example.com/healthz"]);
|
|
181
|
+
expect(ir.activityContracts.httpCheck.entities).toEqual(["url"]);
|
|
182
|
+
|
|
183
|
+
const build = ir.phases.find((p) => p.name === "Build")!;
|
|
184
|
+
const sh = build.steps.find((s): s is OpIRActivityStep => s.kind === "activity" && s.fn === "shellCmd")!;
|
|
185
|
+
expect(sh.entities).toBeUndefined();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("skips a contract zod cannot turn into JSON Schema, keeping the step in the graph", () => {
|
|
189
|
+
const registry = contracts();
|
|
190
|
+
registry.set(
|
|
191
|
+
"testTransformActivity",
|
|
192
|
+
activityContract("testTransformActivity", z.strictObject({ value: z.string().transform((s) => s.length) })),
|
|
193
|
+
);
|
|
194
|
+
const config: OpConfig = {
|
|
195
|
+
name: "test-with-transform",
|
|
196
|
+
overview: "A schema with a transform beside a normal one",
|
|
197
|
+
phases: [
|
|
198
|
+
phase("Run", [
|
|
199
|
+
shell("echo compatible"),
|
|
200
|
+
{ kind: "activity", fn: "testTransformActivity", args: { value: "test" } },
|
|
201
|
+
]),
|
|
202
|
+
],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const ir = buildOpIR(config, registry);
|
|
206
|
+
expect(ir.activityContracts.shellCmd).toBeDefined();
|
|
207
|
+
expect(ir.activityContracts.testTransformActivity).toBeUndefined();
|
|
208
|
+
|
|
209
|
+
const run = ir.phases.find((p) => p.name === "Run")!;
|
|
210
|
+
const step = run.steps.find(
|
|
211
|
+
(s): s is OpIRActivityStep => s.kind === "activity" && s.fn === "testTransformActivity",
|
|
212
|
+
)!;
|
|
213
|
+
expect(step.args).toEqual({ value: "test" });
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it("round-trips through op.json back into an OpConfig that re-serializes byte-identically", () => {
|
|
217
|
+
const original = representativeOp();
|
|
218
|
+
const text = serializeOpIR(original, contracts());
|
|
219
|
+
const reconstructed = opConfigFromIR(JSON.parse(text) as OpIR);
|
|
220
|
+
|
|
221
|
+
expect(serializeOpIR(reconstructed, contracts())).toBe(text);
|
|
222
|
+
expect(reconstructed.labels).toEqual({ Team: "infra", Env: "staging" });
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it("round-trips a minimal Op (no gate, effect, onFailure or labels) too", () => {
|
|
226
|
+
const original: OpConfig = { name: "minimal", overview: "o", phases: [phase("Only", [shell("echo hi")])] };
|
|
227
|
+
const text = serializeOpIR(original);
|
|
228
|
+
const reconstructed = opConfigFromIR(JSON.parse(text) as OpIR);
|
|
229
|
+
|
|
230
|
+
expect(reconstructed.labels).toBeUndefined();
|
|
231
|
+
expect(serializeOpIR(reconstructed)).toBe(text);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it("op.json is valid, parseable JSON with stable 2-space indentation", () => {
|
|
235
|
+
const text = serializeOpIR(representativeOp());
|
|
236
|
+
expect(() => JSON.parse(text)).not.toThrow();
|
|
237
|
+
expect(text.endsWith("\n")).toBe(true);
|
|
238
|
+
expect(text).toContain(' "formatVersion"');
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it("opConfigFromIR throws on a formatVersion mismatch — a 1.0 op.json is not readable as 2.0", () => {
|
|
242
|
+
const staleIR = {
|
|
243
|
+
formatVersion: "1.0",
|
|
244
|
+
name: "stale-op",
|
|
245
|
+
overview: "An op.json from before the labels split",
|
|
246
|
+
depends: [],
|
|
247
|
+
labels: {},
|
|
248
|
+
phases: [{ name: "Run", parallel: false, steps: [] }],
|
|
249
|
+
onFailure: [],
|
|
250
|
+
activityProfiles: {},
|
|
251
|
+
activityContracts: {},
|
|
252
|
+
} as OpIR;
|
|
253
|
+
|
|
254
|
+
expect(() => opConfigFromIR(staleIR)).toThrow(
|
|
255
|
+
/op\.json IR format mismatch: expected "2\.0", got "1\.0"/,
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it("a StepOutputRef in a step's args serializes to a stable, documented shape", () => {
|
|
260
|
+
const build = activity("chantBuild", { path: "." }, { id: "build-step" });
|
|
261
|
+
const config: OpConfig = {
|
|
262
|
+
name: "ref-op",
|
|
263
|
+
overview: "Consumes a prior step's output",
|
|
264
|
+
phases: [
|
|
265
|
+
phase("Build", [build]),
|
|
266
|
+
phase("Deploy", [kubectlApply("dist/k8s.yaml", { context: stepOutput(build, "outputPath") })]),
|
|
267
|
+
],
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const roundTripped = JSON.parse(serializeOpIR(config)) as OpIR;
|
|
271
|
+
const deployStep = roundTripped.phases[1]!.steps[0] as OpIRActivityStep;
|
|
272
|
+
expect(deployStep.args.context).toEqual({ kind: "step-output-ref", step: "build-step", path: "outputPath" });
|
|
273
|
+
expect(Object.keys(deployStep.args.context as object).sort()).toEqual(["kind", "path", "step"]);
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
it("a whole-value StepOutputRef (no path) omits the path key", () => {
|
|
277
|
+
const build = activity("chantBuild", { path: "." }, { id: "build-step" });
|
|
278
|
+
const config: OpConfig = {
|
|
279
|
+
name: "ref-op-whole",
|
|
280
|
+
overview: "Consumes a prior step's whole output",
|
|
281
|
+
phases: [
|
|
282
|
+
phase("Build", [build]),
|
|
283
|
+
phase("Deploy", [kubectlApply("dist/k8s.yaml", { context: build.out })]),
|
|
284
|
+
],
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const roundTripped = JSON.parse(serializeOpIR(config)) as OpIR;
|
|
288
|
+
const deployStep = roundTripped.phases[1]!.steps[0] as OpIRActivityStep;
|
|
289
|
+
expect(deployStep.args.context).toEqual({ kind: "step-output-ref", step: "build-step" });
|
|
290
|
+
expect("path" in (deployStep.args.context as object)).toBe(false);
|
|
291
|
+
});
|
|
292
|
+
});
|