@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,345 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #2748 — an Op run under a work item's lease: claimed before the steps it
|
|
3
|
+
* covers, renewed while they run, fenced before the run is recorded, released
|
|
4
|
+
* when it ends, and a lost lease stopping the run. An Op that changes the
|
|
5
|
+
* checkout runs its leased steps in a worktree of its own.
|
|
6
|
+
*/
|
|
7
|
+
import { describe, test, expect } from "vitest";
|
|
8
|
+
import { withTestDir } from "@intentius/chant-test-utils";
|
|
9
|
+
import { spawnSync } from "node:child_process";
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import type { ActivityFn, ActivityProfile } from "./activity-registry";
|
|
13
|
+
import type { OpConfig, StepDefinition } from "./types";
|
|
14
|
+
import { runOpLocally, OpRunFailure, type OpRunResult } from "./local-executor";
|
|
15
|
+
import { workLeaseOutput, workLeaseProblems, stewardWorkHolder, WORK_LEASE_STEP_ID } from "./work-lease-run";
|
|
16
|
+
import { declareSteward } from "./steward";
|
|
17
|
+
import { Op, shell } from "./builders";
|
|
18
|
+
import { shellCmd } from "./activities/shell";
|
|
19
|
+
import { WORK_LEASE_ITEM_PATTERN } from "./work-lease-decl";
|
|
20
|
+
import { runOperatorRound } from "./operator";
|
|
21
|
+
import { stepOutput, validateStepOutputRefScope } from "./step-output-ref";
|
|
22
|
+
import { claimWorkLease, readLeaseHistory, releaseWorkLease, listWorkLeases, WORK_ITEM_ID_PATTERN } from "../lifecycle/work-lease";
|
|
23
|
+
import { readRunLedger } from "../lifecycle/run-ledger";
|
|
24
|
+
|
|
25
|
+
const PROFILES: Record<string, ActivityProfile> = {
|
|
26
|
+
long: { timeout: "30s" },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function git(args: string[], cwd: string): { stdout: string; status: number } {
|
|
30
|
+
const r = spawnSync("git", args, { cwd, encoding: "utf-8" });
|
|
31
|
+
return { stdout: r.stdout ?? "", status: r.status ?? -1 };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function initRepo(dir: string): Promise<void> {
|
|
35
|
+
git(["init", "-q", "-b", "main"], dir);
|
|
36
|
+
git(["config", "user.email", "test@chant.dev"], dir);
|
|
37
|
+
git(["config", "user.name", "Test"], dir);
|
|
38
|
+
git(["config", "commit.gpgsign", "false"], dir);
|
|
39
|
+
mkdirSync(join(dir, "app"), { recursive: true });
|
|
40
|
+
writeFileSync(join(dir, "app", "index.ts"), "export const hello = 1;\n");
|
|
41
|
+
git(["add", "app/index.ts"], dir);
|
|
42
|
+
git(["commit", "-q", "-m", "init"], dir);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const step = (fn: string, args: Record<string, unknown> = {}, extra: Partial<StepDefinition> = {}): StepDefinition =>
|
|
46
|
+
({ kind: "activity", fn, args, ...extra }) as StepDefinition;
|
|
47
|
+
|
|
48
|
+
function dispatchOp(extra: Partial<OpConfig> = {}, steps: StepDefinition[] = [step("build", { lease: workLeaseOutput() })]): OpConfig {
|
|
49
|
+
return { name: "dispatch", overview: "dispatch fixture", phases: [{ name: "Build", steps }], ...extra };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function run(config: OpConfig, activities: Map<string, ActivityFn>, dir: string, work?: { item?: string; holder?: string }): Promise<OpRunResult> {
|
|
53
|
+
try {
|
|
54
|
+
return await runOpLocally(config, activities, PROFILES, undefined, { cwd: dir, ledger: { cwd: dir }, ...(work ? { work } : {}) });
|
|
55
|
+
} catch (err) {
|
|
56
|
+
if (err instanceof OpRunFailure) return err.result;
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const waitFor = async (check: () => boolean | Promise<boolean>, ms = 15_000): Promise<void> => {
|
|
62
|
+
const until = Date.now() + ms;
|
|
63
|
+
while (!(await check())) {
|
|
64
|
+
if (Date.now() > until) throw new Error("timed out");
|
|
65
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
describe("the declaration", () => {
|
|
70
|
+
test("an Op that changes the checkout without a work lease is refused by the executor and by declareSteward", async () => {
|
|
71
|
+
const config = dispatchOp({ changesCheckout: true }, [step("build")]);
|
|
72
|
+
expect(workLeaseProblems(config)[0]).toMatch(/changes the checkout but declares no workLease/);
|
|
73
|
+
await expect(runOpLocally(config, new Map(), PROFILES)).rejects.toThrow(/declares no workLease/);
|
|
74
|
+
expect(() => declareSteward({ name: "box", ops: [config] })).toThrow(/Steward "box": Op "dispatch" changes the checkout/);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("a scheduled steward Op must say which item it claims; a bad item, ttl or reserved id is refused", () => {
|
|
78
|
+
expect(() =>
|
|
79
|
+
declareSteward({ name: "box", ops: [dispatchOp({ workLease: {}, schedule: { cron: "* * * * *" } })] }),
|
|
80
|
+
).toThrow(/scheduled, but its workLease names no item/);
|
|
81
|
+
expect(declareSteward({ name: "box", ops: [dispatchOp({ workLease: {} })] }).ops).toHaveLength(1);
|
|
82
|
+
expect(workLeaseProblems(dispatchOp({ workLease: { item: "../x" } }))[0]).toMatch(/can't key a work lease/);
|
|
83
|
+
expect(workLeaseProblems(dispatchOp({ workLease: { item: "W-1", ttl: "1s" } }))[0]).toMatch(/shorter than 3s/);
|
|
84
|
+
expect(workLeaseProblems(dispatchOp({ workLease: { item: stepOutput("nope", "id") } }))[0]).toMatch(/references step "nope"/);
|
|
85
|
+
expect(workLeaseProblems(dispatchOp({ workLease: { item: "W-1" } }, [step("x", {}, { id: WORK_LEASE_STEP_ID })]))[0]).toMatch(/reserved/);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("Op() refuses a bad declaration at build time, with the lease's own id pattern", () => {
|
|
89
|
+
expect(() => Op(dispatchOp({ changesCheckout: true }, [step("build")]))).toThrow(/declares no workLease/);
|
|
90
|
+
expect(WORK_LEASE_ITEM_PATTERN.source).toBe(WORK_ITEM_ID_PATTERN.source);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("a step may reference the run's work lease; OPS013's scope check accepts it only when the Op declares one", () => {
|
|
94
|
+
const leased = dispatchOp({ workLease: { item: "W-1" } });
|
|
95
|
+
expect(validateStepOutputRefScope(leased)).toEqual([]);
|
|
96
|
+
expect(validateStepOutputRefScope(dispatchOp())[0].message).toMatch(/unknown step id "workLease"/);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("an Op whose lease leaves the item to the run is refused without --work", async () => {
|
|
100
|
+
await expect(runOpLocally(dispatchOp({ workLease: {} }), new Map(), PROFILES)).rejects.toThrow(/--work <id>/);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("a run under a work lease", () => {
|
|
105
|
+
test("claims before its steps, hands them the lease, releases done, and records the item", async () => {
|
|
106
|
+
await withTestDir(async (dir) => {
|
|
107
|
+
await initRepo(dir);
|
|
108
|
+
let seen: Record<string, unknown> | undefined;
|
|
109
|
+
const activities = new Map<string, ActivityFn>([["build", async (args) => { seen = args.lease as Record<string, unknown>; return {}; }]]);
|
|
110
|
+
const result = await run(dispatchOp({ workLease: { item: "W-1" } }), activities, dir, { holder: "box/dispatch@h1" });
|
|
111
|
+
|
|
112
|
+
expect(result.status).toBe("ok");
|
|
113
|
+
expect(seen).toMatchObject({ item: "W-1", holder: "box/dispatch@h1", worktree: null, branch: null, ref: "refs/chant/lease/work/W-1" });
|
|
114
|
+
expect(result.records.map((r) => [r.fn, r.status])).toEqual([["workLease:claim", "ok"], ["build", "ok"]]);
|
|
115
|
+
expect(result.workLease).toMatchObject({ item: "W-1", released: true, outcome: "done", lost: null, token: seen!.token });
|
|
116
|
+
expect(result.record.outcomes).toMatchObject({ WorkItem: "W-1" });
|
|
117
|
+
|
|
118
|
+
const history = (await readLeaseHistory("W-1", { cwd: dir })).records;
|
|
119
|
+
// The claim, the fence's renewal before the run is recorded, and the release.
|
|
120
|
+
expect(history.map((h) => [h.event, h.outcome ?? null])).toEqual([["claim", null], ["renew", null], ["release", "done"]]);
|
|
121
|
+
expect(new Set(history.map((h) => h.token)).size).toBe(1);
|
|
122
|
+
expect(await listWorkLeases({ cwd: dir, memberPrefix: "" })).toEqual([]);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("a second dispatcher is refused by the lease: it claims nothing, runs nothing, and names the holder", async () => {
|
|
127
|
+
await withTestDir(async (dir) => {
|
|
128
|
+
await initRepo(dir);
|
|
129
|
+
const first = await claimWorkLease("W-1", "other-dispatcher", { cwd: dir, ttlMs: 60_000 });
|
|
130
|
+
expect(first.ok).toBe(true);
|
|
131
|
+
const log: string[] = [];
|
|
132
|
+
const activities = new Map<string, ActivityFn>([["build", async () => { log.push("build"); return {}; }]]);
|
|
133
|
+
const result = await run(dispatchOp({ workLease: { item: "W-1" } }), activities, dir);
|
|
134
|
+
|
|
135
|
+
expect(result.status).toBe("ok");
|
|
136
|
+
expect(log).toEqual([]);
|
|
137
|
+
expect(result.records.map((r) => [r.fn, r.status])).toEqual([["workLease:claim", "skipped"], ["build", "skipped"]]);
|
|
138
|
+
expect(result.records[0].refusal).toMatch(/lease-held: W-1 is held by other-dispatcher/);
|
|
139
|
+
expect(result.workLease).toMatchObject({ item: null, released: false, refusal: expect.stringMatching(/other-dispatcher/) });
|
|
140
|
+
expect((await listWorkLeases({ cwd: dir, memberPrefix: "" }))[0]).toMatchObject({ item: "W-1", holder: "other-dispatcher" });
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("candidates are tried in order, and an item picked by an earlier step is claimed right after it", async () => {
|
|
145
|
+
await withTestDir(async (dir) => {
|
|
146
|
+
await initRepo(dir);
|
|
147
|
+
await claimWorkLease("W-1", "other", { cwd: dir, ttlMs: 60_000 });
|
|
148
|
+
const activities = new Map<string, ActivityFn>([
|
|
149
|
+
["pick", async () => ({ ready: ["W-1", "W-2", "W-3"] })],
|
|
150
|
+
["build", async () => ({})],
|
|
151
|
+
]);
|
|
152
|
+
const op = dispatchOp({ workLease: { item: stepOutput("pick", "ready") } }, [
|
|
153
|
+
step("pick", {}, { id: "pick" }),
|
|
154
|
+
step("build", { item: workLeaseOutput("item") }),
|
|
155
|
+
]);
|
|
156
|
+
const result = await run(op, activities, dir);
|
|
157
|
+
expect(result.records.map((r) => r.fn)).toEqual(["pick", "workLease:claim", "build"]);
|
|
158
|
+
expect(result.records[2].args).toEqual({ item: "W-2" });
|
|
159
|
+
expect(result.workLease).toMatchObject({ item: "W-2", released: true });
|
|
160
|
+
|
|
161
|
+
const none = await run(op, new Map<string, ActivityFn>([["pick", async () => ({ ready: [] })], ["build", async () => ({})]]), dir);
|
|
162
|
+
expect(none.status).toBe("ok");
|
|
163
|
+
expect(none.records.map((r) => [r.fn, r.status])).toEqual([["pick", "ok"], ["workLease:claim", "skipped"], ["build", "skipped"]]);
|
|
164
|
+
expect(none.workLease?.refusal).toMatch(/nothing to claim/);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test("a shell pick step that prints JSON hands the lease its candidates, and the next is claimed when the first is held (#2787)", async () => {
|
|
169
|
+
await withTestDir(async (dir) => {
|
|
170
|
+
await initRepo(dir);
|
|
171
|
+
await claimWorkLease("W-4", "other", { cwd: dir, ttlMs: 60_000 });
|
|
172
|
+
const pick = shell(`echo '["W-4","W-5"]'`, { id: "pick", json: true });
|
|
173
|
+
const op = dispatchOp({ workLease: { item: pick.out.json } }, [pick, step("build", { item: workLeaseOutput("item") })]);
|
|
174
|
+
const activities = new Map<string, ActivityFn>([
|
|
175
|
+
["shellCmd", shellCmd as unknown as ActivityFn],
|
|
176
|
+
["build", async () => ({})],
|
|
177
|
+
]);
|
|
178
|
+
const result = await run(op, activities, dir);
|
|
179
|
+
expect(result.status).toBe("ok");
|
|
180
|
+
expect(result.records.map((r) => r.fn)).toEqual(["shellCmd", "workLease:claim", "build"]);
|
|
181
|
+
expect(result.records[2].args).toEqual({ item: "W-5" });
|
|
182
|
+
expect(result.workLease).toMatchObject({ item: "W-5", released: true });
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("--work takes the place of the Op's item, and a failed run releases not_done", async () => {
|
|
187
|
+
await withTestDir(async (dir) => {
|
|
188
|
+
await initRepo(dir);
|
|
189
|
+
const activities = new Map<string, ActivityFn>([["build", async () => { throw new Error("the check fails"); }]]);
|
|
190
|
+
const result = await run(dispatchOp({ workLease: {} }), activities, dir, { item: "W-9" });
|
|
191
|
+
expect(result.status).toBe("fail");
|
|
192
|
+
expect(result.workLease).toMatchObject({ item: "W-9", released: true, outcome: "not_done" });
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("the heartbeat renews the lease while a long step runs, keeping its token", async () => {
|
|
197
|
+
await withTestDir(async (dir) => {
|
|
198
|
+
await initRepo(dir);
|
|
199
|
+
let during: { expiresAt: string; token: string } | undefined;
|
|
200
|
+
const activities = new Map<string, ActivityFn>([
|
|
201
|
+
[
|
|
202
|
+
"build",
|
|
203
|
+
async (args) => {
|
|
204
|
+
const lease = args.lease as { item: string; expiresAt: string; token: string };
|
|
205
|
+
const claimedUntil = lease.expiresAt;
|
|
206
|
+
// Renewed every third of 3s: wait for the ref to move past the claim's expiry.
|
|
207
|
+
await waitFor(async () => {
|
|
208
|
+
const [l] = await listWorkLeases({ cwd: dir, memberPrefix: "" });
|
|
209
|
+
return !!l && l.expiresAt > claimedUntil;
|
|
210
|
+
});
|
|
211
|
+
const [l] = await listWorkLeases({ cwd: dir, memberPrefix: "" });
|
|
212
|
+
during = { expiresAt: l.expiresAt, token: l.token };
|
|
213
|
+
return {};
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
]);
|
|
217
|
+
const result = await run(dispatchOp({ workLease: { item: "W-1", ttl: "3s" } }, [step("build", { lease: workLeaseOutput() }, { profile: "long" } as never)]), activities, dir);
|
|
218
|
+
expect(result.status).toBe("ok");
|
|
219
|
+
expect(during?.token).toBe(result.workLease?.token);
|
|
220
|
+
const events = (await readLeaseHistory("W-1", { cwd: dir })).records.map((h) => h.event);
|
|
221
|
+
expect(events[0]).toBe("claim");
|
|
222
|
+
expect(events).toContain("renew");
|
|
223
|
+
expect(events.at(-1)).toBe("release");
|
|
224
|
+
});
|
|
225
|
+
}, 30_000);
|
|
226
|
+
|
|
227
|
+
test("a lease lost mid-step stops the step and the run, which fails lease-lost and releases nothing", async () => {
|
|
228
|
+
await withTestDir(async (dir) => {
|
|
229
|
+
await initRepo(dir);
|
|
230
|
+
const log: string[] = [];
|
|
231
|
+
const activities = new Map<string, ActivityFn>([
|
|
232
|
+
[
|
|
233
|
+
"build",
|
|
234
|
+
async (args, signal) => {
|
|
235
|
+
const lease = args.lease as { item: string; holder: string; token: string };
|
|
236
|
+
// The lease runs out from under this run and another worker takes it.
|
|
237
|
+
await releaseWorkLease(lease.item, lease.holder, { cwd: dir, token: lease.token });
|
|
238
|
+
expect((await claimWorkLease(lease.item, "thief", { cwd: dir, ttlMs: 60_000 })).ok).toBe(true);
|
|
239
|
+
await new Promise<void>((resolve, reject) => {
|
|
240
|
+
const t = setTimeout(() => reject(new Error("never stopped")), 20_000);
|
|
241
|
+
signal?.addEventListener("abort", () => { clearTimeout(t); log.push("stopped"); reject(new Error("aborted")); }, { once: true });
|
|
242
|
+
});
|
|
243
|
+
return {};
|
|
244
|
+
},
|
|
245
|
+
],
|
|
246
|
+
["after", async () => { log.push("after"); return {}; }],
|
|
247
|
+
]);
|
|
248
|
+
const op = dispatchOp({ workLease: { item: "W-1", ttl: "3s" } }, [
|
|
249
|
+
step("build", { lease: workLeaseOutput() }, { profile: "long" } as never),
|
|
250
|
+
step("after"),
|
|
251
|
+
]);
|
|
252
|
+
const result = await run(op, activities, dir);
|
|
253
|
+
|
|
254
|
+
expect(log).toEqual(["stopped"]);
|
|
255
|
+
expect(result.status).toBe("fail");
|
|
256
|
+
expect(result.records.map((r) => [r.fn, r.status])).toEqual([
|
|
257
|
+
["workLease:claim", "ok"],
|
|
258
|
+
["build", "fail"],
|
|
259
|
+
["after", "skipped"],
|
|
260
|
+
["workLease:lost", "fail"],
|
|
261
|
+
]);
|
|
262
|
+
expect(result.records.at(-1)?.error).toMatch(/^lease-lost: W-1 is held by thief/);
|
|
263
|
+
expect(result.workLease).toMatchObject({ released: false, lost: expect.stringMatching(/thief/) });
|
|
264
|
+
// The thief still holds it: a lost lease isn't this run's to give back.
|
|
265
|
+
expect((await listWorkLeases({ cwd: dir, memberPrefix: "" }))[0]).toMatchObject({ holder: "thief", state: "active" });
|
|
266
|
+
expect((await readRunLedger("local", "dispatch", { cwd: dir })).records.at(-1)?.status).toBe("fail");
|
|
267
|
+
});
|
|
268
|
+
}, 30_000);
|
|
269
|
+
|
|
270
|
+
test("a run whose lease was lost before it is recorded is never recorded as done", async () => {
|
|
271
|
+
await withTestDir(async (dir) => {
|
|
272
|
+
await initRepo(dir);
|
|
273
|
+
const activities = new Map<string, ActivityFn>([
|
|
274
|
+
[
|
|
275
|
+
"build",
|
|
276
|
+
async (args) => {
|
|
277
|
+
const lease = args.lease as { item: string; holder: string; token: string };
|
|
278
|
+
await releaseWorkLease(lease.item, lease.holder, { cwd: dir, token: lease.token });
|
|
279
|
+
await claimWorkLease(lease.item, "thief", { cwd: dir, ttlMs: 60_000 });
|
|
280
|
+
return {};
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
]);
|
|
284
|
+
const result = await run(dispatchOp({ workLease: { item: "W-1" } }), activities, dir);
|
|
285
|
+
expect(result.status).toBe("fail");
|
|
286
|
+
expect(result.records.map((r) => r.fn)).toEqual(["workLease:claim", "build", "workLease:lost"]);
|
|
287
|
+
expect(result.workLease).toMatchObject({ released: false });
|
|
288
|
+
expect((await readRunLedger("local", "dispatch", { cwd: dir })).records.at(-1)?.status).toBe("fail");
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
describe("an Op that changes the checkout", () => {
|
|
294
|
+
test("applies a build on chant/work/<item> in a worktree of its own, and the coding agent's edits and index lock are untouched", async () => {
|
|
295
|
+
await withTestDir(async (dir) => {
|
|
296
|
+
await initRepo(dir);
|
|
297
|
+
const head = git(["rev-parse", "HEAD"], dir).stdout.trim();
|
|
298
|
+
const edited = "export const hello = 2; // the coding agent's uncommitted edit\n";
|
|
299
|
+
writeFileSync(join(dir, "app", "index.ts"), edited);
|
|
300
|
+
const lock = join(dir, ".git", "index.lock");
|
|
301
|
+
writeFileSync(lock, "");
|
|
302
|
+
|
|
303
|
+
let worktree = "";
|
|
304
|
+
const activities = new Map<string, ActivityFn>([
|
|
305
|
+
[
|
|
306
|
+
"applyBuild",
|
|
307
|
+
async (args) => {
|
|
308
|
+
worktree = String(args.worktree);
|
|
309
|
+
// The build the builder pushed, applied and committed in the worktree.
|
|
310
|
+
writeFileSync(join(worktree, "app", "index.ts"), "export const hello = 3; // the build\n");
|
|
311
|
+
writeFileSync(join(worktree, "app", "feature.ts"), "export const feature = true;\n");
|
|
312
|
+
expect(git(["add", "-A"], worktree).status).toBe(0);
|
|
313
|
+
expect(git(["commit", "-q", "-m", "apply W-7"], worktree).status).toBe(0);
|
|
314
|
+
return {};
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
]);
|
|
318
|
+
const steward = declareSteward({
|
|
319
|
+
name: "box",
|
|
320
|
+
ops: [
|
|
321
|
+
dispatchOp({ workLease: { item: "W-7" }, changesCheckout: true, schedule: { cron: "* * * * *" } }, [
|
|
322
|
+
step("applyBuild", { worktree: workLeaseOutput("worktree"), branch: workLeaseOutput("branch") }),
|
|
323
|
+
]),
|
|
324
|
+
],
|
|
325
|
+
});
|
|
326
|
+
const events = await runOperatorRound({ cwd: dir, holder: "h1", steward, activities, profiles: PROFILES, now: () => new Date(2026, 8, 25, 10, 1) });
|
|
327
|
+
expect(events.map((e) => e.kind)).toEqual(["ticked"]);
|
|
328
|
+
const result = (events[0] as { result: OpRunResult }).result;
|
|
329
|
+
expect(result.workLease).toMatchObject({ item: "W-7", branch: "chant/work/W-7", released: true, outcome: "done" });
|
|
330
|
+
expect(result.workLease?.holder).toBe(stewardWorkHolder("box", "dispatch", "h1"));
|
|
331
|
+
|
|
332
|
+
// The worktree sat under the git directory and is gone; the branch keeps the build.
|
|
333
|
+
expect(worktree.startsWith(join(dir, ".git"))).toBe(true);
|
|
334
|
+
expect(existsSync(worktree)).toBe(false);
|
|
335
|
+
expect(git(["show", "chant/work/W-7:app/feature.ts"], dir).stdout).toBe("export const feature = true;\n");
|
|
336
|
+
|
|
337
|
+
// The coding agent's checkout: its edit, its lock, HEAD and the index, as they were.
|
|
338
|
+
expect(readFileSync(join(dir, "app", "index.ts"), "utf-8")).toBe(edited);
|
|
339
|
+
expect(existsSync(lock)).toBe(true);
|
|
340
|
+
rmSync(lock);
|
|
341
|
+
expect(git(["rev-parse", "HEAD"], dir).stdout.trim()).toBe(head);
|
|
342
|
+
expect(git(["status", "--porcelain"], dir).stdout).toBe(" M app/index.ts\n");
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
});
|