@intentius/chant 0.56.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/catalog.d.ts +51 -1
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/core.d.ts +20 -1
- package/dist/audit/core.d.ts.map +1 -1
- package/dist/audit/discover.d.ts +14 -8
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/lineage.d.ts +10 -0
- package/dist/audit/lineage.d.ts.map +1 -0
- package/dist/audit/prior-art.d.ts +385 -0
- package/dist/audit/prior-art.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +16 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-html.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +8 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +3 -0
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/audit/secrets.d.ts +15 -0
- package/dist/audit/secrets.d.ts.map +1 -1
- package/dist/audit/terraform-state.d.ts +46 -0
- package/dist/audit/terraform-state.d.ts.map +1 -0
- package/dist/build.d.ts +17 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/claimed-fields.d.ts +161 -0
- package/dist/claimed-fields.d.ts.map +1 -0
- package/dist/cli/build-params-cli.d.ts +2 -2
- package/dist/cli/command-group.d.ts +14 -0
- package/dist/cli/command-group.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts +17 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon-docs.d.ts +1 -1
- package/dist/cli/commands/check-lexicon-examples.d.ts +16 -6
- package/dist/cli/commands/check-lexicon-examples.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +7 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +2 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +82 -16
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -42
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts +28 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts +16 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -1
- package/dist/cli/mcp/resources/context.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +26 -9
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/generate-post-synth-barrel.d.ts +1 -1
- package/dist/components/auto-release.d.ts +10 -20
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability.d.ts +4 -4
- package/dist/components/cli-support.d.ts +20 -21
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +3 -3
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/driver-output.d.ts.map +1 -1
- package/dist/components/driver.d.ts +66 -36
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/index.d.ts +1 -2
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/manifest-persistence.d.ts +1 -9
- package/dist/components/manifest-persistence.d.ts.map +1 -1
- package/dist/components/run-progress.d.ts +6 -6
- package/dist/components/run-progress.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts +9 -15
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts +2 -2
- package/dist/deep-observation.d.ts +17 -0
- package/dist/deep-observation.d.ts.map +1 -1
- package/dist/discovery/files.d.ts +4 -4
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +33 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/bundle.d.ts.map +1 -1
- package/dist/discovery/sandbox/config-wire.d.ts +2 -2
- package/dist/fold/foldable-helpers.d.ts +5 -1
- package/dist/fold/foldable-helpers.d.ts.map +1 -1
- package/dist/fold-provenance.d.ts +236 -0
- package/dist/fold-provenance.d.ts.map +1 -0
- package/dist/graph-ops.d.ts +1 -1
- package/dist/held-elsewhere.d.ts +85 -0
- package/dist/held-elsewhere.d.ts.map +1 -0
- package/dist/import/ir-utils.d.ts +1 -1
- package/dist/import/ir-utils.d.ts.map +1 -1
- package/dist/import/parser.d.ts +33 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts +3 -3
- package/dist/lexicon-config.d.ts +4 -4
- package/dist/lexicon-plugin-helpers.d.ts +1 -1
- package/dist/lexicon.d.ts +104 -14
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/change-set.d.ts +13 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -1
- package/dist/lifecycle/deep-diff.d.ts +133 -6
- package/dist/lifecycle/deep-diff.d.ts.map +1 -1
- package/dist/lifecycle/deep-observe.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +71 -3
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts +2 -2
- package/dist/lifecycle/run-ledger.d.ts +53 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -0
- package/dist/lifecycle/symptoms.d.ts +3 -3
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lint/config.d.ts +81 -2
- package/dist/lint/config.d.ts.map +1 -1
- package/dist/lint/post-synth.d.ts +55 -2
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +1 -1
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -1
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts +44 -0
- package/dist/lint/rules/comp/comp004-gate-needs-durable-runtime.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +1 -1
- package/dist/lint/rules/comp/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +16 -0
- package/dist/lint/rules/op/index.d.ts.map +1 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts +27 -0
- package/dist/lint/rules/op/ops012-activity-contract.d.ts.map +1 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts +61 -0
- package/dist/lint/rules/op/ops013-step-output-ref.d.ts.map +1 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts +55 -0
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -0
- package/dist/lint/rules/op/support.d.ts +12 -0
- package/dist/lint/rules/op/support.d.ts.map +1 -0
- package/dist/lint/suppressions.d.ts +108 -0
- package/dist/lint/suppressions.d.ts.map +1 -0
- package/dist/live-endpoint.d.ts +1 -1
- package/dist/op/activities/activity-contracts.d.ts +54 -0
- package/dist/op/activities/activity-contracts.d.ts.map +1 -0
- package/dist/op/activities/apply.d.ts +262 -0
- package/dist/op/activities/apply.d.ts.map +1 -0
- package/dist/op/activities/build.d.ts +13 -0
- package/dist/op/activities/build.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +110 -0
- package/dist/op/activities/converge.d.ts.map +1 -0
- package/dist/op/activities/env-teardown.d.ts +76 -0
- package/dist/op/activities/env-teardown.d.ts.map +1 -0
- package/dist/op/activities/guard-validate.d.ts +64 -0
- package/dist/op/activities/guard-validate.d.ts.map +1 -0
- package/dist/op/activities/http-check.d.ts +34 -0
- package/dist/op/activities/http-check.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +31 -0
- package/dist/op/activities/index.d.ts.map +1 -0
- package/dist/op/activities/lexicon-upgrade.d.ts +182 -0
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -0
- package/dist/op/activities/lifecycle.d.ts +42 -0
- package/dist/op/activities/lifecycle.d.ts.map +1 -0
- package/dist/op/activities/pipeline-audit.d.ts +84 -0
- package/dist/op/activities/pipeline-audit.d.ts.map +1 -0
- package/dist/op/activities/policy.d.ts +17 -0
- package/dist/op/activities/policy.d.ts.map +1 -0
- package/dist/op/activities/reconcile.d.ts +89 -0
- package/dist/op/activities/reconcile.d.ts.map +1 -0
- package/dist/op/activities/shell.d.ts +13 -0
- package/dist/op/activities/shell.d.ts.map +1 -0
- package/dist/op/activities/teardown.d.ts +10 -0
- package/dist/op/activities/teardown.d.ts.map +1 -0
- package/dist/op/activities/util.d.ts +7 -0
- package/dist/op/activities/util.d.ts.map +1 -0
- package/dist/op/activities/wait.d.ts +28 -0
- package/dist/op/activities/wait.d.ts.map +1 -0
- package/dist/op/activities/workflow-audit.d.ts +92 -0
- package/dist/op/activities/workflow-audit.d.ts.map +1 -0
- package/dist/op/activity-contract-registry.d.ts +76 -0
- package/dist/op/activity-contract-registry.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +25 -18
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-failure.d.ts +28 -0
- package/dist/op/activity-failure.d.ts.map +1 -0
- package/dist/op/activity-profiles.d.ts +118 -0
- package/dist/op/activity-profiles.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +22 -38
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +3 -14
- package/dist/op/activity-runtime.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -29
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +113 -0
- package/dist/op/composites/apply-op.d.ts.map +1 -0
- package/dist/op/composites/converge-op.d.ts +120 -0
- package/dist/op/composites/converge-op.d.ts.map +1 -0
- package/dist/op/composites/index.d.ts +26 -0
- package/dist/op/composites/index.d.ts.map +1 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts +78 -0
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -0
- package/dist/op/composites/pipeline-audit-op.d.ts +50 -0
- package/dist/op/composites/pipeline-audit-op.d.ts.map +1 -0
- package/dist/op/composites/reconcile-op.d.ts +57 -0
- package/dist/op/composites/reconcile-op.d.ts.map +1 -0
- package/dist/op/composites/watch-op.d.ts +60 -0
- package/dist/op/composites/watch-op.d.ts.map +1 -0
- package/dist/op/composites/workflow-audit-op.d.ts +57 -0
- package/dist/op/composites/workflow-audit-op.d.ts.map +1 -0
- package/dist/op/converge-rule.d.ts +12 -10
- package/dist/op/converge-rule.d.ts.map +1 -1
- package/dist/op/cron.d.ts +51 -0
- package/dist/op/cron.d.ts.map +1 -0
- package/dist/op/discover.d.ts +9 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/duration.d.ts +11 -0
- package/dist/op/duration.d.ts.map +1 -0
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate.d.ts +90 -0
- package/dist/op/gate.d.ts.map +1 -0
- package/dist/op/generate-pipeline.d.ts +9 -0
- package/dist/op/generate-pipeline.d.ts.map +1 -1
- package/dist/op/index.d.ts +23 -7
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +114 -28
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +11 -1
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +184 -0
- package/dist/op/op-ir.d.ts.map +1 -0
- package/dist/op/op-verb-class.d.ts +10 -2
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +30 -5
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/receipt-store.d.ts +1 -1
- package/dist/op/receipt-store.d.ts.map +1 -1
- package/dist/op/resource.d.ts +15 -1
- package/dist/op/resource.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +200 -0
- package/dist/op/runtime.d.ts.map +1 -0
- package/dist/op/runtimes/local.d.ts +29 -0
- package/dist/op/runtimes/local.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +15 -13
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/types.d.ts +79 -27
- package/dist/op/types.d.ts.map +1 -1
- package/dist/ownership.d.ts +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/serializer-walker.d.ts.map +1 -1
- package/dist/stack-output.d.ts +5 -0
- package/dist/stack-output.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/audit/catalog.test.ts +79 -3
- package/src/audit/catalog.ts +111 -13
- package/src/audit/core.test.ts +26 -0
- package/src/audit/core.ts +61 -17
- package/src/audit/discover.test.ts +39 -0
- package/src/audit/discover.ts +91 -15
- package/src/audit/lineage.ts +100 -0
- package/src/audit/prior-art.ts +153 -0
- package/src/audit/proof.test.ts +67 -0
- package/src/audit/proof.ts +70 -0
- package/src/audit/report-html.ts +9 -4
- package/src/audit/report-model.ts +16 -2
- package/src/audit/report.test.ts +33 -0
- package/src/audit/report.ts +5 -3
- package/src/audit/rules-doc.test.ts +34 -2
- package/src/audit/rules-doc.ts +82 -9
- package/src/audit/secrets.ts +15 -3
- package/src/audit/terraform-state.test.ts +144 -0
- package/src/audit/terraform-state.ts +131 -0
- package/src/build.ts +31 -1
- package/src/claimed-fields.test.ts +145 -0
- package/src/claimed-fields.ts +208 -0
- package/src/cli/build-params-cli.ts +2 -2
- package/src/cli/command-group.ts +14 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/.terraform/providers/lock.txt +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/modules/foo/main.tf +1 -0
- package/src/cli/commands/__fixtures__/audit-terraform/infra/variables.tf +3 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +13 -4
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs-cli.ts +4 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +1 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/src/package-cli.ts +21 -0
- package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +1 -0
- package/src/cli/commands/audit.test.ts +189 -11
- package/src/cli/commands/audit.ts +73 -27
- package/src/cli/commands/build.test.ts +292 -9
- package/src/cli/commands/build.ts +156 -24
- package/src/cli/commands/check-lexicon-docs.ts +1 -1
- package/src/cli/commands/check-lexicon-examples.test.ts +29 -1
- package/src/cli/commands/check-lexicon-examples.ts +42 -10
- package/src/cli/commands/import.ts +6 -0
- package/src/cli/commands/init-lexicon/templates/codegen.ts +9 -0
- package/src/cli/commands/init-lexicon/templates/project.ts +44 -4
- package/src/cli/commands/init-lexicon.test.ts +97 -2
- package/src/cli/commands/init-lexicon.ts +4 -2
- package/src/cli/commands/init.test.ts +35 -1
- package/src/cli/commands/init.ts +14 -3
- package/src/cli/commands/lint.test.ts +48 -0
- package/src/cli/commands/lint.ts +123 -0
- package/src/cli/commands/onboard.test.ts +75 -0
- package/src/cli/commands/onboard.ts +54 -23
- package/src/cli/handlers/build.test.ts +81 -1
- package/src/cli/handlers/build.ts +15 -1
- package/src/cli/handlers/graph.test.ts +3 -3
- package/src/cli/handlers/lifecycle.test.ts +195 -12
- package/src/cli/handlers/lifecycle.ts +123 -6
- package/src/cli/handlers/misc.ts +1 -1
- package/src/cli/handlers/operator.test.ts +183 -11
- package/src/cli/handlers/operator.ts +260 -55
- package/src/cli/handlers/run.test.ts +431 -1287
- package/src/cli/handlers/run.ts +365 -915
- package/src/cli/main.test.ts +23 -15
- package/src/cli/main.ts +88 -33
- package/src/cli/mcp/op-tools.ts +147 -88
- package/src/cli/mcp/resource-handlers.ts +10 -37
- package/src/cli/mcp/resources/context.ts +11 -12
- package/src/cli/mcp/server.test.ts +25 -27
- package/src/cli/mcp/server.ts +2 -2
- package/src/cli/registry.ts +26 -9
- package/src/codegen/docs.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.test.ts +1 -1
- package/src/codegen/generate-post-synth-barrel.ts +1 -1
- package/src/components/README.md +3 -2
- package/src/components/auto-release.test.ts +4 -20
- package/src/components/auto-release.ts +17 -42
- package/src/components/capability.ts +4 -4
- package/src/components/cli-support.test.ts +10 -5
- package/src/components/cli-support.ts +52 -44
- package/src/components/component.schema.json +2 -2
- package/src/components/component.ts +3 -3
- package/src/components/driver-output.ts +14 -2
- package/src/components/driver.test.ts +92 -22
- package/src/components/driver.ts +193 -83
- package/src/components/index.ts +1 -6
- package/src/components/manifest-persistence.test.ts +2 -20
- package/src/components/manifest-persistence.ts +4 -24
- package/src/components/pilots/pilots-lint-clean.test.ts +11 -10
- package/src/components/run-progress.ts +6 -6
- package/src/components/verbs/run-agent.ts +11 -17
- package/src/components/verbs/verify.ts +2 -2
- package/src/deep-observation.test.ts +75 -0
- package/src/deep-observation.ts +48 -0
- package/src/discovery/collect.test.ts +3 -3
- package/src/discovery/files.ts +4 -4
- package/src/discovery/fold-import.ts +105 -2
- package/src/discovery/param-deps.test.ts +112 -1
- package/src/discovery/param-deps.ts +193 -0
- package/src/discovery/sandbox/bundle.ts +15 -1
- package/src/discovery/sandbox/config-wire.test.ts +2 -2
- package/src/discovery/sandbox/config-wire.ts +2 -2
- package/src/fold/fold.test.ts +56 -0
- package/src/fold/foldable-helpers.ts +81 -1
- package/src/fold-provenance-exclusion.test.ts +186 -0
- package/src/fold-provenance.test.ts +495 -0
- package/src/fold-provenance.ts +441 -0
- package/src/graph-ops.test.ts +2 -2
- package/src/graph-ops.ts +1 -1
- package/src/held-elsewhere.test.ts +102 -0
- package/src/held-elsewhere.ts +114 -0
- package/src/import/ir-utils.ts +11 -1
- package/src/import/parser.ts +35 -0
- package/src/index.ts +3 -0
- package/src/kubectl-context.ts +3 -3
- package/src/lexicon-config.test.ts +5 -5
- package/src/lexicon-config.ts +4 -4
- package/src/lexicon-plugin-helpers.ts +1 -1
- package/src/lexicon.ts +120 -14
- package/src/lifecycle/change-set.test.ts +49 -0
- package/src/lifecycle/change-set.ts +67 -0
- package/src/lifecycle/converge-ledger.ts +1 -1
- package/src/lifecycle/deep-diff.test.ts +267 -14
- package/src/lifecycle/deep-diff.ts +217 -14
- package/src/lifecycle/deep-observe.test.ts +3 -1
- package/src/lifecycle/deep-observe.ts +16 -6
- package/src/lifecycle/gate-ledger.test.ts +74 -2
- package/src/lifecycle/gate-ledger.ts +143 -36
- package/src/lifecycle/index.ts +1 -0
- package/src/lifecycle/release-ledger.ts +2 -2
- package/src/lifecycle/run-ledger.test.ts +268 -0
- package/src/lifecycle/run-ledger.ts +201 -0
- package/src/lifecycle/symptoms.ts +3 -3
- package/src/lint/config.test.ts +82 -1
- package/src/lint/config.ts +112 -3
- package/src/lint/policy.test.ts +1 -1
- package/src/lint/post-synth.test.ts +31 -0
- package/src/lint/post-synth.ts +54 -1
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +2 -2
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +1 -1
- package/src/lint/rules/comp/comp.test.ts +1 -1
- package/src/lint/rules/comp/comp004-gate-needs-durable-runtime.ts +74 -0
- package/src/lint/rules/comp/index.ts +3 -3
- package/src/lint/rules/op/index.ts +23 -0
- package/src/lint/rules/op/ops012-activity-contract.test.ts +161 -0
- package/src/lint/rules/op/ops012-activity-contract.ts +64 -0
- package/src/lint/rules/op/ops013-step-output-ref.test.ts +237 -0
- package/src/lint/rules/op/ops013-step-output-ref.ts +98 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +263 -0
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +179 -0
- package/src/lint/rules/op/support.ts +11 -0
- package/src/lint/suppressions.test.ts +163 -0
- package/src/lint/suppressions.ts +204 -0
- package/src/live-endpoint.ts +1 -1
- package/src/op/activities/activity-contracts.ts +66 -0
- package/src/op/activities/apply.test.ts +567 -0
- package/src/op/activities/apply.ts +662 -0
- package/src/op/activities/build.ts +26 -0
- package/src/op/activities/converge.test.ts +330 -0
- package/src/op/activities/converge.ts +449 -0
- package/src/op/activities/env-teardown.test.ts +187 -0
- package/src/op/activities/env-teardown.ts +171 -0
- package/src/op/activities/guard-validate.test.ts +139 -0
- package/src/op/activities/guard-validate.ts +223 -0
- package/src/op/activities/http-check.test.ts +48 -0
- package/src/op/activities/http-check.ts +55 -0
- package/src/op/activities/index.ts +119 -0
- package/src/op/activities/lexicon-upgrade.test.ts +492 -0
- package/src/op/activities/lexicon-upgrade.ts +673 -0
- package/src/op/activities/lifecycle.ts +85 -0
- package/src/op/activities/pipeline-audit.test.ts +67 -0
- package/src/op/activities/pipeline-audit.ts +201 -0
- package/src/op/activities/policy.test.ts +19 -0
- package/src/op/activities/policy.ts +37 -0
- package/src/op/activities/reconcile.test.ts +81 -0
- package/src/op/activities/reconcile.ts +187 -0
- package/src/op/activities/shell.ts +26 -0
- package/src/op/activities/teardown.ts +22 -0
- package/src/op/activities/util.ts +6 -0
- package/src/op/activities/wait.ts +69 -0
- package/src/op/activities/workflow-audit.test.ts +90 -0
- package/src/op/activities/workflow-audit.ts +203 -0
- package/src/op/activity-contract-registry.test.ts +98 -0
- package/src/op/activity-contract-registry.ts +114 -0
- package/src/op/activity-contract.test.ts +1 -1
- package/src/op/activity-contract.ts +37 -33
- package/src/op/activity-failure.ts +34 -0
- package/src/op/activity-profiles.test.ts +56 -0
- package/src/op/activity-profiles.ts +116 -0
- package/src/op/activity-registry.test.ts +37 -28
- package/src/op/activity-registry.ts +27 -56
- package/src/op/activity-runtime.ts +3 -47
- package/src/op/builders-exports.test.ts +0 -49
- package/src/op/builders-typed.test.ts +164 -0
- package/src/op/builders.ts +123 -39
- package/src/op/composites/apply-op.ts +208 -0
- package/src/op/composites/composites.test.ts +366 -0
- package/src/op/composites/converge-op.test.ts +168 -0
- package/src/op/composites/converge-op.ts +214 -0
- package/src/op/composites/index.ts +26 -0
- package/src/op/composites/lexicon-upgrade-op.test.ts +68 -0
- package/src/op/composites/lexicon-upgrade-op.ts +134 -0
- package/src/op/composites/pipeline-audit-op.test.ts +25 -0
- package/src/op/composites/pipeline-audit-op.ts +78 -0
- package/src/op/composites/reconcile-op.test.ts +51 -0
- package/src/op/composites/reconcile-op.ts +107 -0
- package/src/op/composites/watch-op.ts +108 -0
- package/src/op/composites/workflow-audit-op.test.ts +25 -0
- package/src/op/composites/workflow-audit-op.ts +88 -0
- package/src/op/converge-rule.ts +12 -10
- package/src/op/cron.test.ts +128 -0
- package/src/op/cron.ts +149 -0
- package/src/op/discover-root.test.ts +105 -1
- package/src/op/discover.test.ts +6 -2
- package/src/op/discover.ts +84 -16
- package/src/op/duration.ts +21 -0
- package/src/op/effect-step.test.ts +26 -11
- package/src/op/emulator-lifecycle.ts +1 -2
- package/src/op/gate.ts +155 -0
- package/src/op/generate-pipeline.test.ts +65 -3
- package/src/op/generate-pipeline.ts +19 -2
- package/src/op/index.ts +39 -6
- package/src/op/local-executor.test.ts +163 -34
- package/src/op/local-executor.ts +370 -102
- package/src/op/local-output.test.ts +95 -11
- package/src/op/local-output.ts +33 -4
- package/src/op/op-ir.test.ts +281 -0
- package/src/op/op-ir.ts +417 -0
- package/src/op/op-verb-class.ts +19 -5
- package/src/op/op.test.ts +26 -5
- package/src/op/operator.test.ts +175 -6
- package/src/op/operator.ts +63 -16
- package/src/op/receipt-store.ts +1 -1
- package/src/op/resource.ts +20 -2
- package/src/op/runtime.ts +203 -0
- package/src/op/runtimes/local.test.ts +181 -0
- package/src/op/runtimes/local.ts +212 -0
- package/src/op/step-output-ref.ts +15 -13
- package/src/op/types.ts +84 -24
- package/src/ownership.ts +1 -1
- package/src/provenance.test.ts +6 -0
- package/src/provenance.ts +21 -1
- package/src/serializer-walker.test.ts +42 -0
- package/src/serializer-walker.ts +21 -2
- package/src/stack-output.ts +6 -1
- 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
|
@@ -247,7 +247,7 @@ describe("COMP003: mutating-no-rollback", () => {
|
|
|
247
247
|
});
|
|
248
248
|
});
|
|
249
249
|
|
|
250
|
-
describe("COMP004: gate-needs-
|
|
250
|
+
describe("COMP004: gate-needs-durable-runtime", () => {
|
|
251
251
|
it("flags a gate step with no disable directive", async () => {
|
|
252
252
|
const diagnostics = await lintFixture("comp004", "fail");
|
|
253
253
|
const hits = diagnostics.filter((d) => d.checkId === "COMP004");
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMP004: gate-needs-durable-runtime (the file is renamed in #2116; the
|
|
3
|
+
* behaviour changed in #2119)
|
|
4
|
+
*
|
|
5
|
+
* The file name and the exported symbol keep the old wording on purpose
|
|
6
|
+
* (#2195): the id is what an author writes in a `chant-disable` comment and
|
|
7
|
+
* what every import names, and churning both buys nothing. `description`
|
|
8
|
+
* below is the accurate sentence, and it is what the docs page uses as its
|
|
9
|
+
* heading — since #2119 the rule has nothing to do with which runtime hosts
|
|
10
|
+
* the run.
|
|
11
|
+
*
|
|
12
|
+
* Flags a `gate` step anywhere in a component's composition — the point where
|
|
13
|
+
* a run stops and waits for a person. Since #2119 that is not a refusal: the
|
|
14
|
+
* driver decides the gate against the gate ledger (`../../../op/gate.ts`), and
|
|
15
|
+
* one nobody has approved ends the run pending `chant approve <component>
|
|
16
|
+
* <gate>`, to be re-decided next run.
|
|
17
|
+
*
|
|
18
|
+
* The rule survives the change because what it flags survives it. The
|
|
19
|
+
* component contract has no per-component runtime field (the runtime is chosen
|
|
20
|
+
* per *run*, via `chant run --on`, intentionally not part of the declaration),
|
|
21
|
+
* so lint cannot tell from the declaration alone that a `gate` is deliberate;
|
|
22
|
+
* every one is a standing "this component stops for a human" fact worth
|
|
23
|
+
* surfacing, not a mistake to silently allow. Acknowledge it explicitly with a
|
|
24
|
+
* **file-level**
|
|
25
|
+
* disable directive once the human wait is genuinely intended (see
|
|
26
|
+
* lint-rules/disable-directives.mdx) — a COMP* diagnostic carries no real
|
|
27
|
+
* line/column (it is reported for the whole component; see
|
|
28
|
+
* ../../component-checks.ts), so only the file-level `chant-disable` form is
|
|
29
|
+
* honored for these rules — `-line`/`-next-line` do not suppress a COMP*
|
|
30
|
+
* diagnostic (see ../../../cli/commands/lint.ts's
|
|
31
|
+
* `runComponentCheckDiagnostics`):
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* // chant-disable COMP004 -- the rollout waits on a release manager's approval
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Triggers on: any `gate` step in `deploy`/`rollback` with no matching
|
|
38
|
+
* file-level disable directive.
|
|
39
|
+
* OK: a component with no `gate` steps at all, or a `gate` whose file-level
|
|
40
|
+
* disable directive says who is expected to approve it and why.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import type { ComponentCheck, ComponentCheckContext, ComponentCheckDiagnostic } from "../../component-checks";
|
|
44
|
+
import { walkComponent } from "./support";
|
|
45
|
+
|
|
46
|
+
export const comp004GateNeedsDurableRuntimeRule: ComponentCheck = {
|
|
47
|
+
id: "COMP004",
|
|
48
|
+
severity: "error",
|
|
49
|
+
category: "correctness",
|
|
50
|
+
description: "A gate step ends the run pending approval — acknowledge the human wait explicitly",
|
|
51
|
+
check(ctx: ComponentCheckContext): ComponentCheckDiagnostic[] {
|
|
52
|
+
const diagnostics: ComponentCheckDiagnostic[] = [];
|
|
53
|
+
|
|
54
|
+
for (const [name, { component, filePath }] of ctx.components) {
|
|
55
|
+
const { gates } = walkComponent(component);
|
|
56
|
+
for (const { gate, phaseName } of gates) {
|
|
57
|
+
diagnostics.push({
|
|
58
|
+
checkId: "COMP004",
|
|
59
|
+
severity: "error",
|
|
60
|
+
component: name,
|
|
61
|
+
file: filePath,
|
|
62
|
+
message:
|
|
63
|
+
`Component "${name}": gate "${gate.signalName}" (phase "${phaseName}") ends the run pending approval — ` +
|
|
64
|
+
`a run that reaches it records the gate as a fact and stops there until someone runs ` +
|
|
65
|
+
`"chant approve ${name} ${gate.signalName}", and no later phase runs. If that wait is intended, ` +
|
|
66
|
+
`suppress with a file-level "// chant-disable COMP004 -- <reason>" comment anywhere in this file to ` +
|
|
67
|
+
`document who approves it and why.`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return diagnostics;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
@@ -10,7 +10,7 @@ import type { ComponentCheck } from "../../component-checks";
|
|
|
10
10
|
import { comp001PublishesNeverAppliesRule } from "./comp001-publishes-never-applies";
|
|
11
11
|
import { comp002DanglingWiringRefRule } from "./comp002-dangling-wiring-ref";
|
|
12
12
|
import { comp003MutatingNoRollbackRule } from "./comp003-mutating-no-rollback";
|
|
13
|
-
import {
|
|
13
|
+
import { comp004GateNeedsDurableRuntimeRule } from "./comp004-gate-needs-durable-runtime";
|
|
14
14
|
import { comp005CapabilityKindIsNounRule } from "./comp005-capability-kind-is-noun";
|
|
15
15
|
import { comp006ShellNeedsReasonRule } from "./comp006-shell-needs-reason";
|
|
16
16
|
import { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
|
|
@@ -18,7 +18,7 @@ import { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
|
|
|
18
18
|
export { comp001PublishesNeverAppliesRule } from "./comp001-publishes-never-applies";
|
|
19
19
|
export { comp002DanglingWiringRefRule } from "./comp002-dangling-wiring-ref";
|
|
20
20
|
export { comp003MutatingNoRollbackRule } from "./comp003-mutating-no-rollback";
|
|
21
|
-
export {
|
|
21
|
+
export { comp004GateNeedsDurableRuntimeRule } from "./comp004-gate-needs-durable-runtime";
|
|
22
22
|
export { comp005CapabilityKindIsNounRule } from "./comp005-capability-kind-is-noun";
|
|
23
23
|
export { comp006ShellNeedsReasonRule } from "./comp006-shell-needs-reason";
|
|
24
24
|
export { comp007CompositionSprawlRule } from "./comp007-composition-sprawl";
|
|
@@ -29,7 +29,7 @@ export function loadComponentChecks(): ComponentCheck[] {
|
|
|
29
29
|
comp001PublishesNeverAppliesRule,
|
|
30
30
|
comp002DanglingWiringRefRule,
|
|
31
31
|
comp003MutatingNoRollbackRule,
|
|
32
|
-
|
|
32
|
+
comp004GateNeedsDurableRuntimeRule,
|
|
33
33
|
comp005CapabilityKindIsNounRule,
|
|
34
34
|
comp006ShellNeedsReasonRule,
|
|
35
35
|
comp007CompositionSprawlRule,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPS* Op-model post-synth checks (#2122, epic #2114 sub-issue 6) — ported
|
|
3
|
+
* from a hosting lexicon's own TMP012/TMP013/TMP014. Registered as core-owned
|
|
4
|
+
* post-synth checks (`coreOpChecks`, mirroring `../../receipt-checks.ts`'s
|
|
5
|
+
* `coreReceiptChecks`) so they run over the FULL build result regardless of
|
|
6
|
+
* which lexicons are configured — an Op is recognized by entity type
|
|
7
|
+
* (`../../../op/resource.ts`'s `OpResource`), not by which lexicon declared
|
|
8
|
+
* it.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { PostSynthCheck } from "../../post-synth";
|
|
12
|
+
import { ops012 } from "./ops012-activity-contract";
|
|
13
|
+
import { ops013 } from "./ops013-step-output-ref";
|
|
14
|
+
import { ops014 } from "./ops014-converge-rule-refusals";
|
|
15
|
+
|
|
16
|
+
export { ops012 } from "./ops012-activity-contract";
|
|
17
|
+
export { ops013 } from "./ops013-step-output-ref";
|
|
18
|
+
export { ops014 } from "./ops014-converge-rule-refusals";
|
|
19
|
+
|
|
20
|
+
/** Core's own post-synth checks over the Op model — OPS012, OPS013, OPS014. */
|
|
21
|
+
export function coreOpChecks(): PostSynthCheck[] {
|
|
22
|
+
return [ops012, ops013, ops014];
|
|
23
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPS012 tests — ported from a hosting lexicon's own TMP012
|
|
3
|
+
* (chant #1288 Stage 1, moved to core by #2122).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test, expect } from "vitest";
|
|
7
|
+
import type { PostSynthContext } from "../../post-synth";
|
|
8
|
+
import { DECLARABLE_MARKER } from "../../../declarable";
|
|
9
|
+
import { ops012 } from "./ops012-activity-contract";
|
|
10
|
+
import { activityContract, type ActivityContract } from "../../../op";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
function makeEntity(entityType: string, props: Record<string, unknown>) {
|
|
14
|
+
return {
|
|
15
|
+
[DECLARABLE_MARKER]: true,
|
|
16
|
+
entityType,
|
|
17
|
+
lexicon: "chant",
|
|
18
|
+
kind: "resource",
|
|
19
|
+
props,
|
|
20
|
+
attributes: {},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function makeCtxFromEntities(entities: Map<string, unknown>): PostSynthContext {
|
|
25
|
+
return {
|
|
26
|
+
outputs: new Map([["chant", ""]]),
|
|
27
|
+
entities: entities as Map<string, never>,
|
|
28
|
+
buildResult: {
|
|
29
|
+
outputs: new Map([["chant", ""]]),
|
|
30
|
+
entities: entities as Map<string, never>,
|
|
31
|
+
warnings: [],
|
|
32
|
+
errors: [],
|
|
33
|
+
sourceFileCount: 1,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function opEntity(name: string, steps: unknown[], entityType = "Chant::Op") {
|
|
39
|
+
return makeEntity(entityType, { name, overview: "test", phases: [{ name: "Phase", steps }] });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
// ── Cross-lexicon contracts (chant #2101) ────────────────────────────────
|
|
44
|
+
//
|
|
45
|
+
// An Op step may call an activity any configured lexicon contributes, and
|
|
46
|
+
// this check is core-owned, so it fires on every project that declares one.
|
|
47
|
+
// `ctx.activityContracts` is what `chant build`, `chant lint` and
|
|
48
|
+
// check-lexicon's example harness fill in from `loadActivityContracts`; the
|
|
49
|
+
// check merges it over its own static table.
|
|
50
|
+
|
|
51
|
+
/** Stand-in for what a lexicon (terraform) declares for its own activities. */
|
|
52
|
+
const foreignContracts = new Map<string, ActivityContract>([
|
|
53
|
+
[
|
|
54
|
+
"terraformPlan",
|
|
55
|
+
activityContract(
|
|
56
|
+
"terraformPlan",
|
|
57
|
+
z.strictObject({ root: z.string(), planFile: z.string().optional() }),
|
|
58
|
+
z.object({ planFile: z.string(), changed: z.boolean(), text: z.string() }),
|
|
59
|
+
),
|
|
60
|
+
],
|
|
61
|
+
[
|
|
62
|
+
"terraformApply",
|
|
63
|
+
activityContract("terraformApply", z.strictObject({ root: z.string(), planFile: z.string().optional() })),
|
|
64
|
+
],
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
function withContracts(ctx: PostSynthContext, contracts: ReadonlyMap<string, ActivityContract>): PostSynthContext {
|
|
68
|
+
return { ...ctx, activityContracts: contracts };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
describe("OPS012: activity-contract", () => {
|
|
72
|
+
test("passes when args match the registered contract exactly", () => {
|
|
73
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
74
|
+
["op", opEntity("deploy", [{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod", live: true } }])],
|
|
75
|
+
]));
|
|
76
|
+
expect(ops012.check(ctx)).toHaveLength(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("skips a step whose fn has no registered contract", () => {
|
|
80
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
81
|
+
["op", opEntity("deploy", [{ kind: "activity", fn: "kubectlApply", args: { manifest: "dist/k8s.yaml", anything: "goes" } }])],
|
|
82
|
+
]));
|
|
83
|
+
expect(ops012.check(ctx)).toHaveLength(0);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("errors on an unrecognized args key — lifecycleDiff's `env` typo'd as `environment`", () => {
|
|
87
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
88
|
+
["op", opEntity("deploy", [{ kind: "activity", fn: "lifecycleDiff", args: { environment: "prod" } }])],
|
|
89
|
+
]));
|
|
90
|
+
const diags = ops012.check(ctx);
|
|
91
|
+
expect(diags.length).toBeGreaterThan(0);
|
|
92
|
+
expect(diags.every((d) => d.checkId === "OPS012" && d.severity === "error")).toBe(true);
|
|
93
|
+
expect(diags.some((d) => d.message.includes("environment"))).toBe(true);
|
|
94
|
+
expect(diags[0].message).toContain('Op "deploy"');
|
|
95
|
+
expect(diags[0].entity).toBe("op");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("errors on an outcomeAttribute.from path that doesn't exist on the declared return type", () => {
|
|
99
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
100
|
+
["op", opEntity("deploy", [
|
|
101
|
+
{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, outcomeAttribute: { name: "Drift", from: "drifed" } },
|
|
102
|
+
])],
|
|
103
|
+
]));
|
|
104
|
+
const diags = ops012.check(ctx);
|
|
105
|
+
expect(diags.some((d) => d.message.includes('outcomeAttribute.from "drifed"'))).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("errors on an unknown profile", () => {
|
|
109
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
110
|
+
["op", opEntity("deploy", [{ kind: "activity", fn: "shellCmd", args: { cmd: "echo hi" }, profile: "longInfa" }])],
|
|
111
|
+
]));
|
|
112
|
+
const diags = ops012.check(ctx);
|
|
113
|
+
expect(diags.some((d) => d.message.includes('unknown profile "longInfa"'))).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("ignores non-Op entities", () => {
|
|
117
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
118
|
+
["bucket", makeEntity("AWS::S3::Bucket", { name: "default" })],
|
|
119
|
+
]));
|
|
120
|
+
expect(ops012.check(ctx)).toHaveLength(0);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
test("ignores an entity whose entityType isn't Chant::Op", () => {
|
|
124
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
125
|
+
["op", opEntity("deploy", [{ kind: "activity", fn: "lifecycleDiff", args: { environment: "prod" } }], "Legacy::Op")],
|
|
126
|
+
]));
|
|
127
|
+
expect(ops012.check(ctx)).toHaveLength(0);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe("OPS012 reads a lexicon's activity contracts (#2101)", () => {
|
|
132
|
+
const typoedStep = () =>
|
|
133
|
+
makeCtxFromEntities(new Map([
|
|
134
|
+
["op", opEntity("app-plan", [{ kind: "activity", fn: "terraformPlan", args: { root: "app", planfile: "x" } }])],
|
|
135
|
+
]));
|
|
136
|
+
|
|
137
|
+
test("a step calling an unknown activity is skipped when the context carries no contracts", () => {
|
|
138
|
+
expect(ops012.check(typoedStep())).toHaveLength(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("the same step's misspelled arg is an error once the lexicon's contracts reach the check", () => {
|
|
142
|
+
const diags = ops012.check(withContracts(typoedStep(), foreignContracts));
|
|
143
|
+
expect(diags.some((d) => d.message.includes("planfile"))).toBe(true);
|
|
144
|
+
expect(diags.every((d) => d.checkId === "OPS012" && d.severity === "error")).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("a well-formed foreign step passes", () => {
|
|
148
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
149
|
+
["op", opEntity("app-plan", [{ kind: "activity", fn: "terraformPlan", args: { root: "app", planFile: "x" } }])],
|
|
150
|
+
]));
|
|
151
|
+
expect(ops012.check(withContracts(ctx, foreignContracts))).toHaveLength(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("core's own contracts survive the merge", () => {
|
|
155
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
156
|
+
["op", opEntity("deploy", [{ kind: "activity", fn: "lifecycleDiff", args: { environment: "prod" } }])],
|
|
157
|
+
]));
|
|
158
|
+
const diags = ops012.check(withContracts(ctx, foreignContracts));
|
|
159
|
+
expect(diags.some((d) => d.message.includes("environment"))).toBe(true);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPS012: activity step args/outcomeAttribute must match the activity's
|
|
3
|
+
* declared contract (chant #1288 Stage 1; moved from a hosting lexicon's own
|
|
4
|
+
* TMP012 to core in #2122, epic #2114 sub-issue 6, so it fires on every
|
|
5
|
+
* project that declares an Op, not only one with that lexicon configured).
|
|
6
|
+
*
|
|
7
|
+
* Validates every Op entity's steps against core's own base-activity
|
|
8
|
+
* contracts (`../../../op/activities/activity-contracts.ts`, #2117), using
|
|
9
|
+
* the generic walk in `@intentius/chant/op`'s `validateActivitySteps`. A step
|
|
10
|
+
* whose `fn` has no registered contract here is skipped — most activities
|
|
11
|
+
* don't have one yet; see that module's doc for why that's the intended,
|
|
12
|
+
* non-breaking default.
|
|
13
|
+
*
|
|
14
|
+
* Cross-lexicon (chant #2101): an Op step may call an activity any
|
|
15
|
+
* configured lexicon contributes (`terraformPlan`, `k3sInstall`,
|
|
16
|
+
* `flyApply`), and since this check became core-owned it fires on every
|
|
17
|
+
* project that declares an Op. `ctx.activityContracts` carries the contracts
|
|
18
|
+
* every configured lexicon declared, resolved by `loadActivityContracts`
|
|
19
|
+
* (`../../../op/activity-contract-registry.ts`) the same way
|
|
20
|
+
* `loadActivities` resolves the implementations, and is merged over the
|
|
21
|
+
* static table below. A context that carries none — a hand-built one in a
|
|
22
|
+
* test, a caller that has not loaded any — falls back to that table alone,
|
|
23
|
+
* exactly as before.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "../../post-synth";
|
|
27
|
+
import { validateActivitySteps, mergeActivityContracts, type ActivityContract } from "../../../op";
|
|
28
|
+
import type { OpConfig } from "../../../op";
|
|
29
|
+
import * as contracts from "../../../op/activities/activity-contracts";
|
|
30
|
+
import { isOpEntity } from "./support";
|
|
31
|
+
|
|
32
|
+
const CONTRACTS: Map<string, ActivityContract> = new Map(
|
|
33
|
+
Object.values(contracts).map((c) => [c.name, c]),
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
export const ops012: PostSynthCheck = {
|
|
37
|
+
id: "OPS012",
|
|
38
|
+
description: "Activity step args and outcomeAttribute.from must match the activity's declared contract",
|
|
39
|
+
|
|
40
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
41
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
42
|
+
const activeContracts = mergeActivityContracts(CONTRACTS, ctx.activityContracts);
|
|
43
|
+
|
|
44
|
+
for (const [entityKey, entity] of ctx.entities) {
|
|
45
|
+
if (!isOpEntity(entity)) continue;
|
|
46
|
+
const rec = entity as unknown as Record<string, unknown>;
|
|
47
|
+
|
|
48
|
+
const props = ((entity as { props?: Record<string, unknown> }).props ?? {}) as unknown as OpConfig;
|
|
49
|
+
if (typeof props.name !== "string" || !Array.isArray(props.phases)) continue;
|
|
50
|
+
|
|
51
|
+
for (const issue of validateActivitySteps(props, activeContracts)) {
|
|
52
|
+
diagnostics.push({
|
|
53
|
+
checkId: "OPS012",
|
|
54
|
+
severity: "error",
|
|
55
|
+
message: `Op "${issue.opName}", phase "${issue.phase}", step "${issue.fn}": ${issue.message}`,
|
|
56
|
+
entity: entityKey,
|
|
57
|
+
lexicon: typeof rec.lexicon === "string" ? rec.lexicon : undefined,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return diagnostics;
|
|
63
|
+
},
|
|
64
|
+
};
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OPS013 tests — ported from a hosting lexicon's own TMP013
|
|
3
|
+
* (chant #1290, moved to core by #2122).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, test, expect } from "vitest";
|
|
7
|
+
import type { PostSynthContext } from "../../post-synth";
|
|
8
|
+
import { DECLARABLE_MARKER } from "../../../declarable";
|
|
9
|
+
import { stepOutput, activityContract, type ActivityContract } from "../../../op";
|
|
10
|
+
import { z } from "zod";
|
|
11
|
+
import { ops013 } from "./ops013-step-output-ref";
|
|
12
|
+
|
|
13
|
+
function makeEntity(entityType: string, props: Record<string, unknown>) {
|
|
14
|
+
return {
|
|
15
|
+
[DECLARABLE_MARKER]: true,
|
|
16
|
+
entityType,
|
|
17
|
+
lexicon: "chant",
|
|
18
|
+
kind: "resource",
|
|
19
|
+
props,
|
|
20
|
+
attributes: {},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function makeCtxFromEntities(entities: Map<string, unknown>): PostSynthContext {
|
|
25
|
+
return {
|
|
26
|
+
outputs: new Map([["chant", ""]]),
|
|
27
|
+
entities: entities as Map<string, never>,
|
|
28
|
+
buildResult: {
|
|
29
|
+
outputs: new Map([["chant", ""]]),
|
|
30
|
+
entities: entities as Map<string, never>,
|
|
31
|
+
warnings: [],
|
|
32
|
+
errors: [],
|
|
33
|
+
sourceFileCount: 1,
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function opEntity(name: string, steps: unknown[], entityType = "Chant::Op") {
|
|
39
|
+
return makeEntity(entityType, { name, overview: "test", phases: [{ name: "Phase", steps }] });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function opEntityPhases(name: string, phases: Array<{ name: string; steps: unknown[]; parallel?: boolean }>) {
|
|
43
|
+
return makeEntity("Chant::Op", { name, overview: "test", phases });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// ── Cross-lexicon contracts (chant #2101) ────────────────────────────────
|
|
48
|
+
//
|
|
49
|
+
// An Op step may call an activity any configured lexicon contributes, and
|
|
50
|
+
// this check is core-owned, so it fires on every project that declares one.
|
|
51
|
+
// `ctx.activityContracts` is what `chant build`, `chant lint` and
|
|
52
|
+
// check-lexicon's example harness fill in from `loadActivityContracts`; the
|
|
53
|
+
// check merges it over its own static table.
|
|
54
|
+
|
|
55
|
+
/** Stand-in for what a lexicon (terraform) declares for its own activities. */
|
|
56
|
+
const foreignContracts = new Map<string, ActivityContract>([
|
|
57
|
+
[
|
|
58
|
+
"terraformPlan",
|
|
59
|
+
activityContract(
|
|
60
|
+
"terraformPlan",
|
|
61
|
+
z.strictObject({ root: z.string(), planFile: z.string().optional() }),
|
|
62
|
+
z.object({ planFile: z.string(), changed: z.boolean(), text: z.string() }),
|
|
63
|
+
),
|
|
64
|
+
],
|
|
65
|
+
[
|
|
66
|
+
"terraformApply",
|
|
67
|
+
activityContract("terraformApply", z.strictObject({ root: z.string(), planFile: z.string().optional() })),
|
|
68
|
+
],
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
function withContracts(ctx: PostSynthContext, contracts: ReadonlyMap<string, ActivityContract>): PostSynthContext {
|
|
72
|
+
return { ...ctx, activityContracts: contracts };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe("OPS013: step-output-ref", () => {
|
|
76
|
+
test("passes for a valid same-phase reference to a preceding step", () => {
|
|
77
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
78
|
+
["op", opEntity("reconcile", [
|
|
79
|
+
{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, id: "diff" },
|
|
80
|
+
// "output" (string) into "contains" (string) — a type-compatible reference (#1950-3).
|
|
81
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("diff", "output") } },
|
|
82
|
+
])],
|
|
83
|
+
]));
|
|
84
|
+
expect(ops013.check(ctx)).toHaveLength(0);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("errors on a reference to an unknown producer step id", () => {
|
|
88
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
89
|
+
["op", opEntity("reconcile", [
|
|
90
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("nope", "x") } },
|
|
91
|
+
])],
|
|
92
|
+
]));
|
|
93
|
+
const diags = ops013.check(ctx);
|
|
94
|
+
expect(diags.length).toBeGreaterThan(0);
|
|
95
|
+
expect(diags.every((d) => d.checkId === "OPS013" && d.severity === "error")).toBe(true);
|
|
96
|
+
expect(diags.some((d) => d.message.includes('unknown step id "nope"'))).toBe(true);
|
|
97
|
+
expect(diags[0].entity).toBe("op");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// No lexicon is configured, and no activity contract is registered for
|
|
101
|
+
// this step's "fn" either — the dangling reference is still caught
|
|
102
|
+
// structurally (chant #2122 acceptance: OPS013 fires on a project with no
|
|
103
|
+
// lexicons configured).
|
|
104
|
+
test("errors on an unknown producer step id with zero registered contracts", () => {
|
|
105
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
106
|
+
["op", opEntity("reconcile", [
|
|
107
|
+
{ kind: "activity", fn: "someActivity", args: { contains: stepOutput("nope", "x") } },
|
|
108
|
+
])],
|
|
109
|
+
]));
|
|
110
|
+
const diags = ops013.check(ctx);
|
|
111
|
+
expect(diags.some((d) => d.message.includes('unknown step id "nope"'))).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("errors on a path that doesn't exist on the producer's declared return schema", () => {
|
|
115
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
116
|
+
["op", opEntity("reconcile", [
|
|
117
|
+
{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, id: "diff" },
|
|
118
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("diff", "notAField") } },
|
|
119
|
+
])],
|
|
120
|
+
]));
|
|
121
|
+
const diags = ops013.check(ctx);
|
|
122
|
+
expect(diags.some((d) => d.message.includes('path "notAField"') && d.message.includes("does not exist"))).toBe(true);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("errors on a reference to a later step (in a later phase)", () => {
|
|
126
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
127
|
+
["op", opEntityPhases("reconcile", [
|
|
128
|
+
{ name: "Check", steps: [{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("diff", "drifted") } }] },
|
|
129
|
+
{ name: "Diff", steps: [{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, id: "diff" }] },
|
|
130
|
+
])],
|
|
131
|
+
]));
|
|
132
|
+
const diags = ops013.check(ctx);
|
|
133
|
+
expect(diags.some((d) => d.message.includes("later phase"))).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("errors on a reference into a step whose fn has no registered contract", () => {
|
|
137
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
138
|
+
["op", opEntity("reconcile", [
|
|
139
|
+
{ kind: "activity", fn: "customUnregisteredActivity", args: {}, id: "custom" },
|
|
140
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("custom", "x") } },
|
|
141
|
+
])],
|
|
142
|
+
]));
|
|
143
|
+
const diags = ops013.check(ctx);
|
|
144
|
+
expect(diags.some((d) => d.message.includes("no registered activity contract"))).toBe(true);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("ignores non-Op entities", () => {
|
|
148
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
149
|
+
["bucket", makeEntity("AWS::S3::Bucket", { name: "default" })],
|
|
150
|
+
]));
|
|
151
|
+
expect(ops013.check(ctx)).toHaveLength(0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("ignores an entity whose entityType isn't Chant::Op", () => {
|
|
155
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
156
|
+
["op", opEntity("reconcile", [
|
|
157
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("nope", "x") } },
|
|
158
|
+
], "Legacy::Op")],
|
|
159
|
+
]));
|
|
160
|
+
expect(ops013.check(ctx)).toHaveLength(0);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// ── cross-contract type compatibility (#1950-3) ──────────────────────────
|
|
164
|
+
|
|
165
|
+
test("errors when a boolean-returning path feeds a string-typed arg", () => {
|
|
166
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
167
|
+
["op", opEntity("reconcile", [
|
|
168
|
+
{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, id: "diff" },
|
|
169
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("diff", "drifted") } },
|
|
170
|
+
])],
|
|
171
|
+
]));
|
|
172
|
+
const diags = ops013.check(ctx);
|
|
173
|
+
expect(diags.some((d) => d.message.includes("type mismatch") && d.message.includes("boolean") && d.message.includes("string"))).toBe(true);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test("a matching type (string into string) passes", () => {
|
|
177
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
178
|
+
["op", opEntity("reconcile", [
|
|
179
|
+
{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, id: "diff" },
|
|
180
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("diff", "output") } },
|
|
181
|
+
])],
|
|
182
|
+
]));
|
|
183
|
+
expect(ops013.check(ctx)).toHaveLength(0);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("OPS013 reads a lexicon's activity contracts (#2101)", () => {
|
|
188
|
+
const terraformOp = () =>
|
|
189
|
+
makeCtxFromEntities(new Map([
|
|
190
|
+
["op", opEntity("app-apply", [
|
|
191
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app" }, id: "plan" },
|
|
192
|
+
{ kind: "activity", fn: "terraformApply", args: { root: "app", planFile: stepOutput("plan", "planFile") } },
|
|
193
|
+
])],
|
|
194
|
+
]));
|
|
195
|
+
|
|
196
|
+
test("the foreign producer is flagged when the context carries no contracts — the pre-#2101 behaviour", () => {
|
|
197
|
+
const diags = ops013.check(terraformOp());
|
|
198
|
+
expect(diags.some((d) => d.message.includes("no registered activity contract"))).toBe(true);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("it passes once the configured lexicon's contracts reach the check", () => {
|
|
202
|
+
expect(ops013.check(withContracts(terraformOp(), foreignContracts))).toHaveLength(0);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("a path the foreign contract's return schema does not declare is still caught", () => {
|
|
206
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
207
|
+
["op", opEntity("app-apply", [
|
|
208
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app" }, id: "plan" },
|
|
209
|
+
{ kind: "activity", fn: "terraformApply", args: { root: "app", planFile: stepOutput("plan", "planFyle") } },
|
|
210
|
+
])],
|
|
211
|
+
]));
|
|
212
|
+
const diags = ops013.check(withContracts(ctx, foreignContracts));
|
|
213
|
+
expect(diags.some((d) => d.message.includes('path "planFyle"') && d.message.includes("does not exist"))).toBe(true);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
test("a cross-lexicon type mismatch is still caught", () => {
|
|
217
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
218
|
+
["op", opEntity("app-apply", [
|
|
219
|
+
{ kind: "activity", fn: "terraformPlan", args: { root: "app" }, id: "plan" },
|
|
220
|
+
{ kind: "activity", fn: "terraformApply", args: { root: "app", planFile: stepOutput("plan", "changed") } },
|
|
221
|
+
])],
|
|
222
|
+
]));
|
|
223
|
+
const diags = ops013.check(withContracts(ctx, foreignContracts));
|
|
224
|
+
expect(diags.some((d) => d.message.includes("type mismatch"))).toBe(true);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("core's own contracts survive the merge", () => {
|
|
228
|
+
const ctx = makeCtxFromEntities(new Map([
|
|
229
|
+
["op", opEntity("reconcile", [
|
|
230
|
+
{ kind: "activity", fn: "lifecycleDiff", args: { env: "prod" }, id: "diff" },
|
|
231
|
+
{ kind: "activity", fn: "httpCheck", args: { url: "http://x", contains: stepOutput("diff", "notAField") } },
|
|
232
|
+
])],
|
|
233
|
+
]));
|
|
234
|
+
const diags = ops013.check(withContracts(ctx, foreignContracts));
|
|
235
|
+
expect(diags.some((d) => d.message.includes('path "notAField"'))).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
});
|