cloving 1.3.0 → 1.5.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/README.md +329 -215
- package/dist/src/agent-harness-shell.d.ts +2 -1
- package/dist/src/agent-harness-shell.js +8 -5
- package/dist/src/authority/command-wait-checks.d.ts +8 -0
- package/dist/src/authority/command-wait-checks.js +88 -0
- package/dist/src/authority/command-wait-results.d.ts +15 -0
- package/dist/src/authority/command-wait-results.js +73 -0
- package/dist/src/authority/command-wait-view.d.ts +21 -0
- package/dist/src/authority/command-wait-view.js +34 -0
- package/dist/src/authority/command-waits.d.ts +39 -0
- package/dist/src/authority/command-waits.js +97 -0
- package/dist/src/authority/commands.js +22 -1
- package/dist/src/authority/dispatch-decision.d.ts +8 -0
- package/dist/src/authority/dispatch-decision.js +26 -1
- package/dist/src/authority/effect-requests.js +6 -3
- package/dist/src/authority/effect-results.js +4 -2
- package/dist/src/authority/history-state.d.ts +2 -0
- package/dist/src/authority/history-state.js +13 -1
- package/dist/src/authority/instants.d.ts +1 -0
- package/dist/src/authority/instants.js +21 -0
- package/dist/src/authority/reduction-state.d.ts +2 -0
- package/dist/src/authority/reduction-state.js +5 -0
- package/dist/src/authority/reduction.js +24 -0
- package/dist/src/authority/status-input.d.ts +2 -0
- package/dist/src/authority/status.d.ts +2 -0
- package/dist/src/authority/status.js +1 -0
- package/dist/src/authority/stop-sequence.d.ts +1 -0
- package/dist/src/authority/stop-sequence.js +2 -1
- package/dist/src/authority/transitions.d.ts +6 -4
- package/dist/src/authority/transitions.js +7 -0
- package/dist/src/broker/actor/authority-health.d.ts +9 -0
- package/dist/src/broker/actor/authority-health.js +24 -0
- package/dist/src/broker/actor/authority-queries.d.ts +2 -1
- package/dist/src/broker/actor/authority-queries.js +14 -5
- package/dist/src/broker/actor/authority-receipts.js +1 -1
- package/dist/src/broker/actor/authority-runtime.d.ts +4 -2
- package/dist/src/broker/actor/authority-runtime.js +29 -20
- package/dist/src/broker/actor/authority-session.d.ts +6 -19
- package/dist/src/broker/actor/authority-session.js +66 -88
- package/dist/src/broker/actor/authority-settings.d.ts +2 -1
- package/dist/src/broker/actor/authority-settings.js +6 -3
- package/dist/src/broker/actor/authority-upgrades.d.ts +23 -2
- package/dist/src/broker/actor/authority-upgrades.js +8 -2
- package/dist/src/broker/actor/authority-watchdog.d.ts +6 -2
- package/dist/src/broker/actor/authority-watchdog.js +31 -9
- package/dist/src/broker/actor/authority.js +3 -1
- package/dist/src/broker/actor/delivery-intents.js +2 -0
- package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
- package/dist/src/broker/actor/effect-delivery.js +15 -2
- package/dist/src/broker/actor/effect-plan.js +11 -2
- package/dist/src/broker/actor/effect-queries.d.ts +4 -0
- package/dist/src/broker/actor/effect-queries.js +9 -1
- package/dist/src/broker/actor/effect-turn.js +2 -0
- package/dist/src/broker/actor/projection-memory.d.ts +43 -0
- package/dist/src/broker/actor/projection-memory.js +207 -0
- package/dist/src/broker/actor/runtime-parts.d.ts +5 -0
- package/dist/src/broker/actor/runtime-parts.js +8 -3
- package/dist/src/broker/actor/stop-plan.js +9 -3
- package/dist/src/broker/actor/stop-turn.d.ts +2 -0
- package/dist/src/broker/actor/stop-turn.js +24 -11
- package/dist/src/broker/actor/turn-draft.d.ts +1 -0
- package/dist/src/broker/actor/turn-draft.js +4 -0
- package/dist/src/broker/actor/turn-state.js +5 -1
- package/dist/src/broker/authority-admission.js +60 -0
- package/dist/src/broker/authority-restore.d.ts +2 -0
- package/dist/src/broker/authority-restore.js +35 -1
- package/dist/src/broker/command-wait-restore.d.ts +31 -0
- package/dist/src/broker/command-wait-restore.js +86 -0
- package/dist/src/broker/decision-restore.d.ts +1 -1
- package/dist/src/broker/decision-restore.js +1 -0
- package/dist/src/broker/effect-upgrade.d.ts +3 -0
- package/dist/src/broker/effect-upgrade.js +11 -2
- package/dist/src/cli/arguments.d.ts +13 -0
- package/dist/src/cli/arguments.js +43 -3
- package/dist/src/cli/chat-stores.d.ts +2 -0
- package/dist/src/cli/chat-stores.js +113 -0
- package/dist/src/cli/client.d.ts +7 -2
- package/dist/src/cli/client.js +13 -7
- package/dist/src/cli/command-requests.d.ts +46 -0
- package/dist/src/cli/command-requests.js +84 -0
- package/dist/src/cli/command-waits.d.ts +9 -0
- package/dist/src/cli/command-waits.js +51 -0
- package/dist/src/cli/commands.js +70 -88
- package/dist/src/cli/config-file.d.ts +2 -2
- package/dist/src/cli/config-file.js +4 -2
- package/dist/src/cli/config-notice.js +5 -2
- package/dist/src/cli/doctor-retention.d.ts +2 -0
- package/dist/src/cli/doctor-retention.js +93 -0
- package/dist/src/cli/doctor-workflows.d.ts +5 -2
- package/dist/src/cli/doctor-workflows.js +12 -7
- package/dist/src/cli/doctor.js +11 -9
- package/dist/src/cli/harness-probes.d.ts +2 -0
- package/dist/src/cli/harness-probes.js +1 -1
- package/dist/src/cli/harness-processes.d.ts +6 -0
- package/dist/src/cli/harness-processes.js +36 -0
- package/dist/src/cli/init-command.d.ts +9 -0
- package/dist/src/cli/init-command.js +52 -0
- package/dist/src/cli/init-prompt-command.d.ts +10 -0
- package/dist/src/cli/init-prompt-command.js +145 -0
- package/dist/src/cli/init-prompt.d.ts +35 -0
- package/dist/src/cli/init-prompt.js +74 -0
- package/dist/src/cli/init-selection.d.ts +13 -0
- package/dist/src/cli/init-selection.js +75 -0
- package/dist/src/cli/main.d.ts +2 -0
- package/dist/src/cli/main.js +37 -7
- package/dist/src/cli/mcp-server.d.ts +1 -0
- package/dist/src/cli/mcp-server.js +156 -0
- package/dist/src/cli/mcp-tools.d.ts +15 -0
- package/dist/src/cli/mcp-tools.js +242 -0
- package/dist/src/cli/queue-arguments.d.ts +15 -0
- package/dist/src/cli/queue-arguments.js +31 -0
- package/dist/src/cli/queue-render.d.ts +3 -0
- package/dist/src/cli/queue-render.js +26 -0
- package/dist/src/cli/reload-render.d.ts +2 -0
- package/dist/src/cli/reload-render.js +12 -0
- package/dist/src/cli/render.d.ts +1 -1
- package/dist/src/cli/render.js +54 -38
- package/dist/src/cli/report-ci-jobs.d.ts +23 -0
- package/dist/src/cli/report-ci-jobs.js +103 -0
- package/dist/src/cli/report-ci-usage.d.ts +9 -0
- package/dist/src/cli/report-ci-usage.js +185 -0
- package/dist/src/cli/report-ci.d.ts +13 -0
- package/dist/src/cli/report-ci.js +74 -0
- package/dist/src/cli/report-closures.d.ts +6 -0
- package/dist/src/cli/report-closures.js +97 -0
- package/dist/src/cli/report-flow.d.ts +7 -0
- package/dist/src/cli/report-flow.js +122 -0
- package/dist/src/cli/report-github-reader.d.ts +29 -0
- package/dist/src/cli/report-github-reader.js +157 -0
- package/dist/src/cli/report-github.d.ts +15 -8
- package/dist/src/cli/report-github.js +131 -150
- package/dist/src/cli/report-operator.d.ts +3 -0
- package/dist/src/cli/report-operator.js +40 -0
- package/dist/src/cli/report-ready.d.ts +3 -0
- package/dist/src/cli/report-ready.js +96 -0
- package/dist/src/cli/report-text.d.ts +9 -0
- package/dist/src/cli/report-text.js +33 -0
- package/dist/src/cli/report.d.ts +1 -0
- package/dist/src/cli/report.js +104 -27
- package/dist/src/cli/runtime-arguments.d.ts +18 -3
- package/dist/src/cli/runtime-arguments.js +29 -20
- package/dist/src/cli/schedule-arguments.d.ts +14 -0
- package/dist/src/cli/schedule-arguments.js +26 -0
- package/dist/src/cli/schedule-render.d.ts +22 -0
- package/dist/src/cli/schedule-render.js +73 -0
- package/dist/src/cli/terminal-text.d.ts +3 -0
- package/dist/src/cli/terminal-text.js +28 -0
- package/dist/src/cli/toolchain.d.ts +1 -0
- package/dist/src/cli/toolchain.js +1 -0
- package/dist/src/cli/usage-render.d.ts +1 -0
- package/dist/src/cli/usage-render.js +28 -1
- package/dist/src/cli/usage.d.ts +2 -2
- package/dist/src/cli/usage.js +89 -11
- package/dist/src/cli/watch-actions.js +2 -2
- package/dist/src/cli/watch-api-document.d.ts +430 -0
- package/dist/src/cli/watch-api-document.js +291 -0
- package/dist/src/cli/watch-api-run.d.ts +56 -0
- package/dist/src/cli/watch-api-run.js +112 -0
- package/dist/src/cli/watch-api.d.ts +17 -0
- package/dist/src/cli/watch-api.js +230 -0
- package/dist/src/cli/watch-board-view.js +19 -2
- package/dist/src/cli/watch-board.d.ts +4 -2
- package/dist/src/cli/watch-board.js +53 -43
- package/dist/src/cli/watch-card-forms.d.ts +1 -1
- package/dist/src/cli/watch-card-forms.js +11 -10
- package/dist/src/cli/watch-client.d.ts +1 -1
- package/dist/src/cli/watch-client.js +2 -11
- package/dist/src/cli/watch-header-view.d.ts +1 -5
- package/dist/src/cli/watch-header-view.js +17 -21
- package/dist/src/cli/watch-model.d.ts +5 -1
- package/dist/src/cli/watch-model.js +20 -11
- package/dist/src/cli/watch-page.js +15 -15
- package/dist/src/cli/watch-phases.d.ts +12 -3
- package/dist/src/cli/watch-phases.js +68 -11
- package/dist/src/cli/watch-pr-board.js +1 -1
- package/dist/src/cli/watch-pr-command.d.ts +1 -0
- package/dist/src/cli/watch-pull-request.js +18 -8
- package/dist/src/cli/watch-reads.d.ts +3 -0
- package/dist/src/cli/watch-reads.js +22 -20
- package/dist/src/cli/watch-render.d.ts +15 -0
- package/dist/src/cli/watch-run-input.d.ts +0 -1
- package/dist/src/cli/watch-run-input.js +0 -13
- package/dist/src/cli/watch-server.js +24 -10
- package/dist/src/cli/watch-view.d.ts +0 -3
- package/dist/src/cli/watch-view.js +13 -13
- package/dist/src/cli.js +73 -12
- package/dist/src/command-wait.d.ts +4 -0
- package/dist/src/command-wait.js +45 -0
- package/dist/src/configuration-change.d.ts +7 -0
- package/dist/src/configuration-change.js +14 -0
- package/dist/src/configuration-display.js +2 -0
- package/dist/src/configuration-schema.d.ts +14 -0
- package/dist/src/configuration-schema.js +6 -2
- package/dist/src/daemon/attachment-file.d.ts +6 -0
- package/dist/src/daemon/attachment-file.js +86 -0
- package/dist/src/daemon/attachments.d.ts +14 -0
- package/dist/src/daemon/attachments.js +17 -0
- package/dist/src/daemon/claude-recovery.d.ts +50 -0
- package/dist/src/daemon/claude-recovery.js +208 -0
- package/dist/src/daemon/claude-stream.d.ts +27 -0
- package/dist/src/daemon/claude-stream.js +63 -0
- package/dist/src/daemon/claude-turn.d.ts +11 -25
- package/dist/src/daemon/claude-turn.js +64 -81
- package/dist/src/daemon/claude-worker.d.ts +5 -4
- package/dist/src/daemon/claude-worker.js +127 -126
- package/dist/src/daemon/daemon-adapters.d.ts +9 -0
- package/dist/src/daemon/daemon-adapters.js +33 -5
- package/dist/src/daemon/daemon-loops.d.ts +5 -1
- package/dist/src/daemon/daemon-loops.js +18 -10
- package/dist/src/daemon/daemon.js +80 -61
- package/dist/src/daemon/github-attach.d.ts +2 -0
- package/dist/src/daemon/github-attach.js +222 -0
- package/dist/src/daemon/github-calls.js +1 -1
- package/dist/src/daemon/github-ci-filters.d.ts +6 -0
- package/dist/src/daemon/github-ci-filters.js +138 -0
- package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
- package/dist/src/daemon/github-ci-workflows.js +148 -0
- package/dist/src/daemon/github-ci.js +22 -4
- package/dist/src/daemon/github-issue-methods.d.ts +2 -0
- package/dist/src/daemon/github-issue-methods.js +37 -2
- package/dist/src/daemon/github-merge-call.d.ts +2 -1
- package/dist/src/daemon/github-merge-call.js +10 -3
- package/dist/src/daemon/github-merge.js +52 -24
- package/dist/src/daemon/github-methods.js +18 -4
- package/dist/src/daemon/github-operation.d.ts +4 -0
- package/dist/src/daemon/github-operation.js +6 -0
- package/dist/src/daemon/github-worker.d.ts +4 -0
- package/dist/src/daemon/github-worker.js +6 -0
- package/dist/src/daemon/issue-queue.d.ts +4 -1
- package/dist/src/daemon/issue-queue.js +76 -78
- package/dist/src/daemon/merge-turns.d.ts +10 -0
- package/dist/src/daemon/merge-turns.js +37 -0
- package/dist/src/daemon/notify-events.d.ts +6 -0
- package/dist/src/daemon/notify-events.js +84 -0
- package/dist/src/daemon/notify-journal.js +1 -1
- package/dist/src/daemon/notify-message.d.ts +4 -1
- package/dist/src/daemon/notify-message.js +36 -0
- package/dist/src/daemon/notify.d.ts +1 -1
- package/dist/src/daemon/notify.js +5 -77
- package/dist/src/daemon/operation-journal.d.ts +1 -0
- package/dist/src/daemon/queue-control.d.ts +13 -0
- package/dist/src/daemon/queue-control.js +86 -0
- package/dist/src/daemon/queue-journal.d.ts +20 -0
- package/dist/src/daemon/queue-journal.js +89 -0
- package/dist/src/daemon/queue-status.d.ts +9 -3
- package/dist/src/daemon/queue-status.js +17 -4
- package/dist/src/daemon/record-archive.d.ts +19 -0
- package/dist/src/daemon/record-archive.js +82 -0
- package/dist/src/daemon/record-sweep.d.ts +24 -0
- package/dist/src/daemon/record-sweep.js +147 -0
- package/dist/src/daemon/resume-index-lock.d.ts +13 -0
- package/dist/src/daemon/resume-index-lock.js +160 -0
- package/dist/src/daemon/resume-worktree.d.ts +7 -0
- package/dist/src/daemon/resume-worktree.js +29 -0
- package/dist/src/daemon/retrying-turn.d.ts +2 -12
- package/dist/src/daemon/retrying-turn.js +98 -172
- package/dist/src/daemon/schedule-control.d.ts +26 -0
- package/dist/src/daemon/schedule-control.js +110 -0
- package/dist/src/daemon/schedule-journal.d.ts +2 -15
- package/dist/src/daemon/schedule-journal.js +67 -4
- package/dist/src/daemon/settings-reload.d.ts +26 -0
- package/dist/src/daemon/settings-reload.js +82 -0
- package/dist/src/daemon/ticket-registration.d.ts +2 -7
- package/dist/src/daemon/ticket-registration.js +15 -81
- package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
- package/dist/src/daemon/turn-attempt-journal.js +136 -0
- package/dist/src/daemon/turn-attempts.d.ts +47 -0
- package/dist/src/daemon/turn-attempts.js +166 -0
- package/dist/src/daemon/turn-details.d.ts +4 -0
- package/dist/src/daemon/turn-details.js +33 -0
- package/dist/src/daemon/turn-report.d.ts +8 -1
- package/dist/src/daemon/turn-report.js +39 -0
- package/dist/src/daemon/work-tree.d.ts +9 -0
- package/dist/src/daemon/work-tree.js +20 -0
- package/dist/src/daemon/worker-profile.d.ts +9 -0
- package/dist/src/daemon/worker-profile.js +44 -0
- package/dist/src/daemon/workflow-generations.d.ts +7 -4
- package/dist/src/daemon/workflow-generations.js +22 -14
- package/dist/src/daemon/workflow-reload.d.ts +41 -0
- package/dist/src/daemon/workflow-reload.js +244 -0
- package/dist/src/daemon/workflow-replay.d.ts +2 -1
- package/dist/src/daemon/workflow-replay.js +7 -3
- package/dist/src/daemon/workflow-schedules.d.ts +3 -0
- package/dist/src/daemon/workflow-schedules.js +98 -45
- package/dist/src/daemon/worktree-processes.d.ts +9 -0
- package/dist/src/daemon/worktree-processes.js +151 -0
- package/dist/src/doctor-report.d.ts +4 -6
- package/dist/src/doctor-report.js +8 -13
- package/dist/src/harness-start.d.ts +15 -0
- package/dist/src/harness-start.js +37 -0
- package/dist/src/host/command-wait.d.ts +38 -0
- package/dist/src/host/command-wait.js +115 -0
- package/dist/src/host/start-delivery.d.ts +5 -7
- package/dist/src/host/start-delivery.js +3 -15
- package/dist/src/host/step-bridge-host.d.ts +2 -0
- package/dist/src/host/step-bridge-host.js +21 -0
- package/dist/src/operation-identity.d.ts +1 -0
- package/dist/src/operation-identity.js +4 -0
- package/dist/src/private-config.schema.json +71 -0
- package/dist/src/project-config.schema.json +40 -0
- package/dist/src/project-settings.d.ts +16 -2
- package/dist/src/project-settings.js +35 -1
- package/dist/src/queue-spend.d.ts +4 -0
- package/dist/src/queue-spend.js +21 -0
- package/dist/src/queue-state.d.ts +26 -0
- package/dist/src/queue-state.js +34 -0
- package/dist/src/record-retention.d.ts +46 -0
- package/dist/src/record-retention.js +81 -0
- package/dist/src/relay/relay-service.d.ts +3 -0
- package/dist/src/relay/relay-service.js +5 -3
- package/dist/src/report-ci.d.ts +164 -0
- package/dist/src/report-ci.js +32 -0
- package/dist/src/report-effects.d.ts +15 -0
- package/dist/src/report-effects.js +212 -0
- package/dist/src/report-flow-types.d.ts +38 -0
- package/dist/src/report-flow-types.js +7 -0
- package/dist/src/report-flow.d.ts +43 -0
- package/dist/src/report-flow.js +193 -0
- package/dist/src/report-operator.d.ts +67 -0
- package/dist/src/report-operator.js +25 -0
- package/dist/src/report-quality.d.ts +221 -0
- package/dist/src/report-quality.js +94 -0
- package/dist/src/report-ready.d.ts +23 -0
- package/dist/src/report-ready.js +117 -0
- package/dist/src/report-statistics.d.ts +2 -1
- package/dist/src/report-statistics.js +40 -4
- package/dist/src/report.d.ts +588 -47
- package/dist/src/report.js +156 -41
- package/dist/src/schedule-progress.d.ts +33 -0
- package/dist/src/schedule-progress.js +45 -0
- package/dist/src/service/active-tickets.js +11 -6
- package/dist/src/service/command-waits.d.ts +34 -0
- package/dist/src/service/command-waits.js +86 -0
- package/dist/src/service/dispatcher.d.ts +4 -0
- package/dist/src/service/dispatcher.js +33 -0
- package/dist/src/service/expiry.js +2 -0
- package/dist/src/service/gateway.js +3 -1
- package/dist/src/service/queue-handlers.d.ts +4 -0
- package/dist/src/service/queue-handlers.js +32 -0
- package/dist/src/service/queue-spend.d.ts +6 -0
- package/dist/src/service/queue-spend.js +68 -0
- package/dist/src/service/registry.d.ts +28 -2
- package/dist/src/service/registry.js +48 -0
- package/dist/src/service/replies.d.ts +22 -8
- package/dist/src/service/replies.js +16 -2
- package/dist/src/service/run-handlers.js +37 -12
- package/dist/src/service/run-metadata.d.ts +15 -1
- package/dist/src/service/run-metadata.js +93 -8
- package/dist/src/service/service.d.ts +11 -3
- package/dist/src/service/service.js +22 -37
- package/dist/src/service/submission-handler.js +18 -4
- package/dist/src/service/worker-port.js +9 -1
- package/dist/src/service/worker-totals.d.ts +6 -0
- package/dist/src/service/worker-totals.js +8 -1
- package/dist/src/service/workflow-reload.d.ts +35 -0
- package/dist/src/service/workflow-reload.js +52 -0
- package/dist/src/step-bridge.d.ts +20 -0
- package/dist/src/steps/command-wait.d.ts +12 -0
- package/dist/src/steps/command-wait.js +26 -0
- package/dist/src/steps/open-decision-wait.d.ts +2 -0
- package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
- package/dist/src/steps/review-by-person.js +4 -1
- package/dist/src/steps/wait-for-command.d.ts +3 -0
- package/dist/src/steps/wait-for-command.js +17 -0
- package/dist/src/steps/wait-for-decision.js +1 -1
- package/dist/src/storage/command-wait-rows.d.ts +30 -0
- package/dist/src/storage/command-wait-rows.js +96 -0
- package/dist/src/storage/decision-snapshots.d.ts +8 -2
- package/dist/src/storage/decision-snapshots.js +16 -8
- package/dist/src/storage/domain-changes.d.ts +3 -1
- package/dist/src/storage/domain-changes.js +4 -0
- package/dist/src/storage/domain-schema.d.ts +16 -1
- package/dist/src/storage/domain-schema.js +35 -4
- package/dist/src/storage/effect-schema.d.ts +2 -0
- package/dist/src/storage/effect-schema.js +9 -0
- package/dist/src/storage/projection-changes.d.ts +12 -0
- package/dist/src/storage/projection-changes.js +194 -0
- package/dist/src/storage/projection-lookups.d.ts +7 -0
- package/dist/src/storage/projection-lookups.js +30 -0
- package/dist/src/testing-requests.d.ts +15 -0
- package/dist/src/testing-requests.js +93 -0
- package/dist/src/testing.d.ts +4 -10
- package/dist/src/testing.js +33 -86
- package/dist/src/worker-usage.d.ts +1 -0
- package/dist/src/worker-usage.js +3 -0
- package/dist/src/workflow-candidate.d.ts +1 -0
- package/dist/src/workflow-candidate.js +12 -1
- package/dist/src/workflow-contracts.d.ts +1 -0
- package/dist/src/workflow-contracts.js +9 -2
- package/dist/src/workflow-declaration.d.ts +1 -0
- package/dist/src/workflow-declaration.js +3 -0
- package/dist/src/workflow-discovery.d.ts +2 -13
- package/dist/src/workflow-discovery.js +1 -13
- package/dist/src/workflow-refusals.js +0 -1
- package/dist/src/workflows.d.ts +1 -1
- package/dist/src/workflows.js +1 -1
- package/dist/tools/compile-workflows.js +3 -0
- package/dist/tools/project-configuration.d.ts +7 -2
- package/dist/tools/project-configuration.js +30 -1
- package/dist/tools/red-green-command.d.ts +1 -0
- package/dist/tools/red-green-command.js +174 -0
- package/dist/tools/red-green-process.d.ts +10 -0
- package/dist/tools/red-green-process.js +59 -0
- package/dist/tools/self-host-processes.d.ts +2 -0
- package/dist/tools/self-host-processes.js +69 -26
- package/dist/tools/self-host-project.d.ts +2 -0
- package/dist/tools/self-host-project.js +30 -77
- package/dist/tools/self-host-runtime.js +1 -1
- package/dist/tools/self-host.js +76 -34
- package/dist/tools/template-command.d.ts +1 -0
- package/dist/tools/template-command.js +160 -0
- package/dist/tools/template-copy.d.ts +11 -0
- package/dist/tools/template-copy.js +130 -0
- package/dist/tools/template-files.d.ts +17 -0
- package/dist/tools/template-files.js +74 -0
- package/dist/tools/workflow-bundles.js +1 -0
- package/dist/tools/workflow-candidate-counts.js +2 -2
- package/dist/tools/workflow-candidate-program.d.ts +4 -2
- package/dist/tools/workflow-candidate-program.js +30 -11
- package/dist/tools/workflow-candidate-shape.d.ts +3 -0
- package/dist/tools/workflow-candidate-shape.js +90 -0
- package/dist/tools/workflow-candidate-source.d.ts +1 -0
- package/dist/tools/workflow-candidate-source.js +26 -4
- package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
- package/dist/tools/workflow-candidate-syntax.js +24 -94
- package/dist/tools/workflow-command.js +1 -2
- package/dist/tools/workflow-folders.d.ts +2 -3
- package/dist/tools/workflow-folders.js +1 -1
- package/package.json +17 -9
- package/templates/develop/README.md +93 -4
- package/templates/develop/develop/flow-tests.ts +38 -1
- package/templates/develop/develop/interruption-tests.ts +157 -0
- package/templates/develop/develop/local.ts +1 -0
- package/templates/develop/develop/option-tests.ts +138 -9
- package/templates/develop/develop/pull.ts +5 -1
- package/templates/develop/develop/red-green.ts +55 -0
- package/templates/develop/develop/refusal-tests.ts +77 -0
- package/templates/develop/develop/report.ts +31 -11
- package/templates/develop/develop/round.ts +1 -0
- package/templates/develop/develop/scenarios.ts +27 -0
- package/templates/develop/develop/turn.ts +28 -7
- package/templates/develop/develop.declaration.json +1 -1
- package/templates/develop/develop.test.ts +4 -0
- package/templates/develop/template.json +29 -2
- package/templates/idea-to-issue/README.md +27 -0
- package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
- package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
- package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
- package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
- package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
- package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
- package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
- package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
- package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
- package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
- package/templates/idea-to-issue/idea-to-issue.ts +49 -0
- package/templates/idea-to-issue/template.json +81 -0
- package/templates/review-day/README.md +97 -0
- package/templates/review-day/review-day/critique.ts +58 -0
- package/templates/review-day/review-day/decision.ts +29 -0
- package/templates/review-day/review-day/facts.ts +69 -0
- package/templates/review-day/review-day/label.ts +29 -0
- package/templates/review-day/review-day/next.ts +28 -0
- package/templates/review-day/review-day/post.ts +27 -0
- package/templates/review-day/review-day/report.ts +98 -0
- package/templates/review-day/review-day/rules.ts +12 -0
- package/templates/review-day/review-day/scenarios.ts +76 -0
- package/templates/review-day/review-day/turn.ts +73 -0
- package/templates/review-day/review-day.declaration.json +16 -0
- package/templates/review-day/review-day.test.ts +362 -0
- package/templates/review-day/review-day.ts +37 -0
- package/templates/review-day/template.json +56 -0
- package/dist/src/cli/watch-start-view.d.ts +0 -18
- package/dist/src/cli/watch-start-view.js +0 -77
- package/dist/src/steps/decision-gate.d.ts +0 -3
- package/dist/workflows/develop-prd.d.ts +0 -7
- package/dist/workflows/develop-prd.js +0 -165
- package/dist/workflows/develop-ticket.d.ts +0 -9
- package/dist/workflows/develop-ticket.js +0 -207
- package/dist/workflows/issue-labels.d.ts +0 -16
- package/dist/workflows/issue-labels.js +0 -70
- package/dist/workflows/ticket-prompts.d.ts +0 -35
- package/dist/workflows/ticket-prompts.js +0 -147
|
@@ -1,57 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { queueStatus, spendNotice } from "./queue-status.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { readFile,
|
|
3
|
+
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { setTimeout as delay } from "node:timers/promises";
|
|
6
6
|
import { send } from "../cli/client.js";
|
|
7
7
|
import { record } from "../protocol.js";
|
|
8
8
|
import { runIdentity } from "../service/submission-handler.js";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
const held = record(value);
|
|
13
|
-
if (typeof held?.["mark"] !== "string" ||
|
|
14
|
-
typeof held["startedAt"] !== "number" ||
|
|
15
|
-
!Number.isFinite(held["startedAt"]) ||
|
|
16
|
-
held["startedAt"] <= 0)
|
|
17
|
-
throw new Error("invalid-queue-commands");
|
|
18
|
-
return { mark: held["mark"], startedAt: held["startedAt"] };
|
|
19
|
-
}
|
|
20
|
-
function identifiers(value) {
|
|
21
|
-
if (!Array.isArray(value) ||
|
|
22
|
-
!value.every((id) => typeof id === "string" && id.length > 0))
|
|
23
|
-
throw new Error("invalid-queue-commands");
|
|
24
|
-
return value.map(String);
|
|
25
|
-
}
|
|
26
|
-
async function journal(directory) {
|
|
27
|
-
const file = join(directory, "queue-commands.json");
|
|
28
|
-
const text = await readFile(file, "utf8").catch((error) => {
|
|
29
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
30
|
-
return "[]";
|
|
31
|
-
throw error;
|
|
32
|
-
});
|
|
33
|
-
const value = JSON.parse(text);
|
|
34
|
-
const saved = record(value);
|
|
35
|
-
const commands = new Set(identifiers(Array.isArray(value) ? value : saved?.["commands"]));
|
|
36
|
-
const failures = Array.isArray(value) ? [] : identifiers(saved?.["failures"]);
|
|
37
|
-
if (failures.length > 3)
|
|
38
|
-
throw new Error("invalid-queue-commands");
|
|
39
|
-
const pending = {
|
|
40
|
-
commands,
|
|
41
|
-
failures,
|
|
42
|
-
retryCommandId: undefined,
|
|
43
|
-
selfUpdateHold: savedHold(saved?.["selfUpdateHold"]),
|
|
44
|
-
save: async () => {
|
|
45
|
-
await writeFile(`${file}.tmp`, JSON.stringify({
|
|
46
|
-
commands: [...commands],
|
|
47
|
-
failures,
|
|
48
|
-
selfUpdateHold: pending.selfUpdateHold,
|
|
49
|
-
}) + "\n", { mode: 0o600, flush: true });
|
|
50
|
-
await rename(`${file}.tmp`, file);
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
return pending;
|
|
54
|
-
}
|
|
9
|
+
import { queueFailureLimit } from "../queue-state.js";
|
|
10
|
+
import { queueJournal } from "./queue-journal.js";
|
|
11
|
+
import { queueControlPort, queueStopped, } from "./queue-control.js";
|
|
55
12
|
function terminal(run) {
|
|
56
13
|
return ["completed", "failed", "cancelled"].includes(String(run["state"]));
|
|
57
14
|
}
|
|
@@ -111,11 +68,18 @@ function handedOff(run) {
|
|
|
111
68
|
outcome?.["kind"] === "handoff_requested" &&
|
|
112
69
|
outcome["issueUpdate"] !== "failed");
|
|
113
70
|
}
|
|
114
|
-
|
|
71
|
+
function limited(pending) {
|
|
72
|
+
return pending.failures.length >= queueFailureLimit;
|
|
115
73
|
}
|
|
116
|
-
function
|
|
117
|
-
|
|
118
|
-
|
|
74
|
+
function stopNotice() {
|
|
75
|
+
let noticed = false;
|
|
76
|
+
return (pending) => {
|
|
77
|
+
const stopped = limited(pending);
|
|
78
|
+
if (stopped && !noticed)
|
|
79
|
+
process.stderr.write(`Queue stopped: 3 runs in a row ended without handoff: ${pending.failures.join(", ")}\n`);
|
|
80
|
+
noticed = stopped;
|
|
81
|
+
return stopped;
|
|
82
|
+
};
|
|
119
83
|
}
|
|
120
84
|
async function retired(pending, ending) {
|
|
121
85
|
const { commandId, run } = ending;
|
|
@@ -125,14 +89,12 @@ async function retired(pending, ending) {
|
|
|
125
89
|
else
|
|
126
90
|
pending.failures.push(runIdentity(commandId));
|
|
127
91
|
await pending.save();
|
|
128
|
-
|
|
129
|
-
if (handedOff(run))
|
|
92
|
+
if (handedOff(run) || limited(pending))
|
|
130
93
|
return;
|
|
131
94
|
const outcome = record(run["outcome"]);
|
|
132
95
|
process.stderr.write(`Queue run ended ${String(outcome?.["kind"] ?? run["state"])}${outcome?.["issueUpdate"] === "failed" ? "; issueUpdate: failed" : ""}: ${runIdentity(commandId)}; continuing\n`);
|
|
133
96
|
}
|
|
134
97
|
async function completed(settings, pending) {
|
|
135
|
-
checkFailureLimit(pending);
|
|
136
98
|
let pendingRuns = 0;
|
|
137
99
|
const endings = [];
|
|
138
100
|
const tracked = [...pending.commands].filter((commandId) => commandId !== pending.retryCommandId);
|
|
@@ -145,7 +107,8 @@ async function completed(settings, pending) {
|
|
|
145
107
|
endings.sort((first, second) => Number(first.run["terminalSequence"] ?? first.run["latestSequence"] ?? 0) -
|
|
146
108
|
Number(second.run["terminalSequence"] ?? second.run["latestSequence"] ?? 0));
|
|
147
109
|
for (const ending of endings)
|
|
148
|
-
|
|
110
|
+
if (!limited(pending))
|
|
111
|
+
await retired(pending, ending);
|
|
149
112
|
return pendingRuns;
|
|
150
113
|
}
|
|
151
114
|
async function submit(settings, pending) {
|
|
@@ -155,7 +118,7 @@ async function submit(settings, pending) {
|
|
|
155
118
|
const reply = await send(settings.runtimeDirectory, {
|
|
156
119
|
operation: "run",
|
|
157
120
|
parameters: {
|
|
158
|
-
workflowId: settings.workflowId
|
|
121
|
+
workflowId: settings.workflowId,
|
|
159
122
|
source: "ticket",
|
|
160
123
|
submission: { ticket: "next" },
|
|
161
124
|
commandId,
|
|
@@ -178,6 +141,7 @@ async function submit(settings, pending) {
|
|
|
178
141
|
"workflow-active",
|
|
179
142
|
"ticket-active",
|
|
180
143
|
"ticket-dependency-open",
|
|
144
|
+
"queue-spend-limit",
|
|
181
145
|
].includes(reply.error.code))
|
|
182
146
|
throw new Error(reply.error.code);
|
|
183
147
|
pending.commands.delete(commandId);
|
|
@@ -229,20 +193,54 @@ async function admission(settings, attempt) {
|
|
|
229
193
|
const file = join(settings.directory, "queue-submission");
|
|
230
194
|
await writeFile(file, "", { mode: 0o600 });
|
|
231
195
|
try {
|
|
232
|
-
if (!(await attempt.holding()) &&
|
|
196
|
+
if (!(await attempt.holding()) &&
|
|
197
|
+
!attempt.signal.aborted &&
|
|
198
|
+
attempt.pending.drain === undefined)
|
|
233
199
|
await submit(settings, attempt.pending);
|
|
234
200
|
}
|
|
235
201
|
finally {
|
|
236
202
|
await rm(file, { force: true });
|
|
237
203
|
}
|
|
238
204
|
}
|
|
239
|
-
async function
|
|
240
|
-
const
|
|
205
|
+
async function drained(pending, busy) {
|
|
206
|
+
const { drain } = pending;
|
|
207
|
+
if (drain === undefined || drain.drainedAt !== null || busy > 0)
|
|
208
|
+
return;
|
|
209
|
+
pending.drain = { ...drain, drainedAt: Date.now() };
|
|
210
|
+
await pending.save();
|
|
211
|
+
process.stderr.write("Queue drained\n");
|
|
212
|
+
}
|
|
213
|
+
async function admissible(settings, checks) {
|
|
214
|
+
const { pending, stop } = checks;
|
|
215
|
+
const pendingRuns = stop(pending) ? 0 : await completed(settings, pending);
|
|
216
|
+
if (stop(pending) && pending.drain === undefined)
|
|
217
|
+
return false;
|
|
218
|
+
const status = await queueStatus(settings);
|
|
219
|
+
await drained(pending, status.busy);
|
|
220
|
+
if (stop(pending) || pending.drain !== undefined)
|
|
221
|
+
return false;
|
|
222
|
+
const spend = checks.notice(status);
|
|
223
|
+
const updating = await checks.update(status.busy);
|
|
224
|
+
const lanes = settings.lanes ?? 1;
|
|
225
|
+
if (Math.max(pendingRuns, status.busy) >= lanes || status.busy < pendingRuns)
|
|
226
|
+
return false;
|
|
227
|
+
return !updating && !spend;
|
|
228
|
+
}
|
|
229
|
+
async function cycle(settings, run) {
|
|
230
|
+
const { signal, pending } = run;
|
|
241
231
|
if (settings.resetQueue === true) {
|
|
242
232
|
pending.failures.length = 0;
|
|
233
|
+
pending.drain = undefined;
|
|
243
234
|
await pending.save();
|
|
244
235
|
}
|
|
245
|
-
const
|
|
236
|
+
const update = updateHold(settings.directory, pending);
|
|
237
|
+
const notice = spendNotice();
|
|
238
|
+
const stop = stopNotice();
|
|
239
|
+
const holding = async (activeRuns = 0) => {
|
|
240
|
+
const updating = await update(activeRuns);
|
|
241
|
+
const spend = notice(await queueStatus(settings));
|
|
242
|
+
return updating || spend;
|
|
243
|
+
};
|
|
246
244
|
await rm(join(settings.directory, "queue-submission"), { force: true });
|
|
247
245
|
await recover(settings, pending);
|
|
248
246
|
const stopped = () => signal.aborted;
|
|
@@ -250,17 +248,7 @@ async function cycle(settings, signal) {
|
|
|
250
248
|
await delay(settings.intervalMilliseconds, undefined, { signal }).catch(() => undefined);
|
|
251
249
|
if (stopped())
|
|
252
250
|
return;
|
|
253
|
-
|
|
254
|
-
const lanes = settings.lanes ?? 1;
|
|
255
|
-
if (pendingRuns >= lanes) {
|
|
256
|
-
await holding(pendingRuns);
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
const activeRuns = await activeQueueRuns(settings);
|
|
260
|
-
const held = await holding(activeRuns);
|
|
261
|
-
if (activeRuns >= lanes || activeRuns < pendingRuns)
|
|
262
|
-
continue;
|
|
263
|
-
if (held)
|
|
251
|
+
if (!(await admissible(settings, { pending, stop, notice, update })))
|
|
264
252
|
continue;
|
|
265
253
|
if (stopped())
|
|
266
254
|
return;
|
|
@@ -268,16 +256,26 @@ async function cycle(settings, signal) {
|
|
|
268
256
|
}
|
|
269
257
|
}
|
|
270
258
|
export function issueQueue(settings) {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
259
|
+
const runtime = {
|
|
260
|
+
pending: undefined,
|
|
261
|
+
error: undefined,
|
|
262
|
+
stopping: new AbortController(),
|
|
263
|
+
};
|
|
264
|
+
const loaded = queueJournal(settings.directory).then((pending) => {
|
|
265
|
+
runtime.pending = pending;
|
|
266
|
+
return pending;
|
|
267
|
+
});
|
|
268
|
+
const running = loaded
|
|
269
|
+
.then((pending) => cycle(settings, { signal: runtime.stopping.signal, pending }))
|
|
270
|
+
.catch((error) => {
|
|
271
|
+
queueStopped(runtime, error);
|
|
276
272
|
});
|
|
277
273
|
return {
|
|
278
274
|
stop: async () => {
|
|
279
|
-
stopping.abort();
|
|
275
|
+
runtime.stopping.abort();
|
|
280
276
|
await running;
|
|
281
277
|
},
|
|
278
|
+
port: queueControlPort(runtime),
|
|
279
|
+
ready: loaded.then(() => undefined, () => undefined),
|
|
282
280
|
};
|
|
283
281
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Turn {
|
|
2
|
+
readonly key: string;
|
|
3
|
+
readonly signal: AbortSignal;
|
|
4
|
+
}
|
|
5
|
+
export interface MergeTurn {
|
|
6
|
+
readonly repository: string;
|
|
7
|
+
readonly signal: AbortSignal;
|
|
8
|
+
}
|
|
9
|
+
export declare function inTurn<Result>(turn: Turn, work: () => Promise<Result>): Promise<Result>;
|
|
10
|
+
export declare function inMergeTurn<Result>(turn: MergeTurn, work: () => Promise<Result>): Promise<Result>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const turns = new Map();
|
|
2
|
+
function waited(previous, signal) {
|
|
3
|
+
signal.throwIfAborted();
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const aborted = () => {
|
|
6
|
+
const reason = signal.reason;
|
|
7
|
+
reject(reason instanceof Error ? reason : new Error("merge-turn-aborted"));
|
|
8
|
+
};
|
|
9
|
+
signal.addEventListener("abort", aborted, { once: true });
|
|
10
|
+
previous.then(() => {
|
|
11
|
+
signal.removeEventListener("abort", aborted);
|
|
12
|
+
resolve(undefined);
|
|
13
|
+
}, reject);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
export async function inTurn(turn, work) {
|
|
17
|
+
const previous = turns.get(turn.key) ?? Promise.resolve(undefined);
|
|
18
|
+
const ended = Promise.withResolvers();
|
|
19
|
+
const queued = previous
|
|
20
|
+
.then(() => ended.promise)
|
|
21
|
+
.then(() => {
|
|
22
|
+
if (turns.get(turn.key) === queued)
|
|
23
|
+
turns.delete(turn.key);
|
|
24
|
+
return undefined;
|
|
25
|
+
});
|
|
26
|
+
turns.set(turn.key, queued);
|
|
27
|
+
try {
|
|
28
|
+
await waited(previous, turn.signal);
|
|
29
|
+
return await work();
|
|
30
|
+
}
|
|
31
|
+
finally {
|
|
32
|
+
ended.resolve(undefined);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function inMergeTurn(turn, work) {
|
|
36
|
+
return inTurn({ key: turn.repository, signal: turn.signal }, work);
|
|
37
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { type NotifyEvent } from "./notify-message.ts";
|
|
2
|
+
export type OutcomeCache = Map<string, {
|
|
3
|
+
readonly latestSequence: number;
|
|
4
|
+
readonly event: NotifyEvent | undefined;
|
|
5
|
+
}>;
|
|
6
|
+
export declare function notificationEvents(runtimeDirectory: string, cache: OutcomeCache): Promise<readonly NotifyEvent[]>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { send } from "../cli/client.js";
|
|
2
|
+
import { record } from "../protocol.js";
|
|
3
|
+
import { decisionNotification, outcomeNotification, scheduleStopNotifications, } from "./notify-message.js";
|
|
4
|
+
async function request(runtimeDirectory, request) {
|
|
5
|
+
const reply = await send(runtimeDirectory, request);
|
|
6
|
+
if (reply.kind === "error")
|
|
7
|
+
throw new Error(reply.error.code);
|
|
8
|
+
return reply.result;
|
|
9
|
+
}
|
|
10
|
+
function rows(value) {
|
|
11
|
+
if (!Array.isArray(value))
|
|
12
|
+
throw new Error("invalid-notify-list");
|
|
13
|
+
return value.map((item) => {
|
|
14
|
+
const row = record(item);
|
|
15
|
+
if (row === undefined)
|
|
16
|
+
throw new Error("invalid-notify-row");
|
|
17
|
+
return row;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
async function scheduleNotifications(runtimeDirectory) {
|
|
21
|
+
const reply = await send(runtimeDirectory, {
|
|
22
|
+
operation: "schedule",
|
|
23
|
+
parameters: { action: "list" },
|
|
24
|
+
});
|
|
25
|
+
if (reply.kind !== "error")
|
|
26
|
+
return rows(reply.result["stops"]).flatMap(scheduleStopNotifications);
|
|
27
|
+
if (reply.error.code === "schedules-unavailable")
|
|
28
|
+
return [];
|
|
29
|
+
throw new Error(reply.error.code);
|
|
30
|
+
}
|
|
31
|
+
async function observedOutcome(summary, source) {
|
|
32
|
+
const { runtimeDirectory, cache } = source;
|
|
33
|
+
const latestSequence = summary["latestSequence"];
|
|
34
|
+
if (typeof latestSequence !== "number")
|
|
35
|
+
throw new Error("invalid-notify-sequence");
|
|
36
|
+
const runId = String(summary["runId"]);
|
|
37
|
+
const previous = cache.get(runId);
|
|
38
|
+
if (previous !== undefined && previous.latestSequence === latestSequence)
|
|
39
|
+
return previous.event;
|
|
40
|
+
const status = await request(runtimeDirectory, {
|
|
41
|
+
operation: "status",
|
|
42
|
+
parameters: { runId },
|
|
43
|
+
});
|
|
44
|
+
const run = record(status["run"]);
|
|
45
|
+
if (run === undefined)
|
|
46
|
+
throw new Error("invalid-notify-status");
|
|
47
|
+
const event = outcomeNotification(run);
|
|
48
|
+
cache.set(runId, { latestSequence, event });
|
|
49
|
+
return event;
|
|
50
|
+
}
|
|
51
|
+
export async function notificationEvents(runtimeDirectory, cache) {
|
|
52
|
+
const found = [];
|
|
53
|
+
const runs = new Map();
|
|
54
|
+
let after = "";
|
|
55
|
+
do {
|
|
56
|
+
const page = await request(runtimeDirectory, {
|
|
57
|
+
operation: "status",
|
|
58
|
+
parameters: { after, limit: 500 },
|
|
59
|
+
});
|
|
60
|
+
for (const summary of rows(page["runs"])) {
|
|
61
|
+
runs.set(String(summary["runId"]), summary);
|
|
62
|
+
const outcome = await observedOutcome(summary, {
|
|
63
|
+
runtimeDirectory,
|
|
64
|
+
cache,
|
|
65
|
+
});
|
|
66
|
+
if (outcome !== undefined)
|
|
67
|
+
found.push(outcome);
|
|
68
|
+
}
|
|
69
|
+
const next = page["next"];
|
|
70
|
+
if (next !== null && (typeof next !== "string" || next <= after))
|
|
71
|
+
throw new Error("invalid-notify-page");
|
|
72
|
+
after = next;
|
|
73
|
+
} while (after !== null);
|
|
74
|
+
const questions = await request(runtimeDirectory, {
|
|
75
|
+
operation: "questions",
|
|
76
|
+
parameters: {},
|
|
77
|
+
});
|
|
78
|
+
for (const decision of rows(questions["decisions"])) {
|
|
79
|
+
const run = runs.get(String(decision["runId"]));
|
|
80
|
+
if (run !== undefined)
|
|
81
|
+
found.push(decisionNotification(run, decision));
|
|
82
|
+
}
|
|
83
|
+
return [...found, ...(await scheduleNotifications(runtimeDirectory))];
|
|
84
|
+
}
|
|
@@ -46,7 +46,7 @@ function notification(value) {
|
|
|
46
46
|
const message = record(value);
|
|
47
47
|
if (message === undefined)
|
|
48
48
|
return false;
|
|
49
|
-
return (["decision", "blocked", "failed"].includes(String(message["event"])) &&
|
|
49
|
+
return (["decision", "blocked", "failed", "schedule-stopped"].includes(String(message["event"])) &&
|
|
50
50
|
["event", "runId", "workflowId", "subject", "body"].every((field) => typeof message[field] === "string"));
|
|
51
51
|
}
|
|
52
52
|
function deliveryState(value) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type Fields } from "../protocol.ts";
|
|
2
2
|
export interface Notification {
|
|
3
|
-
readonly event: "decision" | "blocked" | "failed";
|
|
3
|
+
readonly event: "decision" | "blocked" | "failed" | "schedule-stopped";
|
|
4
4
|
readonly runId: string;
|
|
5
5
|
readonly workflowId: string;
|
|
6
6
|
readonly subject: string;
|
|
7
7
|
readonly body: string;
|
|
8
|
+
readonly failedRunIds?: readonly string[];
|
|
8
9
|
}
|
|
9
10
|
export interface NotifyEvent {
|
|
10
11
|
readonly key: string;
|
|
@@ -12,3 +13,5 @@ export interface NotifyEvent {
|
|
|
12
13
|
}
|
|
13
14
|
export declare function decisionNotification(run: Fields, decision: Fields): NotifyEvent;
|
|
14
15
|
export declare function outcomeNotification(run: Fields): NotifyEvent | undefined;
|
|
16
|
+
export declare function stoppedScheduleNotification(schedule: Fields): NotifyEvent | undefined;
|
|
17
|
+
export declare function scheduleStopNotifications(schedule: Fields): readonly NotifyEvent[];
|
|
@@ -47,3 +47,39 @@ export function outcomeNotification(run) {
|
|
|
47
47
|
}),
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
|
+
export function stoppedScheduleNotification(schedule) {
|
|
51
|
+
if (schedule["state"] !== "stopped")
|
|
52
|
+
return undefined;
|
|
53
|
+
const failedRunIds = schedule["failedRunIds"];
|
|
54
|
+
if (!Array.isArray(failedRunIds) || failedRunIds.length !== 3)
|
|
55
|
+
throw new Error("invalid-stopped-schedule");
|
|
56
|
+
const ids = failedRunIds.map((runId) => String(runId));
|
|
57
|
+
const workflowId = String(schedule["workflowId"]);
|
|
58
|
+
return {
|
|
59
|
+
key: `schedule-stopped:${workflowId}:${String(schedule["stoppedSlot"])}`,
|
|
60
|
+
message: {
|
|
61
|
+
...message({ workflowId, runId: ids[2] }, {
|
|
62
|
+
event: "schedule-stopped",
|
|
63
|
+
body: body(`Schedule stopped after three failed runs:\n${ids.join("\n")}`, [`cloving schedule resume ${workflowId}`]),
|
|
64
|
+
}),
|
|
65
|
+
failedRunIds: ids,
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export function scheduleStopNotifications(schedule) {
|
|
70
|
+
const stops = schedule["stops"];
|
|
71
|
+
if (stops === undefined) {
|
|
72
|
+
const event = stoppedScheduleNotification(schedule);
|
|
73
|
+
return event === undefined ? [] : [event];
|
|
74
|
+
}
|
|
75
|
+
if (!Array.isArray(stops))
|
|
76
|
+
throw new Error("invalid-stopped-schedule");
|
|
77
|
+
return stops.flatMap((stop) => {
|
|
78
|
+
const event = stoppedScheduleNotification({
|
|
79
|
+
...record(stop),
|
|
80
|
+
workflowId: schedule["workflowId"],
|
|
81
|
+
state: "stopped",
|
|
82
|
+
});
|
|
83
|
+
return event === undefined ? [] : [event];
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -1,27 +1,9 @@
|
|
|
1
1
|
import { recordedNotifications, saveNotifications, } from "./notify-journal.js";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
-
import { send } from "../cli/client.js";
|
|
5
4
|
import { deliveryBackoffMilliseconds } from "../delivery-backoff.js";
|
|
6
|
-
import {
|
|
7
|
-
import { decisionNotification, outcomeNotification, } from "./notify-message.js";
|
|
5
|
+
import { notificationEvents } from "./notify-events.js";
|
|
8
6
|
const retentionMilliseconds = 7 * 24 * 60 * 60_000;
|
|
9
|
-
async function request(runtimeDirectory, request) {
|
|
10
|
-
const reply = await send(runtimeDirectory, request);
|
|
11
|
-
if (reply.kind === "error")
|
|
12
|
-
throw new Error(reply.error.code);
|
|
13
|
-
return reply.result;
|
|
14
|
-
}
|
|
15
|
-
function rows(value) {
|
|
16
|
-
if (!Array.isArray(value))
|
|
17
|
-
throw new Error("invalid-notify-list");
|
|
18
|
-
return value.map((item) => {
|
|
19
|
-
const row = record(item);
|
|
20
|
-
if (row === undefined)
|
|
21
|
-
throw new Error("invalid-notify-row");
|
|
22
|
-
return row;
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
7
|
async function flushNotifications(journal) {
|
|
26
8
|
if (journal.unsaved.size === 0)
|
|
27
9
|
return;
|
|
@@ -36,60 +18,6 @@ async function finishDelivery(journal, entry) {
|
|
|
36
18
|
journal.unsaved.set(entry.key, entry);
|
|
37
19
|
await flushNotifications(journal);
|
|
38
20
|
}
|
|
39
|
-
async function observedOutcome(summary, source) {
|
|
40
|
-
const { runtimeDirectory, cache } = source;
|
|
41
|
-
const latestSequence = summary["latestSequence"];
|
|
42
|
-
if (typeof latestSequence !== "number")
|
|
43
|
-
throw new Error("invalid-notify-sequence");
|
|
44
|
-
const runId = String(summary["runId"]);
|
|
45
|
-
const previous = cache.get(runId);
|
|
46
|
-
if (previous !== undefined && previous.latestSequence === latestSequence)
|
|
47
|
-
return previous.event;
|
|
48
|
-
const status = await request(runtimeDirectory, {
|
|
49
|
-
operation: "status",
|
|
50
|
-
parameters: { runId },
|
|
51
|
-
});
|
|
52
|
-
const run = record(status["run"]);
|
|
53
|
-
if (run === undefined)
|
|
54
|
-
throw new Error("invalid-notify-status");
|
|
55
|
-
const event = outcomeNotification(run);
|
|
56
|
-
cache.set(runId, { latestSequence, event });
|
|
57
|
-
return event;
|
|
58
|
-
}
|
|
59
|
-
async function events(runtimeDirectory, cache) {
|
|
60
|
-
const found = [];
|
|
61
|
-
const runs = new Map();
|
|
62
|
-
let after = "";
|
|
63
|
-
do {
|
|
64
|
-
const page = await request(runtimeDirectory, {
|
|
65
|
-
operation: "status",
|
|
66
|
-
parameters: { after, limit: 500 },
|
|
67
|
-
});
|
|
68
|
-
for (const summary of rows(page["runs"])) {
|
|
69
|
-
runs.set(String(summary["runId"]), summary);
|
|
70
|
-
const outcome = await observedOutcome(summary, {
|
|
71
|
-
runtimeDirectory,
|
|
72
|
-
cache,
|
|
73
|
-
});
|
|
74
|
-
if (outcome !== undefined)
|
|
75
|
-
found.push(outcome);
|
|
76
|
-
}
|
|
77
|
-
const next = page["next"];
|
|
78
|
-
if (next !== null && (typeof next !== "string" || next <= after))
|
|
79
|
-
throw new Error("invalid-notify-page");
|
|
80
|
-
after = next;
|
|
81
|
-
} while (after !== null);
|
|
82
|
-
const questions = await request(runtimeDirectory, {
|
|
83
|
-
operation: "questions",
|
|
84
|
-
parameters: {},
|
|
85
|
-
});
|
|
86
|
-
for (const decision of rows(questions["decisions"])) {
|
|
87
|
-
const run = runs.get(String(decision["runId"]));
|
|
88
|
-
if (run !== undefined)
|
|
89
|
-
found.push(decisionNotification(run, decision));
|
|
90
|
-
}
|
|
91
|
-
return found;
|
|
92
|
-
}
|
|
93
21
|
export function notifyCommand(command, notification) {
|
|
94
22
|
return new Promise((resolve, reject) => {
|
|
95
23
|
const [executable = "", ...argumentsList] = command;
|
|
@@ -210,7 +138,7 @@ async function check(settings, journal) {
|
|
|
210
138
|
journal.retiredKeys.add(key);
|
|
211
139
|
}
|
|
212
140
|
}
|
|
213
|
-
for (const event of await
|
|
141
|
+
for (const event of await notificationEvents(settings.runtimeDirectory, journal.cache)) {
|
|
214
142
|
if (journal.entries.has(event.key) || journal.retiredKeys.has(event.key))
|
|
215
143
|
continue;
|
|
216
144
|
const entry = {
|
|
@@ -237,12 +165,12 @@ export async function notifyRuns(settings) {
|
|
|
237
165
|
unsaved: new Map(),
|
|
238
166
|
};
|
|
239
167
|
if (recorded === undefined) {
|
|
240
|
-
for (const event of await
|
|
168
|
+
for (const event of await notificationEvents(settings.runtimeDirectory, cache))
|
|
241
169
|
journal.entries.set(event.key, {
|
|
242
170
|
...event,
|
|
243
|
-
state: "sent",
|
|
171
|
+
state: event.message.event === "schedule-stopped" ? "pending" : "sent",
|
|
244
172
|
attempts: 0,
|
|
245
|
-
lastAttemptAt: Date.now(),
|
|
173
|
+
lastAttemptAt: event.message.event === "schedule-stopped" ? null : Date.now(),
|
|
246
174
|
lastFailure: null,
|
|
247
175
|
});
|
|
248
176
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { QueuePort } from "../queue-state.ts";
|
|
2
|
+
import { type QueueJournal } from "./queue-journal.ts";
|
|
3
|
+
export interface QueueRuntime {
|
|
4
|
+
pending: QueueJournal | undefined;
|
|
5
|
+
error: string | undefined;
|
|
6
|
+
readonly stopping: AbortController;
|
|
7
|
+
}
|
|
8
|
+
export declare function queueConnection(): {
|
|
9
|
+
readonly port: QueuePort;
|
|
10
|
+
readonly connect: (port: QueuePort) => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function queueStopped(runtime: QueueRuntime, error: unknown): string;
|
|
13
|
+
export declare function queueControlPort(runtime: QueueRuntime): QueuePort;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { queueControlLimit } from "./queue-journal.js";
|
|
2
|
+
const unavailable = {
|
|
3
|
+
kind: "unavailable",
|
|
4
|
+
detail: "The queue is not running.",
|
|
5
|
+
};
|
|
6
|
+
export function queueConnection() {
|
|
7
|
+
let connected;
|
|
8
|
+
return {
|
|
9
|
+
port: {
|
|
10
|
+
control: () => connected?.control(),
|
|
11
|
+
change: (request) => connected?.change(request) ?? Promise.resolve(unavailable),
|
|
12
|
+
},
|
|
13
|
+
connect: (port) => {
|
|
14
|
+
connected = port;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function queueStopped(runtime, error) {
|
|
19
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
20
|
+
runtime.error = reason;
|
|
21
|
+
runtime.stopping.abort();
|
|
22
|
+
process.stderr.write(`Queue stopped: ${JSON.stringify(reason)}\n`);
|
|
23
|
+
return reason;
|
|
24
|
+
}
|
|
25
|
+
function stoppedChange(error) {
|
|
26
|
+
return {
|
|
27
|
+
kind: "unavailable",
|
|
28
|
+
detail: `The queue stopped: ${error}. Restart the service.`,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function recorded(pending, request) {
|
|
32
|
+
const prior = pending.controls.find((control) => control.commandId === request.commandId);
|
|
33
|
+
if (prior === undefined)
|
|
34
|
+
return undefined;
|
|
35
|
+
return {
|
|
36
|
+
kind: prior.command === request.command ? "replayed" : "conflicting-command",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function applied(pending, command) {
|
|
40
|
+
if (command === "drain") {
|
|
41
|
+
pending.drain ??= { startedAt: Date.now(), drainedAt: null };
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
pending.failures.length = 0;
|
|
45
|
+
pending.drain = undefined;
|
|
46
|
+
}
|
|
47
|
+
async function changed(runtime, request) {
|
|
48
|
+
const { pending, error } = runtime;
|
|
49
|
+
if (error !== undefined)
|
|
50
|
+
return stoppedChange(error);
|
|
51
|
+
if (pending === undefined)
|
|
52
|
+
return unavailable;
|
|
53
|
+
const prior = recorded(pending, request);
|
|
54
|
+
if (prior !== undefined)
|
|
55
|
+
return prior;
|
|
56
|
+
applied(pending, request.command);
|
|
57
|
+
pending.controls.push(request);
|
|
58
|
+
pending.controls.splice(0, pending.controls.length - queueControlLimit);
|
|
59
|
+
try {
|
|
60
|
+
await pending.save();
|
|
61
|
+
return { kind: "changed" };
|
|
62
|
+
}
|
|
63
|
+
catch (failure) {
|
|
64
|
+
return stoppedChange(queueStopped(runtime, failure));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export function queueControlPort(runtime) {
|
|
68
|
+
let changing = Promise.resolve();
|
|
69
|
+
return {
|
|
70
|
+
control: () => {
|
|
71
|
+
const { pending, error } = runtime;
|
|
72
|
+
if (pending === undefined && error === undefined)
|
|
73
|
+
return undefined;
|
|
74
|
+
return {
|
|
75
|
+
failedRunIds: [...(pending?.failures ?? [])],
|
|
76
|
+
drain: pending?.drain,
|
|
77
|
+
error,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
change: (request) => {
|
|
81
|
+
const next = changing.then(() => changed(runtime, request));
|
|
82
|
+
changing = next;
|
|
83
|
+
return next;
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|