@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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The factory's rules (#3406, ws-087): readiness, understand, retry, done
|
|
3
|
+
* and the implements proposals, as pure functions. The Op that runs them is
|
|
4
|
+
* tested in ../op/factory.e2e.test.ts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, expect, test } from "vitest";
|
|
8
|
+
import { isAsk, pickable, retryState, doneVerdict, proposeImplements, understandOutcome, type FactoryContext, type FactoryItem } from "./factory-rules";
|
|
9
|
+
|
|
10
|
+
describe("the factory's rules", () => {
|
|
11
|
+
const base: FactoryItem = { id: "W-1", state: "open", openState: "open", proposedState: "proposed", ready: true, data: {}, tier: null, contract: null, warnings: [], answers: [], leased: false };
|
|
12
|
+
const ctx: FactoryContext = { intentDecided: null, claims: [], attempts: { exhausted: false }, branch: null, questions: { tier: null, understand: null } };
|
|
13
|
+
const hold = (i: Partial<FactoryItem>, c: Partial<FactoryContext> = {}) => {
|
|
14
|
+
const v = pickable({ ...base, ...i }, { ...ctx, ...c });
|
|
15
|
+
return v.ok ? "ok" : v.hold;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
test("readiness", () => {
|
|
19
|
+
expect(hold({})).toBe("ok");
|
|
20
|
+
expect(hold({}, { intentDecided: false })).toBe("intent-undecided");
|
|
21
|
+
expect(hold({ ready: false })).toBe("not-ready");
|
|
22
|
+
expect(hold({ ready: false, state: "proposed", data: { source: { ask: { said: "x", by: "a" } } } })).toBe("ok");
|
|
23
|
+
expect(hold({ contract: { id: "C-1", state: "draft" }, warnings: ["work-contract-undecided"] })).toBe("contract-not-approved");
|
|
24
|
+
expect(hold({}, { branch: { state: "done", applied: false } })).toBe("built-not-applied");
|
|
25
|
+
expect(hold({}, { branch: { state: "done", applied: true } })).toBe("ok");
|
|
26
|
+
expect(hold({}, { branch: { state: "dropped", applied: false } })).toBe("dropped-on-branch");
|
|
27
|
+
expect(hold({}, { attempts: { exhausted: true } })).toBe("attempts-exhausted");
|
|
28
|
+
const failed = { claims: [{ token: "t1", ended: "released", outcome: "not_done", attempt: true }] };
|
|
29
|
+
expect(hold({}, failed)).toBe("awaiting-retry");
|
|
30
|
+
expect(hold({ data: { retry: { after: "t1", by: "alice" } } }, failed)).toBe("ok");
|
|
31
|
+
expect(hold({ data: { retry: { after: "t0", by: "alice" } } }, failed)).toBe("awaiting-retry");
|
|
32
|
+
expect(hold({}, { questions: { tier: { state: "escalated", answer: null }, understand: null } })).toBe("question-open");
|
|
33
|
+
expect(hold({ tier: "large" }, { questions: { tier: { state: "escalated", answer: null }, understand: null } })).toBe("ok");
|
|
34
|
+
const ask = { data: { source: { ask: { said: "x", by: "a" } } } };
|
|
35
|
+
expect(hold(ask, { questions: { tier: null, understand: { state: "answered", answer: "redraft" } } })).toBe("redraft-or-ask");
|
|
36
|
+
expect(hold({ leased: true })).toBe("leased");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("understand, retry, done and implements", () => {
|
|
40
|
+
expect(isAsk({ source: { intent: { answer: "x" } } })).toBe(true);
|
|
41
|
+
expect([understandOutcome("proceed"), understandOutcome("refuse"), understandOutcome("redraft"), understandOutcome("ask")]).toEqual([null, "dropped", "redraft", "ask"]);
|
|
42
|
+
const claims = [{ token: "t1", ended: "released", outcome: "not_done", attempt: true }];
|
|
43
|
+
expect(retryState({}, claims, { exhausted: false }, false)).toEqual({ possible: true, after: "t1", asked: false });
|
|
44
|
+
expect(retryState({ retry: { after: "t1", by: "a" } }, claims, { exhausted: false }, false)).toMatchObject({ asked: true });
|
|
45
|
+
expect(retryState({}, claims, { exhausted: true }, false)).toMatchObject({ possible: false, reason: "attempts-exhausted" });
|
|
46
|
+
expect(retryState({}, [], { exhausted: false }, false)).toMatchObject({ possible: false, reason: "no-failed-build" });
|
|
47
|
+
const unit = { id: "AC-1", verification: "unit", met: true };
|
|
48
|
+
expect(doneVerdict({ finished: true, reverted: [] }, { ran: true, ok: true }, [unit])).toEqual({ done: true });
|
|
49
|
+
expect(doneVerdict({ finished: true, reverted: [] }, { ran: true, ok: true }, [unit, { id: "AC-2", verification: "manual", met: false }])).toMatchObject({ done: false });
|
|
50
|
+
expect(doneVerdict({ finished: true, reverted: ["x"] }, { ran: true, ok: true }, [unit])).toMatchObject({ done: false });
|
|
51
|
+
expect(doneVerdict({ finished: true, reverted: [] }, { ran: true, ok: false }, [unit])).toMatchObject({ done: false });
|
|
52
|
+
expect(
|
|
53
|
+
proposeImplements(
|
|
54
|
+
[
|
|
55
|
+
{ path: "a.ts", decisions: [{ id: "d-1", state: "decided", granularity: "path" }, { id: "d-2", state: "decided", granularity: "member" }, { id: "d-3", state: "proposed", granularity: "path" }] },
|
|
56
|
+
{ path: "b.ts", decisions: [{ id: "d-1", state: "decided", granularity: "path" }, { id: "d-4", state: "ratified", granularity: "path" }] },
|
|
57
|
+
],
|
|
58
|
+
["d-4"],
|
|
59
|
+
),
|
|
60
|
+
).toEqual([{ decision: "d-1", paths: ["a.ts", "b.ts"] }]);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The factory's rules (#3406, ws-087): what every orchestrator of a factory
|
|
3
|
+
* must agree on, as pure functions over what the read contract prints. The
|
|
4
|
+
* reference Op (`../op/factory.ts`) runs them; an orchestrator supplies only
|
|
5
|
+
* execution (the builder, the context, the check).
|
|
6
|
+
*
|
|
7
|
+
* - Readiness ({@link pickable}): on top of the work kind's `ready`, the box's
|
|
8
|
+
* intent is decided, a contract the item builds is approved and in force,
|
|
9
|
+
* the item is under its attempt limit, it is not done and waiting to be
|
|
10
|
+
* applied on its branch, its slice-tier and understand questions are not
|
|
11
|
+
* open, an understand answer of redraft or ask holds it, and a failed last
|
|
12
|
+
* build holds it until a person asks for a retry ({@link retryState}).
|
|
13
|
+
* - The understand point's answers ({@link understandOutcome}): proceed
|
|
14
|
+
* builds; refuse drops the item on its branch; redraft and ask build
|
|
15
|
+
* nothing and leave it for its author. Each maps to a lease outcome, and
|
|
16
|
+
* only a build's outcome counts as an attempt (#3147).
|
|
17
|
+
* - Done ({@link doneVerdict}): the builder finished, the check ran and
|
|
18
|
+
* passed, and every criterion the factory may tick has passing evidence. A
|
|
19
|
+
* criterion verified `manual` or `runtime` is not the factory's to tick, so
|
|
20
|
+
* an item with one is not done by the factory.
|
|
21
|
+
* - Retry ({@link retryState}): a failed build is never tried again on its
|
|
22
|
+
* own. A person's request, the item's `retry: { after, by, at }`, names the
|
|
23
|
+
* lease token of the failed build it follows, so one request starts one run.
|
|
24
|
+
* - Implements ({@link proposeImplements}): a build recorded done proposes
|
|
25
|
+
* each decision in force that constrains, by path, a path it changed.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/** The decision point that gates every build of a person's ask (#3150). */
|
|
29
|
+
export const UNDERSTAND_POINT = "understand";
|
|
30
|
+
/** The decision point that picks a builder tier (ws-057, #3150). */
|
|
31
|
+
export const TIER_POINT = "slice-tier";
|
|
32
|
+
|
|
33
|
+
/** What the understand point answers. */
|
|
34
|
+
export const UNDERSTAND_ANSWERS = ["proceed", "redraft", "ask", "refuse"] as const;
|
|
35
|
+
export type UnderstandAnswer = (typeof UNDERSTAND_ANSWERS)[number];
|
|
36
|
+
|
|
37
|
+
/** Why an item is not picked. Closed. */
|
|
38
|
+
export const FACTORY_HOLDS = [
|
|
39
|
+
"not-ready",
|
|
40
|
+
"intent-undecided",
|
|
41
|
+
"contract-not-approved",
|
|
42
|
+
"attempts-exhausted",
|
|
43
|
+
"awaiting-retry",
|
|
44
|
+
"built-not-applied",
|
|
45
|
+
"dropped-on-branch",
|
|
46
|
+
"question-open",
|
|
47
|
+
"redraft-or-ask",
|
|
48
|
+
"leased",
|
|
49
|
+
] as const;
|
|
50
|
+
export type FactoryHold = (typeof FACTORY_HOLDS)[number];
|
|
51
|
+
|
|
52
|
+
/** A work item as the factory reads it: its record from `records --json`, trimmed to what the rules use. */
|
|
53
|
+
export interface FactoryItem {
|
|
54
|
+
id: string;
|
|
55
|
+
state: string | null;
|
|
56
|
+
/** The kind's open state, such as open. */
|
|
57
|
+
openState: string;
|
|
58
|
+
/** The kind's first state for a proposal, such as proposed; an ask in it is a candidate too. */
|
|
59
|
+
proposedState: string | null;
|
|
60
|
+
ready: boolean;
|
|
61
|
+
data: Record<string, unknown> | null;
|
|
62
|
+
/** The item's own tier, or null. */
|
|
63
|
+
tier: string | null;
|
|
64
|
+
/** The contract it builds, with that record's state, or null. */
|
|
65
|
+
contract: { id: string; state: string | null } | null;
|
|
66
|
+
/** The record's warnings' codes. */
|
|
67
|
+
warnings: string[];
|
|
68
|
+
/** The answers about it, as records --json joins them. */
|
|
69
|
+
answers: { id: string; point: string | null; state: string | null; answer: string | boolean | null }[];
|
|
70
|
+
/** Someone holds its lease now. */
|
|
71
|
+
leased: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** One claim of the item's lease history, as `work history --json` prints it. */
|
|
75
|
+
export interface FactoryClaim {
|
|
76
|
+
token: string;
|
|
77
|
+
ended: string;
|
|
78
|
+
outcome: string | null;
|
|
79
|
+
attempt: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** What the factory knows besides the item. */
|
|
83
|
+
export interface FactoryContext {
|
|
84
|
+
/** null when no box names an intent; else whether that decision is decided. */
|
|
85
|
+
intentDecided: boolean | null;
|
|
86
|
+
claims: FactoryClaim[];
|
|
87
|
+
attempts: { exhausted: boolean };
|
|
88
|
+
/** The item as its work branch holds it, when the branch exists: its state there and whether the branch is in the checkout. */
|
|
89
|
+
branch: { state: string | null; applied: boolean } | null;
|
|
90
|
+
/** The current question of each point for the item as it is now (a dry run), or null when not asked yet. */
|
|
91
|
+
questions: { tier: { state: string; answer: string | boolean | null } | null; understand: { state: string; answer: string | boolean | null } | null };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Whether a work item is a person's ask: its source names what they said, or it is the box's first build from its intent. */
|
|
95
|
+
export function isAsk(data: Record<string, unknown> | null): boolean {
|
|
96
|
+
const source = data?.source as { ask?: unknown; intent?: unknown } | undefined;
|
|
97
|
+
return !!(source && typeof source === "object" && (source.ask || source.intent));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The retry request on an item: `retry: { after, by, at }`, or null. */
|
|
101
|
+
export function retryRequest(data: Record<string, unknown> | null): { after: string; by: string; at: string } | null {
|
|
102
|
+
const r = data?.retry as { after?: unknown; by?: unknown; at?: unknown } | undefined;
|
|
103
|
+
if (!r || typeof r !== "object" || typeof r.after !== "string" || typeof r.by !== "string") return null;
|
|
104
|
+
return { after: r.after, by: r.by, at: typeof r.at === "string" ? r.at : "" };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export type RetryState =
|
|
108
|
+
/** The last claim was a failed build and a person may ask for one more. */
|
|
109
|
+
| { possible: true; after: string; asked: boolean }
|
|
110
|
+
| { possible: false; reason: "no-failed-build" | "attempts-exhausted" | "held"; message: string };
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Whether a person may ask for item `data`'s failed build to run again: the
|
|
114
|
+
* last claim is a failed build, nobody holds the lease, and attempts remain.
|
|
115
|
+
* `asked` says a request naming that build is already on the item.
|
|
116
|
+
*/
|
|
117
|
+
export function retryState(data: Record<string, unknown> | null, claims: readonly FactoryClaim[], attempts: { exhausted: boolean }, leased: boolean): RetryState {
|
|
118
|
+
if (leased) return { possible: false, reason: "held", message: "someone holds the item's lease now" };
|
|
119
|
+
const last = claims.at(-1);
|
|
120
|
+
if (!last || !last.attempt) return { possible: false, reason: "no-failed-build", message: "the item's last claim is not a failed build, so there is nothing to retry" };
|
|
121
|
+
if (attempts.exhausted) return { possible: false, reason: "attempts-exhausted", message: "the item has used every attempt its limit allows, so it is left to people" };
|
|
122
|
+
const asked = retryRequest(data)?.after === last.token;
|
|
123
|
+
return { possible: true, after: last.token, asked };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The lease outcome an understand answer leads to: null for proceed, which builds. */
|
|
127
|
+
export function understandOutcome(answer: unknown): "dropped" | "redraft" | "ask" | null {
|
|
128
|
+
if (answer === "refuse") return "dropped";
|
|
129
|
+
if (answer === "redraft" || answer === "ask") return answer;
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const OPEN = new Set(["escalated", "proposed"]);
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Whether the factory picks `item` now, or why not. Asks are candidates while
|
|
137
|
+
* proposed as well as open; any other item only when the work kind reads it
|
|
138
|
+
* ready.
|
|
139
|
+
*/
|
|
140
|
+
export function pickable(item: FactoryItem, ctx: FactoryContext): { ok: true } | { ok: false; hold: FactoryHold; message: string } {
|
|
141
|
+
const no = (hold: FactoryHold, message: string) => ({ ok: false as const, hold, message });
|
|
142
|
+
if (ctx.intentDecided === false) return no("intent-undecided", "the box's intent is not decided yet, so nothing is picked");
|
|
143
|
+
if (item.leased) return no("leased", `${item.id} is leased`);
|
|
144
|
+
const ask = isAsk(item.data);
|
|
145
|
+
const candidate = item.ready || (ask && item.proposedState !== null && item.state === item.proposedState);
|
|
146
|
+
if (!candidate) return no("not-ready", `${item.id} is ${item.state ?? "in no state"} and not ready`);
|
|
147
|
+
if (item.contract && (item.warnings.includes("work-contract-undecided") || item.warnings.includes("work-contract-unknown"))) {
|
|
148
|
+
return no("contract-not-approved", `${item.id} builds ${item.contract.id}, which is not approved and in force`);
|
|
149
|
+
}
|
|
150
|
+
if (ctx.branch && !ctx.branch.applied && ctx.branch.state === "done") return no("built-not-applied", `${item.id} is built on its branch and waits to be applied`);
|
|
151
|
+
if (ctx.branch && !ctx.branch.applied && ctx.branch.state === "dropped") return no("dropped-on-branch", `${item.id} was dropped on its branch`);
|
|
152
|
+
if (ctx.attempts.exhausted) return no("attempts-exhausted", `${item.id} has used every attempt, so it is left to people`);
|
|
153
|
+
const last = ctx.claims.at(-1);
|
|
154
|
+
if (last?.attempt && retryRequest(item.data)?.after !== last.token) {
|
|
155
|
+
return no("awaiting-retry", `${item.id}'s last build failed; a person asks for another with retry.after: ${last.token}`);
|
|
156
|
+
}
|
|
157
|
+
for (const [name, q] of [[TIER_POINT, item.tier === null ? ctx.questions.tier : null], [UNDERSTAND_POINT, ask ? ctx.questions.understand : null]] as const) {
|
|
158
|
+
if (q && OPEN.has(q.state)) return no("question-open", `${item.id}'s ${name} question is ${q.state}; a person answers it first`);
|
|
159
|
+
}
|
|
160
|
+
const u = ask ? ctx.questions.understand : null;
|
|
161
|
+
if (u && u.state === "answered" && (u.answer === "redraft" || u.answer === "ask")) {
|
|
162
|
+
return no("redraft-or-ask", `${item.id}'s understand question was answered ${String(u.answer)}; its author changes the item, which asks again`);
|
|
163
|
+
}
|
|
164
|
+
return { ok: true };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** One acceptance criterion's state, as records --json lists it. */
|
|
168
|
+
export interface CriterionView {
|
|
169
|
+
id: string;
|
|
170
|
+
verification: string;
|
|
171
|
+
met: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Whether a build is done, or why not. */
|
|
175
|
+
export function doneVerdict(build: { finished: boolean; reverted: string[] }, check: { ran: boolean; ok: boolean }, criteria: readonly CriterionView[]): { done: true } | { done: false; reason: string } {
|
|
176
|
+
if (!build.finished) return { done: false, reason: "the builder did not finish" };
|
|
177
|
+
if (build.reverted.length > 0) return { done: false, reason: `the guard put back what the builder changed out of scope: ${build.reverted.join(", ")}` };
|
|
178
|
+
if (!check.ran) return { done: false, reason: "no check ran" };
|
|
179
|
+
if (!check.ok) return { done: false, reason: "the check failed" };
|
|
180
|
+
const people = criteria.filter((c) => c.verification === "manual" || c.verification === "runtime");
|
|
181
|
+
if (people.length > 0) return { done: false, reason: `criteria ${people.map((c) => c.id).join(", ")} are verified ${people.map((c) => c.verification).join("/")}, which the factory never ticks` };
|
|
182
|
+
const unmet = criteria.filter((c) => !c.met);
|
|
183
|
+
if (unmet.length > 0) return { done: false, reason: `criteria ${unmet.map((c) => c.id).join(", ")} have no passing evidence` };
|
|
184
|
+
return { done: true };
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The decisions a done build proposes it implements (studio#243): from the
|
|
189
|
+
* intent graph's `constrains` edges of granularity `path` to each changed
|
|
190
|
+
* path, the decisions in force, less those the item already implements.
|
|
191
|
+
*/
|
|
192
|
+
export function proposeImplements(
|
|
193
|
+
changed: readonly { path: string; decisions: readonly { id: string; state: string | null; granularity: string }[] }[],
|
|
194
|
+
already: readonly string[],
|
|
195
|
+
inForce: readonly string[] = ["decided", "ratified"],
|
|
196
|
+
): { decision: string; paths: string[] }[] {
|
|
197
|
+
const out = new Map<string, string[]>();
|
|
198
|
+
for (const c of changed) {
|
|
199
|
+
for (const d of c.decisions) {
|
|
200
|
+
if (d.granularity !== "path" || d.state === null || !inForce.includes(d.state) || already.includes(d.id)) continue;
|
|
201
|
+
const paths = out.get(d.id) ?? [];
|
|
202
|
+
if (!paths.includes(c.path)) paths.push(c.path);
|
|
203
|
+
out.set(d.id, paths);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return [...out].map(([decision, paths]) => ({ decision, paths }));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* The key of the state an item was read in, for a steward's ready step: a
|
|
211
|
+
* new key is work no run was started for. It names the item, its last claim
|
|
212
|
+
* and its open questions, so a person's answer to either point is a new key.
|
|
213
|
+
*/
|
|
214
|
+
export function readyKey(item: Pick<FactoryItem, "id">, ctx: Pick<FactoryContext, "claims" | "questions">, data: Record<string, unknown> | null): string {
|
|
215
|
+
const last = ctx.claims.at(-1)?.token ?? "none";
|
|
216
|
+
const q = (x: { state: string; answer: unknown } | null) => (x ? `${x.state}:${String(x.answer)}` : "-");
|
|
217
|
+
return `${item.id}@${last}#tier=${q(ctx.questions.tier)}#understand=${q(ctx.questions.understand)}#retry=${retryRequest(data)?.after ?? "-"}`;
|
|
218
|
+
}
|
|
@@ -59,7 +59,7 @@ export type GraphErrorCode = (typeof GRAPH_ERROR_CODES)[number];
|
|
|
59
59
|
const LIVE_AT_REVISION = "--live reads the account as it stands now, and --at reads the source at a revision; chant workspace graph takes one of them";
|
|
60
60
|
|
|
61
61
|
const USAGE =
|
|
62
|
-
"chant workspace graph [dir] [--at <rev>] [--member <name>] [--kind <kind file>] [-o <file>] [--env <env>] [--live [--overlay] [--traffic <level>]] [--no-cache] [--dry-run] | chant workspace graph --composites [--at <rev>] [--member <name>] [-o <file>] | chant workspace graph --intent <path[:start-end]> [--at <rev>] [--kind <kind file>...] [--json]";
|
|
62
|
+
"chant workspace graph [dir] [--at <rev>] [--member <name>] [--kind <kind file>] [-o <file>] [--env <env>] [--live [--overlay] [--traffic <level>]] [--no-cache] [--dry-run] | chant workspace graph --composites [--at <rev>] [--member <name>] [-o <file>] | chant workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]";
|
|
63
63
|
|
|
64
64
|
interface Head {
|
|
65
65
|
$schema: string;
|
|
@@ -260,7 +260,7 @@ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
|
|
|
260
260
|
// Links (#2539) resolve against the declaration that was read, the revision's for --at, and the kinds installed now.
|
|
261
261
|
const graph = composeWorkspaceGraph({ name: declaration.name, root: located.root }, inputs, { declaration, kinds });
|
|
262
262
|
// The box's factory and listing (#3146), from the declaration read, so --at gives them at that revision.
|
|
263
|
-
for (const member of graph.members) member.box = graphBox(declaration.members.find((m) => m.name === member.name)?.box ?? null, located.tree);
|
|
263
|
+
for (const member of graph.members) member.box = graphBox(declaration.members.find((m) => m.name === member.name)?.box ?? null, located.tree, declaration.members);
|
|
264
264
|
// Nested workspaces (#2551, ws-071): read-only, through their own chant workspace graph.
|
|
265
265
|
const nestedFailed = await expandNested(graph, declaration.members, query, located.rootOnDisk, located.at);
|
|
266
266
|
let recordsFailed = false;
|
|
@@ -1114,6 +1114,8 @@
|
|
|
1114
1114
|
"check",
|
|
1115
1115
|
"checks",
|
|
1116
1116
|
"builders",
|
|
1117
|
+
"tiers",
|
|
1118
|
+
"builderFor",
|
|
1117
1119
|
"publish"
|
|
1118
1120
|
],
|
|
1119
1121
|
"properties": {
|
|
@@ -1168,6 +1170,71 @@
|
|
|
1168
1170
|
],
|
|
1169
1171
|
"description": "The member that declares the builder agents, or null."
|
|
1170
1172
|
},
|
|
1173
|
+
"tiers": {
|
|
1174
|
+
"type": "array",
|
|
1175
|
+
"items": {
|
|
1176
|
+
"type": "object",
|
|
1177
|
+
"required": [
|
|
1178
|
+
"tier",
|
|
1179
|
+
"agent",
|
|
1180
|
+
"kinds",
|
|
1181
|
+
"session"
|
|
1182
|
+
],
|
|
1183
|
+
"properties": {
|
|
1184
|
+
"tier": {
|
|
1185
|
+
"type": "string"
|
|
1186
|
+
},
|
|
1187
|
+
"agent": {
|
|
1188
|
+
"type": "string"
|
|
1189
|
+
},
|
|
1190
|
+
"kinds": {
|
|
1191
|
+
"oneOf": [
|
|
1192
|
+
{
|
|
1193
|
+
"type": "null"
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
"type": "array",
|
|
1197
|
+
"items": {
|
|
1198
|
+
"type": "string"
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
]
|
|
1202
|
+
},
|
|
1203
|
+
"session": {
|
|
1204
|
+
"type": [
|
|
1205
|
+
"string",
|
|
1206
|
+
"null"
|
|
1207
|
+
]
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
},
|
|
1211
|
+
"description": "Which builder agent builds at which tier, as declared (#3152, ws-094), each { tier, agent, kinds, session } with kinds and session null when not declared; empty when none is. Added in contract 1 by chant 0.103.0."
|
|
1212
|
+
},
|
|
1213
|
+
"builderFor": {
|
|
1214
|
+
"type": "object",
|
|
1215
|
+
"additionalProperties": {
|
|
1216
|
+
"type": "object",
|
|
1217
|
+
"additionalProperties": {
|
|
1218
|
+
"type": "object",
|
|
1219
|
+
"required": [
|
|
1220
|
+
"agent",
|
|
1221
|
+
"session"
|
|
1222
|
+
],
|
|
1223
|
+
"properties": {
|
|
1224
|
+
"agent": {
|
|
1225
|
+
"type": "string"
|
|
1226
|
+
},
|
|
1227
|
+
"session": {
|
|
1228
|
+
"type": [
|
|
1229
|
+
"string",
|
|
1230
|
+
"null"
|
|
1231
|
+
]
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
},
|
|
1236
|
+
"description": "The builder for each member the factory builds, by tier (#3152): member name, then tier, then { agent, session }. For a member the entry naming its kind is used, else the tier's entry with no kinds; a tier with neither is left out. Added in contract 1 by chant 0.103.0."
|
|
1237
|
+
},
|
|
1171
1238
|
"publish": {
|
|
1172
1239
|
"description": "Where a finished build is published, or null when the factory doesn't publish past the box.",
|
|
1173
1240
|
"oneOf": [
|
|
@@ -161,6 +161,29 @@ describe.skipIf(!hasSshKeygen)("identity.gates", () => {
|
|
|
161
161
|
expect(unknown.refuses(signed(alice, approval("github:alice")))).toMatch(/no pinned package supplies/);
|
|
162
162
|
});
|
|
163
163
|
|
|
164
|
+
test("a relayed approval: the seal covers relayedBy, and status lists it (#3402)", () => {
|
|
165
|
+
const { alice } = keys();
|
|
166
|
+
const policy = policyWith([{ principal: "github:alice", key: alice.pub }]);
|
|
167
|
+
const rule = gateAdmissionFrom({ declaration: decl({ gates: { ship: {} } }), policy, classes: coreClassRegistry() }, "ship")!;
|
|
168
|
+
const relayed = signed(alice, approval("github:alice", { relayedBy: "github:hud-follower" }));
|
|
169
|
+
expect(rule.refuses(relayed)).toBeNull();
|
|
170
|
+
// The relay can't be changed, dropped or added after sealing.
|
|
171
|
+
expect(rule.refuses({ ...relayed, relayedBy: "github:mallory" })).toMatch(/does not verify/);
|
|
172
|
+
const { relayedBy: _dropped, ...unrelayed } = relayed;
|
|
173
|
+
expect(rule.refuses(unrelayed)).toMatch(/does not verify/);
|
|
174
|
+
expect(rule.refuses({ ...signed(alice, approval("github:alice")), relayedBy: "github:hud-follower" })).toMatch(/does not verify/);
|
|
175
|
+
// A seal made before #3402, with no relay, still verifies.
|
|
176
|
+
expect(rule.refuses(signed(alice, approval("github:alice")))).toBeNull();
|
|
177
|
+
|
|
178
|
+
const pending: PendingGateRecord = { version: 1, kind: "pending", op: "web", gate: "ship", environment: "prod", planDigest: "sha256:" + "a".repeat(64), timestamp: "2026-10-03T11:00:00.000Z", expiresAt: "2026-10-10T11:00:00.000Z" };
|
|
179
|
+
const ledger = [pending, relayed, approval("bob")].map((l) => JSON.stringify(l)).join("\n");
|
|
180
|
+
const gate = gatesInLedger("web", ledger, ["prod"], "2026-10-03T13:00:00.000Z").gates[0];
|
|
181
|
+
expect(gate.approvals).toEqual([
|
|
182
|
+
{ principal: "github:alice", channel: null, relayedBy: "github:hud-follower", at: "2026-10-03T12:00:00.000Z" },
|
|
183
|
+
{ principal: "bob", channel: null, relayedBy: null, at: "2026-10-03T12:00:00.000Z" },
|
|
184
|
+
]);
|
|
185
|
+
});
|
|
186
|
+
|
|
164
187
|
test("a run and workspace status leave out approvals the rule refuses", async () => {
|
|
165
188
|
const { alice } = keys();
|
|
166
189
|
const policy = policyWith([{ principal: "github:alice", key: alice.pub }]);
|
package/src/workspace/init.ts
CHANGED
|
@@ -21,10 +21,18 @@
|
|
|
21
21
|
* never written.
|
|
22
22
|
*
|
|
23
23
|
* Nothing here runs project code: it reads file names and package.json files.
|
|
24
|
+
*
|
|
25
|
+
* `--profile ideation|app|infra` starts a workspace from one of the reference
|
|
26
|
+
* workspace's profiles instead (#3174, ws-096): records only with a stub app,
|
|
27
|
+
* an app with its factory, or an estate with a factory and no app. It is
|
|
28
|
+
* `chant init --from` on `reference-workspace/profiles/<profile>` of the chant
|
|
29
|
+
* repository at this chant's release tag, or of the repository or directory
|
|
30
|
+
* `--from` names, so the new workspace gets a lineage lock and can be upgraded
|
|
31
|
+
* like any template copy.
|
|
24
32
|
*/
|
|
25
33
|
|
|
26
34
|
import { execFileSync } from "node:child_process";
|
|
27
|
-
import { existsSync, readdirSync, writeFileSync } from "node:fs";
|
|
35
|
+
import { existsSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
28
36
|
import { basename, join, resolve } from "node:path";
|
|
29
37
|
import { formatError, formatSuccess } from "../cli/format";
|
|
30
38
|
import type { CommandContext } from "../cli/registry";
|
|
@@ -36,12 +44,34 @@ import {
|
|
|
36
44
|
rootExclusions,
|
|
37
45
|
WorkspaceReadError,
|
|
38
46
|
isInside,
|
|
47
|
+
readerVersion,
|
|
39
48
|
} from "./declaration";
|
|
40
49
|
import { expandGlob } from "./glob";
|
|
50
|
+
import { setProperty } from "./json-edit";
|
|
41
51
|
import { holdsChantProject } from "./kinds";
|
|
42
52
|
import { gitTop, skippedDir, workingTree, type WorkspaceTree } from "./tree";
|
|
43
53
|
|
|
44
|
-
const USAGE = "chant workspace init [dir] [--name <name>] [--yes] [--verbose]";
|
|
54
|
+
const USAGE = "chant workspace init [dir] [--name <name>] [--yes] [--verbose]\n chant workspace init --profile ideation|app|infra [dir] [--name <name>] [--param name=<display name>] [--from <repo>@<ref>|<chant checkout>] [--yes]";
|
|
55
|
+
|
|
56
|
+
/** The profiles `--profile` takes (#3174), each a directory under {@link PROFILES_DIR} in the chant repository. */
|
|
57
|
+
export const PROFILES = ["ideation", "app", "infra"] as const;
|
|
58
|
+
export type Profile = (typeof PROFILES)[number];
|
|
59
|
+
|
|
60
|
+
/** Where the profiles live in the chant repository. */
|
|
61
|
+
export const PROFILES_DIR = "reference-workspace/profiles";
|
|
62
|
+
|
|
63
|
+
/** The repository the profiles are read from by default, at this chant's release tag. */
|
|
64
|
+
export const PROFILES_REPO = "https://github.com/INTENTIUS/chant";
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The `chant init --from` source for a profile: `from` (a `<repo>@<ref>` or
|
|
68
|
+
* a directory holding a chant checkout) when given, else the chant repository
|
|
69
|
+
* at the tag of the chant reading, `chant-v<version>`.
|
|
70
|
+
*/
|
|
71
|
+
export function profileSource(profile: Profile, from: string | undefined, version: string): string {
|
|
72
|
+
const base = from ?? `${PROFILES_REPO}@chant-v${version}`;
|
|
73
|
+
return `${base}#${PROFILES_DIR}/${profile}`;
|
|
74
|
+
}
|
|
45
75
|
|
|
46
76
|
/** Directory names whose children are examples or test fixtures, not members. */
|
|
47
77
|
const EXAMPLE_DIRS = new Map<string, "examples" | "fixtures">([
|
|
@@ -544,6 +574,8 @@ export async function runWorkspaceInit(ctx: CommandContext): Promise<number> {
|
|
|
544
574
|
return 1;
|
|
545
575
|
}
|
|
546
576
|
|
|
577
|
+
if (args.profile !== undefined) return initProfile(args, root);
|
|
578
|
+
|
|
547
579
|
let proposal: Proposal;
|
|
548
580
|
try {
|
|
549
581
|
proposal = proposeWorkspace(root, { name: args.selectName });
|
|
@@ -583,3 +615,51 @@ export async function runWorkspaceInit(ctx: CommandContext): Promise<number> {
|
|
|
583
615
|
console.log(formatSuccess(`Wrote ${target}. chant workspace ls lists what it declares.`));
|
|
584
616
|
return 0;
|
|
585
617
|
}
|
|
618
|
+
|
|
619
|
+
/** `chant workspace init --profile <profile>` (#3174): copy a profile with `chant init --from`, then name the workspace. */
|
|
620
|
+
async function initProfile(args: CommandContext["args"], root: string): Promise<number> {
|
|
621
|
+
const profile = args.profile as Profile;
|
|
622
|
+
if (!(PROFILES as readonly string[]).includes(profile)) {
|
|
623
|
+
console.error(formatError({ message: `--profile ${JSON.stringify(args.profile)} is not a profile; the profiles are ${PROFILES.join(", ")}`, hint: USAGE }));
|
|
624
|
+
return 1;
|
|
625
|
+
}
|
|
626
|
+
const { parseParamArgs } = await import("./template-manifest");
|
|
627
|
+
let params: Record<string, string>;
|
|
628
|
+
try {
|
|
629
|
+
params = parseParamArgs(args.param ?? []);
|
|
630
|
+
} catch (err) {
|
|
631
|
+
console.error(formatError({ message: (err as Error).message, hint: USAGE }));
|
|
632
|
+
return 1;
|
|
633
|
+
}
|
|
634
|
+
const from = profileSource(profile, args.migrateFrom, readerVersion());
|
|
635
|
+
console.log(`chant workspace init --profile ${profile} copies ${from} into ${root}, as chant init --from would, with a lineage lock.`);
|
|
636
|
+
if (args.selectName !== undefined) console.log(`The workspace is named ${args.selectName}.`);
|
|
637
|
+
console.log("");
|
|
638
|
+
let write = !!args.yes;
|
|
639
|
+
if (!write) {
|
|
640
|
+
if (!process.stdin.isTTY) {
|
|
641
|
+
console.log("Nothing written. Re-run with --yes to write it.");
|
|
642
|
+
return 0;
|
|
643
|
+
}
|
|
644
|
+
write = await confirm(`Make the ${profile} workspace? [y/N] `);
|
|
645
|
+
}
|
|
646
|
+
if (!write) {
|
|
647
|
+
console.log("Nothing written.");
|
|
648
|
+
return 0;
|
|
649
|
+
}
|
|
650
|
+
const { initFromCommand } = await import("./lineage-init");
|
|
651
|
+
const result = await initFromCommand({ from, path: root, params, force: args.force });
|
|
652
|
+
if (!result.success) {
|
|
653
|
+
console.error(formatError({ message: result.error ?? `the ${profile} profile could not be copied`, hint: USAGE }));
|
|
654
|
+
return 1;
|
|
655
|
+
}
|
|
656
|
+
if (args.selectName !== undefined) {
|
|
657
|
+
const file = join(root, "chant.workspace.json");
|
|
658
|
+
writeFileSync(file, setProperty(readFileSync(file, "utf-8"), false, "", "name", args.selectName));
|
|
659
|
+
}
|
|
660
|
+
console.log(formatSuccess(`Made a ${profile} workspace in ${root}:`));
|
|
661
|
+
for (const f of result.createdFiles) console.log(` ${f}`);
|
|
662
|
+
console.log("");
|
|
663
|
+
console.log("chant workspace check checks it, and chant workspace upgrade takes a later version of the profile.");
|
|
664
|
+
return 0;
|
|
665
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `chant workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--json]`
|
|
2
|
+
* `chant workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]`
|
|
3
3
|
* (#2651, #3034): the intent graph over one region (`intent.ts`), printed as JSON
|
|
4
4
|
* with `--json` or as a walk, one line per node, in the order of #2650
|
|
5
5
|
* section B: the region, its decisions, their artifacts, the commits, and the
|
|
@@ -16,8 +16,8 @@ import type { CommandContext } from "../cli/registry";
|
|
|
16
16
|
import { intentRecord, type IntentRecordDocument } from "./intent-record";
|
|
17
17
|
import { intentGraph, type ArtifactNode, type CommitNode, type DecisionNode, type IntentDocument, type IntentEdge, type IntentNode, type RunNode, type WhyAnswer, type WorkNode } from "./intent";
|
|
18
18
|
|
|
19
|
-
const USAGE = "chant workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--json]";
|
|
20
|
-
const RECORD_USAGE = "chant workspace graph --intent --record <id> [--at <rev>] [--kind <kind file>...] [--json]";
|
|
19
|
+
const USAGE = "chant workspace graph --intent <path[:start-end]|path#symbol> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]";
|
|
20
|
+
const RECORD_USAGE = "chant workspace graph --intent --record <id> [--at <rev>] [--kind <kind file>...] [--follow-squash] [--json]";
|
|
21
21
|
|
|
22
22
|
type Result = Exclude<IntentDocument, { error: unknown }>;
|
|
23
23
|
type RecordResult = Exclude<IntentRecordDocument, { error: unknown }>;
|
|
@@ -110,11 +110,27 @@ function commitLine(doc: Result, c: CommitNode): string[] {
|
|
|
110
110
|
for (const s of edgesFrom(doc, e.to, "cites-evidence")) out.push(` evidence ${s.to.slice("evidence:".length)}`);
|
|
111
111
|
}
|
|
112
112
|
out.push(...joinLines(c.joins));
|
|
113
|
+
if (c.squash) {
|
|
114
|
+
const s = c.squash;
|
|
115
|
+
out.push(` squash #${s.pullRequest}${s.followed ? `: ${s.commits.length} original ${s.commits.length === 1 ? "commit" : "commits"} from ${s.ref}${s.fetched ? ", fetched" : ""}` : ", not followed"}`);
|
|
116
|
+
for (const o of s.commits) {
|
|
117
|
+
const carried = [...o.joins.records.map((r) => `${r.kind}:${r.id}`), ...(o.joins.lease ? [`lease ${o.joins.lease.token}`] : []), ...(o.joins.run ? [`run ${o.joins.run}`] : [])];
|
|
118
|
+
out.push(` original ${short(o.sha)} ${o.subject}; ${o.signature.level}${carried.length > 0 ? `; ${carried.join(", ")}` : ""}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
113
121
|
for (const e of edgesFrom(doc, c.id, "made-by")) {
|
|
114
122
|
const r = doc.nodes.find((n): n is RunNode => n.kind === "run" && n.id === e.to);
|
|
115
123
|
if (!r) continue;
|
|
116
124
|
const what = r.recorded ? `${[r.harness?.name, r.model].filter(Boolean).join("/") || "no model recorded"}${r.by ? ` for ${r.by}` : ""}${r.cost ? `, ${r.cost.amount} ${r.cost.currency}` : ", unpriced"}` : "not in the run ledger";
|
|
117
|
-
|
|
125
|
+
const content =
|
|
126
|
+
e.kind !== "made-by"
|
|
127
|
+
? ""
|
|
128
|
+
: e.joinedBy.includes("patch-id")
|
|
129
|
+
? `; joined by content${e.recordedAs ? `, as ${short(e.recordedAs)}` : ""}`
|
|
130
|
+
: e.joinedBy.length === 1 && e.joinedBy[0] === "squash"
|
|
131
|
+
? `; through the squash, by ${(e.via ?? []).map(short).join(", ")}`
|
|
132
|
+
: "";
|
|
133
|
+
out.push(` run ${r.run}: ${what}${content}`);
|
|
118
134
|
}
|
|
119
135
|
return out;
|
|
120
136
|
}
|
|
@@ -139,7 +155,7 @@ export function whyLines(doc: Result, why: WhyAnswer): string[] {
|
|
|
139
155
|
out.push(` decision ${record(d.decision)} ${d.relevance}${d.lines > 0 ? `, ${d.lines} ${d.lines === 1 ? "line" : "lines"}` : ""}${d.current ? "" : ", superseded"}${d.closed ? ", closed" : ""}`);
|
|
140
156
|
}
|
|
141
157
|
for (const b of why.blame) {
|
|
142
|
-
const by = b.runs.length > 0 ? `; ${b.runs.map(runLabel).join(", ")}${b.narrowedBy ? " (by its hunks)" : ""}` : b.sha ? "; no run" : "";
|
|
158
|
+
const by = b.runs.length > 0 ? `; ${b.runs.map(runLabel).join(", ")}${b.narrowedBy ? " (by its hunks)" : ""}${b.joinedBy?.includes("patch-id") ? " (joined by content)" : ""}${b.via && b.via.length > 0 ? ` (squashed from ${b.via.map(short).join(", ")})` : ""}` : b.sha ? "; no run" : "";
|
|
143
159
|
out.push(` lines ${span(b)} ${b.sha ? short(b.sha) : "uncommitted"}${by}`);
|
|
144
160
|
}
|
|
145
161
|
for (const g of why.gaps) out.push(` gap ${g.code}: ${g.message}${g.lines ? ` (lines ${g.lines.map(span).join(", ")})` : ""}`);
|
|
@@ -195,8 +211,9 @@ export function formatIntentRecord(doc: RecordResult): string {
|
|
|
195
211
|
: c.bucket === "within-other"
|
|
196
212
|
? `within ${c.alsoWithin.map((w) => w.record).join(", ")}`
|
|
197
213
|
: "nothing else accounts for it";
|
|
198
|
-
const runs = (c.runs ?? []).map((r) => `run ${r.id}${r.model ? ` (${r.model})` : ""}`).join(", ");
|
|
199
|
-
|
|
214
|
+
const runs = (c.runs ?? []).map((r) => `run ${r.id}${r.model ? ` (${r.model})` : ""}${r.joinedBy?.includes("squash") && !r.joinedBy.some((j) => j !== "squash") ? " through the squash" : ""}`).join(", ");
|
|
215
|
+
const squash = c.squash ? `; squash of #${c.squash.pullRequest}${c.squash.followed ? `, ${c.squash.commits.length} original ${c.squash.commits.length === 1 ? "commit" : "commits"}` : ", not followed"}` : "";
|
|
216
|
+
out.push(`${c.bucket.padEnd(12)} ${short(c.sha)} ${c.date.slice(0, 10)} ${c.subject}; ${why}${runs ? `; ${runs}` : ""}${squash}; ${c.files.join(", ") || c.entries.join(", ")}`);
|
|
200
217
|
}
|
|
201
218
|
for (const x of doc.reasons) out.push(`reason ${x.code}: ${x.message}`);
|
|
202
219
|
const n = doc.counts;
|
|
@@ -206,7 +223,7 @@ export function formatIntentRecord(doc: RecordResult): string {
|
|
|
206
223
|
|
|
207
224
|
async function runRecord(ctx: CommandContext, cwd: string, kinds: string[] | undefined): Promise<number> {
|
|
208
225
|
const { args } = ctx;
|
|
209
|
-
const { doc, failed } = await intentRecord({ cwd, record: args.record!, at: args.at, kinds: kinds?.map((k) => resolve(k)) });
|
|
226
|
+
const { doc, failed } = await intentRecord({ cwd, record: args.record!, at: args.at, kinds: kinds?.map((k) => resolve(k)), followSquash: args.followSquash });
|
|
210
227
|
if (args.json) console.log(JSON.stringify(doc, null, 2));
|
|
211
228
|
if ("error" in doc) {
|
|
212
229
|
console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: RECORD_USAGE }));
|
|
@@ -231,7 +248,7 @@ export async function runWorkspaceIntent(ctx: CommandContext, cwd: string): Prom
|
|
|
231
248
|
}
|
|
232
249
|
// No --kind: undefined, so the walk reads the kinds the declaration names (#2680).
|
|
233
250
|
const kinds = args.kinds ?? (args.kind !== undefined ? [args.kind] : undefined);
|
|
234
|
-
const { doc, failed } = await intentGraph({ cwd, region: args.intent, at: args.at, kinds: kinds?.map((k) => resolve(k)) });
|
|
251
|
+
const { doc, failed } = await intentGraph({ cwd, region: args.intent, at: args.at, kinds: kinds?.map((k) => resolve(k)), followSquash: args.followSquash });
|
|
235
252
|
if (args.json) console.log(JSON.stringify(doc, null, 2));
|
|
236
253
|
if ("error" in doc) {
|
|
237
254
|
console.error(formatError({ message: `${doc.error.code}: ${doc.error.message}`, hint: USAGE }));
|