@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `chant dev pinned-upgrade <lexicon-dir>` command implementation.
|
|
3
3
|
*
|
|
4
|
-
* Detects whether a
|
|
4
|
+
* Detects whether a lexicon that declares `upstreamPin` has a
|
|
5
5
|
* newer stable upstream release. When it does, it dry-run bumps the pinned
|
|
6
6
|
* version constant, regenerates + surface-diffs (via #524's regenLexicon), then
|
|
7
7
|
* reverts the bump — leaving the working tree unchanged.
|
|
@@ -10,20 +10,21 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { existsSync } from "fs";
|
|
13
|
-
import {
|
|
13
|
+
import { resolve } from "path";
|
|
14
14
|
import {
|
|
15
|
-
|
|
15
|
+
checkPinnedUpgrades,
|
|
16
|
+
lexiconNameOf,
|
|
16
17
|
type LexiconId,
|
|
17
18
|
type UpgradeCheckResult,
|
|
18
19
|
} from "../../codegen/pinned-upgrade";
|
|
19
20
|
|
|
20
|
-
const PINNED_LEXICONS: readonly LexiconId[] = ["k8s", "gcp", "docker", "gitlab"];
|
|
21
|
-
|
|
22
21
|
export interface PinnedUpgradeOptions {
|
|
23
22
|
/** Resolved path to the lexicon directory. */
|
|
24
23
|
lexiconDir: string;
|
|
25
24
|
/** Explicit lexicon id; inferred from the directory name when omitted. */
|
|
26
25
|
lexicon?: LexiconId;
|
|
26
|
+
/** Label of one pin to check, for a lexicon that declares several. Omitted: all of them. */
|
|
27
|
+
pin?: string;
|
|
27
28
|
/** Force re-fetch of the upstream spec during regen. */
|
|
28
29
|
force?: boolean;
|
|
29
30
|
/** Print subprocess output while running. */
|
|
@@ -31,12 +32,13 @@ export interface PinnedUpgradeOptions {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
|
-
* Infer the lexicon id from a directory
|
|
35
|
-
*
|
|
35
|
+
* Infer the lexicon id from a directory: its package name
|
|
36
|
+
* (`@intentius/chant-lexicon-<name>`), else the directory name. Whether the
|
|
37
|
+
* lexicon is pinned is the plugin's `upstreamPin`, checked by
|
|
38
|
+
* `checkPinnedUpgrade`, not a list here.
|
|
36
39
|
*/
|
|
37
|
-
export function inferLexicon(lexiconDir: string): LexiconId
|
|
38
|
-
|
|
39
|
-
return (PINNED_LEXICONS as readonly string[]).includes(name) ? (name as LexiconId) : null;
|
|
40
|
+
export function inferLexicon(lexiconDir: string): LexiconId {
|
|
41
|
+
return lexiconNameOf(lexiconDir);
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
/**
|
|
@@ -52,37 +54,38 @@ export function semverLabel(result: UpgradeCheckResult): "minor" | "major" | "no
|
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
56
|
* Run the pinned-upgrade check and return the structured result.
|
|
55
|
-
* Never throws — a missing dir or
|
|
57
|
+
* Never throws — a missing dir or a lexicon with no `upstreamPin` becomes a fetchError.
|
|
56
58
|
*/
|
|
57
59
|
export async function runPinnedUpgrade(opts: PinnedUpgradeOptions): Promise<UpgradeCheckResult> {
|
|
60
|
+
return (await runPinnedUpgrades(opts))[0]!;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Run the check for every pin the plugin declares (or the one in `opts.pin`),
|
|
65
|
+
* one result each. A lexicon with one pin gives one result.
|
|
66
|
+
*/
|
|
67
|
+
export async function runPinnedUpgrades(opts: PinnedUpgradeOptions): Promise<UpgradeCheckResult[]> {
|
|
58
68
|
const dir = resolve(opts.lexiconDir);
|
|
59
69
|
|
|
60
70
|
const lexicon = opts.lexicon ?? inferLexicon(dir);
|
|
61
|
-
if (!lexicon) {
|
|
62
|
-
return {
|
|
63
|
-
lexicon: "k8s",
|
|
64
|
-
hasUpgrade: false,
|
|
65
|
-
from: "(unknown)",
|
|
66
|
-
to: null,
|
|
67
|
-
validation: null,
|
|
68
|
-
fetchError: `Not a pinned lexicon directory: ${dir}. Expected one of: ${PINNED_LEXICONS.join(", ")}.`,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
71
|
|
|
72
72
|
if (!existsSync(dir)) {
|
|
73
|
-
return
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
return [
|
|
74
|
+
{
|
|
75
|
+
lexicon,
|
|
76
|
+
hasUpgrade: false,
|
|
77
|
+
from: "(unknown)",
|
|
78
|
+
to: null,
|
|
79
|
+
validation: null,
|
|
80
|
+
fetchError: `Lexicon directory not found: ${dir}`,
|
|
81
|
+
},
|
|
82
|
+
];
|
|
81
83
|
}
|
|
82
84
|
|
|
83
|
-
return
|
|
85
|
+
return checkPinnedUpgrades({
|
|
84
86
|
lexiconDir: dir,
|
|
85
87
|
lexicon,
|
|
88
|
+
pin: opts.pin,
|
|
86
89
|
force: opts.force,
|
|
87
90
|
verbose: opts.verbose,
|
|
88
91
|
});
|
|
@@ -112,12 +115,14 @@ function c(text: string, code: string): string {
|
|
|
112
115
|
*/
|
|
113
116
|
export function printPinnedUpgradeResult(result: UpgradeCheckResult, json: boolean): void {
|
|
114
117
|
const label = semverLabel(result);
|
|
118
|
+
const name = result.pin ? `${result.lexicon}/${result.pin}` : result.lexicon;
|
|
115
119
|
|
|
116
120
|
if (json) {
|
|
117
121
|
console.log(
|
|
118
122
|
JSON.stringify(
|
|
119
123
|
{
|
|
120
124
|
lexicon: result.lexicon,
|
|
125
|
+
...(result.pin ? { pin: result.pin } : {}),
|
|
121
126
|
hasUpgrade: result.hasUpgrade,
|
|
122
127
|
from: result.from,
|
|
123
128
|
to: result.to,
|
|
@@ -128,6 +133,7 @@ export function printPinnedUpgradeResult(result: UpgradeCheckResult, json: boole
|
|
|
128
133
|
validationOk: result.validation?.ok ?? null,
|
|
129
134
|
failures: result.validation?.failures ?? [],
|
|
130
135
|
fetchError: result.fetchError,
|
|
136
|
+
manualPin: result.manualPin ?? null,
|
|
131
137
|
},
|
|
132
138
|
null,
|
|
133
139
|
2,
|
|
@@ -137,22 +143,31 @@ export function printPinnedUpgradeResult(result: UpgradeCheckResult, json: boole
|
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
if (result.fetchError) {
|
|
140
|
-
console.log(c(`[${
|
|
146
|
+
console.log(c(`[${name}] error: ${result.fetchError}`, COLORS.red));
|
|
141
147
|
return;
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
if (!result.hasUpgrade) {
|
|
145
151
|
console.log(
|
|
146
|
-
c(`[${
|
|
152
|
+
c(`[${name}] up to date`, COLORS.green) +
|
|
147
153
|
` pinned=${result.from}` +
|
|
148
154
|
(result.to ? ` latest=${result.to}` : ""),
|
|
149
155
|
);
|
|
150
156
|
return;
|
|
151
157
|
}
|
|
152
158
|
|
|
159
|
+
if (result.manualPin) {
|
|
160
|
+
console.log(
|
|
161
|
+
c(`[${name}] upgrade available`, COLORS.bold) + ` ${result.from} -> ${result.to}`,
|
|
162
|
+
);
|
|
163
|
+
console.log(` The pin in ${result.manualPin.file} cannot be rewritten generically, so nothing was edited or regenerated.`);
|
|
164
|
+
console.log(` ${result.manualPin.instructions}`);
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
153
168
|
// Upgrade available
|
|
154
169
|
console.log(
|
|
155
|
-
c(`[${
|
|
170
|
+
c(`[${name}] upgrade available`, COLORS.bold) +
|
|
156
171
|
` ${result.from} -> ${result.to}`,
|
|
157
172
|
);
|
|
158
173
|
|
|
@@ -202,3 +217,28 @@ export function printPinnedUpgradeResult(result: UpgradeCheckResult, json: boole
|
|
|
202
217
|
`Validation: ${validationLabel} Semver: ${c(label, labelColor)} ${result.from} -> ${result.to}`,
|
|
203
218
|
);
|
|
204
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Print one result per pin. One result prints exactly as `printPinnedUpgradeResult`;
|
|
223
|
+
* several print in turn, and `json` emits an array of the same objects.
|
|
224
|
+
*/
|
|
225
|
+
export function printPinnedUpgradeResults(results: UpgradeCheckResult[], json: boolean): void {
|
|
226
|
+
if (results.length === 1) {
|
|
227
|
+
printPinnedUpgradeResult(results[0]!, json);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (json) {
|
|
231
|
+
// Each result prints as its own JSON object; collect them into one array.
|
|
232
|
+
const lines: string[] = [];
|
|
233
|
+
const log = console.log;
|
|
234
|
+
console.log = (...a: unknown[]) => void lines.push(a.join(" "));
|
|
235
|
+
try {
|
|
236
|
+
for (const r of results) printPinnedUpgradeResult(r, true);
|
|
237
|
+
} finally {
|
|
238
|
+
console.log = log;
|
|
239
|
+
}
|
|
240
|
+
console.log(`[${lines.join(",\n")}]`);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
for (const r of results) printPinnedUpgradeResult(r, false);
|
|
244
|
+
}
|
|
@@ -14,7 +14,7 @@ jobs:
|
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
container: node:22-slim
|
|
16
16
|
steps:
|
|
17
|
-
- uses: actions/checkout@
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
18
|
- run: chant run --components shared-alb --env staging --dump-outputs shared-alb.outputs.json
|
|
19
19
|
- name: Upload shared-alb outputs
|
|
20
20
|
uses: actions/upload-artifact@v4
|
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
- shared-alb
|
|
28
28
|
container: node:22-slim
|
|
29
29
|
steps:
|
|
30
|
-
- uses: actions/checkout@
|
|
30
|
+
- uses: actions/checkout@v7
|
|
31
31
|
- name: Download shared-alb outputs
|
|
32
32
|
uses: actions/download-artifact@v4
|
|
33
33
|
with:
|
|
@@ -48,7 +48,7 @@ import type { ComponentBomSummary } from "../../lifecycle/build-ledger";
|
|
|
48
48
|
import type { BuildArchiveManifest } from "../../components/verbs/build-archive";
|
|
49
49
|
import { loadChantConfig } from "../../config";
|
|
50
50
|
import { applyLiveEndpoint } from "../../live-endpoint";
|
|
51
|
-
import {
|
|
51
|
+
import { isObservableDeclarable } from "../../declarable";
|
|
52
52
|
import { build } from "../../build";
|
|
53
53
|
import { discoverComponents } from "../../components/discover";
|
|
54
54
|
import { formatError, formatWarning, formatSuccess, formatBold } from "../format";
|
|
@@ -643,7 +643,7 @@ export async function runComponentsStatus(ctx: CommandContext): Promise<number>
|
|
|
643
643
|
const entities = new Map<string, { entityType: string; props: Record<string, unknown> }>();
|
|
644
644
|
for (const [name, entity] of buildResult.entities) {
|
|
645
645
|
// Resource declarables only (see lifecycle/observe.ts).
|
|
646
|
-
if (entity.lexicon === plugin.name &&
|
|
646
|
+
if (entity.lexicon === plugin.name && isObservableDeclarable(entity)) {
|
|
647
647
|
declared.add(name);
|
|
648
648
|
entities.set(name, {
|
|
649
649
|
entityType: entity.entityType,
|
package/src/cli/handlers/dev.ts
CHANGED
|
@@ -85,16 +85,18 @@ export async function runDevSurfaceDiff(ctx: CommandContext): Promise<number> {
|
|
|
85
85
|
|
|
86
86
|
export async function runDevPinnedUpgrade(ctx: CommandContext): Promise<number> {
|
|
87
87
|
const dir = ctx.args.extraPositional ?? ".";
|
|
88
|
-
const {
|
|
89
|
-
|
|
88
|
+
const { runPinnedUpgrades, printPinnedUpgradeResults } = await import("../commands/pinned-upgrade");
|
|
89
|
+
// The optional second positional is a pin label, for a lexicon that declares several pins.
|
|
90
|
+
const results = await runPinnedUpgrades({
|
|
90
91
|
lexiconDir: resolve(dir),
|
|
92
|
+
pin: ctx.args.extraPositional2,
|
|
91
93
|
force: ctx.args.force,
|
|
92
94
|
verbose: ctx.args.verbose,
|
|
93
95
|
});
|
|
94
|
-
|
|
95
|
-
// Exit non-zero when
|
|
96
|
-
if (
|
|
97
|
-
if (
|
|
96
|
+
printPinnedUpgradeResults(results, ctx.args.format === "json");
|
|
97
|
+
// Exit non-zero when an upstream query failed or a regen validation broke.
|
|
98
|
+
if (results.some((r) => r.fetchError)) return 1;
|
|
99
|
+
if (results.some((r) => r.validation && !r.validation.ok)) return 1;
|
|
98
100
|
return 0;
|
|
99
101
|
}
|
|
100
102
|
|
|
@@ -97,6 +97,19 @@ function makeBuildResult(entitiesByLexicon: Record<string, string[]>): BuildResu
|
|
|
97
97
|
} as unknown as BuildResult;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* #3001 — a build holding one resource and one property-kind declarable that
|
|
102
|
+
* the resource inlines, the shape every grafana dashboard with exported panels
|
|
103
|
+
* has. Only the resource has a live counterpart of its own.
|
|
104
|
+
*/
|
|
105
|
+
function buildWithPropertyKind(lexicon: string): BuildResult {
|
|
106
|
+
const build = makeBuildResult({ [lexicon]: ["board"] });
|
|
107
|
+
const panel = { lexicon, entityType: `${lexicon}::Panel`, kind: "property", props: { title: "CPU" } };
|
|
108
|
+
build.entities.set("board", { lexicon, entityType: `${lexicon}::Board`, kind: "resource", props: { panels: [panel] } } as never);
|
|
109
|
+
build.entities.set("cpuPanel", panel as never);
|
|
110
|
+
return build;
|
|
111
|
+
}
|
|
112
|
+
|
|
100
113
|
const meta = (overrides: Partial<ResourceMetadata> = {}): ResourceMetadata => ({
|
|
101
114
|
type: "AWS::S3::Bucket",
|
|
102
115
|
status: "CREATE_COMPLETE",
|
|
@@ -537,6 +550,37 @@ describe("runLifecycleDiff --live", () => {
|
|
|
537
550
|
});
|
|
538
551
|
|
|
539
552
|
// #1014 — property-level drift, gated purely on the deep capability.
|
|
553
|
+
test("hands only resource-kind entities to describeResources and observeResourcesDeep (#3001)", async () => {
|
|
554
|
+
buildMock.mockResolvedValue(buildWithPropertyKind("graf"));
|
|
555
|
+
fetchLifecycleMock.mockResolvedValue(undefined);
|
|
556
|
+
readSnapshotMock.mockResolvedValue(null);
|
|
557
|
+
const seen: { thin?: string[]; deep?: string[] } = {};
|
|
558
|
+
const plugins: LexiconPlugin[] = [
|
|
559
|
+
createMockPlugin({
|
|
560
|
+
name: "graf",
|
|
561
|
+
describeResources: async (opts) => {
|
|
562
|
+
seen.thin = [...opts.entities.keys()];
|
|
563
|
+
return { board: meta({ type: "graf::Board", physicalId: "uid-1" }) };
|
|
564
|
+
},
|
|
565
|
+
observeResourcesDeep: async (opts) => {
|
|
566
|
+
seen.deep = [...opts.entities.keys()];
|
|
567
|
+
return deepObservation({ board: { type: "graf::Board", properties: { panels: [{ title: "CPU" }] } } });
|
|
568
|
+
},
|
|
569
|
+
}),
|
|
570
|
+
];
|
|
571
|
+
const exit = await runLifecycleDiff({
|
|
572
|
+
args: makeArgs({ path: "diff", extraPositional: "prod", live: true }),
|
|
573
|
+
plugins,
|
|
574
|
+
serializers: plugins.map((p) => p.serializer),
|
|
575
|
+
});
|
|
576
|
+
expect(exit).toBe(0);
|
|
577
|
+
expect(seen.thin).toEqual(["board"]);
|
|
578
|
+
expect(seen.deep).toEqual(["board"]);
|
|
579
|
+
const stdout = stdoutBuf.join("\n");
|
|
580
|
+
expect(stdout).not.toContain("cpuPanel");
|
|
581
|
+
expect(stdout).toContain("0 missing");
|
|
582
|
+
});
|
|
583
|
+
|
|
540
584
|
describe("deep observation (#1014)", () => {
|
|
541
585
|
const withDeep = (over: Parameters<typeof createMockPlugin>[0] = {}) =>
|
|
542
586
|
createMockPlugin({
|
|
@@ -872,6 +916,29 @@ describe("runLifecyclePlan", () => {
|
|
|
872
916
|
expect(stdoutBuf.join("\n")).toContain("bucket");
|
|
873
917
|
});
|
|
874
918
|
|
|
919
|
+
test("proposes no create for a property-kind declarable its parent inlines (#3001)", async () => {
|
|
920
|
+
buildMock.mockResolvedValue(buildWithPropertyKind("graf"));
|
|
921
|
+
let names: string[] = [];
|
|
922
|
+
const plugins: LexiconPlugin[] = [
|
|
923
|
+
createMockPlugin({
|
|
924
|
+
name: "graf",
|
|
925
|
+
describeResources: async (opts) => {
|
|
926
|
+
names = opts.entityNames;
|
|
927
|
+
return { board: meta({ type: "graf::Board", physicalId: "uid-1" }) };
|
|
928
|
+
},
|
|
929
|
+
}),
|
|
930
|
+
];
|
|
931
|
+
const exit = await runLifecyclePlan({
|
|
932
|
+
args: makeArgs({ path: "plan", extraPositional: "prod", json: true }),
|
|
933
|
+
plugins,
|
|
934
|
+
serializers: plugins.map((p) => p.serializer),
|
|
935
|
+
});
|
|
936
|
+
expect(exit).toBe(0);
|
|
937
|
+
expect(names).toEqual(["board"]);
|
|
938
|
+
const plan = JSON.parse(stdoutBuf.join("\n"));
|
|
939
|
+
expect(plan.entries.map((e: { name: string }) => e.name)).not.toContain("cpuPanel");
|
|
940
|
+
});
|
|
941
|
+
|
|
875
942
|
// #1983 — `--report markdown` emits the reviewer-facing projection instead
|
|
876
943
|
// of the human render, same as `--report gitlab-mr` swaps in the widget JSON.
|
|
877
944
|
test("--report markdown emits the markdown projection instead of the human render", async () => {
|
|
@@ -42,7 +42,7 @@ import { unknownEnvError, isProdLikeEnvironment } from "../../env";
|
|
|
42
42
|
import { planTeardown, executeTeardown, type TeardownPlan, type TeardownReport } from "../../lifecycle/teardown";
|
|
43
43
|
import { collectBuildRootContributors } from "../plugins";
|
|
44
44
|
import { applyLiveEndpoint } from "../../live-endpoint";
|
|
45
|
-
import {
|
|
45
|
+
import { isObservableDeclarable } from "../../declarable";
|
|
46
46
|
import { formatError, formatWarning, formatSuccess, formatBold } from "../format";
|
|
47
47
|
import type { CommandContext } from "../registry";
|
|
48
48
|
import type { LifecycleSnapshot } from "../../lifecycle/types";
|
|
@@ -812,13 +812,14 @@ async function runLifecycleDiffLive(args: LiveDiffArgs): Promise<LiveDiffOutcome
|
|
|
812
812
|
}
|
|
813
813
|
|
|
814
814
|
// Build per-lexicon entity index. Resource declarables only — outputs,
|
|
815
|
-
// parameters
|
|
815
|
+
// parameters, serializer directives and property-kind declarables (read
|
|
816
|
+
// inside their parent, #3001) have no live counterpart of their own, and a
|
|
816
817
|
// declared name the reader can never resolve reads as missing (see
|
|
817
818
|
// lifecycle/observe.ts).
|
|
818
819
|
const declared = new Set<string>();
|
|
819
820
|
const entities: DeclaredEntities = new Map();
|
|
820
821
|
for (const [name, entity] of args.buildResult.entities) {
|
|
821
|
-
if (entity.lexicon === lexiconName &&
|
|
822
|
+
if (entity.lexicon === lexiconName && isObservableDeclarable(entity)) {
|
|
822
823
|
declared.add(name);
|
|
823
824
|
// Path origins are carried across explicitly (#1443): this map is plain
|
|
824
825
|
// objects, and the symbol-keyed provenance channel does not survive it.
|
|
@@ -1289,11 +1290,11 @@ export async function runLifecyclePlan(ctx: CommandContext): Promise<number> {
|
|
|
1289
1290
|
// A receipt has no `props` payload of its own but is declared, diffed,
|
|
1290
1291
|
// and observed like any resource (#1832) — it joins the declared axis
|
|
1291
1292
|
// so its lexicon's observation can confirm presence or absence.
|
|
1292
|
-
if (entity.lexicon === lexiconName && (
|
|
1293
|
+
if (entity.lexicon === lexiconName && (isObservableDeclarable(entity) || isEffectReceipt(entity))) {
|
|
1293
1294
|
declared.add(name);
|
|
1294
1295
|
entities.set(name, {
|
|
1295
1296
|
entityType: entity.entityType,
|
|
1296
|
-
props: (
|
|
1297
|
+
props: (isObservableDeclarable(entity) && entity.props != null ? entity.props : {}) as Record<string, unknown>,
|
|
1297
1298
|
});
|
|
1298
1299
|
}
|
|
1299
1300
|
}
|
package/src/cli/handlers/misc.ts
CHANGED
|
@@ -324,6 +324,7 @@ export async function runImport(ctx: CommandContext): Promise<number> {
|
|
|
324
324
|
lexicon: "k8s",
|
|
325
325
|
output: args.output,
|
|
326
326
|
force: args.force,
|
|
327
|
+
parserOptions: args.parserOption,
|
|
327
328
|
});
|
|
328
329
|
printImportResult(result);
|
|
329
330
|
return result.success ? 0 : 1;
|
|
@@ -381,6 +382,7 @@ export async function runImport(ctx: CommandContext): Promise<number> {
|
|
|
381
382
|
output: ctx.args.output,
|
|
382
383
|
force: ctx.args.force,
|
|
383
384
|
lexicon: ctx.args.lexicon,
|
|
385
|
+
parserOptions: ctx.args.parserOption,
|
|
384
386
|
});
|
|
385
387
|
|
|
386
388
|
printImportResult(result);
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { lexiconNames } from "../../lexicon-module";
|
|
10
10
|
import { loadChantConfig } from "../../config";
|
|
11
11
|
import { build } from "../../build";
|
|
12
|
-
import {
|
|
12
|
+
import { isObservableDeclarable } from "../../declarable";
|
|
13
13
|
import { collectBuildRootContributors, collectChangeSubscribers } from "../plugins";
|
|
14
14
|
import { discoverOps, discoverStewards } from "../../op/discover";
|
|
15
15
|
import { pickSteward, stewardBesideOf, stewardFormFor, stewardLeaseName, stewardTurnOps, DEFAULT_STEWARD_ENV } from "../../op/steward";
|
|
@@ -116,7 +116,7 @@ async function collectOperatorSubscribers(
|
|
|
116
116
|
// subscription may watch.
|
|
117
117
|
const entities = new Map<string, Map<string, { entityType: string; props: Record<string, unknown> }>>();
|
|
118
118
|
for (const [name, entity] of buildResult.entities) {
|
|
119
|
-
if (!
|
|
119
|
+
if (!isObservableDeclarable(entity)) continue;
|
|
120
120
|
let perLexicon = entities.get(entity.lexicon);
|
|
121
121
|
if (!perLexicon) entities.set(entity.lexicon, (perLexicon = new Map()));
|
|
122
122
|
perLexicon.set(name, {
|
|
@@ -902,6 +902,7 @@ export async function runApprove(ctx: CommandContext): Promise<number> {
|
|
|
902
902
|
allowSameOrigin: ctx.args.allowSameOrigin,
|
|
903
903
|
...(ctx.args.roles ? { roles: ctx.args.roles } : {}),
|
|
904
904
|
...(ctx.args.agent ? { agent: true } : {}),
|
|
905
|
+
...(ctx.args.sign !== undefined ? { sign: ctx.args.sign } : {}),
|
|
905
906
|
});
|
|
906
907
|
if (!outcome.ok) return 1;
|
|
907
908
|
|
|
@@ -959,6 +960,15 @@ export interface GateApprovalOptions {
|
|
|
959
960
|
* model-authored channel it is one whether or not this is set.
|
|
960
961
|
*/
|
|
961
962
|
agent?: boolean;
|
|
963
|
+
/**
|
|
964
|
+
* `--sign [<key file>]` (#3163, ws-080): seal the approval with an ssh key,
|
|
965
|
+
* a key file or true for git's `user.signingkey`. The seal attests the
|
|
966
|
+
* approver when the signers file at base lists the key for them, and a gate
|
|
967
|
+
* the declaration's `identity.gates` names counts only sealed approvals.
|
|
968
|
+
*/
|
|
969
|
+
sign?: string | true;
|
|
970
|
+
/** Where the workspace and the key file are looked up from. Defaults to the process's directory. */
|
|
971
|
+
cwd?: string;
|
|
962
972
|
/** Replaces the lexicon evaluator a gate policy is loaded from. For tests. */
|
|
963
973
|
evaluator?: GatePolicyEvaluator;
|
|
964
974
|
}
|
|
@@ -1107,6 +1117,78 @@ export async function recordGateApproval(
|
|
|
1107
1117
|
...(opts.roles && opts.roles.length > 0 ? { roles: [...new Set(opts.roles)] } : {}),
|
|
1108
1118
|
};
|
|
1109
1119
|
|
|
1120
|
+
// #3163, ws-080: the declaration at base may require a checkable approver
|
|
1121
|
+
// (identity.attribution "identified") and a signed approval for this gate
|
|
1122
|
+
// (identity.gates). Both are judged before anything is written.
|
|
1123
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
1124
|
+
const identity = await import("../../workspace/identity");
|
|
1125
|
+
const { scopeSource } = await import("../../workspace/write-scope");
|
|
1126
|
+
const scope = scopeSource(cwd);
|
|
1127
|
+
try {
|
|
1128
|
+
identity.refuseUnidentified(scope, [resolvedBy], opts.actor !== undefined ? "--actor" : "the approver");
|
|
1129
|
+
} catch (err) {
|
|
1130
|
+
if (!(err instanceof identity.IdentityError)) throw err;
|
|
1131
|
+
console.error(formatError({
|
|
1132
|
+
message: `Gate "${gate}" on "${opName}" was not approved: ${err.message}`,
|
|
1133
|
+
hint: "Pass --actor <forge identity or signer>, as the surface that signed the person in maps them, for example --actor github:<login>.",
|
|
1134
|
+
}));
|
|
1135
|
+
return { ok: false };
|
|
1136
|
+
}
|
|
1137
|
+
const rule = identity.gateAdmissionFrom(scope, gate);
|
|
1138
|
+
if (rule && opts.sign === undefined) {
|
|
1139
|
+
console.error(formatError({
|
|
1140
|
+
message:
|
|
1141
|
+
`Gate "${gate}" needs a signed approval: chant.workspace.json at base names it in identity.gates` +
|
|
1142
|
+
(rule.requirement.class !== null ? `, from the class ${rule.requirement.class}` : "") +
|
|
1143
|
+
`, and this approval is not signed`,
|
|
1144
|
+
hint: `chant approve ${opName} ${gate} --actor <principal> --sign [<key file>], with a key ${scope.policy.signersPath} at base lists for that principal.`,
|
|
1145
|
+
}));
|
|
1146
|
+
return { ok: false };
|
|
1147
|
+
}
|
|
1148
|
+
const timestamp = new Date().toISOString();
|
|
1149
|
+
let seal: GateResolutionRecord["seal"];
|
|
1150
|
+
if (opts.sign !== undefined) {
|
|
1151
|
+
const { resolveSigningKey, sealGateApproval, SealError } = await import("../../workspace/trust/seal");
|
|
1152
|
+
try {
|
|
1153
|
+
const key = resolveSigningKey(opts.sign, cwd);
|
|
1154
|
+
try {
|
|
1155
|
+
seal = sealGateApproval(key.file, {
|
|
1156
|
+
op: opName,
|
|
1157
|
+
gate,
|
|
1158
|
+
...(environment !== undefined ? { environment } : {}),
|
|
1159
|
+
...(planDigest !== undefined ? { planDigest } : {}),
|
|
1160
|
+
resolvedBy,
|
|
1161
|
+
timestamp,
|
|
1162
|
+
});
|
|
1163
|
+
} finally {
|
|
1164
|
+
key.cleanup();
|
|
1165
|
+
}
|
|
1166
|
+
} catch (err) {
|
|
1167
|
+
if (!(err instanceof SealError)) throw err;
|
|
1168
|
+
console.error(formatError({ message: `Gate "${gate}" on "${opName}" was not approved: ${err.message}` }));
|
|
1169
|
+
return { ok: false };
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (rule) {
|
|
1173
|
+
const why = rule.refuses({
|
|
1174
|
+
op: opName,
|
|
1175
|
+
gate,
|
|
1176
|
+
...(environment !== undefined ? { environment } : {}),
|
|
1177
|
+
...(planDigest !== undefined ? { planDigest } : {}),
|
|
1178
|
+
resolvedBy,
|
|
1179
|
+
timestamp,
|
|
1180
|
+
seal,
|
|
1181
|
+
});
|
|
1182
|
+
if (why !== null) {
|
|
1183
|
+
console.error(formatError({
|
|
1184
|
+
message: `Gate "${gate}" on "${opName}" was not approved: ${why}`,
|
|
1185
|
+
hint: `identity.gates.${gate} in chant.workspace.json at base counts only an approval signed by a key ${scope.policy.signersPath} lists for the approver` +
|
|
1186
|
+
(rule.requirement.class !== null ? `, who holds the role of the class ${rule.requirement.class}` : "") + ".",
|
|
1187
|
+
}));
|
|
1188
|
+
return { ok: false };
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1110
1192
|
// #2508: a gate with a policy has it evaluated for this approval now, while
|
|
1111
1193
|
// the approver is known, against the context the gated run recorded. The
|
|
1112
1194
|
// decision is written next to the approval, so a pass can be traced to the
|
|
@@ -1148,7 +1230,8 @@ export async function recordGateApproval(
|
|
|
1148
1230
|
op: opName,
|
|
1149
1231
|
gate,
|
|
1150
1232
|
resolvedBy,
|
|
1151
|
-
timestamp
|
|
1233
|
+
timestamp,
|
|
1234
|
+
...(seal ? { seal } : {}),
|
|
1152
1235
|
approver,
|
|
1153
1236
|
...(policyDecision ? { policyDecision } : {}),
|
|
1154
1237
|
...(opts.note ? { note: opts.note } : {}),
|
|
@@ -1166,6 +1249,7 @@ export async function recordGateApproval(
|
|
|
1166
1249
|
console.error(formatSuccess(
|
|
1167
1250
|
`Gate "${gate}" on "${opName}" resolved by ${record.resolvedBy} at ${record.timestamp}` +
|
|
1168
1251
|
(record.url ? ` (${record.url})` : "") +
|
|
1252
|
+
(record.seal ? `, signed with ${record.seal.key}` : "") +
|
|
1169
1253
|
(pushed ? "" : " (local only — the push did not land)"),
|
|
1170
1254
|
));
|
|
1171
1255
|
if (record.planDigest) {
|
|
@@ -424,6 +424,30 @@ describe("runOp: a steward's turn (#2750)", () => {
|
|
|
424
424
|
expect(acquireStewardTurnMock).not.toHaveBeenCalled();
|
|
425
425
|
});
|
|
426
426
|
|
|
427
|
+
test("fountain form with run.on: fountain and no flag: runs on fountain like --on fountain (#2523)", async () => {
|
|
428
|
+
const runtime = {
|
|
429
|
+
name: "fountain",
|
|
430
|
+
start: vi.fn(async (op: { name: string }) => ({
|
|
431
|
+
op: op.name,
|
|
432
|
+
runId: "f-1",
|
|
433
|
+
result: async () => ({ op: op.name, runId: "f-1", state: "completed" as const, startedAt: "t", endedAt: "t" }),
|
|
434
|
+
})),
|
|
435
|
+
status: vi.fn(), log: vi.fn(), list: vi.fn(), cancel: vi.fn(),
|
|
436
|
+
};
|
|
437
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([localOp("release", [])]), errors: [] });
|
|
438
|
+
discoverStewardsMock.mockResolvedValueOnce(stewardOwning("release", "box-steward", "fountain"));
|
|
439
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["fountain"], run: { on: "fountain" } } });
|
|
440
|
+
makeStdoutSpy();
|
|
441
|
+
const exit = await runOp({
|
|
442
|
+
args: makeArgs({ path: "release" }),
|
|
443
|
+
plugins: [{ name: "fountain", opRuntime: runtime } as never], serializers: [],
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
expect(exit).toBe(0);
|
|
447
|
+
expect(runtime.start).toHaveBeenCalledTimes(1);
|
|
448
|
+
expect(acquireStewardTurnMock).not.toHaveBeenCalled();
|
|
449
|
+
});
|
|
450
|
+
|
|
427
451
|
test("local form, the turn lease's own acquire throws (a stale .lock): refused as a lease error, not an uncaught exception, and nothing runs", async () => {
|
|
428
452
|
discoverOpsMock.mockResolvedValue({
|
|
429
453
|
ops: new Map([localOp("release", [{ kind: "activity", fn: "shellCmd", args: { cmd: "true" } }])]),
|
|
@@ -899,6 +923,93 @@ describe("run subcommands on the resolved runtime", () => {
|
|
|
899
923
|
expect(stderr.join("\n")).toContain('lexicon "plain" does not host Op runs');
|
|
900
924
|
});
|
|
901
925
|
|
|
926
|
+
test("run.on in chant.config.ts is the default runtime when --on is not given (#2523)", async () => {
|
|
927
|
+
const runtime = makeStubRuntime();
|
|
928
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
929
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub"], run: { on: "stub" } } });
|
|
930
|
+
makeStdoutSpy();
|
|
931
|
+
makeStderrSpy();
|
|
932
|
+
const ctx = (over: Partial<ParsedArgs>) => ({ args: makeArgs(over), plugins: [stubPlugin(runtime)], serializers: [] });
|
|
933
|
+
|
|
934
|
+
expect(await runOp(ctx({ path: "hello" }))).toBe(0);
|
|
935
|
+
expect(await runOpStatus(ctx({ extraPositional: "hello" }))).toBe(0);
|
|
936
|
+
expect(await runOpLog(ctx({ extraPositional: "hello" }))).toBe(0);
|
|
937
|
+
expect(await runOpList(ctx({}))).toBe(0);
|
|
938
|
+
expect(runtime.start).toHaveBeenCalledTimes(1);
|
|
939
|
+
expect(runtime.status).toHaveBeenCalledWith("hello");
|
|
940
|
+
expect(runtime.log).toHaveBeenCalledTimes(1);
|
|
941
|
+
expect(runtime.list).toHaveBeenCalledTimes(1);
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
test("--on local overrides run.on and runs on the built-in runtime (#2523)", async () => {
|
|
945
|
+
const runtime = makeStubRuntime();
|
|
946
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
947
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub"], run: { on: "stub" } } });
|
|
948
|
+
makeStdoutSpy();
|
|
949
|
+
makeStderrSpy();
|
|
950
|
+
await runOpList({ args: makeArgs({ on: "local" }), plugins: [stubPlugin(runtime)], serializers: [] });
|
|
951
|
+
expect(runtime.list).not.toHaveBeenCalled();
|
|
952
|
+
});
|
|
953
|
+
|
|
954
|
+
test("run.on naming an unconfigured lexicon gets --on's refusal, naming run.on (#2523)", async () => {
|
|
955
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
956
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["aws"], run: { on: "nope" } } });
|
|
957
|
+
const stderr = makeStderrSpy();
|
|
958
|
+
const exit = await runOp({ args: makeArgs({ path: "hello" }), plugins: [], serializers: [] });
|
|
959
|
+
expect(exit).toBe(1);
|
|
960
|
+
expect(stderr.join("\n")).toContain('run.on "nope" in chant.config.ts: "nope" is not a configured lexicon');
|
|
961
|
+
expect(stderr.join("\n")).toContain("Configured lexicons: aws");
|
|
962
|
+
expect(stderr.join("\n")).toContain("--on local");
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
test("an explicit --on wins over run.on (#2523)", async () => {
|
|
966
|
+
const chosen = makeStubRuntime();
|
|
967
|
+
const other = makeStubRuntime();
|
|
968
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
969
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub", "other"], run: { on: "other" } } });
|
|
970
|
+
makeStdoutSpy();
|
|
971
|
+
makeStderrSpy();
|
|
972
|
+
const plugins = [stubPlugin(chosen), { name: "other", opRuntime: other } as never];
|
|
973
|
+
expect(await runOpList({ args: makeArgs({ on: "stub" }), plugins, serializers: [] })).toBe(0);
|
|
974
|
+
expect(chosen.list).toHaveBeenCalledTimes(1);
|
|
975
|
+
expect(other.list).not.toHaveBeenCalled();
|
|
976
|
+
});
|
|
977
|
+
|
|
978
|
+
test("run.on: local selects the built-in runtime (#2523)", async () => {
|
|
979
|
+
const runtime = makeStubRuntime();
|
|
980
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
981
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["stub"], run: { on: "local" } } });
|
|
982
|
+
makeStdoutSpy();
|
|
983
|
+
makeStderrSpy();
|
|
984
|
+
await runOpList({ args: makeArgs({}), plugins: [stubPlugin(runtime)], serializers: [] });
|
|
985
|
+
expect(runtime.list).not.toHaveBeenCalled();
|
|
986
|
+
});
|
|
987
|
+
|
|
988
|
+
test("an invalid run block is refused, not run locally (#2523)", async () => {
|
|
989
|
+
const { InvalidChantConfigError } = await vi.importActual<typeof import("../../config")>("../../config");
|
|
990
|
+
const runtime = makeStubRuntime();
|
|
991
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
992
|
+
loadChantConfigMock.mockRejectedValue(
|
|
993
|
+
new InvalidChantConfigError("Invalid chant config in /p/chant.config.ts: run.on: Expected string, received number", "run"),
|
|
994
|
+
);
|
|
995
|
+
const stderr = makeStderrSpy();
|
|
996
|
+
makeStdoutSpy();
|
|
997
|
+
expect(await runOpList({ args: makeArgs({}), plugins: [stubPlugin(runtime)], serializers: [] })).toBe(1);
|
|
998
|
+
expect(stderr.join("\n")).toContain("Invalid chant config in /p/chant.config.ts: run.on");
|
|
999
|
+
expect(runtime.list).not.toHaveBeenCalled();
|
|
1000
|
+
// --on local still runs: the flag needs nothing from the config.
|
|
1001
|
+
expect(await runOpList({ args: makeArgs({ on: "local" }), plugins: [stubPlugin(runtime)], serializers: [] })).toBe(0);
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
test("run.on naming a lexicon with no opRuntime gets --on's refusal, naming run.on (#2523)", async () => {
|
|
1005
|
+
discoverOpsMock.mockResolvedValue({ ops: new Map([makeOp("hello")]), errors: [] });
|
|
1006
|
+
loadChantConfigMock.mockResolvedValue({ config: { lexicons: ["plain"], run: { on: "plain" } } });
|
|
1007
|
+
const stderr = makeStderrSpy();
|
|
1008
|
+
const exit = await runOp({ args: makeArgs({ path: "hello" }), plugins: [{ name: "plain" } as never], serializers: [] });
|
|
1009
|
+
expect(exit).toBe(1);
|
|
1010
|
+
expect(stderr.join("\n")).toContain('run.on "plain" in chant.config.ts: lexicon "plain" does not host Op runs');
|
|
1011
|
+
});
|
|
1012
|
+
|
|
902
1013
|
test("--components on a runtime that cannot host them → one line, exit 1", async () => {
|
|
903
1014
|
const runtime = makeStubRuntime();
|
|
904
1015
|
const stderr = makeStderrSpy();
|