cloving 1.3.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (484) hide show
  1. package/README.md +329 -215
  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-health.d.ts +9 -0
  32. package/dist/src/broker/actor/authority-health.js +24 -0
  33. package/dist/src/broker/actor/authority-queries.d.ts +2 -1
  34. package/dist/src/broker/actor/authority-queries.js +14 -5
  35. package/dist/src/broker/actor/authority-receipts.js +1 -1
  36. package/dist/src/broker/actor/authority-runtime.d.ts +4 -2
  37. package/dist/src/broker/actor/authority-runtime.js +29 -20
  38. package/dist/src/broker/actor/authority-session.d.ts +6 -19
  39. package/dist/src/broker/actor/authority-session.js +66 -88
  40. package/dist/src/broker/actor/authority-settings.d.ts +2 -1
  41. package/dist/src/broker/actor/authority-settings.js +6 -3
  42. package/dist/src/broker/actor/authority-upgrades.d.ts +23 -2
  43. package/dist/src/broker/actor/authority-upgrades.js +8 -2
  44. package/dist/src/broker/actor/authority-watchdog.d.ts +6 -2
  45. package/dist/src/broker/actor/authority-watchdog.js +31 -9
  46. package/dist/src/broker/actor/authority.js +3 -1
  47. package/dist/src/broker/actor/delivery-intents.js +2 -0
  48. package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
  49. package/dist/src/broker/actor/effect-delivery.js +15 -2
  50. package/dist/src/broker/actor/effect-plan.js +11 -2
  51. package/dist/src/broker/actor/effect-queries.d.ts +4 -0
  52. package/dist/src/broker/actor/effect-queries.js +9 -1
  53. package/dist/src/broker/actor/effect-turn.js +2 -0
  54. package/dist/src/broker/actor/projection-memory.d.ts +43 -0
  55. package/dist/src/broker/actor/projection-memory.js +207 -0
  56. package/dist/src/broker/actor/runtime-parts.d.ts +5 -0
  57. package/dist/src/broker/actor/runtime-parts.js +8 -3
  58. package/dist/src/broker/actor/stop-plan.js +9 -3
  59. package/dist/src/broker/actor/stop-turn.d.ts +2 -0
  60. package/dist/src/broker/actor/stop-turn.js +24 -11
  61. package/dist/src/broker/actor/turn-draft.d.ts +1 -0
  62. package/dist/src/broker/actor/turn-draft.js +4 -0
  63. package/dist/src/broker/actor/turn-state.js +5 -1
  64. package/dist/src/broker/authority-admission.js +60 -0
  65. package/dist/src/broker/authority-restore.d.ts +2 -0
  66. package/dist/src/broker/authority-restore.js +35 -1
  67. package/dist/src/broker/command-wait-restore.d.ts +31 -0
  68. package/dist/src/broker/command-wait-restore.js +86 -0
  69. package/dist/src/broker/decision-restore.d.ts +1 -1
  70. package/dist/src/broker/decision-restore.js +1 -0
  71. package/dist/src/broker/effect-upgrade.d.ts +3 -0
  72. package/dist/src/broker/effect-upgrade.js +11 -2
  73. package/dist/src/cli/arguments.d.ts +13 -0
  74. package/dist/src/cli/arguments.js +43 -3
  75. package/dist/src/cli/chat-stores.d.ts +2 -0
  76. package/dist/src/cli/chat-stores.js +113 -0
  77. package/dist/src/cli/client.d.ts +7 -2
  78. package/dist/src/cli/client.js +13 -7
  79. package/dist/src/cli/command-requests.d.ts +46 -0
  80. package/dist/src/cli/command-requests.js +84 -0
  81. package/dist/src/cli/command-waits.d.ts +9 -0
  82. package/dist/src/cli/command-waits.js +51 -0
  83. package/dist/src/cli/commands.js +70 -88
  84. package/dist/src/cli/config-file.d.ts +2 -2
  85. package/dist/src/cli/config-file.js +4 -2
  86. package/dist/src/cli/config-notice.js +5 -2
  87. package/dist/src/cli/doctor-retention.d.ts +2 -0
  88. package/dist/src/cli/doctor-retention.js +93 -0
  89. package/dist/src/cli/doctor-workflows.d.ts +5 -2
  90. package/dist/src/cli/doctor-workflows.js +12 -7
  91. package/dist/src/cli/doctor.js +11 -9
  92. package/dist/src/cli/harness-probes.d.ts +2 -0
  93. package/dist/src/cli/harness-probes.js +1 -1
  94. package/dist/src/cli/harness-processes.d.ts +6 -0
  95. package/dist/src/cli/harness-processes.js +36 -0
  96. package/dist/src/cli/init-command.d.ts +9 -0
  97. package/dist/src/cli/init-command.js +52 -0
  98. package/dist/src/cli/init-prompt-command.d.ts +10 -0
  99. package/dist/src/cli/init-prompt-command.js +145 -0
  100. package/dist/src/cli/init-prompt.d.ts +35 -0
  101. package/dist/src/cli/init-prompt.js +74 -0
  102. package/dist/src/cli/init-selection.d.ts +13 -0
  103. package/dist/src/cli/init-selection.js +75 -0
  104. package/dist/src/cli/main.d.ts +2 -0
  105. package/dist/src/cli/main.js +37 -7
  106. package/dist/src/cli/mcp-server.d.ts +1 -0
  107. package/dist/src/cli/mcp-server.js +156 -0
  108. package/dist/src/cli/mcp-tools.d.ts +15 -0
  109. package/dist/src/cli/mcp-tools.js +242 -0
  110. package/dist/src/cli/queue-arguments.d.ts +15 -0
  111. package/dist/src/cli/queue-arguments.js +31 -0
  112. package/dist/src/cli/queue-render.d.ts +3 -0
  113. package/dist/src/cli/queue-render.js +26 -0
  114. package/dist/src/cli/reload-render.d.ts +2 -0
  115. package/dist/src/cli/reload-render.js +12 -0
  116. package/dist/src/cli/render.d.ts +1 -1
  117. package/dist/src/cli/render.js +54 -38
  118. package/dist/src/cli/report-ci-jobs.d.ts +23 -0
  119. package/dist/src/cli/report-ci-jobs.js +103 -0
  120. package/dist/src/cli/report-ci-usage.d.ts +9 -0
  121. package/dist/src/cli/report-ci-usage.js +185 -0
  122. package/dist/src/cli/report-ci.d.ts +13 -0
  123. package/dist/src/cli/report-ci.js +74 -0
  124. package/dist/src/cli/report-closures.d.ts +6 -0
  125. package/dist/src/cli/report-closures.js +97 -0
  126. package/dist/src/cli/report-flow.d.ts +7 -0
  127. package/dist/src/cli/report-flow.js +122 -0
  128. package/dist/src/cli/report-github-reader.d.ts +29 -0
  129. package/dist/src/cli/report-github-reader.js +157 -0
  130. package/dist/src/cli/report-github.d.ts +15 -8
  131. package/dist/src/cli/report-github.js +131 -150
  132. package/dist/src/cli/report-operator.d.ts +3 -0
  133. package/dist/src/cli/report-operator.js +40 -0
  134. package/dist/src/cli/report-ready.d.ts +3 -0
  135. package/dist/src/cli/report-ready.js +96 -0
  136. package/dist/src/cli/report-text.d.ts +9 -0
  137. package/dist/src/cli/report-text.js +33 -0
  138. package/dist/src/cli/report.d.ts +1 -0
  139. package/dist/src/cli/report.js +104 -27
  140. package/dist/src/cli/runtime-arguments.d.ts +18 -3
  141. package/dist/src/cli/runtime-arguments.js +29 -20
  142. package/dist/src/cli/schedule-arguments.d.ts +14 -0
  143. package/dist/src/cli/schedule-arguments.js +26 -0
  144. package/dist/src/cli/schedule-render.d.ts +22 -0
  145. package/dist/src/cli/schedule-render.js +73 -0
  146. package/dist/src/cli/terminal-text.d.ts +3 -0
  147. package/dist/src/cli/terminal-text.js +28 -0
  148. package/dist/src/cli/toolchain.d.ts +1 -0
  149. package/dist/src/cli/toolchain.js +1 -0
  150. package/dist/src/cli/usage-render.d.ts +1 -0
  151. package/dist/src/cli/usage-render.js +28 -1
  152. package/dist/src/cli/usage.d.ts +2 -2
  153. package/dist/src/cli/usage.js +89 -11
  154. package/dist/src/cli/watch-actions.js +2 -2
  155. package/dist/src/cli/watch-api-document.d.ts +430 -0
  156. package/dist/src/cli/watch-api-document.js +291 -0
  157. package/dist/src/cli/watch-api-run.d.ts +56 -0
  158. package/dist/src/cli/watch-api-run.js +112 -0
  159. package/dist/src/cli/watch-api.d.ts +17 -0
  160. package/dist/src/cli/watch-api.js +230 -0
  161. package/dist/src/cli/watch-board-view.js +19 -2
  162. package/dist/src/cli/watch-board.d.ts +4 -2
  163. package/dist/src/cli/watch-board.js +53 -43
  164. package/dist/src/cli/watch-card-forms.d.ts +1 -1
  165. package/dist/src/cli/watch-card-forms.js +11 -10
  166. package/dist/src/cli/watch-client.d.ts +1 -1
  167. package/dist/src/cli/watch-client.js +2 -11
  168. package/dist/src/cli/watch-header-view.d.ts +1 -5
  169. package/dist/src/cli/watch-header-view.js +17 -21
  170. package/dist/src/cli/watch-model.d.ts +5 -1
  171. package/dist/src/cli/watch-model.js +20 -11
  172. package/dist/src/cli/watch-page.js +15 -15
  173. package/dist/src/cli/watch-phases.d.ts +12 -3
  174. package/dist/src/cli/watch-phases.js +68 -11
  175. package/dist/src/cli/watch-pr-board.js +1 -1
  176. package/dist/src/cli/watch-pr-command.d.ts +1 -0
  177. package/dist/src/cli/watch-pull-request.js +18 -8
  178. package/dist/src/cli/watch-reads.d.ts +3 -0
  179. package/dist/src/cli/watch-reads.js +22 -20
  180. package/dist/src/cli/watch-render.d.ts +15 -0
  181. package/dist/src/cli/watch-run-input.d.ts +0 -1
  182. package/dist/src/cli/watch-run-input.js +0 -13
  183. package/dist/src/cli/watch-server.js +24 -10
  184. package/dist/src/cli/watch-view.d.ts +0 -3
  185. package/dist/src/cli/watch-view.js +13 -13
  186. package/dist/src/cli.js +73 -12
  187. package/dist/src/command-wait.d.ts +4 -0
  188. package/dist/src/command-wait.js +45 -0
  189. package/dist/src/configuration-change.d.ts +7 -0
  190. package/dist/src/configuration-change.js +14 -0
  191. package/dist/src/configuration-display.js +2 -0
  192. package/dist/src/configuration-schema.d.ts +14 -0
  193. package/dist/src/configuration-schema.js +6 -2
  194. package/dist/src/daemon/attachment-file.d.ts +6 -0
  195. package/dist/src/daemon/attachment-file.js +86 -0
  196. package/dist/src/daemon/attachments.d.ts +14 -0
  197. package/dist/src/daemon/attachments.js +17 -0
  198. package/dist/src/daemon/claude-recovery.d.ts +50 -0
  199. package/dist/src/daemon/claude-recovery.js +208 -0
  200. package/dist/src/daemon/claude-stream.d.ts +27 -0
  201. package/dist/src/daemon/claude-stream.js +63 -0
  202. package/dist/src/daemon/claude-turn.d.ts +11 -25
  203. package/dist/src/daemon/claude-turn.js +64 -81
  204. package/dist/src/daemon/claude-worker.d.ts +5 -4
  205. package/dist/src/daemon/claude-worker.js +127 -126
  206. package/dist/src/daemon/daemon-adapters.d.ts +9 -0
  207. package/dist/src/daemon/daemon-adapters.js +33 -5
  208. package/dist/src/daemon/daemon-loops.d.ts +5 -1
  209. package/dist/src/daemon/daemon-loops.js +18 -10
  210. package/dist/src/daemon/daemon.js +80 -61
  211. package/dist/src/daemon/github-attach.d.ts +2 -0
  212. package/dist/src/daemon/github-attach.js +222 -0
  213. package/dist/src/daemon/github-calls.js +1 -1
  214. package/dist/src/daemon/github-ci-filters.d.ts +6 -0
  215. package/dist/src/daemon/github-ci-filters.js +138 -0
  216. package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
  217. package/dist/src/daemon/github-ci-workflows.js +148 -0
  218. package/dist/src/daemon/github-ci.js +22 -4
  219. package/dist/src/daemon/github-issue-methods.d.ts +2 -0
  220. package/dist/src/daemon/github-issue-methods.js +37 -2
  221. package/dist/src/daemon/github-merge-call.d.ts +2 -1
  222. package/dist/src/daemon/github-merge-call.js +10 -3
  223. package/dist/src/daemon/github-merge.js +52 -24
  224. package/dist/src/daemon/github-methods.js +18 -4
  225. package/dist/src/daemon/github-operation.d.ts +4 -0
  226. package/dist/src/daemon/github-operation.js +6 -0
  227. package/dist/src/daemon/github-worker.d.ts +4 -0
  228. package/dist/src/daemon/github-worker.js +6 -0
  229. package/dist/src/daemon/issue-queue.d.ts +4 -1
  230. package/dist/src/daemon/issue-queue.js +76 -78
  231. package/dist/src/daemon/merge-turns.d.ts +10 -0
  232. package/dist/src/daemon/merge-turns.js +37 -0
  233. package/dist/src/daemon/notify-events.d.ts +6 -0
  234. package/dist/src/daemon/notify-events.js +84 -0
  235. package/dist/src/daemon/notify-journal.js +1 -1
  236. package/dist/src/daemon/notify-message.d.ts +4 -1
  237. package/dist/src/daemon/notify-message.js +36 -0
  238. package/dist/src/daemon/notify.d.ts +1 -1
  239. package/dist/src/daemon/notify.js +5 -77
  240. package/dist/src/daemon/operation-journal.d.ts +1 -0
  241. package/dist/src/daemon/queue-control.d.ts +13 -0
  242. package/dist/src/daemon/queue-control.js +86 -0
  243. package/dist/src/daemon/queue-journal.d.ts +20 -0
  244. package/dist/src/daemon/queue-journal.js +89 -0
  245. package/dist/src/daemon/queue-status.d.ts +9 -3
  246. package/dist/src/daemon/queue-status.js +17 -4
  247. package/dist/src/daemon/record-archive.d.ts +19 -0
  248. package/dist/src/daemon/record-archive.js +82 -0
  249. package/dist/src/daemon/record-sweep.d.ts +24 -0
  250. package/dist/src/daemon/record-sweep.js +147 -0
  251. package/dist/src/daemon/resume-index-lock.d.ts +13 -0
  252. package/dist/src/daemon/resume-index-lock.js +160 -0
  253. package/dist/src/daemon/resume-worktree.d.ts +7 -0
  254. package/dist/src/daemon/resume-worktree.js +29 -0
  255. package/dist/src/daemon/retrying-turn.d.ts +2 -12
  256. package/dist/src/daemon/retrying-turn.js +98 -172
  257. package/dist/src/daemon/schedule-control.d.ts +26 -0
  258. package/dist/src/daemon/schedule-control.js +110 -0
  259. package/dist/src/daemon/schedule-journal.d.ts +2 -15
  260. package/dist/src/daemon/schedule-journal.js +67 -4
  261. package/dist/src/daemon/settings-reload.d.ts +26 -0
  262. package/dist/src/daemon/settings-reload.js +82 -0
  263. package/dist/src/daemon/ticket-registration.d.ts +2 -7
  264. package/dist/src/daemon/ticket-registration.js +15 -81
  265. package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
  266. package/dist/src/daemon/turn-attempt-journal.js +136 -0
  267. package/dist/src/daemon/turn-attempts.d.ts +47 -0
  268. package/dist/src/daemon/turn-attempts.js +166 -0
  269. package/dist/src/daemon/turn-details.d.ts +4 -0
  270. package/dist/src/daemon/turn-details.js +33 -0
  271. package/dist/src/daemon/turn-report.d.ts +8 -1
  272. package/dist/src/daemon/turn-report.js +39 -0
  273. package/dist/src/daemon/work-tree.d.ts +9 -0
  274. package/dist/src/daemon/work-tree.js +20 -0
  275. package/dist/src/daemon/worker-profile.d.ts +9 -0
  276. package/dist/src/daemon/worker-profile.js +44 -0
  277. package/dist/src/daemon/workflow-generations.d.ts +7 -4
  278. package/dist/src/daemon/workflow-generations.js +22 -14
  279. package/dist/src/daemon/workflow-reload.d.ts +41 -0
  280. package/dist/src/daemon/workflow-reload.js +244 -0
  281. package/dist/src/daemon/workflow-replay.d.ts +2 -1
  282. package/dist/src/daemon/workflow-replay.js +7 -3
  283. package/dist/src/daemon/workflow-schedules.d.ts +3 -0
  284. package/dist/src/daemon/workflow-schedules.js +98 -45
  285. package/dist/src/daemon/worktree-processes.d.ts +9 -0
  286. package/dist/src/daemon/worktree-processes.js +151 -0
  287. package/dist/src/doctor-report.d.ts +4 -6
  288. package/dist/src/doctor-report.js +8 -13
  289. package/dist/src/harness-start.d.ts +15 -0
  290. package/dist/src/harness-start.js +37 -0
  291. package/dist/src/host/command-wait.d.ts +38 -0
  292. package/dist/src/host/command-wait.js +115 -0
  293. package/dist/src/host/start-delivery.d.ts +5 -7
  294. package/dist/src/host/start-delivery.js +3 -15
  295. package/dist/src/host/step-bridge-host.d.ts +2 -0
  296. package/dist/src/host/step-bridge-host.js +21 -0
  297. package/dist/src/operation-identity.d.ts +1 -0
  298. package/dist/src/operation-identity.js +4 -0
  299. package/dist/src/private-config.schema.json +71 -0
  300. package/dist/src/project-config.schema.json +40 -0
  301. package/dist/src/project-settings.d.ts +16 -2
  302. package/dist/src/project-settings.js +35 -1
  303. package/dist/src/queue-spend.d.ts +4 -0
  304. package/dist/src/queue-spend.js +21 -0
  305. package/dist/src/queue-state.d.ts +26 -0
  306. package/dist/src/queue-state.js +34 -0
  307. package/dist/src/record-retention.d.ts +46 -0
  308. package/dist/src/record-retention.js +81 -0
  309. package/dist/src/relay/relay-service.d.ts +3 -0
  310. package/dist/src/relay/relay-service.js +5 -3
  311. package/dist/src/report-ci.d.ts +164 -0
  312. package/dist/src/report-ci.js +32 -0
  313. package/dist/src/report-effects.d.ts +15 -0
  314. package/dist/src/report-effects.js +212 -0
  315. package/dist/src/report-flow-types.d.ts +38 -0
  316. package/dist/src/report-flow-types.js +7 -0
  317. package/dist/src/report-flow.d.ts +43 -0
  318. package/dist/src/report-flow.js +193 -0
  319. package/dist/src/report-operator.d.ts +67 -0
  320. package/dist/src/report-operator.js +25 -0
  321. package/dist/src/report-quality.d.ts +221 -0
  322. package/dist/src/report-quality.js +94 -0
  323. package/dist/src/report-ready.d.ts +23 -0
  324. package/dist/src/report-ready.js +117 -0
  325. package/dist/src/report-statistics.d.ts +2 -1
  326. package/dist/src/report-statistics.js +40 -4
  327. package/dist/src/report.d.ts +588 -47
  328. package/dist/src/report.js +156 -41
  329. package/dist/src/schedule-progress.d.ts +33 -0
  330. package/dist/src/schedule-progress.js +45 -0
  331. package/dist/src/service/active-tickets.js +11 -6
  332. package/dist/src/service/command-waits.d.ts +34 -0
  333. package/dist/src/service/command-waits.js +86 -0
  334. package/dist/src/service/dispatcher.d.ts +4 -0
  335. package/dist/src/service/dispatcher.js +33 -0
  336. package/dist/src/service/expiry.js +2 -0
  337. package/dist/src/service/gateway.js +3 -1
  338. package/dist/src/service/queue-handlers.d.ts +4 -0
  339. package/dist/src/service/queue-handlers.js +32 -0
  340. package/dist/src/service/queue-spend.d.ts +6 -0
  341. package/dist/src/service/queue-spend.js +68 -0
  342. package/dist/src/service/registry.d.ts +28 -2
  343. package/dist/src/service/registry.js +48 -0
  344. package/dist/src/service/replies.d.ts +22 -8
  345. package/dist/src/service/replies.js +16 -2
  346. package/dist/src/service/run-handlers.js +37 -12
  347. package/dist/src/service/run-metadata.d.ts +15 -1
  348. package/dist/src/service/run-metadata.js +93 -8
  349. package/dist/src/service/service.d.ts +11 -3
  350. package/dist/src/service/service.js +22 -37
  351. package/dist/src/service/submission-handler.js +18 -4
  352. package/dist/src/service/worker-port.js +9 -1
  353. package/dist/src/service/worker-totals.d.ts +6 -0
  354. package/dist/src/service/worker-totals.js +8 -1
  355. package/dist/src/service/workflow-reload.d.ts +35 -0
  356. package/dist/src/service/workflow-reload.js +52 -0
  357. package/dist/src/step-bridge.d.ts +20 -0
  358. package/dist/src/steps/command-wait.d.ts +12 -0
  359. package/dist/src/steps/command-wait.js +26 -0
  360. package/dist/src/steps/open-decision-wait.d.ts +2 -0
  361. package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
  362. package/dist/src/steps/review-by-person.js +4 -1
  363. package/dist/src/steps/wait-for-command.d.ts +3 -0
  364. package/dist/src/steps/wait-for-command.js +17 -0
  365. package/dist/src/steps/wait-for-decision.js +1 -1
  366. package/dist/src/storage/command-wait-rows.d.ts +30 -0
  367. package/dist/src/storage/command-wait-rows.js +96 -0
  368. package/dist/src/storage/decision-snapshots.d.ts +8 -2
  369. package/dist/src/storage/decision-snapshots.js +16 -8
  370. package/dist/src/storage/domain-changes.d.ts +3 -1
  371. package/dist/src/storage/domain-changes.js +4 -0
  372. package/dist/src/storage/domain-schema.d.ts +16 -1
  373. package/dist/src/storage/domain-schema.js +35 -4
  374. package/dist/src/storage/effect-schema.d.ts +2 -0
  375. package/dist/src/storage/effect-schema.js +9 -0
  376. package/dist/src/storage/projection-changes.d.ts +12 -0
  377. package/dist/src/storage/projection-changes.js +194 -0
  378. package/dist/src/storage/projection-lookups.d.ts +7 -0
  379. package/dist/src/storage/projection-lookups.js +30 -0
  380. package/dist/src/testing-requests.d.ts +15 -0
  381. package/dist/src/testing-requests.js +93 -0
  382. package/dist/src/testing.d.ts +4 -10
  383. package/dist/src/testing.js +33 -86
  384. package/dist/src/worker-usage.d.ts +1 -0
  385. package/dist/src/worker-usage.js +3 -0
  386. package/dist/src/workflow-candidate.d.ts +1 -0
  387. package/dist/src/workflow-candidate.js +12 -1
  388. package/dist/src/workflow-contracts.d.ts +1 -0
  389. package/dist/src/workflow-contracts.js +9 -2
  390. package/dist/src/workflow-declaration.d.ts +1 -0
  391. package/dist/src/workflow-declaration.js +3 -0
  392. package/dist/src/workflow-discovery.d.ts +2 -13
  393. package/dist/src/workflow-discovery.js +1 -13
  394. package/dist/src/workflow-refusals.js +0 -1
  395. package/dist/src/workflows.d.ts +1 -1
  396. package/dist/src/workflows.js +1 -1
  397. package/dist/tools/compile-workflows.js +3 -0
  398. package/dist/tools/project-configuration.d.ts +7 -2
  399. package/dist/tools/project-configuration.js +30 -1
  400. package/dist/tools/red-green-command.d.ts +1 -0
  401. package/dist/tools/red-green-command.js +174 -0
  402. package/dist/tools/red-green-process.d.ts +10 -0
  403. package/dist/tools/red-green-process.js +59 -0
  404. package/dist/tools/self-host-processes.d.ts +2 -0
  405. package/dist/tools/self-host-processes.js +69 -26
  406. package/dist/tools/self-host-project.d.ts +2 -0
  407. package/dist/tools/self-host-project.js +30 -77
  408. package/dist/tools/self-host-runtime.js +1 -1
  409. package/dist/tools/self-host.js +76 -34
  410. package/dist/tools/template-command.d.ts +1 -0
  411. package/dist/tools/template-command.js +160 -0
  412. package/dist/tools/template-copy.d.ts +11 -0
  413. package/dist/tools/template-copy.js +130 -0
  414. package/dist/tools/template-files.d.ts +17 -0
  415. package/dist/tools/template-files.js +74 -0
  416. package/dist/tools/workflow-bundles.js +1 -0
  417. package/dist/tools/workflow-candidate-counts.js +2 -2
  418. package/dist/tools/workflow-candidate-program.d.ts +4 -2
  419. package/dist/tools/workflow-candidate-program.js +30 -11
  420. package/dist/tools/workflow-candidate-shape.d.ts +3 -0
  421. package/dist/tools/workflow-candidate-shape.js +90 -0
  422. package/dist/tools/workflow-candidate-source.d.ts +1 -0
  423. package/dist/tools/workflow-candidate-source.js +26 -4
  424. package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
  425. package/dist/tools/workflow-candidate-syntax.js +24 -94
  426. package/dist/tools/workflow-command.js +1 -2
  427. package/dist/tools/workflow-folders.d.ts +2 -3
  428. package/dist/tools/workflow-folders.js +1 -1
  429. package/package.json +17 -9
  430. package/templates/develop/README.md +93 -4
  431. package/templates/develop/develop/flow-tests.ts +38 -1
  432. package/templates/develop/develop/interruption-tests.ts +157 -0
  433. package/templates/develop/develop/local.ts +1 -0
  434. package/templates/develop/develop/option-tests.ts +138 -9
  435. package/templates/develop/develop/pull.ts +5 -1
  436. package/templates/develop/develop/red-green.ts +55 -0
  437. package/templates/develop/develop/refusal-tests.ts +77 -0
  438. package/templates/develop/develop/report.ts +31 -11
  439. package/templates/develop/develop/round.ts +1 -0
  440. package/templates/develop/develop/scenarios.ts +27 -0
  441. package/templates/develop/develop/turn.ts +28 -7
  442. package/templates/develop/develop.declaration.json +1 -1
  443. package/templates/develop/develop.test.ts +4 -0
  444. package/templates/develop/template.json +29 -2
  445. package/templates/idea-to-issue/README.md +27 -0
  446. package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
  447. package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
  448. package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
  449. package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
  450. package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
  451. package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
  452. package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
  453. package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
  454. package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
  455. package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
  456. package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
  457. package/templates/idea-to-issue/idea-to-issue.ts +49 -0
  458. package/templates/idea-to-issue/template.json +81 -0
  459. package/templates/review-day/README.md +97 -0
  460. package/templates/review-day/review-day/critique.ts +58 -0
  461. package/templates/review-day/review-day/decision.ts +29 -0
  462. package/templates/review-day/review-day/facts.ts +69 -0
  463. package/templates/review-day/review-day/label.ts +29 -0
  464. package/templates/review-day/review-day/next.ts +28 -0
  465. package/templates/review-day/review-day/post.ts +27 -0
  466. package/templates/review-day/review-day/report.ts +98 -0
  467. package/templates/review-day/review-day/rules.ts +12 -0
  468. package/templates/review-day/review-day/scenarios.ts +76 -0
  469. package/templates/review-day/review-day/turn.ts +73 -0
  470. package/templates/review-day/review-day.declaration.json +16 -0
  471. package/templates/review-day/review-day.test.ts +362 -0
  472. package/templates/review-day/review-day.ts +37 -0
  473. package/templates/review-day/template.json +56 -0
  474. package/dist/src/cli/watch-start-view.d.ts +0 -18
  475. package/dist/src/cli/watch-start-view.js +0 -77
  476. package/dist/src/steps/decision-gate.d.ts +0 -3
  477. package/dist/workflows/develop-prd.d.ts +0 -7
  478. package/dist/workflows/develop-prd.js +0 -165
  479. package/dist/workflows/develop-ticket.d.ts +0 -9
  480. package/dist/workflows/develop-ticket.js +0 -207
  481. package/dist/workflows/issue-labels.d.ts +0 -16
  482. package/dist/workflows/issue-labels.js +0 -70
  483. package/dist/workflows/ticket-prompts.d.ts +0 -35
  484. package/dist/workflows/ticket-prompts.js +0 -147
