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
@@ -8,7 +8,7 @@ function queueBusy(value) {
8
8
  throw new Error("invalid-queue-status");
9
9
  return busy;
10
10
  }
11
- export async function activeQueueRuns(settings) {
11
+ export async function queueStatus(settings) {
12
12
  let count = 0;
13
13
  let after = "";
14
14
  do {
@@ -20,13 +20,17 @@ export async function activeQueueRuns(settings) {
20
20
  throw new Error(reply.error.code);
21
21
  const busy = queueBusy(reply.result["queue"]);
22
22
  if (busy !== undefined)
23
- return busy;
23
+ return {
24
+ busy,
25
+ holding: record(record(reply.result["queue"])?.["spend"])?.["holding"] ===
26
+ true,
27
+ };
24
28
  const runs = reply.result["runs"];
25
29
  if (!Array.isArray(runs))
26
30
  throw new Error("invalid-run-list");
27
31
  count += runs.filter((value) => {
28
32
  const run = record(value);
29
- return (run?.["workflowId"] === (settings.workflowId ?? "develop-issue") &&
33
+ return (run?.["workflowId"] === settings.workflowId &&
30
34
  !["completed", "failed", "cancelled"].includes(String(run["state"])));
31
35
  }).length;
32
36
  const next = reply.result["next"];
@@ -34,5 +38,14 @@ export async function activeQueueRuns(settings) {
34
38
  throw new Error("invalid-run-page");
35
39
  after = next;
36
40
  } while (after !== null);
37
- return count;
41
+ return { busy: count, holding: false };
42
+ }
43
+ export function spendNotice() {
44
+ let held = false;
45
+ return ({ holding }) => {
46
+ if (holding && !held)
47
+ process.stderr.write("Queue holding for spend limit\n");
48
+ held = holding;
49
+ return holding;
50
+ };
38
51
  }
@@ -0,0 +1,13 @@
1
+ interface LockIdentity {
2
+ readonly path: string;
3
+ readonly dev: number;
4
+ readonly ino: number;
5
+ readonly ctimeMs: number;
6
+ readonly mtimeMs: number;
7
+ }
8
+ export interface OwnedIndexLock extends LockIdentity {
9
+ readonly processIds: readonly number[];
10
+ }
11
+ export declare function ownedIndexLock(path: string): Promise<OwnedIndexLock | undefined>;
12
+ export declare function removeStoppedIndexLock(lock: OwnedIndexLock, signal: AbortSignal): Promise<boolean>;
13
+ export {};
@@ -0,0 +1,160 @@
1
+ import { execFile } from "node:child_process";
2
+ import { lstat, readdir, readFile, stat, unlink } from "node:fs/promises";
3
+ import { promisify } from "node:util";
4
+ const execute = promisify(execFile);
5
+ function disappeared(error) {
6
+ return (error instanceof Error &&
7
+ "code" in error &&
8
+ (error.code === "ENOENT" || error.code === "ESRCH"));
9
+ }
10
+ async function descriptorUsesLock(path, lock) {
11
+ try {
12
+ const opened = await stat(path);
13
+ if (opened.dev !== lock.dev || opened.ino !== lock.ino)
14
+ return false;
15
+ const information = await readFile(path.replace("/fd/", "/fdinfo/"), "utf8");
16
+ const flags = /^flags:\s+([0-7]+)$/m.exec(information)?.[1];
17
+ const inode = /^ino:\s+(\d+)$/m.exec(information)?.[1];
18
+ if (flags === undefined || Number(inode) !== lock.ino)
19
+ return undefined;
20
+ return (Number.parseInt(flags, 8) & 3) !== 0;
21
+ }
22
+ catch (error) {
23
+ return disappeared(error) ? false : undefined;
24
+ }
25
+ }
26
+ async function processUsesLock(directory, lock) {
27
+ try {
28
+ for (const descriptor of await readdir(`${directory}/fd`)) {
29
+ const used = await descriptorUsesLock(`${directory}/fd/${descriptor}`, lock);
30
+ if (used !== false)
31
+ return used;
32
+ }
33
+ return false;
34
+ }
35
+ catch (error) {
36
+ if (disappeared(error))
37
+ return false;
38
+ if (!(error instanceof Error) ||
39
+ !("code" in error) ||
40
+ (error.code !== "EACCES" && error.code !== "EPERM"))
41
+ return undefined;
42
+ return (await unrelatedProcess(directory, lock)) ? false : undefined;
43
+ }
44
+ }
45
+ async function unrelatedProcess(directory, lock) {
46
+ if (lock.uid !== process.geteuid?.() || (lock.mode & 0o022) !== 0)
47
+ return false;
48
+ try {
49
+ const status = await readFile(`${directory}/status`, "utf8");
50
+ const users = /^Uid:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)$/m.exec(status);
51
+ return (users !== null &&
52
+ users
53
+ .slice(1)
54
+ .map(Number)
55
+ .every((user) => Number.isSafeInteger(user) && user !== lock.uid));
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ async function lockOwners(lock) {
62
+ if (process.platform === "linux") {
63
+ const processIds = [];
64
+ for (const entry of await readdir("/proc")) {
65
+ if (!/^\d+$/.test(entry))
66
+ continue;
67
+ const used = await processUsesLock(`/proc/${entry}`, lock);
68
+ if (used === undefined)
69
+ return undefined;
70
+ if (used)
71
+ processIds.push(Number(entry));
72
+ }
73
+ return processIds;
74
+ }
75
+ try {
76
+ const { stdout, stderr } = await execute("lsof", ["-Fpfa", "--", lock.path], {
77
+ encoding: "utf8",
78
+ timeout: 5000,
79
+ maxBuffer: 8 * 1024 * 1024,
80
+ });
81
+ return stderr === "" ? writableOwners(stdout) : undefined;
82
+ }
83
+ catch {
84
+ return undefined;
85
+ }
86
+ }
87
+ function writableOwner(record) {
88
+ const [header, ...descriptors] = record.trim().split(/^f/m);
89
+ const processId = Number(header?.trim().slice(1));
90
+ const access = descriptors.map((descriptor) => descriptor.split("\n")[1]);
91
+ if (!Number.isSafeInteger(processId) ||
92
+ processId < 1 ||
93
+ descriptors.length === 0 ||
94
+ access.some((mode) => mode !== "ar" && mode !== "aw" && mode !== "au"))
95
+ return undefined;
96
+ return access.some((mode) => mode === "aw" || mode === "au")
97
+ ? processId
98
+ : null;
99
+ }
100
+ function writableOwners(text) {
101
+ const owners = text
102
+ .trim()
103
+ .split(/(?=^p)/m)
104
+ .map(writableOwner);
105
+ return owners.includes(undefined)
106
+ ? undefined
107
+ : [
108
+ ...new Set(owners.filter((owner) => typeof owner === "number")),
109
+ ];
110
+ }
111
+ export async function ownedIndexLock(path) {
112
+ try {
113
+ const identity = await lstat(path);
114
+ if (!identity.isFile())
115
+ return undefined;
116
+ const lock = {
117
+ path,
118
+ dev: identity.dev,
119
+ ino: identity.ino,
120
+ ctimeMs: identity.ctimeMs,
121
+ mtimeMs: identity.mtimeMs,
122
+ uid: identity.uid,
123
+ mode: identity.mode,
124
+ };
125
+ const processIds = await lockOwners(lock);
126
+ return processIds === undefined || processIds.length === 0
127
+ ? undefined
128
+ : { ...lock, processIds };
129
+ }
130
+ catch {
131
+ return undefined;
132
+ }
133
+ }
134
+ function stopped(processId) {
135
+ try {
136
+ process.kill(processId, 0);
137
+ return false;
138
+ }
139
+ catch (error) {
140
+ return error instanceof Error && "code" in error && error.code === "ESRCH";
141
+ }
142
+ }
143
+ export async function removeStoppedIndexLock(lock, signal) {
144
+ try {
145
+ if (!lock.processIds.every(stopped))
146
+ return false;
147
+ const after = await lstat(lock.path);
148
+ if (signal.aborted ||
149
+ lock.dev !== after.dev ||
150
+ lock.ino !== after.ino ||
151
+ lock.ctimeMs !== after.ctimeMs ||
152
+ lock.mtimeMs !== after.mtimeMs)
153
+ return false;
154
+ await unlink(lock.path);
155
+ return true;
156
+ }
157
+ catch {
158
+ return false;
159
+ }
160
+ }
@@ -0,0 +1,7 @@
1
+ import { type IndexFailure } from "./retry-worktree.ts";
2
+ export declare function resumeWorktree(request: {
3
+ readonly directory: string;
4
+ readonly folder: string;
5
+ readonly signal: AbortSignal;
6
+ readonly stopMilliseconds?: number;
7
+ }): Promise<IndexFailure | null>;
@@ -0,0 +1,29 @@
1
+ import { ownedIndexLock, removeStoppedIndexLock } from "./resume-index-lock.js";
2
+ import { stopTreeProcesses } from "./worktree-processes.js";
3
+ import { indexFailure } from "./retry-worktree.js";
4
+ async function inspectLock(failure) {
5
+ if (failure?.lockPath === undefined)
6
+ return undefined;
7
+ return ownedIndexLock(failure.lockPath);
8
+ }
9
+ export async function resumeWorktree(request) {
10
+ const aborted = () => request.signal.aborted;
11
+ const before = await indexFailure(request);
12
+ const lock = await inspectLock(before);
13
+ if (aborted())
14
+ return before;
15
+ const stopped = await stopTreeProcesses(request);
16
+ const locked = await indexFailure(request);
17
+ if (!stopped)
18
+ return (locked ?? {
19
+ reason: "worktree-index-locked",
20
+ fix: "Stop the remaining work tree processes before resuming the turn.",
21
+ });
22
+ if (locked?.reason !== "worktree-index-locked" || aborted())
23
+ return locked;
24
+ if (lock === undefined ||
25
+ lock.path !== locked.lockPath ||
26
+ !(await removeStoppedIndexLock(lock, request.signal)))
27
+ return locked;
28
+ return indexFailure(request);
29
+ }
@@ -1,14 +1,4 @@
1
- import { type TokenPrices } from "../token-prices.ts";
2
1
  import { type RunningTurn, type TurnStart } from "./claude-turn.ts";
3
- export interface RetrySettings {
4
- readonly tokenPrices?: TokenPrices;
5
- readonly fallbackModels?: readonly string[];
6
- readonly retryDelay?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
7
- }
8
- interface RetryOptions extends RetrySettings {
9
- readonly stopping?: AbortController;
10
- readonly stopMilliseconds?: number;
11
- readonly metadata: Readonly<Record<string, unknown>>;
12
- }
2
+ import { type RetryOptions } from "./turn-attempts.ts";
3
+ export type { RetrySettings } from "./turn-attempts.ts";
13
4
  export declare function startRetriedTurn(start: TurnStart, options: RetryOptions): Promise<RunningTurn>;
14
- export {};
@@ -1,41 +1,17 @@
1
- import { join } from "node:path";
2
- import { mkdir } from "node:fs/promises";
3
1
  import { setTimeout as delay } from "node:timers/promises";
4
- import { priceOf, sumUsage, usageOf, } from "../worker-usage.js";
5
- import { commandModel, replaceCommandModel, estimatedCostUsd, priceForModel, } from "../token-prices.js";
6
- import { capacityRefusal, retryKind, startTurn, } from "./claude-turn.js";
2
+ import { startTurn, } from "./claude-turn.js";
7
3
  import { turnHead, retryEvidence, indexFailure } from "./retry-worktree.js";
8
4
  import { readRecord, writeRecord } from "./operation-journal.js";
9
- import { nativeHarness } from "../harness-defaults.js";
5
+ import { resumeWorktree } from "./resume-worktree.js";
6
+ import { readResume, resumedCommand, resumePrompt } from "./claude-recovery.js";
7
+ import { attemptCommand, combined, kindOf, } from "./turn-attempts.js";
8
+ import { prepareAttempt, attemptJournal, recordedEnd, } from "./turn-attempt-journal.js";
10
9
  const waits = { capacity: [60_000, 180_000], network: [600_000, 2_400_000] };
11
10
  const networkContinuation = "An earlier attempt stopped because the network was not available. Continue from the current state of the work tree. Check each earlier commit, push, review comment and message. Do not repeat a write that is done. Stop and report a write whose result is not known.";
12
11
  const continuation = "An earlier attempt stopped because the model was at capacity; continue from the current state of the work tree.\n\n";
13
12
  function written(folder, entry) {
14
13
  return writeRecord(folder, { ...entry, permissions: "private" });
15
14
  }
16
- function total(values) {
17
- if (values.includes(null))
18
- return null;
19
- return priceOf(values.reduce((sum, value) => sum + (value ?? 0), 0));
20
- }
21
- function combined(attempts) {
22
- const { ends } = attempts;
23
- const last = ends.at(-1) ?? { exitCode: null, result: undefined };
24
- const result = last.result ?? ends.find((end) => end.result)?.result;
25
- if (result === undefined)
26
- return last;
27
- return {
28
- ...last,
29
- result: {
30
- ...result,
31
- attempts: ends.map((end, index) => attemptUsage(attempts, { end, attempt: index + 1 })),
32
- ...(last.result === undefined ? { failed: true, text: "" } : {}),
33
- usage: sumUsage(ends.map((end) => end.result?.usage ?? usageOf(null))),
34
- costUsd: total(ends.map((end) => end.result?.costUsd ?? null)),
35
- turns: total(ends.map((end) => end.result?.turns ?? null)),
36
- },
37
- };
38
- }
39
15
  function controlled(attempts, start) {
40
16
  const turn = startTurn(start);
41
17
  const stop = () => {
@@ -49,66 +25,6 @@ function controlled(attempts, start) {
49
25
  }),
50
26
  };
51
27
  }
52
- function attemptCommand(attempts, attempt) {
53
- const { start, options } = attempts;
54
- const original = start.command;
55
- const previous = attempts.ends[attempt - 2];
56
- const fallback = previous !== undefined && kindOf(attempts, previous) === "capacity"
57
- ? options.fallbackModels?.[attempt - 3]
58
- : undefined;
59
- const command = fallback === undefined ? original : replaceCommandModel(original, fallback);
60
- const model = start.output === "text" ? null : commandModel(command);
61
- return {
62
- command,
63
- model,
64
- ...(fallback !== undefined && model === null
65
- ? { fallbackReason: "fallback-model-unavailable" }
66
- : {}),
67
- };
68
- }
69
- function attemptUsage(attempts, completion) {
70
- const { end, attempt } = completion;
71
- const result = end.result ?? {
72
- model: null,
73
- usage: usageOf(null),
74
- costUsd: null,
75
- pricingModel: undefined,
76
- };
77
- const model = result.model ?? attemptCommand(attempts, attempt).model;
78
- const usage = result.usage;
79
- return {
80
- attempt,
81
- retryKind: kindOf(attempts, end),
82
- model,
83
- ...(end.result?.models === undefined ? {} : { models: end.result.models }),
84
- usage,
85
- costUsd: result.costUsd,
86
- estimatedCostUsd: attemptEstimate(attempts, { end, model, usage }),
87
- };
88
- }
89
- function attemptEstimate(attempts, result) {
90
- const worker = attempts.start.worker ?? "claude";
91
- if (!nativeHarness(worker))
92
- return null;
93
- const models = result.end.result?.models;
94
- const prices = attempts.options.tokenPrices ?? {};
95
- return estimatedCostUsd(result.usage, worker === "gemini" && models !== undefined
96
- ? { worker, models, prices }
97
- : {
98
- worker,
99
- price: priceForModel(prices, result.end.result?.pricingModel === undefined
100
- ? result.model
101
- : result.end.result.pricingModel),
102
- });
103
- }
104
- function kindOf(attempts, end) {
105
- const { worker, output } = attempts.start;
106
- if (output === "text" || !["claude", "codex"].includes(worker ?? "claude"))
107
- return null;
108
- if (worker !== "codex")
109
- return retryKind(end.result);
110
- return capacityRefusal(end.result) ? "capacity" : null;
111
- }
112
28
  async function promptOf(attempts) {
113
29
  const { start, ends, stopping, turnStartCommit } = attempts;
114
30
  if (ends.length === 0)
@@ -118,53 +34,42 @@ async function promptOf(attempts) {
118
34
  const evidence = await retryEvidence({ directory: start.directory, signal: stopping.signal }, turnStartCommit);
119
35
  return `${networkContinuation}\n\nCommits since the start of this turn:\n${evidence}\n\n${start.prompt}`;
120
36
  }
37
+ function launchCommand(attempts, prepared) {
38
+ const { entry, selected } = prepared;
39
+ if (entry.resumedAfter === undefined)
40
+ return selected.command;
41
+ return (resumedCommand({
42
+ worker: attempts.start.worker,
43
+ command: selected.command,
44
+ sessionId: attempts.options.resume?.sessionId ?? "",
45
+ }) ?? selected.command);
46
+ }
121
47
  async function launched(attempts, prompt = attempts.start.prompt) {
122
- const { start, options, ends } = attempts;
123
- const attempt = ends.length + 1;
124
- const selected = attemptCommand(attempts, attempt);
125
- const folder = attempt === 1
126
- ? start.folder
127
- : join(start.folder, `attempt-${String(attempt)}`);
128
- const entry = {
129
- ...options.metadata,
130
- turnStartCommit: attempts.turnStartCommit,
131
- model: selected.model,
132
- ...(selected.fallbackReason === undefined
133
- ? {}
134
- : { fallbackReason: selected.fallbackReason }),
135
- attempt,
136
- startedAt: attempt === 1 ? attempts.startedAt : new Date().toISOString(),
137
- };
138
- await written(start.folder, {
139
- name: "started.json",
140
- value: {
141
- ...entry,
142
- startedAt: attempts.startedAt,
143
- phase: "starting",
144
- processId: null,
145
- },
146
- });
147
- await mkdir(folder, { recursive: true, mode: 0o700 });
148
- if (attempts.stopping.signal.aborted)
48
+ const prepared = await prepareAttempt(attempts);
49
+ if (prepared === undefined || attempts.stopping.signal.aborted)
149
50
  return undefined;
51
+ const { start } = attempts;
52
+ const { entry, folder } = prepared;
53
+ const journal = attemptJournal(attempts, entry);
150
54
  const turn = controlled(attempts, {
151
55
  ...start,
152
- command: selected.command,
56
+ command: launchCommand(attempts, prepared),
57
+ progress: journal.progress,
153
58
  folder,
154
- ...(attempt === 1 ? {} : { activityFolder: start.folder }),
155
- prompt,
59
+ ...(entry.attempt === 1 ? {} : { activityFolder: start.folder }),
60
+ prompt: entry.resumedAfter === undefined ? prompt : resumePrompt,
156
61
  });
157
62
  try {
158
- const value = { ...entry, phase: "running", processId: turn.processId };
159
- await written(start.folder, {
160
- name: "started.json",
161
- value: { ...value, startedAt: attempts.startedAt },
162
- });
163
- await written(start.folder, {
164
- name: `attempt-${String(attempt)}.json`,
165
- value,
166
- });
167
- return turn;
63
+ await journal.running(turn.processId);
64
+ return {
65
+ ...turn,
66
+ ended: turn.ended.then((end) => ({
67
+ ...end,
68
+ ...(entry.resumedAfter === undefined
69
+ ? {}
70
+ : { resumedAfter: entry.resumedAfter }),
71
+ })),
72
+ };
168
73
  }
169
74
  catch (error) {
170
75
  turn.stop(0);
@@ -172,34 +77,6 @@ async function launched(attempts, prompt = attempts.start.prompt) {
172
77
  throw error;
173
78
  }
174
79
  }
175
- async function recordedEnd(attempts, completion) {
176
- const { start, ends, options } = attempts;
177
- const { processId, end } = completion;
178
- const attempt = ends.length;
179
- const selected = attemptCommand(attempts, attempt);
180
- const name = `attempt-${String(attempt)}.json`;
181
- const started = await readRecord(start.folder, name);
182
- const startedAt = Reflect.get(Object(started), "startedAt");
183
- if (typeof startedAt !== "string")
184
- throw new Error("attempt-start-time-missing");
185
- await written(start.folder, {
186
- name,
187
- value: {
188
- ...options.metadata,
189
- turnStartCommit: attempts.turnStartCommit,
190
- ...attemptUsage(attempts, { end, attempt }),
191
- ...(selected.fallbackReason === undefined
192
- ? {}
193
- : { fallbackReason: selected.fallbackReason }),
194
- attempt,
195
- processId,
196
- startedAt,
197
- endedAt: new Date().toISOString(),
198
- exitCode: end.exitCode,
199
- capacity: capacityRefusal(end.result),
200
- },
201
- });
202
- }
203
80
  async function waited(attempts, milliseconds) {
204
81
  const { start, options, stopping, ends } = attempts;
205
82
  await written(start.folder, {
@@ -250,8 +127,9 @@ async function finished(attempts, first) {
250
127
  const end = await turn.ended;
251
128
  ends.push(end);
252
129
  await recordedEnd(attempts, { processId: turn.processId, end });
253
- const kind = kindOf(attempts, end);
254
- const milliseconds = kind === null ? undefined : waits[kind][ends.length - 1];
130
+ if (interruptedChild(attempts, { processId: turn.processId, end }))
131
+ return resumedChild(attempts);
132
+ const milliseconds = retryWait(attempts, end);
255
133
  if (cancelled(attempts) || milliseconds === undefined)
256
134
  break;
257
135
  await waited(attempts, milliseconds);
@@ -262,9 +140,59 @@ async function finished(attempts, first) {
262
140
  return prepared;
263
141
  turn = await launched(attempts, prepared);
264
142
  }
143
+ return attemptOutcome(attempts);
144
+ }
145
+ function interruptedChild(attempts, completion) {
146
+ const { start } = attempts;
147
+ const { processId, end } = completion;
148
+ return (!cancelled(attempts) &&
149
+ (start.worker ?? "claude") === "claude" &&
150
+ start.output !== "text" &&
151
+ processId !== null &&
152
+ end.exitCode !== 0 &&
153
+ end.result === undefined);
154
+ }
155
+ async function resumedChild(attempts) {
156
+ const started = await readRecord(attempts.start.folder, "started.json");
157
+ const resume = await readResume(attempts.start.folder, started);
158
+ if (resume === undefined)
159
+ return { ...combined(attempts), reason: "claude-turn-interrupted" };
160
+ const locked = await resumeWorktree({
161
+ directory: attempts.start.directory,
162
+ folder: attempts.start.folder,
163
+ signal: attempts.stopping.signal,
164
+ stopMilliseconds: attempts.grace.milliseconds,
165
+ });
166
+ if (locked !== null)
167
+ return { ...combined(attempts), reason: locked.reason };
168
+ const prepared = await preparedRetry(attempts);
169
+ if (typeof prepared !== "string")
170
+ return prepared;
171
+ const recovered = {
172
+ ...attempts,
173
+ options: { ...attempts.options, resume },
174
+ ends: [...resume.ends],
175
+ };
176
+ const turn = await launched(recovered);
177
+ return turn === undefined
178
+ ? attemptOutcome(recovered)
179
+ : finished(recovered, turn);
180
+ }
181
+ function retryWait(attempts, end) {
182
+ const kind = kindOf(attempts, end);
183
+ if (kind === null)
184
+ return undefined;
185
+ if (attemptCommand(attempts, attempts.ends.length + 1).budgetUsd === 0) {
186
+ attempts.failure = "claude-turn-budget-exhausted";
187
+ return undefined;
188
+ }
189
+ return waits[kind][attempts.ends.filter((end) => end.resumedAfter === undefined).length - 1];
190
+ }
191
+ function attemptOutcome(attempts) {
265
192
  return {
266
193
  ...combined(attempts),
267
- ...(cancelled(attempts) || turn === undefined ? { stopped: true } : {}),
194
+ ...(attempts.failure === undefined ? {} : { reason: attempts.failure }),
195
+ ...(cancelled(attempts) ? { stopped: true } : {}),
268
196
  };
269
197
  }
270
198
  function cancelled(attempts) {
@@ -272,28 +200,26 @@ function cancelled(attempts) {
272
200
  }
273
201
  export async function startRetriedTurn(start, options) {
274
202
  const stopping = options.stopping ?? new AbortController();
275
- const turnStartCommit = await turnHead({
276
- directory: start.directory,
277
- signal: stopping.signal,
278
- });
203
+ const turnStartCommit = options.resume === undefined
204
+ ? await turnHead({
205
+ directory: start.directory,
206
+ signal: stopping.signal,
207
+ })
208
+ : options.resume.turnStartCommit;
279
209
  const attempts = {
280
210
  turnStartCommit,
281
211
  start,
282
- startedAt: new Date().toISOString(),
212
+ startedAt: options.resume?.startedAt ?? new Date().toISOString(),
283
213
  options,
284
214
  stopping,
285
- ends: [],
286
- grace: { milliseconds: options.stopMilliseconds ?? 2_000 },
215
+ ends: [...(options.resume?.ends ?? [])],
216
+ grace: { milliseconds: options.stopMilliseconds ?? 15_000 },
287
217
  };
288
218
  const turn = await launched(attempts);
289
219
  if (turn === undefined)
290
220
  return {
291
221
  processId: null,
292
- ended: Promise.resolve({
293
- exitCode: null,
294
- result: undefined,
295
- stopped: true,
296
- }),
222
+ ended: Promise.resolve(attemptOutcome(attempts)),
297
223
  stop: () => {
298
224
  stopping.abort();
299
225
  },
@@ -0,0 +1,26 @@
1
+ import type { SchedulePort, ScheduleProgress } from "../schedule-progress.ts";
2
+ import type { DeclaredWorkflow } from "../workflow-declaration.ts";
3
+ import type { scheduleJournal } from "./schedule-journal.ts";
4
+ type Journal = Awaited<ReturnType<typeof scheduleJournal>>;
5
+ interface ScheduleState {
6
+ readonly journal: Journal;
7
+ readonly declarations: readonly DeclaredWorkflow[];
8
+ readonly clock: () => number;
9
+ readonly recoverPending: (entry: {
10
+ readonly workflowId: string;
11
+ readonly progress: ScheduleProgress;
12
+ }) => Promise<ScheduleProgress>;
13
+ }
14
+ export declare function scheduleConnection(): {
15
+ readonly request: SchedulePort;
16
+ readonly connect: (port: SchedulePort) => void;
17
+ };
18
+ export declare function scheduleControl(settings: ScheduleState & {
19
+ readonly signal: AbortSignal;
20
+ readonly tick: () => Promise<void>;
21
+ }): {
22
+ readonly request: SchedulePort;
23
+ readonly tick: () => Promise<void>;
24
+ readonly drain: () => Promise<void>;
25
+ };
26
+ export {};