@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
|
@@ -950,19 +950,21 @@ export const x = { [Symbol.for("chant.declarable")]: true, entityType: "X", lexi
|
|
|
950
950
|
});
|
|
951
951
|
});
|
|
952
952
|
|
|
953
|
-
test("
|
|
954
|
-
//
|
|
955
|
-
//
|
|
956
|
-
//
|
|
953
|
+
test("serializer files under ops/ go to <project>/dist/ops even with no --output (#878)", async () => {
|
|
954
|
+
// Whatever a serializer emits under `ops/` must land in
|
|
955
|
+
// `<project>/dist/ops/<name>/`, beside the `op.json` core writes there,
|
|
956
|
+
// even when the build has no --output (an Op-only project often has no
|
|
957
|
+
// primary resource manifest to route). The routing is on the `ops/`
|
|
958
|
+
// prefix alone — it does not know or care what the files are.
|
|
957
959
|
const opSerializer: Serializer = {
|
|
958
960
|
name: "multi",
|
|
959
961
|
rulePrefix: "MULTI",
|
|
960
962
|
serialize: () => ({
|
|
961
963
|
primary: "{}",
|
|
962
964
|
files: {
|
|
963
|
-
"ops/
|
|
964
|
-
"ops/
|
|
965
|
-
"ops/
|
|
965
|
+
"ops/nightly-report/steps.json": "{}\n",
|
|
966
|
+
"ops/nightly-report/notes.md": "# notes\n",
|
|
967
|
+
"ops/nightly-report/nested/extra.txt": "extra\n",
|
|
966
968
|
},
|
|
967
969
|
}),
|
|
968
970
|
};
|
|
@@ -979,8 +981,31 @@ export const x = { [Symbol.for("chant.declarable")]: true, entityType: "X", lexi
|
|
|
979
981
|
} as BuildOptions);
|
|
980
982
|
|
|
981
983
|
expect(result.errors).toEqual([]);
|
|
982
|
-
expect(existsSync(join(testDir, "dist", "ops", "
|
|
983
|
-
expect(existsSync(join(testDir, "dist", "ops", "
|
|
984
|
+
expect(existsSync(join(testDir, "dist", "ops", "nightly-report", "steps.json"))).toBe(true);
|
|
985
|
+
expect(existsSync(join(testDir, "dist", "ops", "nightly-report", "notes.md"))).toBe(true);
|
|
986
|
+
expect(existsSync(join(testDir, "dist", "ops", "nightly-report", "nested", "extra.txt"))).toBe(true);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
test("an Op entity emits dist/ops/<name>/op.json from core, with no serializer involved (#2118)", async () => {
|
|
990
|
+
await writeFile(
|
|
991
|
+
join(testDir, "deploy.op.ts"),
|
|
992
|
+
`export default { [Symbol.for("chant.declarable")]: true, entityType: "Chant::Op", lexicon: "chant", kind: "resource", attributes: {},` +
|
|
993
|
+
` props: { name: "deploy", overview: "Deploy the thing", labels: { Env: "staging" },` +
|
|
994
|
+
` phases: [{ name: "Apply", steps: [{ kind: "activity", fn: "shellCmd", args: { cmd: "true" } }] }] } };`,
|
|
995
|
+
);
|
|
996
|
+
|
|
997
|
+
const result = await buildCommand({ path: testDir, format: "json", serializers: [] } as BuildOptions);
|
|
998
|
+
|
|
999
|
+
expect(result.errors).toEqual([]);
|
|
1000
|
+
const ir = JSON.parse(readFileSync(join(testDir, "dist", "ops", "deploy", "op.json"), "utf-8"));
|
|
1001
|
+
expect(ir.formatVersion).toBe("2.0");
|
|
1002
|
+
expect(ir.name).toBe("deploy");
|
|
1003
|
+
expect(ir.labels).toEqual({ Env: "staging" });
|
|
1004
|
+
expect(ir).not.toHaveProperty("taskQueue");
|
|
1005
|
+
expect(ir.phases[0].steps[0]).toMatchObject({ kind: "activity", fn: "shellCmd", profile: "fastIdempotent" });
|
|
1006
|
+
// Ops are held out of `partitionByLexicon` (they have no serializer and
|
|
1007
|
+
// need none), so core's own `chant` lexicon never trips the warning.
|
|
1008
|
+
expect(result.warnings.filter((w) => w.includes("No serializer"))).toEqual([]);
|
|
984
1009
|
});
|
|
985
1010
|
|
|
986
1011
|
// ── #284 bug 2: array-of-objects must serialize to valid YAML ──────────
|
|
@@ -1219,6 +1244,302 @@ export const x = { [Symbol.for("chant.declarable")]: true, entityType: "X", lexi
|
|
|
1219
1244
|
30_000,
|
|
1220
1245
|
);
|
|
1221
1246
|
});
|
|
1247
|
+
|
|
1248
|
+
describe("post-synth findings honor inline chant-ignore comments (chant #2111)", () => {
|
|
1249
|
+
/**
|
|
1250
|
+
* A discovered entity carrying a `suppressions` directive by hand, the
|
|
1251
|
+
* generic, duck-typed field `applyInlineSuppressions` reads (see
|
|
1252
|
+
* `../../lint/suppressions.ts`'s module doc). The terraform lexicon
|
|
1253
|
+
* populates this from real `# chant-ignore*` HCL comments
|
|
1254
|
+
* (`lexicons/terraform/src/hcl/suppressions.ts`); this test proves the
|
|
1255
|
+
* core wiring, that `chant build` applies it to ANY lexicon's entities,
|
|
1256
|
+
* without needing a real HCL parse.
|
|
1257
|
+
*/
|
|
1258
|
+
async function writeSuppressibleEntity(ids: string): Promise<void> {
|
|
1259
|
+
await writeFile(
|
|
1260
|
+
join(testDir, "main.ts"),
|
|
1261
|
+
`export const e = {\n` +
|
|
1262
|
+
` lexicon: "test",\n` +
|
|
1263
|
+
` entityType: "TestEntity",\n` +
|
|
1264
|
+
` [Symbol.for("chant.declarable")]: true,\n` +
|
|
1265
|
+
` suppressions: [{ form: "chant-ignore", ids: new Set(${JSON.stringify(ids.split(","))}), file: "main.ts", line: 1, key: "main.ts:1:chant-ignore" }],\n` +
|
|
1266
|
+
`};\n`,
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/** Same shape as `fakePostSynthPlugin` above, except its check attaches `entity` to whatever entity the build discovered, so a suppression directive on that entity has something to match against. */
|
|
1271
|
+
function fakeEntitySuppressionPlugin(checkId: string, severity: "error" | "warning" | "info"): LexiconPlugin {
|
|
1272
|
+
return {
|
|
1273
|
+
name: "fake",
|
|
1274
|
+
serializer: { name: "fake", rulePrefix: "FAKE", serialize: () => "{}" },
|
|
1275
|
+
generate: async () => {},
|
|
1276
|
+
validate: async () => {},
|
|
1277
|
+
coverage: async () => {},
|
|
1278
|
+
package: async () => {},
|
|
1279
|
+
postSynthChecks: () => [
|
|
1280
|
+
{
|
|
1281
|
+
id: checkId,
|
|
1282
|
+
description: "test check",
|
|
1283
|
+
check: (ctx) => {
|
|
1284
|
+
const [key] = [...ctx.entities.keys()];
|
|
1285
|
+
return key ? [{ checkId, severity, message: `${checkId} triggered`, entity: key }] : [];
|
|
1286
|
+
},
|
|
1287
|
+
},
|
|
1288
|
+
],
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
test("an inline chant-ignore naming the finding's checkId suppresses it and reports a suppressed count", async () => {
|
|
1293
|
+
await writeSuppressibleEntity("FAKE005");
|
|
1294
|
+
await writeFile(join(testDir, "chant.config.ts"), `export default {};\n`);
|
|
1295
|
+
|
|
1296
|
+
const result = await buildCommand({
|
|
1297
|
+
path: testDir,
|
|
1298
|
+
format: "json",
|
|
1299
|
+
serializers: [mockSerializer],
|
|
1300
|
+
plugins: [fakeEntitySuppressionPlugin("FAKE005", "error")],
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
expect(result.success).toBe(true);
|
|
1304
|
+
expect(result.errors).toEqual([]);
|
|
1305
|
+
expect(result.warnings.some((w) => w.includes("1 post-synth finding(s) suppressed via inline chant-ignore comments"))).toBe(true);
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
test("an inline chant-ignore naming a different id has no effect", async () => {
|
|
1309
|
+
await writeSuppressibleEntity("FAKE006");
|
|
1310
|
+
await writeFile(join(testDir, "chant.config.ts"), `export default {};\n`);
|
|
1311
|
+
|
|
1312
|
+
const result = await buildCommand({
|
|
1313
|
+
path: testDir,
|
|
1314
|
+
format: "json",
|
|
1315
|
+
serializers: [mockSerializer],
|
|
1316
|
+
plugins: [fakeEntitySuppressionPlugin("FAKE007", "error")],
|
|
1317
|
+
});
|
|
1318
|
+
|
|
1319
|
+
expect(result.success).toBe(false);
|
|
1320
|
+
expect(result.errors.some((e) => e.includes("FAKE007 triggered"))).toBe(true);
|
|
1321
|
+
expect(result.warnings.some((w) => w.includes("suppressed"))).toBe(false);
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1324
|
+
test("ignorable: false in lint.rules denies the ignore and reports the attempt, keeping the finding active", async () => {
|
|
1325
|
+
await writeSuppressibleEntity("FAKE008");
|
|
1326
|
+
await writeFile(
|
|
1327
|
+
join(testDir, "chant.config.ts"),
|
|
1328
|
+
`export default { lint: { rules: { "FAKE008": ["error", { ignorable: false }] } } };\n`,
|
|
1329
|
+
);
|
|
1330
|
+
|
|
1331
|
+
const result = await buildCommand({
|
|
1332
|
+
path: testDir,
|
|
1333
|
+
format: "json",
|
|
1334
|
+
serializers: [mockSerializer],
|
|
1335
|
+
plugins: [fakeEntitySuppressionPlugin("FAKE008", "error")],
|
|
1336
|
+
});
|
|
1337
|
+
|
|
1338
|
+
expect(result.success).toBe(false);
|
|
1339
|
+
expect(result.errors.some((e) => e.includes("FAKE008 triggered"))).toBe(true);
|
|
1340
|
+
expect(result.warnings.some((w) => w.includes("SUPP003") && w.includes("ignorable: false"))).toBe(true);
|
|
1341
|
+
});
|
|
1342
|
+
});
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* chant #2113: `lint.presets` filters WHICH post-synth check ids are
|
|
1346
|
+
* reported at all, ahead of `lint.rules`' severity overrides (proven
|
|
1347
|
+
* above). A plugin with no `lintPresets()` is unaffected (the common case
|
|
1348
|
+
* today); one that ships presets defaults to `recommended` and is
|
|
1349
|
+
* overridden per-lexicon via `lint.presets: { <name>: "<preset>" }`.
|
|
1350
|
+
*/
|
|
1351
|
+
describe("post-synth findings honor lint.presets (chant #2113)", () => {
|
|
1352
|
+
async function writeTrivialEntity(): Promise<void> {
|
|
1353
|
+
await writeFile(
|
|
1354
|
+
join(testDir, "main.ts"),
|
|
1355
|
+
`export const e = { lexicon: "test", entityType: "TestEntity", [Symbol.for("chant.declarable")]: true };\n`,
|
|
1356
|
+
);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
/** A `LexiconPlugin` whose one post-synth check always fires, with `recommended`/`all` presets. `recommended` excludes the check's own id, `all` includes it. */
|
|
1360
|
+
function fakePresetPlugin(): LexiconPlugin {
|
|
1361
|
+
return {
|
|
1362
|
+
name: "fakepreset",
|
|
1363
|
+
serializer: { name: "fakepreset", rulePrefix: "FP", serialize: () => "{}" },
|
|
1364
|
+
generate: async () => {},
|
|
1365
|
+
validate: async () => {},
|
|
1366
|
+
coverage: async () => {},
|
|
1367
|
+
package: async () => {},
|
|
1368
|
+
postSynthChecks: () => [
|
|
1369
|
+
{
|
|
1370
|
+
id: "FP001",
|
|
1371
|
+
description: "a report-only check, always fires",
|
|
1372
|
+
check: () => [{ checkId: "FP001", severity: "warning" as const, message: "FP001 triggered" }],
|
|
1373
|
+
},
|
|
1374
|
+
],
|
|
1375
|
+
lintPresets: () => ({ recommended: [], all: ["FP001"] }),
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
test("with no lint.presets config, the default preset (recommended) excludes the finding", async () => {
|
|
1380
|
+
await writeTrivialEntity();
|
|
1381
|
+
const result = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [fakePresetPlugin()] });
|
|
1382
|
+
expect(result.warnings.join("\n")).not.toContain("FP001 triggered");
|
|
1383
|
+
expect(result.warnings.some((w) => w.includes("not reported: excluded by the active lint.presets preset"))).toBe(true);
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
test('lint.presets: { fakepreset: "all" } includes the finding', async () => {
|
|
1387
|
+
await writeTrivialEntity();
|
|
1388
|
+
await writeFile(
|
|
1389
|
+
join(testDir, "chant.config.ts"),
|
|
1390
|
+
`export default { lint: { presets: { fakepreset: "all" } } };\n`,
|
|
1391
|
+
);
|
|
1392
|
+
const result = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [fakePresetPlugin()] });
|
|
1393
|
+
expect(result.warnings.join("\n")).toContain("FP001 triggered");
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
test("an explicit lint.rules entry re-enables the rule even under the excluding (recommended) preset", async () => {
|
|
1397
|
+
await writeTrivialEntity();
|
|
1398
|
+
await writeFile(
|
|
1399
|
+
join(testDir, "chant.config.ts"),
|
|
1400
|
+
`export default { lint: { rules: { "FP001": "warning" } } };\n`,
|
|
1401
|
+
);
|
|
1402
|
+
const result = await buildCommand({ path: testDir, format: "json", serializers: [mockSerializer], plugins: [fakePresetPlugin()] });
|
|
1403
|
+
expect(result.warnings.join("\n")).toContain("FP001 triggered");
|
|
1404
|
+
});
|
|
1405
|
+
});
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
// #2122 (epic #2114 sub-issue 6) — OPS012/OPS013/OPS014 are core-owned
|
|
1409
|
+
// post-synth checks over the Op model, run unscoped by `coreOpChecks()`
|
|
1410
|
+
// regardless of which plugins/lexicons are configured. This is the
|
|
1411
|
+
// end-to-end path acceptance criterion 3 needs: `chant build` over a real
|
|
1412
|
+
// ConvergeOp-shaped fixture with a destructive gated dispatch rule fails
|
|
1413
|
+
// with the OPS014 refusal, with zero plugins/lexicons loaded.
|
|
1414
|
+
describe("buildCommand — OPS014 (op-model post-synth checks, #2122)", () => {
|
|
1415
|
+
let testDir: string;
|
|
1416
|
+
|
|
1417
|
+
const mockSerializer: Serializer = {
|
|
1418
|
+
name: "test",
|
|
1419
|
+
rulePrefix: "TEST",
|
|
1420
|
+
serialize: (entities) => JSON.stringify({ resources: [...entities.keys()] }, null, 2),
|
|
1421
|
+
};
|
|
1422
|
+
|
|
1423
|
+
beforeEach(async () => {
|
|
1424
|
+
testDir = join(tmpdir(), `chant-ops014-test-${Date.now()}-${Math.random()}`);
|
|
1425
|
+
await mkdir(testDir, { recursive: true });
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
afterEach(async () => {
|
|
1429
|
+
await rm(testDir, { recursive: true, force: true });
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
test("fails build with the OPS014 refusal on a ConvergeOp dispatching a destructive, gated op", async () => {
|
|
1433
|
+
// The destructive dispatch target: a gated `nativeApply` with
|
|
1434
|
+
// `deleteMode: "gated"` — classified destructive by
|
|
1435
|
+
// `../../op/op-verb-class.ts`, gate or not.
|
|
1436
|
+
await writeFile(
|
|
1437
|
+
join(testDir, "prune-staging.op.ts"),
|
|
1438
|
+
`
|
|
1439
|
+
export const pruneStaging = {
|
|
1440
|
+
[Symbol.for("chant.declarable")]: true,
|
|
1441
|
+
entityType: "Chant::Op",
|
|
1442
|
+
lexicon: "chant",
|
|
1443
|
+
kind: "resource",
|
|
1444
|
+
props: {
|
|
1445
|
+
name: "prune-staging",
|
|
1446
|
+
overview: "test",
|
|
1447
|
+
phases: [
|
|
1448
|
+
{ name: "Approve", steps: [{ kind: "gate", gate: "approve-x" }] },
|
|
1449
|
+
{ name: "Apply", steps: [
|
|
1450
|
+
{ kind: "activity", fn: "nativeApply", args: { target: "kubectl", env: "staging", output: "dist", deleteMode: "gated" } },
|
|
1451
|
+
] },
|
|
1452
|
+
],
|
|
1453
|
+
},
|
|
1454
|
+
};
|
|
1455
|
+
`,
|
|
1456
|
+
);
|
|
1457
|
+
|
|
1458
|
+
// The ConvergeOp-shaped Op: dial "apply", one rule dispatching the
|
|
1459
|
+
// destructive target above.
|
|
1460
|
+
await writeFile(
|
|
1461
|
+
join(testDir, "converge.op.ts"),
|
|
1462
|
+
`
|
|
1463
|
+
export const converge = {
|
|
1464
|
+
[Symbol.for("chant.declarable")]: true,
|
|
1465
|
+
entityType: "Chant::Op",
|
|
1466
|
+
lexicon: "chant",
|
|
1467
|
+
kind: "resource",
|
|
1468
|
+
props: {
|
|
1469
|
+
name: "converge",
|
|
1470
|
+
overview: "test",
|
|
1471
|
+
labels: { Converge: "true", Env: "staging", Dial: "apply" },
|
|
1472
|
+
phases: [
|
|
1473
|
+
{ name: "Observe", steps: [{ kind: "activity", fn: "lifecycleDiff", args: { env: "staging" }, id: "diff" }] },
|
|
1474
|
+
{ name: "Converge", steps: [{ kind: "activity", fn: "convergeTick", args: { rules: [
|
|
1475
|
+
{
|
|
1476
|
+
id: "drift-prune",
|
|
1477
|
+
when: { kind: "field-comparison", field: "status", op: "eq", value: "drifted" },
|
|
1478
|
+
then: { kind: "run", op: "prune-staging" },
|
|
1479
|
+
why: "Prune drifted resources.",
|
|
1480
|
+
},
|
|
1481
|
+
] } }] },
|
|
1482
|
+
],
|
|
1483
|
+
},
|
|
1484
|
+
};
|
|
1485
|
+
`,
|
|
1486
|
+
);
|
|
1487
|
+
|
|
1488
|
+
const result = await buildCommand({
|
|
1489
|
+
path: testDir,
|
|
1490
|
+
format: "json",
|
|
1491
|
+
serializers: [mockSerializer],
|
|
1492
|
+
plugins: [],
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
expect(result.success).toBe(false);
|
|
1496
|
+
// build.ts's plain error strings carry the diagnostic's message text, not
|
|
1497
|
+
// its checkId — OPS014's own message names what it refused and why (see
|
|
1498
|
+
// ../../lint/rules/op/ops014-converge-rule-refusals.ts).
|
|
1499
|
+
expect(
|
|
1500
|
+
result.errors.some(
|
|
1501
|
+
(e) => e.includes("destructive") && e.includes("prune-staging") && e.includes("refused in v1"),
|
|
1502
|
+
),
|
|
1503
|
+
).toBe(true);
|
|
1504
|
+
});
|
|
1505
|
+
|
|
1506
|
+
// #2202: a gate step spelling its name `signalName` still builds, and the
|
|
1507
|
+
// build says so once, naming the Ops involved rather than each gate step.
|
|
1508
|
+
test("warns once, naming the Ops, when a gate step still uses the deprecated `signalName` key", async () => {
|
|
1509
|
+
const opFile = (name: string): string => `
|
|
1510
|
+
export const ${name.replace(/-/g, "")} = {
|
|
1511
|
+
[Symbol.for("chant.declarable")]: true,
|
|
1512
|
+
entityType: "Chant::Op",
|
|
1513
|
+
lexicon: "chant",
|
|
1514
|
+
kind: "resource",
|
|
1515
|
+
props: {
|
|
1516
|
+
name: "${name}",
|
|
1517
|
+
overview: "test",
|
|
1518
|
+
phases: [{ name: "Approve", steps: [{ kind: "gate", signalName: "approve-x" }] }],
|
|
1519
|
+
},
|
|
1520
|
+
};
|
|
1521
|
+
`;
|
|
1522
|
+
await writeFile(join(testDir, "old-a.op.ts"), opFile("old-a"));
|
|
1523
|
+
await writeFile(join(testDir, "old-b.op.ts"), opFile("old-b"));
|
|
1524
|
+
await writeFile(
|
|
1525
|
+
join(testDir, "new-c.op.ts"),
|
|
1526
|
+
opFile("new-c").replace("signalName", "gate"),
|
|
1527
|
+
);
|
|
1528
|
+
|
|
1529
|
+
const result = await buildCommand({
|
|
1530
|
+
path: testDir,
|
|
1531
|
+
format: "json",
|
|
1532
|
+
serializers: [mockSerializer],
|
|
1533
|
+
plugins: [],
|
|
1534
|
+
});
|
|
1535
|
+
|
|
1536
|
+
const deprecation = result.warnings.filter((w) => w.includes("signalName"));
|
|
1537
|
+
expect(deprecation).toHaveLength(1);
|
|
1538
|
+
expect(deprecation[0]).toContain("old-a");
|
|
1539
|
+
expect(deprecation[0]).toContain("old-b");
|
|
1540
|
+
expect(deprecation[0]).not.toContain("new-c");
|
|
1541
|
+
expect(deprecation[0]).toContain("0.60.0");
|
|
1542
|
+
});
|
|
1222
1543
|
});
|
|
1223
1544
|
|
|
1224
1545
|
// ── #284 bug 1: -o extension drives format when --format is absent ────────
|
|
@@ -14,11 +14,19 @@ import type { OwnershipMarker } from "../../ownership";
|
|
|
14
14
|
import { resolveCliBuildParams } from "../build-params-cli";
|
|
15
15
|
import type { Serializer, SerializerResult } from "../../serializer";
|
|
16
16
|
import type { LexiconPlugin } from "../../lexicon";
|
|
17
|
-
import { runPostSynthChecks } from "../../lint/post-synth";
|
|
17
|
+
import { runPostSynthChecks, type PostSynthDiagnostic } from "../../lint/post-synth";
|
|
18
|
+
import { loadActivityContracts } from "../../op/activity-contract-registry";
|
|
19
|
+
import { isOpEntity } from "../../op/resource";
|
|
20
|
+
import { serializeOpIR } from "../../op/op-ir";
|
|
21
|
+
import { opUsesDeprecatedGateKey, DEPRECATED_GATE_KEY_WARNING } from "../../op/gate-name";
|
|
22
|
+
import type { OpConfig } from "../../op/types";
|
|
18
23
|
import { coreReceiptChecks } from "../../lint/receipt-checks";
|
|
19
24
|
import { coreOutputChecks } from "../../lint/output-checks";
|
|
20
25
|
import { coreKnowledgeChecks } from "../../lint/knowledge-checks";
|
|
21
|
-
import { applyConfiguredSeverity } from "../../lint/config";
|
|
26
|
+
import { applyConfiguredSeverity, applyConfiguredPreset, resolvePresetIds } from "../../lint/config";
|
|
27
|
+
import { applyInlineSuppressions, type SuppressionMetaFinding } from "../../lint/suppressions";
|
|
28
|
+
import type { Declarable } from "../../declarable";
|
|
29
|
+
import { coreOpChecks } from "../../lint/rules/op";
|
|
22
30
|
import { loadPolicyChecks } from "../../lint/policy";
|
|
23
31
|
import { armSandboxPolicyExecution, runProjectPolicies } from "../../lint/policy-sandbox";
|
|
24
32
|
import { sortedJsonReplacer } from "../../utils";
|
|
@@ -392,7 +400,46 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
392
400
|
// rule id — so `lint.rules: { WAW019: "off" }` suppresses a post-synth
|
|
393
401
|
// finding just as it suppresses a pre-synth one. A finding it suppresses is
|
|
394
402
|
// counted, not dropped silently — see `suppressedPostSynthCount` below.
|
|
403
|
+
//
|
|
404
|
+
// chant #2111: every batch also goes through `applyInlineSuppressions`
|
|
405
|
+
// right after, so a `# chant-ignore` comment on an entity (terraform's HCL
|
|
406
|
+
// blocks today; any lexicon that attaches `suppressions` to its own
|
|
407
|
+
// entities tomorrow) suppresses identically regardless of which phase
|
|
408
|
+
// produced the diagnostic. Same counted-not-dropped contract, in its own
|
|
409
|
+
// `inlineSuppressedCount`, plus `suppressionMeta` for directives that are
|
|
410
|
+
// themselves worth a finding (expired, misplaced, denied by `ignorable: false`).
|
|
395
411
|
let suppressedPostSynthCount = 0;
|
|
412
|
+
let inlineSuppressedCount = 0;
|
|
413
|
+
const suppressionMeta: SuppressionMetaFinding[] = [];
|
|
414
|
+
// A rule a `lint.presets` preset excludes (chant #2113) is a distinct
|
|
415
|
+
// reason a finding doesn't reach the errors/warnings lists. The preset
|
|
416
|
+
// decided the rule shouldn't run at all under this project's configured
|
|
417
|
+
// tier, not that `lint.rules` downgraded it to "off", so it gets its own
|
|
418
|
+
// counter and its own summary line rather than folding into the one above.
|
|
419
|
+
let suppressedPresetCount = 0;
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Apply every suppression/filtering surface to one batch, in order:
|
|
423
|
+
* `lint.presets` (chant #2113, which check ids are enabled at all; a
|
|
424
|
+
* no-op when `presetIds` is undefined, the case for every call site below
|
|
425
|
+
* except the per-plugin loop, which is the only one with a lexicon a
|
|
426
|
+
* preset could apply to), then `lint.rules` severity overrides (chant
|
|
427
|
+
* #1138), then inline `# chant-ignore` comments (chant #2111).
|
|
428
|
+
*/
|
|
429
|
+
const resolvePostSynth = (
|
|
430
|
+
diags: PostSynthDiagnostic[],
|
|
431
|
+
entities: Map<string, Declarable>,
|
|
432
|
+
presetIds?: Set<string>,
|
|
433
|
+
): PostSynthDiagnostic[] => {
|
|
434
|
+
const { diagnostics: afterPreset, suppressed: presetSuppressed } = applyConfiguredPreset(diags, presetIds, config.lint?.rules);
|
|
435
|
+
suppressedPresetCount += presetSuppressed.length;
|
|
436
|
+
const { diagnostics: afterConfig, suppressed: configSuppressed } = applyConfiguredSeverity(afterPreset, config.lint?.rules);
|
|
437
|
+
suppressedPostSynthCount += configSuppressed.length;
|
|
438
|
+
const { diagnostics: active, suppressed: inlineSuppressed, meta } = applyInlineSuppressions(afterConfig, entities, config.lint?.rules);
|
|
439
|
+
inlineSuppressedCount += inlineSuppressed.length;
|
|
440
|
+
suppressionMeta.push(...meta);
|
|
441
|
+
return active;
|
|
442
|
+
};
|
|
396
443
|
|
|
397
444
|
// Core-owned post-synth checks over effect receipts (#1833). Receipts are
|
|
398
445
|
// recognized by marker, lexicon-independently, so this set runs over the
|
|
@@ -400,9 +447,7 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
400
447
|
// Same severity-resolution funnel as every other post-synth finding.
|
|
401
448
|
if (result.errors.length === 0) {
|
|
402
449
|
const receiptDiags = runPostSynthChecks(coreReceiptChecks(), result, env);
|
|
403
|
-
const
|
|
404
|
-
suppressedPostSynthCount += suppressed.length;
|
|
405
|
-
for (const diag of activeDiags) {
|
|
450
|
+
for (const diag of resolvePostSynth(receiptDiags, result.entities)) {
|
|
406
451
|
const prefix = diag.entity ? `[${diag.entity}] ` : "";
|
|
407
452
|
const lexiconSuffix = diag.lexicon ? ` (${diag.lexicon})` : "";
|
|
408
453
|
if (diag.severity === "error") {
|
|
@@ -419,9 +464,7 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
419
464
|
// the FULL build result the same way the receipt checks above do.
|
|
420
465
|
if (result.errors.length === 0) {
|
|
421
466
|
const outputDiags = runPostSynthChecks(coreOutputChecks(), result, env);
|
|
422
|
-
const
|
|
423
|
-
suppressedPostSynthCount += suppressed.length;
|
|
424
|
-
for (const diag of activeDiags) {
|
|
467
|
+
for (const diag of resolvePostSynth(outputDiags, result.entities)) {
|
|
425
468
|
const prefix = diag.entity ? `[${diag.entity}] ` : "";
|
|
426
469
|
const lexiconSuffix = diag.lexicon ? ` (${diag.lexicon})` : "";
|
|
427
470
|
if (diag.severity === "error") {
|
|
@@ -440,7 +483,36 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
440
483
|
if (result.errors.length === 0) {
|
|
441
484
|
const bundle = await loadOkfBundle(resolveKnowledgeDir(config, configDir));
|
|
442
485
|
const knowledgeDiags = runPostSynthChecks(coreKnowledgeChecks(bundle), result, env);
|
|
443
|
-
const
|
|
486
|
+
for (const diag of resolvePostSynth(knowledgeDiags, result.entities)) {
|
|
487
|
+
const prefix = diag.entity ? `[${diag.entity}] ` : "";
|
|
488
|
+
const lexiconSuffix = diag.lexicon ? ` (${diag.lexicon})` : "";
|
|
489
|
+
if (diag.severity === "error") {
|
|
490
|
+
errors.push(formatError({ message: `${prefix}${diag.message}${lexiconSuffix}` }));
|
|
491
|
+
} else {
|
|
492
|
+
warnings.push(formatWarning({ message: `${prefix}${diag.message}${lexiconSuffix}` }));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Activity contracts across every loaded lexicon (chant #2101): core's own
|
|
498
|
+
// plus each lexicon's, by the same `@intentius/chant-lexicon-<name>/op/...`
|
|
499
|
+
// convention `loadActivities` resolves implementations with. OPS012/OPS013
|
|
500
|
+
// and a plugin's own checks merge this over their own tables, so a step
|
|
501
|
+
// calling a lexicon's activity is validated against that lexicon's contract
|
|
502
|
+
// instead of failing for the absence of one. Resolved once, for both check
|
|
503
|
+
// passes below.
|
|
504
|
+
const activityContracts =
|
|
505
|
+
result.errors.length === 0 ? await loadActivityContracts(options.plugins ?? []) : undefined;
|
|
506
|
+
|
|
507
|
+
// Core-owned post-synth checks over the Op model (#2122, epic #2114
|
|
508
|
+
// sub-issue 6) — OPS012/OPS013/OPS014, ported from a hosting lexicon's
|
|
509
|
+
// own TMP012/TMP013/TMP014. An Op is recognized by entity type
|
|
510
|
+
// (`OpResource`), not by which lexicon declared it, so this runs over the
|
|
511
|
+
// FULL build result regardless of which plugins loaded — same as the
|
|
512
|
+
// receipt/output/knowledge checks above.
|
|
513
|
+
if (result.errors.length === 0) {
|
|
514
|
+
const opDiags = runPostSynthChecks(coreOpChecks(), result, env, { activityContracts });
|
|
515
|
+
const { diagnostics: activeDiags, suppressed } = applyConfiguredSeverity(opDiags, config.lint?.rules);
|
|
444
516
|
suppressedPostSynthCount += suppressed.length;
|
|
445
517
|
for (const diag of activeDiags) {
|
|
446
518
|
const prefix = diag.entity ? `[${diag.entity}] ` : "";
|
|
@@ -471,10 +543,16 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
471
543
|
}
|
|
472
544
|
|
|
473
545
|
const scopedResult = { ...result, outputs: scopedOutputs };
|
|
474
|
-
const postDiags = runPostSynthChecks(checks, scopedResult, env);
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
for
|
|
546
|
+
const postDiags = runPostSynthChecks(checks, scopedResult, env, { activityContracts });
|
|
547
|
+
// `lint.presets` (chant #2113) filters WHICH check ids are reported at
|
|
548
|
+
// all, before `lint.rules`/inline suppression act on the reported
|
|
549
|
+
// ones. It's a no-op for a lexicon that ships no `lintPresets()` (the
|
|
550
|
+
// common case today), since `resolvePresetIds` returns `undefined`
|
|
551
|
+
// for one. This is the only `resolvePostSynth` call site with a
|
|
552
|
+
// lexicon a preset could apply to; every other call below passes no
|
|
553
|
+
// `presetIds` at all.
|
|
554
|
+
const presetIds = resolvePresetIds(plugin.lintPresets?.(), config.lint?.presets?.[plugin.name] ?? "recommended");
|
|
555
|
+
for (const diag of resolvePostSynth(postDiags, result.entities, presetIds)) {
|
|
478
556
|
const prefix = diag.entity ? `[${diag.entity}] ` : "";
|
|
479
557
|
const lexiconSuffix = diag.lexicon ? ` (${diag.lexicon})` : "";
|
|
480
558
|
if (diag.severity === "error") {
|
|
@@ -511,9 +589,7 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
511
589
|
env,
|
|
512
590
|
preloaded: preloadedPolicyChecks,
|
|
513
591
|
});
|
|
514
|
-
const
|
|
515
|
-
suppressedPostSynthCount += suppressed.length;
|
|
516
|
-
for (const diag of activePolicyDiags) {
|
|
592
|
+
for (const diag of resolvePostSynth(policyDiags, result.entities)) {
|
|
517
593
|
const prefix = diag.entity ? `[${diag.entity}] ` : "";
|
|
518
594
|
const where = diag.lexicon ? ` (${diag.lexicon})` : "";
|
|
519
595
|
const msg = `[policy:${diag.checkId}] ${prefix}${diag.message}${where}`;
|
|
@@ -529,15 +605,58 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
529
605
|
}),
|
|
530
606
|
);
|
|
531
607
|
}
|
|
608
|
+
|
|
609
|
+
if (suppressedPresetCount > 0) {
|
|
610
|
+
warnings.push(
|
|
611
|
+
formatWarning({
|
|
612
|
+
message: `${suppressedPresetCount} post-synth finding(s) not reported: excluded by the active lint.presets preset`,
|
|
613
|
+
}),
|
|
614
|
+
);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (inlineSuppressedCount > 0) {
|
|
618
|
+
warnings.push(
|
|
619
|
+
formatWarning({
|
|
620
|
+
message: `${inlineSuppressedCount} post-synth finding(s) suppressed via inline chant-ignore comments`,
|
|
621
|
+
}),
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// Directives that are themselves worth a finding (expired, misplaced, or
|
|
626
|
+
// naming a rule id configured `ignorable: false`) are reported once
|
|
627
|
+
// each, never silently (chant #2111).
|
|
628
|
+
for (const m of suppressionMeta) {
|
|
629
|
+
warnings.push(formatWarning({ message: `[${m.checkId}] ${m.file}:${m.line} ${m.message}` }));
|
|
630
|
+
}
|
|
532
631
|
}
|
|
533
632
|
|
|
534
633
|
// Empty-output guard: source files were discovered but no lexicon produced
|
|
535
634
|
// any output. Almost always indicates broken imports resolving to undefined
|
|
536
635
|
// (e.g. missing root re-exports from a lexicon) or modules that exported no
|
|
537
636
|
// Declarables. Without this guard, chant writes "{}" and exits 0.
|
|
637
|
+
//
|
|
638
|
+
// An Op declaration is the honest exception (#2118): it belongs to no
|
|
639
|
+
// lexicon partition and its build output is the `op.json` written below,
|
|
640
|
+
// not a resource manifest, so an Ops-only project legitimately produces no
|
|
641
|
+
// serializer output and must not be reported as a broken import.
|
|
642
|
+
const declaredOps = [...result.entities.values()].filter(isOpEntity);
|
|
643
|
+
|
|
644
|
+
// The gate step's name key was renamed from `signalName` to `gate` in #2202.
|
|
645
|
+
// The old key is still read; a build that finds an Op still using it says so
|
|
646
|
+
// once, naming every Op involved, rather than once per gate step.
|
|
647
|
+
const opsOnOldGateKey = declaredOps
|
|
648
|
+
.map((entity) => (entity as unknown as { props?: OpConfig }).props)
|
|
649
|
+
.filter((config): config is OpConfig => Boolean(config?.name) && opUsesDeprecatedGateKey(config as OpConfig))
|
|
650
|
+
.map((config) => config.name);
|
|
651
|
+
if (opsOnOldGateKey.length > 0) {
|
|
652
|
+
warnings.push(
|
|
653
|
+
formatWarning({ message: `${DEPRECATED_GATE_KEY_WARNING} Ops: ${opsOnOldGateKey.join(", ")}.` }),
|
|
654
|
+
);
|
|
655
|
+
}
|
|
538
656
|
if (
|
|
539
657
|
result.sourceFileCount > 0 &&
|
|
540
658
|
result.outputs.size === 0 &&
|
|
659
|
+
declaredOps.length === 0 &&
|
|
541
660
|
result.errors.length === 0 &&
|
|
542
661
|
errors.length === 0
|
|
543
662
|
) {
|
|
@@ -658,12 +777,12 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
658
777
|
}
|
|
659
778
|
}
|
|
660
779
|
|
|
661
|
-
//
|
|
662
|
-
//
|
|
663
|
-
//
|
|
664
|
-
// which routes the primary resource manifest. Without this, a
|
|
665
|
-
// only printed them to stderr and `--output foo.yaml`
|
|
666
|
-
//
|
|
780
|
+
// Anything a serializer emits under `ops/` always goes to
|
|
781
|
+
// `<project>/dist/ops/` — the fixed place an Op's build output is read
|
|
782
|
+
// from, alongside the `op.json` core writes below — independent of
|
|
783
|
+
// `--output`, which routes the primary resource manifest. Without this, a
|
|
784
|
+
// bare `chant build` only printed them to stderr and `--output foo.yaml`
|
|
785
|
+
// scattered them next to `foo.yaml`.
|
|
667
786
|
const projectDist = resolve(options.path ?? ".", "dist");
|
|
668
787
|
let opsWritten = 0;
|
|
669
788
|
for (const [filename, entry] of [...additionalFiles]) {
|
|
@@ -677,13 +796,40 @@ export async function buildCommand(options: BuildOptions): Promise<BuildResult>
|
|
|
677
796
|
} catch (err) {
|
|
678
797
|
errors.push(
|
|
679
798
|
formatError({
|
|
680
|
-
message: `Failed to write Op
|
|
799
|
+
message: `Failed to write Op output file ${filename}: ${err instanceof Error ? err.message : String(err)}`,
|
|
681
800
|
}),
|
|
682
801
|
);
|
|
683
802
|
}
|
|
684
803
|
}
|
|
685
804
|
if (opsWritten > 0) {
|
|
686
|
-
console.error(formatInfo(`Wrote ${opsWritten} Op
|
|
805
|
+
console.error(formatInfo(`Wrote ${opsWritten} Op output file(s) under ${join(options.path ?? ".", "dist", "ops")}/`));
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// `dist/ops/<name>/op.json` — the Op IR (#1289), written here by core
|
|
809
|
+
// rather than emitted by a lexicon serializer (#2118). An Op declaration
|
|
810
|
+
// is core's own entity now (`Chant::Op`, lexicon `chant`) and is held out
|
|
811
|
+
// of `partitionByLexicon` entirely, so this is the one place its build
|
|
812
|
+
// output is produced: a deterministic, engine-neutral restatement of the
|
|
813
|
+
// step graph that anything can read without importing chant.
|
|
814
|
+
let irWritten = 0;
|
|
815
|
+
for (const entity of declaredOps) {
|
|
816
|
+
const config = (entity as unknown as { props?: OpConfig }).props;
|
|
817
|
+
if (!config?.name) continue;
|
|
818
|
+
try {
|
|
819
|
+
const targetPath = join(projectDist, "ops", config.name, "op.json");
|
|
820
|
+
mkdirSync(dirname(targetPath), { recursive: true });
|
|
821
|
+
writeFileSync(targetPath, serializeOpIR(config));
|
|
822
|
+
irWritten += 1;
|
|
823
|
+
} catch (err) {
|
|
824
|
+
errors.push(
|
|
825
|
+
formatError({
|
|
826
|
+
message: `Failed to write op.json for Op "${config.name}": ${err instanceof Error ? err.message : String(err)}`,
|
|
827
|
+
}),
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (irWritten > 0) {
|
|
832
|
+
console.error(formatInfo(`Wrote ${irWritten} op.json file(s) under ${join(options.path ?? ".", "dist", "ops")}/`));
|
|
687
833
|
}
|
|
688
834
|
|
|
689
835
|
if (options.output) {
|