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
@@ -0,0 +1,96 @@
1
+ import { integer, optionalInteger, optionalText, text, } from "./domain-rows.js";
2
+ import { upsert } from "./statements.js";
3
+ export const commandWaitSchema = `
4
+ CREATE TABLE command_waits (
5
+ run_id TEXT NOT NULL REFERENCES runs (run_id) DEFERRABLE INITIALLY DEFERRED,
6
+ name TEXT NOT NULL,
7
+ command TEXT NOT NULL,
8
+ input_digest TEXT NOT NULL REFERENCES documents (digest) DEFERRABLE INITIALLY DEFERRED,
9
+ maximum_wait_seconds INTEGER NOT NULL,
10
+ poll_every_seconds INTEGER NOT NULL,
11
+ started_at INTEGER NOT NULL,
12
+ deadline INTEGER NOT NULL,
13
+ next_check_at INTEGER,
14
+ state TEXT NOT NULL,
15
+ settled_at INTEGER,
16
+ reason TEXT,
17
+ PRIMARY KEY (run_id, name)
18
+ ) STRICT;
19
+ `;
20
+ export const commandWaitCheckSchema = `
21
+ ALTER TABLE command_waits ADD COLUMN check_count INTEGER NOT NULL DEFAULT 0;
22
+ ALTER TABLE command_waits ADD COLUMN check_operation TEXT;
23
+ ALTER TABLE command_waits ADD COLUMN last_check_settled_at INTEGER;
24
+ ALTER TABLE command_waits ADD COLUMN result TEXT;
25
+ `;
26
+ const openingColumns = "run_id, name, command, input_digest, maximum_wait_seconds, poll_every_seconds, started_at, deadline, next_check_at, state, settled_at, reason";
27
+ const checkColumns = "check_count, check_operation, last_check_settled_at, result";
28
+ const columns = `${openingColumns}, ${checkColumns}`;
29
+ const save = upsert({
30
+ table: "command_waits",
31
+ names: columns.split(", "),
32
+ key: 2,
33
+ });
34
+ export function commandWaitRow(value) {
35
+ return {
36
+ runId: text(value, "run_id"),
37
+ name: text(value, "name"),
38
+ command: text(value, "command"),
39
+ inputDigest: text(value, "input_digest"),
40
+ maximumWaitSeconds: integer(value, "maximum_wait_seconds"),
41
+ pollEverySeconds: integer(value, "poll_every_seconds"),
42
+ startedAt: integer(value, "started_at"),
43
+ deadline: integer(value, "deadline"),
44
+ nextCheckAt: optionalInteger(value, "next_check_at"),
45
+ state: text(value, "state"),
46
+ checkCount: integer(value, "check_count"),
47
+ checkOperation: optionalText(value, "check_operation"),
48
+ lastCheckSettledAt: optionalInteger(value, "last_check_settled_at"),
49
+ result: optionalText(value, "result"),
50
+ settledAt: optionalInteger(value, "settled_at"),
51
+ reason: optionalText(value, "reason"),
52
+ };
53
+ }
54
+ export async function loadCommandWaits(connection, scope = {}) {
55
+ const { runId } = scope;
56
+ const selected = scope.version === "opened"
57
+ ? `${openingColumns}, 0 AS check_count, NULL AS check_operation, NULL AS last_check_settled_at, NULL AS result`
58
+ : columns;
59
+ const rows = await connection.all(`SELECT ${selected} FROM command_waits` +
60
+ (runId === undefined ? "" : " WHERE run_id = ?") +
61
+ " ORDER BY run_id, name", runId === undefined ? [] : [runId]);
62
+ return rows.map(commandWaitRow);
63
+ }
64
+ export function commandWaitStatements(rows, scope = {}) {
65
+ return rows.map((row) => ({
66
+ sql: scope.version === "opened"
67
+ ? upsert({
68
+ table: "command_waits",
69
+ names: openingColumns.split(", "),
70
+ key: 2,
71
+ })
72
+ : save,
73
+ parameters: [
74
+ row.runId,
75
+ row.name,
76
+ row.command,
77
+ row.inputDigest,
78
+ row.maximumWaitSeconds,
79
+ row.pollEverySeconds,
80
+ row.startedAt,
81
+ row.deadline,
82
+ row.nextCheckAt,
83
+ row.state,
84
+ row.settledAt,
85
+ row.reason,
86
+ ...(scope.version === "opened"
87
+ ? []
88
+ : [
89
+ row.checkCount,
90
+ row.checkOperation,
91
+ row.lastCheckSettledAt,
92
+ row.result,
93
+ ]),
94
+ ],
95
+ }));
96
+ }
@@ -1,3 +1,4 @@
1
+ import { type CommandWaitRow } from "./command-wait-rows.ts";
1
2
  import { type DecisionRows, type DocumentRow } from "./decision-rows.ts";
