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
@@ -0,0 +1,157 @@
1
+ import assert from "node:assert/strict";
2
+ import {
3
+ fakeStepBridge,
4
+ type ScenarioRequest,
5
+ } from "cloving/workflows/testing";
6
+ import { develop } from "../develop.ts";
7
+ import {
8
+ approved,
9
+ callName,
10
+ completeCalls,
11
+ requestInput,
12
+ scenarioEffects,
13
+ } from "./option-tests.ts";
14
+ import { developReport, ticketInput } from "./report.ts";
15
+ import { completed, input } from "./scenarios.ts";
16
+
17
+ const interrupted = {
18
+ kind: "failed" as const,
19
+ operationId: "interrupted-turn",
20
+ reason: "claude-turn-interrupted",
21
+ };
22
+
23
+ function turns(requests: readonly ScenarioRequest[]) {
24
+ return requests.flatMap((request) =>
25
+ request[0] === "effect" && request[1].method === "run-turn"
26
+ ? [request[1]]
27
+ : [],
28
+ );
29
+ }
30
+
31
+ export const interruptionTests = [
32
+ {
33
+ name: "CLV-272 an interrupted turn runs again once in the same work tree",
34
+ run: async () => {
35
+ const scenario = scenarioEffects();
36
+ using bridge = fakeStepBridge({
37
+ effects: {
38
+ ...scenario,
39
+ "claude run-turn": [interrupted, ...scenario["claude run-turn"]],
40
+ },
41
+ decisions: [approved, approved],
42
+ });
43
+ assert.equal((await develop(requestInput())).kind, "handoff_requested");
44
+ const expected = completeCalls();
45
+ expected.splice(expected.indexOf("run-turn"), 0, "run-turn");
46
+ assert.deepEqual(bridge.requests.map(callName), expected);
47
+ const [first, second] = turns(bridge.requests);
48
+ assert.ok(first && second);
49
+ assert.deepEqual(first.scope, {
50
+ phase: "implement",
51
+ operation: "claude-turn",
52
+ round: 1,
53
+ });
54
+ assert.ok(!first.payload.includes("stopped before its report"));
55
+ assert.deepEqual(second.scope, {
56
+ phase: "implement",
57
+ operation: "claude-turn",
58
+ round: 1,
59
+ key: "continuation",
60
+ });
61
+ assert.equal(second.target, first.target);
62
+ assert.ok(second.payload.endsWith(first.payload));
63
+ for (const sentence of [
64
+ "The earlier turn of round 1 stopped before its report.",
65
+ "The work tree holds the work of that turn.",
66
+ "Before you continue, check the work tree, the commits since the start of the round, and the ticket.",
67
+ ])
68
+ assert.ok(second.payload.includes(sentence), sentence);
69
+ },
70
+ },
71
+ {
72
+ name: "CLV-272 a second interruption of the same turn blocks the run",
73
+ run: async () => {
74
+ using bridge = fakeStepBridge({
75
+ effects: {
76
+ ...scenarioEffects(),
77
+ "claude run-turn": [interrupted, interrupted],
78
+ },
79
+ });
80
+ assert.deepEqual(await develop(requestInput()), {
81
+ kind: "blocked",
82
+ cycleId: requestInput().cycleId,
83
+ reason: "turn-interrupted",
84
+ evidenceId: "implement",
85
+ });
86
+ assert.deepEqual(bridge.requests.map(callName), stoppedCalls());
87
+ const labels = bridge.requests.flatMap((request) =>
88
+ request[0] === "effect" && request[1].method === "label"
89
+ ? [request[1].payload]
90
+ : [],
91
+ );
92
+ for (const payload of labels.slice(1))
93
+ assert.match(payload, /"add":\["cloving: blocked"\]/);
94
+ },
95
+ },
96
+ {
97
+ name: "CLV-272 an interrupted fix turn continues with its round and feedback",
98
+ run: async () => {
99
+ using bridge = fakeStepBridge({
100
+ effects: {
101
+ "claude run-turn": [
102
+ interrupted,
103
+ completed({ summary: "Fixed.\nTicket status: complete" }),
104
+ ],
105
+ },
106
+ });
107
+ const output = "Check output.\n";
108
+ const report = await developReport({
109
+ input: ticketInput(input),
110
+ round: 2,
111
+ feedback: { reason: "check-failed", output, remaining: ["Add it."] },
112
+ });
113
+ assert.ok(!("kind" in report));
114
+ assert.equal(report.summary, "Fixed.\nTicket status: complete");
115
+ const [first, second] = turns(bridge.requests);
116
+ assert.ok(first && second);
117
+ assert.equal(first.scope.round, 2);
118
+ assert.equal(second.scope.round, 2);
119
+ assert.equal(second.scope.key, "continuation");
120
+ assert.ok(
121
+ second.payload.startsWith(
122
+ "The earlier turn of round 2 stopped before its report.",
123
+ ),
124
+ );
125
+ assert.ok(second.payload.endsWith(first.payload));
126
+ assert.ok(second.payload.endsWith(output));
127
+ assert.match(
128
+ second.payload,
129
+ /This is fix round 2\. Address check-failed/,
130
+ );
131
+ },
132
+ },
133
+ ...[
134
+ { kind: "failed" as const, operationId: "turn", reason: "worker-failed" },
135
+ { kind: "refused" as const, reason: "budget-exhausted" },
136
+ ].map((failure) => ({
137
+ name: `CLV-272 a ${failure.reason} continuation fails the run`,
138
+ run: async () => {
139
+ using bridge = fakeStepBridge({
140
+ effects: {
141
+ ...scenarioEffects(),
142
+ "claude run-turn": [interrupted, failure],
143
+ },
144
+ });
145
+ await assert.rejects(develop(requestInput()), {
146
+ message: failure.reason,
147
+ });
148
+ assert.deepEqual(bridge.requests.map(callName), stoppedCalls());
149
+ },
150
+ })),
151
+ ];
152
+
153
+ function stoppedCalls(): string[] {
154
+ const complete = completeCalls();
155
+ const started = complete.slice(0, complete.indexOf("run-turn") + 1);
156
+ return started.concat("run-turn", started.includes("label") ? ["label"] : []);
157
+ }
@@ -13,6 +13,7 @@ export async function localRound(
13
13
  ): Promise<CycleOutcome | DevelopFeedback> {
14
14
  const { input, round } = state;
15
15
  const report = await developReport(state);
16
+ if ("kind" in report) return report;
16
17
  const partial = await partialChoice({ input, round, report });
17
18
  if (partial !== null) return partial;
18
19
  if (
@@ -4,12 +4,20 @@ import {
4
4
  type ScenarioRequest,
5
5
  } from "cloving/workflows/testing";
6
6
  import { develop } from "../develop.ts";
7
- import { commit, input, completed, effects, selection } from "./scenarios.ts";
7
+ import {
8
+ commit,
9
+ input,
10
+ completed,
11
+ effects,
12
+ selection,
13
+ repairVerdicts,
14
+ verificationEffects,
15
+ } from "./scenarios.ts";
8
16
 
9
17
  const taskText =
10
18
  "Develop the submitted task text.\n\nGitHub issue: acme/demo#42 (open)";
11
19
 
12
- function requestInput() {
20
+ export function requestInput() {
13
21
  return selection.input === "ticket"
14
22
  ? input
15
23
  : {
@@ -19,7 +27,7 @@ function requestInput() {
19
27
  };
20
28
  }
21
29
 
22
- function scenarioEffects() {
30
+ export function scenarioEffects() {
23
31
  return {
24
32
  ...effects(),
25
33
  "claude run-turn": [
@@ -33,10 +41,13 @@ function scenarioEffects() {
33
41
  completed({ summary: "Reviewed.", reviewVerdict: "accept" }),
34
42
  ],
35
43
  "github partial-issue": [completed({ ticketStatus: "partial" })],
44
+ "command run:red-green": [
45
+ completed({ verdict: "accepted", output: "Passed." }),
46
+ ],
36
47
  };
37
48
  }
38
49
 
39
- function completeCalls(): string[] {
50
+ export function completeCalls(): string[] {
40
51
  const review: Readonly<Record<string, string>> = {
41
52
  greptile: "wait-review",
42
53
  person: "approval",
@@ -46,15 +57,17 @@ function completeCalls(): string[] {
46
57
  if (selection.merge === "local") return acceptance;
47
58
  return ["prepare"].concat(
48
59
  selection.input === "ticket" ? ["label"] : [],
49
- ["run-turn", "open-pr", review[selection.accept] ?? "unknown-review"],
60
+ ["run-turn", "open-pr"],
61
+ selection.verify === "red-green" ? ["run:red-green"] : [],
62
+ [review[selection.accept] ?? "unknown-review"],
50
63
  selection.merge === "person" ? ["approval", "merge"] : ["merge"],
51
64
  selection.input === "ticket" ? ["close-issue"] : [],
52
65
  );
53
66
  }
54
67
 
55
- const approved = { decision: "approved" as const, answer: null };
68
+ export const approved = { decision: "approved" as const, answer: null };
56
69
 
57
- function callName(request: ScenarioRequest): string {
70
+ export function callName(request: ScenarioRequest): string {
58
71
  if (request[0] === "effect") return request[1].method;
59
72
  if (request[0] === "waitForDecision") return request[1].kind;
60
73
  return request[0];
@@ -67,7 +80,7 @@ export const optionTests = [
67
80
  using bridge = fakeStepBridge({
68
81
  effects: scenarioEffects(),
69
82
  decisions: [approved, approved],
70
- effectLimit: 27,
83
+ effectLimit: selection.verify === "red-green" ? 35 : 31,
71
84
  });
72
85
  assert.deepEqual(await develop(requestInput()), {
73
86
  kind: "handoff_requested",
@@ -171,4 +184,120 @@ export const optionTests = [
171
184
  assert.equal(updates.length, Number(selection.partial === "queue"));
172
185
  },
173
186
  },
174
- ];
187
+ ].concat(selection.verify === "red-green" ? verificationTests() : []);
188
+
189
+ function verificationTests() {
190
+ return repairTests().concat(passingTests(), unavailableTests(), limitTests());
191
+ }
192
+
193
+ function repairTests() {
194
+ return repairVerdicts.map((verdict) => ({
195
+ name: `CLV-249 ${verdict} starts a fix turn with the full tool output`,
196
+ run: async () => {
197
+ using bridge = fakeStepBridge({
198
+ effects: verificationEffects([verdict, "accepted"]),
199
+ decisions: [approved, approved],
200
+ effectLimit: 35,
201
+ });
202
+ assert.equal((await develop(requestInput())).kind, "handoff_requested");
203
+ const expected = completeCalls();
204
+ expected.splice(
205
+ expected.indexOf("run:red-green") + 1,
206
+ 0,
207
+ "run-turn",
208
+ "open-pr",
209
+ "run:red-green",
210
+ );
211
+ assert.deepEqual(bridge.requests.map(callName), expected);
212
+ const fix = bridge.requests.find(
213
+ (request) =>
214
+ request[0] === "effect" &&
215
+ request[1].method === "run-turn" &&
216
+ request[1].scope.round === 2,
217
+ );
218
+ assert.ok(fix && fix[0] === "effect");
219
+ assert.ok(fix[1].payload.includes(`Address ${verdict}`));
220
+ assert.ok(fix[1].payload.endsWith(`${verdict}\nFull test output.`));
221
+ },
222
+ }));
223
+ }
224
+
225
+ function passingTests() {
226
+ return ["accepted", "no-test-change"].map((verdict) => ({
227
+ name: `CLV-249 ${verdict} continues to review and merge`,
228
+ run: async () => {
229
+ using bridge = fakeStepBridge({
230
+ effects: verificationEffects([verdict]),
231
+ decisions: [approved, approved],
232
+ });
233
+ assert.equal((await develop(requestInput())).kind, "handoff_requested");
234
+ assert.deepEqual(bridge.requests.map(callName), completeCalls());
235
+ },
236
+ }));
237
+ }
238
+
239
+ function limitTests() {
240
+ return [
241
+ {
242
+ name: "CLV-249 verification repairs stop after four development rounds",
243
+ run: async () => {
244
+ using bridge = fakeStepBridge({
245
+ effects: verificationEffects(Array(4).fill("false-red")),
246
+ effectLimit: 35,
247
+ });
248
+ assert.deepEqual(await develop(requestInput()), {
249
+ kind: "blocked",
250
+ cycleId: input.cycleId,
251
+ reason: "false-red",
252
+ evidenceId: "merge",
253
+ });
254
+ const names = bridge.requests.map(callName);
255
+ assert.equal(names.filter((name) => name === "run-turn").length, 4);
256
+ assert.equal(
257
+ names.filter((name) => name === "run:red-green").length,
258
+ 4,
259
+ );
260
+ assert.ok(!names.includes("wait-review") && !names.includes("merge"));
261
+ },
262
+ },
263
+ ];
264
+ }
265
+
266
+ function unavailableTests() {
267
+ const unavailable = [
268
+ completed({
269
+ verdict: "prepare-failed",
270
+ output: "Dependency preparation failed.",
271
+ }),
272
+ { kind: "failed" as const, operationId: "command", reason: "exit-1" },
273
+ { kind: "unavailable" as const, reason: "cloving-host-unavailable" },
274
+ { ...completed(null), result: "invalid JSON" },
275
+ completed(null),
276
+ completed({ verdict: "unknown", output: "Unknown verdict." }),
277
+ completed({ verdict: "accepted" }),
278
+ completed({ verdict: "false-red", output: 42 }),
279
+ ];
280
+ return unavailable.map((result, index) => ({
281
+ name: `CLV-249 unavailable verification ${String(index)} blocks without a fix turn`,
282
+ run: async () => {
283
+ using bridge = fakeStepBridge({
284
+ effects: {
285
+ ...verificationEffects(["accepted"]),
286
+ "command run:red-green": [result],
287
+ },
288
+ });
289
+ assert.deepEqual(await develop(requestInput()), {
290
+ kind: "blocked",
291
+ cycleId: input.cycleId,
292
+ reason: "red-green-unavailable",
293
+ evidenceId: "red-green",
294
+ });
295
+ const complete = completeCalls();
296
+ const expected = complete.slice(0, complete.indexOf("run:red-green") + 1);
297
+ assert.deepEqual(
298
+ bridge.requests.map(callName),
299
+ expected.concat(selection.input === "ticket" ? ["label"] : []),
300
+ );
301
+ },
302
+ }));
303
+ }
@@ -21,6 +21,7 @@ export async function openPull(request: {
21
21
  readonly round: number;
22
22
  }): Promise<PullRequest> {
23
23
  const { input, round } = request;
24
+ const issue = input.issue;
24
25
  const result = await protectedEffect({
25
26
  scope: { phase: "pull-request", operation: "github-open-pr", round: round },
26
27
  capability: "github",
@@ -30,7 +31,10 @@ export async function openPull(request: {
30
31
  title: (input.ticket.split("\n")[0] ?? "Develop task")
31
32
  .replace(/^#+\s*/, "")
32
33
  .slice(0, 200),
33
- body: "Review the committed changes and repository checks for this task.",
34
+ body:
35
+ issue === null
36
+ ? "Cloving made this change for this task."
37
+ : `Refs ${issue.owner}/${issue.repository}#${String(issue.number)}\n\nCloving made this change for the ticket ${String(issue.number)}.`,
34
38
  }),
35
39
  units: 1,
36
40
  });
@@ -0,0 +1,55 @@
1
+ import { runCommand } from "cloving/workflows/steps/run-command";
2
+ import type { CycleOutcome } from "cloving/workflows";
3
+
4
+ type VerificationFeedback = {
5
+ readonly reason: string;
6
+ readonly output: string;
7
+ };
8
+
9
+ export async function verifyRedGreen(input: {
10
+ readonly cycleId: string;
11
+ readonly head: string;
12
+ readonly round: number;
13
+ }): Promise<CycleOutcome | VerificationFeedback | null> {
14
+ const unavailable: CycleOutcome = {
15
+ kind: "blocked",
16
+ cycleId: input.cycleId,
17
+ reason: "red-green-unavailable",
18
+ evidenceId: "red-green",
19
+ };
20
+ try {
21
+ const result = await runCommand({
22
+ name: "red-green",
23
+ scope: { phase: "verify", operation: "red-green", round: input.round },
24
+ input: { head: input.head },
25
+ });
26
+ if (result.kind !== "completed") return unavailable;
27
+ const feedback = verdictFeedback(result.result);
28
+ return feedback === undefined ? unavailable : feedback;
29
+ } catch {
30
+ return unavailable;
31
+ }
32
+ }
33
+
34
+ function verdictFeedback(
35
+ text: string | null,
36
+ ): VerificationFeedback | null | undefined {
37
+ const value: unknown = JSON.parse(text ?? "null");
38
+ if (
39
+ typeof value !== "object" ||
40
+ value === null ||
41
+ !("verdict" in value) ||
42
+ typeof value.verdict !== "string" ||
43
+ !("output" in value) ||
44
+ typeof value.output !== "string"
45
+ )
46
+ return undefined;
47
+ if (["accepted", "no-test-change"].includes(value.verdict)) return null;
48
+ if (
49
+ ["false-red", "red-not-assertion", "skipped-test", "green-failed"].includes(
50
+ value.verdict,
51
+ )
52
+ )
53
+ return { reason: value.verdict, output: value.output };
54
+ return undefined;
55
+ }
@@ -9,6 +9,83 @@ import { effects } from "./scenarios.ts";
9
9
 
10
10
  export const refusalTests: FlowTest[] = [];
11
11
 
12
+ refusalTests.push({
13
+ name: "CLV-260 a linked pull request names the issue and preserves the ticket title",
14
+ run: async () => {
15
+ using bridge = fakeStepBridge({
16
+ turn: completed({ number: 7, url: "pull/7", commit }),
17
+ });
18
+ await openPull({
19
+ input: {
20
+ cycleId: input.cycleId,
21
+ repository: input.repository,
22
+ ticket:
23
+ "# CLV-228: Stop the registration of the bundled workflows\n\nGitHub issue: cloving-ai/cloving-ai#375 (open)",
24
+ issue: { owner: "cloving-ai", repository: "cloving-ai", number: 375 },
25
+ },
26
+ round: 1,
27
+ });
28
+ assert.equal(bridge.requests.length, 1);
29
+ const opened = bridge.requests[0];
30
+ assert.ok(opened && opened[0] === "effect");
31
+ assert.equal(opened[1].method, "open-pr");
32
+ assert.deepEqual(JSON.parse(opened[1].payload), {
33
+ title: "CLV-228: Stop the registration of the bundled workflows",
34
+ body: "Refs cloving-ai/cloving-ai#375\n\nCloving made this change for the ticket 375.",
35
+ });
36
+ },
37
+ });
38
+
39
+ refusalTests.push({
40
+ name: "CLV-260 a task without an issue opens a pull request without a reference",
41
+ run: async () => {
42
+ using bridge = fakeStepBridge({
43
+ turn: completed({ number: 7, url: "pull/7", commit }),
44
+ });
45
+ await openPull({
46
+ input: {
47
+ cycleId: input.cycleId,
48
+ repository: input.repository,
49
+ ticket: "### Manual task\n\nGitHub issue: acme/demo#42 (open)",
50
+ issue: null,
51
+ },
52
+ round: 1,
53
+ });
54
+ assert.equal(bridge.requests.length, 1);
55
+ const opened = bridge.requests[0];
56
+ assert.ok(opened && opened[0] === "effect");
57
+ assert.equal(opened[1].method, "open-pr");
58
+ assert.deepEqual(JSON.parse(opened[1].payload), {
59
+ title: "Manual task",
60
+ body: "Cloving made this change for this task.",
61
+ });
62
+ },
63
+ });
64
+
65
+ refusalTests.push({
66
+ name: "CLV-260 pull request titles use at most 200 characters from the first line",
67
+ run: async () => {
68
+ using bridge = fakeStepBridge({
69
+ turn: completed({ number: 7, url: "pull/7", commit }),
70
+ });
71
+ await openPull({
72
+ input: {
73
+ cycleId: input.cycleId,
74
+ repository: input.repository,
75
+ ticket: `### ${"Title".repeat(50)}\nSecond line`,
76
+ issue: null,
77
+ },
78
+ round: 1,
79
+ });
80
+ const opened = bridge.requests[0];
81
+ assert.ok(opened && opened[0] === "effect");
82
+ assert.deepEqual(JSON.parse(opened[1].payload), {
83
+ title: "Title".repeat(40),
84
+ body: "Cloving made this change for this task.",
85
+ });
86
+ },
87
+ });
88
+
12
89
  for (const operation of [
13
90
  "claude run-turn",
14
91
  "github open-pr",
@@ -4,7 +4,7 @@ import {
4
4
  type ProtectedResult,
5
5
  type TicketStatus,
6
6
  } from "cloving/workflows";
7
- import { developTurn } from "./turn.ts";
7
+ import { developTurn, type DevelopTurnInput } from "./turn.ts";
8
8
 
9
9
  export interface DevelopInput {
10
10
  readonly cycleId: string;
@@ -72,16 +72,36 @@ export interface DevelopState {
72
72
 
73
73
  export async function developReport(
74
74
  state: DevelopState,
75
- ): Promise<DevelopReport> {
76
- const { input, round, feedback } = state;
77
- return reportOf(
78
- await developTurn({
79
- cycleId: input.cycleId,
80
- repository: input.repository,
81
- ticket: input.ticket,
82
- round,
83
- feedback,
84
- }),
75
+ ): Promise<DevelopReport | CycleOutcome> {
76
+ const first = await developTurn(turnRequest(state, "initial"));
77
+ if (!interrupted(first)) return reportOf(first);
78
+ const second = await developTurn(turnRequest(state, "continuation"));
79
+ if (!interrupted(second)) return reportOf(second);
80
+ return {
81
+ kind: "blocked",
82
+ cycleId: state.input.cycleId,
83
+ reason: "turn-interrupted",
84
+ evidenceId: "implement",
85
+ };
86
+ }
87
+
88
+ function turnRequest(
89
+ state: DevelopState,
90
+ attempt: "initial" | "continuation",
91
+ ): DevelopTurnInput {
92
+ return {
93
+ cycleId: state.input.cycleId,
94
+ repository: state.input.repository,
95
+ ticket: state.input.ticket,
96
+ round: state.round,
97
+ attempt,
98
+ feedback: state.feedback,
99
+ };
100
+ }
101
+
102
+ function interrupted(result: ProtectedResult): boolean {
103
+ return (
104
+ result.kind !== "completed" && result.reason === "claude-turn-interrupted"
85
105
  );
86
106
  }
87
107
 
@@ -13,6 +13,7 @@ export async function developRound(state: {
13
13
  const { input, round } = state;
14
14
  try {
15
15
  const report = await developReport(state);
16
+ if ("kind" in report) return report;
16
17
  const pull = await openPull({ input, round });
17
18
  const subject = {
18
19
  target: pull.target,
@@ -2,6 +2,7 @@ export const commit = "a".repeat(40);
2
2
  export const selection = {
3
3
  input: "ticket",
4
4
  select: "queue",
5
+ verify: "none",
5
6
  accept: "greptile",
6
7
  merge: "green",
7
8
  partial: "queue",
@@ -52,6 +53,32 @@ export function effects() {
52
53
  };
53
54
  }
54
55
 
56
+ export const repairVerdicts = [
57
+ "false-red",
58
+ "red-not-assertion",
59
+ "skipped-test",
60
+ "green-failed",
61
+ ];
62
+
63
+ export function verificationEffects(verdicts: readonly string[]) {
64
+ const scenario = effects();
65
+ return {
66
+ ...scenario,
67
+ "claude run-turn": verdicts.map(() =>
68
+ completed({ summary: "Implemented.\nTicket status: complete" }),
69
+ ),
70
+ "github open-pr": verdicts.map(() =>
71
+ completed({ number: 7, url: "pull/7", commit }),
72
+ ),
73
+ "command run:red-green": verdicts.map((verdict) =>
74
+ completed({ verdict, output: `${verdict}\nFull test output.` }),
75
+ ),
76
+ "claude run-turn:review": [
77
+ completed({ summary: "Reviewed.", reviewVerdict: "accept" }),
78
+ ],
79
+ };
80
+ }
81
+
55
82
  export function partialEffects(
56
83
  pullRequest: number,
57
84
  remaining: readonly string[],