@@ -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 locked = await resumeWorktree({
157
+ directory: attempts.start.directory,
158
+ folder: attempts.start.folder,
159
+ signal: attempts.stopping.signal,
160
+ stopMilliseconds: attempts.grace.milliseconds,
161
+ });
162
+ if (locked !== null)
163
+ return { ...combined(attempts), reason: locked.reason };
164
+ const started = await readRecord(attempts.start.folder, "started.json");
165
+ const resume = await readResume(attempts.start.folder, started);
166
+ if (resume === undefined)
167
+ return { ...combined(attempts), reason: "claude-turn-interrupted" };
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 {};
@@ -0,0 +1,110 @@
1
+ import { scheduleStops } from "../schedule-progress.js";
2
+ import { runIdentity } from "../service/submission-handler.js";
3
+ import { failed, invalidRequest, result } from "../service/replies.js";
4
+ function summary(workflowId, progress) {
5
+ return {
6
+ workflowId,
7
+ everySeconds: progress.everySeconds,
8
+ state: progress.state,
9
+ failuresInRow: progress.failuresInRow,
10
+ failedRunIds: progress.failedRunIds,
11
+ lastSlot: progress.lastSlot,
12
+ nextAt: progress.state === "active" ? progress.nextAt : null,
13
+ activeRunId: progress.activeCommandId === undefined
14
+ ? null
15
+ : runIdentity(progress.activeCommandId),
16
+ stoppedAt: progress.stoppedAt ?? null,
17
+ stoppedSlot: progress.stoppedSlot ?? null,
18
+ stops: scheduleStops(progress),
19
+ };
20
+ }
21
+ export function scheduleConnection() {
22
+ let port;
23
+ return {
24
+ request: (parameters) => port?.(parameters) ??
25
+ Promise.resolve(failed("schedules-unavailable", {})),
26
+ connect: (connected) => {
27
+ port = connected;
28
+ },
29
+ };
30
+ }
31
+ async function change(settings, command) {
32
+ const { workflowId, action, now } = command;
33
+ const { journal } = settings;
34
+ let prior = journal.entries.get(workflowId);
35
+ if (prior === undefined)
36
+ return failed("unknown-schedule", { workflowId });
37
+ const state = action === "pause" ? "paused" : "active";
38
+ if (prior.state === state)
39
+ return result("schedule", { schedule: summary(workflowId, prior) });
40
+ if (prior.pending !== undefined) {
41
+ prior = await settings.recoverPending({ workflowId, progress: prior });
42
+ journal.entries.set(workflowId, prior);
43
+ await journal.save();
44
+ }
45
+ const rest = { ...prior };
46
+ delete rest.stoppedAt;
47
+ delete rest.stoppedSlot;
48
+ const progress = action === "pause"
49
+ ? { ...prior, state, stops: scheduleStops(prior) }
50
+ : {
51
+ ...rest,
52
+ state,
53
+ failuresInRow: 0,
54
+ failedRunIds: [],
55
+ stops: scheduleStops(prior),
56
+ nextAt: now + prior.everySeconds * 1000,
57
+ };
58
+ journal.entries.set(workflowId, progress);
59
+ try {
60
+ await journal.save();
61
+ }
62
+ catch (error) {
63
+ journal.entries.set(workflowId, prior);
64
+ throw error;
65
+ }
66
+ return result("schedule", { schedule: summary(workflowId, progress) });
67
+ }
68
+ function request(settings, parameters) {
69
+ const { action, workflowId } = parameters;
70
+ const scheduled = settings.declarations.filter((entry) => entry.schedule !== undefined);
71
+ if (action === "list")
72
+ return Promise.resolve(result("schedules", {
73
+ stops: Array.from(settings.journal.entries, ([workflowId, progress]) => ({
74
+ workflowId,
75
+ stops: scheduleStops(progress),
76
+ })),
77
+ schedules: scheduled.flatMap((entry) => {
78
+ const progress = settings.journal.entries.get(entry.workflowId);
79
+ return progress === undefined
80
+ ? []
81
+ : [summary(entry.workflowId, progress)];
82
+ }),
83
+ }));
84
+ if ((action !== "pause" && action !== "resume") ||
85
+ typeof workflowId !== "string")
86
+ return Promise.resolve(invalidRequest("Use list, pause or resume with a workflow ID."));
87
+ if (!scheduled.some((entry) => entry.workflowId === workflowId))
88
+ return Promise.resolve(failed("unknown-schedule", { workflowId }));
89
+ return change(settings, {
90
+ workflowId,
91
+ action,
92
+ now: settings.clock(),
93
+ });
94
+ }
95
+ export function scheduleControl(settings) {
96
+ let running = Promise.resolve();
97
+ const serialized = (operation) => {
98
+ const next = running.then(async () => {
99
+ settings.signal.throwIfAborted();
100
+ return operation();
101
+ });
102
+ running = next.then(() => undefined, () => undefined);
103
+ return next;
104
+ };
105
+ return {
106
+ request: (parameters) => serialized(() => request(settings, parameters)).catch(() => failed("schedules-unavailable", {})),
107
+ tick: () => serialized(settings.tick),
108
+ drain: () => running,
109
+ };
110
+ }
@@ -1,18 +1,5 @@
1
- import { type Fields } from "../protocol.ts";
2
- export type PendingSlot = {
3
- readonly kind: "run";
4
- readonly input: Fields;
5
- } | {
6
- readonly kind: "skip";
7
- readonly runId: string;
8
- };
9
- export interface ScheduleProgress {
10
- readonly everySeconds: number;
11
- readonly nextAt: number;
12
- readonly lastSlot: number;
13
- readonly activeCommandId?: string;
14
- readonly pending?: PendingSlot;
15
- }
1
+ import type { ScheduleProgress } from "../schedule-progress.ts";
2
+ export type { PendingSlot, ScheduleProgress } from "../schedule-progress.ts";
16
3
  export declare function scheduleJournal(directory: string): Promise<{
17
4
  readonly entries: Map<string, ScheduleProgress>;
18
5
  readonly save: () => Promise<void>;
@@ -23,10 +23,68 @@ function isProgress(value) {
23
23
  const fields = record(value);
24
24
  if (fields === undefined)
25
25
  return false;
26
- const { activeCommandId, pending } = fields;
26
+ const { activeCommandId } = fields;
27
27
  return (timing(fields) &&
28
- (activeCommandId === undefined || isCommandId(activeCommandId)) &&
29
- (pending === undefined || pendingSlot(pending)));
28
+ control(fields) &&
29
+ history(fields) &&
30
+ pendingProgress(fields) &&
31
+ (activeCommandId === undefined || isCommandId(activeCommandId)));
32
+ }
33
+ function pendingProgress(fields) {
34
+ const { pending, state } = fields;
35
+ if (pending === undefined)
36
+ return true;
37
+ return (state === undefined || state === "active") && pendingSlot(pending);
38
+ }
39
+ function stopped(fields) {
40
+ const { state, stoppedAt, stoppedSlot } = fields;
41
+ if (stoppedAt === undefined && stoppedSlot === undefined)
42
+ return state !== "stopped";
43
+ return (whole(stoppedAt) &&
44
+ whole(stoppedSlot) &&
45
+ stoppedSlot <= Number(fields["lastSlot"]));
46
+ }
47
+ function failures(fields) {
48
+ const { failuresInRow, failedRunIds } = fields;
49
+ return (whole(failuresInRow) &&
50
+ failuresInRow <= 3 &&
51
+ Array.isArray(failedRunIds) &&
52
+ failedRunIds.length === failuresInRow &&
53
+ failedRunIds.every(isRecordKey) &&
54
+ new Set(failedRunIds).size === failuresInRow);
55
+ }
56
+ function history(fields) {
57
+ const stops = fields["stops"];
58
+ if (stops === undefined)
59
+ return true;
60
+ if (!Array.isArray(stops))
61
+ return false;
62
+ let previousSlot = -1;
63
+ for (const item of stops) {
64
+ const stop = record(item);
65
+ if (stop === undefined)
66
+ return false;
67
+ const slot = stop["stoppedSlot"];
68
+ if (!whole(slot) || !whole(stop["stoppedAt"]))
69
+ return false;
70
+ if (slot <= previousSlot || slot > Number(fields["lastSlot"]))
71
+ return false;
72
+ if (!failures({ ...stop, failuresInRow: 3 }))
73
+ return false;
74
+ previousSlot = slot;
75
+ }
76
+ return true;
77
+ }
78
+ function control(fields) {
79
+ const { state, failuresInRow, failedRunIds } = fields;
80
+ if (state === undefined &&
81
+ failuresInRow === undefined &&
82
+ failedRunIds === undefined)
83
+ return (fields["stoppedAt"] === undefined && fields["stoppedSlot"] === undefined);
84
+ return (["active", "paused", "stopped"].includes(String(state)) &&
85
+ failures(fields) &&
86
+ stopped(fields) &&
87
+ (state !== "stopped" || failuresInRow === 3));
30
88
  }