2
3
  import { type AttemptRow, type DomainSnapshot, type ProjectionRows, type SqlConnection } from "./domain-rows.ts";
3
4
  import { type EffectRows } from "./effect-rows.ts";
@@ -5,14 +6,20 @@ export interface ProjectionSnapshot extends ProjectionRows, DecisionRows {
5
6
  readonly deliveries: readonly AttemptRow[];
6
7
  readonly pending: readonly string[];
7
8
  readonly sequence: number;
9
+ readonly waits?: readonly CommandWaitRow[];
8
10
  }
9
11
  export interface DecidedSnapshot extends DomainSnapshot, DecisionRows {
10
12
  readonly documents: readonly DocumentRow[];
11
13
  }
12
- export interface CurrentSnapshot extends DecidedSnapshot {
14
+ export interface EffectedSnapshot extends DecidedSnapshot {
13
15
  readonly effects: EffectRows;
14
16
  }
17
+ export interface CurrentSnapshot extends EffectedSnapshot {
18
+ readonly waits: readonly CommandWaitRow[];
19
+ }
15
20
  export declare function loadOperationSnapshot(connection: SqlConnection): Promise<Pick<ProjectionSnapshot, "outbox" | "deliveries">>;
16
21
  export declare function loadProjections(connection: SqlConnection): Promise<ProjectionSnapshot>;
17
22
  export declare function loadDecided(connection: SqlConnection): Promise<DecidedSnapshot>;
23
+ export declare function loadEffected(connection: SqlConnection): Promise<EffectedSnapshot>;
18
24
  export declare function loadCurrent(connection: SqlConnection): Promise<CurrentSnapshot>;
25
+ export declare function loadWaiting(connection: SqlConnection): Promise<CurrentSnapshot>;
@@ -1,3 +1,4 @@
1
+ import { loadCommandWaits } from "./command-wait-rows.js";
1
2
  import { loadDecisions, loadDocuments, } from "./decision-rows.js";
2
3
  import { latestSequence, loadAttempts, loadSnapshot, outboxRow, projectionRows, text, } from "./domain-rows.js";
3
4
  import { loadEffects } from "./effect-rows.js";
@@ -9,6 +10,7 @@ export async function loadOperationSnapshot(connection) {
9
10
  }
10
11
  export async function loadProjections(connection) {
11
12
  return {
13
+ waits: await loadCommandWaits(connection),
12
14
  ...(await projectionRows(connection)),
13
15
  ...(await loadDecisions(connection)),
14
16
  deliveries: await loadAttempts(connection),
@@ -23,9 +25,21 @@ export async function loadDecided(connection) {
23
25
  documents: await loadDocuments(connection),
24
26
  };
25
27
  }
26
- export async function loadCurrent(connection) {
28
+ export async function loadEffected(connection) {
27
29
  return {
28
30
  ...(await loadDecided(connection)),
29
31
  effects: await loadEffects(connection),
30
32
  };
31
33
  }
34
+ export async function loadCurrent(connection) {
35
+ return {
36
+ ...(await loadEffected(connection)),
37
+ waits: await loadCommandWaits(connection),
38
+ };
39
+ }
40
+ export async function loadWaiting(connection) {
41
+ return {
42
+ ...(await loadEffected(connection)),
43
+ waits: await loadCommandWaits(connection, { version: "opened" }),
44
+ };
45
+ }
@@ -1,3 +1,4 @@
1
+ import { type CommandWaitRow } from "./command-wait-rows.ts";
1
2
  import { type DetailRow, type DocumentRow, type GateRow } from "./decision-rows.ts";
2
3
  import { type CommandRow, type DefinitionRow, type EventRow, type RunRow, type SqlConnection } from "./domain-rows.ts";
3
4
  import { type EffectRows } from "./effect-rows.ts";
@@ -37,6 +38,7 @@ export interface DomainChange extends EffectRows {
37
38
  readonly staged: readonly StagedDelivery[];
38
39
  readonly closed: readonly ClosedDelivery[];
39
40
  readonly gates: readonly GateRow[];
41
+ readonly waits: readonly CommandWaitRow[];
40
42
  readonly details: readonly DetailRow[];
41
43
  readonly documents: readonly DocumentRow[];
42
44
  readonly completesAdoption: boolean;
@@ -1,3 +1,4 @@
1
+ import { commandWaitRow, commandWaitStatements, } from "./command-wait-rows.js";
1
2
  import { detailColumns, detailRow, documentRow, gateColumns, gateRow, } from "./decision-rows.js";
2
3
  import { commandRow, definitionRow, eventRow, integer, latestSequence, optionalInteger, optionalText, runRow, text, } from "./domain-rows.js";
3
4
  import { effectStatements } from "./effect-changes.js";
@@ -48,6 +49,7 @@ function statements(change) {
48
49
  ...deliveryStatements(change),
49
50
  ...decisionStatements(change),
50
51
  ...effectStatements(change),
52
+ ...commandWaitStatements(change.waits),
51
53
  ...(change.completesAdoption
52
54
  ? [
53
55
  {
@@ -195,6 +197,7 @@ function parseChange(value) {
195
197
  receipt: optionalText(row, "receipt"),
196
198
  })),
197
199
  gates: list(value, "gates").map(gateRow),
200
+ waits: list(value, "waits").map(commandWaitRow),
198
201
  details: list(value, "details").map(detailRow),
199
202
  documents: list(value, "documents").map(documentRow),
200
203
  budgets: list(value, "budgets").map(budgetRow),
@@ -2,7 +2,11 @@ import { type SqlConnection } from "./domain-rows.ts";
2
2
  export declare const baselineVersion = 1;
3
3
  export declare const adoptedVersion = 2;
4
4
  export declare const decidedVersion = 3;
5
- export declare const currentVersion = 4;
5
+ export declare const effectedVersion = 4;
6
+ export declare const waitingVersion = 5;
7
+ export declare const checkedVersion = 6;
8
+ export declare const spendingVersion = 7;
9
+ export declare const currentVersion = 8;
6
10
  export declare const baselineSchema = "\nCREATE TABLE schema_migrations (\n version INTEGER PRIMARY KEY,\n checksum TEXT NOT NULL\n) STRICT;\nCREATE TABLE events (\n sequence INTEGER PRIMARY KEY,\n type TEXT NOT NULL,\n run_id TEXT NOT NULL,\n command_id TEXT NOT NULL UNIQUE,\n recorded_at TEXT NOT NULL,\n request TEXT NOT NULL,\n payload TEXT NOT NULL\n) STRICT;\nCREATE TABLE definitions (\n pin TEXT PRIMARY KEY,\n manifest TEXT NOT NULL\n) STRICT;\nCREATE TABLE runs (\n run_id TEXT PRIMARY KEY,\n status TEXT NOT NULL,\n revision INTEGER NOT NULL,\n workflow TEXT NOT NULL REFERENCES definitions (pin) DEFERRABLE INITIALLY DEFERRED,\n paused_from TEXT,\n gates TEXT NOT NULL,\n sequence INTEGER NOT NULL REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\nCREATE TABLE commands (\n command_id TEXT PRIMARY KEY,\n request TEXT NOT NULL,\n sequence INTEGER NOT NULL UNIQUE REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\nCREATE TABLE outbox (\n operation_id TEXT PRIMARY KEY,\n kind TEXT NOT NULL,\n run_id TEXT NOT NULL REFERENCES runs (run_id) DEFERRABLE INITIALLY DEFERRED,\n created_sequence INTEGER NOT NULL REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED,\n released_sequence INTEGER REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\n";
7
11
  export type SchemaRefusal = {
8
12
  readonly kind: "refused";
@@ -17,6 +21,14 @@ export type SchemaState = {
17
21
  readonly kind: "adopted";
18
22
  } | {
19
23
  readonly kind: "decided";
24
+ } | {
25
+ readonly kind: "effected";
26
+ } | {
27
+ readonly kind: "waiting";
28
+ } | {
29
+ readonly kind: "checked";
30
+ } | {
31
+ readonly kind: "indexed";
20
32
  } | {
21
33
  readonly kind: "current";
22
34
  } | SchemaRefusal;
@@ -1,11 +1,16 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { DatabaseSync } from "node:sqlite";
3
3
  import { integer, optionalInteger, optionalText, text, } from "./domain-rows.js";
4
- import { effectSchema } from "./effect-schema.js";
4
+ import { commandWaitSchema, commandWaitCheckSchema, } from "./command-wait-rows.js";
5
+ import { effectSchema, spendingIndexes } from "./effect-schema.js";
5
6
  export const baselineVersion = 1;
6
7
  export const adoptedVersion = 2;
7
8
  export const decidedVersion = 3;
8
- export const currentVersion = 4;
9
+ export const effectedVersion = 4;
10
+ export const waitingVersion = 5;
11
+ export const checkedVersion = 6;
12
+ export const spendingVersion = 7;
13
+ export const currentVersion = 8;
9
14
  export const baselineSchema = `
10
15
  CREATE TABLE schema_migrations (
11
16
  version INTEGER PRIMARY KEY,
@@ -102,9 +107,27 @@ CREATE TABLE documents (
102
107
  body TEXT NOT NULL
103
108
  ) STRICT;
104
109
  `;
105
- const schemas = [baselineSchema, adoption, decisions, effectSchema];
110
+ const schemas = [
111
+ baselineSchema,
112
+ adoption,
113
+ decisions,
114
+ effectSchema,
115
+ commandWaitSchema,
116
+ commandWaitCheckSchema,
117
+ spendingIndexes,
118
+ "CREATE INDEX events_run_type_sequence ON events (run_id, type, sequence);",
119
+ ];
106
120
  const checksums = schemas.map((schema) => createHash("sha256").update(schema).digest("hex"));
107
- const versionKinds = ["baseline", "adopted", "decided", "current"];
121
+ const versionKinds = [
122
+ "baseline",
123
+ "adopted",
124
+ "decided",
125
+ "effected",
126
+ "waiting",
127
+ "checked",
128
+ "indexed",
129
+ "current",
130
+ ];
108
131
  export async function inspectSchema(connection) {
109
132
  const version = integer(await connection.get("PRAGMA user_version"), "user_version");
110
133
  const detail = String(version);
@@ -132,6 +155,10 @@ export async function createSchema(connection) {
132
155
  await recordVersion(connection, baselineVersion);
133
156
  await adoptSchema(connection, null);
134
157
  await upgradeSchema(connection, decidedVersion);
158
+ await upgradeSchema(connection, effectedVersion);
159
+ await upgradeSchema(connection, waitingVersion);
160
+ await upgradeSchema(connection, checkedVersion);
161
+ await upgradeSchema(connection, spendingVersion);
135
162
  await upgradeSchema(connection, currentVersion);
136
163
  }
137
164
  export async function upgradeSchema(connection, version) {
@@ -1 +1,2 @@
1
1
  export declare const effectSchema = "\nCREATE TABLE run_budgets (\n run_id TEXT PRIMARY KEY REFERENCES runs (run_id) DEFERRABLE INITIALLY DEFERRED,\n effect_limit INTEGER NOT NULL,\n spent INTEGER NOT NULL CHECK (spent <= effect_limit),\n capabilities TEXT NOT NULL,\n exhausted_sequence INTEGER REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\nCREATE TABLE operations (\n operation_id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES runs (run_id) DEFERRABLE INITIALLY DEFERRED,\n capability TEXT NOT NULL,\n method TEXT NOT NULL,\n target TEXT NOT NULL,\n payload TEXT NOT NULL,\n input_digest TEXT NOT NULL,\n units INTEGER NOT NULL,\n lease_key TEXT NOT NULL,\n recovery_limit INTEGER NOT NULL,\n status TEXT NOT NULL,\n reason TEXT,\n attempts INTEGER NOT NULL,\n receipt TEXT,\n worker TEXT,\n result TEXT,\n requested_sequence INTEGER NOT NULL REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED,\n wait_sequence INTEGER REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED,\n settled_sequence INTEGER REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\nCREATE INDEX operations_run ON operations (run_id);\nCREATE INDEX operations_waiting ON operations (wait_sequence) WHERE status = 'waiting';\nCREATE TABLE leases (\n resource_key TEXT PRIMARY KEY,\n operation_id TEXT NOT NULL REFERENCES operations (operation_id) DEFERRABLE INITIALLY DEFERRED,\n generation INTEGER NOT NULL CHECK (generation > 0),\n expires_at TEXT NOT NULL,\n state TEXT NOT NULL CHECK (state IN ('held', 'expired', 'released')),\n granted_sequence INTEGER NOT NULL REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\nCREATE INDEX leases_due ON leases (expires_at) WHERE state = 'held';\nCREATE TABLE effect_evidence (\n sequence INTEGER PRIMARY KEY REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED,\n run_id TEXT NOT NULL,\n operation_id TEXT NOT NULL,\n disposition TEXT NOT NULL,\n reason TEXT\n) STRICT;\nCREATE TABLE stopped_operations (\n operation_id TEXT PRIMARY KEY,\n run_id TEXT NOT NULL REFERENCES runs (run_id) DEFERRABLE INITIALLY DEFERRED,\n sequence INTEGER NOT NULL REFERENCES events (sequence) DEFERRABLE INITIALLY DEFERRED\n) STRICT;\nCREATE TABLE dispatch_attempts (\n effect_id TEXT PRIMARY KEY REFERENCES deliveries (effect_id) DEFERRABLE INITIALLY DEFERRED,\n purpose TEXT NOT NULL,\n lease_generation INTEGER,\n authority_epoch INTEGER NOT NULL\n) STRICT;\n";
2
+ export declare const spendingIndexes = "\nCREATE INDEX events_spend_window ON events (julianday(recorded_at));\nCREATE INDEX operations_worker_settled ON operations (settled_sequence) WHERE capability = 'claude';\nCREATE INDEX operations_worker_open ON operations (requested_sequence) WHERE capability = 'claude' AND settled_sequence IS NULL AND status NOT IN ('refused', 'completed', 'failed', 'stopped', 'cancelled');\n";
@@ -57,3 +57,8 @@ CREATE TABLE dispatch_attempts (
57
57
  authority_epoch INTEGER NOT NULL
58
58
  ) STRICT;
59
59
  `;
60
+ export const spendingIndexes = `
61
+ CREATE INDEX events_spend_window ON events (julianday(recorded_at));
62
+ CREATE INDEX operations_worker_settled ON operations (settled_sequence) WHERE capability = 'claude';
63
+ CREATE INDEX operations_worker_open ON operations (requested_sequence) WHERE capability = 'claude' AND settled_sequence IS NULL AND status NOT IN ('refused', 'completed', 'failed', 'stopped', 'cancelled');
64
+ `;
@@ -0,0 +1,15 @@
1
+ import type { ProtectedRequest, GateRequest, HandoffRequest, RunWaitRequest, CommandWaitRequest } from "./step-bridge.ts";
2
+ export type ScenarioRequest = readonly ["effect", ProtectedRequest] | readonly ["gate", GateRequest] | readonly ["waitForDecision", GateRequest] | readonly ["handoff", HandoffRequest] | readonly ["waitForRun", RunWaitRequest] | readonly ["waitForCommand", CommandWaitRequest] | readonly [
3
+ "sleep",
4
+ {
5
+ readonly seconds: number;
6
+ } | {
7
+ readonly until: string;
8
+ }
9
+ ];
10
+ export interface ScenarioResult {
11
+ readonly spent?: number;
12
+ readonly outcome: unknown;
13
+ readonly requests: readonly ScenarioRequest[];
14
+ }
15
+ export declare function scenarioResult(value: unknown): value is ScenarioResult;
@@ -0,0 +1,93 @@
1
+ import { isInstant } from "./authority/instants.js";
2
+ import { isCommandWaitRequest } from "./command-wait.js";
3
+ import { record } from "./protocol.js";
4
+ export function scenarioResult(value) {
5
+ const result = record(value);
6
+ return (result !== undefined &&
7
+ Object.hasOwn(result, "outcome") &&
8
+ Array.isArray(result["requests"]) &&
9
+ result["requests"].every(scenarioRequest) &&
10
+ (result["spent"] === undefined || typeof result["spent"] === "number"));
11
+ }
12
+ function scenarioRequest(value) {
13
+ if (!requestPair(value))
14
+ return false;
15
+ const request = record(value[1]);
16
+ if (request === undefined)
17
+ return false;
18
+ switch (value[0]) {
19
+ case "effect":
20
+ return protectedRequest(request);
21
+ case "gate":
22
+ case "waitForDecision":
23
+ return gateRequest(request);
24
+ case "handoff":
25
+ return handoffRequest(request);
26
+ case "waitForRun":
27
+ return runWaitRequest(request);
28
+ case "waitForCommand":
29
+ return isCommandWaitRequest(request);
30
+ case "sleep":
31
+ return sleepRequest(request);
32
+ default:
33
+ return false;
34
+ }
35
+ }
36
+ function requestPair(value) {
37
+ return Array.isArray(value) && value.length === 2;
38
+ }
39
+ function runWaitRequest(request) {
40
+ return (typeof request["runId"] === "string" &&
41
+ typeof request["maximumWaitSeconds"] === "number");
42
+ }
43
+ function sleepRequest(request) {
44
+ if (typeof request["until"] === "string")
45
+ return isInstant(request["until"]);
46
+ const seconds = request["seconds"];
47
+ return (typeof seconds === "number" &&
48
+ Number.isInteger(seconds) &&
49
+ seconds >= 1 &&
50
+ seconds <= 86400);
51
+ }
52
+ function gateRequest(request) {
53
+ return (typeof request["gateId"] === "string" &&
54
+ (request["kind"] === "question" || request["kind"] === "approval") &&
55
+ typeof request["prompt"] === "string" &&
56
+ optionalText(request["expiresAt"]));
57
+ }
58
+ function handoffRequest(request) {
59
+ const input = record(request["input"]);
60
+ return (typeof request["workflowId"] === "string" &&
61
+ input !== undefined &&
62
+ Object.values(input).every(handoffValue));
63
+ }
64
+ function protectedRequest(request) {
65
+ return (protectedScope(request["scope"]) &&
66
+ ["capability", "method", "target", "payload"].every((key) => typeof request[key] === "string") &&
67
+ typeof request["units"] === "number" &&
68
+ optionalText(request["resource"]));
69
+ }
70
+ function protectedScope(value) {
71
+ const scope = record(value);
72
+ if (scope === undefined)
73
+ return false;
74
+ const stage = record(scope["stage"]);
75
+ return (typeof scope["phase"] === "string" &&
76
+ typeof scope["operation"] === "string" &&
77
+ typeof scope["round"] === "number" &&
78
+ optionalText(scope["key"]) &&
79
+ (scope["stage"] === undefined ||
80
+ (stage !== undefined &&
81
+ ["id", "visit", "branch"].every((key) => typeof stage[key] === "string"))));
82
+ }
83
+ function optionalText(value) {
84
+ return value === undefined || typeof value === "string";
85
+ }
86
+ function handoffValue(value) {
87
+ if (value === null || ["string", "boolean", "number"].includes(typeof value))
88
+ return true;
89
+ if (Array.isArray(value))
90
+ return value.every(handoffValue);
91
+ const fields = record(value);
92
+ return fields !== undefined && Object.values(fields).every(handoffValue);
93
+ }
@@ -1,5 +1,7 @@
1
- import { type HandoffValue, type ProtectedResult, type GateResult, type DecisionResult, type RunWaitOutcome, type ProtectedRequest, type GateRequest, type HandoffRequest, type RunWaitRequest } from "./step-bridge.ts";
1
+ import { type ScenarioRequest, type ScenarioResult } from "./testing-requests.ts";
2
+ import { type HandoffValue, type ProtectedResult, type GateResult, type DecisionResult, type RunWaitOutcome, type CommandWaitProgress } from "./step-bridge.ts";
2
3
  export interface StepScenario {
4
+ readonly commandWaits?: Readonly<Record<string, readonly CommandWaitProgress[]>>;
3
5
  readonly turn?: ProtectedResult;
4
6
  readonly effects?: Readonly<Record<string, readonly ProtectedResult[]>>;
5
7
  readonly gate?: GateResult;
@@ -11,16 +13,8 @@ export interface WorkflowScenario extends StepScenario {
11
13
  readonly workflow: string;
12
14
  readonly exportName: string;
13
15
  readonly input: HandoffValue;
14
- readonly decision?: HandoffValue;
15
- }
16
- export type ScenarioRequest = readonly ["effect", ProtectedRequest] | readonly ["gate", GateRequest] | readonly ["waitForDecision", GateRequest] | readonly ["handoff", HandoffRequest] | readonly ["waitForRun", RunWaitRequest] | readonly ["sleep", {
17
- readonly seconds: number;
18
- }];
19
- export interface ScenarioResult {
20
- readonly spent?: number;
21
- readonly outcome: unknown;
22
- readonly requests: readonly ScenarioRequest[];
23
16
  }
17
+ export type { ScenarioRequest, ScenarioResult } from "./testing-requests.ts";
24
18
  export declare function fakeStepBridge(scenario: StepScenario): {
25
19
  requests: ScenarioRequest[];
26
20
  readonly spent: number;
@@ -5,7 +5,7 @@ import { join, resolve } from "node:path";
5
5
  import { pathToFileURL } from "node:url";
6
6
  import { workflowTestLoader } from "../tools/workflow-test-loader.js";
7
7
  import { authorizeEffect } from "./effect-authorization.js";
8
- import { record } from "./protocol.js";
8
+ import { scenarioResult, } from "./testing-requests.js";
9
9
  import { stepBridgeKey, } from "./step-bridge.js";
10
10
  export function fakeStepBridge(scenario) {
11
11
  const { bridge, requests, budget } = scenarioBridge(scenario);
@@ -22,7 +22,12 @@ export function fakeStepBridge(scenario) {
22
22
  Object.defineProperty(globalThis, sleepKey, {
23
23
  configurable: true,
24
24
  value: (milliseconds) => {
25
- requests.push(["sleep", { seconds: milliseconds / 1000 }]);
25
+ requests.push([
26
+ "sleep",
27
+ milliseconds instanceof Date
28
+ ? { until: milliseconds.toISOString() }
29
+ : { seconds: milliseconds / 1000 },
30
+ ]);
26
31
  return Promise.resolve();
27
32
  },
28
33
  });
@@ -50,6 +55,7 @@ function scenarioBridge(scenario) {
50
55
  const budget = { spent: 0 };
51
56
  const bridge = {
52
57
  ...scenarioEffects(scenario, { requests, budget }),
58
+ ...scenarioCommandWaits(scenario, requests),
53
59
  gate: (request) => {
54
60
  requests.push(["gate", request]);
55
61
  return Promise.resolve(scenario.gate ?? {
@@ -75,6 +81,30 @@ function scenarioBridge(scenario) {
75
81
  };
76
82
  return { bridge, requests, budget };
77
83
  }
84
+ function scenarioCommandWaits(scenario, requests) {
85
+ const waits = new Map(Object.entries(scenario.commandWaits ?? {}).map(([name, values]) => [
86
+ name,
87
+ [...values],
88
+ ]));
89
+ return {
90
+ openCommandWait: (request) => {
91
+ requests.push(["waitForCommand", request]);
92
+ if (!waits.get(request.name)?.length)
93
+ return Promise.reject(new Error("no-scenario-result"));
94
+ return Promise.resolve({
95
+ kind: "pending",
96
+ deadline: 0,
97
+ nextCheckAt: 0,
98
+ });
99
+ },
100
+ checkCommandWait: (request) => {
101
+ const result = waits.get(request.name)?.shift();
102
+ if (result === undefined)
103
+ return Promise.reject(new Error("no-scenario-result"));
104
+ return Promise.resolve(result);
105
+ },
106
+ };
107
+ }
78
108
  function scenarioEffects(scenario, state) {
79
109
  const { requests, budget } = state;
80
110
  const effects = new Map(Object.entries(scenario.effects ?? {}).map(([key, values]) => [
@@ -133,93 +163,10 @@ const given = ${JSON.stringify(scenario)};
133
163
  const workflow = workflows[given.exportName];
134
164
  if (typeof workflow !== "function") throw new Error("workflow-test-export-missing");
135
165
  using bridge = fakeStepBridge(given);
136
- const outcome = await workflow(given.input, given.decision);
166
+ const outcome = await workflow(given.input);
137
167
  await writeFile(process.argv[2], JSON.stringify({ outcome, requests: bridge.requests,
138
168
  ...(given.effectLimit === undefined ? {} : { spent: bridge.spent }) }));`;
139
169
  }
140
- function scenarioResult(value) {
141
- const result = record(value);
142
- return (result !== undefined &&
143
- Object.hasOwn(result, "outcome") &&
144
- Array.isArray(result["requests"]) &&
145
- result["requests"].every(scenarioRequest) &&
146
- (result["spent"] === undefined || typeof result["spent"] === "number"));
147
- }
148
- function scenarioRequest(value) {
149
- if (!Array.isArray(value) || value.length !== 2)
150
- return false;
151
- const request = record(value[1]);
152
- if (request === undefined)
153
- return false;
154
- switch (value[0]) {
155
- case "effect":
156
- return protectedRequest(request);
157
- case "gate":
158
- case "waitForDecision":
159
- return gateRequest(request);
160
- case "handoff":
161
- return handoffRequest(request);
162
- case "waitForRun":
163
- return runWaitRequest(request);
164
- case "sleep":
165
- return sleepRequest(request);
166
- default:
167
- return false;
168
- }
169
- }
170
- function runWaitRequest(request) {
171
- return (typeof request["runId"] === "string" &&
172
- typeof request["maximumWaitSeconds"] === "number");
173
- }
174
- function sleepRequest(request) {
175
- const seconds = request["seconds"];
176
- return (typeof seconds === "number" &&
177
- Number.isInteger(seconds) &&
178
- seconds >= 1 &&
179
- seconds <= 86400);
180
- }
181
- function gateRequest(request) {
182
- return (typeof request["gateId"] === "string" &&
183
- (request["kind"] === "question" || request["kind"] === "approval") &&
184
- typeof request["prompt"] === "string" &&
185
- optionalText(request["expiresAt"]));
186
- }
187
- function handoffRequest(request) {
188
- const input = record(request["input"]);
189
- return (typeof request["workflowId"] === "string" &&
190
- input !== undefined &&
191
- Object.values(input).every(handoffValue));
192
- }
193
- function protectedRequest(request) {
194
- return (protectedScope(request["scope"]) &&
195
- ["capability", "method", "target", "payload"].every((key) => typeof request[key] === "string") &&
196
- typeof request["units"] === "number" &&
197
- optionalText(request["resource"]));
198
- }
199
- function protectedScope(value) {
200
- const scope = record(value);
201
- if (scope === undefined)
202
- return false;
203
- const stage = record(scope["stage"]);
204
- return (typeof scope["phase"] === "string" &&
205
- typeof scope["operation"] === "string" &&
206
- typeof scope["round"] === "number" &&
207
- optionalText(scope["key"]) &&
208
- (scope["stage"] === undefined ||
209
- (stage !== undefined &&
210
- ["id", "visit", "branch"].every((key) => typeof stage[key] === "string"))));
211
- }
212
- function optionalText(value) {
213
- return value === undefined || typeof value === "string";
214
- }
215
- function handoffValue(value) {
216
- if (value === null || ["string", "boolean", "number"].includes(typeof value))
217
- return true;
218
- if (Array.isArray(value))
219
- return value.every(handoffValue);
220
- const fields = record(value);
221
- return fields !== undefined && Object.values(fields).every(handoffValue);
222
- }
223
170
  export async function runScenario(scenario) {
224
171
  const temporary = await mkdtemp(join(tmpdir(), "cloving-scenario-"));
225
172
  try {
@@ -16,6 +16,7 @@ export interface WorkerTurnUsage {
16
16
  readonly costUsd: number | null;
17
17
  }
18
18
  export interface WorkerAttemptUsage {
19
+ readonly resumedAfter?: "daemon-restart";
19
20
  readonly models?: readonly WorkerModelUsage[];
20
21
  readonly retryKind?: RetryKind | null;
21
22
  readonly attempt: number;
@@ -85,6 +85,9 @@ export function attemptsOf(value) {
85
85
  return [
86
86
  {
87
87
  ...retryKindField(field("retryKind")),
88
+ ...(field("resumedAfter") === "daemon-restart"
89
+ ? { resumedAfter: "daemon-restart" }
90
+ : {}),
88
91
  attempt,
89
92
  model,
90
93
  ...(models === undefined ? {} : { models }),
@@ -13,6 +13,7 @@ export interface CandidateEffect {
13
13
  readonly method: string | null;
14
14
  }
15
15
  export interface CandidateAnalysis {
16
+ readonly commandWaits?: readonly (string | null)[];
16
17
  readonly localSources?: readonly string[];
17
18
  readonly imports: readonly CandidateImport[];
18
19
  readonly effects: readonly CandidateEffect[];
@@ -15,6 +15,7 @@ const workflowModules = [
15
15
  "cloving/workflows/steps/wait-for-run",
16
16
  "cloving/workflows/steps/run-command",
17
17
  "cloving/workflows/steps/sleep",
18
+ "cloving/workflows/steps/wait-for-command",
18
19
  ];
19
20
  export function workflowHelperPath(source, imported) {
20
21
  if (!imported.startsWith("./"))
@@ -66,7 +67,7 @@ export function checkWorkflowCandidate(input) {
66
67
  if (!isCandidateDeclaration(input.declaration))
67
68
  return candidateDeclarationRefusals(input.declaration);
68
69
  const { analysis, declaration, installedExports } = input;
69
- const codes = [];
70
+ const codes = commandWaitRefusals(analysis, declaration);
70
71
  if (analysis.imports.some((entry) => !allowedImport(entry, analysis)))
71
72
  codes.push("forbidden-import");
72
73
  if (analysis.imports.some((entry) => {
@@ -100,3 +101,13 @@ export function candidateDeclarationRefusals(value) {
100
101
  const codes = workflowQueueRefusals(record(value)?.["workflow"]);
101
102
  return codes.length > 0 ? codes : ["invalid-declaration"];
102
103
  }
104
+ function commandWaitRefusals(analysis, declaration) {
105
+ const waits = analysis.commandWaits ?? [];
106
+ const codes = [];
107
+ if (waits.includes(null))
108
+ codes.push("dynamic-effect");
109
+ if (waits.some((name) => name !== null &&
110
+ !Object.hasOwn(declaration.workflow.commandWaits ?? {}, name)))
111
+ codes.push("undeclared-effect");
112
+ return codes;
113
+ }
@@ -17,6 +17,7 @@ export type CycleOutcome = {
17
17
  readonly ticketStatus?: "complete" | "partial";
18
18
  readonly issueUpdate?: "failed";
19
19
  readonly reason?: string;
20
+ readonly report?: string;
20
21
  };
21
22
  export type Validation<Value> = {
22
23
  readonly kind: "valid";