@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
|
@@ -316,6 +316,8 @@ describe("chant workspace status on built workspaces", () => {
|
|
|
316
316
|
isolation: null,
|
|
317
317
|
intent: null,
|
|
318
318
|
services: [],
|
|
319
|
+
factory: null,
|
|
320
|
+
listing: null,
|
|
319
321
|
},
|
|
320
322
|
]);
|
|
321
323
|
});
|
|
@@ -451,6 +453,7 @@ describe("gate state in chant workspace status --json (#2674)", () => {
|
|
|
451
453
|
expiresAt: "2026-12-31T00:00:00.000Z",
|
|
452
454
|
approvals: [{ principal: "alice", channel: "cli", at: "2026-09-20T01:00:00.000Z" }],
|
|
453
455
|
needed: 1,
|
|
456
|
+
signed: null,
|
|
454
457
|
approve: `chant approve web deploy --env staging --plan ${D("1")}`,
|
|
455
458
|
},
|
|
456
459
|
{
|
|
@@ -463,6 +466,7 @@ describe("gate state in chant workspace status --json (#2674)", () => {
|
|
|
463
466
|
expiresAt: "2026-12-31T00:00:00.000Z",
|
|
464
467
|
approvals: [],
|
|
465
468
|
needed: 1,
|
|
469
|
+
signed: null,
|
|
466
470
|
approve: `chant approve web deploy --env prod --plan ${D("2")}`,
|
|
467
471
|
},
|
|
468
472
|
{
|
|
@@ -476,6 +480,7 @@ describe("gate state in chant workspace status --json (#2674)", () => {
|
|
|
476
480
|
// The agent's approval doesn't count toward the quorum.
|
|
477
481
|
approvals: [{ principal: "bob", channel: "cli", at: "2026-09-20T02:00:00.000Z" }],
|
|
478
482
|
needed: 2,
|
|
483
|
+
signed: null,
|
|
479
484
|
approve: `chant approve web review --env staging --plan ${D("4")}`,
|
|
480
485
|
},
|
|
481
486
|
]);
|
|
@@ -22,7 +22,7 @@ import { execFileSync } from "node:child_process";
|
|
|
22
22
|
import { samePlanDigest } from "../lifecycle/plan-digest";
|
|
23
23
|
import type { GateResolutionRecord, PendingGateRecord } from "../lifecycle/gate-ledger";
|
|
24
24
|
import { isPendingGateExpired, latestPendingGate, latestResolutionForPlan, parseGateLedger } from "../lifecycle/gate-ledger";
|
|
25
|
-
import { tallyGateApprovals } from "../op/gate";
|
|
25
|
+
import { tallyGateApprovals, type GateApprovalRule } from "../op/gate";
|
|
26
26
|
import type { ReasonCode } from "./reason-codes";
|
|
27
27
|
|
|
28
28
|
/** Why a member's gates can't be listed. Closed: a new code is a contract change. */
|
|
@@ -63,10 +63,20 @@ export interface StatusGate {
|
|
|
63
63
|
approvals: StatusGateApproval[];
|
|
64
64
|
/** How many human approvals the gate needs: its quorum's count, 1 without one. */
|
|
65
65
|
needed: number;
|
|
66
|
+
/**
|
|
67
|
+
* The declaration's rule for the gate (#3163, ws-080): set when
|
|
68
|
+
* `identity.gates` names it, so only an approval sealed by a signer at
|
|
69
|
+
* base (from `class`, when it names one) counts, and `approve` carries
|
|
70
|
+
* `--sign`. Null when any approval counts.
|
|
71
|
+
*/
|
|
72
|
+
signed: { class: string | null } | null;
|
|
66
73
|
/** The command that approves it, run in the member's directory. */
|
|
67
74
|
approve: string;
|
|
68
75
|
}
|
|
69
76
|
|
|
77
|
+
/** The rule for a gate, by name, as `identity.gates` at base sets it (#3163). Null when it sets none. */
|
|
78
|
+
export type GateRuleLookup = (gate: string) => GateApprovalRule | null;
|
|
79
|
+
|
|
70
80
|
export interface StatusGateLedger {
|
|
71
81
|
/** `members` for `_members/<member>/_gates`, `flat` for `_gates`. */
|
|
72
82
|
layout: "members" | "flat";
|
|
@@ -110,15 +120,17 @@ export const defaultGateLedgerReader: GateLedgerReader = async (dir, commit, cwd
|
|
|
110
120
|
* plan this read showed and not whatever happens to be pending by the time
|
|
111
121
|
* someone runs it.
|
|
112
122
|
*/
|
|
113
|
-
export function approveCommand(component: string, gate: string, env: string | null, planDigest?: string | null): string {
|
|
114
|
-
return `chant approve ${component} ${gate}${env !== null ? ` --env ${env}` : ""}${planDigest ? ` --plan ${planDigest}` : ""}`;
|
|
123
|
+
export function approveCommand(component: string, gate: string, env: string | null, planDigest?: string | null, signed = false): string {
|
|
124
|
+
return `chant approve ${component} ${gate}${env !== null ? ` --env ${env}` : ""}${planDigest ? ` --plan ${planDigest}` : ""}${signed ? " --sign" : ""}`;
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
const approvalOf = (r: GateResolutionRecord): StatusGateApproval => ({ principal: r.resolvedBy, channel: r.origin ?? null, at: r.timestamp });
|
|
118
128
|
|
|
119
129
|
/** One gate in one environment, decided against its newest pending fact. */
|
|
120
|
-
function decide(component: string, standing: PendingGateRecord,
|
|
130
|
+
function decide(component: string, standing: PendingGateRecord, all: GateResolutionRecord[], now: string, rule: GateApprovalRule | null): StatusGate {
|
|
121
131
|
const gate = standing.gate;
|
|
132
|
+
// #3163: under identity.gates, an unsigned approval, or one from outside the class, doesn't count, as a run reads it.
|
|
133
|
+
const resolutions = rule === null ? all : all.filter((r) => r.gate !== gate || rule.refuses(r) === null);
|
|
122
134
|
const env = standing.environment ?? null;
|
|
123
135
|
const since = standing.timestamp;
|
|
124
136
|
const planDigest = standing.planDigest;
|
|
@@ -156,7 +168,8 @@ function decide(component: string, standing: PendingGateRecord, resolutions: Gat
|
|
|
156
168
|
expiresAt: standing.expiresAt,
|
|
157
169
|
approvals: approvals.map(approvalOf),
|
|
158
170
|
needed,
|
|
159
|
-
|
|
171
|
+
signed: rule === null ? null : { class: rule.requirement.class },
|
|
172
|
+
approve: approveCommand(component, gate, env, planDigest, rule !== null),
|
|
160
173
|
};
|
|
161
174
|
}
|
|
162
175
|
|
|
@@ -167,7 +180,7 @@ function decide(component: string, standing: PendingGateRecord, resolutions: Gat
|
|
|
167
180
|
* gate whose other facts have one is left out, as a run leaves it out: such a
|
|
168
181
|
* line binds no environment, and `chant approve --expire` writes one.
|
|
169
182
|
*/
|
|
170
|
-
export function gatesInLedger(component: string, content: string, envs: readonly string[], now: string): { gates: StatusGate[]; malformed: number } {
|
|
183
|
+
export function gatesInLedger(component: string, content: string, envs: readonly string[], now: string, rules: GateRuleLookup = () => null): { gates: StatusGate[]; malformed: number } {
|
|
171
184
|
const { pending, resolutions, malformed } = parseGateLedger(content);
|
|
172
185
|
const gates: StatusGate[] = [];
|
|
173
186
|
for (const gate of [...new Set(pending.map((p) => p.gate))].sort()) {
|
|
@@ -181,7 +194,7 @@ export function gatesInLedger(component: string, content: string, envs: readonly
|
|
|
181
194
|
byEnv.set(env, [...(byEnv.get(env) ?? []), p]);
|
|
182
195
|
}
|
|
183
196
|
for (const env of [...byEnv.keys()].sort((a, b) => envs.indexOf(a ?? "") - envs.indexOf(b ?? ""))) {
|
|
184
|
-
gates.push(decide(component, latestPendingGate(byEnv.get(env)!, gate)!, resolutions, now));
|
|
197
|
+
gates.push(decide(component, latestPendingGate(byEnv.get(env)!, gate)!, resolutions, now, rules(gate)));
|
|
185
198
|
}
|
|
186
199
|
}
|
|
187
200
|
return { gates, malformed };
|
|
@@ -196,6 +209,7 @@ export async function readMemberGates(
|
|
|
196
209
|
cwd: string,
|
|
197
210
|
now: string,
|
|
198
211
|
read: GateLedgerReader = defaultGateLedgerReader,
|
|
212
|
+
rules: GateRuleLookup = () => null,
|
|
199
213
|
): Promise<{ gates: StatusGate[]; ledger: StatusGateLedger }> {
|
|
200
214
|
const ledger: StatusGateLedger = { layout, path: dir, shared: false, malformed: 0, reason: null };
|
|
201
215
|
if (commit === null) {
|
|
@@ -213,7 +227,7 @@ export async function readMemberGates(
|
|
|
213
227
|
}
|
|
214
228
|
const gates: StatusGate[] = [];
|
|
215
229
|
for (const component of [...files.keys()].sort()) {
|
|
216
|
-
const read = gatesInLedger(component, files.get(component)!, envs, now);
|
|
230
|
+
const read = gatesInLedger(component, files.get(component)!, envs, now, rules);
|
|
217
231
|
gates.push(...read.gates);
|
|
218
232
|
ledger.malformed += read.malformed;
|
|
219
233
|
}
|
|
@@ -31,6 +31,25 @@
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
|
+
"checkout": {
|
|
35
|
+
"description": "Added in contract 1 by #3160. The branch, head and base of the checkout status ran in. records --uncommitted --json lists the records that working tree holds modified or new against head.",
|
|
36
|
+
"type": "object",
|
|
37
|
+
"required": ["branch", "head", "base", "baseFrom"],
|
|
38
|
+
"properties": {
|
|
39
|
+
"branch": { "type": ["string", "null"], "description": "The checked-out branch's short name, such as chant/work/w-12, or null when HEAD is detached." },
|
|
40
|
+
"head": { "type": ["string", "null"], "pattern": "^[0-9a-f]{40,64}$", "description": "The full commit id HEAD names, or null before the first commit." },
|
|
41
|
+
"base": {
|
|
42
|
+
"type": ["string", "null"],
|
|
43
|
+
"pattern": "^[0-9a-f]{40,64}$",
|
|
44
|
+
"description": "The merge base of head and the target branch tip (origin/HEAD, else main, else master), the commit the branch forked from. Null without a head, a target, or common history."
|
|
45
|
+
},
|
|
46
|
+
"baseFrom": { "enum": ["origin/HEAD", "main", "master", null], "description": "Where the target branch was found; null when there is none." }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"plantable": {
|
|
50
|
+
"description": "Added in contract 1 by #3146. Whether a host such as a studio's planter can plant the workspace as one box: exactly one member's box block declares services. Not a check: a records-only or infra workspace is valid and simply not plantable.",
|
|
51
|
+
"$ref": "#/$defs/plantable"
|
|
52
|
+
},
|
|
34
53
|
"workspace": {
|
|
35
54
|
"type": "object",
|
|
36
55
|
"required": ["name", "root", "file"],
|
|
@@ -470,6 +489,14 @@
|
|
|
470
489
|
"type": "object",
|
|
471
490
|
"required": ["capabilities", "isolation", "intent", "services"],
|
|
472
491
|
"properties": {
|
|
492
|
+
"factory": {
|
|
493
|
+
"description": "Added in contract 1 by #3146. What the box builds and how (ws-077), with every default filled in, or null when the block declares no factory. At most one member's box has one.",
|
|
494
|
+
"oneOf": [{ "type": "null" }, { "$ref": "#/$defs/factory" }]
|
|
495
|
+
},
|
|
496
|
+
"listing": {
|
|
497
|
+
"description": "Added in contract 1 by #3146. What the box shows of itself on a home site, or null when the block declares no listing.",
|
|
498
|
+
"oneOf": [{ "type": "null" }, { "$ref": "#/$defs/listing" }]
|
|
499
|
+
},
|
|
473
500
|
"services": {
|
|
474
501
|
"description": "The services the box block declares (#2880), in declaration order, or empty when it declares none. A reader has them before any converge tick; a steward's ConvergeOp's lastTick carries each one's verdict. cmd is as declared: its ${VAR} references are left for the process that applies it to expand. Added by chant 0.95.0.",
|
|
475
502
|
"type": "array",
|
|
@@ -510,6 +537,91 @@
|
|
|
510
537
|
}
|
|
511
538
|
}
|
|
512
539
|
},
|
|
540
|
+
"plantable": {
|
|
541
|
+
"type": "object",
|
|
542
|
+
"required": ["plantable", "box", "reason"],
|
|
543
|
+
"properties": {
|
|
544
|
+
"plantable": { "type": "boolean" },
|
|
545
|
+
"box": { "type": ["string", "null"], "description": "The member whose box block declares services, or null when the workspace isn't plantable." },
|
|
546
|
+
"reason": {
|
|
547
|
+
"description": "Why the workspace isn't plantable, or null when it is.",
|
|
548
|
+
"oneOf": [
|
|
549
|
+
{ "type": "null" },
|
|
550
|
+
{
|
|
551
|
+
"type": "object",
|
|
552
|
+
"required": ["code", "message"],
|
|
553
|
+
"properties": {
|
|
554
|
+
"code": { "enum": ["box-none", "box-several"] },
|
|
555
|
+
"message": { "type": "string" }
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
]
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
"factory": {
|
|
563
|
+
"type": "object",
|
|
564
|
+
"required": ["builds", "check", "checks", "builders", "publish"],
|
|
565
|
+
"properties": {
|
|
566
|
+
"builds": { "type": "array", "minItems": 1, "items": { "type": "string" }, "description": "The members the factory builds, in declaration order. A work item a person asks for constrains member:<the first>." },
|
|
567
|
+
"check": {
|
|
568
|
+
"description": "The verdict command, run from the workspace root, and what it is; null when none is declared.",
|
|
569
|
+
"oneOf": [
|
|
570
|
+
{ "type": "null" },
|
|
571
|
+
{
|
|
572
|
+
"type": "object",
|
|
573
|
+
"required": ["run", "kind"],
|
|
574
|
+
"properties": {
|
|
575
|
+
"run": { "type": "string" },
|
|
576
|
+
"kind": { "enum": ["test", "build", "lint", "plan", "conformance"] }
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
]
|
|
580
|
+
},
|
|
581
|
+
"checks": { "type": ["string", "null"], "description": "Where a work item's builder writes its check, from the workspace root, or null for the orchestrator's choice." },
|
|
582
|
+
"builders": { "type": ["string", "null"], "description": "The member that declares the builder agents, or null." },
|
|
583
|
+
"publish": {
|
|
584
|
+
"description": "Where a finished build is published, or null when the factory doesn't publish past the box.",
|
|
585
|
+
"oneOf": [
|
|
586
|
+
{ "type": "null" },
|
|
587
|
+
{
|
|
588
|
+
"type": "object",
|
|
589
|
+
"required": ["forge", "repo", "base", "branchPrefix", "head"],
|
|
590
|
+
"properties": {
|
|
591
|
+
"forge": { "const": "github" },
|
|
592
|
+
"repo": { "type": "string", "description": "owner/name: where the pull request is opened." },
|
|
593
|
+
"base": { "type": ["string", "null"], "description": "The branch the pull request targets, or null for the repository's default branch." },
|
|
594
|
+
"branchPrefix": { "type": "string", "description": "What the published branch's name starts with, before the work item's id. chant/work/ unless declared." },
|
|
595
|
+
"head": { "type": ["string", "null"], "description": "owner/name of the fork the branch is pushed to, or null to push to repo." }
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
]
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
},
|
|
602
|
+
"listing": {
|
|
603
|
+
"type": "object",
|
|
604
|
+
"required": ["published", "title", "line", "cover"],
|
|
605
|
+
"properties": {
|
|
606
|
+
"published": { "type": "boolean", "description": "Whether the box is listed; true unless declared otherwise." },
|
|
607
|
+
"title": { "type": "string", "description": "Empty when not declared: a reader shows a name of its own." },
|
|
608
|
+
"line": { "type": "string", "description": "Empty when not declared." },
|
|
609
|
+
"cover": {
|
|
610
|
+
"description": "The cover image, or null when none is declared.",
|
|
611
|
+
"oneOf": [
|
|
612
|
+
{ "type": "null" },
|
|
613
|
+
{
|
|
614
|
+
"type": "object",
|
|
615
|
+
"required": ["path", "sha256"],
|
|
616
|
+
"properties": {
|
|
617
|
+
"path": { "type": "string", "description": "From the workspace root." },
|
|
618
|
+
"sha256": { "type": ["string", "null"], "pattern": "^[0-9a-f]{64}$", "description": "The sha256 of the file's bytes in the working tree, or null when it can't be read." }
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
]
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
},
|
|
513
625
|
"isolation": {
|
|
514
626
|
"type": "object",
|
|
515
627
|
"required": ["host", "slot", "portRange", "ports", "stateDir", "state", "cookies"],
|
|
@@ -571,7 +683,7 @@
|
|
|
571
683
|
},
|
|
572
684
|
"gate": {
|
|
573
685
|
"type": "object",
|
|
574
|
-
"required": ["component", "name", "env", "planDigest", "state", "recordedAt", "expiresAt", "approvals", "needed", "approve"],
|
|
686
|
+
"required": ["component", "name", "env", "planDigest", "state", "recordedAt", "expiresAt", "approvals", "needed", "signed", "approve"],
|
|
575
687
|
"properties": {
|
|
576
688
|
"component": { "type": "string", "description": "The ledger file's key: the component, or the op for a gate an Op run reached." },
|
|
577
689
|
"name": { "type": "string", "description": "The gate's name." },
|
|
@@ -600,7 +712,20 @@
|
|
|
600
712
|
}
|
|
601
713
|
},
|
|
602
714
|
"needed": { "type": "integer", "minimum": 1, "description": "The human approvals the gate needs: its quorum's count, 1 without one." },
|
|
603
|
-
"
|
|
715
|
+
"signed": {
|
|
716
|
+
"description": "Added in contract 1 by #3163. The declaration's rule for the gate, read at base (identity.gates, ws-080): only an approval sealed with chant approve --sign by a key the signers file at base lists for its approver counts, and when class is not null, only one whose approver the role grants at base put in that class. Approvals that don't meet it are left out of approvals and state. Null when any approval counts.",
|
|
717
|
+
"oneOf": [
|
|
718
|
+
{ "type": "null" },
|
|
719
|
+
{
|
|
720
|
+
"type": "object",
|
|
721
|
+
"required": ["class"],
|
|
722
|
+
"properties": {
|
|
723
|
+
"class": { "type": ["string", "null"], "description": "The principal class the signer must be in, or null for any listed signer." }
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
]
|
|
727
|
+
},
|
|
728
|
+
"approve": { "type": "string", "description": "The chant approve line for the gate, run in the member's directory: chant approve <component> <gate>, with --env <env> when env is not null and --plan <planDigest> when planDigest is not null (#2832), so the line approves the plan this read showed, and --sign when signed is not null (#3163). The caller adds --actor <principal>." }
|
|
604
729
|
}
|
|
605
730
|
},
|
|
606
731
|
"environment": {
|
package/src/workspace/status.ts
CHANGED
|
@@ -41,11 +41,15 @@ import { readPathSha } from "../lifecycle/git";
|
|
|
41
41
|
import { latestPerComponent, readReleaseLedger, type ReleaseRecord } from "../lifecycle/release-ledger";
|
|
42
42
|
import { readReleasePlan, type ReleasePlan } from "../lifecycle/plan-ledger";
|
|
43
43
|
import { listWorkLeases, type WorkLeaseState } from "../lifecycle/work-lease";
|
|
44
|
+
import { readCheckoutHead, type CheckoutHead } from "./records-checkout";
|
|
44
45
|
import { findWorkspaceRoot } from "../project-root";
|
|
45
46
|
import { resolveBoxes, type ResolvedIsolation } from "./box-isolation";
|
|
46
47
|
import { resolveBoxIntents, unresolvedIntent, type BoxIntent } from "./box-intent";
|
|
48
|
+
import { factoryView, listingView, plantability, treeBytes, type FactoryView, type ListingView, type Plantable } from "./box-factory";
|
|
47
49
|
import { declaredRecordKinds, readDeclaration, readerVersion, WorkspaceReadError, type Declaration, type ErrorLocation, type Member } from "./declaration";
|
|
48
50
|
import type { ReasonCode } from "./reason-codes";
|
|
51
|
+
import { gateAdmissionFrom } from "./identity";
|
|
52
|
+
import { scopeSource } from "./write-scope";
|
|
49
53
|
import { GATE_REASON_CODES, readMemberGates, type GateLedgerReader, type StatusGate, type StatusGateLedger } from "./status-gates";
|
|
50
54
|
import { STEWARD_REASON_CODES, readMemberStewards, type StatusSteward, type StewardReasonCode } from "./status-stewards";
|
|
51
55
|
import { gitTop, workingTree } from "./tree";
|
|
@@ -188,6 +192,10 @@ export interface StatusBox {
|
|
|
188
192
|
intent: BoxIntent | null;
|
|
189
193
|
/** The services the block declares (#2880), in file order, so a reader has them before any converge tick. Empty when it declares none. */
|
|
190
194
|
services: StatusBoxService[];
|
|
195
|
+
/** What the box builds and how, with defaults filled in (#3146, ws-077), or null when the block declares no factory. */
|
|
196
|
+
factory: FactoryView | null;
|
|
197
|
+
/** What the box shows of itself on a home site, with the cover's sha256 (#3146), or null when the block declares no listing. */
|
|
198
|
+
listing: ListingView | null;
|
|
191
199
|
}
|
|
192
200
|
|
|
193
201
|
/** A declared box service as `status --json` prints it (#2880): every field present, null or false when not declared. */
|
|
@@ -234,7 +242,11 @@ export type StatusDocument =
|
|
|
234
242
|
env: string;
|
|
235
243
|
compareTo: string | null;
|
|
236
244
|
lifecycle: { ref: string; commit: string | null };
|
|
245
|
+
/** The branch, head and base of the checkout read (#3160). `records --uncommitted` lists the records it holds uncommitted. */
|
|
246
|
+
checkout: CheckoutHead;
|
|
237
247
|
workspace: { name: string; root: string; file: string };
|
|
248
|
+
/** Whether a host can plant the workspace as one box (#3146): exactly one member's box block declares services. */
|
|
249
|
+
plantable: Plantable;
|
|
238
250
|
members: StatusMember[];
|
|
239
251
|
/** Every work lease in the workspace's ledgers, active and expired, by member then item (#2732). A released lease has no ref and is not listed. */
|
|
240
252
|
leases: StatusLease[];
|
|
@@ -399,12 +411,16 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
399
411
|
|
|
400
412
|
const isolation = new Map(resolveBoxes(declaration).map((b) => [b.member.name, b.isolation]));
|
|
401
413
|
const intents = new Map((await resolveBoxIntents(declaration, found.dir)).map((i) => [i.member, i.record?.intent ?? unresolvedIntent(i.id)]));
|
|
414
|
+
const coverBytes = treeBytes(workingTree(found.dir));
|
|
415
|
+
// #3163: the declaration's identity.gates at base, read once for every member's gates.
|
|
416
|
+
const ruleSource = scopeSource(found.dir);
|
|
417
|
+
const rules = (gate: string) => gateAdmissionFrom(ruleSource, gate);
|
|
402
418
|
const members: StatusMember[] = [];
|
|
403
419
|
for (const m of declaration.members) {
|
|
404
420
|
const environments: StatusEnvironment[] = [];
|
|
405
421
|
for (const env of envs) environments.push(await readEnvironment(m, env, found.dir, read));
|
|
406
422
|
const own = await hasMemberLedger(m, found.dir);
|
|
407
|
-
const gates = await readMemberGates(own ? `${MEMBERS_DIR}/${m.name}/${GATES_DIR}` : GATES_DIR, own ? "members" : "flat", commit, envs, found.dir, now, query.readGates);
|
|
423
|
+
const gates = await readMemberGates(own ? `${MEMBERS_DIR}/${m.name}/${GATES_DIR}` : GATES_DIR, own ? "members" : "flat", commit, envs, found.dir, now, query.readGates, rules);
|
|
408
424
|
const stewards = await (query.readStewards ?? readMemberStewards)(resolve(found.dir, m.dir), query.env, now, m.kind, m.box);
|
|
409
425
|
members.push({
|
|
410
426
|
name: m.name,
|
|
@@ -431,6 +447,8 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
431
447
|
health: s.health,
|
|
432
448
|
optional: s.optional,
|
|
433
449
|
})),
|
|
450
|
+
factory: factoryView(m.box.factory),
|
|
451
|
+
listing: listingView(m.box.listing, coverBytes),
|
|
434
452
|
},
|
|
435
453
|
stewards: stewards.stewards,
|
|
436
454
|
stewardReasons: stewards.reasons,
|
|
@@ -451,7 +469,9 @@ export async function workspaceStatus(query: StatusQuery): Promise<StatusDocumen
|
|
|
451
469
|
env: query.env,
|
|
452
470
|
compareTo: query.compareTo ?? null,
|
|
453
471
|
lifecycle: { ref: LIFECYCLE_REF, commit },
|
|
472
|
+
checkout: readCheckoutHead(top),
|
|
454
473
|
workspace: { name: declaration.name, root: rootDir === "" ? "." : rootDir, file: declaration.file },
|
|
474
|
+
plantable: plantability(declaration),
|
|
455
475
|
members,
|
|
456
476
|
leases,
|
|
457
477
|
acceptance,
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Symbol regions for the intent walk (#3034): `graph --intent <path>#<symbol>`
|
|
3
|
+
* names a function, class, method, type or constant instead of a line range,
|
|
4
|
+
* because a line range means something else once the file is edited.
|
|
5
|
+
*
|
|
6
|
+
* A resolver turns a symbol into its current lines in the tree read, and the
|
|
7
|
+
* walk follows those lines through history as it follows any line range
|
|
8
|
+
* (`git log -L`). Resolvers are chosen by file extension. Core ships one for
|
|
9
|
+
* TypeScript and JavaScript, parsed with the TypeScript compiler chant already
|
|
10
|
+
* depends on; a file with no resolver is refused with
|
|
11
|
+
* `intent-symbol-unsupported`, and a line range still works for it.
|
|
12
|
+
*
|
|
13
|
+
* A symbol is a name, or a dotted path through classes, namespaces,
|
|
14
|
+
* interfaces, enums and object literals bound to a constant, such as
|
|
15
|
+
* `createApp` or `Server.listen`. A bare name is looked up among the
|
|
16
|
+
* file's top-level declarations first, then among every nested one; when it
|
|
17
|
+
* matches more than one declaration it is refused as ambiguous, with the
|
|
18
|
+
* qualified names that would pick one. The lines run from the declaration's
|
|
19
|
+
* doc comment, when it has one, to its last line.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import * as ts from "typescript";
|
|
23
|
+
import type { LineRange } from "./intent";
|
|
24
|
+
|
|
25
|
+
/** One declaration a resolver found. */
|
|
26
|
+
export interface SymbolDeclaration {
|
|
27
|
+
/** The dotted path from the file's top level, such as `Server.listen`. */
|
|
28
|
+
qualified: string;
|
|
29
|
+
/** What declares it: function, class, method, property, interface, type, enum, namespace, variable, member. */
|
|
30
|
+
kind: string;
|
|
31
|
+
lines: LineRange;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type SymbolResolution =
|
|
35
|
+
| { ok: true; declaration: SymbolDeclaration }
|
|
36
|
+
| { ok: false; reason: "unsupported" | "unknown" | "ambiguous"; message: string; candidates: string[] };
|
|
37
|
+
|
|
38
|
+
/** A language-aware resolver: every declaration in a file, by qualified name. */
|
|
39
|
+
export interface SymbolResolver {
|
|
40
|
+
/** The language it reads, for messages. */
|
|
41
|
+
language: string;
|
|
42
|
+
extensions: readonly string[];
|
|
43
|
+
declarations(path: string, text: string): SymbolDeclaration[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ── TypeScript and JavaScript ────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
function scriptKind(path: string): ts.ScriptKind {
|
|
49
|
+
if (/\.tsx$/.test(path)) return ts.ScriptKind.TSX;
|
|
50
|
+
if (/\.jsx$/.test(path)) return ts.ScriptKind.JSX;
|
|
51
|
+
if (/\.[cm]?js$/.test(path)) return ts.ScriptKind.JS;
|
|
52
|
+
return ts.ScriptKind.TS;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function nameText(name: ts.PropertyName | ts.BindingName | ts.ModuleName | undefined): string | undefined {
|
|
56
|
+
if (!name) return undefined;
|
|
57
|
+
if (ts.isIdentifier(name) || ts.isPrivateIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) return name.text;
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const typescriptResolver: SymbolResolver = {
|
|
62
|
+
language: "TypeScript and JavaScript",
|
|
63
|
+
extensions: [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"],
|
|
64
|
+
declarations(path, text) {
|
|
65
|
+
const sf = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, scriptKind(path));
|
|
66
|
+
const out: SymbolDeclaration[] = [];
|
|
67
|
+
const lineOf = (pos: number) => sf.getLineAndCharacterOfPosition(pos).line + 1;
|
|
68
|
+
// The span: from the doc comment (or the statement holding a lone variable) to the node's end.
|
|
69
|
+
const push = (qualified: string, kind: string, node: ts.Node) => {
|
|
70
|
+
out.push({ qualified, kind, lines: { start: lineOf(node.getStart(sf, true)), end: lineOf(node.getEnd()) } });
|
|
71
|
+
};
|
|
72
|
+
const visitMembers = (prefix: string, members: ts.NodeArray<ts.Node>) => {
|
|
73
|
+
for (const m of members) {
|
|
74
|
+
const name = nameText((m as ts.NamedDeclaration).name as ts.PropertyName | undefined);
|
|
75
|
+
if (name === undefined) {
|
|
76
|
+
if (ts.isConstructorDeclaration(m)) push(`${prefix}.constructor`, "method", m);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const q = `${prefix}.${name}`;
|
|
80
|
+
if (ts.isMethodDeclaration(m) || ts.isMethodSignature(m) || ts.isGetAccessorDeclaration(m) || ts.isSetAccessorDeclaration(m)) push(q, "method", m);
|
|
81
|
+
else if (ts.isPropertyDeclaration(m) || ts.isPropertySignature(m)) {
|
|
82
|
+
push(q, "property", m);
|
|
83
|
+
if (ts.isPropertyDeclaration(m) && m.initializer) visitValue(q, m.initializer);
|
|
84
|
+
} else if (ts.isEnumMember(m)) push(q, "member", m);
|
|
85
|
+
else if (ts.isPropertyAssignment(m)) {
|
|
86
|
+
push(q, "property", m);
|
|
87
|
+
visitValue(q, m.initializer);
|
|
88
|
+
} else if (ts.isShorthandPropertyAssignment(m)) push(q, "property", m);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
// A class expression or object literal bound to a name has members of its own.
|
|
92
|
+
const visitValue = (prefix: string, value: ts.Expression) => {
|
|
93
|
+
let v = value;
|
|
94
|
+
while (ts.isParenthesizedExpression(v) || ts.isAsExpression(v) || ts.isSatisfiesExpression(v)) v = v.expression;
|
|
95
|
+
if (ts.isClassExpression(v)) visitMembers(prefix, v.members);
|
|
96
|
+
else if (ts.isObjectLiteralExpression(v)) visitMembers(prefix, v.properties);
|
|
97
|
+
};
|
|
98
|
+
const visitStatements = (prefix: string, statements: ts.NodeArray<ts.Statement>) => {
|
|
99
|
+
const q = (name: string) => (prefix ? `${prefix}.${name}` : name);
|
|
100
|
+
for (const s of statements) {
|
|
101
|
+
if (ts.isFunctionDeclaration(s) && s.name) push(q(s.name.text), "function", s);
|
|
102
|
+
else if (ts.isClassDeclaration(s) && s.name) {
|
|
103
|
+
push(q(s.name.text), "class", s);
|
|
104
|
+
visitMembers(q(s.name.text), s.members);
|
|
105
|
+
} else if (ts.isInterfaceDeclaration(s)) {
|
|
106
|
+
push(q(s.name.text), "interface", s);
|
|
107
|
+
visitMembers(q(s.name.text), s.members);
|
|
108
|
+
} else if (ts.isTypeAliasDeclaration(s)) push(q(s.name.text), "type", s);
|
|
109
|
+
else if (ts.isEnumDeclaration(s)) {
|
|
110
|
+
push(q(s.name.text), "enum", s);
|
|
111
|
+
visitMembers(q(s.name.text), s.members);
|
|
112
|
+
} else if (ts.isModuleDeclaration(s)) {
|
|
113
|
+
const name = nameText(s.name);
|
|
114
|
+
if (name === undefined) continue;
|
|
115
|
+
push(q(name), "namespace", s);
|
|
116
|
+
let body = s.body;
|
|
117
|
+
let inner = q(name);
|
|
118
|
+
while (body && ts.isModuleDeclaration(body)) {
|
|
119
|
+
inner = `${inner}.${nameText(body.name)}`;
|
|
120
|
+
body = body.body;
|
|
121
|
+
}
|
|
122
|
+
if (body && ts.isModuleBlock(body)) visitStatements(inner, body.statements);
|
|
123
|
+
} else if (ts.isVariableStatement(s)) {
|
|
124
|
+
const lone = s.declarationList.declarations.length === 1;
|
|
125
|
+
for (const d of s.declarationList.declarations) {
|
|
126
|
+
const name = nameText(d.name);
|
|
127
|
+
if (name === undefined) continue;
|
|
128
|
+
// A lone declaration takes its statement's lines, so `export const x = ...` and its doc comment are in the region.
|
|
129
|
+
push(q(name), "variable", lone ? s : d);
|
|
130
|
+
if (d.initializer) visitValue(q(name), d.initializer);
|
|
131
|
+
}
|
|
132
|
+
} else if (ts.isExportAssignment(s) && !s.isExportEquals) {
|
|
133
|
+
push(q("default"), "variable", s);
|
|
134
|
+
visitValue(q("default"), s.expression);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
visitStatements("", sf.statements);
|
|
139
|
+
return out;
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** The resolvers core ships, chosen by the file's extension. */
|
|
144
|
+
export const SYMBOL_RESOLVERS: readonly SymbolResolver[] = [typescriptResolver];
|
|
145
|
+
|
|
146
|
+
/** The resolver for a path, or undefined when none reads its language. */
|
|
147
|
+
export function symbolResolverFor(path: string): SymbolResolver | undefined {
|
|
148
|
+
const lower = path.toLowerCase();
|
|
149
|
+
return SYMBOL_RESOLVERS.find((r) => r.extensions.some((e) => lower.endsWith(e)));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Resolve `symbol` in the file `path` holding `text`: its current lines, or why it can't be. */
|
|
153
|
+
export function resolveSymbol(path: string, text: string, symbol: string): SymbolResolution {
|
|
154
|
+
const resolver = symbolResolverFor(path);
|
|
155
|
+
if (!resolver) {
|
|
156
|
+
const known = SYMBOL_RESOLVERS.flatMap((r) => r.extensions).join(", ");
|
|
157
|
+
return { ok: false, reason: "unsupported", message: `${path} has no symbol resolver: symbols resolve in ${known} files. Give a line range, ${path}:<start>-<end>, instead`, candidates: [] };
|
|
158
|
+
}
|
|
159
|
+
const all = resolver.declarations(path, text);
|
|
160
|
+
const exact = all.filter((d) => d.qualified === symbol);
|
|
161
|
+
// A name declared twice at one path, such as an overload or a merged declaration, is one region from the first to the last.
|
|
162
|
+
if (exact.length > 0) {
|
|
163
|
+
const start = Math.min(...exact.map((d) => d.lines.start));
|
|
164
|
+
const end = Math.max(...exact.map((d) => d.lines.end));
|
|
165
|
+
return { ok: true, declaration: { qualified: symbol, kind: exact[0].kind, lines: { start, end } } };
|
|
166
|
+
}
|
|
167
|
+
const nested = [...new Set(all.filter((d) => d.qualified.endsWith(`.${symbol}`)).map((d) => d.qualified))];
|
|
168
|
+
if (nested.length === 1) return resolveSymbol(path, text, nested[0]);
|
|
169
|
+
if (nested.length > 1) {
|
|
170
|
+
return { ok: false, reason: "ambiguous", message: `${symbol} names ${nested.length} declarations in ${path}: ${nested.join(", ")}. Give the qualified name`, candidates: nested };
|
|
171
|
+
}
|
|
172
|
+
const top = [...new Set(all.filter((d) => !d.qualified.includes(".")).map((d) => d.qualified))];
|
|
173
|
+
const shown = top.slice(0, 20);
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
reason: "unknown",
|
|
177
|
+
message: `${path} declares no ${symbol}${top.length > 0 ? `; its top-level declarations are ${shown.join(", ")}${top.length > shown.length ? `, and ${top.length - shown.length} more` : ""}` : ", and no top-level declaration a resolver reads"}`,
|
|
178
|
+
candidates: shown,
|
|
179
|
+
};
|
|
180
|
+
}
|