@intentius/chant 0.101.0 → 0.104.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/core.d.ts +2 -0
- package/dist/audit/core.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/change-set.d.ts +232 -0
- package/dist/change-set.d.ts.map +1 -0
- package/dist/cli/commands/audit.d.ts +7 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/carve-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/build.d.ts.map +1 -1
- package/dist/cli/handlers/change-set.d.ts +11 -0
- package/dist/cli/handlers/change-set.d.ts.map +1 -0
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/fan-out.d.ts +33 -0
- 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 +16 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/pr.d.ts +31 -0
- package/dist/cli/handlers/pr.d.ts.map +1 -0
- 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/op-tools.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 +4 -3
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +74 -3
- 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/auto-release.d.ts +16 -0
- package/dist/components/auto-release.d.ts.map +1 -1
- package/dist/components/capability-plugin-loader.d.ts.map +1 -1
- package/dist/components/capability.d.ts +53 -0
- package/dist/components/capability.d.ts.map +1 -1
- package/dist/components/changed-files.d.ts +31 -0
- package/dist/components/changed-files.d.ts.map +1 -0
- package/dist/components/cli-support.d.ts +9 -1
- package/dist/components/cli-support.d.ts.map +1 -1
- package/dist/components/component.d.ts +24 -0
- package/dist/components/component.d.ts.map +1 -1
- package/dist/components/deploy-units.d.ts.map +1 -1
- package/dist/components/driver.d.ts +13 -0
- package/dist/components/driver.d.ts.map +1 -1
- package/dist/components/fan-out-output.d.ts +6 -0
- package/dist/components/fan-out-output.d.ts.map +1 -1
- package/dist/components/fan-out-record.d.ts +41 -0
- package/dist/components/fan-out-record.d.ts.map +1 -0
- package/dist/components/fan-out-run.d.ts +65 -0
- package/dist/components/fan-out-run.d.ts.map +1 -1
- package/dist/components/fan-out-support.d.ts +2 -0
- package/dist/components/fan-out-support.d.ts.map +1 -1
- package/dist/components/fan-out.d.ts +6 -0
- package/dist/components/fan-out.d.ts.map +1 -1
- package/dist/components/gated-wave-pipeline.d.ts +50 -0
- package/dist/components/gated-wave-pipeline.d.ts.map +1 -0
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/layers.d.ts +25 -0
- package/dist/components/layers.d.ts.map +1 -0
- package/dist/components/pr-pipeline.d.ts +61 -0
- package/dist/components/pr-pipeline.d.ts.map +1 -0
- package/dist/components/pr-run.d.ts +119 -0
- package/dist/components/pr-run.d.ts.map +1 -0
- package/dist/components/promote.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/shell.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/components/wave-plan.d.ts +49 -0
- package/dist/components/wave-plan.d.ts.map +1 -0
- 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/gated-waves.d.ts +147 -0
- package/dist/gated-waves.d.ts.map +1 -0
- 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/index.d.ts +1 -0
- package/dist/index.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 +103 -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 +22 -0
- package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +46 -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/lint/post-synth.d.ts +18 -0
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +137 -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/change-set.d.ts +62 -0
- package/dist/op/activities/change-set.d.ts.map +1 -0
- package/dist/op/activities/decide.d.ts +2 -0
- package/dist/op/activities/decide.d.ts.map +1 -1
- package/dist/op/activities/factory.d.ts +137 -0
- package/dist/op/activities/factory.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +5 -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-contract.d.ts.map +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 +11 -0
- 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 +5 -1
- package/dist/op/decide-backend.d.ts.map +1 -1
- package/dist/op/decide-read-inputs.d.ts +4 -2
- 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/factory.d.ts +83 -0
- package/dist/op/factory.d.ts.map +1 -0
- 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 +6 -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/op-ir.d.ts +2 -0
- package/dist/op/op-ir.d.ts.map +1 -1
- package/dist/op/op-verb-class.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 +16 -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/plan-summary.d.ts +207 -0
- package/dist/plan-summary.d.ts.map +1 -0
- package/dist/pr-forge.d.ts +105 -0
- package/dist/pr-forge.d.ts.map +1 -0
- package/dist/pr-loop.d.ts +178 -0
- package/dist/pr-loop.d.ts.map +1 -0
- 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/telemetry-attribution.d.ts +31 -0
- package/dist/telemetry-attribution.d.ts.map +1 -1
- package/dist/terraform/emit-source.d.ts.map +1 -1
- package/dist/terraform/hcl2json.d.ts +45 -0
- package/dist/terraform/hcl2json.d.ts.map +1 -0
- package/dist/terraform/parse.d.ts +4 -40
- 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-cli.d.ts +12 -0
- package/dist/workspace/box-cli.d.ts.map +1 -0
- package/dist/workspace/box-factory.d.ts +97 -0
- package/dist/workspace/box-factory.d.ts.map +1 -0
- package/dist/workspace/box-listing.d.ts +85 -0
- package/dist/workspace/box-listing.d.ts.map +1 -0
- package/dist/workspace/box-publish.d.ts +147 -0
- package/dist/workspace/box-publish.d.ts.map +1 -0
- package/dist/workspace/broker-protocol.d.ts +190 -0
- package/dist/workspace/broker-protocol.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/composites.d.ts +2 -2
- package/dist/workspace/composites.d.ts.map +1 -1
- package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts +17 -0
- package/dist/workspace/conformance/__fixtures__/reference-broker.d.ts.map +1 -0
- package/dist/workspace/conformance/broker.d.ts +202 -0
- package/dist/workspace/conformance/broker.d.ts.map +1 -0
- package/dist/workspace/conformance/index.d.ts +68 -6
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/vitest.d.ts +19 -3
- package/dist/workspace/conformance/vitest.d.ts.map +1 -1
- package/dist/workspace/conformance/writer.d.ts +462 -0
- package/dist/workspace/conformance/writer.d.ts.map +1 -0
- package/dist/workspace/decide.d.ts +126 -12
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/decision-points.schema.json +11 -4
- package/dist/workspace/declaration.d.ts +156 -4
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +383 -5
- package/dist/workspace/environments.d.ts +36 -10
- package/dist/workspace/environments.d.ts.map +1 -1
- package/dist/workspace/factory-rules.d.ts +180 -0
- package/dist/workspace/factory-rules.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/init.d.ts +21 -0
- package/dist/workspace/init.d.ts.map +1 -1
- 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 +36 -2
- package/dist/workspace/intent-record.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +249 -8
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/json-edit.d.ts +24 -0
- package/dist/workspace/json-edit.d.ts.map +1 -0
- package/dist/workspace/jsonc.d.ts +11 -0
- package/dist/workspace/jsonc.d.ts.map +1 -1
- package/dist/workspace/kinds.d.ts +66 -4
- package/dist/workspace/kinds.d.ts.map +1 -1
- package/dist/workspace/lexicon-symbols.d.ts +29 -0
- package/dist/workspace/lexicon-symbols.d.ts.map +1 -0
- 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/lineage-upgrade.d.ts +31 -2
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/links.d.ts.map +1 -1
- package/dist/workspace/point-answer.schema.json +42 -2
- package/dist/workspace/points-cli.d.ts +10 -3
- package/dist/workspace/points-cli.d.ts.map +1 -1
- package/dist/workspace/points.d.ts +66 -5
- 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 +53 -4
- 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 +5 -7
- 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 +34 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +7 -3
- 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 +68 -7
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +43 -9
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runs-cli.d.ts +296 -0
- package/dist/workspace/runs-cli.d.ts.map +1 -0
- package/dist/workspace/runs.d.ts +544 -0
- package/dist/workspace/runs.d.ts.map +1 -0
- package/dist/workspace/session-kinds.d.ts +1 -1
- package/dist/workspace/session-kinds.d.ts.map +1 -1
- package/dist/workspace/source-block.d.ts +1 -1
- package/dist/workspace/source-block.d.ts.map +1 -1
- package/dist/workspace/squash.d.ts +80 -0
- package/dist/workspace/squash.d.ts.map +1 -0
- package/dist/workspace/status-gates.d.ts +17 -3
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +64 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/symbols.d.ts +63 -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/run-commits.d.ts +46 -0
- package/dist/workspace/trust/run-commits.d.ts.map +1 -0
- package/dist/workspace/trust/run-statement.d.ts +128 -0
- package/dist/workspace/trust/run-statement.d.ts.map +1 -0
- package/dist/workspace/trust/seal.d.ts +38 -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/trust/verify-cli.d.ts +1 -1
- package/dist/workspace/trust/verify.d.ts +16 -2
- package/dist/workspace/trust/verify.d.ts.map +1 -1
- package/dist/workspace/wip-cli.d.ts +21 -0
- package/dist/workspace/wip-cli.d.ts.map +1 -0
- package/dist/workspace/wip.d.ts +268 -0
- package/dist/workspace/wip.d.ts.map +1 -0
- 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-size.d.ts +55 -0
- package/dist/workspace/work-size.d.ts.map +1 -0
- package/dist/workspace/work.d.ts +26 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/dist/workspace/workspace-kinds.schema.json +122 -0
- package/dist/workspace/workspace-principals.schema.json +60 -0
- package/dist/workspace/write-lock-cli.d.ts +60 -0
- package/dist/workspace/write-lock-cli.d.ts.map +1 -0
- package/dist/workspace/write-lock.d.ts +152 -0
- package/dist/workspace/write-lock.d.ts.map +1 -0
- package/dist/workspace/write-scope.d.ts +83 -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 +7 -2
- package/src/__fixtures__/change-set/lifecycle-plan.floci.json +59 -0
- package/src/__fixtures__/change-set/lifecycle-v1.ts.txt +4 -0
- package/src/__fixtures__/change-set/lifecycle-v2.ts.txt +4 -0
- package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +95 -0
- package/src/__fixtures__/change-set/warden.change-set.golden.json +106 -0
- package/src/__fixtures__/change-set/warden.plan.json +44 -0
- package/src/__fixtures__/plan-summary/normalization-vectors.json +1593 -0
- package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +31 -0
- package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +31 -0
- package/src/__fixtures__/pr-loop/pr-plan.golden.json +127 -0
- package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +37 -0
- package/src/audit/core.test.ts +15 -0
- package/src/audit/core.ts +8 -2
- package/src/audit/discover.test.ts +11 -0
- package/src/audit/discover.ts +7 -2
- package/src/audit/prior-art.ts +15 -0
- package/src/change-set-bundle.test.ts +57 -0
- package/src/change-set.test.ts +215 -0
- package/src/change-set.ts +445 -0
- package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +1 -1
- package/src/cli/commands/audit-opt-in.test.ts +54 -0
- package/src/cli/commands/audit.ts +29 -2
- package/src/cli/commands/build.test.ts +19 -0
- package/src/cli/commands/build.ts +3 -2
- 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/build.ts +13 -1
- package/src/cli/handlers/change-set.test.ts +56 -0
- package/src/cli/handlers/change-set.ts +44 -0
- package/src/cli/handlers/components.ts +2 -2
- package/src/cli/handlers/dev.ts +8 -6
- package/src/cli/handlers/fan-out.test.ts +221 -2
- package/src/cli/handlers/fan-out.ts +165 -75
- package/src/cli/handlers/graph.ts +3 -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.test.ts +21 -0
- package/src/cli/handlers/operator.ts +121 -6
- package/src/cli/handlers/pr.ts +373 -0
- package/src/cli/handlers/run.test.ts +111 -0
- package/src/cli/handlers/run.ts +86 -24
- package/src/cli/handlers/scenario.ts +2 -2
- package/src/cli/main.test.ts +21 -0
- package/src/cli/main.ts +246 -30
- package/src/cli/mcp/never-over-mcp.test.ts +101 -0
- package/src/cli/mcp/op-tools.ts +24 -2
- package/src/cli/mcp/workspace-tools.test.ts +5 -2
- package/src/cli/mcp/workspace-tools.ts +62 -7
- package/src/cli/registry.ts +74 -3
- 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/components/auto-release.ts +1 -1
- package/src/components/capability.ts +55 -0
- package/src/components/changed-files.ts +66 -0
- package/src/components/cli-support.ts +28 -5
- package/src/components/component.schema.json +17 -0
- package/src/components/component.ts +25 -0
- package/src/components/deploy-units.ts +3 -0
- package/src/components/driver.test.ts +52 -0
- package/src/components/driver.ts +31 -13
- package/src/components/fan-out-output.ts +42 -1
- package/src/components/fan-out-record.ts +69 -0
- package/src/components/fan-out-run.test.ts +17 -0
- package/src/components/fan-out-run.ts +304 -1
- package/src/components/fan-out-support.ts +3 -0
- package/src/components/fan-out-waves.test.ts +375 -0
- package/src/components/fan-out.ts +18 -17
- package/src/components/gated-wave-pipeline.ts +82 -0
- package/src/components/index.ts +1 -0
- package/src/components/layers.ts +30 -0
- package/src/components/pr-pipeline.ts +91 -0
- package/src/components/pr-run.test.ts +205 -0
- package/src/components/pr-run.ts +280 -0
- package/src/components/promote.ts +5 -0
- package/src/components/verbs/shell.test.ts +11 -0
- package/src/components/verbs/shell.ts +7 -3
- package/src/components/wave-plan.ts +124 -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/sandbox/bundle.ts +1 -1
- 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/gated-waves-bundle.test.ts +62 -0
- package/src/gated-waves.test.ts +118 -0
- package/src/gated-waves.ts +240 -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/index.ts +1 -0
- package/src/lexicon.ts +106 -3
- package/src/lifecycle/gate-ledger.ts +23 -0
- package/src/lifecycle/git.ts +102 -16
- 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/lint/post-synth.test.ts +19 -0
- package/src/lint/post-synth.ts +19 -1
- package/src/meta/declared-imports.test.ts +1 -1
- package/src/op/activities/activity-contracts.ts +84 -0
- package/src/op/activities/apply.test.ts +189 -5
- package/src/op/activities/apply.ts +230 -8
- package/src/op/activities/change-set.ts +114 -0
- package/src/op/activities/decide.ts +3 -0
- package/src/op/activities/factory.ts +675 -0
- package/src/op/activities/index.ts +9 -0
- package/src/op/activities/lexicon-upgrade.test.ts +160 -6
- package/src/op/activities/lexicon-upgrade.ts +154 -51
- package/src/op/activity-contract.test.ts +21 -1
- package/src/op/activity-contract.ts +19 -9
- package/src/op/builders.ts +23 -0
- 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/decide-backend.ts +5 -1
- package/src/op/decide-read-inputs.ts +33 -5
- package/src/op/emulator-lifecycle.test.ts +23 -0
- package/src/op/emulator-lifecycle.ts +52 -14
- package/src/op/factory.e2e.test.ts +232 -0
- package/src/op/factory.ts +118 -0
- 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 +11 -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/op-ir.test.ts +9 -0
- package/src/op/op-ir.ts +4 -0
- package/src/op/op-verb-class.ts +3 -0
- 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 +16 -3
- package/src/op/work-lease-run.test.ts +47 -0
- package/src/op/work-lease-run.ts +76 -11
- package/src/plan-summary.test.ts +263 -0
- package/src/plan-summary.ts +845 -0
- package/src/pr-forge.test.ts +237 -0
- package/src/pr-forge.ts +332 -0
- package/src/pr-loop-bundle.test.ts +46 -0
- package/src/pr-loop.test.ts +171 -0
- package/src/pr-loop.ts +324 -0
- package/src/provenance.ts +35 -0
- package/src/telemetry-attribution.test.ts +23 -1
- package/src/telemetry-attribution.ts +51 -0
- package/src/terraform/hcl2json.test.ts +21 -0
- package/src/terraform/hcl2json.ts +81 -0
- package/src/terraform/parse.ts +11 -74
- package/src/workspace/agent-cli.ts +7 -3
- package/src/workspace/agent.schema.json +22 -0
- package/src/workspace/app-kind.test.ts +196 -0
- package/src/workspace/box-cli.ts +113 -0
- package/src/workspace/box-factory.test.ts +323 -0
- package/src/workspace/box-factory.ts +136 -0
- package/src/workspace/box-listing-write.schema.json +204 -0
- package/src/workspace/box-listing.test.ts +234 -0
- package/src/workspace/box-listing.ts +324 -0
- package/src/workspace/box-publish.schema.json +498 -0
- package/src/workspace/box-publish.test.ts +204 -0
- package/src/workspace/box-publish.ts +420 -0
- package/src/workspace/box-ship.test.ts +96 -0
- package/src/workspace/broker-protocol.schema.json +186 -0
- package/src/workspace/broker-protocol.test.ts +122 -0
- package/src/workspace/broker-protocol.ts +317 -0
- package/src/workspace/builder-tiers.test.ts +146 -0
- package/src/workspace/change-set.schema.json +460 -0
- package/src/workspace/changes.schema.json +12 -20
- package/src/workspace/changes.ts +4 -1
- package/src/workspace/chant-migrations.e2e.test.ts +17 -5
- package/src/workspace/checks/boxes.test.ts +5 -0
- package/src/workspace/checks.ts +39 -8
- package/src/workspace/component-environments.test.ts +105 -0
- package/src/workspace/compose-graph.ts +7 -0
- package/src/workspace/composites.schema.json +23 -4
- package/src/workspace/composites.test.ts +8 -8
- package/src/workspace/composites.ts +13 -2
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +2 -2
- package/src/workspace/conformance/__fixtures__/reference-broker.ts +207 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.kind.mjs +32 -0
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +135 -0
- package/src/workspace/conformance/__writer_fixture__/contracts/contract.kind.mjs +37 -0
- package/src/workspace/conformance/__writer_fixture__/contracts/contract.schema.json +213 -0
- package/src/workspace/conformance/__writer_fixture__/decisions/points.json +228 -0
- package/src/workspace/conformance/__writer_fixture__/drivers/driver.kind.mjs +15 -0
- package/src/workspace/conformance/__writer_fixture__/drivers/driver.schema.json +38 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.kind.mjs +54 -0
- package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +289 -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 +603 -0
- package/src/workspace/conformance/broker.test.ts +122 -0
- package/src/workspace/conformance/broker.ts +792 -0
- package/src/workspace/conformance/conformance.e2e.test.ts +36 -3
- package/src/workspace/conformance/index.mjs +51 -3
- package/src/workspace/conformance/index.ts +107 -15
- package/src/workspace/conformance/vitest.ts +76 -2
- package/src/workspace/conformance/writer-conformance.e2e.test.ts +177 -0
- package/src/workspace/conformance/writer.test.ts +186 -0
- package/src/workspace/conformance/writer.ts +1191 -0
- package/src/workspace/decide.test.ts +208 -4
- package/src/workspace/decide.ts +433 -73
- package/src/workspace/decision-points.schema.json +11 -4
- package/src/workspace/declaration.schema.json +383 -5
- package/src/workspace/declaration.ts +315 -10
- package/src/workspace/environments.ts +66 -14
- package/src/workspace/factory-rules.test.ts +62 -0
- package/src/workspace/factory-rules.ts +218 -0
- package/src/workspace/graph-cli.ts +6 -3
- package/src/workspace/graph.schema.json +314 -0
- package/src/workspace/identity.test.ts +219 -0
- package/src/workspace/identity.ts +186 -0
- package/src/workspace/init.ts +82 -2
- package/src/workspace/intent-cli.ts +84 -13
- package/src/workspace/intent-patch-id.test.ts +162 -0
- package/src/workspace/intent-record.schema.json +336 -1
- package/src/workspace/intent-record.ts +96 -16
- package/src/workspace/intent-reviews.test.ts +76 -0
- package/src/workspace/intent-squash.test.ts +213 -0
- package/src/workspace/intent-why.test.ts +274 -0
- package/src/workspace/intent.schema.json +950 -9
- package/src/workspace/intent.test.ts +11 -2
- package/src/workspace/intent.ts +722 -65
- package/src/workspace/json-edit.test.ts +121 -0
- package/src/workspace/json-edit.ts +142 -0
- package/src/workspace/jsonc.ts +20 -0
- package/src/workspace/kinds.ts +201 -39
- package/src/workspace/lexicon-symbols.test.ts +148 -0
- package/src/workspace/lexicon-symbols.ts +125 -0
- package/src/workspace/lineage-upgrade.test.ts +63 -0
- package/src/workspace/lineage-upgrade.ts +146 -5
- package/src/workspace/plan-summary.schema.json +155 -0
- package/src/workspace/point-answer.schema.json +42 -2
- package/src/workspace/points-cli.ts +42 -16
- package/src/workspace/points-write.schema.json +163 -4
- package/src/workspace/points.schema.json +157 -2
- package/src/workspace/points.test.ts +13 -1
- package/src/workspace/points.ts +118 -12
- package/src/workspace/pr-report.schema.json +95 -0
- package/src/workspace/principal-classes.test.ts +175 -0
- package/src/workspace/principal-classes.ts +307 -0
- package/src/workspace/profiles.test.ts +159 -0
- package/src/workspace/read-contract.test.ts +2 -2
- package/src/workspace/reason-codes.test.ts +23 -3
- package/src/workspace/reason-codes.ts +61 -4
- package/src/workspace/record-sessions.ts +9 -8
- package/src/workspace/records-amend.schema.json +80 -0
- package/src/workspace/records-checkout.ts +106 -0
- package/src/workspace/records-cli.ts +123 -17
- package/src/workspace/records-close.schema.json +79 -0
- package/src/workspace/records-close.ts +31 -7
- package/src/workspace/records-concurrent.e2e.test.ts +135 -0
- package/src/workspace/records-concurrent.test.ts +145 -0
- package/src/workspace/records-formats.test.ts +46 -10
- package/src/workspace/records-new.schema.json +9 -0
- package/src/workspace/records-review.schema.json +80 -0
- package/src/workspace/records-sessions-write.test.ts +69 -0
- package/src/workspace/records-sessions.test.ts +22 -0
- package/src/workspace/records-uncommitted.test.ts +171 -0
- package/src/workspace/records-write.ts +219 -30
- package/src/workspace/records.schema.json +88 -1
- package/src/workspace/records.ts +76 -8
- package/src/workspace/reference-kinds/app/package.json +10 -0
- package/src/workspace/reference-kinds/app/workspace-kinds.json +45 -0
- package/src/workspace/run-statement.schema.json +622 -0
- package/src/workspace/runs-cli.ts +710 -0
- package/src/workspace/runs-statement.test.ts +239 -0
- package/src/workspace/runs-write.schema.json +994 -0
- package/src/workspace/runs.schema.json +1257 -0
- package/src/workspace/runs.test.ts +281 -0
- package/src/workspace/runs.ts +970 -0
- package/src/workspace/session-kinds.ts +3 -3
- package/src/workspace/squash.ts +198 -0
- package/src/workspace/status-contract.test.ts +10 -2
- package/src/workspace/status-gates.ts +25 -9
- package/src/workspace/status.schema.json +252 -4
- package/src/workspace/status.ts +110 -1
- package/src/workspace/symbols.ts +191 -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/run-commits.ts +115 -0
- package/src/workspace/trust/run-statement.ts +226 -0
- package/src/workspace/trust/seal.ts +58 -1
- package/src/workspace/trust/verify-cli.ts +6 -5
- package/src/workspace/trust/verify.ts +39 -6
- package/src/workspace/wip-cli.ts +97 -0
- package/src/workspace/wip-write.schema.json +504 -0
- package/src/workspace/wip.schema.json +334 -0
- package/src/workspace/wip.test.ts +294 -0
- package/src/workspace/wip.ts +753 -0
- package/src/workspace/work-cli.e2e.test.ts +28 -1
- package/src/workspace/work-cli.ts +136 -8
- package/src/workspace/work-evidence.schema.json +5 -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-size.test.ts +77 -0
- package/src/workspace/work-size.ts +93 -0
- package/src/workspace/work.ts +82 -0
- package/src/workspace/workspace-kinds.schema.json +122 -0
- package/src/workspace/workspace-principals.schema.json +60 -0
- package/src/workspace/write-lock-cli.ts +131 -0
- package/src/workspace/write-lock.schema.json +172 -0
- package/src/workspace/write-lock.test.ts +189 -0
- package/src/workspace/write-lock.ts +474 -0
- package/src/workspace/write-scope.test.ts +144 -3
- package/src/workspace/write-scope.ts +192 -45
- package/src/yaml.test.ts +111 -1
- package/src/yaml.ts +141 -549
|
@@ -15,10 +15,15 @@
|
|
|
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";
|
|
21
|
-
import { realpathSync } from "node:fs";
|
|
26
|
+
import { readFileSync, realpathSync } from "node:fs";
|
|
22
27
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
23
28
|
import { formatError } from "../cli/format";
|
|
24
29
|
import type { CommandContext } from "../cli/registry";
|
|
@@ -35,6 +40,7 @@ import {
|
|
|
35
40
|
loadRecordKind,
|
|
36
41
|
normalisePrincipal,
|
|
37
42
|
readRecords,
|
|
43
|
+
readSessionSubjects,
|
|
38
44
|
RECORD_SEAL_FIELD,
|
|
39
45
|
RecordReadError,
|
|
40
46
|
type LoadedRecordKind,
|
|
@@ -48,8 +54,18 @@ import {
|
|
|
48
54
|
type SealInput,
|
|
49
55
|
type VerdictAttestation,
|
|
50
56
|
} from "./records";
|
|
57
|
+
import { readCheckoutHead, worktreeStates, type CheckoutView, type WorktreeState } from "./records-checkout";
|
|
58
|
+
import { lastWriteReader, type LastWrite } from "./write-lock";
|
|
59
|
+
|
|
60
|
+
function readTextOrNull(file: string): string | null {
|
|
61
|
+
try {
|
|
62
|
+
return readFileSync(file, "utf-8");
|
|
63
|
+
} catch {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
51
67
|
import { gitTree, workingTree, type WorkspaceTree } from "./tree";
|
|
52
|
-
import type { DecisionWork } from "./work";
|
|
68
|
+
import type { DecisionWork, WorkAnswer } from "./work";
|
|
53
69
|
import { activeAttestors, type CommitAttestor, type ProvenanceLevel } from "./trust/attestor";
|
|
54
70
|
import { policyAtBase, recordProvenance, resolveBase, type BaseSource, type RecordProvenance } from "./trust/provenance";
|
|
55
71
|
import { returnedPolicy, type TrustPolicy } from "./trust/policy";
|
|
@@ -62,7 +78,7 @@ export const RECORDS_CONTRACT_VERSION = 1;
|
|
|
62
78
|
export const RECORDS_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/records/v1/records.schema.json";
|
|
63
79
|
|
|
64
80
|
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)";
|
|
81
|
+
"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
82
|
|
|
67
83
|
/** Exit code when the read worked and a record falls below `--require`. */
|
|
68
84
|
export const EXIT_BELOW_REQUIRED = 2;
|
|
@@ -81,6 +97,8 @@ export interface RecordsQuery {
|
|
|
81
97
|
* false; the intent graph passes false, since it raises the warning itself.
|
|
82
98
|
*/
|
|
83
99
|
workGaps?: boolean;
|
|
100
|
+
/** List only the records that are modified or new against `HEAD` (#3160). Needs git and a working-tree read. */
|
|
101
|
+
uncommitted?: boolean;
|
|
84
102
|
}
|
|
85
103
|
|
|
86
104
|
/**
|
|
@@ -108,6 +126,26 @@ export type RecordView = RecordEntry & {
|
|
|
108
126
|
* (`record-decided.ts`). The intent graph's windows open at this commit.
|
|
109
127
|
*/
|
|
110
128
|
decidedIn?: DecidedIn | null;
|
|
129
|
+
/**
|
|
130
|
+
* For a read in the working tree of a git repository (#3160): whether the
|
|
131
|
+
* record's file is as `HEAD` holds it (`committed`), changed from it
|
|
132
|
+
* (`modified`), or not in it (`new`), staged or not. Absent under `--at`.
|
|
133
|
+
*/
|
|
134
|
+
worktree?: WorktreeState;
|
|
135
|
+
/**
|
|
136
|
+
* Beside `worktree` (#3173): the last chant write of the record's file, from
|
|
137
|
+
* the write journal (`write-lock.ts`), while the file holds the text it
|
|
138
|
+
* left, or null.
|
|
139
|
+
*/
|
|
140
|
+
lastWrite?: LastWrite | null;
|
|
141
|
+
/**
|
|
142
|
+
* For a work kind whose work block names an answer kind (#3147): the
|
|
143
|
+
* decision-point answers about the item, the answer records whose
|
|
144
|
+
* `constrains` names its id, joined by id and never copied onto the item.
|
|
145
|
+
* Read at the same revision; in the working tree, with the questions a
|
|
146
|
+
* steward keeps on the lifecycle ledger (#2786) laid over it.
|
|
147
|
+
*/
|
|
148
|
+
answers?: WorkAnswer[];
|
|
111
149
|
};
|
|
112
150
|
|
|
113
151
|
/** The role in the trust policy whose holders' verdicts the quorum does not count (#2671). */
|
|
@@ -134,6 +172,10 @@ export type RecordsDocument =
|
|
|
134
172
|
/** The directory pinned paths resolve in, from the repository root: the workspace holding the kind file, or the repository root (#2549). */
|
|
135
173
|
workspaceRoot: string;
|
|
136
174
|
current: boolean;
|
|
175
|
+
/** Whether only the records `HEAD` doesn't hold were listed (`--uncommitted`, #3160). */
|
|
176
|
+
uncommitted: boolean;
|
|
177
|
+
/** 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`. */
|
|
178
|
+
checkout?: CheckoutView;
|
|
137
179
|
trust: TrustView;
|
|
138
180
|
records: RecordView[];
|
|
139
181
|
summary: { total: number; valid: number; invalid: number; superseded: number };
|
|
@@ -227,7 +269,8 @@ export async function queryDeclaredRecords(kinds: { declared: RecordKindDeclarat
|
|
|
227
269
|
const doc = await queryRecords({ ...query, kind: k.file });
|
|
228
270
|
out.push({ ...doc, declared: { member: k.declared.member, path: k.declared.path, name: k.declared.name } });
|
|
229
271
|
}
|
|
230
|
-
|
|
272
|
+
// The spec is every current spec record; an --uncommitted read lists only some, so it has none.
|
|
273
|
+
return { $schema: RECORDS_OUTPUT_SCHEMA_ID, contract: RECORDS_CONTRACT_VERSION, kinds: out, ...(query.current && !query.uncommitted ? { spec: specOf(out) } : {}) };
|
|
231
274
|
}
|
|
232
275
|
|
|
233
276
|
/** A records read, before provenance. */
|
|
@@ -345,17 +388,15 @@ export async function readRecordsFor(
|
|
|
345
388
|
if (at === null && query.overlay) source = await query.overlay(source, { loaded, root });
|
|
346
389
|
const history = top ? gitHistory(top, at ?? "HEAD", workspaceRoot) : undefined;
|
|
347
390
|
// A session kind's verdicts name records of another kind, read from the same tree (#2673).
|
|
348
|
-
|
|
349
|
-
if (loaded.kind.session) {
|
|
350
|
-
const subjectKind = await loadRecordKind(resolve(dirname(loaded.file), loaded.kind.session.subjects.kind), cwd);
|
|
351
|
-
subjects = { records: (await readRecords(subjectKind, { root, source })).records, reviews: subjectKind.kind.reviews?.field ?? "reviews" };
|
|
352
|
-
}
|
|
391
|
+
const subjects = await readSessionSubjects(loaded, root, source);
|
|
353
392
|
const result = await readRecords(loaded, { root, source, current: !!query.current, assets, workspaceRoot, ...(history ? { history } : {}), ...(subjects ? { subjects } : {}) });
|
|
354
393
|
return { loaded, root, top, at, workspaceRoot, tree: assets, result };
|
|
355
394
|
}
|
|
356
395
|
|
|
357
396
|
/** Run the query and build the document `--json` prints. Never throws a {@link RecordReadError}. */
|
|
358
397
|
export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument> {
|
|
398
|
+
// A revision has no working tree to hold anything uncommitted; the CLI and the MCP tool refuse the pair first.
|
|
399
|
+
if (query.uncommitted && query.at !== undefined) throw new Error("--uncommitted reads the working tree and takes no --at");
|
|
359
400
|
try {
|
|
360
401
|
const { loaded, root, top, at, workspaceRoot, tree, result } = await readRecordsFor(query);
|
|
361
402
|
// Provenance, judged by the policy at base and never by the tree read (#2547).
|
|
@@ -395,6 +436,43 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
|
|
|
395
436
|
const leases = await activeWorkLeases(loaded.file);
|
|
396
437
|
for (const r of records) if (r.id !== null) r.lease = leases.get(r.id) ?? null;
|
|
397
438
|
}
|
|
439
|
+
if (loaded.kind.work?.answers) {
|
|
440
|
+
// 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.
|
|
441
|
+
const [{ workAnswers }, { readLedgerAnswers, withLedgerAnswers }] = await Promise.all([import("./work"), import("./answers-ledger")]);
|
|
442
|
+
const answersRead = await readRecordsFor({
|
|
443
|
+
kind: resolve(dirname(loaded.file), loaded.kind.work.answers),
|
|
444
|
+
cwd: query.cwd,
|
|
445
|
+
...(query.at !== undefined ? { at: query.at } : {}),
|
|
446
|
+
overlay: async (base, { loaded: l, root: r }) =>
|
|
447
|
+
withLedgerAnswers(base, await readLedgerAnswers({ file: l.file, name: l.kind.name, dirRel: relative(r, l.dir).split(sep).join("/") || "." })),
|
|
448
|
+
}).catch((err: unknown) => {
|
|
449
|
+
if (err instanceof RecordReadError) return undefined;
|
|
450
|
+
throw err;
|
|
451
|
+
});
|
|
452
|
+
if (answersRead) {
|
|
453
|
+
const byItem = workAnswers(answersRead.result.records, records.flatMap((r) => (r.id !== null ? [r.id] : [])));
|
|
454
|
+
for (const r of records) if (r.id !== null) r.answers = byItem.get(r.id) ?? [];
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// Which records the checkout holds uncommitted, judged after every record is read, so links still resolve across the whole set (#3160).
|
|
458
|
+
let checkout: CheckoutView | undefined;
|
|
459
|
+
let listed = records;
|
|
460
|
+
let summary = result.summary;
|
|
461
|
+
if (at === null && top) {
|
|
462
|
+
const { head, ...rest } = readCheckoutHead(top, query.base);
|
|
463
|
+
const dir = relative(root, loaded.dir).split(sep).join("/") || ".";
|
|
464
|
+
const { states, deleted } = worktreeStates(top, head, dir, records.map((r) => r.path), new RegExp(loaded.kind.location.match));
|
|
465
|
+
for (const r of records) r.worktree = states.get(r.path) ?? "new";
|
|
466
|
+
const lastWrite = lastWriteReader(top);
|
|
467
|
+
for (const r of records) r.lastWrite = lastWrite(r.path, readTextOrNull(join(top, ...r.path.split("/"))));
|
|
468
|
+
checkout = { branch: rest.branch, head, base: rest.base, baseFrom: rest.baseFrom, deleted };
|
|
469
|
+
if (query.uncommitted) {
|
|
470
|
+
listed = records.filter((r) => r.worktree !== "committed");
|
|
471
|
+
summary = { ...summary, total: listed.length, valid: listed.filter((r) => r.valid).length, invalid: listed.filter((r) => !r.valid).length };
|
|
472
|
+
}
|
|
473
|
+
} else if (query.uncommitted && !top) {
|
|
474
|
+
throw new RecordReadError("not-a-git-repository", "--uncommitted compares the working tree with HEAD, and this directory is not in a git repository");
|
|
475
|
+
}
|
|
398
476
|
return {
|
|
399
477
|
$schema: RECORDS_OUTPUT_SCHEMA_ID,
|
|
400
478
|
contract: RECORDS_CONTRACT_VERSION,
|
|
@@ -408,9 +486,11 @@ export async function queryRecords(query: RecordsQuery): Promise<RecordsDocument
|
|
|
408
486
|
at,
|
|
409
487
|
workspaceRoot,
|
|
410
488
|
current: !!query.current,
|
|
489
|
+
uncommitted: !!query.uncommitted,
|
|
490
|
+
...(checkout ? { checkout } : {}),
|
|
411
491
|
trust: { base: base.commit, baseFrom: base.from, active: policy.active, signersPath: policy.signersPath, problems: policy.problems },
|
|
412
|
-
records,
|
|
413
|
-
summary
|
|
492
|
+
records: listed,
|
|
493
|
+
summary,
|
|
414
494
|
...(result.decisions ? { decisions: result.decisions } : {}),
|
|
415
495
|
};
|
|
416
496
|
} catch (err) {
|
|
@@ -561,6 +641,11 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
|
|
|
561
641
|
console.error(formatError({ message: `chant workspace records takes no argument but pin, new, amend, review or close (got ${args.extraPositional})`, hint: USAGE }));
|
|
562
642
|
return 1;
|
|
563
643
|
}
|
|
644
|
+
const pairing = uncommittedPairing(args);
|
|
645
|
+
if (pairing) {
|
|
646
|
+
console.error(formatError({ message: pairing, hint: USAGE }));
|
|
647
|
+
return 1;
|
|
648
|
+
}
|
|
564
649
|
if (!args.kind) return runDeclaredRecords(args);
|
|
565
650
|
if (args.require !== undefined && args.require !== "attested") {
|
|
566
651
|
console.error(formatError({ message: `--require takes one level, attested, not ${JSON.stringify(args.require)}`, hint: USAGE }));
|
|
@@ -579,13 +664,13 @@ export async function runWorkspaceRecords(ctx: CommandContext): Promise<number>
|
|
|
579
664
|
else console.log(formatSince(since));
|
|
580
665
|
return "error" in since ? 1 : 0;
|
|
581
666
|
}
|
|
582
|
-
const doc = await queryRecords({ kind: args.kind, current: args.current, at: args.at, base: args.base, cwd: process.cwd() });
|
|
667
|
+
const doc = await queryRecords({ kind: args.kind, current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd() });
|
|
583
668
|
if (args.json) {
|
|
584
669
|
console.log(JSON.stringify(doc, null, 2));
|
|
585
670
|
} else if ("error" in doc) {
|
|
586
671
|
console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
|
|
587
672
|
} else {
|
|
588
|
-
console.log(formatRecords(doc.records, doc.summary, doc.at));
|
|
673
|
+
console.log(formatRecords(doc.records, doc.summary, doc.at, doc.checkout));
|
|
589
674
|
}
|
|
590
675
|
if ("error" in doc) return 1;
|
|
591
676
|
if (args.require) {
|
|
@@ -643,7 +728,7 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
|
|
|
643
728
|
return 1;
|
|
644
729
|
}
|
|
645
730
|
if (args.since !== undefined) return runDeclaredSince(declared, args);
|
|
646
|
-
const set = await queryDeclaredRecords(declared, { current: args.current, at: args.at, base: args.base, cwd: process.cwd() });
|
|
731
|
+
const set = await queryDeclaredRecords(declared, { current: args.current, uncommitted: args.uncommitted, at: args.at, base: args.base, cwd: process.cwd() });
|
|
647
732
|
if (args.json) console.log(JSON.stringify(set, null, 2));
|
|
648
733
|
for (const doc of args.json ? [] : set.kinds) {
|
|
649
734
|
if ("error" in doc) {
|
|
@@ -651,7 +736,7 @@ async function runDeclaredRecords(args: CommandContext["args"]): Promise<number>
|
|
|
651
736
|
continue;
|
|
652
737
|
}
|
|
653
738
|
console.log(`${doc.declared.name ?? doc.kind.name} (${doc.declared.path})`);
|
|
654
|
-
console.log(formatRecords(doc.records, doc.summary, doc.at));
|
|
739
|
+
console.log(formatRecords(doc.records, doc.summary, doc.at, doc.checkout));
|
|
655
740
|
}
|
|
656
741
|
if (set.kinds.some((d) => "error" in d)) return 1;
|
|
657
742
|
if (args.require) {
|
|
@@ -717,7 +802,20 @@ export function belowRequired(records: RecordView[], required: ProvenanceLevel):
|
|
|
717
802
|
return records.filter((r) => r.provenance.level !== required);
|
|
718
803
|
}
|
|
719
804
|
|
|
720
|
-
|
|
805
|
+
/** Why `--uncommitted` can't be given with the other flags given, or undefined when it can (#3160). */
|
|
806
|
+
function uncommittedPairing(args: CommandContext["args"]): string | undefined {
|
|
807
|
+
if (!args.uncommitted) return undefined;
|
|
808
|
+
if (args.at !== undefined) return "--uncommitted reads the working tree against HEAD, and takes no --at";
|
|
809
|
+
if (args.since !== undefined) return "--uncommitted takes no --since: --since compares two revisions";
|
|
810
|
+
return undefined;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function formatRecords(
|
|
814
|
+
records: RecordView[],
|
|
815
|
+
summary: { total: number; valid: number; invalid: number; superseded: number },
|
|
816
|
+
at: string | null,
|
|
817
|
+
checkout?: CheckoutView,
|
|
818
|
+
): string {
|
|
721
819
|
const lines: string[] = [];
|
|
722
820
|
const idWidth = Math.max(2, ...records.map((r) => (r.id ?? "-").length));
|
|
723
821
|
const stateWidth = Math.max(5, ...records.map((r) => (r.state ?? "-").length));
|
|
@@ -728,7 +826,8 @@ function formatRecords(records: RecordView[], summary: { total: number; valid: n
|
|
|
728
826
|
const remediated = r.remediatedBy.length > 0 ? ` remediated by ${r.remediatedBy.join(", ")}` : "";
|
|
729
827
|
const attested = r.provenance.level === "attested" ? ` attested by ${r.provenance.principal}` : "";
|
|
730
828
|
const sealed = r.attested === true ? ` sealed by ${(r.data?.[RECORD_SEAL_FIELD] as { signer: string }).signer}` : "";
|
|
731
|
-
|
|
829
|
+
const uncommitted = r.worktree === "new" || r.worktree === "modified" ? ` (${r.worktree}, uncommitted)` : "";
|
|
830
|
+
lines.push(`${(r.id ?? "-").padEnd(idWidth)} ${(r.state ?? "-").padEnd(stateWidth)} ${title}${superseded}${remediated}${attested}${sealed}${uncommitted}${flag}`);
|
|
732
831
|
if (r.ready !== undefined) {
|
|
733
832
|
const blocked = (r.blockedBy ?? []).map((b) => `${b.id} (${b.state ?? "unknown"})`).join(", ");
|
|
734
833
|
const implemented = (r.implements ?? []).map((d) => `${d.id} (${d.state ?? "unknown"})`).join(", ");
|
|
@@ -745,9 +844,16 @@ function formatRecords(records: RecordView[], summary: { total: number; valid: n
|
|
|
745
844
|
lines.push(`${" ".repeat(idWidth + 2)}quorum ${q.agreed} of ${q.need} agreed, ${verdict}; ${q.notCounted.length} not counted${concerns}`);
|
|
746
845
|
}
|
|
747
846
|
}
|
|
847
|
+
for (const path of checkout?.deleted ?? []) lines.push(`${"-".padEnd(idWidth)} ${"-".padEnd(stateWidth)} ${path} (deleted, uncommitted)`);
|
|
748
848
|
lines.push(
|
|
749
849
|
`${summary.total} records${at ? ` at ${at.slice(0, 8)}` : ""}: ${summary.valid} valid, ${summary.invalid} invalid, ${summary.superseded} superseded`,
|
|
750
850
|
);
|
|
851
|
+
if (checkout) {
|
|
852
|
+
const branch = checkout.branch ?? "a detached HEAD";
|
|
853
|
+
const head = checkout.head ? ` at ${checkout.head.slice(0, 8)}` : " with no commits";
|
|
854
|
+
const base = checkout.base ? `, forked from ${checkout.base.slice(0, 8)} of ${checkout.baseFrom}` : "";
|
|
855
|
+
lines.push(`working tree on ${branch}${head}${base}`);
|
|
856
|
+
}
|
|
751
857
|
return lines.join("\n");
|
|
752
858
|
}
|
|
753
859
|
|
|
@@ -108,6 +108,11 @@
|
|
|
108
108
|
"$ref": "#/$defs/warning"
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
|
+
"digest": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
114
|
+
"description": "Added in contract 1 by #3173. The record's digest after the write, as chant workspace records --json reports it (with --dry-run, the digest it would have). Pass it as --expect to the next amend, review or close of the record, so that write is refused with record-conflict if someone else wrote the record in between."
|
|
115
|
+
},
|
|
111
116
|
"text": {
|
|
112
117
|
"type": "string",
|
|
113
118
|
"description": "With --dry-run only: the whole text of the file the command would write."
|
|
@@ -171,8 +176,12 @@
|
|
|
171
176
|
"write-usage-invalid",
|
|
172
177
|
"record-not-found",
|
|
173
178
|
"record-closed",
|
|
179
|
+
"record-conflict",
|
|
180
|
+
"write-lock-timeout",
|
|
181
|
+
"write-lock-not-held",
|
|
174
182
|
"write-scope-member",
|
|
175
183
|
"write-scope-kind",
|
|
184
|
+
"write-scope-class-unknown",
|
|
176
185
|
"agent-unknown",
|
|
177
186
|
"record-unparseable",
|
|
178
187
|
"record-schema-invalid",
|
|
@@ -192,6 +201,76 @@
|
|
|
192
201
|
}
|
|
193
202
|
}
|
|
194
203
|
},
|
|
204
|
+
"conflict": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"description": "Added in contract 1 by #3173. With record-conflict only: the record as it is now. Re-read it, and write again with --expect set to digest if the change still stands.",
|
|
207
|
+
"required": [
|
|
208
|
+
"id",
|
|
209
|
+
"path",
|
|
210
|
+
"expected",
|
|
211
|
+
"digest",
|
|
212
|
+
"lastWrite"
|
|
213
|
+
],
|
|
214
|
+
"properties": {
|
|
215
|
+
"id": {
|
|
216
|
+
"type": "string"
|
|
217
|
+
},
|
|
218
|
+
"path": {
|
|
219
|
+
"type": "string",
|
|
220
|
+
"description": "The record file, from the repository root."
|
|
221
|
+
},
|
|
222
|
+
"expected": {
|
|
223
|
+
"type": "string",
|
|
224
|
+
"description": "The digest given with --expect."
|
|
225
|
+
},
|
|
226
|
+
"digest": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"pattern": "^[0-9a-f]{64}$",
|
|
229
|
+
"description": "The record's digest now."
|
|
230
|
+
},
|
|
231
|
+
"lastWrite": {
|
|
232
|
+
"description": "The last chant write of the record file, from the write journal in the git directory, while the file still holds the text it left; null when the journal does not know it.",
|
|
233
|
+
"oneOf": [
|
|
234
|
+
{
|
|
235
|
+
"type": "null"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"type": "object",
|
|
239
|
+
"required": [
|
|
240
|
+
"verb",
|
|
241
|
+
"by",
|
|
242
|
+
"agent",
|
|
243
|
+
"at"
|
|
244
|
+
],
|
|
245
|
+
"properties": {
|
|
246
|
+
"verb": {
|
|
247
|
+
"type": "string",
|
|
248
|
+
"description": "The write command, such as records amend or points answer."
|
|
249
|
+
},
|
|
250
|
+
"by": {
|
|
251
|
+
"type": [
|
|
252
|
+
"string",
|
|
253
|
+
"null"
|
|
254
|
+
],
|
|
255
|
+
"description": "The principal the write named, or null when it named none."
|
|
256
|
+
},
|
|
257
|
+
"agent": {
|
|
258
|
+
"type": [
|
|
259
|
+
"string",
|
|
260
|
+
"null"
|
|
261
|
+
],
|
|
262
|
+
"description": "The agent session it was made in (CHANT_AGENT), or null."
|
|
263
|
+
},
|
|
264
|
+
"at": {
|
|
265
|
+
"type": "string",
|
|
266
|
+
"description": "When, as an ISO 8601 time."
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
195
274
|
"path": false
|
|
196
275
|
}
|
|
197
276
|
}
|
|
@@ -10,10 +10,11 @@
|
|
|
10
10
|
* in place and writes only when the session comes back valid: a verdict
|
|
11
11
|
* naming a record the subjects lack is refused with
|
|
12
12
|
* `session-verdict-unknown-record`. A session already closed is
|
|
13
|
-
* `record-closed`. It writes one file or none and never commits.
|
|
13
|
+
* `record-closed`. It writes one file or none and never commits. Like them
|
|
14
|
+
* it holds the working tree's write lock, and takes `--expect <digest>`
|
|
15
|
+
* (#3173).
|
|
14
16
|
*/
|
|
15
17
|
|
|
16
|
-
import { writeFileSync } from "node:fs";
|
|
17
18
|
import type { ReasonCode } from "./reason-codes";
|
|
18
19
|
import { sessionSeal } from "./record-sessions";
|
|
19
20
|
import { RECORD_REASON_CODES } from "./records";
|
|
@@ -28,12 +29,15 @@ import {
|
|
|
28
29
|
RECORDS_WRITE_CONTRACT_VERSION,
|
|
29
30
|
RecordWriteError,
|
|
30
31
|
refuseOutOfScope,
|
|
32
|
+
refuseStale,
|
|
31
33
|
replaceFields,
|
|
32
34
|
stableJson,
|
|
33
|
-
|
|
35
|
+
validatedEntry,
|
|
36
|
+
writeWho,
|
|
34
37
|
type WriteFailure,
|
|
35
38
|
type WriteResult,
|
|
36
39
|
} from "./records-write";
|
|
40
|
+
import { noteWrites, withWriteLock, writeFileAtomic, WRITE_LOCK_CODES } from "./write-lock";
|
|
37
41
|
import { headCommit } from "./session-kinds";
|
|
38
42
|
import { WRITE_SCOPE_CODES } from "./write-scope";
|
|
39
43
|
|
|
@@ -45,6 +49,8 @@ export const CLOSE_ERROR_CODES = [
|
|
|
45
49
|
"write-usage-invalid",
|
|
46
50
|
"record-not-found",
|
|
47
51
|
"record-closed",
|
|
52
|
+
"record-conflict",
|
|
53
|
+
...WRITE_LOCK_CODES,
|
|
48
54
|
...WRITE_SCOPE_CODES,
|
|
49
55
|
...RECORD_REASON_CODES,
|
|
50
56
|
] as const satisfies readonly ReasonCode[];
|
|
@@ -71,6 +77,8 @@ export interface CloseRecordOptions {
|
|
|
71
77
|
now?: Date;
|
|
72
78
|
/** The agent session the write is made in (#2548). */
|
|
73
79
|
agent?: string;
|
|
80
|
+
/** The session's digest the caller last read (#3173): the close is refused with record-conflict when it has another. */
|
|
81
|
+
expect?: string;
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
/** An ISO 8601 time in UTC to the second, as the reference session schema's dateTime takes it. */
|
|
@@ -78,8 +86,19 @@ function isoSeconds(d: Date): string {
|
|
|
78
86
|
return d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
79
87
|
}
|
|
80
88
|
|
|
81
|
-
/** `records close`: close one open session and seal it. */
|
|
89
|
+
/** `records close`: close one open session and seal it, holding the write lock (#3173). */
|
|
82
90
|
export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocument> {
|
|
91
|
+
try {
|
|
92
|
+
if (opts.expect !== undefined && !/^[0-9a-f]{64}$/.test(opts.expect)) {
|
|
93
|
+
throw new RecordWriteError("write-usage-invalid", `--expect takes a record's digest, 64 lowercase hex digits as records --json prints it, not ${JSON.stringify(opts.expect)}`);
|
|
94
|
+
}
|
|
95
|
+
return await withWriteLock(opts.cwd, writeWho("records close", opts), opts.dryRun, () => closeRecordLocked(opts));
|
|
96
|
+
} catch (err) {
|
|
97
|
+
return failure<CloseErrorCode>(RECORDS_CLOSE_SCHEMA_ID, err);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function closeRecordLocked(opts: CloseRecordOptions): Promise<CloseDocument> {
|
|
83
102
|
try {
|
|
84
103
|
const o = await open(opts.kind, opts.cwd);
|
|
85
104
|
refuseOutOfScope(o, "close", opts.cwd, opts);
|
|
@@ -92,6 +111,7 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
|
|
|
92
111
|
if (closedState === undefined) throw new RecordWriteError("write-usage-invalid", `the ${kind.name} kind lists no closed state, so a session of it can't close`);
|
|
93
112
|
const before = await readAll(o, o.source);
|
|
94
113
|
const target = findRecord(before, opts.id, kind.name);
|
|
114
|
+
refuseStale(o, target, opts.id, opts.expect);
|
|
95
115
|
if (target.state !== null && (kind.closedStates ?? []).includes(target.state)) {
|
|
96
116
|
throw new RecordWriteError("record-closed", `${opts.id} is already ${target.state}, and a closed session is sealed and stays as it is`);
|
|
97
117
|
}
|
|
@@ -115,8 +135,11 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
|
|
|
115
135
|
if (text === undefined || sessionSeal(text, decl.seal, kind.format) !== digest) {
|
|
116
136
|
throw new RecordWriteError("record-unparseable", `${target.path}: the ${decl.seal} line can't be added without changing the text it seals`);
|
|
117
137
|
}
|
|
118
|
-
const
|
|
119
|
-
if (!opts.dryRun)
|
|
138
|
+
const entry = await validatedEntry(o, before, target.path, text);
|
|
139
|
+
if (!opts.dryRun) {
|
|
140
|
+
writeFileAtomic(abs(o, target.path), text);
|
|
141
|
+
noteWrites(o.root, [{ path: target.path, text }], writeWho("records close", opts));
|
|
142
|
+
}
|
|
120
143
|
const written = { ...merged, [decl.seal]: digest };
|
|
121
144
|
return {
|
|
122
145
|
$schema: RECORDS_CLOSE_SCHEMA_ID,
|
|
@@ -128,7 +151,8 @@ export async function closeRecord(opts: CloseRecordOptions): Promise<CloseDocume
|
|
|
128
151
|
seal: { field: decl.seal, digest },
|
|
129
152
|
closedRev,
|
|
130
153
|
dryRun: !!opts.dryRun,
|
|
131
|
-
warnings,
|
|
154
|
+
warnings: entry.warnings,
|
|
155
|
+
digest: entry.digest,
|
|
132
156
|
...(opts.dryRun ? { text } : {}),
|
|
133
157
|
};
|
|
134
158
|
} catch (err) {
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Concurrent writers in one working tree through the command line (#3173,
|
|
3
|
+
* ws-089): three `records amend --expect` processes started at once against
|
|
4
|
+
* one record, two people's and an agent's (CHANT_AGENT). Exactly one wins;
|
|
5
|
+
* the others exit 1 with record-conflict and the digest to retry from, and the
|
|
6
|
+
* retry wins. Then a batch: `lock acquire` holds the lock, a write run with
|
|
7
|
+
* CHANT_WRITE_LOCK goes ahead, and one without it is refused with
|
|
8
|
+
* write-lock-timeout until `lock release`. The in-process cases are in
|
|
9
|
+
* records-concurrent.test.ts.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { execFile } from "node:child_process";
|
|
13
|
+
import { readFileSync } from "node:fs";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { pathToFileURL } from "node:url";
|
|
16
|
+
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
17
|
+
import { cleanScratch, git, REPO, repo } from "./__fixtures__/contract-repo";
|
|
18
|
+
import { parseFrontMatter } from "./records";
|
|
19
|
+
import { renderRecord } from "./records-write";
|
|
20
|
+
|
|
21
|
+
const DECISIONS = join(REPO, "docs", "design", "decisions");
|
|
22
|
+
const KIND = "decisions/decision.kind.mjs";
|
|
23
|
+
type Data = Record<string, unknown>;
|
|
24
|
+
|
|
25
|
+
const SAMPLE = (() => {
|
|
26
|
+
const fm = parseFrontMatter(readFileSync(join(DECISIONS, "ws-003-seal-scope.md"), "utf-8"));
|
|
27
|
+
if (!fm.ok) throw new Error(fm.message);
|
|
28
|
+
return fm.value;
|
|
29
|
+
})();
|
|
30
|
+
const proposal = (over: Data): Data => ({ ...structuredClone(SAMPLE), state: "proposed", choice: null, decided_by: null, decided_on: null, ...over });
|
|
31
|
+
|
|
32
|
+
let root: string;
|
|
33
|
+
beforeAll(() => {
|
|
34
|
+
root = repo(
|
|
35
|
+
{
|
|
36
|
+
"chant.workspace.json": JSON.stringify({
|
|
37
|
+
name: "box",
|
|
38
|
+
schema: 1,
|
|
39
|
+
members: [{ name: "app", dir: "app", kind: "other", because: "a plain Node server" }],
|
|
40
|
+
records: [{ kind: KIND }],
|
|
41
|
+
writeScope: { agent: { records: { decision: ["new", "amend", "review"] } } },
|
|
42
|
+
agents: [{ name: "app-agent", member: "app" }],
|
|
43
|
+
}),
|
|
44
|
+
[KIND]: readFileSync(join(DECISIONS, "decision.kind.mjs"), "utf-8"),
|
|
45
|
+
"decisions/decision.schema.json": readFileSync(join(DECISIONS, "decision.schema.json"), "utf-8"),
|
|
46
|
+
"decisions/ws-001-one.md": renderRecord(proposal({ id: "ws-001", title: "One" }), "\n# One\n"),
|
|
47
|
+
"app/server.mjs": "export const port = 8080;\n",
|
|
48
|
+
},
|
|
49
|
+
true,
|
|
50
|
+
);
|
|
51
|
+
git(root, "branch", "-M", "main");
|
|
52
|
+
});
|
|
53
|
+
afterAll(cleanScratch);
|
|
54
|
+
|
|
55
|
+
/** Run chant in the repository, without waiting on the others. */
|
|
56
|
+
function chant(args: string[], opts: { input?: string; env?: Record<string, string> } = {}): Promise<{ status: number; doc: Record<string, any> }> {
|
|
57
|
+
const loader = pathToFileURL(join(REPO, "node_modules", "tsx", "dist", "loader.mjs")).href;
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
const child = execFile(
|
|
60
|
+
process.execPath,
|
|
61
|
+
["--import", loader, join(REPO, "packages", "core", "src", "cli", "main.ts"), "workspace", ...args],
|
|
62
|
+
{ cwd: root, encoding: "utf-8", env: { ...process.env, TSX_DISABLE_CACHE: "1", ...opts.env } },
|
|
63
|
+
(err, stdout, stderr) => {
|
|
64
|
+
try {
|
|
65
|
+
resolve({ status: err ? Number((err as { code?: number }).code ?? 1) : 0, doc: JSON.parse(stdout) });
|
|
66
|
+
} catch {
|
|
67
|
+
reject(new Error(`chant ${args.join(" ")} printed no JSON: ${stderr}`));
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
child.stdin!.end(opts.input ?? "");
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const amend = (fields: Data, expect: string, env?: Record<string, string>) => chant(["records", "amend", "ws-001", "--kind", KIND, "--set", "-", "--expect", expect], { input: JSON.stringify(fields), env });
|
|
76
|
+
|
|
77
|
+
describe("concurrent writers through the command line (#3173)", () => {
|
|
78
|
+
test(
|
|
79
|
+
"three amends from one digest in three processes: one wins, two conflict, and their retries win",
|
|
80
|
+
async () => {
|
|
81
|
+
const read = await chant(["records", "--kind", KIND, "--json"]);
|
|
82
|
+
const d0 = read.doc.records.find((r: { id: string }) => r.id === "ws-001").digest as string;
|
|
83
|
+
const runs = await Promise.all([
|
|
84
|
+
amend({ title: "One, as alice has it" }, d0),
|
|
85
|
+
amend({ question: "What does bob ask?" }, d0),
|
|
86
|
+
amend({ constrains: ["member:app"] }, d0, { CHANT_AGENT: "app-agent" }),
|
|
87
|
+
]);
|
|
88
|
+
const won = runs.filter((r) => r.status === 0);
|
|
89
|
+
const lost = runs.filter((r) => r.status !== 0);
|
|
90
|
+
expect(won).toHaveLength(1);
|
|
91
|
+
expect(lost.map((r) => r.doc.error.code)).toEqual(["record-conflict", "record-conflict"]);
|
|
92
|
+
const d1 = won[0].doc.digest as string;
|
|
93
|
+
for (const r of lost) expect(r.doc.conflict).toMatchObject({ expected: d0, digest: d1, lastWrite: { verb: "records amend" } });
|
|
94
|
+
|
|
95
|
+
// Each loser retries from what it was told, one after the other.
|
|
96
|
+
const fieldsOf = (i: number) => [{ title: "One, as alice has it" }, { question: "What does bob ask?" }, { constrains: ["member:app"] }][i];
|
|
97
|
+
let digest = d1;
|
|
98
|
+
for (const r of lost) {
|
|
99
|
+
const i = runs.indexOf(r);
|
|
100
|
+
const retry = await amend(fieldsOf(i), digest, i === 2 ? { CHANT_AGENT: "app-agent" } : undefined);
|
|
101
|
+
expect(retry.status).toBe(0);
|
|
102
|
+
digest = retry.doc.digest;
|
|
103
|
+
}
|
|
104
|
+
const end = await chant(["records", "--kind", KIND, "--json"]);
|
|
105
|
+
const rec = end.doc.records.find((r: { id: string }) => r.id === "ws-001");
|
|
106
|
+
expect(rec.digest).toBe(digest);
|
|
107
|
+
expect(rec.data).toMatchObject({ title: "One, as alice has it", question: "What does bob ask?", constrains: ["member:app"] });
|
|
108
|
+
expect(rec.lastWrite).toMatchObject({ verb: "records amend" });
|
|
109
|
+
},
|
|
110
|
+
120_000,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
test(
|
|
114
|
+
"a batch holds the lock across calls: its own writes go ahead, another writer's waits and is refused",
|
|
115
|
+
async () => {
|
|
116
|
+
const taken = await chant(["lock", "acquire", "--holder", "hud:alice", "--ttl", "60s"]);
|
|
117
|
+
expect(taken).toMatchObject({ status: 0, doc: { verb: "acquire", holder: { verb: "batch", by: "hud:alice" } } });
|
|
118
|
+
const token = taken.doc.token as string;
|
|
119
|
+
const status = await chant(["lock"]);
|
|
120
|
+
expect(status.doc.holder).toMatchObject({ by: "hud:alice" });
|
|
121
|
+
expect(status.doc.token).toBeUndefined();
|
|
122
|
+
|
|
123
|
+
const outsider = await chant(["records", "review", "ws-001", "--kind", KIND, "--verdict", "agree", "--by", "bob"], { env: { CHANT_WRITE_LOCK_WAIT_MS: "300" } });
|
|
124
|
+
expect(outsider).toMatchObject({ status: 1, doc: { error: { code: "write-lock-timeout" } } });
|
|
125
|
+
expect(outsider.doc.error.message).toContain("hud:alice");
|
|
126
|
+
const inside = await chant(["records", "review", "ws-001", "--kind", KIND, "--verdict", "agree", "--by", "alice"], { env: { CHANT_WRITE_LOCK: token } });
|
|
127
|
+
expect(inside.status).toBe(0);
|
|
128
|
+
|
|
129
|
+
expect((await chant(["lock", "release", "--token", token])).status).toBe(0);
|
|
130
|
+
const after = await chant(["records", "review", "ws-001", "--kind", KIND, "--verdict", "agree", "--by", "bob"]);
|
|
131
|
+
expect(after.status).toBe(0);
|
|
132
|
+
},
|
|
133
|
+
120_000,
|
|
134
|
+
);
|
|
135
|
+
});
|