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.
Files changed (396) hide show
  1. package/README.md +235 -206
  2. package/dist/src/agent-harness-shell.d.ts +2 -1
  3. package/dist/src/agent-harness-shell.js +8 -5
  4. package/dist/src/authority/command-wait-checks.d.ts +8 -0
  5. package/dist/src/authority/command-wait-checks.js +88 -0
  6. package/dist/src/authority/command-wait-results.d.ts +15 -0
  7. package/dist/src/authority/command-wait-results.js +73 -0
  8. package/dist/src/authority/command-wait-view.d.ts +21 -0
  9. package/dist/src/authority/command-wait-view.js +34 -0
  10. package/dist/src/authority/command-waits.d.ts +39 -0
  11. package/dist/src/authority/command-waits.js +97 -0
  12. package/dist/src/authority/commands.js +22 -1
  13. package/dist/src/authority/dispatch-decision.d.ts +8 -0
  14. package/dist/src/authority/dispatch-decision.js +26 -1
  15. package/dist/src/authority/effect-requests.js +6 -3
  16. package/dist/src/authority/effect-results.js +4 -2
  17. package/dist/src/authority/history-state.d.ts +2 -0
  18. package/dist/src/authority/history-state.js +13 -1
  19. package/dist/src/authority/instants.d.ts +1 -0
  20. package/dist/src/authority/instants.js +21 -0
  21. package/dist/src/authority/reduction-state.d.ts +2 -0
  22. package/dist/src/authority/reduction-state.js +5 -0
  23. package/dist/src/authority/reduction.js +24 -0
  24. package/dist/src/authority/status-input.d.ts +2 -0
  25. package/dist/src/authority/status.d.ts +2 -0
  26. package/dist/src/authority/status.js +1 -0
  27. package/dist/src/authority/stop-sequence.d.ts +1 -0
  28. package/dist/src/authority/stop-sequence.js +2 -1
  29. package/dist/src/authority/transitions.d.ts +6 -4
  30. package/dist/src/authority/transitions.js +7 -0
  31. package/dist/src/broker/actor/authority-queries.d.ts +2 -1
  32. package/dist/src/broker/actor/authority-queries.js +14 -5
  33. package/dist/src/broker/actor/authority-receipts.js +1 -1
  34. package/dist/src/broker/actor/authority-settings.js +2 -2
  35. package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
  36. package/dist/src/broker/actor/authority-upgrades.js +7 -2
  37. package/dist/src/broker/actor/delivery-intents.js +2 -0
  38. package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
  39. package/dist/src/broker/actor/effect-delivery.js +15 -2
  40. package/dist/src/broker/actor/effect-plan.js +11 -2
  41. package/dist/src/broker/actor/effect-queries.d.ts +4 -0
  42. package/dist/src/broker/actor/effect-queries.js +9 -1
  43. package/dist/src/broker/actor/effect-turn.js +2 -0
  44. package/dist/src/broker/actor/stop-plan.js +9 -3
  45. package/dist/src/broker/actor/stop-turn.d.ts +2 -0
  46. package/dist/src/broker/actor/stop-turn.js +24 -11
  47. package/dist/src/broker/actor/turn-draft.d.ts +1 -0
  48. package/dist/src/broker/actor/turn-draft.js +4 -0
  49. package/dist/src/broker/actor/turn-state.js +5 -1
  50. package/dist/src/broker/authority-admission.js +60 -0
  51. package/dist/src/broker/authority-restore.d.ts +2 -0
  52. package/dist/src/broker/authority-restore.js +35 -1
  53. package/dist/src/broker/command-wait-restore.d.ts +31 -0
  54. package/dist/src/broker/command-wait-restore.js +86 -0
  55. package/dist/src/broker/decision-restore.d.ts +1 -1
  56. package/dist/src/broker/decision-restore.js +1 -0
  57. package/dist/src/broker/effect-upgrade.d.ts +2 -0
  58. package/dist/src/broker/effect-upgrade.js +8 -2
  59. package/dist/src/cli/arguments.d.ts +13 -0
  60. package/dist/src/cli/arguments.js +42 -3
  61. package/dist/src/cli/chat-stores.d.ts +2 -0
  62. package/dist/src/cli/chat-stores.js +113 -0
  63. package/dist/src/cli/client.d.ts +4 -1
  64. package/dist/src/cli/client.js +9 -3
  65. package/dist/src/cli/command-requests.d.ts +17 -0
  66. package/dist/src/cli/command-requests.js +43 -0
  67. package/dist/src/cli/command-waits.d.ts +9 -0
  68. package/dist/src/cli/command-waits.js +51 -0
  69. package/dist/src/cli/commands.js +14 -41
  70. package/dist/src/cli/config-file.d.ts +2 -2
  71. package/dist/src/cli/config-file.js +4 -2
  72. package/dist/src/cli/doctor-workflows.d.ts +5 -2
  73. package/dist/src/cli/doctor-workflows.js +12 -7
  74. package/dist/src/cli/doctor.js +6 -9
  75. package/dist/src/cli/harness-probes.d.ts +2 -0
  76. package/dist/src/cli/harness-probes.js +1 -1
  77. package/dist/src/cli/harness-processes.d.ts +6 -0
  78. package/dist/src/cli/harness-processes.js +36 -0
  79. package/dist/src/cli/init-command.d.ts +9 -0
  80. package/dist/src/cli/init-command.js +52 -0
  81. package/dist/src/cli/init-prompt-command.d.ts +10 -0
  82. package/dist/src/cli/init-prompt-command.js +145 -0
  83. package/dist/src/cli/init-prompt.d.ts +35 -0
  84. package/dist/src/cli/init-prompt.js +74 -0
  85. package/dist/src/cli/init-selection.d.ts +13 -0
  86. package/dist/src/cli/init-selection.js +75 -0
  87. package/dist/src/cli/main.d.ts +2 -0
  88. package/dist/src/cli/main.js +37 -7
  89. package/dist/src/cli/mcp-server.d.ts +1 -0
  90. package/dist/src/cli/mcp-server.js +156 -0
  91. package/dist/src/cli/mcp-tools.d.ts +15 -0
  92. package/dist/src/cli/mcp-tools.js +80 -0
  93. package/dist/src/cli/render.d.ts +1 -1
  94. package/dist/src/cli/render.js +17 -30
  95. package/dist/src/cli/report-ci-jobs.d.ts +23 -0
  96. package/dist/src/cli/report-ci-jobs.js +103 -0
  97. package/dist/src/cli/report-ci-usage.d.ts +9 -0
  98. package/dist/src/cli/report-ci-usage.js +185 -0
  99. package/dist/src/cli/report-ci.d.ts +13 -0
  100. package/dist/src/cli/report-ci.js +74 -0
  101. package/dist/src/cli/report-closures.d.ts +6 -0
  102. package/dist/src/cli/report-closures.js +97 -0
  103. package/dist/src/cli/report-flow.d.ts +7 -0
  104. package/dist/src/cli/report-flow.js +122 -0
  105. package/dist/src/cli/report-github-reader.d.ts +29 -0
  106. package/dist/src/cli/report-github-reader.js +157 -0
  107. package/dist/src/cli/report-github.d.ts +15 -8
  108. package/dist/src/cli/report-github.js +131 -150
  109. package/dist/src/cli/report-operator.d.ts +3 -0
  110. package/dist/src/cli/report-operator.js +40 -0
  111. package/dist/src/cli/report-ready.d.ts +3 -0
  112. package/dist/src/cli/report-ready.js +96 -0
  113. package/dist/src/cli/report-text.d.ts +9 -0
  114. package/dist/src/cli/report-text.js +33 -0
  115. package/dist/src/cli/report.d.ts +1 -0
  116. package/dist/src/cli/report.js +104 -27
  117. package/dist/src/cli/runtime-arguments.d.ts +2 -1
  118. package/dist/src/cli/runtime-arguments.js +7 -2
  119. package/dist/src/cli/schedule-arguments.d.ts +14 -0
  120. package/dist/src/cli/schedule-arguments.js +26 -0
  121. package/dist/src/cli/schedule-render.d.ts +22 -0
  122. package/dist/src/cli/schedule-render.js +73 -0
  123. package/dist/src/cli/terminal-text.d.ts +3 -0
  124. package/dist/src/cli/terminal-text.js +28 -0
  125. package/dist/src/cli/usage-render.d.ts +1 -0
  126. package/dist/src/cli/usage-render.js +28 -1
  127. package/dist/src/cli/usage.d.ts +1 -1
  128. package/dist/src/cli/usage.js +50 -7
  129. package/dist/src/cli/watch-actions.js +2 -2
  130. package/dist/src/cli/watch-board-view.js +19 -2
  131. package/dist/src/cli/watch-board.d.ts +4 -2
  132. package/dist/src/cli/watch-board.js +53 -43
  133. package/dist/src/cli/watch-card-forms.d.ts +1 -1
  134. package/dist/src/cli/watch-card-forms.js +11 -10
  135. package/dist/src/cli/watch-client.d.ts +1 -1
  136. package/dist/src/cli/watch-client.js +2 -11
  137. package/dist/src/cli/watch-header-view.d.ts +0 -5
  138. package/dist/src/cli/watch-header-view.js +8 -20
  139. package/dist/src/cli/watch-model.d.ts +4 -1
  140. package/dist/src/cli/watch-model.js +17 -11
  141. package/dist/src/cli/watch-page.js +12 -14
  142. package/dist/src/cli/watch-phases.d.ts +12 -3
  143. package/dist/src/cli/watch-phases.js +68 -11
  144. package/dist/src/cli/watch-pr-board.js +1 -1
  145. package/dist/src/cli/watch-pr-command.d.ts +1 -0
  146. package/dist/src/cli/watch-pull-request.js +18 -8
  147. package/dist/src/cli/watch-reads.d.ts +2 -0
  148. package/dist/src/cli/watch-reads.js +17 -16
  149. package/dist/src/cli/watch-render.d.ts +14 -0
  150. package/dist/src/cli/watch-run-input.d.ts +0 -1
  151. package/dist/src/cli/watch-run-input.js +0 -13
  152. package/dist/src/cli/watch-server.js +6 -6
  153. package/dist/src/cli/watch-view.d.ts +0 -3
  154. package/dist/src/cli/watch-view.js +13 -13
  155. package/dist/src/cli.js +59 -5
  156. package/dist/src/command-wait.d.ts +4 -0
  157. package/dist/src/command-wait.js +45 -0
  158. package/dist/src/configuration-change.d.ts +7 -0
  159. package/dist/src/configuration-change.js +14 -0
  160. package/dist/src/configuration-schema.d.ts +9 -0
  161. package/dist/src/configuration-schema.js +6 -2
  162. package/dist/src/daemon/attachment-file.d.ts +6 -0
  163. package/dist/src/daemon/attachment-file.js +86 -0
  164. package/dist/src/daemon/attachments.d.ts +14 -0
  165. package/dist/src/daemon/attachments.js +17 -0
  166. package/dist/src/daemon/claude-recovery.d.ts +50 -0
  167. package/dist/src/daemon/claude-recovery.js +204 -0
  168. package/dist/src/daemon/claude-stream.d.ts +27 -0
  169. package/dist/src/daemon/claude-stream.js +63 -0
  170. package/dist/src/daemon/claude-turn.d.ts +11 -25
  171. package/dist/src/daemon/claude-turn.js +64 -81
  172. package/dist/src/daemon/claude-worker.d.ts +2 -1
  173. package/dist/src/daemon/claude-worker.js +124 -122
  174. package/dist/src/daemon/daemon-adapters.d.ts +2 -0
  175. package/dist/src/daemon/daemon-adapters.js +26 -2
  176. package/dist/src/daemon/daemon-loops.d.ts +3 -1
  177. package/dist/src/daemon/daemon-loops.js +3 -1
  178. package/dist/src/daemon/daemon.js +14 -6
  179. package/dist/src/daemon/github-attach.d.ts +2 -0
  180. package/dist/src/daemon/github-attach.js +149 -0
  181. package/dist/src/daemon/github-ci-filters.d.ts +6 -0
  182. package/dist/src/daemon/github-ci-filters.js +138 -0
  183. package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
  184. package/dist/src/daemon/github-ci-workflows.js +148 -0
  185. package/dist/src/daemon/github-ci.js +22 -4
  186. package/dist/src/daemon/github-issue-methods.d.ts +2 -0
  187. package/dist/src/daemon/github-issue-methods.js +37 -2
  188. package/dist/src/daemon/github-merge-call.d.ts +2 -1
  189. package/dist/src/daemon/github-merge-call.js +10 -3
  190. package/dist/src/daemon/github-merge.js +52 -24
  191. package/dist/src/daemon/github-methods.js +12 -2
  192. package/dist/src/daemon/github-operation.d.ts +4 -0
  193. package/dist/src/daemon/github-operation.js +6 -0
  194. package/dist/src/daemon/github-worker.d.ts +4 -0
  195. package/dist/src/daemon/github-worker.js +6 -0
  196. package/dist/src/daemon/issue-queue.d.ts +1 -1
  197. package/dist/src/daemon/issue-queue.js +16 -11
  198. package/dist/src/daemon/notify-events.d.ts +6 -0
  199. package/dist/src/daemon/notify-events.js +84 -0
  200. package/dist/src/daemon/notify-journal.js +1 -1
  201. package/dist/src/daemon/notify-message.d.ts +4 -1
  202. package/dist/src/daemon/notify-message.js +36 -0
  203. package/dist/src/daemon/notify.d.ts +1 -1
  204. package/dist/src/daemon/notify.js +5 -77
  205. package/dist/src/daemon/operation-journal.d.ts +1 -0
  206. package/dist/src/daemon/queue-status.d.ts +9 -3
  207. package/dist/src/daemon/queue-status.js +17 -4
  208. package/dist/src/daemon/resume-index-lock.d.ts +13 -0
  209. package/dist/src/daemon/resume-index-lock.js +160 -0
  210. package/dist/src/daemon/resume-worktree.d.ts +7 -0
  211. package/dist/src/daemon/resume-worktree.js +29 -0
  212. package/dist/src/daemon/retrying-turn.d.ts +2 -12
  213. package/dist/src/daemon/retrying-turn.js +98 -172
  214. package/dist/src/daemon/schedule-control.d.ts +26 -0
  215. package/dist/src/daemon/schedule-control.js +110 -0
  216. package/dist/src/daemon/schedule-journal.d.ts +2 -15
  217. package/dist/src/daemon/schedule-journal.js +67 -4
  218. package/dist/src/daemon/ticket-registration.d.ts +2 -7
  219. package/dist/src/daemon/ticket-registration.js +15 -81
  220. package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
  221. package/dist/src/daemon/turn-attempt-journal.js +136 -0
  222. package/dist/src/daemon/turn-attempts.d.ts +47 -0
  223. package/dist/src/daemon/turn-attempts.js +166 -0
  224. package/dist/src/daemon/turn-report.d.ts +8 -1
  225. package/dist/src/daemon/turn-report.js +39 -0
  226. package/dist/src/daemon/work-tree.d.ts +9 -0
  227. package/dist/src/daemon/work-tree.js +20 -0
  228. package/dist/src/daemon/worker-profile.d.ts +8 -0
  229. package/dist/src/daemon/worker-profile.js +40 -0
  230. package/dist/src/daemon/workflow-schedules.d.ts +3 -0
  231. package/dist/src/daemon/workflow-schedules.js +98 -45
  232. package/dist/src/daemon/worktree-processes.d.ts +9 -0
  233. package/dist/src/daemon/worktree-processes.js +151 -0
  234. package/dist/src/doctor-report.d.ts +3 -5
  235. package/dist/src/doctor-report.js +6 -13
  236. package/dist/src/harness-start.d.ts +15 -0
  237. package/dist/src/harness-start.js +37 -0
  238. package/dist/src/host/command-wait.d.ts +38 -0
  239. package/dist/src/host/command-wait.js +115 -0
  240. package/dist/src/host/start-delivery.d.ts +5 -7
  241. package/dist/src/host/start-delivery.js +3 -15
  242. package/dist/src/host/step-bridge-host.d.ts +2 -0
  243. package/dist/src/host/step-bridge-host.js +21 -0
  244. package/dist/src/operation-identity.d.ts +1 -0
  245. package/dist/src/operation-identity.js +4 -0
  246. package/dist/src/private-config.schema.json +40 -0
  247. package/dist/src/project-config.schema.json +40 -0
  248. package/dist/src/project-settings.d.ts +2 -2
  249. package/dist/src/project-settings.js +3 -1
  250. package/dist/src/queue-spend.d.ts +4 -0
  251. package/dist/src/queue-spend.js +21 -0
  252. package/dist/src/relay/relay-service.d.ts +3 -0
  253. package/dist/src/relay/relay-service.js +5 -3
  254. package/dist/src/report-ci.d.ts +164 -0
  255. package/dist/src/report-ci.js +32 -0
  256. package/dist/src/report-effects.d.ts +15 -0
  257. package/dist/src/report-effects.js +212 -0
  258. package/dist/src/report-flow-types.d.ts +38 -0
  259. package/dist/src/report-flow-types.js +7 -0
  260. package/dist/src/report-flow.d.ts +43 -0
  261. package/dist/src/report-flow.js +193 -0
  262. package/dist/src/report-operator.d.ts +67 -0
  263. package/dist/src/report-operator.js +25 -0
  264. package/dist/src/report-quality.d.ts +221 -0
  265. package/dist/src/report-quality.js +94 -0
  266. package/dist/src/report-ready.d.ts +23 -0
  267. package/dist/src/report-ready.js +117 -0
  268. package/dist/src/report-statistics.d.ts +2 -1
  269. package/dist/src/report-statistics.js +40 -4
  270. package/dist/src/report.d.ts +588 -47
  271. package/dist/src/report.js +156 -41
  272. package/dist/src/schedule-progress.d.ts +33 -0
  273. package/dist/src/schedule-progress.js +45 -0
  274. package/dist/src/service/active-tickets.js +6 -6
  275. package/dist/src/service/command-waits.d.ts +34 -0
  276. package/dist/src/service/command-waits.js +86 -0
  277. package/dist/src/service/expiry.js +2 -0
  278. package/dist/src/service/gateway.js +3 -1
  279. package/dist/src/service/queue-spend.d.ts +6 -0
  280. package/dist/src/service/queue-spend.js +68 -0
  281. package/dist/src/service/replies.d.ts +8 -1
  282. package/dist/src/service/replies.js +11 -0
  283. package/dist/src/service/run-handlers.js +22 -2
  284. package/dist/src/service/run-metadata.d.ts +15 -1
  285. package/dist/src/service/run-metadata.js +93 -8
  286. package/dist/src/service/service.d.ts +4 -0
  287. package/dist/src/service/service.js +8 -8
  288. package/dist/src/service/submission-handler.js +18 -4
  289. package/dist/src/service/worker-port.js +9 -1
  290. package/dist/src/service/worker-totals.d.ts +2 -0
  291. package/dist/src/service/worker-totals.js +1 -1
  292. package/dist/src/step-bridge.d.ts +20 -0
  293. package/dist/src/steps/command-wait.d.ts +12 -0
  294. package/dist/src/steps/command-wait.js +26 -0
  295. package/dist/src/steps/open-decision-wait.d.ts +2 -0
  296. package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
  297. package/dist/src/steps/review-by-person.js +4 -1
  298. package/dist/src/steps/wait-for-command.d.ts +3 -0
  299. package/dist/src/steps/wait-for-command.js +17 -0
  300. package/dist/src/steps/wait-for-decision.js +1 -1
  301. package/dist/src/storage/command-wait-rows.d.ts +30 -0
  302. package/dist/src/storage/command-wait-rows.js +96 -0
  303. package/dist/src/storage/decision-snapshots.d.ts +8 -1
  304. package/dist/src/storage/decision-snapshots.js +15 -1
  305. package/dist/src/storage/domain-changes.d.ts +2 -0
  306. package/dist/src/storage/domain-changes.js +3 -0
  307. package/dist/src/storage/domain-schema.d.ts +13 -1
  308. package/dist/src/storage/domain-schema.js +31 -4
  309. package/dist/src/storage/effect-schema.d.ts +1 -0
  310. package/dist/src/storage/effect-schema.js +5 -0
  311. package/dist/src/testing-requests.d.ts +15 -0
  312. package/dist/src/testing-requests.js +93 -0
  313. package/dist/src/testing.d.ts +4 -10
  314. package/dist/src/testing.js +33 -86
  315. package/dist/src/worker-usage.d.ts +1 -0
  316. package/dist/src/worker-usage.js +3 -0
  317. package/dist/src/workflow-candidate.d.ts +1 -0
  318. package/dist/src/workflow-candidate.js +12 -1
  319. package/dist/src/workflow-contracts.d.ts +1 -0
  320. package/dist/src/workflow-contracts.js +9 -2
  321. package/dist/src/workflow-discovery.d.ts +2 -13
  322. package/dist/src/workflow-discovery.js +1 -13
  323. package/dist/src/workflow-refusals.js +0 -1
  324. package/dist/src/workflows.d.ts +1 -1
  325. package/dist/src/workflows.js +1 -1
  326. package/dist/tools/red-green-command.d.ts +1 -0
  327. package/dist/tools/red-green-command.js +174 -0
  328. package/dist/tools/red-green-process.d.ts +10 -0
  329. package/dist/tools/red-green-process.js +59 -0
  330. package/dist/tools/self-host-processes.d.ts +2 -0
  331. package/dist/tools/self-host-processes.js +32 -13
  332. package/dist/tools/self-host-project.js +13 -78
  333. package/dist/tools/self-host-runtime.js +1 -1
  334. package/dist/tools/self-host.js +42 -10
  335. package/dist/tools/template-command.d.ts +1 -0
  336. package/dist/tools/template-command.js +160 -0
  337. package/dist/tools/template-copy.d.ts +11 -0
  338. package/dist/tools/template-copy.js +130 -0
  339. package/dist/tools/template-files.d.ts +17 -0
  340. package/dist/tools/template-files.js +74 -0
  341. package/dist/tools/workflow-bundles.js +1 -0
  342. package/dist/tools/workflow-candidate-counts.js +2 -2
  343. package/dist/tools/workflow-candidate-shape.d.ts +3 -0
  344. package/dist/tools/workflow-candidate-shape.js +90 -0
  345. package/dist/tools/workflow-candidate-source.js +15 -1
  346. package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
  347. package/dist/tools/workflow-candidate-syntax.js +24 -94
  348. package/dist/tools/workflow-command.js +1 -2
  349. package/dist/tools/workflow-folders.d.ts +2 -3
  350. package/dist/tools/workflow-folders.js +1 -1
  351. package/package.json +16 -8
  352. package/templates/develop/README.md +12 -0
  353. package/templates/develop/develop/flow-tests.ts +37 -0
  354. package/templates/develop/develop/pull.ts +5 -1
  355. package/templates/develop/develop/refusal-tests.ts +77 -0
  356. package/templates/develop/develop/turn.ts +2 -1
  357. package/templates/idea-to-issue/README.md +27 -0
  358. package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
  359. package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
  360. package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
  361. package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
  362. package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
  363. package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
  364. package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
  365. package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
  366. package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
  367. package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
  368. package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
  369. package/templates/idea-to-issue/idea-to-issue.ts +49 -0
  370. package/templates/idea-to-issue/template.json +81 -0
  371. package/templates/review-day/README.md +97 -0
  372. package/templates/review-day/review-day/critique.ts +58 -0
  373. package/templates/review-day/review-day/decision.ts +29 -0
  374. package/templates/review-day/review-day/facts.ts +69 -0
  375. package/templates/review-day/review-day/label.ts +29 -0
  376. package/templates/review-day/review-day/next.ts +28 -0
  377. package/templates/review-day/review-day/post.ts +27 -0
  378. package/templates/review-day/review-day/report.ts +98 -0
  379. package/templates/review-day/review-day/rules.ts +12 -0
  380. package/templates/review-day/review-day/scenarios.ts +76 -0
  381. package/templates/review-day/review-day/turn.ts +73 -0
  382. package/templates/review-day/review-day.declaration.json +16 -0
  383. package/templates/review-day/review-day.test.ts +362 -0
  384. package/templates/review-day/review-day.ts +37 -0
  385. package/templates/review-day/template.json +56 -0
  386. package/dist/src/cli/watch-start-view.d.ts +0 -18
  387. package/dist/src/cli/watch-start-view.js +0 -77
  388. package/dist/src/steps/decision-gate.d.ts +0 -3
  389. package/dist/workflows/develop-prd.d.ts +0 -7
  390. package/dist/workflows/develop-prd.js +0 -165
  391. package/dist/workflows/develop-ticket.d.ts +0 -9
  392. package/dist/workflows/develop-ticket.js +0 -207
  393. package/dist/workflows/issue-labels.d.ts +0 -16
  394. package/dist/workflows/issue-labels.js +0 -70
  395. package/dist/workflows/ticket-prompts.d.ts +0 -35
  396. package/dist/workflows/ticket-prompts.js +0 -147
