@intentius/chant 0.90.0 → 0.93.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/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts +8 -0
- package/dist/cli/handlers/components.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +20 -0
- package/dist/cli/handlers/operator.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/mcp/workspace-tools.d.ts +7 -4
- package/dist/cli/mcp/workspace-tools.d.ts.map +1 -1
- package/dist/cli/plugins.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +42 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -1
- package/dist/components/verbs/vuln-scan.d.ts +72 -0
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -1
- package/dist/config.d.ts +22 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/lexicon-module.d.ts +22 -0
- package/dist/lexicon-module.d.ts.map +1 -1
- package/dist/lifecycle/converge-ledger.d.ts +19 -0
- package/dist/lifecycle/converge-ledger.d.ts.map +1 -1
- package/dist/lifecycle/git.d.ts +40 -5
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/lease.d.ts +72 -16
- package/dist/lifecycle/lease.d.ts.map +1 -1
- package/dist/lifecycle/member-ledger.d.ts +3 -2
- package/dist/lifecycle/member-ledger.d.ts.map +1 -1
- package/dist/lifecycle/plan-ledger.d.ts +114 -0
- package/dist/lifecycle/plan-ledger.d.ts.map +1 -0
- package/dist/lifecycle/run-ledger.d.ts +2 -0
- package/dist/lifecycle/run-ledger.d.ts.map +1 -1
- package/dist/lifecycle/symptoms.d.ts +51 -0
- package/dist/lifecycle/symptoms.d.ts.map +1 -1
- package/dist/lifecycle/work-lease.d.ts +140 -0
- package/dist/lifecycle/work-lease.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +4 -2
- package/dist/lint/rules/index.d.ts.map +1 -1
- package/dist/lint/rules/op/index.d.ts +14 -3
- package/dist/lint/rules/op/index.d.ts.map +1 -1
- package/dist/lint/rules/op/ops014-converge-rule-refusals.d.ts.map +1 -1
- package/dist/lint/rules/op/sys010-key-over-plain-http.d.ts +29 -0
- package/dist/lint/rules/op/sys010-key-over-plain-http.d.ts.map +1 -0
- package/dist/lint/rules/sys001-no-literal-key.d.ts +3 -0
- package/dist/lint/rules/sys001-no-literal-key.d.ts.map +1 -0
- package/dist/op/__fixtures__/decide-stub-backend.d.ts +53 -0
- package/dist/op/__fixtures__/decide-stub-backend.d.ts.map +1 -0
- package/dist/op/activities/activity-contracts.d.ts +203 -2
- package/dist/op/activities/activity-contracts.d.ts.map +1 -1
- package/dist/op/activities/apply.d.ts.map +1 -1
- package/dist/op/activities/change-coverage.d.ts +44 -0
- package/dist/op/activities/change-coverage.d.ts.map +1 -0
- package/dist/op/activities/converge.d.ts +24 -1
- package/dist/op/activities/converge.d.ts.map +1 -1
- package/dist/op/activities/decide.d.ts +107 -0
- package/dist/op/activities/decide.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +10 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/shell.d.ts +32 -0
- package/dist/op/activities/shell.d.ts.map +1 -1
- package/dist/op/activities/source-release.d.ts +86 -0
- package/dist/op/activities/source-release.d.ts.map +1 -0
- package/dist/op/activities/source-rollback.d.ts +100 -0
- package/dist/op/activities/source-rollback.d.ts.map +1 -0
- package/dist/op/activities/work-evidence.d.ts +42 -0
- package/dist/op/activities/work-evidence.d.ts.map +1 -0
- package/dist/op/activity-contract.d.ts +1 -0
- package/dist/op/activity-contract.d.ts.map +1 -1
- package/dist/op/activity-profiles.d.ts +16 -0
- package/dist/op/activity-profiles.d.ts.map +1 -1
- package/dist/op/builders.d.ts +84 -2
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/composites/converge-op.d.ts +31 -4
- package/dist/op/composites/converge-op.d.ts.map +1 -1
- package/dist/op/decide-backend.d.ts +116 -0
- package/dist/op/decide-backend.d.ts.map +1 -0
- package/dist/op/decide-config.d.ts +120 -0
- package/dist/op/decide-config.d.ts.map +1 -0
- package/dist/op/decide-read-inputs.d.ts +40 -0
- package/dist/op/decide-read-inputs.d.ts.map +1 -0
- package/dist/op/discover.d.ts +25 -0
- package/dist/op/discover.d.ts.map +1 -1
- package/dist/op/gate-wait.d.ts +43 -0
- package/dist/op/gate-wait.d.ts.map +1 -0
- package/dist/op/gate.d.ts +5 -2
- package/dist/op/gate.d.ts.map +1 -1
- package/dist/op/index.d.ts +18 -5
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/lifecycle-receipt-store.d.ts +34 -0
- package/dist/op/lifecycle-receipt-store.d.ts.map +1 -0
- package/dist/op/local-executor.d.ts +48 -4
- package/dist/op/local-executor.d.ts.map +1 -1
- package/dist/op/local-output.d.ts +2 -0
- package/dist/op/local-output.d.ts.map +1 -1
- package/dist/op/op-ir.d.ts +12 -1
- package/dist/op/op-ir.d.ts.map +1 -1
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +126 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +46 -5
- package/dist/op/runtime.d.ts.map +1 -1
- package/dist/op/runtimes/local.d.ts.map +1 -1
- package/dist/op/source-archive.d.ts +63 -0
- package/dist/op/source-archive.d.ts.map +1 -0
- package/dist/op/step-output-ref.d.ts +2 -2
- package/dist/op/step-output-ref.d.ts.map +1 -1
- package/dist/op/steward-points.d.ts +136 -0
- package/dist/op/steward-points.d.ts.map +1 -0
- package/dist/op/steward-turn.d.ts +53 -0
- package/dist/op/steward-turn.d.ts.map +1 -0
- package/dist/op/steward.d.ts +161 -0
- package/dist/op/steward.d.ts.map +1 -0
- package/dist/op/types.d.ts +58 -0
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-decl.d.ts +18 -0
- package/dist/op/work-lease-decl.d.ts.map +1 -0
- package/dist/op/work-lease-run.d.ts +173 -0
- package/dist/op/work-lease-run.d.ts.map +1 -0
- package/dist/workspace/__fixtures__/decide-workspace.d.ts +100 -0
- package/dist/workspace/__fixtures__/decide-workspace.d.ts.map +1 -0
- package/dist/workspace/answers-ledger.d.ts +76 -0
- package/dist/workspace/answers-ledger.d.ts.map +1 -0
- package/dist/workspace/box-isolation.d.ts +99 -0
- package/dist/workspace/box-isolation.d.ts.map +1 -0
- package/dist/workspace/changes-cli.d.ts +16 -0
- package/dist/workspace/changes-cli.d.ts.map +1 -0
- package/dist/workspace/changes.d.ts +200 -0
- package/dist/workspace/changes.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.d.ts +59 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-fly-site.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.d.ts +51 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-live-names.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.d.ts +26 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-rollback.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.d.ts +59 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.d.ts +22 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit-ship-policy.d.ts.map +1 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts +71 -0
- package/dist/workspace/chant-migrations/chud-lexicon-exit.d.ts.map +1 -0
- package/dist/workspace/chant-migrations.d.ts +99 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -0
- package/dist/workspace/checks/box-isolation.d.ts +18 -0
- package/dist/workspace/checks/box-isolation.d.ts.map +1 -0
- package/dist/workspace/checks/boxes.d.ts +104 -0
- package/dist/workspace/checks/boxes.d.ts.map +1 -0
- package/dist/workspace/checks/diagrams.d.ts +31 -0
- package/dist/workspace/checks/diagrams.d.ts.map +1 -0
- package/dist/workspace/checks/records.d.ts +8 -0
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +13 -1
- package/dist/workspace/checks.d.ts.map +1 -1
- package/dist/workspace/compose-graph.d.ts +2 -0
- package/dist/workspace/compose-graph.d.ts.map +1 -1
- package/dist/workspace/conformance/index.d.ts.map +1 -1
- package/dist/workspace/decide.d.ts +229 -0
- package/dist/workspace/decide.d.ts.map +1 -0
- package/dist/workspace/decision-points.schema.json +137 -0
- package/dist/workspace/declaration.d.ts +103 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +285 -0
- package/dist/workspace/declared-kinds.d.ts +32 -0
- package/dist/workspace/declared-kinds.d.ts.map +1 -1
- package/dist/workspace/generated-files.d.ts +7 -0
- package/dist/workspace/generated-files.d.ts.map +1 -1
- package/dist/workspace/intent.d.ts.map +1 -1
- package/dist/workspace/lineage-check.d.ts +3 -0
- package/dist/workspace/lineage-check.d.ts.map +1 -1
- package/dist/workspace/lineage-upgrade.d.ts +6 -1
- package/dist/workspace/lineage-upgrade.d.ts.map +1 -1
- package/dist/workspace/ls.d.ts +39 -1
- package/dist/workspace/ls.d.ts.map +1 -1
- package/dist/workspace/point-answer.schema.json +95 -0
- package/dist/workspace/points-cli.d.ts +113 -0
- package/dist/workspace/points-cli.d.ts.map +1 -0
- package/dist/workspace/points.d.ts +320 -0
- package/dist/workspace/points.d.ts.map +1 -0
- package/dist/workspace/reason-codes.d.ts +38 -0
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/record-assets.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +23 -1
- package/dist/workspace/records-cli.d.ts.map +1 -1
- package/dist/workspace/records-close.d.ts +1 -1
- package/dist/workspace/records-close.d.ts.map +1 -1
- package/dist/workspace/records-write.d.ts +11 -5
- package/dist/workspace/records-write.d.ts.map +1 -1
- package/dist/workspace/records.d.ts +42 -6
- package/dist/workspace/records.d.ts.map +1 -1
- package/dist/workspace/status-gates.d.ts +7 -2
- package/dist/workspace/status-gates.d.ts.map +1 -1
- package/dist/workspace/status-stewards.d.ts +179 -0
- package/dist/workspace/status-stewards.d.ts.map +1 -0
- package/dist/workspace/status.d.ts +68 -1
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +189 -0
- package/dist/workspace/work-cli.d.ts.map +1 -0
- package/dist/workspace/work-evidence.d.ts +74 -0
- package/dist/workspace/work-evidence.d.ts.map +1 -0
- package/dist/workspace/work.d.ts +43 -1
- package/dist/workspace/work.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/audit.test.ts +9 -1
- package/src/cli/commands/build.ts +3 -2
- package/src/cli/commands/lint.ts +7 -1
- package/src/cli/handlers/components.test.ts +93 -0
- package/src/cli/handlers/components.ts +44 -3
- package/src/cli/handlers/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +16 -2
- package/src/cli/handlers/operator.ts +141 -4
- package/src/cli/handlers/run.test.ts +173 -2
- package/src/cli/handlers/run.ts +174 -3
- package/src/cli/main.test.ts +50 -0
- package/src/cli/main.ts +97 -2
- package/src/cli/mcp/workspace-tools.test.ts +48 -19
- package/src/cli/mcp/workspace-tools.ts +110 -8
- package/src/cli/plugins.ts +11 -3
- package/src/cli/registry.ts +42 -1
- package/src/codegen/pinned-upgrade.ts +2 -1
- package/src/codegen/release-wiring.test.ts +49 -2
- package/src/components/capability-plugin-loader.ts +3 -3
- package/src/components/cli-support.ts +2 -2
- package/src/components/verbs/vuln-scan.test.ts +124 -1
- package/src/components/verbs/vuln-scan.ts +142 -1
- package/src/config.ts +11 -0
- package/src/lexicon-module.ts +56 -1
- package/src/lexicon-package.test.ts +96 -0
- package/src/lifecycle/converge-ledger.ts +20 -0
- package/src/lifecycle/git.ts +65 -15
- package/src/lifecycle/lease.test.ts +22 -0
- package/src/lifecycle/lease.ts +133 -29
- package/src/lifecycle/member-ledger.ts +3 -2
- package/src/lifecycle/plan-ledger.test.ts +148 -0
- package/src/lifecycle/plan-ledger.ts +158 -0
- package/src/lifecycle/run-ledger.ts +6 -0
- package/src/lifecycle/symptoms.ts +95 -0
- package/src/lifecycle/work-lease.test.ts +236 -0
- package/src/lifecycle/work-lease.ts +426 -0
- package/src/lint/rules/index.ts +6 -2
- package/src/lint/rules/op/index.ts +17 -4
- package/src/lint/rules/op/ops014-converge-rule-refusals.test.ts +23 -1
- package/src/lint/rules/op/ops014-converge-rule-refusals.ts +10 -6
- package/src/lint/rules/op/sys010-key-over-plain-http.test.ts +52 -0
- package/src/lint/rules/op/sys010-key-over-plain-http.ts +100 -0
- package/src/lint/rules/sys001-no-literal-key.test.ts +31 -0
- package/src/lint/rules/sys001-no-literal-key.ts +67 -0
- package/src/op/__fixtures__/decide-stub-backend.ts +143 -0
- package/src/op/activities/activity-contracts.ts +190 -1
- package/src/op/activities/apply.ts +7 -6
- package/src/op/activities/change-coverage.ts +60 -0
- package/src/op/activities/converge.test.ts +55 -1
- package/src/op/activities/converge.ts +208 -29
- package/src/op/activities/decide.live.test.ts +78 -0
- package/src/op/activities/decide.test.ts +300 -0
- package/src/op/activities/decide.ts +267 -0
- package/src/op/activities/index.ts +21 -0
- package/src/op/activities/shell.test.ts +91 -1
- package/src/op/activities/shell.ts +91 -2
- package/src/op/activities/source-release.test.ts +118 -0
- package/src/op/activities/source-release.ts +168 -0
- package/src/op/activities/source-rollback.test.ts +131 -0
- package/src/op/activities/source-rollback.ts +239 -0
- package/src/op/activities/work-evidence.ts +62 -0
- package/src/op/activity-contract-registry.test.ts +2 -0
- package/src/op/activity-contract-registry.ts +2 -2
- package/src/op/activity-contract.test.ts +12 -0
- package/src/op/activity-contract.ts +6 -1
- package/src/op/activity-profiles.ts +29 -0
- package/src/op/activity-registry.test.ts +1 -1
- package/src/op/activity-registry.ts +2 -2
- package/src/op/builders.ts +124 -5
- package/src/op/command-gate.test.ts +192 -0
- package/src/op/composites/converge-op.test.ts +27 -1
- package/src/op/composites/converge-op.ts +69 -15
- package/src/op/decide-backend.test.ts +44 -0
- package/src/op/decide-backend.ts +212 -0
- package/src/op/decide-config.test.ts +52 -0
- package/src/op/decide-config.ts +80 -0
- package/src/op/decide-read-inputs.ts +117 -0
- package/src/op/discover.ts +71 -0
- package/src/op/gate-approval.ts +2 -2
- package/src/op/gate-wait.ts +57 -0
- package/src/op/gate.ts +8 -3
- package/src/op/generate-pipeline.ts +2 -2
- package/src/op/index.ts +34 -6
- package/src/op/lifecycle-receipt-store.test.ts +60 -0
- package/src/op/lifecycle-receipt-store.ts +61 -0
- package/src/op/local-executor.ts +367 -33
- package/src/op/local-output.test.ts +33 -0
- package/src/op/local-output.ts +33 -2
- package/src/op/op-ir.test.ts +10 -0
- package/src/op/op-ir.ts +18 -0
- package/src/op/op-verb-class.ts +1 -0
- package/src/op/operator.ts +360 -10
- package/src/op/runtime.ts +37 -8
- package/src/op/runtimes/local.ts +6 -1
- package/src/op/source-archive.ts +161 -0
- package/src/op/step-output-ref.ts +6 -2
- package/src/op/steward-points.test.ts +313 -0
- package/src/op/steward-points.ts +194 -0
- package/src/op/steward-turn.ts +79 -0
- package/src/op/steward.test.ts +296 -0
- package/src/op/steward.ts +277 -0
- package/src/op/types.ts +60 -0
- package/src/op/work-lease-decl.ts +80 -0
- package/src/op/work-lease-run.test.ts +345 -0
- package/src/op/work-lease-run.ts +395 -0
- package/src/workspace/__fixtures__/decide-workspace.ts +129 -0
- package/src/workspace/__fixtures__/studio-template-84c8b21.json +67 -0
- package/src/workspace/answers-ledger.ts +118 -0
- package/src/workspace/box-isolation.test.ts +261 -0
- package/src/workspace/box-isolation.ts +205 -0
- package/src/workspace/changes-cli.ts +56 -0
- package/src/workspace/changes.schema.json +472 -0
- package/src/workspace/changes.test.ts +315 -0
- package/src/workspace/changes.ts +512 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-fly-site.ts +234 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-live-names.ts +164 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-rollback.ts +223 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-ship-inputs.ts +195 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit-ship-policy.ts +108 -0
- package/src/workspace/chant-migrations/chud-lexicon-exit.ts +882 -0
- package/src/workspace/chant-migrations.e2e.test.ts +1089 -0
- package/src/workspace/chant-migrations.ts +164 -0
- package/src/workspace/check-contract.test.ts +5 -1
- package/src/workspace/check.schema.json +25 -7
- package/src/workspace/checks/box-isolation.ts +68 -0
- package/src/workspace/checks/boxes.test.ts +276 -0
- package/src/workspace/checks/boxes.ts +442 -0
- package/src/workspace/checks/diagrams.test.ts +134 -0
- package/src/workspace/checks/diagrams.ts +108 -0
- package/src/workspace/checks/records.ts +72 -0
- package/src/workspace/checks.test.ts +7 -0
- package/src/workspace/checks.ts +27 -3
- package/src/workspace/compose-graph.ts +2 -0
- package/src/workspace/conformance/__fixture__/decisions/decision.kind.mjs +10 -0
- package/src/workspace/conformance/__fixture__/decisions/decision.schema.json +26 -0
- package/src/workspace/conformance/index.ts +5 -0
- package/src/workspace/decide.test.ts +224 -0
- package/src/workspace/decide.ts +709 -0
- package/src/workspace/decision-points.schema.json +137 -0
- package/src/workspace/declaration.schema.json +285 -0
- package/src/workspace/declaration.test.ts +58 -0
- package/src/workspace/declaration.ts +241 -0
- package/src/workspace/declared-kinds.test.ts +5 -5
- package/src/workspace/declared-kinds.ts +71 -3
- package/src/workspace/generated-files.ts +14 -0
- package/src/workspace/graph-cli.ts +1 -1
- package/src/workspace/graph.schema.json +9 -1
- package/src/workspace/intent.schema.json +12 -3
- package/src/workspace/intent.ts +3 -10
- package/src/workspace/lineage-check.ts +16 -1
- package/src/workspace/lineage-upgrade-cli.ts +1 -1
- package/src/workspace/lineage-upgrade.test.ts +70 -0
- package/src/workspace/lineage-upgrade.ts +81 -10
- package/src/workspace/ls-contract.test.ts +57 -0
- package/src/workspace/ls.schema.json +46 -1
- package/src/workspace/ls.ts +62 -3
- package/src/workspace/point-answer.schema.json +95 -0
- package/src/workspace/points-cli.ts +284 -0
- package/src/workspace/points-write.schema.json +499 -0
- package/src/workspace/points.schema.json +743 -0
- package/src/workspace/points.test.ts +264 -0
- package/src/workspace/points.ts +564 -0
- package/src/workspace/read-contract.test.ts +18 -1
- package/src/workspace/reason-codes.test.ts +24 -1
- package/src/workspace/reason-codes.ts +49 -0
- package/src/workspace/record-assets.test.ts +3 -2
- package/src/workspace/record-assets.ts +4 -1
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-cli.ts +28 -5
- package/src/workspace/records-close.schema.json +2 -0
- package/src/workspace/records-contract.test.ts +3 -2
- package/src/workspace/records-new.schema.json +2 -0
- package/src/workspace/records-review.schema.json +2 -0
- package/src/workspace/records-sessions-write.e2e.test.ts +77 -0
- package/src/workspace/records-sessions-write.test.ts +2 -34
- package/src/workspace/records-write.e2e.test.ts +108 -0
- package/src/workspace/records-write.test.ts +32 -41
- package/src/workspace/records-write.ts +33 -15
- package/src/workspace/records.schema.json +53 -2
- package/src/workspace/records.test.ts +48 -3
- package/src/workspace/records.ts +142 -6
- package/src/workspace/status-contract.test.ts +189 -6
- package/src/workspace/status-gates.ts +9 -4
- package/src/workspace/status-stewards.ts +329 -0
- package/src/workspace/status.schema.json +335 -5
- package/src/workspace/status.ts +142 -6
- package/src/workspace/work-acceptance.test.ts +274 -0
- package/src/workspace/work-cli.e2e.test.ts +180 -0
- package/src/workspace/work-cli.ts +429 -0
- package/src/workspace/work-evidence.schema.json +212 -0
- package/src/workspace/work-evidence.ts +169 -0
- package/src/workspace/work-history.schema.json +349 -0
- package/src/workspace/work-history.test.ts +156 -0
- package/src/workspace/work-lease.schema.json +233 -0
- package/src/workspace/work-readiness-chud.test.ts +145 -0
- package/src/workspace/work.test.ts +3 -2
- package/src/workspace/work.ts +100 -1
- /package/src/cli/{discovery-skip.test.ts → discovery-skip.e2e.test.ts} +0 -0
- /package/src/workspace/conformance/{conformance.test.ts → conformance.e2e.test.ts} +0 -0
- /package/src/workspace/{which-chant.test.ts → which-chant.e2e.test.ts} +0 -0
|
@@ -96,6 +96,17 @@ export interface Suppression {
|
|
|
96
96
|
/** What `checks` may set a declaration check's severity to. */
|
|
97
97
|
export type CheckSeverity = "error" | "warning" | "info" | "off";
|
|
98
98
|
|
|
99
|
+
/** How `chant workspace check --changes` treats its findings (#2773): not at all, as warnings, or as failures. */
|
|
100
|
+
export type ChangeSeverity = "off" | "warn" | "fail";
|
|
101
|
+
|
|
102
|
+
/** The declaration's `changes` block (#2773): the severity of the forward coverage check, and the paths it never reports. */
|
|
103
|
+
export interface ChangesPolicy {
|
|
104
|
+
/** Absent from the declaration, `warn`. */
|
|
105
|
+
severity: ChangeSeverity;
|
|
106
|
+
/** Globs over files, relative to the workspace root, whose changes need no record: lockfiles, generated output. */
|
|
107
|
+
ignore: string[];
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
export interface MemberRole {
|
|
100
111
|
name: string;
|
|
101
112
|
/** Relative to the member's directory, or null for the whole member. */
|
|
@@ -128,6 +139,40 @@ export interface LinkDeclaration {
|
|
|
128
139
|
pointer: string;
|
|
129
140
|
}
|
|
130
141
|
|
|
142
|
+
/** A renderer a diagram is pinned to (#2764): a closed list of tools chant does not run. */
|
|
143
|
+
export const DIAGRAM_TOOLS = ["d2", "mermaid", "graphviz"] as const;
|
|
144
|
+
export type DiagramTool = (typeof DIAGRAM_TOOLS)[number];
|
|
145
|
+
|
|
146
|
+
/** The renderer a diagram's render was made with (#2764). chant never runs it; it is recorded so a reader can. */
|
|
147
|
+
export interface DiagramRenderer {
|
|
148
|
+
tool: DiagramTool;
|
|
149
|
+
/** The exact release the render was made with, such as "0.9.0". */
|
|
150
|
+
version: string;
|
|
151
|
+
/** Passed before the input and output paths, in order. */
|
|
152
|
+
args: string[];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* A diagram artifact the declaration names (#2764): the workspace's own, in
|
|
157
|
+
* the top-level `diagrams`, or a member's, in that member's `diagrams`. A
|
|
158
|
+
* name is given once across the declaration, like a record kind's.
|
|
159
|
+
*/
|
|
160
|
+
export interface DiagramDeclaration {
|
|
161
|
+
name: string;
|
|
162
|
+
title: string;
|
|
163
|
+
/** From the workspace root, with / separators. Null for an SVG with no source. */
|
|
164
|
+
source: string | null;
|
|
165
|
+
/** From the workspace root, with / separators. */
|
|
166
|
+
render: string;
|
|
167
|
+
renderer: DiagramRenderer;
|
|
168
|
+
/** sha256 hex of the source's bytes when the render was last produced, for `chant workspace check`'s drift finding. Null when not recorded, or when source is null: the render is then never checked for drift. */
|
|
169
|
+
sourceHash: string | null;
|
|
170
|
+
/** The member that declares it, or null for the workspace's own. */
|
|
171
|
+
member: string | null;
|
|
172
|
+
/** The entry's JSON Pointer in the file, for messages. */
|
|
173
|
+
pointer: string;
|
|
174
|
+
}
|
|
175
|
+
|
|
131
176
|
/**
|
|
132
177
|
* A record kind the declaration names (#2680): the workspace's own, in the
|
|
133
178
|
* top-level `records`, or a member's, in that member's `records`.
|
|
@@ -145,6 +190,54 @@ export interface RecordKindDeclaration {
|
|
|
145
190
|
pointer: string;
|
|
146
191
|
}
|
|
147
192
|
|
|
193
|
+
/**
|
|
194
|
+
* A capability a box reaches through a broker (#2726): inference, Fountain,
|
|
195
|
+
* a third-party API. The broker is runtime (a lobby, a door, a studio) and
|
|
196
|
+
* holds the credential; the declaration names it and the scope it enforces.
|
|
197
|
+
*/
|
|
198
|
+
export interface BoxCapability {
|
|
199
|
+
name: string;
|
|
200
|
+
/** What brokers it, such as `lobby`, or null when the entry names no broker (WSP122). */
|
|
201
|
+
broker: string | null;
|
|
202
|
+
/** What of the box's own the broker lets it reach, such as `agent`, `vault`, `conversations`, `sandboxes`. */
|
|
203
|
+
scope: string[];
|
|
204
|
+
/** The entry's JSON Pointer in the file, for messages. */
|
|
205
|
+
pointer: string;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** A member that is a box, or a box's declarations (#2726). */
|
|
209
|
+
export interface BoxDeclaration {
|
|
210
|
+
capabilities: BoxCapability[];
|
|
211
|
+
/** The host and slot of the box's isolation, or null when the block declares none (#2727). The values are derived in `boxes.ts`. */
|
|
212
|
+
isolation: BoxIsolationDeclaration | null;
|
|
213
|
+
/** The block's JSON Pointer in the file, for messages. */
|
|
214
|
+
pointer: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** What a box declares about its isolation (#2727): its identity on a host, and the names of what it needs kept apart. */
|
|
218
|
+
export interface BoxIsolationDeclaration {
|
|
219
|
+
host: string;
|
|
220
|
+
slot: number;
|
|
221
|
+
/** Port name to offset in the box's block, in file order. */
|
|
222
|
+
ports: Record<string, number>;
|
|
223
|
+
/** State name to a path relative to the box's state directory, in file order. */
|
|
224
|
+
state: Record<string, string>;
|
|
225
|
+
cookies: string[];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** A host boxes run on (#2727): one machine under one hostname. */
|
|
229
|
+
export interface Host {
|
|
230
|
+
name: string;
|
|
231
|
+
ports: { from: number; to: number; perBox: number };
|
|
232
|
+
/** As written, or null for the default ({@link DEFAULT_STATE_ROOT}). */
|
|
233
|
+
stateRoot: string | null;
|
|
234
|
+
/** The entry's JSON Pointer in the file, for messages. */
|
|
235
|
+
pointer: string;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** Where boxes keep state when their host names no stateRoot (#2727). */
|
|
239
|
+
export const DEFAULT_STATE_ROOT = "${XDG_STATE_HOME}/chant/boxes";
|
|
240
|
+
|
|
148
241
|
export interface Member {
|
|
149
242
|
type: "member";
|
|
150
243
|
name: string;
|
|
@@ -160,6 +253,10 @@ export interface Member {
|
|
|
160
253
|
links: LinkDeclaration[];
|
|
161
254
|
/** The record kinds this member declares, in file order (#2680). */
|
|
162
255
|
records: RecordKindDeclaration[];
|
|
256
|
+
/** The diagram artifacts this member declares, in file order (#2764). */
|
|
257
|
+
diagrams: DiagramDeclaration[];
|
|
258
|
+
/** The member's box block, or null when it declares none (#2726). */
|
|
259
|
+
box: BoxDeclaration | null;
|
|
163
260
|
upstream: string | null;
|
|
164
261
|
because: string | null;
|
|
165
262
|
suppress: Suppression[];
|
|
@@ -202,6 +299,12 @@ export interface Declaration {
|
|
|
202
299
|
quorum: number | null;
|
|
203
300
|
/** The workspace's own record kinds, from the top-level `records`, in file order (#2680). A member's are on the member. */
|
|
204
301
|
records: RecordKindDeclaration[];
|
|
302
|
+
/** The workspace's own diagram artifacts, from the top-level `diagrams`, in file order (#2764). A member's are on the member. */
|
|
303
|
+
diagrams: DiagramDeclaration[];
|
|
304
|
+
/** The hosts boxes run on, in file order (#2727). */
|
|
305
|
+
hosts: Host[];
|
|
306
|
+
/** The forward coverage check's policy (#2773), or null when the declaration has no `changes` block. */
|
|
307
|
+
changes: ChangesPolicy | null;
|
|
205
308
|
/** The file, relative to the workspace root's tree (`chant.workspace.json` or `.jsonc`). */
|
|
206
309
|
file: string;
|
|
207
310
|
}
|
|
@@ -414,6 +517,8 @@ export function parseDeclaration(text: string, file: string, reader: string = re
|
|
|
414
517
|
pointer: `${pointer}/links/${j}`,
|
|
415
518
|
}));
|
|
416
519
|
const records = recordKindsOf(e.records, e.dir as string, e.name as string, `${pointer}/records`);
|
|
520
|
+
const diagrams = diagramsOf(e.diagrams, e.name as string, `${pointer}/diagrams`);
|
|
521
|
+
const box = boxOf(e.box, `${pointer}/box`);
|
|
417
522
|
return {
|
|
418
523
|
type: "member",
|
|
419
524
|
name: e.name as string,
|
|
@@ -424,6 +529,8 @@ export function parseDeclaration(text: string, file: string, reader: string = re
|
|
|
424
529
|
outputs: e.outputs === undefined ? null : [...(e.outputs as string[])],
|
|
425
530
|
links,
|
|
426
531
|
records,
|
|
532
|
+
diagrams,
|
|
533
|
+
box,
|
|
427
534
|
upstream: (e.upstream as string | undefined) ?? null,
|
|
428
535
|
because: (e.because as string | undefined) ?? null,
|
|
429
536
|
suppress,
|
|
@@ -508,6 +615,29 @@ export function parseDeclaration(text: string, file: string, reader: string = re
|
|
|
508
615
|
byName.set(r.name, r);
|
|
509
616
|
}
|
|
510
617
|
|
|
618
|
+
// A box names each capability once (#2726): a broker reads its scope by name.
|
|
619
|
+
for (const m of members) {
|
|
620
|
+
const seen = new Map<string, BoxCapability>();
|
|
621
|
+
for (const c of m.box?.capabilities ?? []) {
|
|
622
|
+
const first = seen.get(c.name);
|
|
623
|
+
if (first) throw new WorkspaceReadError("declaration-invalid", `member ${m.name}'s box lists the capability ${c.name} twice; the first is at ${first.pointer}`, at(`${c.pointer}/name`));
|
|
624
|
+
seen.set(c.name, c);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// A diagram name is given once across the declaration (#2764): a reader keys diagrams by name.
|
|
629
|
+
const ownDiagrams = diagramsOf(obj.diagrams, null, "/diagrams");
|
|
630
|
+
const byDiagramName = new Map<string, DiagramDeclaration>();
|
|
631
|
+
for (const d of [...ownDiagrams, ...members.flatMap((m) => m.diagrams)]) {
|
|
632
|
+
const first = byDiagramName.get(d.name);
|
|
633
|
+
if (first) {
|
|
634
|
+
throw new WorkspaceReadError("declaration-invalid", `the diagram name ${JSON.stringify(d.name)} is already used by the diagram at ${first.pointer}`, at(`${d.pointer}/name`));
|
|
635
|
+
}
|
|
636
|
+
byDiagramName.set(d.name, d);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const hosts = hostsOf(obj, members, at);
|
|
640
|
+
|
|
511
641
|
const pins = ((obj.pins as Record<string, string>[] | undefined) ?? []).map((p) => ({
|
|
512
642
|
package: p.package ?? null,
|
|
513
643
|
version: p.version ?? null,
|
|
@@ -526,10 +656,19 @@ export function parseDeclaration(text: string, file: string, reader: string = re
|
|
|
526
656
|
checks: { ...((obj.checks as Record<string, CheckSeverity> | undefined) ?? {}) },
|
|
527
657
|
quorum: typeof obj.quorum === "number" ? obj.quorum : null,
|
|
528
658
|
records: ownRecords,
|
|
659
|
+
diagrams: ownDiagrams,
|
|
660
|
+
hosts,
|
|
661
|
+
changes: changesOf(obj.changes as Record<string, unknown> | undefined),
|
|
529
662
|
file,
|
|
530
663
|
};
|
|
531
664
|
}
|
|
532
665
|
|
|
666
|
+
/** The `changes` block, already validated, with its defaults (#2773). */
|
|
667
|
+
function changesOf(block: Record<string, unknown> | undefined): ChangesPolicy | null {
|
|
668
|
+
if (block === undefined) return null;
|
|
669
|
+
return { severity: (block.severity as ChangeSeverity | undefined) ?? "warn", ignore: [...((block.ignore as string[] | undefined) ?? [])] };
|
|
670
|
+
}
|
|
671
|
+
|
|
533
672
|
/** The `records` list at `pointer`, already validated, with each kind file's path from the workspace root. */
|
|
534
673
|
function recordKindsOf(raw: unknown, dir: string, member: string | null, pointer: string): RecordKindDeclaration[] {
|
|
535
674
|
return ((raw as { kind: string; name?: string }[] | undefined) ?? []).map((r, i) => ({
|
|
@@ -541,6 +680,99 @@ function recordKindsOf(raw: unknown, dir: string, member: string | null, pointer
|
|
|
541
680
|
}));
|
|
542
681
|
}
|
|
543
682
|
|
|
683
|
+
/**
|
|
684
|
+
* The `diagrams` list at `pointer`, already validated. `source` and `render`
|
|
685
|
+
* are workspace-root-relative as written: unlike a generated file or a
|
|
686
|
+
* record kind, a diagram's files need not sit inside the declaring member's
|
|
687
|
+
* directory, so there is no member-relative form to resolve (#2764).
|
|
688
|
+
*/
|
|
689
|
+
function diagramsOf(raw: unknown, member: string | null, pointer: string): DiagramDeclaration[] {
|
|
690
|
+
return (
|
|
691
|
+
(raw as
|
|
692
|
+
| { name: string; title: string; source?: string | null; render: string; renderer: { tool: DiagramTool; version: string; args?: string[] }; sourceHash?: string | null }[]
|
|
693
|
+
| undefined) ?? []
|
|
694
|
+
).map((d, i) => ({
|
|
695
|
+
name: d.name,
|
|
696
|
+
title: d.title,
|
|
697
|
+
source: d.source ?? null,
|
|
698
|
+
render: d.render,
|
|
699
|
+
renderer: { tool: d.renderer.tool, version: d.renderer.version, args: [...(d.renderer.args ?? [])] },
|
|
700
|
+
sourceHash: d.sourceHash ?? null,
|
|
701
|
+
member,
|
|
702
|
+
pointer: `${pointer}/${i}`,
|
|
703
|
+
}));
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/** The `box` block at `pointer`, already validated, or null when there is none (#2726, #2727). */
|
|
707
|
+
function boxOf(raw: unknown, pointer: string): BoxDeclaration | null {
|
|
708
|
+
if (raw === undefined) return null;
|
|
709
|
+
const b = raw as {
|
|
710
|
+
capabilities?: { name: string; broker?: string; scope?: string[] }[];
|
|
711
|
+
host?: string;
|
|
712
|
+
slot?: number;
|
|
713
|
+
ports?: Record<string, number>;
|
|
714
|
+
state?: Record<string, string>;
|
|
715
|
+
cookies?: string[];
|
|
716
|
+
};
|
|
717
|
+
return {
|
|
718
|
+
capabilities: (b.capabilities ?? []).map((c, i) => ({ name: c.name, broker: c.broker ?? null, scope: [...(c.scope ?? [])], pointer: `${pointer}/capabilities/${i}` })),
|
|
719
|
+
// The schema requires host and slot together, and host for ports, state and cookies.
|
|
720
|
+
isolation:
|
|
721
|
+
b.host === undefined
|
|
722
|
+
? null
|
|
723
|
+
: { host: b.host, slot: b.slot!, ports: { ...(b.ports ?? {}) }, state: { ...(b.state ?? {}) }, cookies: [...(b.cookies ?? [])] },
|
|
724
|
+
pointer,
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* The hosts, already validated, with the rules the schema can't say (#2727):
|
|
730
|
+
* host names are unique, a box's host is declared, its slot's block fits the
|
|
731
|
+
* host's range and its offsets fit the block. What two boxes resolve to is
|
|
732
|
+
* `chant workspace check`'s question (WSP123), not a read error.
|
|
733
|
+
*/
|
|
734
|
+
function hostsOf(obj: Record<string, unknown>, members: Member[], at: (pointer: string, key?: boolean) => ErrorLocation): Host[] {
|
|
735
|
+
const hosts = ((obj.hosts as Record<string, unknown>[] | undefined) ?? []).map((h, i): Host => ({
|
|
736
|
+
name: h.name as string,
|
|
737
|
+
ports: { ...(h.ports as Host["ports"]) },
|
|
738
|
+
stateRoot: (h.stateRoot as string | undefined) ?? null,
|
|
739
|
+
pointer: `/hosts/${i}`,
|
|
740
|
+
}));
|
|
741
|
+
const invalid = (message: string, pointer: string) => new WorkspaceReadError("declaration-invalid", message, at(pointer));
|
|
742
|
+
const byName = new Map<string, Host>();
|
|
743
|
+
for (const h of hosts) {
|
|
744
|
+
const first = byName.get(h.name);
|
|
745
|
+
if (first) throw invalid(`the host name ${JSON.stringify(h.name)} is already used by the host at ${first.pointer}`, `${h.pointer}/name`);
|
|
746
|
+
byName.set(h.name, h);
|
|
747
|
+
if (h.ports.from > h.ports.to) throw invalid(`host ${h.name}'s port range starts at ${h.ports.from}, after its end ${h.ports.to}`, `${h.pointer}/ports`);
|
|
748
|
+
}
|
|
749
|
+
for (const m of members) {
|
|
750
|
+
const iso = m.box?.isolation;
|
|
751
|
+
if (!iso) continue;
|
|
752
|
+
const box = m.box!.pointer;
|
|
753
|
+
const host = byName.get(iso.host);
|
|
754
|
+
if (!host) {
|
|
755
|
+
const known = hosts.map((h) => h.name).join(", ") || "none are declared";
|
|
756
|
+
throw invalid(`member ${m.name}'s box names the host ${JSON.stringify(iso.host)}, which hosts does not declare; declared hosts: ${known}`, `${box}/host`);
|
|
757
|
+
}
|
|
758
|
+
const { from, to, perBox } = host.ports;
|
|
759
|
+
const last = from + (iso.slot + 1) * perBox - 1;
|
|
760
|
+
if (last > to) {
|
|
761
|
+
const slots = Math.floor((to - from + 1) / perBox);
|
|
762
|
+
throw invalid(
|
|
763
|
+
`member ${m.name}'s box has slot ${iso.slot} on host ${host.name}, which needs ports ${from + iso.slot * perBox} to ${last}, past the end of the host's range (${to}); the range holds ${slots} slot${slots === 1 ? "" : "s"}`,
|
|
764
|
+
`${box}/slot`,
|
|
765
|
+
);
|
|
766
|
+
}
|
|
767
|
+
for (const [port, offset] of Object.entries(iso.ports)) {
|
|
768
|
+
if (offset >= perBox) {
|
|
769
|
+
throw invalid(`member ${m.name}'s box gives port ${port} offset ${offset}, and host ${host.name} gives each box ${perBox} port${perBox === 1 ? "" : "s"} (offsets 0 to ${perBox - 1})`, `${box}/ports/${pointerToken(port)}`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return hosts;
|
|
774
|
+
}
|
|
775
|
+
|
|
544
776
|
/**
|
|
545
777
|
* Every record kind the declaration names (#2680), in the order readers use
|
|
546
778
|
* them: the workspace's own first, then each member's, members in file order
|
|
@@ -550,6 +782,15 @@ export function declaredRecordKinds(declaration: Declaration): RecordKindDeclara
|
|
|
550
782
|
return [...declaration.records, ...declaration.members.flatMap((m) => m.records)];
|
|
551
783
|
}
|
|
552
784
|
|
|
785
|
+
/**
|
|
786
|
+
* Every diagram artifact the declaration names (#2764), in the order readers
|
|
787
|
+
* use them: the workspace's own first, then each member's, members in file
|
|
788
|
+
* order and each list in its own order.
|
|
789
|
+
*/
|
|
790
|
+
export function declaredDiagrams(declaration: Declaration): DiagramDeclaration[] {
|
|
791
|
+
return [...declaration.diagrams, ...declaration.members.flatMap((m) => m.diagrams)];
|
|
792
|
+
}
|
|
793
|
+
|
|
553
794
|
/** Whether `path` is `dir` or sits under it. Both are tree-relative; `"."` is the root. */
|
|
554
795
|
export function isInside(path: string, dir: string): boolean {
|
|
555
796
|
if (dir === ".") return true;
|
|
@@ -111,20 +111,20 @@ describe("workspace ls lists the declared kinds (#2680)", () => {
|
|
|
111
111
|
]);
|
|
112
112
|
const doc = result(await listWorkspaceWithKinds({ cwd: root }));
|
|
113
113
|
expect(doc.contract).toBe(1);
|
|
114
|
-
expect(doc.workspace.records).toEqual([{ name: "decision", path: "decisions/decision.kind.mjs", kind: "decision", reason: null }]);
|
|
114
|
+
expect(doc.workspace.records).toEqual([{ name: "decision", path: "decisions/decision.kind.mjs", kind: "decision", reason: null, acceptance: null }]);
|
|
115
115
|
const design = doc.members.find((m) => m.name === "design")!;
|
|
116
116
|
expect(design.readable).toBe(true);
|
|
117
117
|
expect(design.records).toEqual([
|
|
118
|
-
{ name: "notes", path: "design/notes/note.kind.mjs", kind: "note", reason: null },
|
|
119
|
-
{ name: null, path: "design/gone.kind.mjs", kind: null, reason: { code: "kind-unreadable", message: "kind file design/gone.kind.mjs does not exist" } },
|
|
120
|
-
{ name: null, path: "design/broken.kind.mjs", kind: null, reason: { code: "kind-invalid", message: "kind file design/broken.kind.mjs is not a record kind: it has no recordKind export" } },
|
|
118
|
+
{ name: "notes", path: "design/notes/note.kind.mjs", kind: "note", reason: null, acceptance: null },
|
|
119
|
+
{ name: null, path: "design/gone.kind.mjs", kind: null, reason: { code: "kind-unreadable", message: "kind file design/gone.kind.mjs does not exist" }, acceptance: null },
|
|
120
|
+
{ name: null, path: "design/broken.kind.mjs", kind: null, reason: { code: "kind-invalid", message: "kind file design/broken.kind.mjs is not a record kind: it has no recordKind export" }, acceptance: null },
|
|
121
121
|
]);
|
|
122
122
|
expect(doc.members.find((m) => m.name === "app")!.records).toEqual([]);
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
test("listWorkspace lists them unloaded, and a workspace that names none lists empty arrays", async () => {
|
|
126
126
|
const doc = result(listWorkspace({ cwd: workspace() }));
|
|
127
|
-
expect(doc.workspace.records).toEqual([{ name: null, path: "decisions/decision.kind.mjs", kind: null, reason: null }]);
|
|
127
|
+
expect(doc.workspace.records).toEqual([{ name: null, path: "decisions/decision.kind.mjs", kind: null, reason: null, acceptance: null }]);
|
|
128
128
|
const none = result(await listWorkspaceWithKinds({ cwd: repo({ "chant.workspace.json": decl(members()), "app/x": "", "design/x": "" }) }));
|
|
129
129
|
expect(none.workspace.records).toEqual([]);
|
|
130
130
|
expect(none.members.map((m) => m.records)).toEqual([[], []]);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* kind file.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import { join } from "node:path";
|
|
16
|
+
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
17
17
|
import { declaredRecordKinds, type Declaration, type RecordKindDeclaration } from "./declaration";
|
|
18
18
|
import type { ReasonCode } from "./reason-codes";
|
|
19
19
|
import type { WorkspaceTree } from "./tree";
|
|
@@ -30,6 +30,31 @@ export interface DeclaredKind {
|
|
|
30
30
|
kind: string | null;
|
|
31
31
|
/** Why it can't be loaded, or null. */
|
|
32
32
|
reason: { code: DeclaredKindReasonCode; message: string } | null;
|
|
33
|
+
/**
|
|
34
|
+
* For an answer kind (ws-058, #2738): its points file from the workspace
|
|
35
|
+
* root, and what is wrong with it, empty when nothing is. Absent for any
|
|
36
|
+
* other kind, and when the kind wasn't loaded.
|
|
37
|
+
*/
|
|
38
|
+
points?: { file: string; problems: { field: string | null; message: string }[] };
|
|
39
|
+
/**
|
|
40
|
+
* For a work kind with acceptance criteria (#2772), read with
|
|
41
|
+
* `acceptance: true`: each current record that lists criteria, with how
|
|
42
|
+
* many are met. Absent for any other kind, when the kind wasn't loaded,
|
|
43
|
+
* and when its records can't be read (`records` reports why).
|
|
44
|
+
*/
|
|
45
|
+
acceptance?: DeclaredAcceptance[];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One work record's acceptance criteria, counted, as `ls`, `status` and `check` read them (#2772). */
|
|
49
|
+
export interface DeclaredAcceptance {
|
|
50
|
+
item: string;
|
|
51
|
+
state: string | null;
|
|
52
|
+
met: number;
|
|
53
|
+
total: number;
|
|
54
|
+
/** The record file on disk, absolute. */
|
|
55
|
+
file: string;
|
|
56
|
+
/** The record's `work-acceptance-unmet` warning, when it is done with a criterion unmet; null otherwise. */
|
|
57
|
+
unmet: string | null;
|
|
33
58
|
}
|
|
34
59
|
|
|
35
60
|
/** The kind file of `declared` on disk, under the workspace root `rootOnDisk`. */
|
|
@@ -47,7 +72,7 @@ export async function loadDeclaredKinds(
|
|
|
47
72
|
declaration: Declaration,
|
|
48
73
|
tree: WorkspaceTree,
|
|
49
74
|
rootOnDisk: string,
|
|
50
|
-
options: { load?: boolean } = {},
|
|
75
|
+
options: { load?: boolean; acceptance?: boolean; at?: string } = {},
|
|
51
76
|
): Promise<DeclaredKind[]> {
|
|
52
77
|
const declared = declaredRecordKinds(declaration);
|
|
53
78
|
if (declared.length === 0) return [];
|
|
@@ -65,7 +90,13 @@ export async function loadDeclaredKinds(
|
|
|
65
90
|
}
|
|
66
91
|
try {
|
|
67
92
|
const loaded = await records.loadRecordKind(file);
|
|
68
|
-
|
|
93
|
+
const entry: DeclaredKind = { declared: d, file, kind: loaded.kind.name, reason: null };
|
|
94
|
+
if (loaded.kind.answers) entry.points = await checkPoints(resolve(dirname(file), loaded.kind.answers.points), rootOnDisk, tree);
|
|
95
|
+
if (loaded.kind.work?.acceptance && options.acceptance) {
|
|
96
|
+
const counted = await countAcceptance(file, rootOnDisk, options.at);
|
|
97
|
+
if (counted) entry.acceptance = counted;
|
|
98
|
+
}
|
|
99
|
+
out.push(entry);
|
|
69
100
|
} catch (err) {
|
|
70
101
|
if (!(err instanceof records.RecordReadError)) throw err;
|
|
71
102
|
// loadRecordKind names the file as it was given, here an absolute path: name it from the workspace root instead.
|
|
@@ -74,3 +105,40 @@ export async function loadDeclaredKinds(
|
|
|
74
105
|
}
|
|
75
106
|
return out;
|
|
76
107
|
}
|
|
108
|
+
|
|
109
|
+
/** The acceptance counts of a work kind's current records, or undefined when they can't be read. */
|
|
110
|
+
async function countAcceptance(file: string, rootOnDisk: string, at: string | undefined): Promise<DeclaredAcceptance[] | undefined> {
|
|
111
|
+
const { readRecordsFor } = await import("./records-cli");
|
|
112
|
+
const { RecordReadError } = await import("./records");
|
|
113
|
+
try {
|
|
114
|
+
const read = await readRecordsFor({ kind: file, cwd: rootOnDisk, current: true, ...(at !== undefined ? { at } : {}) });
|
|
115
|
+
return read.result.records
|
|
116
|
+
.filter((r): r is typeof r & { id: string; acceptance: NonNullable<typeof r.acceptance> } => r.id !== null && r.acceptance != null)
|
|
117
|
+
.map((r) => ({
|
|
118
|
+
item: r.id,
|
|
119
|
+
state: r.state,
|
|
120
|
+
met: r.acceptance.met,
|
|
121
|
+
total: r.acceptance.total,
|
|
122
|
+
file: join(read.root, ...r.path.split("/")),
|
|
123
|
+
unmet: r.warnings.find((w) => w.code === "work-acceptance-unmet")?.message ?? null,
|
|
124
|
+
}));
|
|
125
|
+
} catch (err) {
|
|
126
|
+
if (err instanceof RecordReadError) return undefined;
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** An answer kind's points file (ws-058), read from `tree` and validated as `points ask` reads it. */
|
|
132
|
+
async function checkPoints(abs: string, rootOnDisk: string, tree: WorkspaceTree): Promise<NonNullable<DeclaredKind["points"]>> {
|
|
133
|
+
const file = relative(rootOnDisk, abs).split(sep).join("/");
|
|
134
|
+
if (file.startsWith("../")) return { file, problems: [{ field: null, message: "is outside the workspace" }] };
|
|
135
|
+
if (tree.stat(file) !== "file") return { file, problems: [{ field: null, message: `does not exist${tree.label}` }] };
|
|
136
|
+
const { parsePoints, PointsError } = await import("./points");
|
|
137
|
+
try {
|
|
138
|
+
parsePoints(tree.read(file), file);
|
|
139
|
+
return { file, problems: [] };
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err instanceof PointsError) return { file, problems: err.problems };
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { relative, resolve, sep } from "node:path";
|
|
23
23
|
import { findWorkspaceRoot } from "../project-root";
|
|
24
24
|
import { isInside, readDeclaration, type Declaration } from "./declaration";
|
|
25
|
+
import { memberHolding } from "./record-assets";
|
|
25
26
|
import { workingTree } from "./tree";
|
|
26
27
|
|
|
27
28
|
/** What `chant update` rewrites, registered as implicit generated entries. */
|
|
@@ -79,6 +80,19 @@ export function declaredFilesUnder(declaration: Declaration, dir: string): Map<s
|
|
|
79
80
|
return out;
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Whether a workspace path (from the workspace root) is a generated file:
|
|
85
|
+
* declared in its member's `generated` entries, or one core registers. The
|
|
86
|
+
* `generated` of `graph --intent`'s region and file nodes and of `check
|
|
87
|
+
* --changes`'s paths.
|
|
88
|
+
*/
|
|
89
|
+
export function isGeneratedPath(declaration: Declaration, path: string): boolean {
|
|
90
|
+
const m = declaration.members.find((x) => x.name === memberHolding(path, declaration.members));
|
|
91
|
+
const dir = m?.dir ?? ".";
|
|
92
|
+
const rel = dir === "." ? path : path.slice(dir.length + 1);
|
|
93
|
+
return classifyFile(rel, declaredFilesUnder(declaration, dir)).class === "generated";
|
|
94
|
+
}
|
|
95
|
+
|
|
82
96
|
/**
|
|
83
97
|
* The declared generated files for the absolute directory `absDir`, from the
|
|
84
98
|
* nearest `chant.workspace.json` above it. Empty when there is none, which is
|
|
@@ -220,7 +220,7 @@ export async function workspaceGraph(query: GraphQuery): Promise<GraphResult> {
|
|
|
220
220
|
try {
|
|
221
221
|
const read = await readRecordsFor({ kind: query.kind, cwd: query.cwd, at: query.at });
|
|
222
222
|
const name = read.loaded.kind.name;
|
|
223
|
-
graph.records = read.result.records.map((r) => ({ kind: name, id: r.id, path: r.path, state: r.state, valid: r.valid, supersededBy: r.supersededBy }));
|
|
223
|
+
graph.records = read.result.records.map((r) => ({ kind: name, id: r.id, path: r.path, state: r.state, valid: r.valid, supersededBy: r.supersededBy, remediatedBy: r.remediatedBy }));
|
|
224
224
|
graph.links.push(...recordLinkRows(name, read.result.records, read.loaded.kind.constrains?.field, located.tree, declaration.members));
|
|
225
225
|
} catch (err) {
|
|
226
226
|
if (!(err instanceof RecordReadError)) throw err;
|
|
@@ -752,7 +752,8 @@
|
|
|
752
752
|
"path",
|
|
753
753
|
"state",
|
|
754
754
|
"valid",
|
|
755
|
-
"supersededBy"
|
|
755
|
+
"supersededBy",
|
|
756
|
+
"remediatedBy"
|
|
756
757
|
],
|
|
757
758
|
"properties": {
|
|
758
759
|
"kind": {
|
|
@@ -784,6 +785,13 @@
|
|
|
784
785
|
"string",
|
|
785
786
|
"null"
|
|
786
787
|
]
|
|
788
|
+
},
|
|
789
|
+
"remediatedBy": {
|
|
790
|
+
"description": "Added in contract 1 by #2774. The ids of records whose remediates link names this one.",
|
|
791
|
+
"type": "array",
|
|
792
|
+
"items": {
|
|
793
|
+
"type": "string"
|
|
794
|
+
}
|
|
787
795
|
}
|
|
788
796
|
}
|
|
789
797
|
}
|
|
@@ -600,7 +600,9 @@
|
|
|
600
600
|
"record-schema-invalid",
|
|
601
601
|
"record-id-duplicate",
|
|
602
602
|
"record-supersedes-unknown",
|
|
603
|
-
"record-supersedes-conflict"
|
|
603
|
+
"record-supersedes-conflict",
|
|
604
|
+
"record-remediates-unknown",
|
|
605
|
+
"record-remediates-not-closed"
|
|
604
606
|
]
|
|
605
607
|
},
|
|
606
608
|
"message": {
|
|
@@ -794,7 +796,9 @@
|
|
|
794
796
|
"record-schema-invalid",
|
|
795
797
|
"record-id-duplicate",
|
|
796
798
|
"record-supersedes-unknown",
|
|
797
|
-
"record-supersedes-conflict"
|
|
799
|
+
"record-supersedes-conflict",
|
|
800
|
+
"record-remediates-unknown",
|
|
801
|
+
"record-remediates-not-closed"
|
|
798
802
|
]
|
|
799
803
|
},
|
|
800
804
|
"message": {
|
|
@@ -940,7 +944,12 @@
|
|
|
940
944
|
"work-implements-undecided",
|
|
941
945
|
"work-done-unpinned",
|
|
942
946
|
"work-closed-without-date",
|
|
943
|
-
"work-done-gap-open"
|
|
947
|
+
"work-done-gap-open",
|
|
948
|
+
"work-acceptance-unmet",
|
|
949
|
+
"work-acceptance-self-verified",
|
|
950
|
+
"answer-points-unreadable",
|
|
951
|
+
"answer-point-unknown",
|
|
952
|
+
"answer-point-changed"
|
|
944
953
|
]
|
|
945
954
|
},
|
|
946
955
|
"message": {
|
package/src/workspace/intent.ts
CHANGED
|
@@ -38,7 +38,7 @@ import { realpathSync } from "node:fs";
|
|
|
38
38
|
import { basename, dirname, relative, resolve, sep } from "node:path";
|
|
39
39
|
import { declaredRecordKinds, readDeclaration, readerVersion, resolveGroups, WORKSPACE_ERROR_CODES, WorkspaceReadError, type Declaration } from "./declaration";
|
|
40
40
|
import { declaredKindFile } from "./declared-kinds";
|
|
41
|
-
import {
|
|
41
|
+
import { isGeneratedPath } from "./generated-files";
|
|
42
42
|
import { entityDecisions, hasTrailer, readCommitJoins, runCommitJoins, type CommitJoins, type IntentCommit, type JoinedEntity, type PluginFinding } from "./intent-joins";
|
|
43
43
|
import { loadKindRegistry } from "./kinds";
|
|
44
44
|
import { resolveLinks, type LinkTableRow } from "./links";
|
|
@@ -541,13 +541,6 @@ function filesUnder(tree: WorkspaceTree, dir: string): string[] {
|
|
|
541
541
|
return out;
|
|
542
542
|
}
|
|
543
543
|
|
|
544
|
-
function isGenerated(declaration: Declaration, path: string): boolean {
|
|
545
|
-
const m = declaration.members.find((x) => x.name === memberHolding(path, declaration.members));
|
|
546
|
-
const dir = m?.dir ?? ".";
|
|
547
|
-
const rel = dir === "." ? path : path.slice(dir.length + 1);
|
|
548
|
-
return classifyFile(rel, declaredFilesUnder(declaration, dir)).class === "generated";
|
|
549
|
-
}
|
|
550
|
-
|
|
551
544
|
function stringOr(v: unknown): string | null {
|
|
552
545
|
return typeof v === "string" ? v : null;
|
|
553
546
|
}
|
|
@@ -707,9 +700,9 @@ async function walk(query: IntentQuery, head: Head): Promise<IntentResult> {
|
|
|
707
700
|
// The region, its files and its member.
|
|
708
701
|
const member = region.path === "." ? (declaration.members.find((m) => m.dir === ".")?.name ?? null) : memberHolding(region.path, declaration.members);
|
|
709
702
|
const rid = regionId(region.path, region.lines);
|
|
710
|
-
add<RegionNode>({ id: rid, kind: "region", path: region.path, lines: region.lines, member, at: located.at, type, generated: type === "file" ?
|
|
703
|
+
add<RegionNode>({ id: rid, kind: "region", path: region.path, lines: region.lines, member, at: located.at, type, generated: type === "file" ? isGeneratedPath(declaration, region.path) : null, node: region.node });
|
|
711
704
|
const files = type === "dir" ? filesUnder(located.tree, region.path) : [];
|
|
712
|
-
for (const f of files) add<FileNode>({ id: `file:${f}`, kind: "file", path: f, member: memberHolding(f, declaration.members), generated:
|
|
705
|
+
for (const f of files) add<FileNode>({ id: `file:${f}`, kind: "file", path: f, member: memberHolding(f, declaration.members), generated: isGeneratedPath(declaration, f) });
|
|
713
706
|
const memberNode = (name: string) => {
|
|
714
707
|
const m = declaration.members.find((x) => x.name === name);
|
|
715
708
|
return add<MemberNode>({ id: `member:${name}`, kind: "member", name, dir: m?.dir ?? "", memberKind: m?.kind ?? "" });
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
*
|
|
17
17
|
* `chant workspace upgrade` runs the same checks in its staging worktree
|
|
18
18
|
* before it reaches its gate.
|
|
19
|
+
*
|
|
20
|
+
* `--changes <base>..<head>` runs the forward coverage check instead
|
|
21
|
+
* (`changes-cli.ts`, #2773), which prints a document of its own.
|
|
19
22
|
*/
|
|
20
23
|
|
|
21
24
|
import { realpathSync } from "node:fs";
|
|
@@ -84,7 +87,8 @@ export function findingKey(f: CheckFinding): string {
|
|
|
84
87
|
return `${f.code}\0${f.scope ?? ""}\0${f.path ?? ""}`;
|
|
85
88
|
}
|
|
86
89
|
|
|
87
|
-
const USAGE =
|
|
90
|
+
const USAGE =
|
|
91
|
+
"chant workspace check [--at <rev>] [--json] [--format stylish|json|sarif] [--generated] [--kind <kind file>]\n chant workspace check --changes <base>..<head> [--work <id>] [--severity off|warn|fail] [--kind <kind file>...] [--json]";
|
|
88
92
|
const FORMATS = ["stylish", "json", "sarif"] as const;
|
|
89
93
|
|
|
90
94
|
/** A lock finding as a lint diagnostic, for `--format json` and `--format sarif`. */
|
|
@@ -223,6 +227,17 @@ export async function runWorkspaceCheck(ctx: CommandContext): Promise<number> {
|
|
|
223
227
|
console.error(formatError({ message: `chant workspace check takes no argument (got ${ctx.args.extraPositional})`, hint: USAGE }));
|
|
224
228
|
return 1;
|
|
225
229
|
}
|
|
230
|
+
// The forward coverage check (#2773) is its own document.
|
|
231
|
+
if (ctx.args.changes !== undefined) {
|
|
232
|
+
if (ctx.args.at !== undefined) {
|
|
233
|
+
console.error(formatError({ message: "--changes reads the records at the range's head, so it takes no --at", hint: USAGE }));
|
|
234
|
+
return 1;
|
|
235
|
+
}
|
|
236
|
+
const { handToRootChant } = await import("./which-chant");
|
|
237
|
+
const handed = await handToRootChant(root, undefined);
|
|
238
|
+
if (handed !== undefined) return handed;
|
|
239
|
+
return (await import("./changes-cli")).runWorkspaceChanges(ctx, root);
|
|
240
|
+
}
|
|
226
241
|
const format = (ctx.args.format || "stylish") as (typeof FORMATS)[number];
|
|
227
242
|
if (!FORMATS.includes(format)) {
|
|
228
243
|
console.error(formatError({ message: `--format ${format} is not a check format; use stylish, json or sarif`, hint: USAGE }));
|
|
@@ -71,7 +71,7 @@ export async function upgradeCommand(opts: UpgradeCommandOptions): Promise<Upgra
|
|
|
71
71
|
}),
|
|
72
72
|
);
|
|
73
73
|
}
|
|
74
|
-
if (!staged.changed) {
|
|
74
|
+
if (!staged.changed && staged.checksOk) {
|
|
75
75
|
if (!opts.json) console.error(formatSuccess(`scope "${staged.scope}" is already at ${staged.to ?? "its source"}; nothing to upgrade`));
|
|
76
76
|
return report("up-to-date", 0);
|
|
77
77
|
}
|