31
89
  export async function scheduleJournal(directory) {
32
90
  const file = join(directory, "schedules.json");
@@ -42,7 +100,12 @@ export async function scheduleJournal(directory) {
42
100
  for (const [workflowId, value] of Object.entries(parsed)) {
43
101
  if (!isProgress(value))
44
102
  throw new Error("invalid-schedule-state");
45
- entries.set(workflowId, value);
103
+ entries.set(workflowId, {
104
+ ...value,
105
+ state: value.state ?? "active",
106
+ failuresInRow: value.failuresInRow ?? 0,
107
+ failedRunIds: value.failedRunIds ?? [],
108
+ });
46
109
  }
47
110
  return {
48
111
  entries,
@@ -0,0 +1,26 @@
1
+ import { type HarnessSettings } from "../harness-defaults.ts";
2
+ import { type TurnSettings } from "../project-settings.ts";
3
+ import type { SettingChanges } from "../service/workflow-reload.ts";
4
+ import { type TurnProfiles } from "../turn-profile.ts";
5
+ export type SettingDigests = Readonly<Record<string, string>>;
6
+ export interface ReloadedSettings {
7
+ readonly turnSettings: TurnSettings;
8
+ readonly settingDigests: SettingDigests;
9
+ }
10
+ export interface TurnSettingsSource {
11
+ readonly current: () => TurnSettings;
12
+ readonly replace: (next: TurnSettings) => void;
13
+ }
14
+ export declare function turnSettingsSource(initial: TurnSettings): TurnSettingsSource;
15
+ export declare function reloadedSettings(output: Readonly<Record<string, unknown>> | undefined): ReloadedSettings | undefined;
16
+ export declare function settingChanges(current: SettingDigests, next: SettingDigests): SettingChanges;
17
+ export declare function keptDigests(current: SettingDigests, change: {
18
+ readonly digests: SettingDigests;
19
+ readonly reloadedFields: readonly string[];
20
+ }): SettingDigests;
21
+ export declare function profileRefusal(settings: HarnessSettings & {
22
+ readonly profiles?: TurnProfiles;
23
+ }, turnSettings: TurnSettings): {
24
+ readonly code: string;
25
+ readonly detail: string;
26
+ } | undefined;