@@ -1,11 +1,11 @@
1
+ import { reportQualityMetrics, } from "./report-quality.js";
1
2
  import { reportMetric, outsideReportBand, } from "./report-statistics.js";
2
- export const reportOutcomes = [
3
- "completed",
4
- "partial",
5
- "blocked",
6
- "failed",
7
- "cancelled",
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 metrics = outcomeMetrics(input, window);
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["cost.medianRunUsd"] = costMetric;
41
- const githubWindow = {
42
- ...window,
43
- observedSince: input.github?.observedSince ?? null,
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
- baseline: costMetric.baseline,
60
- ...(outsideReportBand(cost.costUsd, costMetric.baseline.band)
61
- ? { mark: "outside" }
62
- : {}),
130
+ ...costHistory(cost.costUsd, costMetric.baseline),
63
131
  }));
64
- return { metrics, runCosts };
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: "currently-closed-completed",
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
- coverage: {
91
- ci: unavailable,
92
- flow: unavailable,
93
- quality: unavailable,
94
- operator: unavailable,
95
- phases: unavailable,
96
- fixRounds: unavailable,
97
- leadTime: unavailable,
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 turns = costs.find((cost) => cost.runId === event.runId)?.turns ?? null;
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 === "develop-issue" ||
10
- context.registry.byId(workflowId)?.queue === true ||
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
+ }