@intentius/chant 0.102.0 → 0.104.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/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/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/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/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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gated-wave primitives bundle small (#3049, ruling #3421).
|
|
3
|
+
*
|
|
4
|
+
* terragucci runs a wave per CI job from one bundled file with no TypeScript
|
|
5
|
+
* toolchain. It calls wave planning, the set digest and the gate names from
|
|
6
|
+
* `@intentius/chant/gated-waves`, and decides a wave's gate with
|
|
7
|
+
* `evaluateGate` from `@intentius/chant/op/gate` against the
|
|
8
|
+
* `chant/lifecycle` ledger. This bundles both the way terragucci would and
|
|
9
|
+
* reads esbuild's own list of what went in.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { build } from "esbuild";
|
|
14
|
+
import { describe, expect, test } from "vitest";
|
|
15
|
+
import { REPO } from "./workspace/__fixtures__/contract-repo";
|
|
16
|
+
|
|
17
|
+
const HEAVY = [/node_modules\/typescript\//, /node_modules\/esbuild\//, /\/src\/fold\//, /\/src\/lint\//, /\/src\/codegen\//];
|
|
18
|
+
|
|
19
|
+
async function inputsOf(entry: string): Promise<{ inputs: string[]; bytes: number }> {
|
|
20
|
+
const result = await build({
|
|
21
|
+
entryPoints: [join(REPO, "packages", "core", "src", entry)],
|
|
22
|
+
bundle: true,
|
|
23
|
+
platform: "node",
|
|
24
|
+
format: "esm",
|
|
25
|
+
write: false,
|
|
26
|
+
metafile: true,
|
|
27
|
+
outdir: "out",
|
|
28
|
+
logLevel: "silent",
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
inputs: Object.keys(result.metafile.inputs),
|
|
32
|
+
bytes: result.outputFiles.reduce((n, f) => n + f.contents.byteLength, 0),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("the gated-wave bundle", () => {
|
|
37
|
+
test("wave planning, the set digest and the records hold the change-set digest code and nothing heavier", async () => {
|
|
38
|
+
const { inputs, bytes } = await inputsOf("gated-waves.ts");
|
|
39
|
+
for (const pattern of [...HEAVY, /node_modules\/zod\//]) {
|
|
40
|
+
expect(inputs.filter((i) => pattern.test(i)), String(pattern)).toEqual([]);
|
|
41
|
+
}
|
|
42
|
+
expect(inputs.map((i) => i.replace(/^.*?packages\//, "packages/")).sort()).toEqual([
|
|
43
|
+
"packages/core/src/change-set.ts",
|
|
44
|
+
"packages/core/src/content-digest.ts",
|
|
45
|
+
"packages/core/src/declarable.ts",
|
|
46
|
+
"packages/core/src/effect-receipt.ts",
|
|
47
|
+
"packages/core/src/gated-waves.ts",
|
|
48
|
+
"packages/core/src/intrinsic.ts",
|
|
49
|
+
"packages/core/src/lifecycle/plan-digest.ts",
|
|
50
|
+
]);
|
|
51
|
+
expect(bytes).toBeLessThan(24 * 1024);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("the gate decision reaches no TypeScript, esbuild, fold, lint or codegen module", async () => {
|
|
55
|
+
// It does reach zod, through the workspace identity rules a gate reads
|
|
56
|
+
// (#3163). That is weight, not a toolchain, and #3421 forbids the toolchain.
|
|
57
|
+
const { inputs } = await inputsOf("op/gate.ts");
|
|
58
|
+
for (const pattern of HEAVY) {
|
|
59
|
+
expect(inputs.filter((i) => pattern.test(i)), String(pattern)).toEqual([]);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gated waves (#3049): the parts with no runner. Wave layering with a canary
|
|
3
|
+
* list, the set digest, gate names and the attempt record's wave entries.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from "vitest";
|
|
7
|
+
import {
|
|
8
|
+
ProvisionalWaveMemberError,
|
|
9
|
+
UnknownCanaryError,
|
|
10
|
+
WaveCycleError,
|
|
11
|
+
describeChangedWave,
|
|
12
|
+
layerWaves,
|
|
13
|
+
readWaveRecords,
|
|
14
|
+
waveGateName,
|
|
15
|
+
waveSetDigest,
|
|
16
|
+
withWaveRecord,
|
|
17
|
+
type WaveRecord,
|
|
18
|
+
} from "./gated-waves";
|
|
19
|
+
import { changeSetDigest } from "./change-set";
|
|
20
|
+
|
|
21
|
+
/** net → a, b → app-a (on a), app-b (on b). */
|
|
22
|
+
const ROOTS = [
|
|
23
|
+
{ name: "net" },
|
|
24
|
+
{ name: "a", dependsOn: ["net"] },
|
|
25
|
+
{ name: "b", dependsOn: ["net"] },
|
|
26
|
+
{ name: "app-a", dependsOn: ["a"] },
|
|
27
|
+
{ name: "app-b", dependsOn: ["b"] },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
describe("layerWaves", () => {
|
|
31
|
+
test("dependency order, each wave sorted", () => {
|
|
32
|
+
expect(layerWaves(ROOTS)).toEqual([["net"], ["a", "b"], ["app-a", "app-b"]]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("a canary list is wave 1 whatever the graph order, and the rest follow the graph", () => {
|
|
36
|
+
expect(layerWaves(ROOTS, { canary: ["app-b"] })).toEqual([["app-b"], ["net"], ["a", "b"], ["app-a"]]);
|
|
37
|
+
expect(layerWaves(ROOTS, { canary: ["b", "net"] })).toEqual([["b", "net"], ["a", "app-b"], ["app-a"]]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("a dependency outside the set does not hold a node back", () => {
|
|
41
|
+
expect(layerWaves([{ name: "a", dependsOn: ["net"] }, { name: "app-a", dependsOn: ["a"] }])).toEqual([["a"], ["app-a"]]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("a canary that is not in the set is refused, unless it is known and just not changing", () => {
|
|
45
|
+
expect(() => layerWaves(ROOTS, { canary: ["nope"] })).toThrow(UnknownCanaryError);
|
|
46
|
+
expect(layerWaves(ROOTS.slice(0, 2), { canary: ["app-b"], known: ["app-b"] })).toEqual([["net"], ["a"]]);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("a cycle is refused by name", () => {
|
|
50
|
+
expect(() => layerWaves([{ name: "x", dependsOn: ["y"] }, { name: "y", dependsOn: ["x"] }])).toThrow(WaveCycleError);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe("the set digest", () => {
|
|
55
|
+
const members = [
|
|
56
|
+
{ member: "net", planDigest: "jcs1-sha256:" + "1".repeat(64) },
|
|
57
|
+
{ member: "a", planDigest: "jcs1-sha256:" + "2".repeat(64) },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
test("is the change-set digest of the wave's members", () => {
|
|
61
|
+
expect(waveSetDigest(members)).toBe(changeSetDigest(members));
|
|
62
|
+
expect(waveSetDigest(members)).toMatch(/^jcs1-sha256:[0-9a-f]{64}$/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("does not depend on order and moves when one root's plan does", () => {
|
|
66
|
+
expect(waveSetDigest([...members].reverse())).toBe(waveSetDigest(members));
|
|
67
|
+
expect(waveSetDigest([members[0], { member: "a", planDigest: "jcs1-sha256:" + "3".repeat(64) }])).not.toBe(
|
|
68
|
+
waveSetDigest(members),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("refuses a wave that names one root twice", () => {
|
|
73
|
+
expect(() => waveSetDigest([members[0], members[0]])).toThrow(/twice/);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("refuses a wave holding a provisional plan, by name", () => {
|
|
77
|
+
expect(() => waveSetDigest([members[0], { ...members[1], provisional: true }])).toThrow(ProvisionalWaveMemberError);
|
|
78
|
+
expect(() => waveSetDigest([members[0], { ...members[1], provisional: true }])).toThrow(/provisional plan: a planned/);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("wave records", () => {
|
|
83
|
+
const record: WaveRecord = {
|
|
84
|
+
wave: 2,
|
|
85
|
+
op: "fan-out",
|
|
86
|
+
gate: waveGateName("release", 2),
|
|
87
|
+
components: ["a", "b"],
|
|
88
|
+
digest: "jcs1-sha256:" + "4".repeat(64),
|
|
89
|
+
members: [{ member: "a", planDigest: "jcs1-sha256:" + "5".repeat(64) }],
|
|
90
|
+
status: "gated",
|
|
91
|
+
approved: "jcs1-sha256:" + "6".repeat(64),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
test("each wave has its own gate", () => {
|
|
95
|
+
expect(waveGateName("release", 1)).toBe("release-wave-1");
|
|
96
|
+
expect(waveGateName("release", 1)).not.toBe(waveGateName("release", 2));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("round-trip through JSON, dropping malformed entries", () => {
|
|
100
|
+
const read = readWaveRecords(JSON.parse(JSON.stringify([record, { wave: "x" }, null])));
|
|
101
|
+
expect(read).toEqual([record]);
|
|
102
|
+
expect(readWaveRecords(undefined)).toEqual([]);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("a later record for the same wave replaces the earlier one", () => {
|
|
106
|
+
const applied: WaveRecord = { ...record, status: "applied", approvedBy: "ana" };
|
|
107
|
+
delete applied.approved;
|
|
108
|
+
expect(withWaveRecord([record], applied)).toEqual([applied]);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("the changed-set refusal names both digests and says nothing applied", () => {
|
|
112
|
+
const line = describeChangedWave(record);
|
|
113
|
+
expect(line).toContain(record.approved);
|
|
114
|
+
expect(line).toContain(record.digest);
|
|
115
|
+
expect(line).toContain("nothing in it was applied");
|
|
116
|
+
expect(line).toContain("chant approve fan-out release-wave-2 --plan");
|
|
117
|
+
});
|
|
118
|
+
});
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gated waves (#3049): one change rolled out across many roots, a wave at a
|
|
3
|
+
* time, each wave behind its own gate bound to that wave's set digest.
|
|
4
|
+
*
|
|
5
|
+
* `chant components fan-out --wave-gate` is the runner. This module holds the
|
|
6
|
+
* parts that need no runner: how a set of roots is cut into waves, what a
|
|
7
|
+
* wave's digest is, what its gate is called, and what the attempt record says
|
|
8
|
+
* about each wave. They are kept apart from the runner because terragucci
|
|
9
|
+
* calls them from its CI stages as one bundled file with no TypeScript
|
|
10
|
+
* toolchain (#3421). Everything here imports `./change-set` and nothing else,
|
|
11
|
+
* and `gated-waves-bundle.test.ts` holds that.
|
|
12
|
+
*
|
|
13
|
+
* ## The set digest
|
|
14
|
+
*
|
|
15
|
+
* A wave's digest is {@link changeSetDigest} over its members' `{ member,
|
|
16
|
+
* planDigest }` pairs, the function #3181's change-set document uses. A
|
|
17
|
+
* terraform-family member is named after its root, so this is the issue's
|
|
18
|
+
* `jcs1-sha256` over sorted `{ root, planDigest }`, and a wave's digest equals
|
|
19
|
+
* the change-set digest of the wave's members. Each member's plan digest is
|
|
20
|
+
* the one a gate on that root alone binds (`terraformPlanDigest`, #2300), so a
|
|
21
|
+
* one-root wave and a one-root `TerraformApplyOp` approve the same value.
|
|
22
|
+
*
|
|
23
|
+
* ## Why a wave is planned when it is reached
|
|
24
|
+
*
|
|
25
|
+
* A later wave planned up front reads outputs that do not exist yet. Planned
|
|
26
|
+
* after the wave before it applied, its digest describes the real upstream
|
|
27
|
+
* values, which is what an approver of that wave is shown.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
import { changeSetDigest } from "./change-set";
|
|
31
|
+
|
|
32
|
+
/** A root, or a component, as wave planning sees it: a name and what it reads. */
|
|
33
|
+
export interface WaveNode {
|
|
34
|
+
name: string;
|
|
35
|
+
/** Names this node reads from. A name outside the node set is already satisfied. */
|
|
36
|
+
dependsOn?: readonly string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Thrown when a canary names something that is not in the set being planned. */
|
|
40
|
+
export class UnknownCanaryError extends Error {
|
|
41
|
+
constructor(
|
|
42
|
+
readonly canary: string,
|
|
43
|
+
known: readonly string[],
|
|
44
|
+
) {
|
|
45
|
+
super(`canary names "${canary}", which is not in this set (known: ${known.join(", ") || "none"})`);
|
|
46
|
+
this.name = "UnknownCanaryError";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Thrown when the nodes being layered depend on each other in a cycle. */
|
|
51
|
+
export class WaveCycleError extends Error {
|
|
52
|
+
constructor(readonly members: string[]) {
|
|
53
|
+
super(`dependency cycle among: ${members.join(", ")}`);
|
|
54
|
+
this.name = "WaveCycleError";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface LayerWavesOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Nodes that form wave 1 whatever the graph says (#3049). Only those in the
|
|
61
|
+
* set are placed; one outside it has nothing to apply. The rest follow in
|
|
62
|
+
* dependency order, and a dependency on a canary counts as satisfied by
|
|
63
|
+
* wave 1.
|
|
64
|
+
*/
|
|
65
|
+
canary?: readonly string[];
|
|
66
|
+
/**
|
|
67
|
+
* Names known to exist outside `nodes`. A canary naming one of them is
|
|
68
|
+
* left out quietly, since it is real but not part of this change; any
|
|
69
|
+
* other unknown canary is refused. Defaults to `nodes` alone.
|
|
70
|
+
*/
|
|
71
|
+
known?: readonly string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Cut `nodes` into ordered waves: each node lands after every node it reads
|
|
76
|
+
* from, and each wave is sorted by name so the same graph always produces the
|
|
77
|
+
* same waves. With a canary list, those nodes are wave 1.
|
|
78
|
+
*
|
|
79
|
+
* A dependency on a name outside `nodes` does not hold a node back. That is
|
|
80
|
+
* the subset case: the dependency already applied, or is not changing.
|
|
81
|
+
*/
|
|
82
|
+
export function layerWaves(nodes: readonly WaveNode[], options: LayerWavesOptions = {}): string[][] {
|
|
83
|
+
const names = new Set(nodes.map((n) => n.name));
|
|
84
|
+
const known = new Set([...names, ...(options.known ?? [])]);
|
|
85
|
+
for (const canary of options.canary ?? []) {
|
|
86
|
+
if (!known.has(canary)) throw new UnknownCanaryError(canary, [...known].sort());
|
|
87
|
+
}
|
|
88
|
+
const canaries = [...new Set((options.canary ?? []).filter((c) => names.has(c)))].sort();
|
|
89
|
+
|
|
90
|
+
const waves: string[][] = canaries.length > 0 ? [canaries] : [];
|
|
91
|
+
const remaining = new Set([...names].filter((n) => !canaries.includes(n)));
|
|
92
|
+
const deps = new Map(nodes.map((n) => [n.name, (n.dependsOn ?? []).filter((d) => remaining.has(d))]));
|
|
93
|
+
while (remaining.size > 0) {
|
|
94
|
+
const wave = [...remaining].filter((n) => deps.get(n)!.every((d) => !remaining.has(d))).sort();
|
|
95
|
+
if (wave.length === 0) throw new WaveCycleError([...remaining].sort());
|
|
96
|
+
for (const n of wave) remaining.delete(n);
|
|
97
|
+
waves.push(wave);
|
|
98
|
+
}
|
|
99
|
+
return waves;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** One member of a wave: a root (or a component with no plannable step) and its plan digest. */
|
|
103
|
+
export interface WaveMember {
|
|
104
|
+
member: string;
|
|
105
|
+
planDigest: string;
|
|
106
|
+
/** A provisional plan (`ChangeSetMember.provisional`, #3416). A wave refuses it. */
|
|
107
|
+
provisional?: true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Thrown when a wave's members include a provisional plan, which no gate may bind. */
|
|
111
|
+
export class ProvisionalWaveMemberError extends Error {
|
|
112
|
+
constructor(readonly members: string[]) {
|
|
113
|
+
super(
|
|
114
|
+
`a wave cannot be gated on a provisional plan: ${members.join(", ")} planned before what it reads applied. ` +
|
|
115
|
+
`Plan it again in its own wave, after its upstream applied`,
|
|
116
|
+
);
|
|
117
|
+
this.name = "ProvisionalWaveMemberError";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* A wave's set digest: {@link changeSetDigest} over its members. Order does
|
|
123
|
+
* not matter, and it moves whenever one member's plan digest does. A wave
|
|
124
|
+
* naming one member twice has no digest and is refused, and so is a wave
|
|
125
|
+
* holding a provisional plan (#3416): a gate bound to it would approve
|
|
126
|
+
* values nobody has yet.
|
|
127
|
+
*/
|
|
128
|
+
export function waveSetDigest(members: readonly WaveMember[]): string {
|
|
129
|
+
const provisional = members.filter((m) => m.provisional === true).map((m) => m.member);
|
|
130
|
+
if (provisional.length > 0) throw new ProvisionalWaveMemberError([...new Set(provisional)].sort());
|
|
131
|
+
return changeSetDigest(members);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The gate a wave waits on: `<gate>-wave-<n>`, under the fan-out's op.
|
|
136
|
+
*
|
|
137
|
+
* One gate name per wave keeps each wave's approvals apart. With one name
|
|
138
|
+
* shared by every wave, wave 2 would find wave 1's approval and report it as
|
|
139
|
+
* an approval of another plan, which is true and useless. Wave numbers come
|
|
140
|
+
* from the derivation, not from what is left to run, so wave 3 stays wave 3
|
|
141
|
+
* on a resumed attempt and its approval still applies.
|
|
142
|
+
*/
|
|
143
|
+
export function waveGateName(gate: string, wave: number): string {
|
|
144
|
+
return `${gate}-wave-${wave}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Where a wave got to. */
|
|
148
|
+
export type WaveStatus =
|
|
149
|
+
/** Planned, and its gate is not approved for this digest. Nothing in it ran. */
|
|
150
|
+
| "gated"
|
|
151
|
+
/** Every member applied. */
|
|
152
|
+
| "applied"
|
|
153
|
+
/** At least one member failed to plan or to apply. Its dependents are blocked. */
|
|
154
|
+
| "failed";
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* One wave, as an attempt record and a run result carry it. The plan report
|
|
158
|
+
* (#3349) reads these and points at the gate record rather than copying it:
|
|
159
|
+
* `op` and `gate` name the ledger entry (`_gates/<op>.jsonl` on
|
|
160
|
+
* `chant/lifecycle`).
|
|
161
|
+
*/
|
|
162
|
+
export interface WaveRecord {
|
|
163
|
+
/** 1-based, from the derivation. */
|
|
164
|
+
wave: number;
|
|
165
|
+
op: string;
|
|
166
|
+
/** The full gate name, {@link waveGateName}. */
|
|
167
|
+
gate: string;
|
|
168
|
+
/** What was planned in this wave on this attempt. */
|
|
169
|
+
components: string[];
|
|
170
|
+
/** The set digest the gate was decided against. */
|
|
171
|
+
digest: string;
|
|
172
|
+
members: WaveMember[];
|
|
173
|
+
status: WaveStatus;
|
|
174
|
+
/** Who approved this digest, once the gate passed. */
|
|
175
|
+
approvedBy?: string;
|
|
176
|
+
/** When the gate refused because an approval stands for another digest: that digest. */
|
|
177
|
+
approved?: string;
|
|
178
|
+
/** Components that failed to plan or to apply, with why. */
|
|
179
|
+
failed?: Array<{ component: string; error?: string }>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const isObject = (value: unknown): value is Record<string, unknown> =>
|
|
183
|
+
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
184
|
+
const strings = (value: unknown): string[] =>
|
|
185
|
+
Array.isArray(value) ? value.filter((v): v is string => typeof v === "string") : [];
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Read the `waves` array of an attempt record, dropping anything malformed.
|
|
189
|
+
* A record written before #3049 has none, which reads as no waves.
|
|
190
|
+
*/
|
|
191
|
+
export function readWaveRecords(value: unknown): WaveRecord[] {
|
|
192
|
+
if (!Array.isArray(value)) return [];
|
|
193
|
+
const out: WaveRecord[] = [];
|
|
194
|
+
for (const raw of value) {
|
|
195
|
+
if (!isObject(raw)) continue;
|
|
196
|
+
const { wave, op, gate, digest, status } = raw;
|
|
197
|
+
if (typeof wave !== "number" || typeof op !== "string" || typeof gate !== "string" || typeof digest !== "string") continue;
|
|
198
|
+
if (status !== "gated" && status !== "applied" && status !== "failed") continue;
|
|
199
|
+
const members = Array.isArray(raw.members)
|
|
200
|
+
? raw.members.filter(
|
|
201
|
+
(m): m is WaveMember => isObject(m) && typeof m.member === "string" && typeof m.planDigest === "string",
|
|
202
|
+
)
|
|
203
|
+
: [];
|
|
204
|
+
const failed = Array.isArray(raw.failed)
|
|
205
|
+
? raw.failed
|
|
206
|
+
.filter((f): f is { component: string; error?: unknown } => isObject(f) && typeof f.component === "string")
|
|
207
|
+
.map((f) => ({ component: f.component, ...(typeof f.error === "string" ? { error: f.error } : {}) }))
|
|
208
|
+
: [];
|
|
209
|
+
out.push({
|
|
210
|
+
wave,
|
|
211
|
+
op,
|
|
212
|
+
gate,
|
|
213
|
+
components: strings(raw.components),
|
|
214
|
+
digest,
|
|
215
|
+
members: members.map((m) => ({ member: m.member, planDigest: m.planDigest })),
|
|
216
|
+
status,
|
|
217
|
+
...(typeof raw.approvedBy === "string" ? { approvedBy: raw.approvedBy } : {}),
|
|
218
|
+
...(typeof raw.approved === "string" ? { approved: raw.approved } : {}),
|
|
219
|
+
...(failed.length > 0 ? { failed } : {}),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Put `record` in `records` in place of any earlier record for the same wave, sorted by wave. */
|
|
226
|
+
export function withWaveRecord(records: readonly WaveRecord[], record: WaveRecord): WaveRecord[] {
|
|
227
|
+
return [...records.filter((r) => r.wave !== record.wave), record].sort((a, b) => a.wave - b.wave);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The refusal for a wave whose set changed after it was approved: the digest
|
|
232
|
+
* that was approved, the one planned now, and that nothing in the wave ran.
|
|
233
|
+
*/
|
|
234
|
+
export function describeChangedWave(record: Pick<WaveRecord, "wave" | "op" | "gate" | "digest" | "approved">): string {
|
|
235
|
+
return (
|
|
236
|
+
`wave ${record.wave} changed after it was approved, so nothing in it was applied. ` +
|
|
237
|
+
`approved: ${record.approved ?? "(none)"}; planned now: ${record.digest}. ` +
|
|
238
|
+
`Read the new plan, then approve it: chant approve ${record.op} ${record.gate} --plan ${record.digest}`
|
|
239
|
+
);
|
|
240
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -149,6 +149,7 @@ export * from "./resource-attributes";
|
|
|
149
149
|
export * from "./stack-output";
|
|
150
150
|
export * from "./child-project";
|
|
151
151
|
export * from "./lsp/types";
|
|
152
|
+
export type { SymbolDeclaration, SymbolResolver } from "./workspace/symbols";
|
|
152
153
|
export * from "./lsp/lexicon-providers";
|
|
153
154
|
export * from "./mcp/types";
|
|
154
155
|
export * from "./lifecycle/index";
|
package/src/lexicon.ts
CHANGED
|
@@ -8,6 +8,8 @@ import type { TypeScriptGenerator } from "./import/generator";
|
|
|
8
8
|
import type { EmbeddedContentImporter } from "./import/embedded";
|
|
9
9
|
import type { AgentConfigImporter } from "./agents/importer";
|
|
10
10
|
import type { ArtifactIntegrity } from "./lexicon-integrity";
|
|
11
|
+
import type { GatedWavePipelineOptions } from "./components/gated-wave-pipeline";
|
|
12
|
+
import type { PrLoopPipelineOptions } from "./components/pr-pipeline";
|
|
11
13
|
import type { OkfFile } from "./okf";
|
|
12
14
|
import type { CompletionContext, CompletionItem, HoverContext, HoverInfo, CodeActionContext, CodeAction } from "./lsp/types";
|
|
13
15
|
import type { McpToolContribution, McpResourceContribution } from "./mcp/types";
|
|
@@ -26,6 +28,7 @@ import type { DescribeIdentityOptions, DescribeIdentityResult } from "./identity
|
|
|
26
28
|
import type { DeepNormalizationHooks, DeepObservationResult } from "./deep-observation";
|
|
27
29
|
import type { BehaviourResult, PredictBehaviourOptions } from "./behaviour";
|
|
28
30
|
import type { BehaviourKinds } from "./behaviour-kinds";
|
|
31
|
+
import type { SymbolResolver } from "./workspace/symbols";
|
|
29
32
|
import type { DisruptionQuery, DisruptionVerdict } from "./lifecycle/disruption";
|
|
30
33
|
import type { OwnerChainVerdict } from "./owner-chain";
|
|
31
34
|
import type { CommandGroup } from "./cli/command-group";
|
|
@@ -536,6 +539,20 @@ export interface ComponentPipelineOptions {
|
|
|
536
539
|
* `["chant", "components", "promote", "--from", <env>, "--to", <promoteTo>]`.
|
|
537
540
|
*/
|
|
538
541
|
promoteCommand?: string[];
|
|
542
|
+
/**
|
|
543
|
+
* Gated waves (#3049): one job per wave instead of one per component. Job
|
|
544
|
+
* `wave-<k>` runs `chant components fan-out --wave-gate <gate> --wave <k>`
|
|
545
|
+
* and hands the attempt record to the next job. See
|
|
546
|
+
* `components/gated-wave-pipeline.ts`. Unset, the pipeline is unchanged.
|
|
547
|
+
*/
|
|
548
|
+
gatedWaves?: GatedWavePipelineOptions;
|
|
549
|
+
/**
|
|
550
|
+
* The pull-request loop (#3183): a plan job on each pull request and an
|
|
551
|
+
* apply job on each push to the target branch, bound to the digest a
|
|
552
|
+
* reviewer approved, instead of one job per component. See
|
|
553
|
+
* `components/pr-pipeline.ts`. Unset, the pipeline is unchanged.
|
|
554
|
+
*/
|
|
555
|
+
prLoop?: PrLoopPipelineOptions;
|
|
539
556
|
/**
|
|
540
557
|
* The workspace member this pipeline belongs to (#2542, #2524 D19). Core
|
|
541
558
|
* sets it when the project sits in a member of a `chant.workspace.json`;
|
|
@@ -890,6 +907,16 @@ export interface StackStatusObservation {
|
|
|
890
907
|
/**
|
|
891
908
|
* Context for a lexicon's {@link LexiconPlugin.buildRoots} hook (#1548).
|
|
892
909
|
*/
|
|
910
|
+
/** What {@link LexiconPlugin.changedUnits} reads (#3183). */
|
|
911
|
+
export interface ChangedUnitsContext {
|
|
912
|
+
/** The directory `chant.config.*` lives in. Config paths resolve against it. */
|
|
913
|
+
projectRoot: string;
|
|
914
|
+
/** The resolved project configuration, for the lexicon's own namespace. */
|
|
915
|
+
config: Record<string, unknown>;
|
|
916
|
+
/** Files the change added, modified or deleted, relative to `projectRoot`, with `/` separators. */
|
|
917
|
+
changedFiles: string[];
|
|
918
|
+
}
|
|
919
|
+
|
|
893
920
|
export interface BuildRootContext {
|
|
894
921
|
/**
|
|
895
922
|
* The directory the project configuration was loaded from (where
|
|
@@ -1311,6 +1338,18 @@ export interface LexiconPlugin {
|
|
|
1311
1338
|
*/
|
|
1312
1339
|
buildRoots?(ctx: BuildRootContext): Promise<BuildRootContribution>;
|
|
1313
1340
|
|
|
1341
|
+
/**
|
|
1342
|
+
* The units this lexicon owns that a change touched, judged from the
|
|
1343
|
+
* changed files alone (#3183). A unit is a name a component's deploy step
|
|
1344
|
+
* targets (`deploy-units.ts`): the terraform lexicon answers with the
|
|
1345
|
+
* `terraform.roots` entries whose directory, local modules or var files a
|
|
1346
|
+
* changed file sits in. `chant components fan-out --base` and `chant
|
|
1347
|
+
* components pr-plan` add these to the stacks `lifecycle affected` finds,
|
|
1348
|
+
* since a root that is not chant source never shows in an artifact diff.
|
|
1349
|
+
* Omit for lexicons whose units are chant source.
|
|
1350
|
+
*/
|
|
1351
|
+
changedUnits?(ctx: ChangedUnitsContext): Promise<string[]>;
|
|
1352
|
+
|
|
1314
1353
|
/**
|
|
1315
1354
|
* Facts about the whole read that this lexicon's entities imply, for the
|
|
1316
1355
|
* graph IR's `meta` bag (#2559). The otel lexicon answers
|
|
@@ -1337,6 +1376,18 @@ export interface LexiconPlugin {
|
|
|
1337
1376
|
/** Provide code actions for LSP */
|
|
1338
1377
|
codeActionProvider?(ctx: CodeActionContext): CodeAction[];
|
|
1339
1378
|
|
|
1379
|
+
/**
|
|
1380
|
+
* Symbol resolvers for the languages this lexicon's members are written in
|
|
1381
|
+
* (#3313), so `chant workspace graph --intent <path>#<symbol>` names a
|
|
1382
|
+
* declaration in such a file: a table in a `.sql` file, a resource in a
|
|
1383
|
+
* manifest. Each is `{ language, extensions, declarations(path, text) }`,
|
|
1384
|
+
* returning every declaration's dotted name, kind and 1-based line range.
|
|
1385
|
+
* The intent walk asks the lexicons a file's member configures, after
|
|
1386
|
+
* core's TypeScript and JavaScript resolver, which keeps `.ts` and `.js`.
|
|
1387
|
+
* Pure: no I/O, no network. Omit for a lexicon with no such language.
|
|
1388
|
+
*/
|
|
1389
|
+
symbolResolvers?(): SymbolResolver[];
|
|
1390
|
+
|
|
1340
1391
|
// Docs
|
|
1341
1392
|
/** Generate documentation pages */
|
|
1342
1393
|
docs?(options?: { verbose?: boolean }): Promise<void>;
|
|
@@ -117,6 +117,13 @@ export interface GateResolutionRecord {
|
|
|
117
117
|
gate: string;
|
|
118
118
|
/** Who resolved it — an actor name, the same convention `components release --actor` and `run approve --approver` use. */
|
|
119
119
|
resolvedBy: string;
|
|
120
|
+
/**
|
|
121
|
+
* Who carried this approval to chant for `resolvedBy` (#3402): `chant
|
|
122
|
+
* approve --relayed-by`, such as a follower program relaying a person's
|
|
123
|
+
* approval. The approver is still `resolvedBy`; a seal covers this field
|
|
124
|
+
* too. Absent when the approver recorded it themselves.
|
|
125
|
+
*/
|
|
126
|
+
relayedBy?: string;
|
|
120
127
|
/** ISO-8601 timestamp, caller-supplied (library code never calls `Date.now()` internally). */
|
|
121
128
|
timestamp: string;
|
|
122
129
|
/** Optional free-text context. Before #2028 this was also where a PR link went by convention; put the link in {@link GateResolutionRecord.url} instead and leave this for prose. */
|