@intentius/chant 0.100.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/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/builder.d.ts.map +1 -1
- package/dist/cli/build-options.d.ts +2 -0
- package/dist/cli/build-options.d.ts.map +1 -1
- package/dist/cli/commands/build.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/fan-out.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 +9 -1
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +33 -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 +30 -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 +78 -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-digest.d.ts +26 -5
- package/dist/lifecycle/plan-digest.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 +4 -4
- 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 +12 -11
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- 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/activities/propose-upgrade.d.ts +2 -0
- package/dist/op/activities/propose-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 +3 -1
- 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/serializer.d.ts +8 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/telemetry-attribution.d.ts +77 -0
- package/dist/telemetry-attribution.d.ts.map +1 -0
- 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 +88 -0
- package/dist/workspace/agent-cli.d.ts.map +1 -0
- package/dist/workspace/box-factory.d.ts +73 -0
- package/dist/workspace/box-factory.d.ts.map +1 -0
- package/dist/workspace/changes-cli.d.ts.map +1 -1
- package/dist/workspace/changes.d.ts +8 -1
- 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/links.d.ts +1 -0
- package/dist/workspace/checks/links.d.ts.map +1 -1
- package/dist/workspace/checks/live.d.ts +40 -0
- package/dist/workspace/checks/live.d.ts.map +1 -0
- package/dist/workspace/checks.d.ts +26 -2
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +73 -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 +119 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +368 -4
- package/dist/workspace/export-cli.d.ts +12 -0
- package/dist/workspace/export-cli.d.ts.map +1 -0
- package/dist/workspace/export.d.ts +145 -0
- package/dist/workspace/export.d.ts.map +1 -0
- 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/import.d.ts +73 -0
- package/dist/workspace/import.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 +24 -6
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lineage-adopt-cli.d.ts +15 -0
- package/dist/workspace/lineage-adopt-cli.d.ts.map +1 -0
- package/dist/workspace/lineage-adopt.d.ts +106 -0
- package/dist/workspace/lineage-adopt.d.ts.map +1 -0
- package/dist/workspace/lineage-check.d.ts +9 -2
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-cli.d.ts +6 -1
- package/dist/workspace/lineage-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-hash-index.d.ts +110 -0
- package/dist/workspace/lineage-hash-index.d.ts.map +1 -0
- package/dist/workspace/lineage-init.d.ts +10 -0
- package/dist/workspace/lineage-init.d.ts.map +1 -1
- package/dist/workspace/lineage-lock.d.ts +149 -2
- package/dist/workspace/lineage-lock.d.ts.map +1 -1
- package/dist/workspace/lineage-migrations.d.ts +15 -3
- package/dist/workspace/lineage-migrations.d.ts.map +1 -1
- package/dist/workspace/lineage-provenance.d.ts +18 -0
- package/dist/workspace/lineage-provenance.d.ts.map +1 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts +2 -0
- package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts +30 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/lineage-versions.d.ts +116 -0
- package/dist/workspace/lineage-versions.d.ts.map +1 -0
- package/dist/workspace/links.d.ts +31 -5
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/ls-generated.d.ts +37 -0
- package/dist/workspace/ls-generated.d.ts.map +1 -0
- package/dist/workspace/ls.d.ts +4 -0
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/member-commands.d.ts.map +1 -1
- package/dist/workspace/nested-graph.d.ts +56 -0
- package/dist/workspace/nested-graph.d.ts.map +1 -0
- package/dist/workspace/nesting.d.ts +21 -0
- package/dist/workspace/nesting.d.ts.map +1 -0
- package/dist/workspace/pin-cli.d.ts +10 -0
- package/dist/workspace/pin-cli.d.ts.map +1 -0
- package/dist/workspace/pin-integrity.d.ts +51 -0
- package/dist/workspace/pin-integrity.d.ts.map +1 -0
- 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 +47 -3
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-assets.d.ts.map +1 -1
- package/dist/workspace/record-sessions.d.ts +7 -11
- package/dist/workspace/record-sessions.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 +57 -2
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +5 -2
- 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 +23 -4
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +58 -5
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/returns.d.ts +129 -0
- package/dist/workspace/returns.d.ts.map +1 -0
- 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/template-manifest.d.ts +11 -3
- package/dist/workspace/template-manifest.d.ts.map +1 -1
- 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 +8 -0
- package/dist/workspace/trust/attestor.d.ts.map +1 -1
- package/dist/workspace/trust/dsse.d.ts +58 -0
- package/dist/workspace/trust/dsse.d.ts.map +1 -0
- package/dist/workspace/trust/evidence-cli.d.ts +66 -0
- package/dist/workspace/trust/evidence-cli.d.ts.map +1 -0
- package/dist/workspace/trust/evidence.d.ts +93 -0
- package/dist/workspace/trust/evidence.d.ts.map +1 -0
- package/dist/workspace/trust/policy.d.ts +54 -1
- package/dist/workspace/trust/policy.d.ts.map +1 -1
- package/dist/workspace/trust/provenance.d.ts +21 -1
- package/dist/workspace/trust/provenance.d.ts.map +1 -1
- package/dist/workspace/trust/rotation.d.ts +132 -0
- package/dist/workspace/trust/rotation.d.ts.map +1 -0
- package/dist/workspace/trust/seal.d.ts +33 -0
- package/dist/workspace/trust/seal.d.ts.map +1 -1
- package/dist/workspace/trust/signers-cli.d.ts +49 -0
- package/dist/workspace/trust/signers-cli.d.ts.map +1 -0
- package/dist/workspace/trust/ssh-commit.d.ts +12 -0
- package/dist/workspace/trust/ssh-commit.d.ts.map +1 -1
- package/dist/workspace/trust/test-repo.d.ts +13 -0
- package/dist/workspace/trust/test-repo.d.ts.map +1 -1
- package/dist/workspace/trust/verify.d.ts +15 -0
- package/dist/workspace/trust/verify.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 +259 -0
- package/dist/workspace/write-scope.d.ts.map +1 -0
- 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/build.ts +8 -0
- package/src/cli/build-options.ts +4 -1
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
- package/src/cli/commands/build.ts +2 -0
- 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 +87 -1
- package/src/cli/commands/import-parser-options.test.ts +133 -0
- package/src/cli/commands/import.ts +69 -25
- 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/fan-out.test.ts +6 -6
- package/src/cli/handlers/fan-out.ts +2 -1
- package/src/cli/handlers/graph.test.ts +42 -0
- package/src/cli/handlers/graph.ts +22 -0
- 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 +88 -4
- 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 +33 -0
- package/src/cli/main.ts +168 -17
- package/src/cli/mcp/workspace-tools.test.ts +5 -2
- package/src/cli/mcp/workspace-tools.ts +62 -4
- package/src/cli/registry.ts +33 -2
- package/src/cli/serve-mcp-workspace.test.ts +1 -1
- 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/release-wiring.test.ts +5 -1
- package/src/codegen/testdata/postgres-tags.json +242 -0
- package/src/components/fan-out-output.test.ts +1 -1
- package/src/components/fan-out.test.ts +1 -1
- package/src/components/promote.test.ts +1 -1
- package/src/config.test.ts +21 -0
- package/src/config.ts +27 -0
- package/src/content-digest.test.ts +2 -2
- 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 +79 -3
- package/src/lifecycle/gate-ledger.test.ts +14 -0
- package/src/lifecycle/gate-ledger.ts +18 -1
- package/src/lifecycle/observe.test.ts +18 -0
- package/src/lifecycle/observe.ts +6 -4
- package/src/lifecycle/plan-digest.test.ts +54 -3
- package/src/lifecycle/plan-digest.ts +38 -8
- 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/activity-contracts.ts +1 -0
- 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/activities/propose-upgrade.ts +8 -5
- 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 +91 -0
- package/src/op/gate-approval.ts +5 -1
- package/src/op/gate.ts +34 -8
- package/src/op/index.ts +3 -1
- 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/serializer.ts +9 -0
- package/src/telemetry-attribution.test.ts +91 -0
- package/src/telemetry-attribution.ts +145 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
- package/src/workspace/agent-cli.ts +138 -0
- package/src/workspace/agent.schema.json +378 -0
- package/src/workspace/behold-kinds.test.ts +1 -1
- package/src/workspace/box-factory.test.ts +320 -0
- package/src/workspace/box-factory.ts +103 -0
- package/src/workspace/changes-cli.ts +5 -0
- package/src/workspace/changes.schema.json +171 -1
- package/src/workspace/changes.ts +61 -4
- package/src/workspace/check-live.test.ts +192 -0
- package/src/workspace/check.schema.json +64 -0
- package/src/workspace/checks/boxes.test.ts +2 -0
- package/src/workspace/checks/links.ts +23 -2
- package/src/workspace/checks/live.ts +113 -0
- package/src/workspace/checks.test.ts +2 -2
- package/src/workspace/checks.ts +49 -8
- package/src/workspace/compose-graph.test.ts +47 -0
- package/src/workspace/compose-graph.ts +127 -3
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +7 -1
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +5 -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 +368 -4
- package/src/workspace/declaration.ts +270 -1
- package/src/workspace/declared-kinds.test.ts +33 -0
- package/src/workspace/evidence.schema.json +279 -0
- package/src/workspace/export-cli.ts +180 -0
- package/src/workspace/export-import.test.ts +282 -0
- package/src/workspace/export.ts +486 -0
- package/src/workspace/graph-cli.ts +51 -2
- package/src/workspace/graph-contract.test.ts +89 -4
- package/src/workspace/graph.schema.json +453 -1
- package/src/workspace/identity.test.ts +196 -0
- package/src/workspace/identity.ts +186 -0
- package/src/workspace/import.ts +325 -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.test.ts +5 -5
- package/src/workspace/kinds.ts +62 -33
- package/src/workspace/lineage-adopt-cli.ts +103 -0
- package/src/workspace/lineage-adopt.test.ts +552 -0
- package/src/workspace/lineage-adopt.ts +452 -0
- package/src/workspace/lineage-check.ts +26 -5
- package/src/workspace/lineage-cli.ts +12 -2
- package/src/workspace/lineage-hash-index.ts +305 -0
- package/src/workspace/lineage-init.test.ts +9 -0
- package/src/workspace/lineage-init.ts +29 -9
- package/src/workspace/lineage-lock.ts +54 -0
- package/src/workspace/lineage-migrations.test.ts +27 -0
- package/src/workspace/lineage-migrations.ts +44 -14
- package/src/workspace/lineage-provenance.ts +40 -0
- package/src/workspace/lineage-upgrade-cli.ts +9 -5
- package/src/workspace/lineage-upgrade.test.ts +92 -1
- package/src/workspace/lineage-upgrade.ts +111 -16
- package/src/workspace/lineage-versions.ts +348 -0
- package/src/workspace/links.test.ts +121 -2
- package/src/workspace/links.ts +97 -6
- package/src/workspace/ls-contract.test.ts +84 -1
- package/src/workspace/ls-generated.ts +111 -0
- package/src/workspace/ls.schema.json +19 -0
- package/src/workspace/ls.ts +8 -1
- package/src/workspace/member-commands.ts +3 -1
- package/src/workspace/nested-graph.test.ts +176 -0
- package/src/workspace/nested-graph.ts +169 -0
- package/src/workspace/nesting.ts +37 -0
- package/src/workspace/pin-cli.test.ts +71 -0
- package/src/workspace/pin-cli.ts +57 -0
- package/src/workspace/pin-integrity.test.ts +121 -0
- package/src/workspace/pin-integrity.ts +104 -0
- package/src/workspace/points-write.schema.json +2 -0
- package/src/workspace/principal-classes.test.ts +175 -0
- package/src/workspace/principal-classes.ts +307 -0
- package/src/workspace/read-contract.test.ts +24 -0
- package/src/workspace/reason-codes.test.ts +20 -2
- package/src/workspace/reason-codes.ts +53 -3
- package/src/workspace/record-sessions.ts +12 -14
- package/src/workspace/records-amend.schema.json +6 -0
- package/src/workspace/records-checkout.ts +106 -0
- package/src/workspace/records-cli.ts +195 -22
- package/src/workspace/records-close.schema.json +7 -2
- package/src/workspace/records-close.ts +10 -2
- package/src/workspace/records-formats.test.ts +13 -5
- package/src/workspace/records-new.schema.json +6 -0
- package/src/workspace/records-review.schema.json +6 -0
- package/src/workspace/records-sessions.test.ts +23 -6
- package/src/workspace/records-uncommitted.test.ts +171 -0
- package/src/workspace/records-write.test.ts +65 -8
- package/src/workspace/records-write.ts +92 -6
- package/src/workspace/records.schema.json +127 -4
- package/src/workspace/records.ts +123 -5
- package/src/workspace/returns.ts +328 -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/signers.schema.json +206 -0
- package/src/workspace/status-contract.test.ts +5 -0
- package/src/workspace/status-gates.ts +24 -9
- 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/template-manifest.ts +22 -4
- 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/attestor.ts +15 -0
- package/src/workspace/trust/dsse.ts +134 -0
- package/src/workspace/trust/evidence-cli.ts +195 -0
- package/src/workspace/trust/evidence.test.ts +241 -0
- package/src/workspace/trust/evidence.ts +207 -0
- package/src/workspace/trust/policy.ts +110 -3
- package/src/workspace/trust/provenance.ts +41 -4
- package/src/workspace/trust/record-seal.test.ts +1 -1
- package/src/workspace/trust/rotation.test.ts +258 -0
- package/src/workspace/trust/rotation.ts +336 -0
- package/src/workspace/trust/seal.ts +63 -1
- package/src/workspace/trust/signers-cli.ts +178 -0
- package/src/workspace/trust/ssh-commit.ts +53 -4
- package/src/workspace/trust/test-repo.ts +18 -0
- package/src/workspace/trust/trust.test.ts +8 -2
- package/src/workspace/trust/verify-cli.ts +1 -0
- package/src/workspace/trust/verify.ts +22 -0
- 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 +6 -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 +466 -0
- package/src/workspace/write-scope.ts +573 -0
- package/src/yaml.test.ts +111 -1
- package/src/yaml.ts +141 -549
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The checkout a working-tree read is made in, and which records in it are
|
|
3
|
+
* uncommitted (#3160).
|
|
4
|
+
*
|
|
5
|
+
* Work in progress lives as uncommitted files in a work branch's working tree
|
|
6
|
+
* (#3158, #3145). A reader such as hud or studio shows "kept, not yet
|
|
7
|
+
* applied" from `records --json` alone: each record says whether it is
|
|
8
|
+
* committed, modified or new against `HEAD`, and the document names the
|
|
9
|
+
* branch, the commit `HEAD` names and the base that branch forked from. The
|
|
10
|
+
* reader never diffs git itself.
|
|
11
|
+
*
|
|
12
|
+
* Every git call here reads. None takes the index lock or refreshes the
|
|
13
|
+
* index (`--no-optional-locks`), so a read leaves the checkout as it was,
|
|
14
|
+
* `.git` included, which the reader conformance suite checks.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { execFileSync } from "node:child_process";
|
|
18
|
+
import { resolveBase, type BaseSource } from "./trust/provenance";
|
|
19
|
+
|
|
20
|
+
/** Where a record's file stands against `HEAD`. */
|
|
21
|
+
export type WorktreeState = "committed" | "modified" | "new";
|
|
22
|
+
|
|
23
|
+
/** The branch, head and base of a checkout (#3160). `records` and `status` both print it. */
|
|
24
|
+
export interface CheckoutHead {
|
|
25
|
+
/** The checked-out branch's short name, such as `chant/work/w-12`, or null when `HEAD` is detached. */
|
|
26
|
+
branch: string | null;
|
|
27
|
+
/** The commit `HEAD` names, or null before the first commit. Each record's `worktree` is judged against it. */
|
|
28
|
+
head: string | null;
|
|
29
|
+
/** The merge base of `head` and the target branch tip: the commit the branch forked from. Null without a head, a target, or common history. */
|
|
30
|
+
base: string | null;
|
|
31
|
+
/** Where the target branch was found: `flag` for `--base`, else `origin/HEAD`, `main` or `master`. Null when there is none. */
|
|
32
|
+
baseFrom: BaseSource | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** {@link CheckoutHead} for one record kind, with the record files `HEAD` has and the working tree does not. */
|
|
36
|
+
export interface CheckoutView extends CheckoutHead {
|
|
37
|
+
/** Record files of the kind at `head` that the working tree no longer has, from the repository root, sorted. */
|
|
38
|
+
deleted: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function git(top: string, args: string[]): string {
|
|
42
|
+
return execFileSync("git", ["--no-optional-locks", "--literal-pathspecs", ...args], {
|
|
43
|
+
cwd: top,
|
|
44
|
+
encoding: "utf-8",
|
|
45
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
46
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function tryGit(top: string, args: string[]): string | undefined {
|
|
51
|
+
try {
|
|
52
|
+
return git(top, args);
|
|
53
|
+
} catch {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The branch, head and base of the checkout at `top`. `explicitBase` is
|
|
60
|
+
* `--base`, as the trust policy takes it; otherwise the target branch is the
|
|
61
|
+
* one the policy is read at (`origin/HEAD`, then `main`, then `master`).
|
|
62
|
+
*/
|
|
63
|
+
export function readCheckoutHead(top: string, explicitBase?: string): CheckoutHead {
|
|
64
|
+
const branch = tryGit(top, ["symbolic-ref", "--quiet", "--short", "HEAD"])?.trim() || null;
|
|
65
|
+
const head = tryGit(top, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])?.trim() || null;
|
|
66
|
+
const target = resolveBase(top, explicitBase);
|
|
67
|
+
const base = head && target.commit ? tryGit(top, ["merge-base", head, target.commit])?.trim() || null : null;
|
|
68
|
+
return { branch, head, base, baseFrom: target.from };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** NUL-separated git output as a list. */
|
|
72
|
+
function zList(out: string | undefined): string[] {
|
|
73
|
+
return (out ?? "").split("\0").filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Each of `paths` (record files directly in `dir`, from the repository root)
|
|
78
|
+
* as committed, modified or new against `head`, and the files directly in
|
|
79
|
+
* `dir` at `head` whose names `match` and that `paths` leaves out: the
|
|
80
|
+
* records deleted in the working tree. With no head every path is new.
|
|
81
|
+
*
|
|
82
|
+
* Staged and unstaged changes count alike: anything `HEAD` does not hold is
|
|
83
|
+
* uncommitted. A rename is a new record and a deleted one.
|
|
84
|
+
*/
|
|
85
|
+
export function worktreeStates(
|
|
86
|
+
top: string,
|
|
87
|
+
head: string | null,
|
|
88
|
+
dir: string,
|
|
89
|
+
paths: readonly string[],
|
|
90
|
+
match: RegExp,
|
|
91
|
+
): { states: Map<string, WorktreeState>; deleted: string[] } {
|
|
92
|
+
const states = new Map<string, WorktreeState>();
|
|
93
|
+
if (head === null) {
|
|
94
|
+
for (const p of paths) states.set(p, "new");
|
|
95
|
+
return { states, deleted: [] };
|
|
96
|
+
}
|
|
97
|
+
const prefix = dir === "." ? "" : `${dir}/`;
|
|
98
|
+
const direct = (p: string) => p.startsWith(prefix) && !p.slice(prefix.length).includes("/");
|
|
99
|
+
const spec = dir === "." ? "." : dir;
|
|
100
|
+
const atHead = new Set(zList(tryGit(top, ["ls-tree", "-r", "-z", "--name-only", head, "--", spec])).filter(direct));
|
|
101
|
+
const changed = new Set(zList(tryGit(top, ["diff", "--name-only", "-z", "--no-renames", head, "--", spec])));
|
|
102
|
+
for (const p of paths) states.set(p, !atHead.has(p) ? "new" : changed.has(p) ? "modified" : "committed");
|
|
103
|
+
const present = new Set(paths);
|
|
104
|
+
const deleted = [...atHead].filter((p) => !present.has(p) && match.test(p.slice(prefix.length))).sort();
|
|
105
|
+
return { states, deleted };
|
|
106
|
+
}
|
|
@@ -15,6 +15,11 @@
|
|
|
15
15
|
*
|
|
16
16
|
* With `--since <rev>` it prints what changed between two revisions instead,
|
|
17
17
|
* through `records-since.ts` (#2673).
|
|
18
|
+
*
|
|
19
|
+
* A working-tree read in git says, for each record, whether it is committed,
|
|
20
|
+
* modified or new against `HEAD`, and names the branch, head and base of the
|
|
21
|
+
* checkout; `--uncommitted` lists only the records `HEAD` doesn't hold
|
|
22
|
+
* (#3160, `records-checkout.ts`).
|
|
18
23
|
*/
|
|
19
24
|
|
|
20
25
|
import { execFileSync } from "node:child_process";
|
|
@@ -48,11 +53,12 @@ import {
|
|
|
48
53
|
type SealInput,
|
|
49
54
|
type VerdictAttestation,
|
|
50
55
|
} from "./records";
|
|
56
|
+
import { readCheckoutHead, worktreeStates, type CheckoutView, type WorktreeState } from "./records-checkout";
|
|
51
57
|
import { gitTree, workingTree, type WorkspaceTree } from "./tree";
|
|
52
|
-
import type { DecisionWork } from "./work";
|
|
53
|
-
import { activeAttestors, type ProvenanceLevel } from "./trust/attestor";
|
|
58
|
+
import type { DecisionWork, WorkAnswer } from "./work";
|
|
59
|
+
import { activeAttestors, type CommitAttestor, type ProvenanceLevel } from "./trust/attestor";
|
|
54
60
|
import { policyAtBase, recordProvenance, resolveBase, type BaseSource, type RecordProvenance } from "./trust/provenance";
|
|
55
|
-
import type
|
|
61
|
+
import { returnedPolicy, type TrustPolicy } from "./trust/policy";
|
|
56
62
|
import type { SealedRecord } from "./trust/seal";
|
|
57
63
|
|
|
58
64
|
/** The version of the `records` output this chant writes. */
|
|
@@ -62,7 +68,7 @@ export const RECORDS_CONTRACT_VERSION = 1;
|
|
|
62
68
|
export const RECORDS_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/records/v1/records.schema.json";
|
|
63
69
|
|
|
64
70
|
const USAGE =
|
|
65
|
-
"chant workspace records [--kind <kind file>] [--current] [--at <rev>] [--base <rev>] [--require attested] [--json] | chant workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json] | chant workspace records pin <path> | chant workspace records new|amend|review|close (#2670, #2693)";
|
|
71
|
+
"chant workspace records [--kind <kind file>] [--current] [--uncommitted | --at <rev>] [--base <rev>] [--require attested] [--json] | chant workspace records [--kind <kind file>] --since <rev|session id> [--at <rev>] [--json] | chant workspace records pin <path> | chant workspace records new|amend|review|close (#2670, #2693)";
|
|
66
72
|
|
|
67
73
|
/** Exit code when the read worked and a record falls below `--require`. */
|
|
68
74
|
export const EXIT_BELOW_REQUIRED = 2;
|
|
@@ -81,6 +87,8 @@ export interface RecordsQuery {
|
|
|
81
87
|
* false; the intent graph passes false, since it raises the warning itself.
|
|
82
88
|
*/
|
|
83
89
|
workGaps?: boolean;
|
|
90
|
+
/** List only the records that are modified or new against `HEAD` (#3160). Needs git and a working-tree read. */
|
|
91
|
+
uncommitted?: boolean;
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
/**
|
|
@@ -108,6 +116,20 @@ export type RecordView = RecordEntry & {
|
|
|
108
116
|
* (`record-decided.ts`). The intent graph's windows open at this commit.
|
|
109
117
|
*/
|
|
110
118
|
decidedIn?: DecidedIn | null;
|
|
119
|
+
/**
|
|
120
|
+
* For a read in the working tree of a git repository (#3160): whether the
|
|
121
|
+
* record's file is as `HEAD` holds it (`committed`), changed from it
|
|
122
|
+
* (`modified`), or not in it (`new`), staged or not. Absent under `--at`.
|
|
123
|
+
*/
|
|
124
|
+
worktree?: WorktreeState;
|
|
125
|
+
/**
|
|
126
|
+
* For a work kind whose work block names an answer kind (#3147): the
|
|
127
|
+
* decision-point answers about the item, the answer records whose
|
|
128
|
+
* `constrains` names its id, joined by id and never copied onto the item.
|
|
129
|
+
* Read at the same revision; in the working tree, with the questions a
|
|
130
|
+
* steward keeps on the lifecycle ledger (#2786) laid over it.
|
|
131
|
+
*/
|
|
132
|
+
answers?: WorkAnswer[];
|
|
111
133
|
};
|
|
112
134
|
|
|
113
135
|
/** The role in the trust policy whose holders' verdicts the quorum does not count (#2671). */
|
|
@@ -129,11 +151,15 @@ export type RecordsDocument =
|
|
|
129
151
|
| {
|
|
130
152
|
$schema: string;
|
|
131
153
|
contract: number;
|
|
132
|
-
kind: { name: string; schema: string; file: string; format: RecordFormat };
|
|
154
|
+
kind: { name: string; schema: string; file: string; format: RecordFormat; spec: boolean };
|
|
133
155
|
at: string | null;
|
|
134
156
|
/** The directory pinned paths resolve in, from the repository root: the workspace holding the kind file, or the repository root (#2549). */
|
|
135
157
|
workspaceRoot: string;
|
|
136
158
|
current: boolean;
|
|
159
|
+
/** Whether only the records `HEAD` doesn't hold were listed (`--uncommitted`, #3160). */
|
|
160
|
+
uncommitted: boolean;
|
|
161
|
+
/** For a read in the working tree of a git repository (#3160): the branch, head and base it is on, and the records deleted from it. Absent under `--at`. */
|
|
162
|
+
checkout?: CheckoutView;
|
|
137
163
|
trust: TrustView;
|
|
138
164
|
records: RecordView[];
|
|
139
165
|
summary: { total: number; valid: number; invalid: number; superseded: number };
|
|
@@ -161,6 +187,47 @@ export interface RecordsSetDocument {
|
|
|
161
187
|
$schema: string;
|
|
162
188
|
contract: number;
|
|
163
189
|
kinds: (RecordsDocument & { declared: DeclaredKindView })[];
|
|
190
|
+
/** With `--current`: the workspace's spec (#2524 D20, #2546), from the kinds marked `spec: true`. */
|
|
191
|
+
spec?: SpecView;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* The spec (#2524 D20): the current records of every declared kind marked
|
|
196
|
+
* `spec: true`, each with the workspace files it pins. `records --current
|
|
197
|
+
* --json` prints it beside the kinds, so an agent resumes from the
|
|
198
|
+
* declaration and this one read.
|
|
199
|
+
*/
|
|
200
|
+
export interface SpecView {
|
|
201
|
+
/** The spec kinds, as the declaration names them, in its order. */
|
|
202
|
+
kinds: DeclaredKindView[];
|
|
203
|
+
records: SpecRecord[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** One current record of a spec kind. */
|
|
207
|
+
export interface SpecRecord {
|
|
208
|
+
/** The kind's name, such as decision. */
|
|
209
|
+
kind: string;
|
|
210
|
+
id: string | null;
|
|
211
|
+
path: string;
|
|
212
|
+
state: string | null;
|
|
213
|
+
valid: boolean;
|
|
214
|
+
/** The digest a verdict names (#2672). */
|
|
215
|
+
digest: string;
|
|
216
|
+
/** Each workspace file the record pins: its path from the workspace root, the sha256 pinned, and how it compares with the tree read (#2549). */
|
|
217
|
+
assets: RecordEntry["assets"];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** The spec of a set of declared kinds already read with `--current`. */
|
|
221
|
+
export function specOf(kinds: RecordsSetDocument["kinds"]): SpecView {
|
|
222
|
+
const spec: SpecView = { kinds: [], records: [] };
|
|
223
|
+
for (const doc of kinds) {
|
|
224
|
+
if ("error" in doc || !doc.kind.spec) continue;
|
|
225
|
+
spec.kinds.push(doc.declared);
|
|
226
|
+
for (const r of doc.records) {
|
|
227
|
+
spec.records.push({ kind: doc.kind.name, id: r.id, path: r.path, state: r.state, valid: r.valid, digest: r.digest, assets: r.assets });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return spec;
|
|
164
231
|
}
|
|
165
232
|
|
|
166
233
|
/**
|
|
@@ -186,7 +253,8 @@ export async function queryDeclaredRecords(kinds: { declared: RecordKindDeclarat
|
|
|
186
253
|
const doc = await queryRecords({ ...query, kind: k.file });
|
|
187
254
|
out.push({ ...doc, declared: { member: k.declared.member, path: k.declared.path, name: k.declared.name } });
|
|
188
255
|
}
|
|
189
|
-
|
|
256
|
+
// The spec is every current spec record; an --uncommitted read lists only some, so it has none.
|
|
257
|
+
return { $schema: RECORDS_OUTPUT_SCHEMA_ID, contract: RECORDS_CONTRACT_VERSION, kinds: out, ...(query.current && !query.uncommitted ? { spec: specOf(out) } : {}) };
|
|
190
258
|
}
|
|
191
259
|
|
|
192
260
|
/** A records read, before provenance. */
|
|
@@ -315,26 +383,37 @@ export async function readRecordsFor(
|
|
|
315
383
|
|
|
316
384
|
/** Run the query and build the document `--json` prints. Never throws a {@link RecordReadError}. */
|
|
317
385
|
export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument> {
|
|
386
|
+
// A revision has no working tree to hold anything uncommitted; the CLI and the MCP tool refuse the pair first.
|
|
387
|
+
if (query.uncommitted && query.at !== undefined) throw new Error("--uncommitted reads the working tree and takes no --at");
|
|
318
388
|
try {
|
|
319
389
|
const { loaded, root, top, at, workspaceRoot, tree, result } = await readRecordsFor(query);
|
|
320
390
|
// Provenance, judged by the policy at base and never by the tree read (#2547).
|
|
321
391
|
const base = top ? resolveBase(top, query.base) : { commit: null, from: null };
|
|
322
392
|
const policy = top ? policyAtBase(top, base) : policyAtBase(root, base);
|
|
393
|
+
const attestors = policy.active ? await activeAttestors() : [];
|
|
323
394
|
const provenance = recordProvenance({
|
|
324
395
|
repo: top,
|
|
325
396
|
policy,
|
|
326
397
|
at,
|
|
327
398
|
paths: result.records.map((r) => r.path),
|
|
328
|
-
attestors
|
|
399
|
+
attestors,
|
|
400
|
+
...(policy.active ? { revisit: await returnedRevisit(tree, workspaceRoot, policy, attestors) } : {}),
|
|
329
401
|
});
|
|
330
402
|
const seals = loaded.kind.reviews ? await import("./trust/seal") : undefined;
|
|
331
403
|
const quorumOptions = await quorumOptionsFor(loaded, tree, policy);
|
|
332
|
-
const records: RecordView[] =
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
404
|
+
const records: RecordView[] = [];
|
|
405
|
+
for (const r of result.records) {
|
|
406
|
+
const p = provenance.get(r.path)!;
|
|
407
|
+
// Returned work (#2552): its seals verify against the signers admitted for its return too.
|
|
408
|
+
const recordPolicy = p.returned ? returnedPolicy(policy, p.returned.id) : policy;
|
|
409
|
+
const options = p.returned ? await quorumOptionsFor(loaded, tree, recordPolicy) : quorumOptions;
|
|
410
|
+
records.push({
|
|
411
|
+
...r,
|
|
412
|
+
provenance: p,
|
|
413
|
+
...(options ? { quorum: computeQuorum(loaded.kind, r, options) } : {}),
|
|
414
|
+
...(seals ? authorSeal(loaded.kind, r, recordPolicy, seals.checkRecordSeal) : {}),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
338
417
|
if (loaded.kind.approval && !loaded.kind.work && top) {
|
|
339
418
|
const decided = decidedCommits(top, at ?? "HEAD", records.map((r) => r.path), loaded.kind);
|
|
340
419
|
for (const r of records) r.decidedIn = decided.get(r.path) ?? null;
|
|
@@ -345,6 +424,41 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
|
|
|
345
424
|
const leases = await activeWorkLeases(loaded.file);
|
|
346
425
|
for (const r of records) if (r.id !== null) r.lease = leases.get(r.id) ?? null;
|
|
347
426
|
}
|
|
427
|
+
if (loaded.kind.work?.answers) {
|
|
428
|
+
// The answers about each item, joined by id (#3147). An answer kind that can't be read leaves answers out rather than failing the work read.
|
|
429
|
+
const [{ workAnswers }, { readLedgerAnswers, withLedgerAnswers }] = await Promise.all([import("./work"), import("./answers-ledger")]);
|
|
430
|
+
const answersRead = await readRecordsFor({
|
|
431
|
+
kind: resolve(dirname(loaded.file), loaded.kind.work.answers),
|
|
432
|
+
cwd: query.cwd,
|
|
433
|
+
...(query.at !== undefined ? { at: query.at } : {}),
|
|
434
|
+
overlay: async (base, { loaded: l, root: r }) =>
|
|
435
|
+
withLedgerAnswers(base, await readLedgerAnswers({ file: l.file, name: l.kind.name, dirRel: relative(r, l.dir).split(sep).join("/") || "." })),
|
|
436
|
+
}).catch((err: unknown) => {
|
|
437
|
+
if (err instanceof RecordReadError) return undefined;
|
|
438
|
+
throw err;
|
|
439
|
+
});
|
|
440
|
+
if (answersRead) {
|
|
441
|
+
const byItem = workAnswers(answersRead.result.records, records.flatMap((r) => (r.id !== null ? [r.id] : [])));
|
|
442
|
+
for (const r of records) if (r.id !== null) r.answers = byItem.get(r.id) ?? [];
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
// Which records the checkout holds uncommitted, judged after every record is read, so links still resolve across the whole set (#3160).
|
|
446
|
+
let checkout: CheckoutView | undefined;
|
|
447
|
+
let listed = records;
|
|
448
|
+
let summary = result.summary;
|
|
449
|
+
if (at === null && top) {
|
|
450
|
+
const { head, ...rest } = readCheckoutHead(top, query.base);
|
|
451
|
+
const dir = relative(root, loaded.dir).split(sep).join("/") || ".";
|
|
452
|
+
const { states, deleted } = worktreeStates(top, head, dir, records.map((r) => r.path), new RegExp(loaded.kind.location.match));
|
|
453
|
+
for (const r of records) r.worktree = states.get(r.path) ?? "new";
|
|
454
|
+
checkout = { branch: rest.branch, head, base: rest.base, baseFrom: rest.baseFrom, deleted };
|
|
455
|
+
if (query.uncommitted) {
|
|
456
|
+
listed = records.filter((r) => r.worktree !== "committed");
|
|
457
|
+
summary = { ...summary, total: listed.length, valid: listed.filter((r) => r.valid).length, invalid: listed.filter((r) => !r.valid).length };
|
|
458
|
+
}
|
|
459
|
+
} else if (query.uncommitted && !top) {
|
|
460
|
+
throw new RecordReadError("not-a-git-repository", "--uncommitted compares the working tree with HEAD, and this directory is not in a git repository");
|
|
461
|
+
}
|
|
348
462
|
return {
|
|
349
463
|
$schema: RECORDS_OUTPUT_SCHEMA_ID,
|
|
350
464
|
contract: RECORDS_CONTRACT_VERSION,
|
|
@@ -353,13 +467,16 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
|
|
|
353
467
|
schema: loaded.kind.schema.id,
|
|
354
468
|
file: relative(root, loaded.file).split("\\").join("/"),
|
|
355
469
|
format: loaded.kind.format,
|
|
470
|
+
spec: loaded.kind.spec === true,
|
|
356
471
|
},
|
|
357
472
|
at,
|
|
358
473
|
workspaceRoot,
|
|
359
474
|
current: !!query.current,
|
|
475
|
+
uncommitted: !!query.uncommitted,
|
|
476
|
+
...(checkout ? { checkout } : {}),
|
|
360
477
|
trust: { base: base.commit, baseFrom: base.from, active: policy.active, signersPath: policy.signersPath, problems: policy.problems },
|
|
361
|
-
records,
|
|
362
|
-
summary
|
|
478
|
+
records: listed,
|
|
479
|
+
summary,
|
|
363
480
|
...(result.decisions ? { decisions: result.decisions } : {}),
|
|
364
481
|
};
|
|
365
482
|
} catch (err) {
|
|
@@ -368,6 +485,36 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
|
|
|
368
485
|
}
|
|
369
486
|
}
|
|
370
487
|
|
|
488
|
+
/**
|
|
489
|
+
* The second look recordProvenance takes at each record (#2552): a record
|
|
490
|
+
* whose bytes came back in a return is judged by the commit it was made in,
|
|
491
|
+
* which the return carries, rather than by the import here. `tree` is the
|
|
492
|
+
* workspace root's tree, as read; record paths are from the repository root.
|
|
493
|
+
*/
|
|
494
|
+
async function returnedRevisit(
|
|
495
|
+
tree: WorkspaceTree,
|
|
496
|
+
workspaceRoot: string,
|
|
497
|
+
policy: TrustPolicy,
|
|
498
|
+
attestors: readonly CommitAttestor[],
|
|
499
|
+
): Promise<((path: string, host: RecordProvenance) => RecordProvenance | undefined) | undefined> {
|
|
500
|
+
const { readReturns, returnedProvenance } = await import("./returns");
|
|
501
|
+
const { returns } = readReturns(tree);
|
|
502
|
+
if (returns.length === 0) return undefined;
|
|
503
|
+
const prefix = workspaceRoot === "." ? "" : `${workspaceRoot}/`;
|
|
504
|
+
return (path, host) => {
|
|
505
|
+
if (!path.startsWith(prefix)) return undefined;
|
|
506
|
+
const rel = path.slice(prefix.length);
|
|
507
|
+
const holding = returns.filter((r) => r.paths[rel] !== undefined);
|
|
508
|
+
if (holding.length === 0 || tree.stat(rel) !== "file") return undefined;
|
|
509
|
+
const content = Buffer.from(tree.bytes ? tree.bytes(rel) : tree.read(rel));
|
|
510
|
+
for (const r of holding) {
|
|
511
|
+
const p = returnedProvenance(policy, attestors, r, rel, content, host);
|
|
512
|
+
if (p) return p;
|
|
513
|
+
}
|
|
514
|
+
return undefined;
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
|
|
371
518
|
/**
|
|
372
519
|
* A record's author seal checked against the policy at base (#2688), for a
|
|
373
520
|
* kind with a reviews list: its author is the kind's `reviews.decider` field.
|
|
@@ -480,6 +627,11 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
|
|
|
480
627
|
console.error(formatError({ message: `chant workspace records takes no argument but pin, new, amend, review or close (got ${args.extraPositional})`, hint: USAGE }));
|
|
481
628
|
return 1;
|
|
482
629
|
}
|
|
630
|
+
const pairing = uncommittedPairing(args);
|
|
631
|
+
if (pairing) {
|
|
632
|
+
console.error(formatError({ message: pairing, hint: USAGE }));
|
|
633
|
+
return 1;
|
|
634
|
+
}
|
|
483
635
|
if (!args.kind) return runDeclaredRecords(args);
|
|
484
636
|
if (args.require !== undefined && args.require !== "attested") {
|
|
485
637
|
console.error(formatError({ message: `--require takes one level, attested, not ${JSON.stringify(args.require)}`, hint: USAGE }));
|
|
@@ -498,13 +650,13 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
|
|
|
498
650
|
else console.log(formatSince(since));
|
|
499
651
|
return "error" in since ? 1 : 0;
|
|
500
652
|
}
|
|
501
|
-
const doc = await queryRecords({ kind: args.kind, current: args.current, at: args.at, base: args.base, cwd: process.cwd() });
|
|
653
|
+
const doc = await queryRecords({ kind: args.kind, current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd() });
|
|
502
654
|
if (args.json) {
|
|
503
655
|
console.log(JSON.stringify(doc, null, 2));
|
|
504
656
|
} else if ("error" in doc) {
|
|
505
657
|
console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
|
|
506
658
|
} else {
|
|
507
|
-
console.log(formatRecords(doc.records, doc.summary, doc.at));
|
|
659
|
+
console.log(formatRecords(doc.records, doc.summary, doc.at, doc.checkout));
|
|
508
660
|
}
|
|
509
661
|
if ("error" in doc) return 1;
|
|
510
662
|
if (args.require) {
|
|
@@ -562,7 +714,7 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
|
|
|
562
714
|
return 1;
|
|
563
715
|
}
|
|
564
716
|
if (args.since !== undefined) return runDeclaredSince(declared, args);
|
|
565
|
-
const set = await queryDeclaredRecords(declared, { current: args.current, at: args.at, base: args.base, cwd: process.cwd() });
|
|
717
|
+
const set = await queryDeclaredRecords(declared, { current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd() });
|
|
566
718
|
if (args.json) console.log(JSON.stringify(set, null, 2));
|
|
567
719
|
for (const doc of args.json ? [] : set.kinds) {
|
|
568
720
|
if ("error" in doc) {
|
|
@@ -570,7 +722,7 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
|
|
|
570
722
|
continue;
|
|
571
723
|
}
|
|
572
724
|
console.log(`${doc.declared.name ?? doc.kind.name} (${doc.declared.path})`);
|
|
573
|
-
console.log(formatRecords(doc.records, doc.summary, doc.at));
|
|
725
|
+
console.log(formatRecords(doc.records, doc.summary, doc.at, doc.checkout));
|
|
574
726
|
}
|
|
575
727
|
if (set.kinds.some((d) => "error" in d)) return 1;
|
|
576
728
|
if (args.require) {
|
|
@@ -636,7 +788,20 @@ export function belowRequired(records: RecordView[], required: ProvenanceLevel):
|
|
|
636
788
|
return records.filter((r) => r.provenance.level !== required);
|
|
637
789
|
}
|
|
638
790
|
|
|
639
|
-
|
|
791
|
+
/** Why `--uncommitted` can't be given with the other flags given, or undefined when it can (#3160). */
|
|
792
|
+
function uncommittedPairing(args: CommandContext["args"]): string | undefined {
|
|
793
|
+
if (!args.uncommitted) return undefined;
|
|
794
|
+
if (args.at !== undefined) return "--uncommitted reads the working tree against HEAD, and takes no --at";
|
|
795
|
+
if (args.since !== undefined) return "--uncommitted takes no --since: --since compares two revisions";
|
|
796
|
+
return undefined;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function formatRecords(
|
|
800
|
+
records: RecordView[],
|
|
801
|
+
summary: { total: number; valid: number; invalid: number; superseded: number },
|
|
802
|
+
at: string | null,
|
|
803
|
+
checkout?: CheckoutView,
|
|
804
|
+
): string {
|
|
640
805
|
const lines: string[] = [];
|
|
641
806
|
const idWidth = Math.max(2, ...records.map((r) => (r.id ?? "-").length));
|
|
642
807
|
const stateWidth = Math.max(5, ...records.map((r) => (r.state ?? "-").length));
|
|
@@ -647,7 +812,8 @@ function formatRecords(records: RecordView[], summary: { total: number; valid: n
|
|
|
647
812
|
const remediated = r.remediatedBy.length > 0 ? ` remediated by ${r.remediatedBy.join(", ")}` : "";
|
|
648
813
|
const attested = r.provenance.level === "attested" ? ` attested by ${r.provenance.principal}` : "";
|
|
649
814
|
const sealed = r.attested === true ? ` sealed by ${(r.data?.[RECORD_SEAL_FIELD] as { signer: string }).signer}` : "";
|
|
650
|
-
|
|
815
|
+
const uncommitted = r.worktree === "new" || r.worktree === "modified" ? ` (${r.worktree}, uncommitted)` : "";
|
|
816
|
+
lines.push(`${(r.id ?? "-").padEnd(idWidth)} ${(r.state ?? "-").padEnd(stateWidth)} ${title}${superseded}${remediated}${attested}${sealed}${uncommitted}${flag}`);
|
|
651
817
|
if (r.ready !== undefined) {
|
|
652
818
|
const blocked = (r.blockedBy ?? []).map((b) => `${b.id} (${b.state ?? "unknown"})`).join(", ");
|
|
653
819
|
const implemented = (r.implements ?? []).map((d) => `${d.id} (${d.state ?? "unknown"})`).join(", ");
|
|
@@ -664,9 +830,16 @@ function formatRecords(records: RecordView[], summary: { total: number; valid: n
|
|
|
664
830
|
lines.push(`${" ".repeat(idWidth + 2)}quorum ${q.agreed} of ${q.need} agreed, ${verdict}; ${q.notCounted.length} not counted${concerns}`);
|
|
665
831
|
}
|
|
666
832
|
}
|
|
833
|
+
for (const path of checkout?.deleted ?? []) lines.push(`${"-".padEnd(idWidth)} ${"-".padEnd(stateWidth)} ${path} (deleted, uncommitted)`);
|
|
667
834
|
lines.push(
|
|
668
835
|
`${summary.total} records${at ? ` at ${at.slice(0, 8)}` : ""}: ${summary.valid} valid, ${summary.invalid} invalid, ${summary.superseded} superseded`,
|
|
669
836
|
);
|
|
837
|
+
if (checkout) {
|
|
838
|
+
const branch = checkout.branch ?? "a detached HEAD";
|
|
839
|
+
const head = checkout.head ? ` at ${checkout.head.slice(0, 8)}` : " with no commits";
|
|
840
|
+
const base = checkout.base ? `, forked from ${checkout.base.slice(0, 8)} of ${checkout.baseFrom}` : "";
|
|
841
|
+
lines.push(`working tree on ${branch}${head}${base}`);
|
|
842
|
+
}
|
|
670
843
|
return lines.join("\n");
|
|
671
844
|
}
|
|
672
845
|
|
|
@@ -676,7 +849,7 @@ export async function runWorkspaceUnknown(ctx: CommandContext): Promise<number>
|
|
|
676
849
|
console.error(
|
|
677
850
|
formatError({
|
|
678
851
|
message: sub ? `Unknown workspace subcommand: ${sub}` : "chant workspace needs a subcommand",
|
|
679
|
-
hint: `Workspace subcommands: audit, build, check, graph, init, lineage, lint, ls, records, status, upgrade, verify, work. Run "chant --help" for their options.`,
|
|
852
|
+
hint: `Workspace subcommands: adopt-lineage, audit, build, check, evidence, graph, hash-index, init, lineage, lint, ls, pin, records, signers, status, upgrade, verify, versions, work. Run "chant --help" for their options.`,
|
|
680
853
|
}),
|
|
681
854
|
);
|
|
682
855
|
return 1;
|
|
@@ -84,8 +84,8 @@
|
|
|
84
84
|
},
|
|
85
85
|
"digest": {
|
|
86
86
|
"type": "string",
|
|
87
|
-
"pattern": "^[0-9a-f]{64}$",
|
|
88
|
-
"description": "The seal written: the lowercase hex SHA-256 of the
|
|
87
|
+
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
88
|
+
"description": "The seal written: sha256: and the lowercase hex SHA-256 of the JCS form of the whole session without its seal field (its front matter as core and the text below it as body, line endings LF), which records checks on every read (session-seal-mismatch). Before #2546 it was the bare hex SHA-256 of the file's text without the seal line."
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
},
|
|
@@ -171,6 +171,10 @@
|
|
|
171
171
|
"write-usage-invalid",
|
|
172
172
|
"record-not-found",
|
|
173
173
|
"record-closed",
|
|
174
|
+
"write-scope-member",
|
|
175
|
+
"write-scope-kind",
|
|
176
|
+
"write-scope-class-unknown",
|
|
177
|
+
"agent-unknown",
|
|
174
178
|
"record-unparseable",
|
|
175
179
|
"record-schema-invalid",
|
|
176
180
|
"record-id-duplicate",
|
|
@@ -178,6 +182,7 @@
|
|
|
178
182
|
"record-supersedes-conflict",
|
|
179
183
|
"record-remediates-unknown",
|
|
180
184
|
"record-remediates-not-closed",
|
|
185
|
+
"record-seal-mismatch",
|
|
181
186
|
"session-seal-mismatch",
|
|
182
187
|
"session-verdict-unknown-record"
|
|
183
188
|
]
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* session in one write. It sets the session's state to its kind's closed
|
|
4
4
|
* state, the time it closed and the commit it closed at (the fields the
|
|
5
5
|
* kind's `session` block names in `closedOn` and `closedRev`), and then the
|
|
6
|
-
* seal by chant's rule ({@link sessionSeal}), so a UI never
|
|
6
|
+
* seal by chant's whole-file rule ({@link sessionSeal}, #2546), so a UI never
|
|
7
|
+
* computes a seal.
|
|
7
8
|
*
|
|
8
9
|
* Like `new`, `amend` and `review`, it reads the records again with the file
|
|
9
10
|
* in place and writes only when the session comes back valid: a verdict
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
readAll,
|
|
27
28
|
RECORDS_WRITE_CONTRACT_VERSION,
|
|
28
29
|
RecordWriteError,
|
|
30
|
+
refuseOutOfScope,
|
|
29
31
|
replaceFields,
|
|
30
32
|
stableJson,
|
|
31
33
|
validateWrite,
|
|
@@ -33,6 +35,7 @@ import {
|
|
|
33
35
|
type WriteResult,
|
|
34
36
|
} from "./records-write";
|
|
35
37
|
import { headCommit } from "./session-kinds";
|
|
38
|
+
import { WRITE_SCOPE_CODES } from "./write-scope";
|
|
36
39
|
|
|
37
40
|
export const RECORDS_CLOSE_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/records-close/v1/records-close.schema.json";
|
|
38
41
|
|
|
@@ -42,6 +45,7 @@ export const CLOSE_ERROR_CODES = [
|
|
|
42
45
|
"write-usage-invalid",
|
|
43
46
|
"record-not-found",
|
|
44
47
|
"record-closed",
|
|
48
|
+
...WRITE_SCOPE_CODES,
|
|
45
49
|
...RECORD_REASON_CODES,
|
|
46
50
|
] as const satisfies readonly ReasonCode[];
|
|
47
51
|
export type CloseErrorCode = (typeof CLOSE_ERROR_CODES)[number];
|
|
@@ -65,6 +69,8 @@ export interface CloseRecordOptions {
|
|
|
65
69
|
cwd: string;
|
|
66
70
|
/** The close time. Defaults to now. */
|
|
67
71
|
now?: Date;
|
|
72
|
+
/** The agent session the write is made in (#2548). */
|
|
73
|
+
agent?: string;
|
|
68
74
|
}
|
|
69
75
|
|
|
70
76
|
/** An ISO 8601 time in UTC to the second, as the reference session schema's dateTime takes it. */
|
|
@@ -76,6 +82,7 @@ function isoSeconds(d: Date): string {
|
|
|
76
82
|
export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocument> {
|
|
77
83
|
try {
|
|
78
84
|
const o = await open(opts.kind, opts.cwd);
|
|
85
|
+
refuseOutOfScope(o, "close", opts.cwd, opts);
|
|
79
86
|
const { kind } = o.loaded;
|
|
80
87
|
const decl = kind.session;
|
|
81
88
|
if (!decl || kind.stateField === undefined) {
|
|
@@ -97,12 +104,13 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
|
|
|
97
104
|
...(decl.closedOn ? { [decl.closedOn]: isoSeconds(opts.now ?? new Date()) } : {}),
|
|
98
105
|
...(decl.closedRev ? { [decl.closedRev]: closedRev } : {}),
|
|
99
106
|
};
|
|
100
|
-
// The seal
|
|
107
|
+
// The seal is written last, into text that already holds everything else: the whole-file seal of the session without its seal field (#2546).
|
|
101
108
|
const merged = { ...old, ...set };
|
|
102
109
|
delete merged[decl.seal];
|
|
103
110
|
const unsealed = replaceFields(o.source.read(target.path), set, merged);
|
|
104
111
|
if (unsealed === undefined) throw new RecordWriteError("record-unparseable", `${target.path}: its fields can't be rewritten in place without changing the rest of the file`);
|
|
105
112
|
const digest = sessionSeal(unsealed, decl.seal, kind.format);
|
|
113
|
+
if (digest === null) throw new RecordWriteError("record-unparseable", `${target.path} can't be read, so it can't be sealed`);
|
|
106
114
|
const text = replaceFields(unsealed, { [decl.seal]: digest }, { ...merged, [decl.seal]: digest });
|
|
107
115
|
if (text === undefined || sessionSeal(text, decl.seal, kind.format) !== digest) {
|
|
108
116
|
throw new RecordWriteError("record-unparseable", `${target.path}: the ${decl.seal} line can't be added without changing the text it seals`);
|
|
@@ -154,7 +154,8 @@ const SESSION_SCHEMA = {
|
|
|
154
154
|
comments: { type: "array" },
|
|
155
155
|
follow_ups: { type: "array", items: ref("contractId") },
|
|
156
156
|
closed_at: ref("datetime"),
|
|
157
|
-
seal:
|
|
157
|
+
// The whole-file seal (#2546): sha256: and the hex SHA-256 of the session's JCS form without it.
|
|
158
|
+
seal: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" },
|
|
158
159
|
},
|
|
159
160
|
};
|
|
160
161
|
|
|
@@ -294,7 +295,7 @@ const SESSION = {
|
|
|
294
295
|
comments: [],
|
|
295
296
|
follow_ups: ["C-002"],
|
|
296
297
|
closed_at: "2026-09-24T11:00:00.000Z",
|
|
297
|
-
seal: "e".repeat(64)
|
|
298
|
+
seal: `sha256:${"e".repeat(64)}`,
|
|
298
299
|
};
|
|
299
300
|
|
|
300
301
|
// ── 1. The json format ───────────────────────────────────────────────────────
|
|
@@ -372,11 +373,16 @@ describe("format json", () => {
|
|
|
372
373
|
});
|
|
373
374
|
|
|
374
375
|
describe("a JSON session kind (#2673)", () => {
|
|
375
|
-
test("its seal is the
|
|
376
|
+
test("its seal is the whole-file JCS seal without the seal member (#2546)", async () => {
|
|
376
377
|
write("design/session.kind.mjs", kindFile({ ...KINDS.session, session: { verdicts: "approvals", seal: "seal", subjects: { kind: "contract.kind.mjs" } } }));
|
|
377
378
|
write("design/contracts/C-001-first.md", '---\nid: "C-001"\nstatus: "approved"\n---\n');
|
|
379
|
+
// JCS of the object without its seal: keys sorted, no whitespace. The member order and indentation of the file play no part.
|
|
380
|
+
const sorted = (v: unknown): unknown =>
|
|
381
|
+
v === null || typeof v !== "object" ? v : Array.isArray(v) ? v.map(sorted) : Object.fromEntries(Object.keys(v).sort().map((k) => [k, sorted((v as Record<string, unknown>)[k])]));
|
|
382
|
+
const jcsSeal = (v: object): string => `sha256:${sha256(JSON.stringify(sorted(v)))}`;
|
|
378
383
|
const { seal: _drop, ...unsealed } = SESSION;
|
|
379
|
-
const seal =
|
|
384
|
+
const seal = jcsSeal(unsealed);
|
|
385
|
+
// Sealed over SESSION, then changed: a verdict added, or the id.
|
|
380
386
|
write("design/sessions/S-0001.json", json({ ...SESSION, approvals: [{ record: "C-001" }], seal }));
|
|
381
387
|
write("design/sessions/S-0002.json", json({ ...SESSION, id: "S-0002", seal }));
|
|
382
388
|
const doc = await query("session");
|
|
@@ -385,7 +391,7 @@ describe("a JSON session kind (#2673)", () => {
|
|
|
385
391
|
["S-0002", ["session-seal-mismatch"]],
|
|
386
392
|
]);
|
|
387
393
|
const good = { ...unsealed, id: "S-0003" };
|
|
388
|
-
write("design/sessions/S-0003.json",
|
|
394
|
+
write("design/sessions/S-0003.json", `${JSON.stringify({ seal: jcsSeal(good), ...Object.fromEntries(Object.entries(good).reverse()) })}\n`);
|
|
389
395
|
const [, , third] = (await query("session")).records;
|
|
390
396
|
expect([third.id, codes(third)]).toEqual(["S-0003", []]);
|
|
391
397
|
});
|
|
@@ -547,6 +553,8 @@ describe("kinds without a lifecycle", () => {
|
|
|
547
553
|
["neither idField nor idFrom", { ...KINDS.closure, idField: undefined }, /idField: a kind names its id with exactly one of idField and idFrom/],
|
|
548
554
|
["an unknown format", { ...KINDS.closure, format: "yaml" }, /format/],
|
|
549
555
|
["a session block without states", { ...KINDS.closure, session: { verdicts: "approvals", seal: "seal", subjects: { kind: "contract.kind.mjs" } } }, /session: a session kind must have states/],
|
|
556
|
+
["a seal without states (#2546)", { ...KINDS.closure, seal: { field: "seal" } }, /seal: a kind without states cannot have seal/],
|
|
557
|
+
["a seal naming another field than session.seal (#2546)", { ...KINDS.session, session: { verdicts: "approvals", seal: "seal", subjects: { kind: "contract.kind.mjs" } }, seal: { field: "closed_digest" } }, /seal: seal.field and session.seal name the field/],
|
|
550
558
|
])("a kind with %s is kind-invalid, naming the field", async (_label, kind, why) => {
|
|
551
559
|
write("design/closure.kind.mjs", kindFile(kind));
|
|
552
560
|
const error = await failure("closure");
|