@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
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://intentius.io/chant/schemas/workspace/work-lease/v1/work-lease.schema.json",
|
|
4
|
+
"title": "chant workspace work output",
|
|
5
|
+
"description": "What `chant workspace work claim|renew|release <id> --holder <name> --json` prints (#2732, ws-055): the work lease it wrote, the reason it was refused, or the reason the command could not run. Version 1 of the write contract for work leases. The lease is the ref refs/chant/lease/[_members/<member>/]work/<id>; each write also appends to _leases/<id>.jsonl on chant/lifecycle. Readers ignore fields they do not know; a field is only ever added within a version. The refusal and error codes are closed lists, each in the one closed list of `reason-codes.ts`.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{
|
|
8
|
+
"$ref": "#/$defs/result"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"$ref": "#/$defs/refusal"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"$ref": "#/$defs/failure"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"$defs": {
|
|
18
|
+
"lease": {
|
|
19
|
+
"type": "object",
|
|
20
|
+
"required": [
|
|
21
|
+
"holder",
|
|
22
|
+
"token",
|
|
23
|
+
"acquiredAt",
|
|
24
|
+
"expiresAt"
|
|
25
|
+
],
|
|
26
|
+
"properties": {
|
|
27
|
+
"holder": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Who holds the lease, as --holder gave it."
|
|
30
|
+
},
|
|
31
|
+
"token": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The fencing token: new on every claim, kept by every renew."
|
|
34
|
+
},
|
|
35
|
+
"acquiredAt": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "When the lease was claimed, ISO 8601."
|
|
38
|
+
},
|
|
39
|
+
"expiresAt": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "When the lease expires unless renewed, ISO 8601."
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"result": {
|
|
46
|
+
"description": "The lease was written. Exit code 0.",
|
|
47
|
+
"type": "object",
|
|
48
|
+
"required": [
|
|
49
|
+
"$schema",
|
|
50
|
+
"contract",
|
|
51
|
+
"item",
|
|
52
|
+
"event",
|
|
53
|
+
"kind",
|
|
54
|
+
"ref",
|
|
55
|
+
"lease",
|
|
56
|
+
"history"
|
|
57
|
+
],
|
|
58
|
+
"properties": {
|
|
59
|
+
"$schema": {
|
|
60
|
+
"const": "https://intentius.io/chant/schemas/workspace/work-lease/v1/work-lease.schema.json"
|
|
61
|
+
},
|
|
62
|
+
"contract": {
|
|
63
|
+
"const": 1
|
|
64
|
+
},
|
|
65
|
+
"item": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"description": "The work item's id."
|
|
68
|
+
},
|
|
69
|
+
"event": {
|
|
70
|
+
"enum": [
|
|
71
|
+
"claim",
|
|
72
|
+
"renew",
|
|
73
|
+
"release"
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"kind": {
|
|
77
|
+
"type": "string",
|
|
78
|
+
"description": "The work kind file, relative to the repository root, with / separators."
|
|
79
|
+
},
|
|
80
|
+
"ref": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"description": "The lease ref, in the ledger of the member owning the work kind."
|
|
83
|
+
},
|
|
84
|
+
"lease": {
|
|
85
|
+
"description": "The lease after a claim or renew; the lease that was given back after a release.",
|
|
86
|
+
"$ref": "#/$defs/lease"
|
|
87
|
+
},
|
|
88
|
+
"history": {
|
|
89
|
+
"type": "object",
|
|
90
|
+
"required": [
|
|
91
|
+
"path",
|
|
92
|
+
"commit",
|
|
93
|
+
"pushed"
|
|
94
|
+
],
|
|
95
|
+
"properties": {
|
|
96
|
+
"path": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"description": "The history file on chant/lifecycle, _leases/<id>.jsonl under the member's prefix."
|
|
99
|
+
},
|
|
100
|
+
"commit": {
|
|
101
|
+
"type": "string",
|
|
102
|
+
"description": "The chant/lifecycle commit that appended the line."
|
|
103
|
+
},
|
|
104
|
+
"pushed": {
|
|
105
|
+
"type": "boolean",
|
|
106
|
+
"description": "Whether chant/lifecycle reached the remote. False with no remote, and when the remote refused it; the lease stands either way."
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"refused": false,
|
|
111
|
+
"error": false
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"refusal": {
|
|
115
|
+
"description": "The lease was refused. Exit code 2. Nothing was written.",
|
|
116
|
+
"type": "object",
|
|
117
|
+
"required": [
|
|
118
|
+
"$schema",
|
|
119
|
+
"contract",
|
|
120
|
+
"item",
|
|
121
|
+
"event",
|
|
122
|
+
"kind",
|
|
123
|
+
"ref",
|
|
124
|
+
"refused"
|
|
125
|
+
],
|
|
126
|
+
"properties": {
|
|
127
|
+
"$schema": {
|
|
128
|
+
"const": "https://intentius.io/chant/schemas/workspace/work-lease/v1/work-lease.schema.json"
|
|
129
|
+
},
|
|
130
|
+
"contract": {
|
|
131
|
+
"const": 1
|
|
132
|
+
},
|
|
133
|
+
"item": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "The work item's id."
|
|
136
|
+
},
|
|
137
|
+
"event": {
|
|
138
|
+
"enum": [
|
|
139
|
+
"claim",
|
|
140
|
+
"renew",
|
|
141
|
+
"release"
|
|
142
|
+
]
|
|
143
|
+
},
|
|
144
|
+
"kind": {
|
|
145
|
+
"type": "string",
|
|
146
|
+
"description": "The work kind file, relative to the repository root, with / separators."
|
|
147
|
+
},
|
|
148
|
+
"ref": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"description": "The lease ref, in the ledger of the member owning the work kind."
|
|
151
|
+
},
|
|
152
|
+
"refused": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"required": [
|
|
155
|
+
"code",
|
|
156
|
+
"message",
|
|
157
|
+
"heldBy"
|
|
158
|
+
],
|
|
159
|
+
"properties": {
|
|
160
|
+
"code": {
|
|
161
|
+
"enum": [
|
|
162
|
+
"lease-held",
|
|
163
|
+
"lease-not-held",
|
|
164
|
+
"lease-token-mismatch",
|
|
165
|
+
"lease-race",
|
|
166
|
+
"lease-push-rejected"
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"message": {
|
|
170
|
+
"type": "string",
|
|
171
|
+
"description": "Names the holder when someone holds the lease."
|
|
172
|
+
},
|
|
173
|
+
"heldBy": {
|
|
174
|
+
"description": "The live lease, when there is one.",
|
|
175
|
+
"oneOf": [
|
|
176
|
+
{
|
|
177
|
+
"type": "null"
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"$ref": "#/$defs/lease"
|
|
181
|
+
}
|
|
182
|
+
]
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"lease": false,
|
|
187
|
+
"error": false
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"failure": {
|
|
191
|
+
"description": "The command could not run. Exit code 1. Nothing was written.",
|
|
192
|
+
"type": "object",
|
|
193
|
+
"required": [
|
|
194
|
+
"$schema",
|
|
195
|
+
"contract",
|
|
196
|
+
"error"
|
|
197
|
+
],
|
|
198
|
+
"properties": {
|
|
199
|
+
"$schema": {
|
|
200
|
+
"const": "https://intentius.io/chant/schemas/workspace/work-lease/v1/work-lease.schema.json"
|
|
201
|
+
},
|
|
202
|
+
"contract": {
|
|
203
|
+
"const": 1
|
|
204
|
+
},
|
|
205
|
+
"error": {
|
|
206
|
+
"type": "object",
|
|
207
|
+
"required": [
|
|
208
|
+
"code",
|
|
209
|
+
"message"
|
|
210
|
+
],
|
|
211
|
+
"properties": {
|
|
212
|
+
"code": {
|
|
213
|
+
"enum": [
|
|
214
|
+
"write-usage-invalid",
|
|
215
|
+
"kind-unreadable",
|
|
216
|
+
"work-kind-missing",
|
|
217
|
+
"work-kind-ambiguous",
|
|
218
|
+
"work-item-unknown",
|
|
219
|
+
"work-item-closed",
|
|
220
|
+
"not-a-git-repository"
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
"message": {
|
|
224
|
+
"type": "string"
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
},
|
|
228
|
+
"lease": false,
|
|
229
|
+
"refused": false
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The work kind's `ready`/`blockedBy` against chud's ready queue (ws-055,
|
|
3
|
+
* #2734).
|
|
4
|
+
*
|
|
5
|
+
* chud's `readyQueue` (packages/runtime/src/ready.mjs at jhgaylor/chud
|
|
6
|
+
* 43afcf1) puts a contract in the ready set once it is approved and in
|
|
7
|
+
* force, every `depends_on` id is `met` (drivers.mjs's `memberState`), and no
|
|
8
|
+
* unit is open against it; a contract that is itself met leaves the queue.
|
|
9
|
+
*
|
|
10
|
+
* Of those rules, the work kind's `ready` and `blockedBy` (work.ts) can
|
|
11
|
+
* express two directly, with no new field:
|
|
12
|
+
*
|
|
13
|
+
* - dependencies closed: a work item's `needs` id counts as closed once its
|
|
14
|
+
* `state` is the kind's `done` state, same as `depends_on` counts a
|
|
15
|
+
* dependency closed once it is `met`. A `needs` id that never reaches
|
|
16
|
+
* `done` (here, one that is `dropped`) blocks forever, the same way a
|
|
17
|
+
* retired contract does.
|
|
18
|
+
* - a record that is itself already `done` (or `dropped`, or merely
|
|
19
|
+
* `in-progress`) is not `ready`: `ready` requires `state === "open"`, the
|
|
20
|
+
* same "already met/already running leaves the queue" rule ready.mjs
|
|
21
|
+
* states for a contract that is itself met.
|
|
22
|
+
*
|
|
23
|
+
* The rules ready.mjs adds on top -- approval before a dependency counts
|
|
24
|
+
* (memberState's draft/failing cases), re-checking that a `met` dependency's
|
|
25
|
+
* newest evidence still passes, and refusing a contract while a unit is open
|
|
26
|
+
* against it -- have no equivalent in a `needs` link. The full table of
|
|
27
|
+
* every ready.mjs rule and its home (a `needs` link, the runner, or "not
|
|
28
|
+
* applicable") is on INTENTIUS/chant#2734.
|
|
29
|
+
*
|
|
30
|
+
* This fixture mirrors a small chud contract graph as work items and checks
|
|
31
|
+
* `queryRecords`'s `ready`/`blockedBy` against a minimal, local
|
|
32
|
+
* re-implementation of the expressible subset of ready.mjs's rule (met
|
|
33
|
+
* depends_on, self not already met), so a regression in either the work
|
|
34
|
+
* kind's readiness formula or this reading of ready.mjs's rule shows up as a
|
|
35
|
+
* mismatch.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { readFileSync } from "node:fs";
|
|
39
|
+
import { join } from "node:path";
|
|
40
|
+
import { afterAll, beforeAll, describe, expect, test } from "vitest";
|
|
41
|
+
import { cleanScratch, REPO, repo } from "./__fixtures__/contract-repo";
|
|
42
|
+
import { queryRecords } from "./records-cli";
|
|
43
|
+
|
|
44
|
+
const REF = join(REPO, "reference-workspace");
|
|
45
|
+
const WORK = "work/work.kind.mjs";
|
|
46
|
+
|
|
47
|
+
function work(id: string, fields: Record<string, unknown>): string {
|
|
48
|
+
const data = {
|
|
49
|
+
schema: 1,
|
|
50
|
+
id,
|
|
51
|
+
title: `Work ${id}`,
|
|
52
|
+
state: "open",
|
|
53
|
+
implements: [],
|
|
54
|
+
needs: [],
|
|
55
|
+
constrains: ["member:app"],
|
|
56
|
+
evidence: [],
|
|
57
|
+
opened_on: "2026-09-24",
|
|
58
|
+
source: { kind: "workspace", member: "app" },
|
|
59
|
+
supersedes: [],
|
|
60
|
+
...fields,
|
|
61
|
+
};
|
|
62
|
+
return `---\n${JSON.stringify(data, null, 2)}\n---\n\n# ${id}\n`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The expressible subset of ready.mjs's rule, on a graph shaped like
|
|
67
|
+
* `depends_on`: ready once the item is `open` (not already met, not already
|
|
68
|
+
* running) and every dependency is `done` (chud's "met"). A dependency that
|
|
69
|
+
* is `dropped` never becomes done, so it blocks forever -- the same effect
|
|
70
|
+
* ready.mjs gets from a retired dependency.
|
|
71
|
+
*/
|
|
72
|
+
function chudReadyIsh(items: { id: string; state: string; needs: string[] }[]): { ready: Set<string>; blockedBy: Map<string, string[]> } {
|
|
73
|
+
const stateOf = new Map(items.map((i) => [i.id, i.state]));
|
|
74
|
+
const ready = new Set<string>();
|
|
75
|
+
const blockedBy = new Map<string, string[]>();
|
|
76
|
+
for (const item of items) {
|
|
77
|
+
const unmet = item.needs.filter((id) => stateOf.get(id) !== "done");
|
|
78
|
+
blockedBy.set(item.id, unmet);
|
|
79
|
+
if (item.state === "open" && unmet.length === 0) ready.add(item.id);
|
|
80
|
+
}
|
|
81
|
+
return { ready, blockedBy };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let root: string;
|
|
85
|
+
|
|
86
|
+
beforeAll(() => {
|
|
87
|
+
root = repo({
|
|
88
|
+
"chant.workspace.json": JSON.stringify({ name: "w", schema: 1, members: [] }, null, 2),
|
|
89
|
+
"decisions/decision.kind.mjs": readFileSync(join(REF, "decisions", "decision.kind.mjs"), "utf-8"),
|
|
90
|
+
"decisions/decision.schema.json": readFileSync(join(REF, "decisions", "decision.schema.json"), "utf-8"),
|
|
91
|
+
"work/work.kind.mjs": readFileSync(join(REF, "work", "work.kind.mjs"), "utf-8"),
|
|
92
|
+
"work/work.schema.json": readFileSync(join(REF, "work", "work.schema.json"), "utf-8"),
|
|
93
|
+
// W-001: already done, so it leaves the ready queue -- ready.mjs's "a
|
|
94
|
+
// contract that is itself met is done, not ready".
|
|
95
|
+
"work/W-001-base.md": work("W-001", { state: "done", closed_on: "2026-09-24", evidence: [{ title: "shipped", url: "https://example.com/w1" }] }),
|
|
96
|
+
// W-002: needs W-001, which is done -- both rules agree this is ready.
|
|
97
|
+
"work/W-002-after-base.md": work("W-002", { needs: ["W-001"] }),
|
|
98
|
+
// W-003: needs W-004, which is dropped and so never turns done -- blocked
|
|
99
|
+
// forever, the same as a retired dependency in ready.mjs.
|
|
100
|
+
"work/W-003-needs-dropped.md": work("W-003", { needs: ["W-004"] }),
|
|
101
|
+
"work/W-004-dropped.md": work("W-004", { state: "dropped", closed_on: "2026-09-24" }),
|
|
102
|
+
// W-005: needs W-002, which is open (not done yet) -- blocked.
|
|
103
|
+
"work/W-005-needs-open.md": work("W-005", { needs: ["W-002"] }),
|
|
104
|
+
// W-006: already in-progress -- not ready, the same "already running"
|
|
105
|
+
// leaves-the-queue rule as a met contract with an open unit does not
|
|
106
|
+
// apply here in the same way, but "not open, so not ready" is shared.
|
|
107
|
+
"work/W-006-in-progress.md": work("W-006", { state: "in-progress" }),
|
|
108
|
+
// W-007: needs W-006, which is in-progress, not done -- blocked.
|
|
109
|
+
"work/W-007-needs-in-progress.md": work("W-007", { needs: ["W-006"] }),
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
afterAll(cleanScratch);
|
|
113
|
+
|
|
114
|
+
describe("work kind ready/blockedBy vs. chud's ready queue, expressible rules (#2734)", () => {
|
|
115
|
+
test("matches on every item, for the rules a needs link can express", async () => {
|
|
116
|
+
const doc = await queryRecords({ kind: WORK, cwd: root });
|
|
117
|
+
if ("error" in doc) throw new Error(doc.error.message);
|
|
118
|
+
expect(doc.summary).toEqual({ total: 7, valid: 7, invalid: 0, superseded: 0 });
|
|
119
|
+
|
|
120
|
+
const items = doc.records.map((r) => ({ id: r.id!, state: r.state!, needs: (r.data as { needs?: string[] }).needs ?? [] }));
|
|
121
|
+
const chud = chudReadyIsh(items);
|
|
122
|
+
|
|
123
|
+
for (const r of doc.records) {
|
|
124
|
+
const id = r.id!;
|
|
125
|
+
expect(r.ready, `${id}.ready`).toBe(chud.ready.has(id));
|
|
126
|
+
expect(
|
|
127
|
+
(r.blockedBy ?? []).map((b) => b.id),
|
|
128
|
+
`${id}.blockedBy`,
|
|
129
|
+
).toEqual(chud.blockedBy.get(id));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("individually: done leaves the queue, a dropped need blocks forever, an in-progress need still blocks", async () => {
|
|
134
|
+
const doc = await queryRecords({ kind: WORK, cwd: root });
|
|
135
|
+
if ("error" in doc) throw new Error(doc.error.message);
|
|
136
|
+
const by = Object.fromEntries(doc.records.map((r) => [r.id, r]));
|
|
137
|
+
|
|
138
|
+
expect(by["W-001"]).toMatchObject({ ready: false, blockedBy: [] }); // done: no longer ready
|
|
139
|
+
expect(by["W-002"]).toMatchObject({ ready: true, blockedBy: [] }); // needs W-001, which is done
|
|
140
|
+
expect(by["W-003"]).toMatchObject({ ready: false, blockedBy: [{ id: "W-004", state: "dropped" }] }); // needs a dropped item: blocked forever
|
|
141
|
+
expect(by["W-005"]).toMatchObject({ ready: false, blockedBy: [{ id: "W-002", state: "open" }] }); // needs an open (not yet done) item
|
|
142
|
+
expect(by["W-006"]).toMatchObject({ ready: false, blockedBy: [] }); // in-progress: not ready, but not "blocked" either
|
|
143
|
+
expect(by["W-007"]).toMatchObject({ ready: false, blockedBy: [{ id: "W-006", state: "in-progress" }] });
|
|
144
|
+
});
|
|
145
|
+
});
|
|
@@ -32,6 +32,7 @@ import { queryRecords } from "./records-cli";
|
|
|
32
32
|
import { amendRecord, newRecord, resolveWriteKind, reviewRecord } from "./records-write";
|
|
33
33
|
import recordsSchema from "./records.schema.json";
|
|
34
34
|
import { WORK_WARNING_CODES } from "./work";
|
|
35
|
+
import { CHANGES_FINDING_CODES } from "./changes";
|
|
35
36
|
|
|
36
37
|
const REF = join(REPO, "reference-workspace");
|
|
37
38
|
const DECISION = (() => {
|
|
@@ -227,9 +228,9 @@ describe("work records on read (#2683)", () => {
|
|
|
227
228
|
expect("error" in doc && doc.error.code).toBe("kind-invalid");
|
|
228
229
|
});
|
|
229
230
|
|
|
230
|
-
test("the reference work schema's finding codes are the intent graph's", () => {
|
|
231
|
+
test("the reference work schema's finding codes are the intent graph's and the change check's (#2773)", () => {
|
|
231
232
|
const schema = JSON.parse(readFileSync(join(REF, "work", "work.schema.json"), "utf-8")) as { definitions: { sourceGap: { properties: { finding: { anyOf: [{ enum: string[] }] } } } } };
|
|
232
|
-
expect(schema.definitions.sourceGap.properties.finding.anyOf[0].enum).toEqual([...INTENT_FINDING_CODES]);
|
|
233
|
+
expect(schema.definitions.sourceGap.properties.finding.anyOf[0].enum).toEqual([...INTENT_FINDING_CODES, ...CHANGES_FINDING_CODES]);
|
|
233
234
|
});
|
|
234
235
|
});
|
|
235
236
|
|
package/src/workspace/work.ts
CHANGED
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
* record is valid and not superseded;
|
|
16
16
|
* - `blockedBy`: each need that is not done, with its state;
|
|
17
17
|
* - `implements`: each decision it names, with that decision's state;
|
|
18
|
+
* - `acceptance`, for a kind with acceptance criteria (#2772): how many of the
|
|
19
|
+
* record's criteria passing evidence of the expected verification meets;
|
|
18
20
|
*
|
|
19
21
|
* and each decision `implementedBy`, the work records naming it. The
|
|
20
22
|
* warnings are closed codes. `work-done-gap-open` is not raised here: it
|
|
@@ -24,7 +26,7 @@
|
|
|
24
26
|
|
|
25
27
|
import { dirname } from "node:path";
|
|
26
28
|
import type { ReasonCode } from "./reason-codes";
|
|
27
|
-
import { loadRecordKind, readRecords, type LoadedRecordKind, type ReadRecordsOptions, type RecordEntry } from "./records";
|
|
29
|
+
import { loadRecordKind, normalisePrincipal, readRecords, type LoadedRecordKind, type ReadRecordsOptions, type RecordEntry } from "./records";
|
|
28
30
|
|
|
29
31
|
/** Why a work record carries a warning. Closed, like the record warning codes. */
|
|
30
32
|
export const WORK_WARNING_CODES = [
|
|
@@ -42,6 +44,10 @@ export const WORK_WARNING_CODES = [
|
|
|
42
44
|
"work-closed-without-date",
|
|
43
45
|
/** The record is done, and the finding it came from (`source.finding`) still fires on its region. Raised by `graph --intent`, and by `records` through a walk of that region (#2686). */
|
|
44
46
|
"work-done-gap-open",
|
|
47
|
+
/** The record is done, and an acceptance criterion has no passing evidence of the verification it expects (#2772). `check` fails on it (WSP117). */
|
|
48
|
+
"work-acceptance-unmet",
|
|
49
|
+
/** A passing `manual` verdict on a criterion names the record's implementer, so it does not count: a manual verdict comes from someone else (#2772). */
|
|
50
|
+
"work-acceptance-self-verified",
|
|
45
51
|
] as const satisfies readonly ReasonCode[];
|
|
46
52
|
export type WorkWarningCode = (typeof WORK_WARNING_CODES)[number];
|
|
47
53
|
|
|
@@ -61,6 +67,82 @@ export interface DecisionWork {
|
|
|
61
67
|
implementedBy: WorkLink[];
|
|
62
68
|
}
|
|
63
69
|
|
|
70
|
+
/** How a criterion is to be verified (#2772): by a unit, integration or end-to-end test, by the running system, or by a person. */
|
|
71
|
+
export const VERIFICATION_TYPES = ["unit", "integration", "e2e", "runtime", "manual"] as const;
|
|
72
|
+
export type VerificationType = (typeof VERIFICATION_TYPES)[number];
|
|
73
|
+
|
|
74
|
+
/** The results a piece of evidence for a criterion can carry. */
|
|
75
|
+
export const EVIDENCE_RESULTS = ["pass", "fail"] as const;
|
|
76
|
+
export type EvidenceResult = (typeof EVIDENCE_RESULTS)[number];
|
|
77
|
+
|
|
78
|
+
/** One acceptance criterion as a work record states it. */
|
|
79
|
+
export interface Criterion {
|
|
80
|
+
id: string;
|
|
81
|
+
text: string;
|
|
82
|
+
verification: VerificationType;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** One criterion on read: whether passing evidence of its verification meets it. */
|
|
86
|
+
export interface CriterionState {
|
|
87
|
+
id: string;
|
|
88
|
+
verification: VerificationType;
|
|
89
|
+
met: boolean;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** A work record's acceptance criteria on read (#2772): how many are met, of how many. */
|
|
93
|
+
export interface WorkAcceptance {
|
|
94
|
+
met: number;
|
|
95
|
+
total: number;
|
|
96
|
+
criteria: CriterionState[];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** The acceptance criteria a record lists in `field`, leaving out entries without a string id and a known verification (the schema reports those). */
|
|
100
|
+
export function acceptanceCriteria(data: Record<string, unknown> | null, field: string): Criterion[] {
|
|
101
|
+
const v = data?.[field];
|
|
102
|
+
if (!Array.isArray(v)) return [];
|
|
103
|
+
return v.filter(
|
|
104
|
+
(c): c is Criterion =>
|
|
105
|
+
c !== null && typeof c === "object" && typeof c.id === "string" && typeof c.text === "string" && (VERIFICATION_TYPES as readonly string[]).includes(c.verification),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Which of a record's criteria its evidence meets. Evidence meets a criterion
|
|
111
|
+
* when it names the criterion, its result is `pass` and its verification is
|
|
112
|
+
* the criterion's. A `manual` verdict also names who gave it in `by`, and
|
|
113
|
+
* counts only when that is not the record's implementer; one that is lands in
|
|
114
|
+
* `selfVerified`. `acceptance` is null when the record lists no criteria.
|
|
115
|
+
*/
|
|
116
|
+
export function workAcceptance(
|
|
117
|
+
data: Record<string, unknown> | null,
|
|
118
|
+
spec: { field: string; implementer: string },
|
|
119
|
+
pinsField: string,
|
|
120
|
+
): { acceptance: WorkAcceptance | null; selfVerified: string[] } {
|
|
121
|
+
if (!Array.isArray(data?.[spec.field])) return { acceptance: null, selfVerified: [] };
|
|
122
|
+
const criteria = acceptanceCriteria(data, spec.field);
|
|
123
|
+
const implementer = typeof data?.[spec.implementer] === "string" ? normalisePrincipal(data[spec.implementer] as string) : null;
|
|
124
|
+
const evidence = Array.isArray(data?.[pinsField]) ? (data![pinsField] as unknown[]) : [];
|
|
125
|
+
const selfVerified: string[] = [];
|
|
126
|
+
const states = criteria.map((c): CriterionState => {
|
|
127
|
+
let met = false;
|
|
128
|
+
for (const e of evidence) {
|
|
129
|
+
if (e === null || typeof e !== "object") continue;
|
|
130
|
+
const ev = e as Record<string, unknown>;
|
|
131
|
+
if (ev.criterion !== c.id || ev.result !== "pass" || ev.verification !== c.verification) continue;
|
|
132
|
+
if (c.verification === "manual") {
|
|
133
|
+
if (typeof ev.by !== "string") continue;
|
|
134
|
+
if (implementer !== null && normalisePrincipal(ev.by) === implementer) {
|
|
135
|
+
if (!selfVerified.includes(c.id)) selfVerified.push(c.id);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
met = true;
|
|
140
|
+
}
|
|
141
|
+
return { id: c.id, verification: c.verification, met };
|
|
142
|
+
});
|
|
143
|
+
return { acceptance: { met: states.filter((c) => c.met).length, total: states.length, criteria: states }, selfVerified };
|
|
144
|
+
}
|
|
145
|
+
|
|
64
146
|
/** The strings of a front-matter list, or none. */
|
|
65
147
|
export function idList(data: Record<string, unknown> | null, field: string): string[] {
|
|
66
148
|
const v = data?.[field];
|
|
@@ -143,6 +225,23 @@ export async function applyWork(loaded: LoadedRecordKind, entries: RecordEntry[]
|
|
|
143
225
|
if (e.state === work.done && kind.pins && !(Array.isArray(evidence) && evidence.length > 0)) {
|
|
144
226
|
e.warnings.push({ code: "work-done-unpinned", message: `${e.id ?? e.path} is ${work.done} and ${kind.pins.field} is empty or missing: nothing shows the work was done` });
|
|
145
227
|
}
|
|
228
|
+
if (work.acceptance && kind.pins) {
|
|
229
|
+
const { acceptance, selfVerified } = workAcceptance(e.data, work.acceptance, kind.pins.field);
|
|
230
|
+
e.acceptance = acceptance;
|
|
231
|
+
if (selfVerified.length > 0) {
|
|
232
|
+
e.warnings.push({
|
|
233
|
+
code: "work-acceptance-self-verified",
|
|
234
|
+
message: `the manual verdict on ${selfVerified.join(", ")} is by ${String(e.data[work.acceptance.implementer])}, the implementer, so it does not count: a manual verdict comes from someone else`,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
const unmet = acceptance?.criteria.filter((c) => !c.met) ?? [];
|
|
238
|
+
if (e.state === work.done && unmet.length > 0) {
|
|
239
|
+
e.warnings.push({
|
|
240
|
+
code: "work-acceptance-unmet",
|
|
241
|
+
message: `${e.id ?? e.path} is ${work.done}, and no passing evidence meets ${unmet.map((c) => `${c.id} (${c.verification})`).join(", ")}`,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
146
245
|
if (e.state !== null && closed.has(e.state) && typeof e.data[work.closedOn] !== "string") {
|
|
147
246
|
e.warnings.push({ code: "work-closed-without-date", message: `${e.id ?? e.path} is ${e.state} and has no ${work.closedOn}` });
|
|
148
247
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|