@intentius/chant 0.102.0 → 0.104.1
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/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/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/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/fan-out.d.ts +33 -0
- package/dist/cli/handlers/fan-out.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +7 -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/main.d.ts.map +1 -1
- package/dist/cli/mcp/op-tools.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +3 -2
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +63 -1
- package/dist/cli/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.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/verbs/shell.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/gated-waves.d.ts +147 -0
- package/dist/gated-waves.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon.d.ts +48 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/gate-ledger.d.ts +7 -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/lint/post-synth.d.ts +18 -0
- package/dist/lint/post-synth.d.ts.map +1 -1
- package/dist/op/activities/activity-contracts.d.ts +126 -0
- package/dist/op/activities/activity-contracts.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 +4 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activity-contract.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/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/factory.d.ts +83 -0
- package/dist/op/factory.d.ts.map +1 -0
- package/dist/op/index.d.ts +4 -1
- package/dist/op/index.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/types.d.ts +8 -0
- package/dist/op/types.d.ts.map +1 -1
- 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/telemetry-attribution.d.ts +31 -0
- package/dist/telemetry-attribution.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/vendor/js-yaml-load.d.ts +56 -0
- package/dist/vendor/js-yaml-load.d.ts.map +1 -0
- 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 +27 -3
- package/dist/workspace/box-factory.d.ts.map +1 -1
- 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/checks.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 +2 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/conformance/vitest.d.ts +9 -2
- package/dist/workspace/conformance/vitest.d.ts.map +1 -1
- package/dist/workspace/conformance/writer.d.ts +108 -14
- package/dist/workspace/conformance/writer.d.ts.map +1 -1
- 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 +65 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +153 -4
- 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/init.d.ts +21 -0
- package/dist/workspace/init.d.ts.map +1 -1
- package/dist/workspace/intent-cli.d.ts +1 -1
- package/dist/workspace/intent-cli.d.ts.map +1 -1
- package/dist/workspace/intent-record.d.ts +28 -1
- package/dist/workspace/intent-record.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts +102 -5
- 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 +48 -0
- 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-upgrade.d.ts +31 -2
- package/dist/workspace/lineage-upgrade.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/reason-codes.d.ts +33 -4
- package/dist/workspace/reason-codes.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-cli.d.ts +7 -0
- 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-write.d.ts +66 -6
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +28 -9
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/runs-cli.d.ts +138 -6
- package/dist/workspace/runs-cli.d.ts.map +1 -1
- package/dist/workspace/runs.d.ts +132 -11
- package/dist/workspace/runs.d.ts.map +1 -1
- package/dist/workspace/session-kinds.d.ts +1 -1
- package/dist/workspace/session-kinds.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 +2 -0
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +54 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/symbols.d.ts +16 -5
- package/dist/workspace/symbols.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 +6 -1
- package/dist/workspace/trust/seal.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.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/workspace-kinds.schema.json +122 -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 +6 -2
- package/dist/workspace/write-scope.d.ts.map +1 -1
- package/dist/yaml.d.ts +1 -1
- package/dist/yaml.d.ts.map +1 -1
- package/package.json +6 -1
- 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/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/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/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/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/operator.test.ts +21 -0
- package/src/cli/handlers/operator.ts +34 -3
- package/src/cli/handlers/pr.ts +373 -0
- package/src/cli/handlers/run.ts +1 -0
- package/src/cli/main.test.ts +20 -0
- package/src/cli/main.ts +185 -23
- 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 +1 -1
- package/src/cli/mcp/workspace-tools.ts +34 -6
- package/src/cli/registry.ts +63 -1
- 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/discovery/sandbox/bundle.ts +1 -1
- 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/index.ts +1 -0
- package/src/lexicon.ts +51 -0
- package/src/lifecycle/gate-ledger.ts +7 -0
- package/src/lifecycle/git.ts +102 -16
- 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/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 +6 -0
- 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/decide-backend.ts +5 -1
- package/src/op/decide-read-inputs.ts +33 -5
- package/src/op/factory.e2e.test.ts +232 -0
- package/src/op/factory.ts +118 -0
- package/src/op/index.ts +9 -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/types.ts +8 -0
- package/src/op/work-lease-run.ts +3 -0
- 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/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/vendor/js-yaml-load.test.ts +115 -0
- package/src/vendor/js-yaml-load.ts +1939 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +1 -1
- package/src/workspace/app-kind.test.ts +196 -0
- package/src/workspace/box-cli.ts +113 -0
- package/src/workspace/box-factory.test.ts +4 -1
- package/src/workspace/box-factory.ts +37 -4
- 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/chant-migrations.e2e.test.ts +17 -5
- package/src/workspace/checks/boxes.test.ts +3 -0
- package/src/workspace/checks.ts +10 -3
- package/src/workspace/component-environments.test.ts +105 -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 +7 -2
- package/src/workspace/conformance/__writer_fixture__/answers/answer.schema.json +42 -2
- 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 +96 -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 +14 -8
- package/src/workspace/conformance/__writer_fixture__/sessions/session.schema.json +187 -6
- package/src/workspace/conformance/__writer_fixture__/work/work.schema.json +25 -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 +2 -1
- package/src/workspace/conformance/index.mjs +22 -1
- package/src/workspace/conformance/index.ts +5 -1
- package/src/workspace/conformance/vitest.ts +33 -1
- package/src/workspace/conformance/writer-conformance.e2e.test.ts +49 -3
- package/src/workspace/conformance/writer.test.ts +32 -2
- package/src/workspace/conformance/writer.ts +339 -28
- package/src/workspace/decide.test.ts +208 -4
- package/src/workspace/decide.ts +427 -73
- package/src/workspace/decision-points.schema.json +11 -4
- package/src/workspace/declaration.schema.json +153 -4
- package/src/workspace/declaration.ts +141 -0
- 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 +2 -2
- package/src/workspace/graph.schema.json +67 -0
- package/src/workspace/identity.test.ts +23 -0
- package/src/workspace/init.ts +82 -2
- package/src/workspace/intent-cli.ts +26 -9
- package/src/workspace/intent-patch-id.test.ts +162 -0
- package/src/workspace/intent-record.schema.json +130 -3
- package/src/workspace/intent-record.ts +74 -28
- 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 +5 -5
- package/src/workspace/intent.schema.json +234 -4
- package/src/workspace/intent.ts +260 -66
- 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 +157 -2
- 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 +162 -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/profiles.test.ts +159 -0
- package/src/workspace/read-contract.test.ts +2 -2
- package/src/workspace/reason-codes.test.ts +13 -1
- package/src/workspace/reason-codes.ts +38 -4
- package/src/workspace/record-sessions.ts +9 -8
- package/src/workspace/records-amend.schema.json +78 -0
- package/src/workspace/records-cli.ts +20 -6
- package/src/workspace/records-close.schema.json +78 -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 +7 -0
- package/src/workspace/records-review.schema.json +78 -0
- package/src/workspace/records-sessions-write.test.ts +69 -0
- package/src/workspace/records-sessions.test.ts +22 -0
- package/src/workspace/records-write.ts +192 -25
- package/src/workspace/records.schema.json +16 -0
- package/src/workspace/records.ts +50 -9
- 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 +343 -14
- package/src/workspace/runs-statement.test.ts +239 -0
- package/src/workspace/runs-write.schema.json +416 -5
- package/src/workspace/runs.schema.json +451 -4
- package/src/workspace/runs.test.ts +39 -0
- package/src/workspace/runs.ts +296 -35
- package/src/workspace/session-kinds.ts +3 -3
- package/src/workspace/squash.ts +198 -0
- package/src/workspace/status-contract.test.ts +5 -2
- package/src/workspace/status-gates.ts +3 -1
- package/src/workspace/status.schema.json +126 -3
- package/src/workspace/status.ts +90 -1
- package/src/workspace/symbols.ts +20 -9
- package/src/workspace/trust/run-commits.ts +115 -0
- package/src/workspace/trust/run-statement.ts +226 -0
- package/src/workspace/trust/seal.ts +8 -2
- 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.ts +3 -0
- package/src/workspace/work-evidence.schema.json +3 -0
- package/src/workspace/work-size.test.ts +77 -0
- package/src/workspace/work-size.ts +93 -0
- package/src/workspace/workspace-kinds.schema.json +122 -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 +16 -1
- package/src/workspace/write-scope.ts +33 -11
- package/src/yaml.ts +31 -21
|
@@ -10,9 +10,16 @@
|
|
|
10
10
|
* - `runs start --from <file|->`, `runs end <id> --from <file|->` and `runs
|
|
11
11
|
* record --from <file|->` append a run's start, its end, or both, to
|
|
12
12
|
* `_agent-runs/<id>.jsonl` on `chant/lifecycle` (`runs-write.schema.json`).
|
|
13
|
+
* - `runs sign <id> --key <runner key.pem> | --envelope <file|->` appends a
|
|
14
|
+
* statement over an ended run, signed by a runner or steward key the trust
|
|
15
|
+
* policy at base lists, or signed elsewhere and checked here (#3192).
|
|
16
|
+
* `runs statement <id> --signer <principal>` prints the statement for a
|
|
17
|
+
* signer that holds its key elsewhere, and `runs verify [<id>] [--require
|
|
18
|
+
* signed]` judges every stored statement (`run-statement.schema.json`).
|
|
13
19
|
*
|
|
14
20
|
* chant records runs and never starts one: whatever ran the agent writes the
|
|
15
|
-
* record. Reads never fetch
|
|
21
|
+
* record. Reads never fetch, except a pull request ref `--follow-squash`
|
|
22
|
+
* needs (#3035).
|
|
16
23
|
*/
|
|
17
24
|
|
|
18
25
|
import { readFileSync } from "node:fs";
|
|
@@ -25,9 +32,12 @@ import type { ReasonCode } from "./reason-codes";
|
|
|
25
32
|
import { loadRecordKind, RecordReadError } from "./records";
|
|
26
33
|
import { queryRecords } from "./records-cli";
|
|
27
34
|
import {
|
|
35
|
+
appendRunStatement,
|
|
28
36
|
commitsByRunTrailer,
|
|
29
37
|
endRun,
|
|
30
38
|
foldRun,
|
|
39
|
+
joinPatchIdCommits,
|
|
40
|
+
joinSquashCommits,
|
|
31
41
|
joinTrailerCommits,
|
|
32
42
|
LEDGER_BRANCH,
|
|
33
43
|
readRuns,
|
|
@@ -43,6 +53,10 @@ import { locateWorkspace } from "./which-chant";
|
|
|
43
53
|
import { idList } from "./work";
|
|
44
54
|
import { AGENT_ENV } from "./write-scope";
|
|
45
55
|
import { execFileSync } from "node:child_process";
|
|
56
|
+
import { IN_TOTO_PAYLOAD_TYPE, loadRunnerKey, type DsseEnvelope } from "./trust/dsse";
|
|
57
|
+
import { policyAtBase, resolveBase, type BaseSource } from "./trust/provenance";
|
|
58
|
+
import type { TrustPolicy } from "./trust/policy";
|
|
59
|
+
import { attestRun, buildRunStatement, runStatementPayload, signRunStatement, verifyRunStatement, type RunAttestation, type RunStatement } from "./trust/run-statement";
|
|
46
60
|
|
|
47
61
|
/** The version of the `runs` documents this chant writes: read contract 1. */
|
|
48
62
|
export const RUNS_CONTRACT_VERSION = 1;
|
|
@@ -50,14 +64,20 @@ export const RUNS_CONTRACT_VERSION = 1;
|
|
|
50
64
|
/** `$id` of the JSON Schema for `runs --json`, shipped beside this file. */
|
|
51
65
|
export const RUNS_OUTPUT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/runs/v1/runs.schema.json";
|
|
52
66
|
|
|
53
|
-
/** `$id` of the JSON Schema for what `runs start|end|record` print. */
|
|
67
|
+
/** `$id` of the JSON Schema for what `runs start|end|record|sign` print. */
|
|
54
68
|
export const RUNS_WRITE_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/runs-write/v1/runs-write.schema.json";
|
|
55
69
|
|
|
70
|
+
/** `$id` of the JSON Schema for what `runs statement` and `runs verify` print (#3192). */
|
|
71
|
+
export const RUN_STATEMENT_SCHEMA_ID = "https://intentius.io/chant/schemas/workspace/run-statement/v1/run-statement.schema.json";
|
|
72
|
+
|
|
56
73
|
const USAGE = [
|
|
57
|
-
"chant workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--json]",
|
|
74
|
+
"chant workspace runs [--unit <id>] [--decision <id>] [--by <principal>] [--since <rev>] [--follow-squash] [--json]",
|
|
58
75
|
"chant workspace runs start --from <file|->",
|
|
59
76
|
"chant workspace runs end <run id> [--from <file|->]",
|
|
60
77
|
"chant workspace runs record --from <file|->",
|
|
78
|
+
"chant workspace runs sign <run id> (--key <runner key.pem> | --envelope <file|->) [--base <rev>]",
|
|
79
|
+
"chant workspace runs statement <run id> --signer <principal>",
|
|
80
|
+
"chant workspace runs verify [<run id>] [--require signed] [--base <rev>] [--json]",
|
|
61
81
|
].join("\n");
|
|
62
82
|
|
|
63
83
|
/** Why `runs` read nothing. Closed. */
|
|
@@ -65,13 +85,36 @@ export const RUNS_ERROR_CODES = [...WORKSPACE_ERROR_CODES] as const satisfies re
|
|
|
65
85
|
export type RunsErrorCode = (typeof RUNS_ERROR_CODES)[number];
|
|
66
86
|
|
|
67
87
|
/** Why part of the read is missing. The read still succeeds. Closed. */
|
|
68
|
-
export const RUNS_REASON_CODES = ["runs-no-ledger", "runs-ledger-malformed", "kind-unreadable"] as const satisfies readonly ReasonCode[];
|
|
88
|
+
export const RUNS_REASON_CODES = ["runs-no-ledger", "runs-ledger-malformed", "kind-unreadable", "squash-unfollowed"] as const satisfies readonly ReasonCode[];
|
|
69
89
|
export type RunsReasonCode = (typeof RUNS_REASON_CODES)[number];
|
|
70
90
|
|
|
71
91
|
/** Why a run write was refused or could not run. Closed. */
|
|
72
|
-
export const RUNS_WRITE_ERROR_CODES = [
|
|
92
|
+
export const RUNS_WRITE_ERROR_CODES = [
|
|
93
|
+
...WORKSPACE_ERROR_CODES,
|
|
94
|
+
"write-usage-invalid",
|
|
95
|
+
"write-input-invalid",
|
|
96
|
+
"run-exists",
|
|
97
|
+
"run-unknown",
|
|
98
|
+
"run-ended",
|
|
99
|
+
// runs sign (#3192)
|
|
100
|
+
"run-not-ended",
|
|
101
|
+
"trust-policy-unreadable",
|
|
102
|
+
"runner-key-invalid",
|
|
103
|
+
"runner-key-is-signer",
|
|
104
|
+
"runner-key-unlisted",
|
|
105
|
+
"envelope-unreadable",
|
|
106
|
+
"envelope-invalid",
|
|
107
|
+
"envelope-untrusted",
|
|
108
|
+
"run-statement-invalid",
|
|
109
|
+
"run-statement-signer-mismatch",
|
|
110
|
+
"run-statement-mismatch",
|
|
111
|
+
] as const satisfies readonly ReasonCode[];
|
|
73
112
|
export type RunsWriteErrorCode = (typeof RUNS_WRITE_ERROR_CODES)[number];
|
|
74
113
|
|
|
114
|
+
/** Why `runs statement` or `runs verify` printed nothing to judge. Closed. */
|
|
115
|
+
export const RUN_STATEMENT_ERROR_CODES = [...WORKSPACE_ERROR_CODES, "write-usage-invalid", "run-unknown", "run-not-ended"] as const satisfies readonly ReasonCode[];
|
|
116
|
+
export type RunStatementErrorCode = (typeof RUN_STATEMENT_ERROR_CODES)[number];
|
|
117
|
+
|
|
75
118
|
// ── Totals ───────────────────────────────────────────────────────────────────
|
|
76
119
|
|
|
77
120
|
/** Tokens and cost summed over some runs. */
|
|
@@ -130,6 +173,10 @@ export interface RunsQuery {
|
|
|
130
173
|
by?: string;
|
|
131
174
|
/** Only the runs that made a commit after `since` on HEAD, or started after its commit date. */
|
|
132
175
|
since?: string;
|
|
176
|
+
/** The revision whose trust policy judges the run statements (#3192). Default: origin/HEAD, then main, then master. */
|
|
177
|
+
base?: string;
|
|
178
|
+
/** Follow squash merges on HEAD to their pull requests' original commits, fetching missing pull request refs (#3035). */
|
|
179
|
+
followSquash?: boolean;
|
|
133
180
|
}
|
|
134
181
|
|
|
135
182
|
export interface RunsReason {
|
|
@@ -137,6 +184,31 @@ export interface RunsReason {
|
|
|
137
184
|
message: string;
|
|
138
185
|
}
|
|
139
186
|
|
|
187
|
+
/** The trust policy at base, as far as run statements need it (#3192). */
|
|
188
|
+
export interface RunsTrust {
|
|
189
|
+
base: string | null;
|
|
190
|
+
baseFrom: BaseSource | null;
|
|
191
|
+
/** The runner and steward keys at base, by principal and class. */
|
|
192
|
+
runners: { principal: string; class: "runner" | "service" }[];
|
|
193
|
+
/** Why the policy at base can't be read. Nothing verifies then. */
|
|
194
|
+
problems: string[];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/** The policy at base for run statements, and its summary. */
|
|
198
|
+
export function runsTrust(top: string, base?: string): { policy: TrustPolicy; trust: RunsTrust } {
|
|
199
|
+
const resolved = resolveBase(top, base);
|
|
200
|
+
const policy = policyAtBase(top, resolved);
|
|
201
|
+
return {
|
|
202
|
+
policy,
|
|
203
|
+
trust: { base: resolved.commit, baseFrom: resolved.from, runners: policy.runners.map((r) => ({ principal: r.principal, class: r.class })), problems: policy.problems },
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** Judge each run's statements in place against the runners at base. */
|
|
208
|
+
export function attestRuns(runs: Iterable<RunView>, policy: TrustPolicy): void {
|
|
209
|
+
for (const r of runs) r.attestation = attestRun(r, policy.problems.length > 0 ? [] : policy.runners);
|
|
210
|
+
}
|
|
211
|
+
|
|
140
212
|
/** What `runs --json` prints. */
|
|
141
213
|
export type RunsDocument =
|
|
142
214
|
| {
|
|
@@ -145,8 +217,11 @@ export type RunsDocument =
|
|
|
145
217
|
chant: string;
|
|
146
218
|
workspace: { name: string; root: string };
|
|
147
219
|
ledger: { branch: string; dir: string; commit: string | null };
|
|
148
|
-
|
|
149
|
-
|
|
220
|
+
/** The options given. `followSquash` was added within contract 1 (#3035). */
|
|
221
|
+
filter: { unit: string | null; decision: string | null; by: string | null; since: string | null; followSquash?: boolean };
|
|
222
|
+
/** The trust policy the run statements are judged by (#3192). */
|
|
223
|
+
trust: RunsTrust;
|
|
224
|
+
/** Newest first, by when each started. Each carries its `attestation`. */
|
|
150
225
|
runs: RunView[];
|
|
151
226
|
totals: {
|
|
152
227
|
all: RunTotals;
|
|
@@ -215,7 +290,18 @@ export async function workspaceRuns(query: RunsQuery): Promise<RunsDocument> {
|
|
|
215
290
|
const { tip, dir, runs, malformed } = await readRuns(top, located.rootOnDisk);
|
|
216
291
|
if (!tip) reasons.push({ code: "runs-no-ledger", message: `this checkout has no ${LEDGER_BRANCH} branch, so there are no agent runs to read; fetch it first if the remote has one` });
|
|
217
292
|
if (malformed > 0) reasons.push({ code: "runs-ledger-malformed", message: `${malformed} ${malformed === 1 ? "line" : "lines"} of ${dir} on ${LEDGER_BRANCH} ${malformed === 1 ? "is" : "are"} not a run event and ${malformed === 1 ? "was" : "were"} left out` });
|
|
218
|
-
|
|
293
|
+
const byTrailer = commitsByRunTrailer(top);
|
|
294
|
+
joinTrailerCommits(runs, byTrailer, top);
|
|
295
|
+
// A commit that lost its trailer joins by content (#3036).
|
|
296
|
+
joinPatchIdCommits(runs, byTrailer, top);
|
|
297
|
+
// A squash merge joins the runs its pull request's commits join, when asked (#3035).
|
|
298
|
+
if (query.followSquash) {
|
|
299
|
+
for (const f of joinSquashCommits(runs, top)) {
|
|
300
|
+
if (f.problem) reasons.push({ code: "squash-unfollowed", message: `${f.sha.slice(0, 8)} squashes pull request #${f.pullRequest}, and ${f.problem}, so the runs of its original commits are not joined to it` });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const { policy, trust } = runsTrust(top, query.base);
|
|
304
|
+
attestRuns(runs.values(), policy);
|
|
219
305
|
const all = [...runs.values()];
|
|
220
306
|
await resolveRunDecisions(all, located.rootOnDisk, declaredRecordKinds(declaration).map((d) => declaredKindFile(d, located.rootOnDisk)), reasons);
|
|
221
307
|
|
|
@@ -241,7 +327,8 @@ export async function workspaceRuns(query: RunsQuery): Promise<RunsDocument> {
|
|
|
241
327
|
...head,
|
|
242
328
|
workspace: { name: declaration.name, root: located.root },
|
|
243
329
|
ledger: { branch: LEDGER_BRANCH, dir, commit: tip },
|
|
244
|
-
filter: { unit: query.unit ?? null, decision: query.decision ?? null, by: query.by ?? null, since: query.since ?? null },
|
|
330
|
+
filter: { unit: query.unit ?? null, decision: query.decision ?? null, by: query.by ?? null, since: query.since ?? null, followSquash: query.followSquash === true },
|
|
331
|
+
trust,
|
|
245
332
|
runs: selected,
|
|
246
333
|
totals: { all: totalRuns(selected), byUnit, byDecision, byPrincipal },
|
|
247
334
|
malformed,
|
|
@@ -255,7 +342,7 @@ export async function workspaceRuns(query: RunsQuery): Promise<RunsDocument> {
|
|
|
255
342
|
|
|
256
343
|
// ── The writes ───────────────────────────────────────────────────────────────
|
|
257
344
|
|
|
258
|
-
export type RunsVerb = "start" | "end" | "record";
|
|
345
|
+
export type RunsVerb = "start" | "end" | "record" | "sign";
|
|
259
346
|
|
|
260
347
|
/** What `runs start|end|record` print. */
|
|
261
348
|
export type RunsWriteDocument =
|
|
@@ -268,6 +355,8 @@ export type RunsWriteDocument =
|
|
|
268
355
|
/** The line a commit the run makes carries, `Chant-Run: <id>` (#3149). */
|
|
269
356
|
trailer: string;
|
|
270
357
|
ledger: RunLedgerWrite;
|
|
358
|
+
/** For `sign`: who signed the statement it stored, and the envelope (#3192). */
|
|
359
|
+
statement?: { signer: string; class: "runner" | "service"; keyid: string; envelope: DsseEnvelope };
|
|
271
360
|
}
|
|
272
361
|
| { $schema: string; contract: number; verb: RunsVerb | null; error: { code: RunsWriteErrorCode; message: string } };
|
|
273
362
|
|
|
@@ -311,6 +400,194 @@ export async function runsWrite(req: RunsWriteRequest): Promise<RunsWriteDocumen
|
|
|
311
400
|
}
|
|
312
401
|
}
|
|
313
402
|
|
|
403
|
+
// ── Run statements (#3192) ──────────────────────────────────────────────────
|
|
404
|
+
|
|
405
|
+
/** A run as the ledger holds it now, with the commits its trailer joins. */
|
|
406
|
+
async function currentRun(top: string, rootOnDisk: string, id: string): Promise<RunView | undefined> {
|
|
407
|
+
const { runs } = await readRuns(top, rootOnDisk);
|
|
408
|
+
joinTrailerCommits(runs, commitsByRunTrailer(top), top);
|
|
409
|
+
return runs.get(id);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export interface RunsSignRequest {
|
|
413
|
+
id: string;
|
|
414
|
+
cwd: string;
|
|
415
|
+
/** A runner key in PEM: chant builds the statement and signs it. */
|
|
416
|
+
keyPem?: string;
|
|
417
|
+
/** An envelope signed elsewhere, as text: chant checks it and stores it. */
|
|
418
|
+
envelope?: string;
|
|
419
|
+
base?: string;
|
|
420
|
+
now?: () => Date;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* `runs sign <id>`: store a statement over an ended run, signed here with
|
|
425
|
+
* `keyPem` or signed elsewhere and given as `envelope`. Either way it must
|
|
426
|
+
* verify against a runner key at base and agree with the run's record before
|
|
427
|
+
* anything is written.
|
|
428
|
+
*/
|
|
429
|
+
export async function runsSign(req: RunsSignRequest): Promise<RunsWriteDocument> {
|
|
430
|
+
const head = { $schema: RUNS_WRITE_SCHEMA_ID, contract: RUNS_CONTRACT_VERSION };
|
|
431
|
+
const fail = (code: RunsWriteErrorCode, message: string): RunsWriteDocument => ({ ...head, verb: "sign", error: { code, message } });
|
|
432
|
+
try {
|
|
433
|
+
const located = locateWorkspace(req.cwd);
|
|
434
|
+
const declaration = readDeclaration(located.tree, "", { rootChant: true });
|
|
435
|
+
const top = located.top;
|
|
436
|
+
if (!top) return fail("not-a-git-repository", "agent runs are kept on the chant/lifecycle branch, and this directory is not in a git repository");
|
|
437
|
+
const run = await currentRun(top, located.rootOnDisk, req.id);
|
|
438
|
+
if (!run) return fail("run-unknown", `the ledger has no agent run ${req.id}`);
|
|
439
|
+
if (!run.record) return fail("run-not-ended", `agent run ${req.id} has not ended; a statement is signed over a run's whole record, so record its end first`);
|
|
440
|
+
const { policy } = runsTrust(top, req.base);
|
|
441
|
+
if (policy.problems.length > 0) return fail("trust-policy-unreadable", `the policy at base cannot be read: ${policy.problems.join("; ")}`);
|
|
442
|
+
|
|
443
|
+
let envelope: DsseEnvelope;
|
|
444
|
+
if (req.keyPem !== undefined) {
|
|
445
|
+
let runner;
|
|
446
|
+
try {
|
|
447
|
+
runner = loadRunnerKey(req.keyPem);
|
|
448
|
+
} catch (err) {
|
|
449
|
+
return fail("runner-key-invalid", `the key is not an Ed25519 private key in PEM: ${err instanceof Error ? err.message : String(err)}`);
|
|
450
|
+
}
|
|
451
|
+
if (policy.signers.some((s) => s.key === runner.publicKey)) {
|
|
452
|
+
return fail("runner-key-is-signer", `this is a signer's key in ${policy.signersPath}. A run statement is signed by a runner or steward identity, never by a person's key`);
|
|
453
|
+
}
|
|
454
|
+
const listed = policy.runners.find((r) => r.key === runner.publicKey);
|
|
455
|
+
if (!listed) return fail("runner-key-unlisted", `the policy at base lists no runner with this key. Add it to .chant/trust.json "runners" first:\n ${runner.publicKey}`);
|
|
456
|
+
envelope = signRunStatement(buildRunStatement(run, listed.principal), runner.key, runner.publicKey);
|
|
457
|
+
} else {
|
|
458
|
+
try {
|
|
459
|
+
envelope = JSON.parse(req.envelope ?? "") as DsseEnvelope;
|
|
460
|
+
} catch (err) {
|
|
461
|
+
return fail("envelope-unreadable", `--envelope could not be read as JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
const verdict = verifyRunStatement(envelope, policy.runners, run);
|
|
465
|
+
if (verdict.status !== "verified") return fail(verdict.code, verdict.reason);
|
|
466
|
+
|
|
467
|
+
const ctx: RunWriteContext = { top, rootOnDisk: located.rootOnDisk, cwd: req.cwd, now: req.now };
|
|
468
|
+
const result = await appendRunStatement(run.id, envelope, run.record.sha256, ctx);
|
|
469
|
+
const view = foldRun(result.id, result.ledger.path, result.lines)!;
|
|
470
|
+
const joined = new Map([[view.id, view]]);
|
|
471
|
+
joinTrailerCommits(joined, commitsByRunTrailer(top), top);
|
|
472
|
+
await resolveRunDecisions([view], located.rootOnDisk, declaredRecordKinds(declaration).map((d) => declaredKindFile(d, located.rootOnDisk)), []);
|
|
473
|
+
attestRuns([view], policy);
|
|
474
|
+
return {
|
|
475
|
+
...head,
|
|
476
|
+
verb: "sign",
|
|
477
|
+
run: view,
|
|
478
|
+
trailer: `${RUN_TRAILER}: ${view.id}`,
|
|
479
|
+
ledger: result.ledger,
|
|
480
|
+
statement: { signer: verdict.signer, class: verdict.class, keyid: verdict.keyid, envelope },
|
|
481
|
+
};
|
|
482
|
+
} catch (err) {
|
|
483
|
+
if (err instanceof RunWriteError) return fail(err.code, err.message);
|
|
484
|
+
if (err instanceof WorkspaceReadError) return fail(err.code as RunsWriteErrorCode, err.describe());
|
|
485
|
+
throw err;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/** What `runs statement` and `runs verify` print. */
|
|
490
|
+
export type RunStatementDocument =
|
|
491
|
+
| {
|
|
492
|
+
$schema: string;
|
|
493
|
+
contract: number;
|
|
494
|
+
chant: string;
|
|
495
|
+
verb: "statement";
|
|
496
|
+
run: string;
|
|
497
|
+
/** The DSSE payload type to sign under. */
|
|
498
|
+
payloadType: string;
|
|
499
|
+
/** Base64 of the payload bytes to sign: the statement's canonical JSON. */
|
|
500
|
+
payload: string;
|
|
501
|
+
statement: RunStatement;
|
|
502
|
+
}
|
|
503
|
+
| {
|
|
504
|
+
$schema: string;
|
|
505
|
+
contract: number;
|
|
506
|
+
chant: string;
|
|
507
|
+
verb: "verify";
|
|
508
|
+
trust: RunsTrust;
|
|
509
|
+
require: "signed" | null;
|
|
510
|
+
runs: { id: string; state: "running" | "ended"; record: { sha256: string } | null; attestation: RunAttestation }[];
|
|
511
|
+
summary: { runs: number; signed: number; unsigned: number; failed: number; running: number };
|
|
512
|
+
/** Why `--require signed` fails. Empty without it: the report alone never fails (studio-035 d). */
|
|
513
|
+
failures: string[];
|
|
514
|
+
ok: boolean;
|
|
515
|
+
}
|
|
516
|
+
| { $schema: string; contract: number; chant: string; verb: "statement" | "verify" | null; error: { code: RunStatementErrorCode; message: string } };
|
|
517
|
+
|
|
518
|
+
function statementHead() {
|
|
519
|
+
return { $schema: RUN_STATEMENT_SCHEMA_ID, contract: RUNS_CONTRACT_VERSION, chant: readerVersion() };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/** `runs statement <id> --signer <principal>`: the statement a signer holding its key elsewhere signs. */
|
|
523
|
+
export async function runStatement(id: string, signer: string, cwd: string): Promise<RunStatementDocument> {
|
|
524
|
+
const fail = (code: RunStatementErrorCode, message: string): RunStatementDocument => ({ ...statementHead(), verb: "statement", error: { code, message } });
|
|
525
|
+
try {
|
|
526
|
+
const located = locateWorkspace(cwd);
|
|
527
|
+
readDeclaration(located.tree, "", { rootChant: true });
|
|
528
|
+
if (!located.top) return fail("not-a-git-repository", "agent runs are kept on the chant/lifecycle branch, and this directory is not in a git repository");
|
|
529
|
+
const run = await currentRun(located.top, located.rootOnDisk, id);
|
|
530
|
+
if (!run) return fail("run-unknown", `the ledger has no agent run ${id}`);
|
|
531
|
+
if (!run.record) return fail("run-not-ended", `agent run ${id} has not ended; a statement is signed over a run's whole record`);
|
|
532
|
+
const statement = buildRunStatement(run, signer);
|
|
533
|
+
return { ...statementHead(), verb: "statement", run: id, payloadType: IN_TOTO_PAYLOAD_TYPE, payload: runStatementPayload(statement).toString("base64"), statement };
|
|
534
|
+
} catch (err) {
|
|
535
|
+
if (err instanceof WorkspaceReadError) return fail(err.code as RunStatementErrorCode, err.describe());
|
|
536
|
+
throw err;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* `runs verify [<id>] [--require signed]`: judge each run's statements
|
|
542
|
+
* against the runner keys at base. Report only unless `--require signed`
|
|
543
|
+
* asks every ended run, or the one named, to be signed.
|
|
544
|
+
*/
|
|
545
|
+
export async function runsVerify(query: { cwd: string; id?: string; require?: "signed"; base?: string }): Promise<RunStatementDocument> {
|
|
546
|
+
const fail = (code: RunStatementErrorCode, message: string): RunStatementDocument => ({ ...statementHead(), verb: "verify", error: { code, message } });
|
|
547
|
+
try {
|
|
548
|
+
const located = locateWorkspace(query.cwd);
|
|
549
|
+
readDeclaration(located.tree, "", { rootChant: true });
|
|
550
|
+
const top = located.top;
|
|
551
|
+
if (!top) return fail("not-a-git-repository", "agent runs are kept on the chant/lifecycle branch, and this directory is not in a git repository");
|
|
552
|
+
const { runs } = await readRuns(top, located.rootOnDisk);
|
|
553
|
+
joinTrailerCommits(runs, commitsByRunTrailer(top), top);
|
|
554
|
+
if (query.id !== undefined && !runs.has(query.id)) return fail("run-unknown", `the ledger has no agent run ${query.id}`);
|
|
555
|
+
const { policy, trust } = runsTrust(top, query.base);
|
|
556
|
+
const selected = [...runs.values()]
|
|
557
|
+
.filter((r) => query.id === undefined || r.id === query.id)
|
|
558
|
+
.sort((a, b) => (a.startedAt < b.startedAt ? 1 : a.startedAt > b.startedAt ? -1 : a.id < b.id ? 1 : -1));
|
|
559
|
+
attestRuns(selected, policy);
|
|
560
|
+
const out = selected.map((r) => ({ id: r.id, state: r.state, record: r.record, attestation: r.attestation! }));
|
|
561
|
+
const failures: string[] = [];
|
|
562
|
+
if (query.require === "signed") {
|
|
563
|
+
for (const r of out) {
|
|
564
|
+
if (r.state === "running" || r.attestation.status === "signed") continue;
|
|
565
|
+
failures.push(`${r.id} is ${r.attestation.status}, and --require signed was given: ${r.attestation.reason}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
const count = (f: (r: (typeof out)[number]) => boolean) => out.filter(f).length;
|
|
569
|
+
return {
|
|
570
|
+
...statementHead(),
|
|
571
|
+
verb: "verify",
|
|
572
|
+
trust,
|
|
573
|
+
require: query.require ?? null,
|
|
574
|
+
runs: out,
|
|
575
|
+
summary: {
|
|
576
|
+
runs: out.length,
|
|
577
|
+
signed: count((r) => r.attestation.status === "signed"),
|
|
578
|
+
unsigned: count((r) => r.attestation.status === "unsigned"),
|
|
579
|
+
failed: count((r) => r.attestation.status !== "signed" && r.attestation.status !== "unsigned"),
|
|
580
|
+
running: count((r) => r.state === "running"),
|
|
581
|
+
},
|
|
582
|
+
failures,
|
|
583
|
+
ok: failures.length === 0,
|
|
584
|
+
};
|
|
585
|
+
} catch (err) {
|
|
586
|
+
if (err instanceof WorkspaceReadError) return fail(err.code as RunStatementErrorCode, err.describe());
|
|
587
|
+
throw err;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
314
591
|
// ── Text ─────────────────────────────────────────────────────────────────────
|
|
315
592
|
|
|
316
593
|
function money(t: RunTotals): string {
|
|
@@ -325,7 +602,8 @@ export function formatRuns(doc: Extract<RunsDocument, { runs: unknown }>): strin
|
|
|
325
602
|
const model = [r.harness.name, r.model].filter(Boolean).join("/");
|
|
326
603
|
const tokens = r.usage ? `${r.usage.inputTokens ?? "?"} in, ${r.usage.outputTokens ?? "?"} out` : "no usage";
|
|
327
604
|
const cost = r.cost ? `${r.cost.amount} ${r.cost.currency} (${r.cost.source})` : "unpriced";
|
|
328
|
-
|
|
605
|
+
const signed = r.attestation ? (r.attestation.status === "signed" ? ` signed by ${r.attestation.signer}` : r.attestation.status === "unsigned" ? "" : ` statement ${r.attestation.status}`) : "";
|
|
606
|
+
out.push(`${r.id} ${r.state}${r.outcome ? ` ${r.outcome}` : ""} ${r.startedAt} ${model}${r.by ? ` for ${r.by}` : ""}${r.unit ? ` on ${r.unit.id}` : ""} ${tokens} ${cost} ${r.commits.length} commit${r.commits.length === 1 ? "" : "s"}${signed}`);
|
|
329
607
|
}
|
|
330
608
|
if (doc.runs.length === 0) out.push("no agent runs");
|
|
331
609
|
const t = doc.totals.all;
|
|
@@ -345,13 +623,14 @@ export async function runWorkspaceRuns(ctx: CommandContext): Promise<number> {
|
|
|
345
623
|
const { args } = ctx;
|
|
346
624
|
const cwd = process.cwd();
|
|
347
625
|
const verb = args.extraPositional;
|
|
626
|
+
if (verb === "sign" || verb === "statement" || verb === "verify") return runStatementVerb(ctx, verb, cwd);
|
|
348
627
|
if (verb === "start" || verb === "end" || verb === "record") {
|
|
349
628
|
const print = (doc: RunsWriteDocument): number => {
|
|
350
629
|
console.log(JSON.stringify(doc, null, 2));
|
|
351
630
|
return "error" in doc ? 1 : 0;
|
|
352
631
|
};
|
|
353
632
|
const usage = (message: string) => print({ $schema: RUNS_WRITE_SCHEMA_ID, contract: RUNS_CONTRACT_VERSION, verb, error: { code: "write-usage-invalid", message } });
|
|
354
|
-
for (const [flag, v] of [["--unit", args.unit], ["--decision", args.decision], ["--by", args.by], ["--since", args.since]] as const) {
|
|
633
|
+
for (const [flag, v] of [["--unit", args.unit], ["--decision", args.decision], ["--by", args.by], ["--since", args.since], ["--follow-squash", args.followSquash]] as const) {
|
|
355
634
|
if (v !== undefined) return usage(`runs ${verb} takes its fields with --from, not ${flag}`);
|
|
356
635
|
}
|
|
357
636
|
if (verb === "end" && !args.extraPositional2) return usage("runs end needs the run id: runs end <run id>");
|
|
@@ -366,16 +645,66 @@ export async function runWorkspaceRuns(ctx: CommandContext): Promise<number> {
|
|
|
366
645
|
return print(await runsWrite({ verb, id: args.extraPositional2, fields, cwd, agent: process.env[AGENT_ENV] || undefined }));
|
|
367
646
|
}
|
|
368
647
|
if (verb !== undefined) {
|
|
369
|
-
console.error(formatError({ message: `chant workspace runs takes start, end or
|
|
648
|
+
console.error(formatError({ message: `chant workspace runs takes start, end, record, sign, statement or verify, or no verb to read, not ${verb}`, hint: USAGE }));
|
|
370
649
|
return 1;
|
|
371
650
|
}
|
|
372
651
|
if (args.migrateFrom !== undefined) {
|
|
373
652
|
console.error(formatError({ message: "runs is a read and takes no --from; write with runs start, end or record", hint: USAGE }));
|
|
374
653
|
return 1;
|
|
375
654
|
}
|
|
376
|
-
const doc = await workspaceRuns({ cwd, unit: args.unit, decision: args.decision, by: args.by, since: args.since });
|
|
655
|
+
const doc = await workspaceRuns({ cwd, unit: args.unit, decision: args.decision, by: args.by, since: args.since, base: args.base, followSquash: args.followSquash });
|
|
377
656
|
if (args.json) console.log(JSON.stringify(doc, null, 2));
|
|
378
657
|
else if ("error" in doc) console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
|
|
379
658
|
else console.log(formatRuns(doc));
|
|
380
659
|
return "error" in doc ? 1 : 0;
|
|
381
660
|
}
|
|
661
|
+
|
|
662
|
+
async function runStatementVerb(ctx: CommandContext, verb: "sign" | "statement" | "verify", cwd: string): Promise<number> {
|
|
663
|
+
const { args } = ctx;
|
|
664
|
+
const id = args.extraPositional2;
|
|
665
|
+
if (verb === "sign") {
|
|
666
|
+
const print = (doc: RunsWriteDocument): number => {
|
|
667
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
668
|
+
return "error" in doc ? 1 : 0;
|
|
669
|
+
};
|
|
670
|
+
const usage = (message: string) => print({ $schema: RUNS_WRITE_SCHEMA_ID, contract: RUNS_CONTRACT_VERSION, verb, error: { code: "write-usage-invalid", message } });
|
|
671
|
+
if (!id) return usage("runs sign needs the run id: runs sign <run id> --key <runner key.pem>");
|
|
672
|
+
if ((args.key === undefined) === (args.envelope === undefined)) return usage("runs sign takes --key <runner key.pem> to sign here, or --envelope <file|-> for a statement signed elsewhere, not both");
|
|
673
|
+
let keyPem: string | undefined;
|
|
674
|
+
let envelope: string | undefined;
|
|
675
|
+
try {
|
|
676
|
+
if (args.key !== undefined) keyPem = readFileSync(resolve(cwd, args.key), "utf-8");
|
|
677
|
+
else envelope = readFields(args.envelope, cwd);
|
|
678
|
+
} catch (err) {
|
|
679
|
+
const which = args.key !== undefined ? "runner-key-invalid" : "envelope-unreadable";
|
|
680
|
+
return print({ $schema: RUNS_WRITE_SCHEMA_ID, contract: RUNS_CONTRACT_VERSION, verb, error: { code: which, message: `${args.key ?? args.envelope} could not be read: ${err instanceof Error ? err.message : String(err)}` } });
|
|
681
|
+
}
|
|
682
|
+
return print(await runsSign({ id, cwd, keyPem, envelope, base: args.base }));
|
|
683
|
+
}
|
|
684
|
+
const print = (doc: RunStatementDocument): number => {
|
|
685
|
+
if (args.json || verb === "statement" || "error" in doc) console.log(JSON.stringify(doc, null, 2));
|
|
686
|
+
else console.log(formatRunsVerify(doc as Extract<RunStatementDocument, { verb: "verify"; runs: unknown }>));
|
|
687
|
+
return "error" in doc || ("ok" in doc && !doc.ok) ? 1 : 0;
|
|
688
|
+
};
|
|
689
|
+
const usage = (message: string) => print({ ...statementHead(), verb, error: { code: "write-usage-invalid", message } });
|
|
690
|
+
if (verb === "statement") {
|
|
691
|
+
if (!id) return usage("runs statement needs the run id: runs statement <run id> --signer <principal>");
|
|
692
|
+
if (!args.signer) return usage("runs statement needs --signer <principal>: the runner principal whose key will sign it, as .chant/trust.json lists it");
|
|
693
|
+
return print(await runStatement(id, args.signer, cwd));
|
|
694
|
+
}
|
|
695
|
+
if (args.require !== undefined && args.require !== "signed") return usage(`runs verify --require takes one level, signed, not ${JSON.stringify(args.require)}`);
|
|
696
|
+
return print(await runsVerify({ cwd, id, require: args.require === "signed" ? "signed" : undefined, base: args.base }));
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** `runs verify` as lines for a person. */
|
|
700
|
+
export function formatRunsVerify(doc: Extract<RunStatementDocument, { verb: "verify"; runs: unknown }>): string {
|
|
701
|
+
const out: string[] = [];
|
|
702
|
+
const t = doc.trust;
|
|
703
|
+
out.push(`trust at base ${t.base?.slice(0, 8) ?? "none"}: ${t.runners.length} runner key${t.runners.length === 1 ? "" : "s"}${t.runners.length ? ` (${t.runners.map((r) => `${r.principal} ${r.class}`).join(", ")})` : ""}`);
|
|
704
|
+
for (const p of t.problems) out.push(` ${p}`);
|
|
705
|
+
for (const r of doc.runs) out.push(` ${r.id} ${r.state === "running" ? "running" : r.attestation.status}${r.attestation.signer ? ` by ${r.attestation.signer}` : ""} ${r.attestation.reason}`);
|
|
706
|
+
const s = doc.summary;
|
|
707
|
+
out.push(`${s.runs} run${s.runs === 1 ? "" : "s"}: ${s.signed} signed, ${s.unsigned} unsigned, ${s.failed} with a statement that does not verify, ${s.running} running`);
|
|
708
|
+
for (const f of doc.failures) out.push(`failure ${f}`);
|
|
709
|
+
return out.join("\n");
|
|
710
|
+
}
|