@intentius/chant 0.91.0 → 0.94.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/graph.d.ts.map +1 -1
- package/dist/cli/handlers/operator.d.ts +6 -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-plugins.d.ts +6 -6
- package/dist/cli/mcp/workspace-tools.d.ts +2 -1
- 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 +12 -2
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/pinned-upgrade.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/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/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 +201 -0
- 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/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 +11 -4
- package/dist/op/index.d.ts.map +1 -1
- package/dist/op/local-executor.d.ts +29 -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 +2 -0
- package/dist/op/op-ir.d.ts.map +1 -1
- package/dist/op/op-verb-class.d.ts.map +1 -1
- package/dist/op/operator.d.ts +97 -1
- package/dist/op/operator.d.ts.map +1 -1
- package/dist/op/runtime.d.ts +37 -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/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 +22 -1
- package/dist/op/steward.d.ts.map +1 -1
- package/dist/op/types.d.ts +7 -0
- package/dist/op/types.d.ts.map +1 -1
- package/dist/op/work-lease-run.d.ts +1 -1
- package/dist/op/work-lease-run.d.ts.map +1 -1
- 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-intent.d.ts +85 -0
- package/dist/workspace/box-intent.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 +104 -0
- package/dist/workspace/chant-migrations.d.ts.map +1 -0
- package/dist/workspace/checks/boxes.d.ts +47 -1
- package/dist/workspace/checks/boxes.d.ts.map +1 -1
- 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 +7 -0
- package/dist/workspace/checks/records.d.ts.map +1 -1
- package/dist/workspace/checks.d.ts +7 -0
- 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 +49 -4
- package/dist/workspace/decide.d.ts.map +1 -1
- package/dist/workspace/decision-points.schema.json +3 -3
- package/dist/workspace/declaration.d.ts +58 -0
- package/dist/workspace/declaration.d.ts.map +1 -1
- package/dist/workspace/declaration.schema.json +119 -1
- package/dist/workspace/declared-kinds.d.ts +20 -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-joins.d.ts +5 -5
- 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 +2 -0
- package/dist/workspace/points-cli.d.ts.map +1 -1
- package/dist/workspace/points.d.ts +4 -6
- package/dist/workspace/points.d.ts.map +1 -1
- package/dist/workspace/reason-codes.d.ts +16 -2
- package/dist/workspace/reason-codes.d.ts.map +1 -1
- package/dist/workspace/records-cli.d.ts +11 -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 +34 -4
- 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 +65 -1
- package/dist/workspace/status-stewards.d.ts.map +1 -1
- package/dist/workspace/status.d.ts +24 -0
- package/dist/workspace/status.d.ts.map +1 -1
- package/dist/workspace/work-cli.d.ts +113 -2
- package/dist/workspace/work-cli.d.ts.map +1 -1
- 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/graph.test.ts +20 -0
- package/src/cli/handlers/graph.ts +16 -2
- package/src/cli/handlers/operator.ts +34 -2
- package/src/cli/handlers/run.test.ts +154 -2
- package/src/cli/handlers/run.ts +121 -2
- package/src/cli/main.test.ts +10 -0
- package/src/cli/main.ts +23 -3
- package/src/cli/mcp/workspace-plugins.ts +6 -6
- package/src/cli/mcp/workspace-tools.test.ts +35 -19
- package/src/cli/mcp/workspace-tools.ts +62 -4
- package/src/cli/plugins.ts +11 -3
- package/src/cli/registry.ts +12 -2
- package/src/cli/serve-mcp-workspace.test.ts +6 -6
- 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/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/run-ledger.ts +6 -0
- package/src/lifecycle/symptoms.ts +95 -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 +308 -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 +119 -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/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 +18 -3
- package/src/op/local-executor.ts +151 -15
- package/src/op/local-output.test.ts +33 -0
- package/src/op/local-output.ts +20 -2
- package/src/op/op-ir.test.ts +10 -0
- package/src/op/op-ir.ts +4 -0
- package/src/op/op-verb-class.ts +1 -0
- package/src/op/operator.ts +290 -11
- package/src/op/runtime.ts +31 -8
- package/src/op/runtimes/local.ts +3 -1
- package/src/op/source-archive.ts +161 -0
- package/src/op/steward-points.test.ts +364 -0
- package/src/op/steward-points.ts +194 -0
- package/src/op/steward-turn.ts +79 -0
- package/src/op/steward.test.ts +87 -3
- package/src/op/steward.ts +25 -1
- package/src/op/types.ts +7 -0
- package/src/op/work-lease-run.test.ts +20 -1
- package/src/op/work-lease-run.ts +2 -2
- 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-intent.test.ts +205 -0
- package/src/workspace/box-intent.ts +159 -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 +169 -0
- package/src/workspace/check-contract.test.ts +4 -2
- package/src/workspace/check.schema.json +19 -5
- package/src/workspace/checks/boxes.test.ts +82 -2
- package/src/workspace/checks/boxes.ts +202 -1
- package/src/workspace/checks/diagrams.test.ts +134 -0
- package/src/workspace/checks/diagrams.ts +108 -0
- package/src/workspace/checks/records.ts +47 -0
- package/src/workspace/checks.ts +20 -2
- 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 +1 -1
- package/src/workspace/decide.ts +156 -23
- package/src/workspace/decision-points.schema.json +3 -3
- package/src/workspace/declaration.schema.json +119 -1
- package/src/workspace/declaration.test.ts +58 -0
- package/src/workspace/declaration.ts +112 -0
- package/src/workspace/declared-kinds.test.ts +31 -5
- package/src/workspace/declared-kinds.ts +46 -1
- 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-joins.test.ts +5 -5
- package/src/workspace/intent-joins.ts +5 -5
- package/src/workspace/intent.schema.json +8 -2
- 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/points-cli.ts +17 -3
- package/src/workspace/points-write.schema.json +11 -1
- package/src/workspace/points.schema.json +51 -0
- package/src/workspace/points.test.ts +16 -15
- package/src/workspace/points.ts +5 -17
- package/src/workspace/read-contract.test.ts +9 -1
- package/src/workspace/reason-codes.test.ts +18 -8
- package/src/workspace/reason-codes.ts +21 -2
- package/src/workspace/records-amend.schema.json +2 -0
- package/src/workspace/records-cli.ts +27 -4
- package/src/workspace/records-close.schema.json +2 -0
- package/src/workspace/records-formats.test.ts +15 -15
- 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-since.test.ts +8 -7
- 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 +36 -2
- package/src/workspace/records.test.ts +49 -4
- package/src/workspace/records.ts +109 -3
- package/src/workspace/status-contract.test.ts +14 -8
- package/src/workspace/status-gates.ts +9 -4
- package/src/workspace/status-stewards.ts +155 -5
- package/src/workspace/status.schema.json +129 -8
- package/src/workspace/status.ts +46 -1
- package/src/workspace/work-acceptance.test.ts +274 -0
- package/src/workspace/{work-cli.test.ts → work-cli.e2e.test.ts} +1 -1
- package/src/workspace/work-cli.ts +193 -10
- 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-readiness-chud.test.ts → work-readiness.test.ts} +30 -32
- package/src/workspace/work.test.ts +20 -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
package/src/op/local-executor.ts
CHANGED
|
@@ -25,10 +25,14 @@ import { resolveActivity, type ActivityFn, type ActivityProfile } from "./activi
|
|
|
25
25
|
import type { ReceiptReadResult } from "./receipt-store";
|
|
26
26
|
import { isStepOutputRef } from "./step-output-ref";
|
|
27
27
|
import { parseDuration } from "./duration";
|
|
28
|
+
import { stepTimeoutProblem } from "./activity-profiles";
|
|
28
29
|
import { describeGateMismatch, evaluateGate, gitGateLedgerPort, type GateCheck, type GateLedgerPort } from "./gate";
|
|
29
30
|
import { gateName } from "./gate-name";
|
|
30
31
|
import type { ResolvedGateApproval } from "./gate-approval";
|
|
31
32
|
import type { PendingGateRecord } from "../lifecycle/gate-ledger";
|
|
33
|
+
import { isPointWait, type WaitingPoint } from "./steward-points";
|
|
34
|
+
import { isGateWait } from "./gate-wait";
|
|
35
|
+
import { currentStewardTurn, enterStewardTurn } from "./steward-turn";
|
|
32
36
|
import { appendRunRecord, buildRunRecord } from "../lifecycle/run-ledger";
|
|
33
37
|
import type { OpRunRecord } from "./runtime";
|
|
34
38
|
import { randomUUID } from "node:crypto";
|
|
@@ -78,6 +82,18 @@ export interface StepRecord {
|
|
|
78
82
|
* was applied.
|
|
79
83
|
*/
|
|
80
84
|
refusal?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Set on an activity step that asked a decision point still open (#2749):
|
|
87
|
+
* the question the run now waits on. The step's status is `skipped` and the
|
|
88
|
+
* run's is `waiting`: nothing broke, and a person answers the question.
|
|
89
|
+
*/
|
|
90
|
+
point?: WaitingPoint;
|
|
91
|
+
/**
|
|
92
|
+
* Set on an activity step whose command stopped at a gate of its own
|
|
93
|
+
* (#2779): the op the gate is recorded under and its name, what `chant
|
|
94
|
+
* approve` takes. The step's status is `skipped` and the run's is `gated`.
|
|
95
|
+
*/
|
|
96
|
+
gate?: { op: string; gate: string };
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
export interface OpRunResult {
|
|
@@ -91,11 +107,17 @@ export interface OpRunResult {
|
|
|
91
107
|
*
|
|
92
108
|
* Written to the run ledger as the record's own `status` (#2118).
|
|
93
109
|
*/
|
|
94
|
-
status: "ok" | "fail" | "gated";
|
|
110
|
+
status: "ok" | "fail" | "gated" | "waiting";
|
|
95
111
|
/** ISO-8601 start of this run. */
|
|
96
112
|
startedAt: string;
|
|
97
113
|
/** Present when `status === "gated"`: the pending fact the run ended on. */
|
|
98
114
|
gate?: PendingGateRecord;
|
|
115
|
+
/**
|
|
116
|
+
* Present when `status === "waiting"` (#2749): the open decision point the
|
|
117
|
+
* run stopped on. Like a gate, it is a fact and not a wait: a person answers
|
|
118
|
+
* the question, and the next run reads the answer.
|
|
119
|
+
*/
|
|
120
|
+
point?: WaitingPoint;
|
|
99
121
|
/**
|
|
100
122
|
* Present when `status === "gated"` and this run's own append tried to push:
|
|
101
123
|
* whether it reached the remote (#2310). Absent when the run stopped on a
|
|
@@ -173,6 +195,18 @@ class GateStop extends Error {
|
|
|
173
195
|
}
|
|
174
196
|
}
|
|
175
197
|
|
|
198
|
+
/** Internal: a step asked a decision point that is still open (#2749). Not a failure — no compensation follows it. */
|
|
199
|
+
class PointStop extends Error {
|
|
200
|
+
constructor(
|
|
201
|
+
public readonly records: StepRecord[],
|
|
202
|
+
public readonly question: WaitingPoint,
|
|
203
|
+
public readonly phase: string,
|
|
204
|
+
) {
|
|
205
|
+
super(`waiting on decision point ${question.id}`);
|
|
206
|
+
this.name = "PointStop";
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
176
210
|
/**
|
|
177
211
|
* Internal: a step boundary found the run can't go on under its work lease
|
|
178
212
|
* (#2748). `lost` false is a claim that found nothing to claim: not a
|
|
@@ -312,6 +346,8 @@ async function callWithTimeout(
|
|
|
312
346
|
interface RanStep {
|
|
313
347
|
record: StepRecord;
|
|
314
348
|
result?: unknown;
|
|
349
|
+
/** The pending fact a step's command stopped at (#2779), when it threw {@link GateWait}. */
|
|
350
|
+
pending?: PendingGateRecord;
|
|
315
351
|
}
|
|
316
352
|
|
|
317
353
|
/**
|
|
@@ -341,9 +377,16 @@ async function runStep(
|
|
|
341
377
|
}
|
|
342
378
|
|
|
343
379
|
const profile = profiles[step.profile ?? DEFAULT_PROFILE] ?? {};
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
380
|
+
// A step's own timeout (#2787) replaces the profile's and keeps its retries.
|
|
381
|
+
if (step.timeout !== undefined) {
|
|
382
|
+
const problem = stepTimeoutProblem(step.timeout);
|
|
383
|
+
if (problem) return { record: { ...base, status: "fail", durationMs: 0, error: problem } };
|
|
384
|
+
}
|
|
385
|
+
const timeoutMs = step.timeout !== undefined
|
|
386
|
+
? parseDuration(step.timeout)
|
|
387
|
+
: profile.timeout
|
|
388
|
+
? parseDuration(profile.timeout)
|
|
389
|
+
: FALLBACK_TIMEOUT_MS;
|
|
347
390
|
const maxAttempts =
|
|
348
391
|
profile.retry?.maximumAttempts && profile.retry.maximumAttempts > 0
|
|
349
392
|
? profile.retry.maximumAttempts
|
|
@@ -368,6 +411,17 @@ async function runStep(
|
|
|
368
411
|
}
|
|
369
412
|
return { record, result };
|
|
370
413
|
} catch (err) {
|
|
414
|
+
// An open decision point (#2749) is not a failure and not worth a retry:
|
|
415
|
+
// the answer comes from a person, on a later run.
|
|
416
|
+
if (isPointWait(err)) {
|
|
417
|
+
return { record: { ...base, status: "skipped", durationMs: Date.now() - start, point: err.question } };
|
|
418
|
+
}
|
|
419
|
+
// Nor is a command that stopped at a gate of its own (#2779): the run
|
|
420
|
+
// ends `gated` there, and the next run asks the command again.
|
|
421
|
+
if (isGateWait(err)) {
|
|
422
|
+
const gate = { op: err.pending.op, gate: err.pending.gate };
|
|
423
|
+
return { record: { ...base, status: "skipped", durationMs: Date.now() - start, gate }, pending: err.pending };
|
|
424
|
+
}
|
|
371
425
|
lastErr = err;
|
|
372
426
|
// Stop retrying on abort (Ctrl-C / timeout cascade) or a non-retryable error.
|
|
373
427
|
const fatal =
|
|
@@ -640,6 +694,7 @@ async function runEffectStep(
|
|
|
640
694
|
pending?: PendingGateRecord;
|
|
641
695
|
pushed?: boolean;
|
|
642
696
|
pushWarning?: string;
|
|
697
|
+
point?: WaitingPoint;
|
|
643
698
|
}> {
|
|
644
699
|
const records: StepRecord[] = [];
|
|
645
700
|
|
|
@@ -705,6 +760,18 @@ async function runEffectStep(
|
|
|
705
760
|
}
|
|
706
761
|
const ran = await runStep(nested, phaseName, activities, profiles, resultsById, signal);
|
|
707
762
|
pushRecord(records, gates, ran.record);
|
|
763
|
+
if (ran.pending) {
|
|
764
|
+
// A command's own gate (#2779): the receipt is left untouched, as for a
|
|
765
|
+
// pending gate step.
|
|
766
|
+
skipRest(i + 1);
|
|
767
|
+
return { records, failed: false, pending: ran.pending };
|
|
768
|
+
}
|
|
769
|
+
if (ran.record.point) {
|
|
770
|
+
// Receipt left untouched, as for a pending gate: the next run re-proposes
|
|
771
|
+
// the effect and asks the point again, and finds the answer by then.
|
|
772
|
+
skipRest(i + 1);
|
|
773
|
+
return { records, failed: false, point: ran.record.point };
|
|
774
|
+
}
|
|
708
775
|
if (ran.record.status === "fail") {
|
|
709
776
|
// Receipt left untouched (stale) — the next run re-proposes the effect.
|
|
710
777
|
skipRest(i + 1);
|
|
@@ -779,12 +846,19 @@ async function runPhase(
|
|
|
779
846
|
}
|
|
780
847
|
}
|
|
781
848
|
const steps = phase.steps.filter(isActivity);
|
|
782
|
-
const
|
|
783
|
-
|
|
784
|
-
).map((r) => r.record);
|
|
849
|
+
const settled = await Promise.all(steps.map((s) => runStep(s, phase.name, activities, profiles, resultsById, signal)));
|
|
850
|
+
const ran = settled.map((r) => r.record);
|
|
785
851
|
for (const r of ran) gates.onRecord?.(r);
|
|
786
852
|
const records = gateRecords.concat(ran);
|
|
787
853
|
if (records.some((r) => r.status === "fail")) throw new PhaseFailure(records);
|
|
854
|
+
// A step whose command stopped at its own gate (#2779) stops the run at
|
|
855
|
+
// this phase, as a gate step would, once its siblings have finished.
|
|
856
|
+
const gated = settled.find((r) => r.pending)?.pending;
|
|
857
|
+
if (gated) throw new GateStop(records, gated, phase.name);
|
|
858
|
+
// The fan-out has already run, so the siblings of an open decision point
|
|
859
|
+
// finished; the run still stops at this phase (#2749).
|
|
860
|
+
const waiting = ran.find((r) => r.point);
|
|
861
|
+
if (waiting?.point) throw new PointStop(records, waiting.point, phase.name);
|
|
788
862
|
return records;
|
|
789
863
|
}
|
|
790
864
|
|
|
@@ -828,10 +902,14 @@ async function runPhase(
|
|
|
828
902
|
continue;
|
|
829
903
|
}
|
|
830
904
|
if (isEffect(step)) {
|
|
831
|
-
const { records: effRecords, failed, pending, pushed, pushWarning } = await runEffectStep(
|
|
905
|
+
const { records: effRecords, failed, pending, pushed, pushWarning, point } = await runEffectStep(
|
|
832
906
|
step, phase.name, activities, profiles, resultsById, gates, signal,
|
|
833
907
|
);
|
|
834
908
|
records.push(...effRecords); // already emitted by runEffectStep
|
|
909
|
+
if (point) {
|
|
910
|
+
skipRemaining(i + 1);
|
|
911
|
+
throw new PointStop(records, point, phase.name);
|
|
912
|
+
}
|
|
835
913
|
if (pending) {
|
|
836
914
|
skipRemaining(i + 1);
|
|
837
915
|
throw new GateStop(records, pending, phase.name, pushed, pushWarning);
|
|
@@ -842,8 +920,19 @@ async function runPhase(
|
|
|
842
920
|
}
|
|
843
921
|
continue;
|
|
844
922
|
}
|
|
845
|
-
const { record } = await runStep(step, phase.name, activities, profiles, resultsById, signal);
|
|
923
|
+
const { record, pending } = await runStep(step, phase.name, activities, profiles, resultsById, signal);
|
|
846
924
|
pushRecord(records, gates, record);
|
|
925
|
+
if (pending) {
|
|
926
|
+
// The step's command stopped at its own gate (#2779): the run ends
|
|
927
|
+
// `gated` there, as at a pending gate step.
|
|
928
|
+
skipRemaining(i + 1);
|
|
929
|
+
throw new GateStop(records, pending, phase.name);
|
|
930
|
+
}
|
|
931
|
+
if (record.point) {
|
|
932
|
+
// An open decision point (#2749) ends the run as a pending gate does.
|
|
933
|
+
skipRemaining(i + 1);
|
|
934
|
+
throw new PointStop(records, record.point, phase.name);
|
|
935
|
+
}
|
|
847
936
|
if (record.status === "fail") {
|
|
848
937
|
// Mark the remaining steps in this phase as skipped, then abort.
|
|
849
938
|
skipRemaining(i + 1);
|
|
@@ -890,6 +979,8 @@ export interface RunOpOptions {
|
|
|
890
979
|
* never promoted into a run failure.
|
|
891
980
|
*/
|
|
892
981
|
onLedgerError?: (err: unknown) => void;
|
|
982
|
+
/** The steward whose turn this run is (#2749), recorded on its ledger record. */
|
|
983
|
+
steward?: string;
|
|
893
984
|
/**
|
|
894
985
|
* For an Op that declares `workLease` (#2748): the item this run is for
|
|
895
986
|
* (`chant run <op> --work <id>`), which takes the place of the Op's own, and
|
|
@@ -907,9 +998,10 @@ export interface RunOpOptions {
|
|
|
907
998
|
* Resolves with the run result when every phase succeeds (`status: "ok"`), and
|
|
908
999
|
* also when the run stopped at an unapproved gate (`status: "gated"`, with the
|
|
909
1000
|
* pending fact on `result.gate`) — a gate is a fact, not an error, so it is not
|
|
910
|
-
* thrown.
|
|
911
|
-
*
|
|
912
|
-
*
|
|
1001
|
+
* thrown. So does an activity's open decision point (`status: "waiting"`, with
|
|
1002
|
+
* the question on `result.point`, #2749). Rejects with `OpRunFailure` (carrying the partial result) on terminal
|
|
1003
|
+
* failure, after running any `onFailure` phases in reverse order; a gated or
|
|
1004
|
+
* waiting run runs no `onFailure` phase, because nothing failed and nothing was left
|
|
913
1005
|
* half-applied to compensate for.
|
|
914
1006
|
*
|
|
915
1007
|
* Whichever of the three ways it settles, the run carries an
|
|
@@ -928,6 +1020,27 @@ export async function runOpLocally(
|
|
|
928
1020
|
// The run id is the gate ledger's and the run ledger's alike: a pending gate
|
|
929
1021
|
// fact and the record naming that gate carry the same string.
|
|
930
1022
|
const runId = options.runId ?? randomUUID();
|
|
1023
|
+
// In a steward's turn (#2749: `chant operator --steward`, or `chant acp
|
|
1024
|
+
// --steward` on Fountain) the run is the steward's: its record names the
|
|
1025
|
+
// steward, and a decision point asked during it names this run.
|
|
1026
|
+
const turn = currentStewardTurn();
|
|
1027
|
+
const steward = options.steward ?? turn?.steward;
|
|
1028
|
+
const restore = turn && turn.run !== runId ? enterStewardTurn({ ...turn, run: runId }) : undefined;
|
|
1029
|
+
try {
|
|
1030
|
+
return await runOpInTurn(config, activities, profiles, signal, { ...options, runId, ...(steward ? { steward } : {}) });
|
|
1031
|
+
} finally {
|
|
1032
|
+
restore?.();
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
async function runOpInTurn(
|
|
1037
|
+
config: OpConfig,
|
|
1038
|
+
activities: Map<string, ActivityFn>,
|
|
1039
|
+
profiles: Record<string, ActivityProfile>,
|
|
1040
|
+
signal: AbortSignal | undefined,
|
|
1041
|
+
options: RunOpOptions & { runId: string },
|
|
1042
|
+
): Promise<OpRunResult> {
|
|
1043
|
+
const runId = options.runId;
|
|
931
1044
|
|
|
932
1045
|
const gates: GateContext = {
|
|
933
1046
|
op: config.name,
|
|
@@ -1023,13 +1136,18 @@ export async function runOpLocally(
|
|
|
1023
1136
|
settled: StepRecord[],
|
|
1024
1137
|
status: OpRunResult["status"],
|
|
1025
1138
|
gate?: PendingGateRecord,
|
|
1139
|
+
point?: WaitingPoint,
|
|
1026
1140
|
): Promise<OpRunRecord> => {
|
|
1141
|
+
const ended = options.now ?? new Date().toISOString();
|
|
1027
1142
|
const input = buildRunRecord(config, settled, {
|
|
1028
1143
|
started: startedAt,
|
|
1029
|
-
ended
|
|
1144
|
+
ended,
|
|
1030
1145
|
status,
|
|
1031
1146
|
id: runId,
|
|
1032
|
-
|
|
1147
|
+
// A gate recorded under another op (#2779: a command's own gate) names it.
|
|
1148
|
+
...(gate ? { gate: { name: gate.gate, since: gate.timestamp, ...(gate.op !== config.name ? { op: gate.op } : {}) } } : {}),
|
|
1149
|
+
...(point ? { point: { ...point, since: ended } } : {}),
|
|
1150
|
+
...(options.steward ? { steward: options.steward } : {}),
|
|
1033
1151
|
});
|
|
1034
1152
|
const record: OpRunRecord = { version: 1, ...input, id: runId };
|
|
1035
1153
|
if (!options.ledger) return record;
|
|
@@ -1072,6 +1190,24 @@ export async function runOpLocally(
|
|
|
1072
1190
|
};
|
|
1073
1191
|
}
|
|
1074
1192
|
|
|
1193
|
+
// An open decision point ends the run the way a pending gate does (#2749):
|
|
1194
|
+
// no later phase and no compensation, and the run is `waiting`.
|
|
1195
|
+
if (err instanceof PointStop) {
|
|
1196
|
+
records.push(...err.records);
|
|
1197
|
+
skipLaterPhases(err.phase);
|
|
1198
|
+
const record = await settle(records, "waiting", undefined, err.question);
|
|
1199
|
+
return {
|
|
1200
|
+
op: config.name,
|
|
1201
|
+
records,
|
|
1202
|
+
totalMs: Date.now() - start,
|
|
1203
|
+
status: "waiting",
|
|
1204
|
+
startedAt,
|
|
1205
|
+
point: err.question,
|
|
1206
|
+
record,
|
|
1207
|
+
...(await finishWork("waiting")),
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1075
1211
|
// A pending gate ends the run where it stands: no later phase, and no
|
|
1076
1212
|
// `onFailure` compensation — nothing failed, so there is nothing to undo.
|
|
1077
1213
|
if (err instanceof GateStop) {
|
|
@@ -1135,7 +1271,7 @@ export async function runOpLocally(
|
|
|
1135
1271
|
try {
|
|
1136
1272
|
records.push(...(await runPhase(phase, activities, profiles, resultsById, compensation, signal)));
|
|
1137
1273
|
} catch (compErr) {
|
|
1138
|
-
if (compErr instanceof PhaseFailure || compErr instanceof GateStop) {
|
|
1274
|
+
if (compErr instanceof PhaseFailure || compErr instanceof GateStop || compErr instanceof PointStop) {
|
|
1139
1275
|
records.push(...compErr.records);
|
|
1140
1276
|
} else {
|
|
1141
1277
|
// The same swallow, one level down (#2301): a compensation phase
|
|
@@ -75,6 +75,23 @@ const GATED: OpRunResult = withRecord({
|
|
|
75
75
|
],
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
+
/** A gate bound to a plan (#2300), for the --plan tests (#2832). */
|
|
79
|
+
const GATED_WITH_PLAN: OpRunResult = withRecord({
|
|
80
|
+
op: "prod-apply",
|
|
81
|
+
totalMs: 300,
|
|
82
|
+
status: "gated",
|
|
83
|
+
startedAt: "2026-09-05T12:00:00.000Z",
|
|
84
|
+
gate: {
|
|
85
|
+
version: 1, kind: "pending", op: "prod-apply", gate: "rollout-gate",
|
|
86
|
+
timestamp: "2026-09-05T12:00:00.000Z",
|
|
87
|
+
expiresAt: "2026-09-07T12:00:00.000Z",
|
|
88
|
+
planDigest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
89
|
+
},
|
|
90
|
+
records: [
|
|
91
|
+
{ phase: "Apply", fn: "gate:rollout-gate", args: {}, status: "skipped", durationMs: 0 },
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
|
|
78
95
|
describe("renderHuman — gated (#2119)", () => {
|
|
79
96
|
test("names the gate, the approve line and the expiry, and points at no other runtime", () => {
|
|
80
97
|
const lines: string[] = [];
|
|
@@ -88,6 +105,14 @@ describe("renderHuman — gated (#2119)", () => {
|
|
|
88
105
|
expect(out).not.toContain("--on");
|
|
89
106
|
});
|
|
90
107
|
|
|
108
|
+
test("a gate bound to a plan (#2300) carries --plan on the approve line (#2832)", () => {
|
|
109
|
+
const lines: string[] = [];
|
|
110
|
+
renderHuman(GATED_WITH_PLAN, (l) => lines.push(l));
|
|
111
|
+
const out = lines.join("\n");
|
|
112
|
+
expect(out).toContain(`plan : ${GATED_WITH_PLAN.gate!.planDigest}`);
|
|
113
|
+
expect(out).toContain(`approve : chant approve prod-apply rollout-gate --plan ${GATED_WITH_PLAN.gate!.planDigest}`);
|
|
114
|
+
});
|
|
115
|
+
|
|
91
116
|
test("shows the approver on a gate that passed", () => {
|
|
92
117
|
const lines: string[] = [];
|
|
93
118
|
renderHuman(withRecord({
|
|
@@ -157,6 +182,14 @@ describe("renderJson", () => {
|
|
|
157
182
|
expect(parsed.approve).toBe("chant approve prod-apply rollout-gate");
|
|
158
183
|
});
|
|
159
184
|
|
|
185
|
+
test("a gate bound to a plan (#2300) carries --plan on the approve line (#2832)", () => {
|
|
186
|
+
const lines: string[] = [];
|
|
187
|
+
renderJson(GATED_WITH_PLAN, (l) => lines.push(l));
|
|
188
|
+
const parsed = JSON.parse(lines[0]) as OpRunRecord & { approve: string };
|
|
189
|
+
expect(parsed.status).toBe("gated");
|
|
190
|
+
expect(parsed.approve).toBe(`chant approve prod-apply rollout-gate --plan ${GATED_WITH_PLAN.gate!.planDigest}`);
|
|
191
|
+
});
|
|
192
|
+
|
|
160
193
|
// #2310: a JSON consumer (CI tooling reading `chant run --json`) needs the
|
|
161
194
|
// same fact the human render shows — not just a silent success.
|
|
162
195
|
test("a gated record whose push failed carries pushed:false and why", () => {
|
package/src/op/local-output.ts
CHANGED
|
@@ -50,6 +50,9 @@ export function renderHuman(result: OpRunResult, write: Writer = stderr): void {
|
|
|
50
50
|
if (record.refusal) {
|
|
51
51
|
write(` [refused] ${record.refusal}`);
|
|
52
52
|
}
|
|
53
|
+
if (record.point) {
|
|
54
|
+
write(` [waiting] decision point ${record.point.point}: ${record.point.id} is ${record.point.state}`);
|
|
55
|
+
}
|
|
53
56
|
if (record.error) {
|
|
54
57
|
write(` ${record.error}`);
|
|
55
58
|
}
|
|
@@ -73,6 +76,16 @@ export function renderHuman(result: OpRunResult, write: Writer = stderr): void {
|
|
|
73
76
|
write(`Op "${result.op}" completed in ${total}`);
|
|
74
77
|
return;
|
|
75
78
|
}
|
|
79
|
+
if (result.status === "waiting" && result.point) {
|
|
80
|
+
// An open decision point (#2749): a fact like a gate. A person answers the
|
|
81
|
+
// question, through hud or at a shell, and the next run reads the answer.
|
|
82
|
+
const { point } = result;
|
|
83
|
+
write(`Op "${result.op}" is waiting on decision point "${point.point}" after ${total}`);
|
|
84
|
+
write(` question: ${point.id} (${point.state}) at ${point.path}`);
|
|
85
|
+
if (point.subject) write(` subject : ${point.subject}`);
|
|
86
|
+
write(` answer : ${pointAnswerCommand(point.id)}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
76
89
|
if (result.status === "fail" || !result.gate) {
|
|
77
90
|
write(`Op "${result.op}" failed after ${total}`);
|
|
78
91
|
return;
|
|
@@ -87,7 +100,7 @@ export function renderHuman(result: OpRunResult, write: Writer = stderr): void {
|
|
|
87
100
|
// #2300: the plan the approval will be bound to. Printed before the
|
|
88
101
|
// command, because it is what the command approves.
|
|
89
102
|
if (gate.planDigest) write(` plan : ${gate.planDigest}`);
|
|
90
|
-
write(` approve : ${approveCommand(gate.op, gate.gate)}`);
|
|
103
|
+
write(` approve : ${approveCommand(gate.op, gate.gate, undefined, gate.planDigest)}`);
|
|
91
104
|
if (gate.url) write(` approve at: ${gate.url}`);
|
|
92
105
|
write(` expires : ${gate.expiresAt}`);
|
|
93
106
|
// #2310: this run's own append reached only the local chant/lifecycle
|
|
@@ -115,7 +128,7 @@ export function renderHuman(result: OpRunResult, write: Writer = stderr): void {
|
|
|
115
128
|
* the gate doesn't have to reassemble it from the op and gate names.
|
|
116
129
|
*/
|
|
117
130
|
export function renderJson(result: OpRunResult, write: Writer = stdout): void {
|
|
118
|
-
const approve = result.gate ? { approve: approveCommand(result.gate.op, result.gate.gate) } : {};
|
|
131
|
+
const approve = result.gate ? { approve: approveCommand(result.gate.op, result.gate.gate, undefined, result.gate.planDigest) } : {};
|
|
119
132
|
// #2310: whether this run's own append reached the remote — not part of
|
|
120
133
|
// the persisted ledger record (a replay has nothing new to report), but a
|
|
121
134
|
// live run's JSON consumer needs it exactly where the human render shows it.
|
|
@@ -126,3 +139,8 @@ export function renderJson(result: OpRunResult, write: Writer = stdout): void {
|
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
export type { OpRunResult, StepRecord };
|
|
142
|
+
|
|
143
|
+
/** The command a person answers an open decision point with (#2749). */
|
|
144
|
+
export function pointAnswerCommand(id: string): string {
|
|
145
|
+
return `chant workspace points answer ${id} --answer <answer> --by <your name>`;
|
|
146
|
+
}
|
package/src/op/op-ir.test.ts
CHANGED
|
@@ -112,6 +112,16 @@ describe("op.json IR", () => {
|
|
|
112
112
|
expect(serializeOpIR(reconstructed)).toBe(text);
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
+
it("carries a step's own timeout, and round-trips it (#2787)", () => {
|
|
116
|
+
const original: OpConfig = { name: "dispatch", overview: "o", phases: [phase("Build", [shell("./build.sh", { timeout: "45m" }), shell("echo hi")])] };
|
|
117
|
+
const ir = buildOpIR(original);
|
|
118
|
+
const steps = ir.phases[0].steps as { profile: string; timeout?: string }[];
|
|
119
|
+
expect(steps[0]).toMatchObject({ profile: "atMostOnce", timeout: "45m" });
|
|
120
|
+
expect(steps[1]).not.toHaveProperty("timeout");
|
|
121
|
+
const text = serializeOpIR(original);
|
|
122
|
+
expect(serializeOpIR(opConfigFromIR(JSON.parse(text) as OpIR))).toBe(text);
|
|
123
|
+
});
|
|
124
|
+
|
|
115
125
|
it("two serializations of the same config are byte-identical (determinism)", () => {
|
|
116
126
|
const config = representativeOp();
|
|
117
127
|
expect(serializeOpIR(config)).toBe(serializeOpIR(config));
|
package/src/op/op-ir.ts
CHANGED
|
@@ -118,6 +118,8 @@ export interface OpIRActivityStep {
|
|
|
118
118
|
args: Record<string, unknown>;
|
|
119
119
|
/** Resolved to its effective value — `ActivityStep.profile ?? "fastIdempotent"`. */
|
|
120
120
|
profile: string;
|
|
121
|
+
/** The step's own timeout, replacing its profile's (#2787). Absent when the profile's applies. */
|
|
122
|
+
timeout?: string;
|
|
121
123
|
/**
|
|
122
124
|
* What this step's effect touches in the estate (#2022): the string values
|
|
123
125
|
* of the args its contract declares as entity-identifying
|
|
@@ -240,6 +242,7 @@ function irActivityStep(step: ActivityStep, contracts: ReadonlyMap<string, Activ
|
|
|
240
242
|
fn: step.fn,
|
|
241
243
|
args: step.args ?? {},
|
|
242
244
|
profile: effectiveProfile(step),
|
|
245
|
+
...(step.timeout !== undefined ? { timeout: step.timeout } : {}),
|
|
243
246
|
...(entities ? { entities } : {}),
|
|
244
247
|
...(step.outcomeAttribute ? { outcomeAttribute: step.outcomeAttribute } : {}),
|
|
245
248
|
};
|
|
@@ -394,6 +397,7 @@ function opStepFromIR(step: OpIRStep): StepDefinition {
|
|
|
394
397
|
fn: step.fn,
|
|
395
398
|
args: step.args,
|
|
396
399
|
profile: step.profile as ActivityStep["profile"],
|
|
400
|
+
...(step.timeout !== undefined ? { timeout: step.timeout } : {}),
|
|
397
401
|
...(step.outcomeAttribute ? { outcomeAttribute: step.outcomeAttribute } : {}),
|
|
398
402
|
};
|
|
399
403
|
}
|
package/src/op/op-verb-class.ts
CHANGED