@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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$comment": "The studio kit's app template, arugula-salad/studio 84c8b21 template/, which is chud's template at jhgaylor/chud 43afcf1 with the chud CLI steps taken out of its prose and delivery/package.json. The fixture for chant workspace upgrade's chud-lexicon-exit migration (INTENTIUS/chant#2737). Data only: nothing here is imported or run by chant's tests, and delivery/ names @intentius/chud-runtime, which chant never installs.",
|
|
3
3
|
"source": "https://github.com/arugula-salad/studio/tree/84c8b21/template",
|
|
4
4
|
"files": {
|
|
5
|
-
".github/workflows/ci.yml": "name: ci\n\non:\n push:\n branches:\n - main\n pull_request: {}\n\npermissions:\n contents: read\n\nconcurrency:\n group: ci-${{ github.ref }}\n cancel-in-progress: '${{ github.event_name == ''pull_request'' }}'\n\njobs:\n ci:\n runs-on: ubuntu-latest\n timeout-minutes: 20\n steps:\n - name: Checkout\n uses: actions/checkout@
|
|
5
|
+
".github/workflows/ci.yml": "name: ci\n\non:\n push:\n branches:\n - main\n pull_request: {}\n\npermissions:\n contents: read\n\nconcurrency:\n group: ci-${{ github.ref }}\n cancel-in-progress: '${{ github.event_name == ''pull_request'' }}'\n\njobs:\n ci:\n runs-on: ubuntu-latest\n timeout-minutes: 20\n steps:\n - name: Checkout\n uses: actions/checkout@v4\n - name: Setup Node.js\n uses: actions/setup-node@v4\n with:\n node-version: '22.x'\n cache: npm\n cache-dependency-path: delivery/package-lock.json\n - name: Read access to @intentius/chud-runtime\n env:\n CHUD_RUNTIME_TOKEN: '${{ secrets.CHUD_RUNTIME_TOKEN }}'\n CHUD_RUNTIME_DEPLOY_KEY: '${{ secrets.CHUD_RUNTIME_DEPLOY_KEY }}'\n run: |\n if [ -n \"$CHUD_RUNTIME_TOKEN\" ]; then\n git config --global url.\"https://x-access-token:${CHUD_RUNTIME_TOKEN}@github.com/\".insteadOf ssh://git@github.com/\n git config --global --add url.\"https://x-access-token:${CHUD_RUNTIME_TOKEN}@github.com/\".insteadOf git@github.com:\n elif [ -n \"$CHUD_RUNTIME_DEPLOY_KEY\" ]; then\n mkdir -p ~/.ssh && chmod 700 ~/.ssh\n printf '%s\\n' \"$CHUD_RUNTIME_DEPLOY_KEY\" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519\n echo \"github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl\" >> ~/.ssh/known_hosts\n else\n echo \"neither CHUD_RUNTIME_TOKEN nor CHUD_RUNTIME_DEPLOY_KEY is set: a private @intentius/chud-runtime will not install\"\n fi\n - name: Install\n run: npm ci\n working-directory: delivery\n - name: Workflow YAML is built from ci/ci.ts\n run: npm run ci:check\n working-directory: delivery\n - name: chant workspace check\n run: npx chant workspace check\n working-directory: delivery\n - name: chant build\n run: npx chant build -o \"$RUNNER_TEMP/chant-build.out\"\n working-directory: delivery\n - name: chant lint\n run: npx chant lint\n working-directory: delivery\n - name: App tests and approved contract checks\n run: npm run check\n working-directory: delivery\n",
|
|
6
6
|
".gitignore": "node_modules/\n.chud/\n.data/\n.hud/\n# chant build output (the Ops as op.json)\ndist/\n",
|
|
7
7
|
"CLAUDE.md": "# {{chant:name}}: notes for agents\n\nThis repo is a chant workspace made from the studio kit's template\n(`chant init --from <studio>/template`, then the kit's `box/setup-app.mjs`).\nIt holds everything about {{chant:name}}: the app, the contracts it must\nmeet, the decisions made about it, the agents that build it, and the Ops that\nship it. `chant.workspace.json` declares its members, and chant finds\neverything through them.\n\nYou are probably running as hud's agent in the app's page: the app's own\nserver (its `dev` script, `node --watch`) behind hud's proxy. Your working\ndirectory is this one (the workspace root), and your job is to build the app\nin `app/`. See `app/CLAUDE.md`. Decisions and work items are chant records:\nwrite them with `chant workspace records`, and hud shows them.\n\n## Where things are\n\n| Member | Directory | What it holds |\n|---|---|---|\n| app | `app/` | The product. Edited live while its `dev` script runs. |\n| delivery | `delivery/` | The chant project: `chant.config.ts` (this repo's settings as `buildParams`: ports and how to run the app), `ops/` (the release, rollback, upgrade and dispatch Ops), `deploy/` (the app as a chant component and the sites it ships to), `agents/team.ts` (the agents that build the app, as fountain specs), `decisions/` (the decision points and the ship gate's Cedar policy), `ci/ci.ts` (this repo's CI), `.chant/` (the write-scope policy and the contract-sizing lint rules), and `node_modules/`. |\n| design | `design/` | chud's records, written by the design app: `contracts/` (and `contracts/checks/`), `drivers/`, `evidence/`, `sessions/`, `context/`, `proposals/`. Also the design app's overrides and the screen specs in `screens/`. |\n| design-client | `design-client/` | A placeholder for a vendored design client. Nothing here yet. |\n\n`work/` at the root holds the units of work: chant work items\n(`W-NNN-<slug>.md`, `work.kind.mjs` and `work.schema.json` beside them), one\nper agent run or piece of manual work, with chud's fields under `x-chud`.\nchud writes them through `chant workspace records new|amend`; list them with\n`chant workspace records --kind work/work.kind.mjs`.\n\n`decisions/` at the root holds the decision records: chant decision records\n(`decision.kind.mjs` and `decision.schema.json` beside them define the\nformat), the template's `ref-001`, `ref-002` and `chud-001`, and the\n`chud-NNN` records the design app writes. Each cites the issue in\n`chud.decisions.issue` (`delivery/chant.config.ts`) as its source, or\n`chud-001`'s when none is set. List the\ncurrent ones with\n`chant workspace records --kind decisions/decision.kind.mjs --current`.\n\nA decision pins the files it rests on by path and hash: an `evidence` entry\n`{ title, path, sha256, as_of }`, `path` from the workspace root and inside a\nmember (such as `design/screens/home.json`), and `sha256` the hash of the\nfile's bytes, which `chant workspace records pin <path>` prints. What it\ngoverns goes in `constrains` as `member:<name>` or `path:<path>`. `chant\nworkspace records` reports each pin as `pinned`, `drifted` (the file changed\nsince), `missing` or `stale` (a superseding decision pins the file at the\nsame hash, and the file has not changed since). A drifted pin means the file\nmoved away from what was decided: say so, and do not re-pin it yourself. The\ndesign app pins a file by name (`POST /api/decisions` with `{ title, path }`\nin `evidence` hashes it) and re-pins with `PUT /api/decisions/<id>`.\n\nEvidence (`design/evidence/<sha256>.json`) and design sessions\n(`design/sessions/S-NNNN.json`) are chant record kinds too, declared on the\ndesign member in `chant.workspace.json` (`design/evidence/evidence.kind.mjs`,\n`design/sessions/session.kind.mjs`). Read them with\n`chant workspace records --json`, which reads every declared kind. An\nevidence record's id is the sha256 of its bytes; a file whose name is not\nthat hash carries `asset-drift` and is not evidence. A closed session is\nsealed, and an edit to it is `session-seal-mismatch`. Only the design app and\nthe Ops' Check phases write these files, and each write is read back through\nchant before it is committed. Never write or edit one by hand, and never\nrename one.\n\n- Records keep their core fields in YAML front matter (or JSON), validated\n against the schemas in\n `delivery/node_modules/@intentius/chud-runtime/schemas/`. Extra fields must\n start with `x-`.\n- `delivery/node_modules/@intentius/chud-runtime` is the machinery (the design\n app, the Op steps). It is a versioned dependency: never edit it. Upgrade it\n with the upgrade Op (`npm run upgrade` in `delivery/`), which stops at a\n gate for a person.\n- `.chud/`: local state (hud data, staged releases, the local site). Never\n edit it, and never commit it.\n\nEvery path chud records (a unit's scope, a protected path, a record's `file`)\nis relative to this directory, the git top-level.\n\n## Rules\n\n- Contracts and decisions are written by the design app. Read the ones that\n touch your work and do not undo them. Never edit `design/contracts/`,\n `decisions/`, `work/`, `design/evidence/`, `design/drivers/`,\n `design/context/`, `design/sessions/`, `delivery/decisions/`,\n `delivery/.chant/`, `delivery/chant.config.ts` or `chant.workspace.json`\n (or the paths `chud.protectedPaths` in `delivery/chant.config.ts` adds)\n from the app's page. This is enforced: an agent run's changes there are thrown\n away, and a release refuses to plan while a commit that changes them lacks\n the design app's `Chud-Author: design-app` trailer.\n- Never commit, tag or push. Committing, planning and shipping happen in the\n design app, through the release Op and its gate.\n\n## Why a line is the way it is\n\nThe design app's commits for a unit of work carry trailers that tie the\ncommit to that unit: `Chud-Unit: W-NNN`, `Chud-Contract: C-NNN` (or\n`Chud-Driver: D-NNN` for a split) and one `Chud-Evidence: <sha256>` per\nevidence record the unit cites, beside `Chud-Author: design-app`.\n`decisions/decision.kind.mjs` exports `commitJoins`, which joins a commit\nto its unit from the work item whose `x-chud.result.commit` is the commit\n(chant 0.86's `list` on the hook's context), and from those trailers for the\nunit's other commits, so\n\n```sh\nchant workspace graph --intent app/server.js --json\n```\n\nlists the commits that touched the file (a `path:start-end` region narrows\nit to lines), each joined to the unit that made it (`work/W-NNN-<slug>.md`),\nthe contract it served and the evidence it cited, with the decisions whose\n`constrains` cover the file and what they pin, and the work items whose\nwindow a commit fell in (`worked`). Read it before changing code\na decision governs. The template pins chant 0.86, which has all of this.\nchant marks a commit inside a decision's window `decided-by-window`, and the\nkind adds `plugin:chud:contract-moved` for a unit commit whose contract's\ncriteria differ from those the first unit under that decision served: the\ncontract moved with no decision. Record a decision before you change what a\ncontract asks for.\n",
|
|
8
8
|
"README.md": "# {{chant:name}}\n\nA chant workspace made from the studio kit's template\n([arugula-salad/studio](https://github.com/arugula-salad/studio) `template/`),\nwhich is chant's reference workspace with an app, a delivery project and\nrecords laid onto it. It started as chud's template (jhgaylor/chud 43afcf1)\nand moved into the kit when chud began to retire (arugula-salad/studio#37).\nThe app is built with hud in the page, and releases ship through chant Ops.\n\n```sh\nchant init --from <studio checkout>/template --param name=\"{{chant:name}}\" [--param issue=owner/repo#1] <dir>\nnode <studio checkout>/box/setup-app.mjs <dir> # free ports, npm install in delivery/, git init, the first commit\ncd <dir>/app && npm run dev # the app, restarted on every change (node --watch)\n```\n\nA studio box does both steps in `box/provision-template.sh` and runs the\napp's `dev` script behind hud's proxy. No chud command runs.\n\n[`chant.workspace.json`](chant.workspace.json) declares the members, and\n`chant workspace ls` lists them. The release Op finds everything through them.\n\n| Member | Directory | Kind | What it is |\n|---|---|---|---|\n| app | [`app/`](app) | `other` | the product: a Node HTTP server with SQLite migrations and its own tests |\n| delivery | [`delivery/`](delivery) | `chant` | the chant project, which still runs on chud's runtime package (`@intentius/chud-runtime`) and the chud lexicon until they are split out of chud (INTENTIUS/chant#2713): the release, rollback, upgrade and dispatch Ops, the sites releases ship to, the agents that build the app, the decision points, the write-scope policy and this repo's CI |\n| design-client | [`design-client/`](design-client) | `other`, role `design-app` | a placeholder for a vendored design client (chant's reference workspace, #2524 D18) |\n| design | [`design/`](design) | `other` | the design data: chud's records (contracts, drivers, evidence, sessions, context, proposals), the screen specs, and the design app's overrides |\n\nThe root also holds [`decisions/`](decisions), the decision records. They\nare chant decision records, the format of chant's `docs/design/decisions/`\n(the kind file and schema are beside them): `ref-001` and `ref-002` from\nchant's reference workspace, `chud-001`, and the `chud-NNN` records the design\napp writes as the team decides. Each cites the issue this app's design is\ntracked in (`chud.decisions.issue` in `delivery/chant.config.ts`, the\ntemplate's optional `issue` parameter) as its source, or without one the\nsource of `chud-001`:\n\n```sh\nchant workspace records --kind decisions/decision.kind.mjs --current\n```\n\n[`work/`](work) holds the units of work: every agent run and piece of\nmanual work is a chant work item (`W-NNN-<slug>.md`, chant's work kind with\nchud's fields under `x-chud`), written by chud through `chant workspace\nrecords new` and `records amend`. Both kinds are declared in\n`chant.workspace.json` `records`:\n\n```sh\nchant workspace records --kind work/work.kind.mjs\n```\n\n`.chant/workspace.lock.json`, written by `chant init --from`, records the\ntemplate's lineage: where it came from, at which commit, and the parameters\nit was made with.\n\n## Everyday commands\n\nIn `delivery/`:\n\n```sh\nnpm run release # chant run release --on chud: check, plan, stop at the ship gate\nchant approve release ship --plan <digest>\nnpm run release # ship it\nnpm run rollback\nnpm run upgrade # move to a newer @intentius/chud-runtime, through its gate\nnpm run check # the app's tests and every approved contract's check (CI runs this)\nnpm run lint # chant lint agents, with the contract-sizing rules\n```\n\nAt the root:\n\n```sh\nchant workspace check\nchant workspace build\nchant workspace lint\n```\n\n`CLAUDE.md` is the map for agents, and `design/CLAUDE.md` has the rules for\nthe records.\n",
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The app member kind and member fields (#3151, ws-093): a data-only kinds
|
|
3
|
+
* file that declares the fields its members set, a probe that reads a key of
|
|
4
|
+
* a JSON file, WSP005 on a field the kind doesn't declare, and status --json
|
|
5
|
+
* printing every field with its defaults.
|
|
6
|
+
*/
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
8
|
+
import { cpSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { afterAll, describe, expect, test } from "vitest";
|
|
13
|
+
import { runDeclarationChecks } from "./checks";
|
|
14
|
+
import { createKindRegistry, parseKindData, probeKind, readPackageKinds, resolveKind, resolveMemberFields, type MemberKind } from "./kinds";
|
|
15
|
+
import { workspaceStatus } from "./status";
|
|
16
|
+
import { workingTree } from "./tree";
|
|
17
|
+
|
|
18
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const canonical = join(here, "reference-kinds", "app");
|
|
20
|
+
const reference = join(here, "..", "..", "..", "..", "reference-workspace");
|
|
21
|
+
|
|
22
|
+
const scratch: string[] = [];
|
|
23
|
+
afterAll(() => {
|
|
24
|
+
for (const d of scratch) rmSync(d, { recursive: true, force: true });
|
|
25
|
+
});
|
|
26
|
+
function repo(files: Record<string, string>): string {
|
|
27
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "chant-3151-")));
|
|
28
|
+
scratch.push(root);
|
|
29
|
+
execFileSync("git", ["init", "-q"], { cwd: root });
|
|
30
|
+
for (const [path, text] of Object.entries(files)) {
|
|
31
|
+
mkdirSync(dirname(join(root, path)), { recursive: true });
|
|
32
|
+
writeFileSync(join(root, path), text);
|
|
33
|
+
}
|
|
34
|
+
return root;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A workspace that pins a copy of the app kind at kinds/app. */
|
|
38
|
+
function appWorkspace(members: unknown[], files: Record<string, string> = {}): string {
|
|
39
|
+
const root = repo({ "chant.workspace.json": JSON.stringify({ name: "acme", schema: 1, members, pins: [{ path: "kinds/app" }] }, null, 2), ...files });
|
|
40
|
+
cpSync(canonical, join(root, "kinds", "app"), { recursive: true });
|
|
41
|
+
return root;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const app = (): MemberKind => {
|
|
45
|
+
const read = readPackageKinds(canonical, "kinds/app");
|
|
46
|
+
expect(read.problems).toEqual([]);
|
|
47
|
+
return read.kinds[0];
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const pkg = (scripts: Record<string, string>) => JSON.stringify({ name: "web", scripts });
|
|
51
|
+
|
|
52
|
+
describe("the app kind's file", () => {
|
|
53
|
+
test("is valid kind data: one kind, app, with the outputs and fields a delivery member and an orchestrator read", () => {
|
|
54
|
+
const k = app();
|
|
55
|
+
expect(k.name).toBe("app");
|
|
56
|
+
expect(k.outputs).toEqual({ from: "declared", names: ["source", "url"] });
|
|
57
|
+
expect(Object.keys(k.fields ?? {})).toEqual(["scripts", "env", "health"]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("the reference workspace pins a byte-for-byte copy of it", () => {
|
|
61
|
+
const files = readdirSync(canonical).sort();
|
|
62
|
+
expect(readdirSync(join(reference, "kinds", "app")).sort()).toEqual(files);
|
|
63
|
+
for (const f of files) expect(readFileSync(join(reference, "kinds", "app", f), "utf-8")).toBe(readFileSync(join(canonical, f), "utf-8"));
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("its probe claims a Node package with a start script and nothing else, and a chant project outranks it", () => {
|
|
67
|
+
const tree = workingTree(
|
|
68
|
+
repo({
|
|
69
|
+
"web/package.json": pkg({ start: "node server.js" }),
|
|
70
|
+
"lib/package.json": pkg({ test: "node --test" }),
|
|
71
|
+
"broken/package.json": "{ not json",
|
|
72
|
+
"none/README.md": "",
|
|
73
|
+
"both/package.json": pkg({ start: "node x.js" }),
|
|
74
|
+
"both/chant.config.ts": "",
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
const k = app();
|
|
78
|
+
expect(["web", "lib", "broken", "none"].map((d) => probeKind(k, tree, d))).toEqual([true, false, false, false]);
|
|
79
|
+
const kinds = createKindRegistry([k]);
|
|
80
|
+
expect(resolveKind(kinds, tree, "both").winner?.name).toBe("chant");
|
|
81
|
+
expect(resolveKind(kinds, tree, "web").winner?.name).toBe("app");
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
describe("anyJsonKey and fields in a kinds file", () => {
|
|
86
|
+
const base = { name: "svc", description: "a service", precedence: 10, probe: { anyJsonKey: { in: ["svc.json"], pointers: ["/run", "/a~1b/c"] } } };
|
|
87
|
+
const file = (kinds: unknown[]) => JSON.stringify({ schema: 1, kinds });
|
|
88
|
+
|
|
89
|
+
test("anyJsonKey takes JSON Pointers, with ~1 for a slash in a key", () => {
|
|
90
|
+
const tree = workingTree(repo({ "a/svc.json": '{"run":null}', "b/svc.json": '{"a/b":{"c":1}}', "c/svc.json": '{"a":{"b":{"c":1}}}' }));
|
|
91
|
+
const k = parseKindData(file([base]), "p").kinds[0];
|
|
92
|
+
expect(["a", "b", "c"].map((d) => probeKind(k, tree, d))).toEqual([true, true, false]);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("a pointer must start with a slash, and a field's type, description and default are checked", () => {
|
|
96
|
+
expect(parseKindData(file([{ ...base, probe: { anyJsonKey: { in: ["svc.json"], pointers: ["run"] } } }]), "p").problems[0]).toMatch(/pointers/);
|
|
97
|
+
expect(parseKindData(file([{ ...base, fields: { port: { type: "integer", description: "the port", default: 80 } } }]), "p").problems).toEqual([]);
|
|
98
|
+
expect(parseKindData(file([{ ...base, fields: { port: { type: "number", description: "the port" } } }]), "p").problems.length).toBeGreaterThan(0);
|
|
99
|
+
expect(parseKindData(file([{ ...base, fields: { port: { type: "integer", description: "the port", default: "80" } } }]), "p").problems).toEqual([
|
|
100
|
+
"p: kind svc declares field port whose default is \"80\", and the field is an integer",
|
|
101
|
+
]);
|
|
102
|
+
expect(parseKindData(file([{ ...base, fields: { path: { type: "string", description: "a path", default: "x", pattern: "^/" } } }]), "p").problems).toEqual([
|
|
103
|
+
'p: kind svc declares field path whose default is "x", which does not match ^/',
|
|
104
|
+
]);
|
|
105
|
+
expect(parseKindData(file([{ ...base, fields: { path: { type: "string", description: "a path", pattern: "(" } } }]), "p").problems[0]).toMatch(/not a regular expression/);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe("resolveMemberFields", () => {
|
|
110
|
+
test("fills every declared field from its default, and keeps what the entry sets", () => {
|
|
111
|
+
const r = resolveMemberFields(app(), { health: "/healthz", scripts: { start: "serve" } });
|
|
112
|
+
expect(r.problems).toEqual([]);
|
|
113
|
+
expect(r.fields).toEqual({
|
|
114
|
+
scripts: { start: "serve", dev: "dev", test: "test", migrate: "migrate" },
|
|
115
|
+
env: { port: "PORT", data: "APP_DATA", revision: "APP_REVISION" },
|
|
116
|
+
health: "/healthz",
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("a field the kind doesn't declare, a value of the wrong type or pattern, and an object given a scalar are problems, and read as the default", () => {
|
|
121
|
+
const r = resolveMemberFields(app(), { healthz: "/x", health: "health", env: { port: 8080, host: "x" }, scripts: "start" });
|
|
122
|
+
expect(r.problems.map((p) => p.path)).toEqual(["healthz", "scripts", "env/host", "env/port", "health"]);
|
|
123
|
+
expect(r.fields?.health).toBe("/health");
|
|
124
|
+
expect(r.fields?.env).toEqual({ port: "PORT", data: "APP_DATA", revision: "APP_REVISION" });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("a kind with no fields reads as null, and fields set on it are one problem; an unknown kind has nothing to check", () => {
|
|
128
|
+
const other: MemberKind = { ...app(), name: "plain", fields: undefined };
|
|
129
|
+
expect(resolveMemberFields(other, null)).toEqual({ fields: null, problems: [] });
|
|
130
|
+
expect(resolveMemberFields(other, { health: "/" }).problems).toHaveLength(1);
|
|
131
|
+
expect(resolveMemberFields(undefined, { health: "/" })).toEqual({ fields: null, problems: [] });
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe("chant workspace check and status on an app member", () => {
|
|
136
|
+
const ids = async (root: string) => (await runDeclarationChecks(root)).diagnostics.map((d) => `${d.ruleId}:${d.entity ?? ""}:${d.message}`);
|
|
137
|
+
|
|
138
|
+
test("an app member with valid fields checks clean, and a delivery member may link to its source", async () => {
|
|
139
|
+
const root = appWorkspace(
|
|
140
|
+
[
|
|
141
|
+
{ name: "web", dir: "web", kind: "app", fields: { health: "/healthz" } },
|
|
142
|
+
{ name: "deploy", dir: "deploy", kind: "chant", links: [{ member: "web", output: "source" }] },
|
|
143
|
+
],
|
|
144
|
+
{ "web/package.json": pkg({ start: "node server.js" }), "deploy/chant.config.ts": "" },
|
|
145
|
+
);
|
|
146
|
+
expect(await ids(root)).toEqual([]);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("a bad field, fields on a kind that declares none, and an app with no start script fail WSP005", async () => {
|
|
150
|
+
const root = appWorkspace(
|
|
151
|
+
[
|
|
152
|
+
{ name: "web", dir: "web", kind: "app", fields: { health: "healthz", port: 3000 } },
|
|
153
|
+
{ name: "lib", dir: "lib", kind: "app" },
|
|
154
|
+
{ name: "deploy", dir: "deploy", kind: "chant", fields: { health: "/" } },
|
|
155
|
+
],
|
|
156
|
+
{ "web/package.json": pkg({ start: "node server.js" }), "lib/package.json": pkg({ test: "x" }), "deploy/chant.config.ts": "" },
|
|
157
|
+
);
|
|
158
|
+
const found = await ids(root);
|
|
159
|
+
expect(found).toHaveLength(4);
|
|
160
|
+
expect(found.every((f) => f.startsWith("WSP005:"))).toBe(true);
|
|
161
|
+
expect(found.join("\n")).toMatch(/web: field health is "healthz", which does not match \^\//);
|
|
162
|
+
expect(found.join("\n")).toMatch(/web: kind app declares no field port/);
|
|
163
|
+
expect(found.join("\n")).toMatch(/lib \(lib\) is not an app/);
|
|
164
|
+
expect(found.join("\n")).toMatch(/deploy: kind chant declares no fields/);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("an other member that is a Node package with a start script is claimed by the app kind (WSP008), and one without isn't", async () => {
|
|
168
|
+
const root = appWorkspace(
|
|
169
|
+
[
|
|
170
|
+
{ name: "server", dir: "server", kind: "other", because: "a server" },
|
|
171
|
+
{ name: "tools", dir: "tools", kind: "other", because: "scripts" },
|
|
172
|
+
],
|
|
173
|
+
{ "server/package.json": pkg({ start: "node s.js" }), "tools/package.json": pkg({ build: "x" }) },
|
|
174
|
+
);
|
|
175
|
+
const found = await ids(root);
|
|
176
|
+
expect(found.filter((f) => f.startsWith("WSP008"))).toEqual([expect.stringMatching(/^WSP008:server:.*declare it as app/)]);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("status --json prints each member's fields, with defaults, and null for a kind with none", async () => {
|
|
180
|
+
const root = appWorkspace([
|
|
181
|
+
{ name: "web", dir: "web", kind: "app", fields: { scripts: { migrate: "db:migrate" } } },
|
|
182
|
+
{ name: "notes", dir: "notes", kind: "other", because: "notes" },
|
|
183
|
+
], { "web/package.json": pkg({ start: "node server.js" }), "notes/README.md": "" });
|
|
184
|
+
execFileSync("git", ["add", "-A"], { cwd: root });
|
|
185
|
+
execFileSync("git", ["-c", "user.name=t", "-c", "user.email=t@example.com", "commit", "-qm", "init"], { cwd: root });
|
|
186
|
+
const doc = await workspaceStatus({ cwd: root, env: "prod" });
|
|
187
|
+
if ("error" in doc) throw new Error(doc.error.message);
|
|
188
|
+
const byName = Object.fromEntries(doc.members.map((m) => [m.name, m.fields]));
|
|
189
|
+
expect(byName.web).toEqual({
|
|
190
|
+
scripts: { start: "start", dev: "dev", test: "test", migrate: "db:migrate" },
|
|
191
|
+
env: { port: "PORT", data: "APP_DATA", revision: "APP_REVISION" },
|
|
192
|
+
health: "/health",
|
|
193
|
+
});
|
|
194
|
+
expect(byName.notes).toBeNull();
|
|
195
|
+
});
|
|
196
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `chant workspace box`: a member's box block. Two verbs:
|
|
3
|
+
*
|
|
4
|
+
* - `box listing set <member>` (#3308) changes the box's listing
|
|
5
|
+
* (`box-listing.ts`). A box's other configuration is changed by editing the
|
|
6
|
+
* declaration in a reviewed change.
|
|
7
|
+
* - `box publish <member> <item> | --records` (#3165, ws-088) runs the
|
|
8
|
+
* publisher the box block names and checks what it did (`box-publish.ts`).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { resolve } from "node:path";
|
|
13
|
+
import { formatError } from "../cli/format";
|
|
14
|
+
import type { CommandContext } from "../cli/registry";
|
|
15
|
+
import { BOX_LISTING_CONTRACT_VERSION, BOX_LISTING_WRITE_SCHEMA_ID, boxListingSet, type BoxListingWriteDocument } from "./box-listing";
|
|
16
|
+
import { BOX_PUBLISH_CONTRACT_VERSION, BOX_PUBLISH_SCHEMA_ID, boxPublish, type BoxPublishDocument } from "./box-publish";
|
|
17
|
+
import { readerVersion } from "./declaration";
|
|
18
|
+
import { AGENT_ENV } from "./write-scope";
|
|
19
|
+
|
|
20
|
+
const USAGE = "chant workspace box listing set <member> [--from <file|->] [--cover <image> [--cover-path <path>]] [--by <principal>] [--dry-run] [--json]";
|
|
21
|
+
const PUBLISH_USAGE = "chant workspace box publish <member> (<item> | --records) [--by <principal>] [--head <owner/name>] [--dry-run] [--json]";
|
|
22
|
+
|
|
23
|
+
export async function runWorkspaceBox(ctx: CommandContext): Promise<number> {
|
|
24
|
+
const { args } = ctx;
|
|
25
|
+
const cwd = process.cwd();
|
|
26
|
+
// workspace box <noun> <verb> <member>, or workspace box publish <member> [<item>]
|
|
27
|
+
const [, noun, verb, member, ...extra] = args.positionals ?? [];
|
|
28
|
+
// For publish, the third positional is the member and the fourth the item.
|
|
29
|
+
if (noun === "publish") return runPublish(ctx, verb, member === undefined ? [] : [member, ...extra]);
|
|
30
|
+
if (noun !== "listing" || verb !== "set") {
|
|
31
|
+
console.error(
|
|
32
|
+
formatError({
|
|
33
|
+
message: noun === undefined ? "chant workspace box takes listing set <member> or publish <member>" : `chant workspace box takes listing set <member> or publish <member>, not ${[noun, verb].filter(Boolean).join(" ")}`,
|
|
34
|
+
hint: `${USAGE}\n${PUBLISH_USAGE}`,
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
|
+
const print = (doc: BoxListingWriteDocument): number => {
|
|
40
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
41
|
+
return "error" in doc ? 1 : 0;
|
|
42
|
+
};
|
|
43
|
+
const usage = (message: string) =>
|
|
44
|
+
print({ $schema: BOX_LISTING_WRITE_SCHEMA_ID, contract: BOX_LISTING_CONTRACT_VERSION, chant: readerVersion(), member: member ?? null, error: { code: "write-usage-invalid", message } });
|
|
45
|
+
if (member === undefined) return usage("box listing set needs the member whose box it lists: box listing set <member>");
|
|
46
|
+
if (extra.length > 0) return usage(`box listing set takes one member, and was also given ${extra.join(" ")}`);
|
|
47
|
+
for (const [flag, v] of [["--set", args.set], ["--kind", args.kind], ["--at", args.at]] as const) {
|
|
48
|
+
if (v !== undefined) return usage(`box listing set takes its fields with --from, not ${flag}`);
|
|
49
|
+
}
|
|
50
|
+
let fields: string | undefined;
|
|
51
|
+
if (args.migrateFrom !== undefined) {
|
|
52
|
+
try {
|
|
53
|
+
fields = readFileSync(args.migrateFrom === "-" ? 0 : resolve(cwd, args.migrateFrom), "utf-8");
|
|
54
|
+
} catch (err) {
|
|
55
|
+
return print({
|
|
56
|
+
$schema: BOX_LISTING_WRITE_SCHEMA_ID,
|
|
57
|
+
contract: BOX_LISTING_CONTRACT_VERSION,
|
|
58
|
+
chant: readerVersion(),
|
|
59
|
+
member,
|
|
60
|
+
error: { code: "write-input-invalid", message: `--from ${args.migrateFrom} could not be read: ${err instanceof Error ? err.message : String(err)}` },
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return print(
|
|
65
|
+
boxListingSet({
|
|
66
|
+
cwd,
|
|
67
|
+
member,
|
|
68
|
+
fields,
|
|
69
|
+
cover: args.cover,
|
|
70
|
+
coverPath: args.coverPath,
|
|
71
|
+
by: args.by,
|
|
72
|
+
agent: process.env[AGENT_ENV] || undefined,
|
|
73
|
+
dryRun: args.dryRun === true,
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** `box publish <member> <item>` or `box publish <member> --records`. */
|
|
79
|
+
function runPublish(ctx: CommandContext, member: string | undefined, rest: string[]): number {
|
|
80
|
+
const { args } = ctx;
|
|
81
|
+
const [item, ...extra] = rest;
|
|
82
|
+
const print = (doc: BoxPublishDocument): number => {
|
|
83
|
+
console.log(JSON.stringify(doc, null, 2));
|
|
84
|
+
return "error" in doc ? 1 : 0;
|
|
85
|
+
};
|
|
86
|
+
const usage = (message: string) =>
|
|
87
|
+
print({
|
|
88
|
+
$schema: BOX_PUBLISH_SCHEMA_ID,
|
|
89
|
+
contract: BOX_PUBLISH_CONTRACT_VERSION,
|
|
90
|
+
chant: readerVersion(),
|
|
91
|
+
member: member ?? null,
|
|
92
|
+
action: args.records ? "records" : "item",
|
|
93
|
+
item: item ?? null,
|
|
94
|
+
error: { code: "write-usage-invalid", message: `${message}; ${PUBLISH_USAGE}` },
|
|
95
|
+
});
|
|
96
|
+
if (member === undefined) return usage("box publish needs the member whose box publishes");
|
|
97
|
+
if (extra.length > 0) return usage(`box publish takes one work item, and was also given ${extra.join(" ")}`);
|
|
98
|
+
for (const [flag, v] of [["--from", args.migrateFrom], ["--set", args.set], ["--kind", args.kind], ["--at", args.at], ["--cover", args.cover]] as const) {
|
|
99
|
+
if (v !== undefined) return usage(`box publish takes no ${flag}`);
|
|
100
|
+
}
|
|
101
|
+
return print(
|
|
102
|
+
boxPublish({
|
|
103
|
+
cwd: process.cwd(),
|
|
104
|
+
member,
|
|
105
|
+
item,
|
|
106
|
+
records: args.records === true,
|
|
107
|
+
by: args.by,
|
|
108
|
+
head: args.head,
|
|
109
|
+
dryRun: args.dryRun === true,
|
|
110
|
+
agent: process.env[AGENT_ENV] || undefined,
|
|
111
|
+
}),
|
|
112
|
+
);
|
|
113
|
+
}
|
|
@@ -79,6 +79,7 @@ describe("the factory on a box block (#3146)", () => {
|
|
|
79
79
|
check: { run: "npm run --silent check && npm test --silent", kind: "test" },
|
|
80
80
|
checks: "checks",
|
|
81
81
|
builders: "steward",
|
|
82
|
+
tiers: [],
|
|
82
83
|
publish: { forge: "github", repo: "arugula-salad/studio", base: null, branchPrefix: "box/", head: null },
|
|
83
84
|
pointer: "/members/1/box/factory",
|
|
84
85
|
});
|
|
@@ -223,7 +224,7 @@ describe("protected paths in writeScope (#3146)", () => {
|
|
|
223
224
|
expect(judgePath(d, writer, "chant.workspace.json", { before: () => before, after: () => alsoName })).toMatchObject({
|
|
224
225
|
ok: false,
|
|
225
226
|
code: "write-scope-protected",
|
|
226
|
-
message: expect.stringContaining("outside
|
|
227
|
+
message: expect.stringContaining("outside what its except allows (diagrams)"),
|
|
227
228
|
});
|
|
228
229
|
// Without the change in hand, an except entry can't be satisfied.
|
|
229
230
|
expect(judgePath(d, writer, "chant.workspace.json")).toMatchObject({ ok: false, code: "write-scope-protected" });
|
|
@@ -262,6 +263,8 @@ describe("the read contract: status, graph, agent and check --changes (#3146)",
|
|
|
262
263
|
check: { run: "npm run --silent check && npm test --silent", kind: "test" },
|
|
263
264
|
checks: "checks",
|
|
264
265
|
builders: "steward",
|
|
266
|
+
tiers: [],
|
|
267
|
+
builderFor: { studio: {} },
|
|
265
268
|
publish: { forge: "github", repo: "arugula-salad/studio", base: null, branchPrefix: "box/", head: null },
|
|
266
269
|
});
|
|
267
270
|
expect(box.listing).toEqual({
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
-
import type { BoxFactory, BoxListing, Declaration, FactoryCheckKind } from "./declaration";
|
|
14
|
+
import type { BoxFactory, BoxListing, Declaration, FactoryCheckKind, FactoryTier } from "./declaration";
|
|
15
15
|
import type { ReasonCode } from "./reason-codes";
|
|
16
16
|
import type { WorkspaceTree } from "./tree";
|
|
17
17
|
|
|
@@ -36,6 +36,15 @@ export interface FactoryView {
|
|
|
36
36
|
check: { run: string; kind: FactoryCheckKind } | null;
|
|
37
37
|
checks: string | null;
|
|
38
38
|
builders: string | null;
|
|
39
|
+
/** Which builder agent builds at which tier, as declared (#3152, ws-094); empty when none is. */
|
|
40
|
+
tiers: FactoryTier[];
|
|
41
|
+
/**
|
|
42
|
+
* The builder for each member the factory builds, by tier (#3152): the
|
|
43
|
+
* entry for the tier that names the member's kind, else the tier's entry
|
|
44
|
+
* with no kinds. A tier with no entry for the member is left out, so a
|
|
45
|
+
* reader resolves a work item's tier with one lookup, without studio.
|
|
46
|
+
*/
|
|
47
|
+
builderFor: Record<string, Record<string, { agent: string; session: string | null }>>;
|
|
39
48
|
publish: { forge: "github"; repo: string; base: string | null; branchPrefix: string; head: string | null } | null;
|
|
40
49
|
}
|
|
41
50
|
|
|
@@ -62,17 +71,37 @@ export function plantability(declaration: Declaration): Plantable {
|
|
|
62
71
|
};
|
|
63
72
|
}
|
|
64
73
|
|
|
65
|
-
|
|
74
|
+
/** A box's factory as the read contract prints it. `members` gives each built member's kind, for `builderFor`. */
|
|
75
|
+
export function factoryView(f: BoxFactory | null, members: readonly { name: string; kind: string }[] = []): FactoryView | null {
|
|
66
76
|
if (f === null) return null;
|
|
67
77
|
return {
|
|
68
78
|
builds: [...f.builds],
|
|
69
79
|
check: f.check === null ? null : { ...f.check },
|
|
70
80
|
checks: f.checks,
|
|
71
81
|
builders: f.builders,
|
|
82
|
+
tiers: f.tiers.map((t) => ({ ...t, kinds: t.kinds === null ? null : [...t.kinds] })),
|
|
83
|
+
builderFor: builderTable(f, members),
|
|
72
84
|
publish: f.publish === null ? null : { ...f.publish },
|
|
73
85
|
};
|
|
74
86
|
}
|
|
75
87
|
|
|
88
|
+
/** The builder table (#3152): for each built member, each tier's agent, by the member's kind first and the tier's default second. */
|
|
89
|
+
export function builderTable(f: BoxFactory, members: readonly { name: string; kind: string }[]): FactoryView["builderFor"] {
|
|
90
|
+
const tiers = [...new Set(f.tiers.map((t) => t.tier))];
|
|
91
|
+
const out: FactoryView["builderFor"] = {};
|
|
92
|
+
for (const name of f.builds) {
|
|
93
|
+
const kind = members.find((m) => m.name === name)?.kind;
|
|
94
|
+
const row: Record<string, { agent: string; session: string | null }> = {};
|
|
95
|
+
for (const tier of tiers) {
|
|
96
|
+
const entry =
|
|
97
|
+
f.tiers.find((t) => t.tier === tier && kind !== undefined && t.kinds?.includes(kind)) ?? f.tiers.find((t) => t.tier === tier && t.kinds === null);
|
|
98
|
+
if (entry) row[tier] = { agent: entry.agent, session: entry.session };
|
|
99
|
+
}
|
|
100
|
+
out[name] = row;
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
76
105
|
/** The listing, hashing the cover through `read` (bytes, or null when the file can't be read). */
|
|
77
106
|
export function listingView(l: BoxListing | null, read: (path: string) => Uint8Array | null): ListingView | null {
|
|
78
107
|
if (l === null) return null;
|
|
@@ -97,7 +126,11 @@ export function treeBytes(tree: WorkspaceTree): (path: string) => Uint8Array | n
|
|
|
97
126
|
}
|
|
98
127
|
|
|
99
128
|
/** The box fields the graph prints on a member (#3146): its factory and listing, or null when the member's box declares neither. */
|
|
100
|
-
export function graphBox(
|
|
129
|
+
export function graphBox(
|
|
130
|
+
box: { factory: BoxFactory | null; listing: BoxListing | null } | null,
|
|
131
|
+
tree: WorkspaceTree,
|
|
132
|
+
members: readonly { name: string; kind: string }[] = [],
|
|
133
|
+
): { factory: FactoryView | null; listing: ListingView | null } | null {
|
|
101
134
|
if (box === null || (box.factory === null && box.listing === null)) return null;
|
|
102
|
-
return { factory: factoryView(box.factory), listing: listingView(box.listing, treeBytes(tree)) };
|
|
135
|
+
return { factory: factoryView(box.factory, members), listing: listingView(box.listing, treeBytes(tree)) };
|
|
103
136
|
}
|