cloving 1.3.0 → 1.4.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 +235 -206
- 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-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-settings.js +2 -2
- package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
- package/dist/src/broker/actor/authority-upgrades.js +7 -2
- 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/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 +2 -0
- package/dist/src/broker/effect-upgrade.js +8 -2
- package/dist/src/cli/arguments.d.ts +13 -0
- package/dist/src/cli/arguments.js +42 -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 +4 -1
- package/dist/src/cli/client.js +9 -3
- package/dist/src/cli/command-requests.d.ts +17 -0
- package/dist/src/cli/command-requests.js +43 -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 +14 -41
- package/dist/src/cli/config-file.d.ts +2 -2
- package/dist/src/cli/config-file.js +4 -2
- 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 +6 -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 +80 -0
- package/dist/src/cli/render.d.ts +1 -1
- package/dist/src/cli/render.js +17 -30
- 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 +2 -1
- package/dist/src/cli/runtime-arguments.js +7 -2
- 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/usage-render.d.ts +1 -0
- package/dist/src/cli/usage-render.js +28 -1
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +50 -7
- package/dist/src/cli/watch-actions.js +2 -2
- 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 +0 -5
- package/dist/src/cli/watch-header-view.js +8 -20
- package/dist/src/cli/watch-model.d.ts +4 -1
- package/dist/src/cli/watch-model.js +17 -11
- package/dist/src/cli/watch-page.js +12 -14
- 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 +2 -0
- package/dist/src/cli/watch-reads.js +17 -16
- package/dist/src/cli/watch-render.d.ts +14 -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 +6 -6
- package/dist/src/cli/watch-view.d.ts +0 -3
- package/dist/src/cli/watch-view.js +13 -13
- package/dist/src/cli.js +59 -5
- 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-schema.d.ts +9 -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 +204 -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 +2 -1
- package/dist/src/daemon/claude-worker.js +124 -122
- package/dist/src/daemon/daemon-adapters.d.ts +2 -0
- package/dist/src/daemon/daemon-adapters.js +26 -2
- package/dist/src/daemon/daemon-loops.d.ts +3 -1
- package/dist/src/daemon/daemon-loops.js +3 -1
- package/dist/src/daemon/daemon.js +14 -6
- package/dist/src/daemon/github-attach.d.ts +2 -0
- package/dist/src/daemon/github-attach.js +149 -0
- 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 +12 -2
- 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 +1 -1
- package/dist/src/daemon/issue-queue.js +16 -11
- 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-status.d.ts +9 -3
- package/dist/src/daemon/queue-status.js +17 -4
- 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/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-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 +8 -0
- package/dist/src/daemon/worker-profile.js +40 -0
- 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 +3 -5
- package/dist/src/doctor-report.js +6 -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 +40 -0
- package/dist/src/project-config.schema.json +40 -0
- package/dist/src/project-settings.d.ts +2 -2
- package/dist/src/project-settings.js +3 -1
- package/dist/src/queue-spend.d.ts +4 -0
- package/dist/src/queue-spend.js +21 -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 +6 -6
- package/dist/src/service/command-waits.d.ts +34 -0
- package/dist/src/service/command-waits.js +86 -0
- package/dist/src/service/expiry.js +2 -0
- package/dist/src/service/gateway.js +3 -1
- package/dist/src/service/queue-spend.d.ts +6 -0
- package/dist/src/service/queue-spend.js +68 -0
- package/dist/src/service/replies.d.ts +8 -1
- package/dist/src/service/replies.js +11 -0
- package/dist/src/service/run-handlers.js +22 -2
- 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 +4 -0
- package/dist/src/service/service.js +8 -8
- 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 +2 -0
- package/dist/src/service/worker-totals.js +1 -1
- 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 -1
- package/dist/src/storage/decision-snapshots.js +15 -1
- package/dist/src/storage/domain-changes.d.ts +2 -0
- package/dist/src/storage/domain-changes.js +3 -0
- package/dist/src/storage/domain-schema.d.ts +13 -1
- package/dist/src/storage/domain-schema.js +31 -4
- package/dist/src/storage/effect-schema.d.ts +1 -0
- package/dist/src/storage/effect-schema.js +5 -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-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/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 +32 -13
- package/dist/tools/self-host-project.js +13 -78
- package/dist/tools/self-host-runtime.js +1 -1
- package/dist/tools/self-host.js +42 -10
- 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-shape.d.ts +3 -0
- package/dist/tools/workflow-candidate-shape.js +90 -0
- package/dist/tools/workflow-candidate-source.js +15 -1
- 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 +16 -8
- package/templates/develop/README.md +12 -0
- package/templates/develop/develop/flow-tests.ts +37 -0
- package/templates/develop/develop/pull.ts +5 -1
- package/templates/develop/develop/refusal-tests.ts +77 -0
- package/templates/develop/develop/turn.ts +2 -1
- 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
package/dist/src/report.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { reportQualityMetrics, } from "./report-quality.js";
|
|
1
2
|
import { reportMetric, outsideReportBand, } from "./report-statistics.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
];
|
|
3
|
+
import { reportOperatorMetrics } from "./report-operator.js";
|
|
4
|
+
import { reportCiMetrics } from "./report-ci.js";
|
|
5
|
+
import { reportEffectData } from "./report-effects.js";
|
|
6
|
+
import { reportFlowMetrics } from "./report-flow.js";
|
|
7
|
+
import { reportOutcomes } from "./report-flow-types.js";
|
|
8
|
+
export { reportOutcomes, } from "./report-flow-types.js";
|
|
9
9
|
function outcomeMetrics(input, window) {
|
|
10
10
|
return Object.fromEntries(reportOutcomes.map((outcome) => {
|
|
11
11
|
const data = input.completions === null
|
|
@@ -23,9 +23,80 @@ function outcomeMetrics(input, window) {
|
|
|
23
23
|
];
|
|
24
24
|
}));
|
|
25
25
|
}
|
|
26
|
+
function effectMetrics(input, window) {
|
|
27
|
+
const data = reportEffectData(input.effects ?? null);
|
|
28
|
+
const metrics = {
|
|
29
|
+
"fixRounds.medianPerMergedPullRequest": reportMetric({
|
|
30
|
+
data: data.fixRounds,
|
|
31
|
+
window,
|
|
32
|
+
aggregation: "median",
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
35
|
+
for (const phase of ["workerTurn", "codeReview", "merge"])
|
|
36
|
+
metrics[`phases.${phase}.medianMinutes`] = reportMetric({
|
|
37
|
+
data: data[phase],
|
|
38
|
+
window,
|
|
39
|
+
aggregation: "median",
|
|
40
|
+
});
|
|
41
|
+
return metrics;
|
|
42
|
+
}
|
|
43
|
+
function githubMetrics(input, window) {
|
|
44
|
+
const observed = githubWindow(input, window);
|
|
45
|
+
return {
|
|
46
|
+
"leadTime.medianHours": reportMetric({
|
|
47
|
+
data: input.github?.leadTimes ?? null,
|
|
48
|
+
window: observed,
|
|
49
|
+
aggregation: "median",
|
|
50
|
+
}),
|
|
51
|
+
"tickets.completed": reportMetric({
|
|
52
|
+
data: input.github?.issues ?? null,
|
|
53
|
+
window: observed,
|
|
54
|
+
aggregation: "count",
|
|
55
|
+
}),
|
|
56
|
+
"pullRequests.merged": reportMetric({
|
|
57
|
+
data: input.github?.pulls ?? null,
|
|
58
|
+
window: observed,
|
|
59
|
+
aggregation: "count",
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function githubWindow(input, window) {
|
|
64
|
+
return { ...window, observedSince: input.github?.observedSince ?? null };
|
|
65
|
+
}
|
|
66
|
+
function ticketCostMetric(input, window) {
|
|
67
|
+
const costs = reportRunCosts((input.completions ?? []).filter((event) => event.at < window.end), input.costs ?? []);
|
|
68
|
+
const tickets = completedCosts((input.github?.issues ?? []).filter((issue) => issue.at < window.end), costs);
|
|
69
|
+
const metric = reportMetric({
|
|
70
|
+
data: input.completions === null || input.github?.issues == null
|
|
71
|
+
? null
|
|
72
|
+
: tickets.map((ticket) => ({
|
|
73
|
+
at: ticket.closedAt,
|
|
74
|
+
value: ticket.costUsd,
|
|
75
|
+
evidence: ticket.evidence,
|
|
76
|
+
})),
|
|
77
|
+
window: {
|
|
78
|
+
...window,
|
|
79
|
+
observedSince: commonHistory(window.observedSince, input.github?.observedSince ?? null),
|
|
80
|
+
},
|
|
81
|
+
aggregation: "median",
|
|
82
|
+
});
|
|
83
|
+
const completedTicketCosts = tickets
|
|
84
|
+
.filter((ticket) => ticket.closedAt >= window.start && ticket.closedAt < window.end)
|
|
85
|
+
.map((ticket) => ({
|
|
86
|
+
...ticket,
|
|
87
|
+
...costHistory(ticket.costUsd, metric.baseline),
|
|
88
|
+
}));
|
|
89
|
+
return { metric, completedTicketCosts };
|
|
90
|
+
}
|
|
91
|
+
function costHistory(cost, baseline) {
|
|
92
|
+
return {
|
|
93
|
+
baseline,
|
|
94
|
+
...(outsideReportBand(cost, baseline.band) ? { mark: "outside" } : {}),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
26
97
|
function metricCollection(input, window) {
|
|
27
98
|
const costs = reportRunCosts(input.completions ?? [], input.costs ?? []);
|
|
28
|
-
const
|
|
99
|
+
const tickets = ticketCostMetric(input, window);
|
|
29
100
|
const costMetric = reportMetric({
|
|
30
101
|
data: input.completions === null
|
|
31
102
|
? null
|
|
@@ -37,43 +108,59 @@ function metricCollection(input, window) {
|
|
|
37
108
|
window,
|
|
38
109
|
aggregation: "median",
|
|
39
110
|
});
|
|
40
|
-
metrics
|
|
41
|
-
|
|
42
|
-
...
|
|
43
|
-
|
|
111
|
+
const metrics = {
|
|
112
|
+
...outcomeMetrics(input, window),
|
|
113
|
+
...reportFlowMetrics({
|
|
114
|
+
flow: input.flow,
|
|
115
|
+
completions: input.completions,
|
|
116
|
+
ready: {
|
|
117
|
+
tickets: input.github?.ready ?? null,
|
|
118
|
+
observedSince: input.github?.observedSince ?? null,
|
|
119
|
+
},
|
|
120
|
+
}, window),
|
|
121
|
+
...effectMetrics(input, window),
|
|
122
|
+
...githubMetrics(input, window),
|
|
123
|
+
"cost.medianRunUsd": costMetric,
|
|
124
|
+
"cost.medianCompletedTicketUsd": tickets.metric,
|
|
44
125
|
};
|
|
45
|
-
metrics["tickets.completed"] = reportMetric({
|
|
46
|
-
data: input.github?.issues ?? null,
|
|
47
|
-
window: githubWindow,
|
|
48
|
-
aggregation: "count",
|
|
49
|
-
});
|
|
50
|
-
metrics["pullRequests.merged"] = reportMetric({
|
|
51
|
-
data: input.github?.pulls ?? null,
|
|
52
|
-
window: githubWindow,
|
|
53
|
-
aggregation: "count",
|
|
54
|
-
});
|
|
55
126
|
const runCosts = costs
|
|
56
127
|
.filter((cost) => cost.completedAt >= window.start && cost.completedAt < window.end)
|
|
57
128
|
.map((cost) => ({
|
|
58
129
|
...cost,
|
|
59
|
-
|
|
60
|
-
...(outsideReportBand(cost.costUsd, costMetric.baseline.band)
|
|
61
|
-
? { mark: "outside" }
|
|
62
|
-
: {}),
|
|
130
|
+
...costHistory(cost.costUsd, costMetric.baseline),
|
|
63
131
|
}));
|
|
64
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
metrics,
|
|
134
|
+
runCosts,
|
|
135
|
+
completedTicketCosts: tickets.completedTicketCosts,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
function operatorMetrics(input, window) {
|
|
139
|
+
return reportOperatorMetrics({
|
|
140
|
+
effects: input.effects ?? null,
|
|
141
|
+
pulls: input.github?.pulls ?? null,
|
|
142
|
+
labels: input.github?.labels ?? null,
|
|
143
|
+
}, {
|
|
144
|
+
...window,
|
|
145
|
+
observedSince: commonHistory(window.observedSince, input.github?.observedSince ?? null),
|
|
146
|
+
});
|
|
65
147
|
}
|
|
66
148
|
export function loopReport(input) {
|
|
67
149
|
const end = input.now;
|
|
68
150
|
const start = end - input.since * 3_600_000;
|
|
69
151
|
const window = { start, end, observedSince: input.observedSince ?? null };
|
|
70
|
-
const unavailable = { status: "unavailable", reason: "not-implemented" };
|
|
71
152
|
return {
|
|
72
153
|
schemaVersion: 1,
|
|
73
154
|
kind: "loop-report",
|
|
74
155
|
window: { start, end, hours: input.since },
|
|
75
156
|
semantics: {
|
|
76
|
-
tickets: "
|
|
157
|
+
tickets: "closed-completed-in-window",
|
|
158
|
+
flowLanes: "current-queue-workflow-and-lane-setting",
|
|
159
|
+
flowTickets: "ready-label-intervals-and-current-ticket-body-dependency-close-times",
|
|
160
|
+
flowIdle: "elapsed-hours-with-spare-capacity-and-at-least-one-eligible-ticket",
|
|
161
|
+
flowWait: "sum-of-ticket-hours-by-dependency-or-full-lanes",
|
|
162
|
+
flowStreak: "terminal-time-then-sequence;current-carries-history;longest-counts-window-ends",
|
|
163
|
+
operatorLabels: "time-match-within-2-minutes-for-same-issue",
|
|
77
164
|
costs: "lifetime-turn-cost-attributed-at-run-end",
|
|
78
165
|
},
|
|
79
166
|
...metricCollection(input, window),
|
|
@@ -87,28 +174,31 @@ export function loopReport(input) {
|
|
|
87
174
|
history: { status: "unavailable" },
|
|
88
175
|
},
|
|
89
176
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
ticketClosureHistory: unavailable,
|
|
99
|
-
completedTicketCosts: unavailable,
|
|
100
|
-
},
|
|
177
|
+
quality: reportQualityMetrics({
|
|
178
|
+
...input.github,
|
|
179
|
+
effects: input.effects ?? null,
|
|
180
|
+
daemonObservedSince: input.observedSince ?? null,
|
|
181
|
+
}, githubWindow(input, window)),
|
|
182
|
+
ci: reportCiMetrics(input.github?.ciUsage ?? undefined, githubWindow(input, window)),
|
|
183
|
+
operator: operatorMetrics(input, window),
|
|
184
|
+
coverage: {},
|
|
101
185
|
};
|
|
102
186
|
}
|
|
103
187
|
export function reportRunCosts(completions, costs) {
|
|
188
|
+
const byRun = new Map();
|
|
189
|
+
for (const cost of costs)
|
|
190
|
+
if (!byRun.has(cost.runId))
|
|
191
|
+
byRun.set(cost.runId, cost);
|
|
104
192
|
return completions.map((event) => {
|
|
105
|
-
const
|
|
193
|
+
const cost = byRun.get(event.runId);
|
|
194
|
+
const turns = cost?.turns ?? null;
|
|
106
195
|
const known = turns !== null && turns.every((turn) => turn.costUsd !== null);
|
|
107
196
|
const total = known
|
|
108
197
|
? turns.reduce((sum, turn) => sum + (turn.costUsd ?? 0), 0)
|
|
109
198
|
: null;
|
|
110
199
|
return {
|
|
111
200
|
runId: event.runId,
|
|
201
|
+
ticketId: cost?.ticketId,
|
|
112
202
|
outcome: event.outcome,
|
|
113
203
|
completedAt: event.at,
|
|
114
204
|
costUsd: total !== null && Number.isFinite(total) ? total : null,
|
|
@@ -117,3 +207,28 @@ export function reportRunCosts(completions, costs) {
|
|
|
117
207
|
};
|
|
118
208
|
});
|
|
119
209
|
}
|
|
210
|
+
function commonHistory(daemon, github) {
|
|
211
|
+
return daemon === null || github === null ? null : Math.max(daemon, github);
|
|
212
|
+
}
|
|
213
|
+
function completedCosts(completed, costs) {
|
|
214
|
+
const issues = new Map(completed
|
|
215
|
+
.toSorted((left, right) => left.at - right.at)
|
|
216
|
+
.map((issue) => [issue.evidence["issue"], issue]));
|
|
217
|
+
return [...issues.values()].map((issue) => {
|
|
218
|
+
const number = issue.evidence["issue"];
|
|
219
|
+
const runs = costs.filter((cost) => cost.ticketId === String(number));
|
|
220
|
+
const unknown = costs.some((cost) => cost.ticketId === undefined) ||
|
|
221
|
+
runs.length === 0 ||
|
|
222
|
+
runs.some((run) => run.costUsd === null);
|
|
223
|
+
const total = unknown
|
|
224
|
+
? null
|
|
225
|
+
: runs.reduce((sum, run) => sum + (run.costUsd ?? 0), 0);
|
|
226
|
+
return {
|
|
227
|
+
issue: number,
|
|
228
|
+
closedAt: issue.at,
|
|
229
|
+
costUsd: total !== null && Number.isFinite(total) ? total : null,
|
|
230
|
+
runs,
|
|
231
|
+
evidence: { issue: number, runIds: runs.map((run) => run.runId) },
|
|
232
|
+
};
|
|
233
|
+
});
|
|
234
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type Fields, type Reply } from "./protocol.ts";
|
|
2
|
+
export type SchedulePort = (parameters: Fields) => Promise<Reply>;
|
|
3
|
+
export type PendingSlot = {
|
|
4
|
+
readonly kind: "run";
|
|
5
|
+
readonly input: Fields;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: "skip";
|
|
8
|
+
readonly runId: string;
|
|
9
|
+
};
|
|
10
|
+
export interface ScheduleStop {
|
|
11
|
+
readonly stoppedAt: number;
|
|
12
|
+
readonly stoppedSlot: number;
|
|
13
|
+
readonly failedRunIds: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ScheduleProgress {
|
|
16
|
+
readonly everySeconds: number;
|
|
17
|
+
readonly nextAt: number;
|
|
18
|
+
readonly lastSlot: number;
|
|
19
|
+
readonly state: "active" | "paused" | "stopped";
|
|
20
|
+
readonly failuresInRow: number;
|
|
21
|
+
readonly failedRunIds: readonly string[];
|
|
22
|
+
readonly stoppedAt?: number;
|
|
23
|
+
readonly stoppedSlot?: number;
|
|
24
|
+
readonly stops?: readonly ScheduleStop[];
|
|
25
|
+
readonly activeCommandId?: string;
|
|
26
|
+
readonly pending?: PendingSlot;
|
|
27
|
+
}
|
|
28
|
+
export declare function scheduleStops(progress: ScheduleProgress): readonly ScheduleStop[];
|
|
29
|
+
export declare function finishedScheduleRun(progress: ScheduleProgress, run: {
|
|
30
|
+
readonly runId: string;
|
|
31
|
+
readonly status: Fields;
|
|
32
|
+
readonly now: number;
|
|
33
|
+
}): ScheduleProgress;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { record } from "./protocol.js";
|
|
2
|
+
export function scheduleStops(progress) {
|
|
3
|
+
if (progress.stops !== undefined)
|
|
4
|
+
return progress.stops;
|
|
5
|
+
if (progress.state !== "stopped" ||
|
|
6
|
+
progress.stoppedAt === undefined ||
|
|
7
|
+
progress.stoppedSlot === undefined)
|
|
8
|
+
return [];
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
stoppedAt: progress.stoppedAt,
|
|
12
|
+
stoppedSlot: progress.stoppedSlot,
|
|
13
|
+
failedRunIds: progress.failedRunIds,
|
|
14
|
+
},
|
|
15
|
+
];
|
|
16
|
+
}
|
|
17
|
+
export function finishedScheduleRun(progress, run) {
|
|
18
|
+
const rest = { ...progress };
|
|
19
|
+
delete rest.activeCommandId;
|
|
20
|
+
const state = run.status["state"];
|
|
21
|
+
if (state === "cancelled")
|
|
22
|
+
return rest;
|
|
23
|
+
const failed = state === "failed" || record(run.status["outcome"])?.["kind"] === "blocked";
|
|
24
|
+
if (!failed)
|
|
25
|
+
return { ...rest, failuresInRow: 0, failedRunIds: [] };
|
|
26
|
+
const failedRunIds = [...progress.failedRunIds, run.runId];
|
|
27
|
+
const failuresInRow = failedRunIds.length;
|
|
28
|
+
const stop = {
|
|
29
|
+
stoppedAt: run.now,
|
|
30
|
+
stoppedSlot: progress.lastSlot,
|
|
31
|
+
failedRunIds,
|
|
32
|
+
};
|
|
33
|
+
return {
|
|
34
|
+
...rest,
|
|
35
|
+
failedRunIds,
|
|
36
|
+
failuresInRow,
|
|
37
|
+
...(failuresInRow < 3
|
|
38
|
+
? {}
|
|
39
|
+
: {
|
|
40
|
+
state: "stopped",
|
|
41
|
+
...stop,
|
|
42
|
+
stops: [...scheduleStops(progress), stop],
|
|
43
|
+
}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { record } from "../protocol.js";
|
|
2
|
+
import { queueRun, queueSpend } from "./queue-spend.js";
|
|
2
3
|
export async function activeWorkflow(context, workflowId) {
|
|
3
4
|
const workflows = await context.authority.queries.workflowIds();
|
|
4
5
|
const state = await context.authority.state();
|
|
5
|
-
const queueRun = async (runId) => workflows[runId] === "develop-issue" ||
|
|
6
|
-
(await context.authority.queries.manifest(runId))?.queue === true;
|
|
7
6
|
const runs = Object.values(state.runs).filter((run) => !["completed", "failed", "cancelled"].includes(run.status));
|
|
8
7
|
const own = runs.filter((run) => workflows[run.runId] === workflowId);
|
|
9
|
-
const queued = workflowId ===
|
|
10
|
-
|
|
11
|
-
(await Promise.all(own.map((run) => queueRun(run.runId)))).includes(true);
|
|
8
|
+
const queued = context.registry.byId(workflowId)?.queue === true ||
|
|
9
|
+
(await Promise.all(own.map((run) => queueRun(context, run.runId)))).includes(true);
|
|
12
10
|
const active = await Promise.all(runs.map(async (run) => {
|
|
13
11
|
if (workflows[run.runId] !== workflowId &&
|
|
14
|
-
!(queued && (await queueRun(run.runId))))
|
|
12
|
+
!(queued && (await queueRun(context, run.runId))))
|
|
15
13
|
return undefined;
|
|
16
14
|
return storedTicket(context, state.details[run.runId]?.input);
|
|
17
15
|
}));
|
|
@@ -26,6 +24,8 @@ export async function queueOverview(context) {
|
|
|
26
24
|
const active = await activeWorkflow(context, workflow.workflowId);
|
|
27
25
|
return {
|
|
28
26
|
queue: {
|
|
27
|
+
workflowId: workflow.workflowId,
|
|
28
|
+
...(await queueSpend(context)),
|
|
29
29
|
lanes: workflow.admission === "when-idle" ? 1 : workflow.admission,
|
|
30
30
|
busy: active.length,
|
|
31
31
|
skipped: [
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type CommandWaitView } from "../authority/command-wait-view.ts";
|
|
2
|
+
import { type CommandWaitState, type CommandWaitsState } from "../authority/command-waits.ts";
|
|
3
|
+
import type { Authority, StoreReceipt } from "../broker/actor/authority.ts";
|
|
4
|
+
import type { Gateway } from "./gateway.ts";
|
|
5
|
+
export declare function expireCommandWaits(parts: {
|
|
6
|
+
readonly waits: CommandWaitsState;
|
|
7
|
+
readonly gateway: Gateway;
|
|
8
|
+
readonly now: string;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
interface WaitName {
|
|
11
|
+
readonly runId: string;
|
|
12
|
+
readonly name: string;
|
|
13
|
+
}
|
|
14
|
+
interface WaitCall extends WaitName {
|
|
15
|
+
readonly commandId: string;
|
|
16
|
+
}
|
|
17
|
+
export interface CommandWaitPort {
|
|
18
|
+
readonly open: (wait: WaitCall & {
|
|
19
|
+
readonly input: string;
|
|
20
|
+
}) => Promise<StoreReceipt>;
|
|
21
|
+
readonly check: (wait: WaitCall) => Promise<StoreReceipt>;
|
|
22
|
+
readonly read: (wait: WaitName) => Promise<CommandWaitState | undefined>;
|
|
23
|
+
}
|
|
24
|
+
export declare function commandWaitPort(parts: {
|
|
25
|
+
readonly authority: Authority;
|
|
26
|
+
readonly gateway: Gateway;
|
|
27
|
+
}): CommandWaitPort;
|
|
28
|
+
export declare function commandWaitDisplay(input: {
|
|
29
|
+
readonly authority: Authority;
|
|
30
|
+
readonly runId: string;
|
|
31
|
+
readonly waits: CommandWaitsState | undefined;
|
|
32
|
+
readonly now: string;
|
|
33
|
+
}): Promise<readonly CommandWaitView[]>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { commandWaitViews, } from "../authority/command-wait-view.js";
|
|
2
|
+
import { commandWaitKey, } from "../authority/command-waits.js";
|
|
3
|
+
import { documentDigest } from "../broker/decision-restore.js";
|
|
4
|
+
import { effectInputDigest } from "../broker/effect-restore.js";
|
|
5
|
+
import { operationDigest } from "../broker/operation-digest.js";
|
|
6
|
+
export async function expireCommandWaits(parts) {
|
|
7
|
+
for (const wait of Object.values(parts.waits)) {
|
|
8
|
+
if (wait.state !== "waiting" || Date.parse(parts.now) < wait.deadline)
|
|
9
|
+
continue;
|
|
10
|
+
await parts.gateway.execute({
|
|
11
|
+
principal: "controller",
|
|
12
|
+
command: {
|
|
13
|
+
kind: "expire-command-wait",
|
|
14
|
+
commandId: `expire-wait:${operationDigest(commandWaitKey(wait.runId, wait.name))}`,
|
|
15
|
+
runId: wait.runId,
|
|
16
|
+
waitName: wait.name,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function commandWaitPort(parts) {
|
|
22
|
+
const { authority, gateway } = parts;
|
|
23
|
+
const read = async (wait) => (await authority.state()).commandWaits?.[commandWaitKey(wait.runId, wait.name)];
|
|
24
|
+
return {
|
|
25
|
+
read,
|
|
26
|
+
open: async (wait) => {
|
|
27
|
+
const declarations = (await authority.queries.manifest(wait.runId))?.commandWaits ?? {};
|
|
28
|
+
const permission = Object.hasOwn(declarations, wait.name)
|
|
29
|
+
? declarations[wait.name]
|
|
30
|
+
: undefined;
|
|
31
|
+
if (permission === undefined)
|
|
32
|
+
return { kind: "rejected", reason: "command-wait-not-declared" };
|
|
33
|
+
return gateway.execute({
|
|
34
|
+
principal: "worker",
|
|
35
|
+
command: {
|
|
36
|
+
kind: "open-command-wait",
|
|
37
|
+
runId: wait.runId,
|
|
38
|
+
commandId: wait.commandId,
|
|
39
|
+
waitName: wait.name,
|
|
40
|
+
target: permission.command,
|
|
41
|
+
maximumWaitSeconds: permission.maximumWaitSeconds,
|
|
42
|
+
pollEverySeconds: permission.pollEverySeconds,
|
|
43
|
+
payload: documentDigest(wait.input),
|
|
44
|
+
},
|
|
45
|
+
documents: [wait.input],
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
check: async (call) => {
|
|
49
|
+
const wait = await read(call);
|
|
50
|
+
if (wait === undefined)
|
|
51
|
+
return { kind: "rejected", reason: "unknown-command-wait" };
|
|
52
|
+
return gateway.execute({
|
|
53
|
+
principal: "worker",
|
|
54
|
+
command: {
|
|
55
|
+
kind: "check-command-wait",
|
|
56
|
+
runId: call.runId,
|
|
57
|
+
commandId: call.commandId,
|
|
58
|
+
waitName: call.name,
|
|
59
|
+
inputDigest: effectInputDigest({
|
|
60
|
+
capability: "command",
|
|
61
|
+
method: `run:${wait.command}`,
|
|
62
|
+
target: wait.command,
|
|
63
|
+
payload: wait.inputDigest,
|
|
64
|
+
}),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function displayTime(time) {
|
|
71
|
+
return time === null ? null : new Date(time).toISOString();
|
|
72
|
+
}
|
|
73
|
+
export async function commandWaitDisplay(input) {
|
|
74
|
+
const manifest = await input.authority.queries.manifest(input.runId);
|
|
75
|
+
return commandWaitViews({
|
|
76
|
+
...input,
|
|
77
|
+
declarations: manifest?.commandWaits ?? {},
|
|
78
|
+
waits: input.waits ?? {},
|
|
79
|
+
now: Date.parse(input.now),
|
|
80
|
+
}).map((wait) => ({
|
|
81
|
+
...wait,
|
|
82
|
+
startedAt: displayTime(wait.startedAt),
|
|
83
|
+
deadline: displayTime(wait.deadline),
|
|
84
|
+
nextCheckAt: displayTime(wait.nextCheckAt),
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
3
|
import { notBefore } from "../authority/instants.js";
|
|
4
4
|
import { operationDigest } from "../broker/operation-digest.js";
|
|
5
|
+
import { expireCommandWaits } from "./command-waits.js";
|
|
5
6
|
function expiryIdentity(runId, gateId) {
|
|
6
7
|
return `expire:${createHash("sha256").update(`${runId} ${gateId}`).digest("hex").slice(0, 32)}`;
|
|
7
8
|
}
|
|
@@ -9,6 +10,7 @@ async function scanned(parts) {
|
|
|
9
10
|
const { authority, gateway, clock } = parts;
|
|
10
11
|
const state = await authority.state();
|
|
11
12
|
const now = clock();
|
|
13
|
+
await expireCommandWaits({ waits: state.commandWaits ?? {}, gateway, now });
|
|
12
14
|
const due = Object.values(state.gates).filter((gate) => gate.outcome === null &&
|
|
13
15
|
gate.expiresAt !== null &&
|
|
14
16
|
notBefore(now, gate.expiresAt) &&
|
|
@@ -11,6 +11,8 @@ const permitted = {
|
|
|
11
11
|
"cancel-run",
|
|
12
12
|
],
|
|
13
13
|
worker: [
|
|
14
|
+
"open-command-wait",
|
|
15
|
+
"check-command-wait",
|
|
14
16
|
"record-schedule-skip",
|
|
15
17
|
"mark-started",
|
|
16
18
|
"open-gate",
|
|
@@ -21,7 +23,7 @@ const permitted = {
|
|
|
21
23
|
"request-effect",
|
|
22
24
|
"report-effect",
|
|
23
25
|
],
|
|
24
|
-
controller: ["expire-gate", "expire-lease"],
|
|
26
|
+
controller: ["expire-gate", "expire-lease", "expire-command-wait"],
|
|
25
27
|
};
|
|
26
28
|
const pinned = ["decide-gate", "resume-run"];
|
|
27
29
|
function denied(principal, subject) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Fields } from "../protocol.ts";
|
|
2
|
+
import type { ServiceContext } from "./replies.ts";
|
|
3
|
+
type SpendContext = Pick<ServiceContext, "authority" | "clock" | "queueDailySpendLimitUsd">;
|
|
4
|
+
export declare function queueRun(context: Pick<ServiceContext, "authority">, runId: string): Promise<boolean>;
|
|
5
|
+
export declare function queueSpend(context: SpendContext): Promise<Fields>;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { queueSpending } from "../queue-spend.js";
|
|
2
|
+
import { priceOf } from "../worker-usage.js";
|
|
3
|
+
import { reportFields } from "./worker-totals.js";
|
|
4
|
+
export async function queueRun(context, runId) {
|
|
5
|
+
const { queries } = context.authority;
|
|
6
|
+
if ((await queries.manifest(runId))?.queue === true)
|
|
7
|
+
return true;
|
|
8
|
+
if ((await queries.workflowIds([runId]))[runId] !== "develop-issue")
|
|
9
|
+
return false;
|
|
10
|
+
const { events } = await queries.eventsAfter({ runId, after: 0, limit: 1 });
|
|
11
|
+
return (events[0]?.type === "run.created" &&
|
|
12
|
+
events[0].payload["resolver"] === "issue-command:1");
|
|
13
|
+
}
|
|
14
|
+
export async function queueSpend(context) {
|
|
15
|
+
const limitUsd = context.queueDailySpendLimitUsd;
|
|
16
|
+
if (limitUsd === undefined)
|
|
17
|
+
return {};
|
|
18
|
+
const now = Date.parse(context.clock());
|
|
19
|
+
const operations = await context.authority.effects.workerTurns({
|
|
20
|
+
after: new Date(now - 24 * 3_600_000).toISOString(),
|
|
21
|
+
through: new Date(now).toISOString(),
|
|
22
|
+
});
|
|
23
|
+
const runIds = [...new Set(operations.map((operation) => operation.runId))];
|
|
24
|
+
const costs = [];
|
|
25
|
+
const totals = {
|
|
26
|
+
costs,
|
|
27
|
+
unknownTurns: 0,
|
|
28
|
+
turnsInWork: 0,
|
|
29
|
+
};
|
|
30
|
+
const queued = new Set();
|
|
31
|
+
for (const runId of runIds) {
|
|
32
|
+
if (await queueRun(context, runId))
|
|
33
|
+
queued.add(runId);
|
|
34
|
+
}
|
|
35
|
+
for (const operation of operations) {
|
|
36
|
+
if (queued.has(operation.runId))
|
|
37
|
+
await countTurn(context, { operation, totals });
|
|
38
|
+
}
|
|
39
|
+
const spending = queueSpending(totals.costs, limitUsd);
|
|
40
|
+
const spentUsd = totals.costs.length === 0 && totals.unknownTurns > 0
|
|
41
|
+
? null
|
|
42
|
+
: spending.spentUsd;
|
|
43
|
+
return {
|
|
44
|
+
spend: {
|
|
45
|
+
limitUsd,
|
|
46
|
+
spentUsd,
|
|
47
|
+
unknownTurns: totals.unknownTurns,
|
|
48
|
+
turnsInWork: totals.turnsInWork,
|
|
49
|
+
windowHours: 24,
|
|
50
|
+
holding: spentUsd !== null && spending.holding,
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function countTurn(context, turn) {
|
|
55
|
+
const { operation, totals } = turn;
|
|
56
|
+
if (operation.settledSequence === null) {
|
|
57
|
+
totals.turnsInWork += 1;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const report = reportFields(operation.result === null
|
|
61
|
+
? undefined
|
|
62
|
+
: await context.authority.queries.document(operation.result));
|
|
63
|
+
const cost = priceOf(report["costUsd"]) ?? priceOf(report["estimatedCostUsd"]);
|
|
64
|
+
if (cost === null)
|
|
65
|
+
totals.unknownTurns += 1;
|
|
66
|
+
else
|
|
67
|
+
totals.costs.push(cost);
|
|
68
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SchedulePort } from "../schedule-progress.ts";
|
|
1
2
|
import type { Authority, CommandRecord, StoreReceipt } from "../broker/actor/authority.ts";
|
|
2
3
|
import type { Fields, Reply } from "../protocol.ts";
|
|
3
4
|
import type { ChangeSignal } from "./changes.ts";
|
|
@@ -6,6 +7,8 @@ import type { Registry } from "./registry.ts";
|
|
|
6
7
|
import { type WorkflowRefusal } from "../workflow-refusals.ts";
|
|
7
8
|
import type { CommandEffects } from "../command-settings.ts";
|
|
8
9
|
export interface ServiceContext {
|
|
10
|
+
readonly schedule?: SchedulePort;
|
|
11
|
+
readonly queueDailySpendLimitUsd?: number;
|
|
9
12
|
readonly workerHarness?: (operationId: string) => Promise<string | undefined>;
|
|
10
13
|
readonly commandEffects?: CommandEffects;
|
|
11
14
|
readonly authority: Authority;
|
|
@@ -22,10 +25,12 @@ export interface ServiceContext {
|
|
|
22
25
|
readonly changed?: ChangeSignal["next"];
|
|
23
26
|
}
|
|
24
27
|
export declare function queryMetadata(options: {
|
|
28
|
+
readonly schedule?: SchedulePort;
|
|
29
|
+
readonly queueDailySpendLimitUsd?: number;
|
|
25
30
|
readonly workerHarness?: ServiceContext["workerHarness"];
|
|
26
31
|
readonly commandEffects?: CommandEffects;
|
|
27
32
|
readonly workflowRefusals?: readonly WorkflowRefusal[];
|
|
28
|
-
}): Pick<ServiceContext, "workerHarness" | "commandEffects" | "workflowRefusals">;
|
|
33
|
+
}): Pick<ServiceContext, "schedule" | "workerHarness" | "commandEffects" | "workflowRefusals" | "queueDailySpendLimitUsd">;
|
|
29
34
|
export declare function result(kind: string, fields: Fields): Reply;
|
|
30
35
|
export declare function failed(code: string, extra: Fields): Reply;
|
|
31
36
|
export declare function invalidRequest(detail: string): Reply;
|
|
@@ -34,3 +39,5 @@ export declare function committed(receipt: StoreReceipt): boolean;
|
|
|
34
39
|
export declare function isIdentifier(value: unknown): value is string;
|
|
35
40
|
export declare function wholeNumber(value: unknown, minimum: number): value is number;
|
|
36
41
|
export declare function publicReceipt(record: CommandRecord): Fields;
|
|
42
|
+
export declare function scheduleRequest(context: ServiceContext, parameters: Fields): Promise<Reply>;
|
|
43
|
+
export declare function errorMessage(error: unknown): string;
|
|
@@ -2,6 +2,10 @@ import { isRecordKey } from "../authority/commands.js";
|
|
|
2
2
|
import { workflowRefusals, } from "../workflow-refusals.js";
|
|
3
3
|
export function queryMetadata(options) {
|
|
4
4
|
return {
|
|
5
|
+
...(options.schedule === undefined ? {} : { schedule: options.schedule }),
|
|
6
|
+
...(options.queueDailySpendLimitUsd === undefined
|
|
7
|
+
? {}
|
|
8
|
+
: { queueDailySpendLimitUsd: options.queueDailySpendLimitUsd }),
|
|
5
9
|
...(options.workerHarness === undefined
|
|
6
10
|
? {}
|
|
7
11
|
: { workerHarness: options.workerHarness }),
|
|
@@ -66,3 +70,10 @@ export function publicReceipt(record) {
|
|
|
66
70
|
commandDigest: record.commandDigest,
|
|
67
71
|
};
|
|
68
72
|
}
|
|
73
|
+
export function scheduleRequest(context, parameters) {
|
|
74
|
+
return (context.schedule?.(parameters) ??
|
|
75
|
+
Promise.resolve(failed("schedules-unavailable", {})));
|
|
76
|
+
}
|
|
77
|
+
export function errorMessage(error) {
|
|
78
|
+
return String(Reflect.get(Object(error), "message"));
|
|
79
|
+
}
|