@intentius/chant 0.101.0 → 0.102.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/agents/checks.d.ts.map +1 -1
- package/dist/attrref.d.ts.map +1 -1
- package/dist/audit/discover.d.ts.map +1 -1
- package/dist/audit/nginx.d.ts.map +1 -1
- package/dist/audit/prior-art.d.ts +48 -0
- package/dist/audit/prior-art.d.ts.map +1 -1
- package/dist/behaviour-kinds.d.ts.map +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/cli/commands/carve-emit.d.ts.map +1 -1
- package/dist/cli/commands/check-lexicon.d.ts +16 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/import.d.ts +8 -0
- package/dist/cli/commands/import.d.ts.map +1 -1
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -1
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/onboard.d.ts +9 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +19 -5
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -1
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +9 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/lsp/server.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/server.d.ts.map +1 -1
- package/dist/cli/mcp/tools/build.d.ts.map +1 -1
- package/dist/cli/mcp/tools/composites.d.ts.map +1 -1
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -1
- package/dist/cli/mcp/tools/import.d.ts.map +1 -1
- package/dist/cli/mcp/tools/lint.d.ts.map +1 -1
- package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -1
- package/dist/cli/mcp/tools/search.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +11 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/naming.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.d.ts +67 -3
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/codegen/registry.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/starter-plugin.d.ts.map +1 -1
- package/dist/components/verbs/process-runner.d.ts.map +1 -1
- package/dist/components/verbs/r2-sync.d.ts.map +1 -1
- package/dist/components/verbs/run-agent.d.ts.map +1 -1
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -1
- package/dist/components/verbs/sign.d.ts.map +1 -1
- package/dist/components/verbs/verify.d.ts.map +1 -1
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/components/verbs/wrangler.d.ts.map +1 -1
- package/dist/composite.d.ts.map +1 -1
- package/dist/config.d.ts +19 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/declarable.d.ts +16 -0
- package/dist/declarable.d.ts.map +1 -1
- package/dist/discovery/cache.d.ts.map +1 -1
- package/dist/discovery/fold-import.d.ts +15 -5
- package/dist/discovery/fold-import.d.ts.map +1 -1
- package/dist/discovery/param-deps.d.ts +16 -0
- package/dist/discovery/param-deps.d.ts.map +1 -1
- package/dist/discovery/sandbox/policy-wire.d.ts.map +1 -1
- package/dist/discovery/specifier-resolve.d.ts +37 -0
- package/dist/discovery/specifier-resolve.d.ts.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/fold/fold.d.ts +3 -1
- package/dist/fold/fold.d.ts.map +1 -1
- package/dist/graph-detail.d.ts.map +1 -1
- package/dist/graph-ir.d.ts.map +1 -1
- package/dist/graph-layout.d.ts.map +1 -1
- package/dist/import/base-parser.d.ts.map +1 -1
- package/dist/import/embedded.d.ts +7 -0
- package/dist/import/embedded.d.ts.map +1 -1
- package/dist/import/parser-options.d.ts +14 -0
- package/dist/import/parser-options.d.ts.map +1 -0
- package/dist/import/parser.d.ts +17 -0
- package/dist/import/parser.d.ts.map +1 -1
- package/dist/kubectl-context.d.ts.map +1 -1
- package/dist/lexicon-output.d.ts.map +1 -1
- package/dist/lexicon-schema.d.ts +3 -3
- package/dist/lexicon.d.ts +55 -3
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/assert-live.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +15 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/member-ledger.d.ts.map +1 -1
- package/dist/lifecycle/observe.d.ts.map +1 -1
- package/dist/lifecycle/plan-ledger.d.ts.map +1 -1
- package/dist/lifecycle/release-ledger.d.ts.map +1 -1
- package/dist/lifecycle/scenario.d.ts.map +1 -1
- package/dist/lifecycle/status.d.ts.map +1 -1
- package/dist/lifecycle/work-lease.d.ts +61 -2
- package/dist/lifecycle/work-lease.d.ts.map +1 -1
- package/dist/lint/config.d.ts +8 -8
- package/dist/lint/engine.d.ts +6 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +11 -11
- package/dist/op/activities/apply.d.ts +79 -9
- package/dist/op/activities/apply.d.ts.map +1 -1
- package/dist/op/activities/decide.d.ts.map +1 -1
- package/dist/op/activities/index.d.ts +1 -1
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/lexicon-upgrade.d.ts +44 -12
- package/dist/op/activities/lexicon-upgrade.d.ts.map +1 -1
- package/dist/op/activity-contract.d.ts +1 -1
- package/dist/op/activity-failure.d.ts.map +1 -1
- package/dist/op/activity-profiles.d.ts.map +1 -1
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/apply-op.d.ts +12 -5
- package/dist/op/composites/apply-op.d.ts.map +1 -1
- package/dist/op/composites/lexicon-upgrade-op.d.ts +22 -6
- package/dist/op/composites/lexicon-upgrade-op.d.ts.map +1 -1
- package/dist/op/decide-backend.d.ts.map +1 -1
- package/dist/op/decide-read-inputs.d.ts.map +1 -1
- package/dist/op/emulator-lifecycle.d.ts +21 -2
- package/dist/op/emulator-lifecycle.d.ts.map +1 -1
- package/dist/op/gate-approval.d.ts +5 -1
- package/dist/op/gate-approval.d.ts.map +1 -1
- package/dist/op/gate-wait.d.ts.map +1 -1
- package/dist/op/gate.d.ts +19 -3
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -0
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +6 -0
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/run-context.d.ts +39 -0
- package/dist/op/run-context.d.ts.map +1 -0
- package/dist/op/run-live.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/source-archive.d.ts.map +1 -1
- package/dist/op/steward-beside.d.ts.map +1 -1
- package/dist/op/steward-points.d.ts.map +1 -1
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/types.d.ts +8 -3
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-run.d.ts +24 -4
- package/dist/op/work-lease-run.d.ts.map +1 -1
- package/dist/project-root.d.ts.map +1 -1
- package/dist/provenance.d.ts +21 -0
- package/dist/provenance.d.ts.map +1 -1
- package/dist/pseudo-parameter.d.ts.map +1 -1
- package/dist/reconcile.d.ts.map +1 -1
- package/dist/secret-materialization.d.ts.map +1 -1
- package/dist/terraform/emit-source.d.ts.map +1 -1
- package/dist/terraform/parse.d.ts.map +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/contract-repo.d.ts.map +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts +1 -1
- package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -1
- package/dist/workspace/agent-cli.d.ts +5 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -1
- package/dist/workspace/box-factory.d.ts +73 -0
- package/dist/workspace/box-factory.d.ts.map +1 -0
- package/dist/workspace/changes.d.ts.map +1 -1
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -1
- package/dist/workspace/checks/boxes.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +5 -0
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +10 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts +67 -6
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/vitest.d.ts +12 -3
- package/dist/workspace/conformance/vitest.d.ts.map +1 -1
- package/dist/workspace/conformance/writer.d.ts +368 -0
- package/dist/workspace/conformance/writer.d.ts.map +1 -0
- package/dist/workspace/decide.d.ts +1 -1
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/declaration.d.ts +91 -4
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +234 -5
- package/dist/workspace/generated-files.d.ts.map +1 -1
- package/dist/workspace/graph-cache.d.ts.map +1 -1
- package/dist/workspace/graph-cli.d.ts +2 -0
- package/dist/workspace/graph-cli.d.ts.map +1 -1
- package/dist/workspace/identity.d.ts +111 -0
- package/dist/workspace/identity.d.ts.map +1 -0
- package/dist/workspace/intent-cli.d.ts +7 -4
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-record.d.ts +8 -1
- package/dist/workspace/intent-record.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +149 -5
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/kinds.d.ts +18 -4
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +2 -2
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/points.d.ts.map +1 -1
- package/dist/workspace/principal-classes.d.ts +114 -0
- package/dist/workspace/principal-classes.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +21 -1
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-assets.d.ts.map +1 -1
- package/dist/workspace/records-checkout.d.ts +54 -0
- package/dist/workspace/records-checkout.d.ts.map +1 -0
- package/dist/workspace/records-cli.d.ts +27 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +1 -1
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-since.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +5 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +15 -0
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runs-cli.d.ts +164 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -0
- package/dist/workspace/runs.d.ts +423 -0
- package/dist/workspace/runs.d.ts.map +1 -0
- package/dist/workspace/source-block.d.ts +1 -1
- package/dist/workspace/source-block.d.ts.map +1 -1
- package/dist/workspace/status-gates.d.ts +15 -3
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +10 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/symbols.d.ts +52 -0
- package/dist/workspace/symbols.d.ts.map +1 -0
- package/dist/workspace/trailer-joins.d.ts +46 -0
- package/dist/workspace/trailer-joins.d.ts.map +1 -0
- package/dist/workspace/trailers.d.ts +89 -0
- package/dist/workspace/trailers.d.ts.map +1 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts.map +1 -1
- package/dist/workspace/trust/seal.d.ts +33 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +39 -3
- package/dist/workspace/work-cli.d.ts.map +1 -1
- package/dist/workspace/work-evidence.d.ts +1 -1
- package/dist/workspace/work-evidence.d.ts.map +1 -1
- package/dist/workspace/work.d.ts +26 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/dist/workspace/workspace-principals.schema.json +60 -0
- package/dist/workspace/write-scope.d.ts +79 -19
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/yaml.d.ts +21 -39
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/audit/prior-art.ts +15 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
- package/src/cli/commands/carve-emit-state.test.ts +4 -0
- package/src/cli/commands/carve-emit.ts +5 -19
- package/src/cli/commands/check-lexicon.test.ts +62 -1
- package/src/cli/commands/check-lexicon.ts +36 -6
- package/src/cli/commands/import-live.test.ts +18 -0
- package/src/cli/commands/import-parser-options.test.ts +133 -0
- package/src/cli/commands/import.ts +21 -3
- package/src/cli/commands/init-lexicon/templates/project.ts +1 -1
- package/src/cli/commands/init.test.ts +8 -1
- package/src/cli/commands/init.ts +23 -8
- package/src/cli/commands/lint.test.ts +37 -0
- package/src/cli/commands/onboard.test.ts +53 -3
- package/src/cli/commands/onboard.ts +52 -4
- package/src/cli/commands/pinned-upgrade.ts +73 -33
- package/src/cli/handlers/__snapshots__/build-member.test.ts.snap +2 -2
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/dev.ts +8 -6
- package/src/cli/handlers/lifecycle.test.ts +67 -0
- package/src/cli/handlers/lifecycle.ts +6 -5
- package/src/cli/handlers/misc.ts +2 -0
- package/src/cli/handlers/operator.ts +87 -3
- package/src/cli/handlers/run.test.ts +111 -0
- package/src/cli/handlers/run.ts +85 -24
- package/src/cli/handlers/scenario.ts +2 -2
- package/src/cli/main.test.ts +1 -0
- package/src/cli/main.ts +65 -11
- package/src/cli/mcp/workspace-tools.test.ts +5 -2
- package/src/cli/mcp/workspace-tools.ts +29 -2
- package/src/cli/registry.ts +11 -2
- package/src/codegen/pinned-upgrade-multi.test.ts +157 -0
- package/src/codegen/pinned-upgrade-tags.test.ts +112 -0
- package/src/codegen/pinned-upgrade.test.ts +104 -0
- package/src/codegen/pinned-upgrade.ts +250 -56
- package/src/codegen/testdata/postgres-tags.json +242 -0
- package/src/config.test.ts +21 -0
- package/src/config.ts +15 -0
- package/src/declarable.test.ts +21 -0
- package/src/declarable.ts +19 -0
- package/src/discovery/fold-composite.test.ts +41 -5
- package/src/discovery/fold-import.test.ts +129 -0
- package/src/discovery/fold-import.ts +643 -131
- package/src/discovery/fold-package-composite.test.ts +264 -0
- package/src/discovery/fold-specifiers.test.ts +191 -0
- package/src/discovery/param-deps.test.ts +66 -1
- package/src/discovery/param-deps.ts +45 -0
- package/src/discovery/specifier-resolve.ts +344 -0
- package/src/fold/fold.test.ts +39 -1
- package/src/fold/fold.ts +83 -9
- package/src/fold-provenance.test.ts +140 -0
- package/src/graph-ir.test.ts +83 -0
- package/src/graph-ir.ts +29 -0
- package/src/import/embedded.test.ts +20 -0
- package/src/import/embedded.ts +13 -0
- package/src/import/parser-options.ts +46 -0
- package/src/import/parser.ts +20 -0
- package/src/lexicon.ts +55 -3
- package/src/lifecycle/gate-ledger.ts +16 -0
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +6 -4
- package/src/lifecycle/snapshot.ts +2 -2
- package/src/lifecycle/work-lease.test.ts +1 -1
- package/src/lifecycle/work-lease.ts +93 -2
- package/src/lint/engine.test.ts +55 -0
- package/src/lint/engine.ts +30 -4
- package/src/lint/output-docs.test.ts +59 -56
- package/src/op/activities/apply.test.ts +189 -5
- package/src/op/activities/apply.ts +230 -8
- package/src/op/activities/index.ts +3 -0
- package/src/op/activities/lexicon-upgrade.test.ts +160 -6
- package/src/op/activities/lexicon-upgrade.ts +154 -51
- package/src/op/composites/apply-op.ts +12 -5
- package/src/op/composites/composites.test.ts +30 -3
- package/src/op/composites/lexicon-upgrade-op.test.ts +7 -2
- package/src/op/composites/lexicon-upgrade-op.ts +29 -8
- package/src/op/emulator-lifecycle.test.ts +23 -0
- package/src/op/emulator-lifecycle.ts +52 -14
- package/src/op/gate-approval.test.ts +74 -0
- package/src/op/gate-approval.ts +5 -1
- package/src/op/gate.ts +31 -5
- package/src/op/index.ts +2 -0
- package/src/op/local-executor.test.ts +15 -0
- package/src/op/local-executor.ts +40 -12
- package/src/op/local-output.ts +1 -1
- package/src/op/operator.ts +4 -0
- package/src/op/run-context.test.ts +141 -0
- package/src/op/run-context.ts +51 -0
- package/src/op/runtimes/local.test.ts +14 -0
- package/src/op/runtimes/local.ts +2 -0
- package/src/op/steward-beside.ts +2 -0
- package/src/op/types.ts +8 -3
- package/src/op/work-lease-run.test.ts +47 -0
- package/src/op/work-lease-run.ts +73 -11
- package/src/provenance.ts +35 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
- package/src/workspace/agent-cli.ts +7 -3
- package/src/workspace/agent.schema.json +22 -0
- package/src/workspace/box-factory.test.ts +320 -0
- package/src/workspace/box-factory.ts +103 -0
- package/src/workspace/changes.schema.json +12 -20
- package/src/workspace/changes.ts +4 -1
- package/src/workspace/checks/boxes.test.ts +2 -0
- package/src/workspace/checks.ts +29 -5
- package/src/workspace/compose-graph.ts +7 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +27 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +95 -0
- package/src/workspace/conformance/__writer_fixture__/decisions/points.json +132 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +48 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +108 -0
- package/src/workspace/conformance/__writer_fixture__/work/W-001-the-writer-suite-works-this-item.md +25 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.kind.mjs +60 -0
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +578 -0
- package/src/workspace/conformance/conformance.e2e.test.ts +35 -3
- package/src/workspace/conformance/index.mjs +30 -3
- package/src/workspace/conformance/index.ts +103 -15
- package/src/workspace/conformance/vitest.ts +44 -2
- package/src/workspace/conformance/writer-conformance.e2e.test.ts +131 -0
- package/src/workspace/conformance/writer.test.ts +156 -0
- package/src/workspace/conformance/writer.ts +880 -0
- package/src/workspace/decide.ts +7 -1
- package/src/workspace/declaration.schema.json +234 -5
- package/src/workspace/declaration.ts +174 -10
- package/src/workspace/graph-cli.ts +5 -2
- package/src/workspace/graph.schema.json +247 -0
- package/src/workspace/identity.test.ts +196 -0
- package/src/workspace/identity.ts +186 -0
- package/src/workspace/intent-cli.ts +64 -10
- package/src/workspace/intent-record.schema.json +208 -0
- package/src/workspace/intent-record.ts +35 -1
- package/src/workspace/intent-why.test.ts +274 -0
- package/src/workspace/intent.schema.json +718 -7
- package/src/workspace/intent.test.ts +11 -2
- package/src/workspace/intent.ts +495 -32
- package/src/workspace/kinds.ts +44 -37
- package/src/workspace/points-write.schema.json +1 -0
- package/src/workspace/principal-classes.test.ts +175 -0
- package/src/workspace/principal-classes.ts +307 -0
- package/src/workspace/reason-codes.test.ts +11 -3
- package/src/workspace/reason-codes.ts +24 -1
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-checkout.ts +106 -0
- package/src/workspace/records-cli.ts +103 -11
- package/src/workspace/records-close.schema.json +1 -0
- package/src/workspace/records-new.schema.json +2 -0
- package/src/workspace/records-review.schema.json +2 -0
- package/src/workspace/records-uncommitted.test.ts +171 -0
- package/src/workspace/records-write.ts +28 -6
- package/src/workspace/records.schema.json +72 -1
- package/src/workspace/records.ts +27 -0
- package/src/workspace/runs-cli.ts +381 -0
- package/src/workspace/runs-write.schema.json +583 -0
- package/src/workspace/runs.schema.json +810 -0
- package/src/workspace/runs.test.ts +242 -0
- package/src/workspace/runs.ts +709 -0
- package/src/workspace/status-contract.test.ts +5 -0
- package/src/workspace/status-gates.ts +22 -8
- package/src/workspace/status.schema.json +127 -2
- package/src/workspace/status.ts +21 -1
- package/src/workspace/symbols.ts +180 -0
- package/src/workspace/trailer-joins.test.ts +171 -0
- package/src/workspace/trailer-joins.ts +82 -0
- package/src/workspace/trailers.ts +148 -0
- package/src/workspace/trust/seal.ts +52 -1
- package/src/workspace/work-cli.e2e.test.ts +28 -1
- package/src/workspace/work-cli.ts +133 -8
- package/src/workspace/work-evidence.schema.json +2 -0
- package/src/workspace/work-history.schema.json +73 -1
- package/src/workspace/work-history.test.ts +48 -5
- package/src/workspace/work-kind-fields.test.ts +154 -0
- package/src/workspace/work-lease.schema.json +14 -1
- package/src/workspace/work.ts +82 -0
- package/src/workspace/workspace-principals.schema.json +60 -0
- package/src/workspace/write-scope.test.ts +128 -2
- package/src/workspace/write-scope.ts +170 -45
- package/src/yaml.test.ts +111 -1
- package/src/yaml.ts +141 -549
|
@@ -508,3 +508,143 @@ describe("fold provenance over a real composite build (#2161)", () => {
|
|
|
508
508
|
expect(verdicts[3].resolution.kind).toBe("edit-declaration");
|
|
509
509
|
});
|
|
510
510
|
});
|
|
511
|
+
|
|
512
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
513
|
+
// chant #3212 — a registered tag inside a composite factory.
|
|
514
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
515
|
+
|
|
516
|
+
const runtimePath = resolve(thisDir, "./runtime");
|
|
517
|
+
const provenancePath = resolve(thisDir, "./provenance");
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* A SQL-shaped tag, the shape the sql lexicon's `table` takes: it builds one
|
|
521
|
+
* entity out of its text and reports, per interpolation, which of the entity's
|
|
522
|
+
* fields that interpolation fed. An interpolated entity is a reference and
|
|
523
|
+
* feeds nothing the author typed, so it reports no field.
|
|
524
|
+
*/
|
|
525
|
+
const TAGS = `
|
|
526
|
+
import { createResource } from ${JSON.stringify(runtimePath)};
|
|
527
|
+
import { setInterpolationFields } from ${JSON.stringify(provenancePath)};
|
|
528
|
+
const Table = createResource("Test::Table", ${JSON.stringify(LEXICON_NAME)}, {});
|
|
529
|
+
const CLAUSE = { TABLE: "name", "ENGINE =": "engine", "ORDER BY": "orderBy", TTL: "ttl", COMMENT: "comment" };
|
|
530
|
+
|
|
531
|
+
export function table(strings, ...values) {
|
|
532
|
+
const isEntity = (v) => typeof v === "object" && v !== null && "props" in v;
|
|
533
|
+
let ddl = strings[0];
|
|
534
|
+
const fields = values.map((v, i) => {
|
|
535
|
+
ddl += (isEntity(v) ? v.props.name : String(v)) + strings[i + 1];
|
|
536
|
+
if (isEntity(v)) return [];
|
|
537
|
+
const at = (k) => strings[i].lastIndexOf(k);
|
|
538
|
+
const clause = Object.keys(CLAUSE).sort((a, b) => at(b) - at(a))[0];
|
|
539
|
+
return at(clause) >= 0 ? [CLAUSE[clause], "ddl"] : ["ddl"];
|
|
540
|
+
});
|
|
541
|
+
const m = /CREATE TABLE (\\w+) ENGINE = (\\w+) ORDER BY (\\w+)(?: TTL (.+?))?(?: COMMENT (\\w+))?$/.exec(ddl);
|
|
542
|
+
if (!m) throw new Error("cannot parse: " + ddl);
|
|
543
|
+
const props = { name: m[1], engine: m[2], orderBy: m[3], ddl };
|
|
544
|
+
if (m[4]) props.ttl = m[4];
|
|
545
|
+
if (m[5]) props.comment = m[5];
|
|
546
|
+
const entity = new Table(props);
|
|
547
|
+
setInterpolationFields(entity, fields);
|
|
548
|
+
return entity;
|
|
549
|
+
}
|
|
550
|
+
`;
|
|
551
|
+
|
|
552
|
+
const TAG_COMPOSITES = `
|
|
553
|
+
import { table } from "./tags";
|
|
554
|
+
import { Composite } from ${JSON.stringify(compositePath)};
|
|
555
|
+
|
|
556
|
+
export const EventsTable = Composite((props) => {
|
|
557
|
+
const rollupName = \`\${props.name}_daily\`;
|
|
558
|
+
const events = table\`CREATE TABLE \${props.name} ENGINE = MergeTree ORDER BY ts TTL ts + INTERVAL \${props.ttlDays} DAY\`;
|
|
559
|
+
const rollup = table\`CREATE TABLE \${rollupName} ENGINE = \${"SummingMergeTree"} ORDER BY ts COMMENT \${events}\`;
|
|
560
|
+
return { events, rollup };
|
|
561
|
+
}, "EventsTable");
|
|
562
|
+
`;
|
|
563
|
+
|
|
564
|
+
const TAG_MAIN = `
|
|
565
|
+
import { table } from "../tags";
|
|
566
|
+
import { EventsTable } from "../composites";
|
|
567
|
+
|
|
568
|
+
export const audit = table\`CREATE TABLE audit ENGINE = MergeTree ORDER BY ts\`;
|
|
569
|
+
export const clicks = EventsTable({ name: "clicks", ttlDays: 30 });
|
|
570
|
+
`;
|
|
571
|
+
|
|
572
|
+
describe("fold provenance through a tagged template in a composite factory (#3212)", () => {
|
|
573
|
+
let testDir: string;
|
|
574
|
+
let srcDir: string;
|
|
575
|
+
|
|
576
|
+
beforeEach(async () => {
|
|
577
|
+
const dir = join(repoRoot, ".cache", `chant-3212-${process.pid}-${seq++}`);
|
|
578
|
+
await rm(dir, { recursive: true, force: true });
|
|
579
|
+
await mkdir(dir, { recursive: true });
|
|
580
|
+
testDir = await realpath(dir);
|
|
581
|
+
srcDir = join(testDir, "src");
|
|
582
|
+
await mkdir(srcDir, { recursive: true });
|
|
583
|
+
await writeFile(join(testDir, "tags.ts"), TAGS);
|
|
584
|
+
await writeFile(join(testDir, "composites.ts"), TAG_COMPOSITES);
|
|
585
|
+
await writeFile(join(srcDir, "main.ts"), TAG_MAIN);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
afterEach(async () => {
|
|
589
|
+
await rm(testDir, { recursive: true, force: true });
|
|
590
|
+
});
|
|
591
|
+
|
|
592
|
+
const buildIt = () =>
|
|
593
|
+
build(srcDir, [namesSerializer], undefined, {
|
|
594
|
+
fold: true,
|
|
595
|
+
lexicons: [LEXICON_NAME],
|
|
596
|
+
intrinsics: [{ name: "table", isTag: true }],
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
test("a field fed by ${props.x} is that parameter, a field the text fixes is a literal", async () => {
|
|
600
|
+
const result = await buildIt();
|
|
601
|
+
expect(result.errors).toEqual([]);
|
|
602
|
+
const events = result.foldProvenance.clicksEvents;
|
|
603
|
+
const rollup = result.foldProvenance.clicksRollup;
|
|
604
|
+
const at = (parameters: string[]) => ({ kind: "composite-parameter", composite: "EventsTable", instance: "clicks", parameters });
|
|
605
|
+
const fixed = { kind: "composite-literal", composite: "EventsTable", instance: "clicks" };
|
|
606
|
+
|
|
607
|
+
expect(events.fields.name).toEqual(at(["name"]));
|
|
608
|
+
expect(events.fields.ttl).toEqual(at(["ttlDays"]));
|
|
609
|
+
expect(events.fields.ddl).toEqual(at(["name", "ttlDays"]));
|
|
610
|
+
expect(events.fields.engine).toEqual(fixed);
|
|
611
|
+
expect(events.fields.orderBy).toEqual(fixed);
|
|
612
|
+
|
|
613
|
+
// Through a body `const`; an interpolated literal string is fixed; an
|
|
614
|
+
// interpolated sibling entity is wiring, so it governs nothing.
|
|
615
|
+
expect(rollup.fields.name).toEqual(at(["name"]));
|
|
616
|
+
expect(rollup.fields.engine).toEqual(fixed);
|
|
617
|
+
expect(rollup.fields.comment).toEqual(fixed);
|
|
618
|
+
expect(rollup.fields.ddl).toEqual(at(["name"]));
|
|
619
|
+
|
|
620
|
+
expect(result.foldProvenance.audit.fields.ttl).toBeUndefined();
|
|
621
|
+
expect(result.foldProvenance.audit.fields.name).toEqual({ kind: "direct" });
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
test("drift on a parameter-fed field proposes the parameter at the call site", async () => {
|
|
625
|
+
const result = await buildIt();
|
|
626
|
+
const provenanceOf = (name: string): EntityProvenance | undefined => {
|
|
627
|
+
const entity = result.entities.get(name);
|
|
628
|
+
return entity ? getProvenance(entity) : undefined;
|
|
629
|
+
};
|
|
630
|
+
const drift = (name: string, path: string, declared: unknown, live: unknown): DeepEntityDrift => {
|
|
631
|
+
const origin = originOfPath(provenanceOf(name)?.paths, path);
|
|
632
|
+
return { name, type: "Test::Table", changes: [{ path, kind: "changed", declared, live, ...(origin ? { origin } : {}) }] };
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
const [ttl, engine] = resolveDeepDrift(
|
|
636
|
+
[
|
|
637
|
+
drift("clicksEvents", "ttl", "ts + INTERVAL 30 DAY", "ts + INTERVAL 90 DAY"),
|
|
638
|
+
drift("clicksEvents", "engine", "MergeTree", "ReplacingMergeTree"),
|
|
639
|
+
],
|
|
640
|
+
provenanceOf,
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
expect(ttl.resolution.kind).toBe("propose-parameter");
|
|
644
|
+
if (ttl.resolution.kind !== "propose-parameter") throw new Error("unreachable");
|
|
645
|
+
expect(ttl.resolution.parameters).toEqual(["ttlDays"]);
|
|
646
|
+
expect(ttl.resolution.instance).toBe("clicks");
|
|
647
|
+
expect(ttl.resolution.sourceFile).toContain("main.ts");
|
|
648
|
+
expect(engine.resolution.kind).toBe("refuse-fixed");
|
|
649
|
+
});
|
|
650
|
+
});
|
package/src/graph-ir.test.ts
CHANGED
|
@@ -287,3 +287,86 @@ describe("entity-declared references and stacks", () => {
|
|
|
287
287
|
expect(ir.nodes.find((n) => n.id === "app/vpc")!.attrs).not.toHaveProperty("stack");
|
|
288
288
|
});
|
|
289
289
|
});
|
|
290
|
+
|
|
291
|
+
describe("whole declarables held in an entity's fields (#3257)", () => {
|
|
292
|
+
/** An entity shaped like the sql lexicon's: props non-enumerable, `dependsOn` enumerable. */
|
|
293
|
+
function holder(dependsOn: unknown[], extra: object = {}) {
|
|
294
|
+
const e = decl({ lexicon: "sql", entityType: "ClickHouse::Table", ...extra });
|
|
295
|
+
Object.defineProperty(e, "dependsOn", { value: dependsOn, enumerable: true });
|
|
296
|
+
return e;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
test("draws an edge to each node named directly in an enumerable array", () => {
|
|
300
|
+
const db = decl({ lexicon: "sql", entityType: "ClickHouse::Database" });
|
|
301
|
+
const target = holder([db]);
|
|
302
|
+
const events = holder([db]);
|
|
303
|
+
const mv = holder([db, target, events]);
|
|
304
|
+
const entities = new Map<string, Declarable>([
|
|
305
|
+
["analytics", db],
|
|
306
|
+
["dailyActive", target],
|
|
307
|
+
["events", events],
|
|
308
|
+
["dailyActiveMv", mv],
|
|
309
|
+
]);
|
|
310
|
+
resolveAttrRefs(entities);
|
|
311
|
+
|
|
312
|
+
const ir = buildGraphIr(entities);
|
|
313
|
+
const dep = (from: string, to: string) => ({ from, to, kind: "ref", viaAttr: "dependsOn" });
|
|
314
|
+
expect(ir.edges).toEqual([
|
|
315
|
+
dep("dailyActive", "analytics"),
|
|
316
|
+
dep("dailyActiveMv", "analytics"),
|
|
317
|
+
dep("dailyActiveMv", "dailyActive"),
|
|
318
|
+
dep("dailyActiveMv", "events"),
|
|
319
|
+
dep("events", "analytics"),
|
|
320
|
+
]);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("does not walk into the referenced node, and projects it as a $ref", () => {
|
|
324
|
+
const db = decl({ lexicon: "sql", entityType: "ClickHouse::Database", props: { name: "analytics" } });
|
|
325
|
+
const events = holder([db]);
|
|
326
|
+
const entities = new Map<string, Declarable>([
|
|
327
|
+
["analytics", db],
|
|
328
|
+
["events", events],
|
|
329
|
+
]);
|
|
330
|
+
resolveAttrRefs(entities);
|
|
331
|
+
|
|
332
|
+
const ir = buildGraphIr(entities);
|
|
333
|
+
expect(ir.nodes.find((n) => n.id === "events")!.attrs.dependsOn).toEqual([{ $ref: "analytics" }]);
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
test("an AttrRef and the whole node to the same target collapse to one edge", () => {
|
|
337
|
+
const db = decl({ lexicon: "sql", entityType: "ClickHouse::Database" });
|
|
338
|
+
const events = decl({ lexicon: "sql", entityType: "ClickHouse::Table" });
|
|
339
|
+
const mv = holder([events, new AttrRef(events, "user_id")]);
|
|
340
|
+
const entities = new Map<string, Declarable>([
|
|
341
|
+
["analytics", db],
|
|
342
|
+
["events", events],
|
|
343
|
+
["mv", mv],
|
|
344
|
+
]);
|
|
345
|
+
resolveAttrRefs(entities);
|
|
346
|
+
|
|
347
|
+
const ir = buildGraphIr(entities);
|
|
348
|
+
expect(ir.edges).toEqual([{ from: "mv", to: "events", kind: "ref", viaAttr: "dependsOn" }]);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test("ignores a self reference, and still descends into an inlined property-kind resource", () => {
|
|
352
|
+
const self: Declarable = holder([]);
|
|
353
|
+
(self as unknown as { dependsOn: unknown[] }).dependsOn.push(self);
|
|
354
|
+
const vpc = decl({ lexicon: "aws", entityType: "Vpc" });
|
|
355
|
+
const inline = decl({
|
|
356
|
+
lexicon: "aws",
|
|
357
|
+
entityType: "Listener_Action",
|
|
358
|
+
kind: "property",
|
|
359
|
+
props: { vpc: new AttrRef(vpc, "Id") },
|
|
360
|
+
});
|
|
361
|
+
const lb = decl({ lexicon: "aws", entityType: "Lb", props: { actions: [inline] } });
|
|
362
|
+
const entities = new Map<string, Declarable>([
|
|
363
|
+
["self", self],
|
|
364
|
+
["vpc", vpc],
|
|
365
|
+
["lb", lb],
|
|
366
|
+
]);
|
|
367
|
+
resolveAttrRefs(entities);
|
|
368
|
+
|
|
369
|
+
const ir = buildGraphIr(entities);
|
|
370
|
+
expect(ir.edges).toEqual([{ from: "lb", to: "vpc", kind: "ref", viaAttr: "actions" }]);
|
|
371
|
+
});
|
|
372
|
+
});
|
package/src/graph-ir.ts
CHANGED
|
@@ -384,6 +384,23 @@ function refTarget(ref: AttrRef, reverse: Map<object, string>): string | undefin
|
|
|
384
384
|
return ref.getLogicalName() ?? (parent ? reverse.get(parent) : undefined);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
|
+
/**
|
|
388
|
+
* The node id a whole declarable held in a config bag stands for, or undefined
|
|
389
|
+
* when it is not a node of this graph (an inlined property-kind resource, say,
|
|
390
|
+
* which is config of the holder and gets walked instead). An entity can name
|
|
391
|
+
* another entity directly rather than through an AttrRef, as the sql lexicon's
|
|
392
|
+
* enumerable `dependsOn` does with the tables, views and databases a statement
|
|
393
|
+
* mentions (#3257); that is a reference to the node, not a nested bag.
|
|
394
|
+
*/
|
|
395
|
+
function nodeReference(
|
|
396
|
+
value: unknown,
|
|
397
|
+
reverse: Map<object, string>,
|
|
398
|
+
nodeIds: Set<string>,
|
|
399
|
+
): string | undefined {
|
|
400
|
+
const id = reverse.get(value as object);
|
|
401
|
+
return id !== undefined && nodeIds.has(id) ? id : undefined;
|
|
402
|
+
}
|
|
403
|
+
|
|
387
404
|
/** Project a value into a JSON-safe, scrubbed form. References become `{ $ref }`. */
|
|
388
405
|
function project(value: unknown, seen: Set<unknown>, reverse: Map<object, string>, nodeIds: Set<string>): unknown {
|
|
389
406
|
if (value === null) return null;
|
|
@@ -414,6 +431,9 @@ function project(value: unknown, seen: Set<unknown>, reverse: Map<object, string
|
|
|
414
431
|
// Nested declarables (e.g. an inlined property-kind resource) keep their
|
|
415
432
|
// config in a non-enumerable `props` bag, like top-level nodes — project that.
|
|
416
433
|
if (isDeclarable(value)) {
|
|
434
|
+
// Another node held whole is a reference to it, not config to inline.
|
|
435
|
+
const to = nodeReference(value, reverse, nodeIds);
|
|
436
|
+
if (to) return { $ref: to } satisfies AttrRefEnvelope;
|
|
417
437
|
const out = projectConfig(value, seen, reverse, nodeIds);
|
|
418
438
|
seen.delete(value);
|
|
419
439
|
return out;
|
|
@@ -518,6 +538,15 @@ function collectEdges(
|
|
|
518
538
|
if (to && to !== from && nodeIds.has(to)) edges.push({ from, to, kind: "ref", viaAttr });
|
|
519
539
|
return;
|
|
520
540
|
}
|
|
541
|
+
// A whole node held in props or an enumerable field (the sql lexicon's
|
|
542
|
+
// `dependsOn`) is a reference to it: draw the edge, and do not walk its
|
|
543
|
+
// config, whose refs belong to that node's own edges (#3257). Checked
|
|
544
|
+
// before `seen` so the same node under two attributes yields both edges.
|
|
545
|
+
const held = nodeReference(value, reverse, nodeIds);
|
|
546
|
+
if (held) {
|
|
547
|
+
if (held !== from) edges.push({ from, to: held, kind: "ref", viaAttr });
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
521
550
|
if (seen.has(value)) return;
|
|
522
551
|
seen.add(value);
|
|
523
552
|
// A nested declarable (an inlined property-kind resource, e.g. a listener's
|
|
@@ -85,6 +85,26 @@ describe("EmbeddedImports", () => {
|
|
|
85
85
|
]);
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
+
test("an importer that keeps matched content as written is not asked to import it, and its reason is the warning", () => {
|
|
89
|
+
let imported = 0;
|
|
90
|
+
const resolver = new EmbeddedImports([
|
|
91
|
+
{
|
|
92
|
+
lexicon: "acme",
|
|
93
|
+
importer: {
|
|
94
|
+
...widgets,
|
|
95
|
+
keepsAsWritten: (c) => ((c.document as { size?: number }).size === 3 ? "a size-3 widget builds back as size 4" : undefined),
|
|
96
|
+
import: (c) => (imported++, widgets.import(c)),
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
]);
|
|
100
|
+
expect(resolver.resolve(site())).toBeUndefined();
|
|
101
|
+
expect(imported).toBe(0);
|
|
102
|
+
expect(resolver.files).toEqual([]);
|
|
103
|
+
expect(resolver.warnings).toEqual(['ConfigMap agent data["config.yaml"] is a widget, kept as written: a size-3 widget builds back as size 4']);
|
|
104
|
+
expect(isEmbeddedReference(resolver.resolve(site({ text: "kind: widget\nsize: 2\n" })))).toBe(true);
|
|
105
|
+
expect(imported).toBe(1);
|
|
106
|
+
});
|
|
107
|
+
|
|
88
108
|
test("when two lexicons match, the first imports it and the other is named", () => {
|
|
89
109
|
const resolver = new EmbeddedImports([
|
|
90
110
|
{ lexicon: "acme", importer: widgets },
|
package/src/import/embedded.ts
CHANGED
|
@@ -102,6 +102,13 @@ export interface EmbeddedContentImporter {
|
|
|
102
102
|
readonly what: string;
|
|
103
103
|
/** Whether this content is one it imports. Must not throw on content that is not. */
|
|
104
104
|
matches(content: EmbeddedContent): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Why content it matches is kept as written instead of imported, or
|
|
107
|
+
* `undefined` to import it. Asked before `import`; the reason is the
|
|
108
|
+
* warning's. grafana keeps a v2 dashboard, which it reads but would build
|
|
109
|
+
* back as v1 JSON (#3031).
|
|
110
|
+
*/
|
|
111
|
+
keepsAsWritten?(content: EmbeddedContent): string | undefined;
|
|
105
112
|
/** Import it. A throw keeps the content as written, with a warning. */
|
|
106
113
|
import(content: EmbeddedContent): EmbeddedImport;
|
|
107
114
|
}
|
|
@@ -233,6 +240,12 @@ export class EmbeddedImports implements EmbeddedContentResolver {
|
|
|
233
240
|
);
|
|
234
241
|
}
|
|
235
242
|
|
|
243
|
+
const kept = chosen.importer.keepsAsWritten?.(content);
|
|
244
|
+
if (kept !== undefined) {
|
|
245
|
+
this.warnings.push(`${content.location} is ${chosen.importer.what}, kept as written: ${kept}`);
|
|
246
|
+
return undefined;
|
|
247
|
+
}
|
|
248
|
+
|
|
236
249
|
let result: EmbeddedImport;
|
|
237
250
|
try {
|
|
238
251
|
result = chosen.importer.import(content);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { LexiconPlugin } from "../lexicon";
|
|
2
|
+
import type { ParserOptions, ParserOptionSpec } from "./parser";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Check `--parser-option` entries (`key` or `key=value`) against what the
|
|
6
|
+
* lexicon declares in `parserOptions()` and convert each value to its declared
|
|
7
|
+
* type (#2994). Returns the options, or an error message naming what is wrong
|
|
8
|
+
* and what the lexicon accepts. An empty list is always valid.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveParserOptions(
|
|
11
|
+
plugin: Pick<LexiconPlugin, "name" | "parserOptions">,
|
|
12
|
+
entries: readonly string[] | undefined,
|
|
13
|
+
): { options: ParserOptions } | { error: string } {
|
|
14
|
+
const options: ParserOptions = {};
|
|
15
|
+
if (!entries || entries.length === 0) return { options };
|
|
16
|
+
|
|
17
|
+
const specs: ParserOptionSpec[] = plugin.parserOptions?.() ?? [];
|
|
18
|
+
const accepted =
|
|
19
|
+
specs.length === 0
|
|
20
|
+
? `lexicon "${plugin.name}" declares no parser options`
|
|
21
|
+
: `${plugin.name} accepts: ${specs.map((s) => `${s.name} (${s.type}): ${s.description}`).join("; ")}`;
|
|
22
|
+
|
|
23
|
+
for (const entry of entries) {
|
|
24
|
+
const eq = entry.indexOf("=");
|
|
25
|
+
const name = eq === -1 ? entry : entry.slice(0, eq);
|
|
26
|
+
const raw = eq === -1 ? undefined : entry.slice(eq + 1);
|
|
27
|
+
const spec = specs.find((s) => s.name === name);
|
|
28
|
+
if (!spec) {
|
|
29
|
+
return { error: `Unknown parser option "${name}" for lexicon "${plugin.name}"; ${accepted}.` };
|
|
30
|
+
}
|
|
31
|
+
if (spec.type === "boolean") {
|
|
32
|
+
if (raw === undefined || raw === "true") options[name] = true;
|
|
33
|
+
else if (raw === "false") options[name] = false;
|
|
34
|
+
else return { error: `Parser option "${name}" is a boolean: use ${name}, ${name}=true or ${name}=false, got "${raw}".` };
|
|
35
|
+
} else if (raw === undefined) {
|
|
36
|
+
return { error: `Parser option "${name}" needs a value: --parser-option ${name}=<${spec.type}>.` };
|
|
37
|
+
} else if (spec.type === "number") {
|
|
38
|
+
const n = Number(raw);
|
|
39
|
+
if (raw.trim() === "" || !Number.isFinite(n)) return { error: `Parser option "${name}" is a number, got "${raw}".` };
|
|
40
|
+
options[name] = n;
|
|
41
|
+
} else {
|
|
42
|
+
options[name] = raw;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { options };
|
|
46
|
+
}
|
package/src/import/parser.ts
CHANGED
|
@@ -78,6 +78,26 @@ export interface ParseContext {
|
|
|
78
78
|
readonly embedded?: EmbeddedContentResolver;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
/** A value a parser option takes. */
|
|
82
|
+
export type ParserOptionValue = string | number | boolean;
|
|
83
|
+
|
|
84
|
+
/** The options `chant import --parser-option` hands a lexicon's parser, by name. */
|
|
85
|
+
export type ParserOptions = Record<string, ParserOptionValue>;
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* One option a lexicon's parser accepts, declared by `LexiconPlugin.parserOptions()`.
|
|
89
|
+
* `chant import --parser-option <name>[=<value>]` is checked against this list and
|
|
90
|
+
* the value is converted to `type` before `templateParser(options)` sees it.
|
|
91
|
+
*/
|
|
92
|
+
export interface ParserOptionSpec {
|
|
93
|
+
/** The key after `--parser-option`, e.g. "acceptLossyV1". */
|
|
94
|
+
name: string;
|
|
95
|
+
/** `boolean` takes `true`/`false`, and a bare `--parser-option name` means true. */
|
|
96
|
+
type: "boolean" | "string" | "number";
|
|
97
|
+
/** One line, shown when the option is refused and in the docs. */
|
|
98
|
+
description: string;
|
|
99
|
+
}
|
|
100
|
+
|
|
81
101
|
/**
|
|
82
102
|
* Interface for template parsers that convert external formats to IR
|
|
83
103
|
*/
|
package/src/lexicon.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Declarable } from "./declarable";
|
|
|
3
3
|
import type { LintRule } from "./lint/rule";
|
|
4
4
|
import type { RuleSpec } from "./lint/declarative";
|
|
5
5
|
import type { PostSynthCheck } from "./lint/post-synth";
|
|
6
|
-
import type { TemplateParser, TemplateIR, ParseContext } from "./import/parser";
|
|
6
|
+
import type { TemplateParser, TemplateIR, ParseContext, ParserOptions, ParserOptionSpec } from "./import/parser";
|
|
7
7
|
import type { TypeScriptGenerator } from "./import/generator";
|
|
8
8
|
import type { EmbeddedContentImporter } from "./import/embedded";
|
|
9
9
|
import type { AgentConfigImporter } from "./agents/importer";
|
|
@@ -426,12 +426,26 @@ export interface InitTemplateSet {
|
|
|
426
426
|
* repos or version-constant locations. See ./codegen/pinned-upgrade.ts.
|
|
427
427
|
*/
|
|
428
428
|
export interface UpstreamPin {
|
|
429
|
+
/**
|
|
430
|
+
* Short name for this pin (`clickhouse`, `postgres-17`), used to pick one
|
|
431
|
+
* pin of a plugin that declares several (`chant dev pinned-upgrade <dir>
|
|
432
|
+
* <label>`). Optional on a lone `upstreamPin`, where it defaults to the
|
|
433
|
+
* lexicon's name; required on every entry of `upstreamPins`.
|
|
434
|
+
*/
|
|
435
|
+
readonly label?: string;
|
|
429
436
|
/** Source file (relative to the lexicon package root) holding the pinned version constant. */
|
|
430
437
|
readonly file: string;
|
|
431
438
|
/** Regex whose first capture group is the current pinned version in `file`. */
|
|
432
439
|
readonly pattern: RegExp;
|
|
433
440
|
/** Rebuild a line that matched `pattern`, substituting the new version. */
|
|
434
441
|
replace(newVersion: string, line: string): string;
|
|
442
|
+
/**
|
|
443
|
+
* Set when the version line is not the whole pin: other constants (an image
|
|
444
|
+
* digest, a checksum) move with it and `replace` cannot compute them. The
|
|
445
|
+
* upgrade tooling then reports the newer version and this text, and edits
|
|
446
|
+
* nothing. Say what to move and how.
|
|
447
|
+
*/
|
|
448
|
+
readonly alsoMoves?: string;
|
|
435
449
|
/** Where to query for the latest stable upstream tag. */
|
|
436
450
|
readonly upstream: {
|
|
437
451
|
readonly owner: string;
|
|
@@ -440,6 +454,25 @@ export interface UpstreamPin {
|
|
|
440
454
|
readonly kind: "releases" | "tags";
|
|
441
455
|
/** Only consider tags ending with this suffix (e.g. "-ee" for GitLab). */
|
|
442
456
|
readonly tagSuffix?: string;
|
|
457
|
+
/**
|
|
458
|
+
* Map a raw tag to the version string the pin stores, or null to skip the
|
|
459
|
+
* tag. For upstreams whose tags are not versions: postgres/postgres tags
|
|
460
|
+
* are `REL_18_6`, `REL_18_BETA1`, `REL9_6_24`, so
|
|
461
|
+
* `(t) => /^REL_(\d+)_(\d+)$/.exec(t)?.slice(1).join(".") ?? null` gives
|
|
462
|
+
* `18.6` and drops betas, release candidates and the old spelling.
|
|
463
|
+
*
|
|
464
|
+
* When set, the tool reads every page of the tag list (not just the first
|
|
465
|
+
* 50), because such lists are not ordered by version, and the version this
|
|
466
|
+
* returns is what `replace` receives and what the report shows. The pinned
|
|
467
|
+
* constant may hold either the mapped version or the raw tag.
|
|
468
|
+
*/
|
|
469
|
+
readonly tagVersion?: (tag: string) => string | null;
|
|
470
|
+
/**
|
|
471
|
+
* Only consider versions whose first numeric segment equals the pinned
|
|
472
|
+
* version's, so a pin on `17.9` reports `17.11` and never `18.6`. For
|
|
473
|
+
* lexicons that pin one snapshot per major line. Default false.
|
|
474
|
+
*/
|
|
475
|
+
readonly trackMajor?: boolean;
|
|
443
476
|
};
|
|
444
477
|
}
|
|
445
478
|
|
|
@@ -1160,8 +1193,19 @@ export interface LexiconPlugin {
|
|
|
1160
1193
|
*/
|
|
1161
1194
|
detectTemplate?(data: unknown): boolean;
|
|
1162
1195
|
|
|
1163
|
-
/**
|
|
1164
|
-
|
|
1196
|
+
/**
|
|
1197
|
+
* Return a parser for importing external templates into IR. `options` holds
|
|
1198
|
+
* the `chant import --parser-option` values, already checked against
|
|
1199
|
+
* `parserOptions()` and converted to their declared types; it is empty when
|
|
1200
|
+
* none were given.
|
|
1201
|
+
*/
|
|
1202
|
+
templateParser?(options?: ParserOptions): TemplateParser;
|
|
1203
|
+
|
|
1204
|
+
/**
|
|
1205
|
+
* The options `templateParser` accepts. `chant import --parser-option` refuses
|
|
1206
|
+
* any name not listed here, so a lexicon with no options leaves this out.
|
|
1207
|
+
*/
|
|
1208
|
+
parserOptions?(): ParserOptionSpec[];
|
|
1165
1209
|
|
|
1166
1210
|
/** Return a generator for converting IR to TypeScript */
|
|
1167
1211
|
templateGenerator?(): TypeScriptGenerator;
|
|
@@ -1200,6 +1244,14 @@ export interface LexiconPlugin {
|
|
|
1200
1244
|
/** How this lexicon pins its upstream schema version + where to check for a newer one (self-upgrade tooling — see ./codegen/pinned-upgrade.ts). Omit for lexicons with no pinned upstream. */
|
|
1201
1245
|
readonly upstreamPin?: UpstreamPin;
|
|
1202
1246
|
|
|
1247
|
+
/**
|
|
1248
|
+
* Further pins, for a lexicon that pins more than one upstream (sql: the
|
|
1249
|
+
* ClickHouse pin in `upstreamPin` plus one Postgres server per major). Each
|
|
1250
|
+
* entry needs a unique `label`. The upgrade tooling checks `upstreamPin` and
|
|
1251
|
+
* every entry here, one report per pin. Omit when there is one pin.
|
|
1252
|
+
*/
|
|
1253
|
+
readonly upstreamPins?: readonly (UpstreamPin & { readonly label: string })[];
|
|
1254
|
+
|
|
1203
1255
|
/**
|
|
1204
1256
|
* Generate a CI pipeline from the discovered component graph — the
|
|
1205
1257
|
* `chant build --components --generate <name>` seam. Only CI-provider
|
|
@@ -187,6 +187,22 @@ export interface GateResolutionRecord {
|
|
|
187
187
|
* was recorded. Present only when the gate declared a policy.
|
|
188
188
|
*/
|
|
189
189
|
policyDecision?: GatePolicyDecision;
|
|
190
|
+
/**
|
|
191
|
+
* An ssh signature over this approval by `resolvedBy` (#3163, ws-080):
|
|
192
|
+
* `chant approve --sign`, in the `chant-gate` namespace over the op, gate,
|
|
193
|
+
* environment, plan digest, approver and timestamp. It attests the approver
|
|
194
|
+
* only when the signers file at base lists a key for them. A gate the
|
|
195
|
+
* declaration's `identity.gates` names counts only approvals whose seal
|
|
196
|
+
* verifies. Absent on an unsigned approval.
|
|
197
|
+
*/
|
|
198
|
+
seal?: GateApprovalSeal;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** A gate approval's seal (#3163): who signed, the key's fingerprint (reported, never trusted), and the armored signature. */
|
|
202
|
+
export interface GateApprovalSeal {
|
|
203
|
+
signer: string;
|
|
204
|
+
key: string;
|
|
205
|
+
signature: string;
|
|
190
206
|
}
|
|
191
207
|
|
|
192
208
|
export type GateResolutionInput = Omit<GateResolutionRecord, "version" | "kind">;
|
|
@@ -97,6 +97,24 @@ describe("observeResources", () => {
|
|
|
97
97
|
expect(names).toEqual(["web-vpc"]);
|
|
98
98
|
});
|
|
99
99
|
|
|
100
|
+
it("keeps property-kind declarables out of the observation universe (#3001)", async () => {
|
|
101
|
+
// A property-kind declarable (a grafana panel, an aws SecurityGroup_Ingress
|
|
102
|
+
// const) carries props but lives only inside the resource holding it.
|
|
103
|
+
// Asked about on its own, a reader can only say "absent" or "not observed".
|
|
104
|
+
const buildResult = {
|
|
105
|
+
outputs: new Map<string, string>([["aws", "{}"]]),
|
|
106
|
+
entities: new Map<string, unknown>([
|
|
107
|
+
["web-sg", { lexicon: "aws", entityType: "AWS::EC2::SecurityGroup", kind: "resource", props: {} }],
|
|
108
|
+
["ingress", { lexicon: "aws", entityType: "AWS::EC2::SecurityGroup.Ingress", kind: "property", props: { FromPort: 443 } }],
|
|
109
|
+
]),
|
|
110
|
+
errors: [],
|
|
111
|
+
} as unknown as BuildResult;
|
|
112
|
+
let names: string[] = [];
|
|
113
|
+
const plugins = [awsPlugin(({ entityNames }) => { names = entityNames; return {}; })];
|
|
114
|
+
await observeResources("prod", plugins, buildResult);
|
|
115
|
+
expect(names).toEqual(["web-sg"]);
|
|
116
|
+
});
|
|
117
|
+
|
|
100
118
|
it("collects a throwing plugin into errors instead of failing the whole graph, and reports its entities unobserved (#1089)", async () => {
|
|
101
119
|
const plugins = [
|
|
102
120
|
awsPlugin(() => { throw new Error("access denied"); }),
|
package/src/lifecycle/observe.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "../observation";
|
|
26
26
|
import { zeroResourcesWarning } from "../live-endpoint";
|
|
27
27
|
import { unqualifiedKey } from "./identity";
|
|
28
|
-
import {
|
|
28
|
+
import { isObservableDeclarable } from "../declarable";
|
|
29
29
|
|
|
30
30
|
export interface ObserveResult {
|
|
31
31
|
observations: LiveObservation[];
|
|
@@ -150,8 +150,10 @@ export async function observeResources(
|
|
|
150
150
|
// parameters and serializer directives (gcp's `defaultAnnotations`) are
|
|
151
151
|
// build-time inputs — declared, but with nothing in any cloud to compare
|
|
152
152
|
// against, so keeping them in the universe makes every diff report a
|
|
153
|
-
// hole (or worse, a deletion) for a resource that cannot exist.
|
|
154
|
-
|
|
153
|
+
// hole (or worse, a deletion) for a resource that cannot exist. A
|
|
154
|
+
// property-kind declarable is read as part of the resource holding it,
|
|
155
|
+
// never on its own (#3001).
|
|
156
|
+
if (!isObservableDeclarable(entity)) continue;
|
|
155
157
|
entityNames.push(name);
|
|
156
158
|
entities.set(name, {
|
|
157
159
|
entityType: entity.entityType,
|
|
@@ -174,7 +176,7 @@ export async function observeResources(
|
|
|
174
176
|
stackEntityNames = [];
|
|
175
177
|
stackEntities = new Map();
|
|
176
178
|
for (const [name, entity] of sb.entities) {
|
|
177
|
-
if (entity.lexicon !== plugin.name || !
|
|
179
|
+
if (entity.lexicon !== plugin.name || !isObservableDeclarable(entity)) continue;
|
|
178
180
|
stackEntityNames.push(name);
|
|
179
181
|
stackEntities.set(name, {
|
|
180
182
|
entityType: entity.entityType,
|
|
@@ -18,7 +18,7 @@ import { formatUnobserved, normalizeObservation, unobservedAll, type UnobservedE
|
|
|
18
18
|
// property tree is ever rendered or committed. Two lists would eventually
|
|
19
19
|
// disagree about what counts as a secret.
|
|
20
20
|
import { isSensitiveKey } from "../deep-observation";
|
|
21
|
-
import {
|
|
21
|
+
import { isObservableDeclarable } from "../declarable";
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Check for potential sensitive data in resource attributes and return warnings.
|
|
@@ -128,7 +128,7 @@ export async function takeSnapshot(
|
|
|
128
128
|
for (const [name, entity] of buildResult.entities) {
|
|
129
129
|
// Resource declarables only — outputs, parameters and serializer
|
|
130
130
|
// directives have no live counterpart (see lifecycle/observe.ts).
|
|
131
|
-
if (entity.lexicon === plugin.name &&
|
|
131
|
+
if (entity.lexicon === plugin.name && isObservableDeclarable(entity)) {
|
|
132
132
|
entityNames.push(name);
|
|
133
133
|
entities.set(name, {
|
|
134
134
|
entityType: entity.entityType,
|
|
@@ -107,7 +107,7 @@ describe("a work lease in one clone", () => {
|
|
|
107
107
|
|
|
108
108
|
const again = await claimWorkLease("W-003", "carol", { cwd, ttlMs: 1_000, now: at(t0) });
|
|
109
109
|
if (!again.ok) throw new Error(again.message);
|
|
110
|
-
const closed = await releaseWorkLease("W-003", "dispatcher", { cwd, outcome: "
|
|
110
|
+
const closed = await releaseWorkLease("W-003", "dispatcher", { cwd, outcome: "abandoned", now: at("2026-09-25T11:00:00.000Z") });
|
|
111
111
|
expect(closed).toMatchObject({ ok: true, lease: { holder: "carol" } });
|
|
112
112
|
});
|
|
113
113
|
|