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
@@ -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";
@@ -43,7 +43,7 @@ const outcomes = {
43
43
  },
44
44
  handoff_requested: {
45
45
  keys: ["cycleId", "kind", "promotionId"],
46
- optional: ["ticketStatus", "issueUpdate", "reason"],
46
+ optional: ["ticketStatus", "issueUpdate", "reason", "report"],
47
47
  parse: (fields) => {
48
48
  const cycleId = textField(fields, {
49
49
  key: "cycleId",
@@ -58,6 +58,9 @@ const outcomes = {
58
58
  const ticketStatus = fields["ticketStatus"];
59
59
  const issueUpdate = fields["issueUpdate"];
60
60
  const reason = fields["reason"];
61
+ const report = fields["report"];
62
+ if (!validReport(report))
63
+ return null;
61
64
  if (issueUpdate !== undefined && issueUpdate !== "failed")
62
65
  return null;
63
66
  if (ticketStatus !== undefined && !isTicketStatus(ticketStatus))
@@ -68,11 +71,15 @@ const outcomes = {
68
71
  kind: "handoff_requested",
69
72
  cycleId,
70
73
  promotionId,
71
- ...Object.fromEntries(Object.entries({ issueUpdate, ticketStatus, reason }).filter(([, value]) => value !== undefined)),
74
+ ...Object.fromEntries(Object.entries({ issueUpdate, ticketStatus, reason, report }).filter(([, value]) => value !== undefined)),
72
75
  };
73
76
  },
74
77
  },
75
78
  };
79
+ function validReport(report) {
80
+ return (report === undefined ||
81
+ (typeof report === "string" && report.length > 0 && report.length <= 65536));
82
+ }
76
83
  export function validateDevelopmentInput(value) {
77
84
  const fields = exactFields(value, { required: ["cycleId"] });
78
85
  const cycleId = fields && textField(fields, { key: "cycleId", pattern: identifier });
@@ -31,6 +31,7 @@ export interface DeclaredWorkflow extends WorkflowDeclaration {
31
31
  }
32
32
  export type WorkflowQueueCode = "queue-needs-ticket-field" | "queue-input-incomplete";
33
33
  export declare function workflowQueueRefusals(value: unknown): readonly WorkflowQueueCode[];
34
+ export declare function workflowStagesOf(value: unknown): WorkflowStages;
34
35
  export declare function isWorkflowStages(value: unknown): value is readonly WorkflowStage[];
35
36
  export declare function isWorkflowNames(value: unknown): value is readonly string[];
36
37
  export declare function isCommandWaits(value: unknown, permissions: {
@@ -89,6 +89,9 @@ function isStage(value) {
89
89
  function unique(values) {
90
90
  return new Set(values).size === values.length;
91
91
  }
92
+ export function workflowStagesOf(value) {
93
+ return Object.fromEntries(Object.entries(record(value) ?? {}).flatMap(([workflowId, stages]) => isWorkflowStages(stages) ? [[workflowId, stages]] : []));
94
+ }
92
95
  export function isWorkflowStages(value) {
93
96
  if (!Array.isArray(value) || value.length < 1 || value.length > 12)
94
97
  return false;
@@ -1,19 +1,8 @@
1
- export interface WorkflowReservation {
2
- readonly baseName?: string;
3
- readonly workflowId?: string;
4
- readonly functionName?: string;
5
- }
6
1
  export interface WorkflowDiscoveryIdentity {
7
2
  readonly queue?: boolean;
8
3
  readonly baseName: string;
9
4
  readonly workflowId: string | undefined;
10
5
  readonly functionNames: readonly string[];
11
6
  }
12
- export type WorkflowConflictCode = "workflow-conflict" | "bundled-name-reserved" | "queue-workflow-conflict";
13
- export declare function workflowConflicts(candidates: readonly WorkflowDiscoveryIdentity[], bundled: readonly WorkflowReservation[]): readonly (readonly WorkflowConflictCode[])[];
14
- export declare const bundledWorkflowIds: {
15
- readonly ticket: "develop-ticket";
16
- readonly issue: "develop-issue";
17
- readonly prompt: "develop-prompt";
18
- readonly prd: "develop-prd";
19
- };
7
+ export type WorkflowConflictCode = "workflow-conflict" | "queue-workflow-conflict";
8
+ export declare function workflowConflicts(candidates: readonly WorkflowDiscoveryIdentity[]): readonly (readonly WorkflowConflictCode[])[];
@@ -5,32 +5,20 @@ function identityKeys(identity) {
5
5
  ...identity.functionNames.map((name) => `function:${name}`),
6
6
  ];
7
7
  }
8
- export function workflowConflicts(candidates, bundled) {
8
+ export function workflowConflicts(candidates) {
9
9
  const keys = candidates.map(identityKeys);
10
10
  const counts = new Map();
11
11
  for (const names of keys) {
12
12
  for (const name of new Set(names))
13
13
  counts.set(name, (counts.get(name) ?? 0) + 1);
14
14
  }
15
- const reserved = new Set(bundled.flatMap((entry) => identityKeys({
16
- ...entry,
17
- functionNames: entry.functionName === undefined ? [] : [entry.functionName],
18
- })));
19
15
  const queued = candidates.filter((candidate) => candidate.queue === true);
20
16
  return keys.map((names, index) => {
21
17
  const codes = [];
22
18
  if (queued.length > 1 && candidates[index]?.queue === true)
23
19
  codes.push("queue-workflow-conflict");
24
- if (names.some((name) => reserved.has(name)))
25
- codes.push("bundled-name-reserved");
26
20
  if (names.some((name) => (counts.get(name) ?? 0) > 1))
27
21
  codes.push("workflow-conflict");
28
22
  return codes;
29
23
  });
30
24
  }
31
- export const bundledWorkflowIds = {
32
- ticket: "develop-ticket",
33
- issue: "develop-issue",
34
- prompt: "develop-prompt",
35
- prd: "develop-prd",
36
- };
@@ -12,7 +12,6 @@ const refusalCodes = new Set([
12
12
  "effect-limit",
13
13
  "direct-effect",
14
14
  "workflow-conflict",
15
- "bundled-name-reserved",
16
15
  "unsafe-workflow-path",
17
16
  "workflow-read-failed",
18
17
  "workflow-source-changed",
@@ -1,6 +1,5 @@
1
1
  export { loadCycle, type CycleLoad } from "./steps/load-cycle.ts";
2
2
  export { protectedEffect } from "./steps/protected-effect.ts";
3
- export { decisionGate } from "./steps/decision-gate.ts";
4
3
  export { waitForDecision } from "./steps/wait-for-decision.ts";
5
4
  export { reviewByPerson } from "./steps/review-by-person.ts";
6
5
  export { reviewByAgent } from "./steps/review-by-agent.ts";
@@ -11,6 +10,7 @@ export type { MergeCheckName, MergeReason, MergeRequest, MergeResult, PullReques
11
10
  export { handoff } from "./steps/handoff.ts";
12
11
  export { waitForRun } from "./steps/wait-for-run.ts";
13
12
  export { runCommand } from "./steps/run-command.ts";
13
+ export { waitForCommand, type CommandWaitRequest, type CommandWaitOutcome, } from "./steps/wait-for-command.ts";
14
14
  export { sleep } from "./steps/sleep.ts";
15
15
  export type { CommandRequest, GateRequest, GateResult, DecisionResult, HandoffRequest, HandoffValue, RunWaitRequest, RunWaitOutcome, ProtectedRequest, ProtectedResult, ProtectedScope, } from "./step-bridge.ts";
16
16
  export type { CycleOutcome, DevelopmentInput } from "./workflow-contracts.ts";
@@ -1,6 +1,5 @@
1
1
  export { loadCycle } from "./steps/load-cycle.js";
2
2
  export { protectedEffect } from "./steps/protected-effect.js";
3
- export { decisionGate } from "./steps/decision-gate.js";
4
3
  export { waitForDecision } from "./steps/wait-for-decision.js";
5
4
  export { reviewByPerson } from "./steps/review-by-person.js";
6
5
  export { reviewByAgent } from "./steps/review-by-agent.js";
@@ -9,5 +8,6 @@ export { mergeWhenGreen } from "./steps/merge-when-green.js";
9
8
  export { handoff } from "./steps/handoff.js";
10
9
  export { waitForRun } from "./steps/wait-for-run.js";
11
10
  export { runCommand } from "./steps/run-command.js";
11
+ export { waitForCommand, } from "./steps/wait-for-command.js";
12
12
  export { sleep } from "./steps/sleep.js";
13
13
  export { ticketStatus } from "./ticket-status.js";
@@ -8,6 +8,9 @@ if (!sourceDirectory || !outputDirectory) {
8
8
  }
9
9
  const output = resolve(outputDirectory);
10
10
  class WorkflowBuilder extends BaseBuilder {
11
+ async getInputFiles() {
12
+ return (await super.getInputFiles()).toSorted();
13
+ }
11
14
  async build() {
12
15
  const inputFiles = await this.getInputFiles();
13
16
  await mkdir(output, { recursive: true });
@@ -1,4 +1,9 @@
1
- import { resolveSettings } from "../src/project-settings.ts";
1
+ import { resolveSettings, type TurnSettings } from "../src/project-settings.ts";
2
+ import { type TurnProfiles } from "../src/turn-profile.ts";
3
+ type ProjectConfiguration = ReturnType<typeof resolveSettings>;
2
4
  export declare function topLevel(given: string): string;
3
5
  export declare function projectRoot(given: string): Promise<string>;
4
- export declare function projectConfiguration(repository: string, environment: Readonly<NodeJS.ProcessEnv>): Promise<ReturnType<typeof resolveSettings>>;
6
+ export declare function projectConfiguration(repository: string, environment: Readonly<NodeJS.ProcessEnv>): Promise<ProjectConfiguration>;
7
+ export declare function configuredTurnSettings(configuration: ProjectConfiguration): Promise<TurnSettings>;
8
+ export declare function configurationDigests(configuration: ProjectConfiguration, profiles: TurnProfiles): Readonly<Record<string, string>>;
9
+ export {};
@@ -3,8 +3,10 @@ import { createHash } from "node:crypto";
3
3
  import { readFile, realpath } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
5
  import { basename, join, resolve } from "node:path";
6
- import { configurationRoot, resolveSettings } from "../src/project-settings.js";
6
+ import { configurationRoot, resolveSettings, settingDigests, } from "../src/project-settings.js";
7
7
  import { repositoryFileName } from "../src/repository-identity.js";
8
+ import { parseFallbackModels, parseTokenPrices } from "../src/token-prices.js";
9
+ import { isProfilesFile } from "../src/turn-profile.js";
8
10
  async function settingsFile(file) {
9
11
  try {
10
12
  return await readFile(file.path, "utf8");
@@ -45,3 +47,30 @@ export async function projectConfiguration(repository, environment) {
45
47
  }),
46
48
  });
47
49
  }
50
+ async function configuredProfiles(configuration) {
51
+ const path = configuration.environment["CLOVING_PROFILES"];
52
+ if (path === undefined)
53
+ return configuration.profiles;
54
+ try {
55
+ const value = JSON.parse(await readFile(path, "utf8"));
56
+ if (isProfilesFile(value))
57
+ return value.profiles;
58
+ }
59
+ catch {
60
+ throw new Error("invalid-profiles");
61
+ }
62
+ throw new Error("invalid-profiles");
63
+ }
64
+ export async function configuredTurnSettings(configuration) {
65
+ const { environment } = configuration;
66
+ const tokenPrices = parseTokenPrices(environment["CLOVING_TOKEN_PRICES"]);
67
+ const fallbackModels = parseFallbackModels(environment["CLOVING_FALLBACK_MODELS"]);
68
+ return {
69
+ profiles: await configuredProfiles(configuration),
70
+ fallbackModels,
71
+ tokenPrices,
72
+ };
73
+ }
74
+ export function configurationDigests(configuration, profiles) {
75
+ return settingDigests({ ...configuration, profiles }, (text) => createHash("sha256").update(text).digest("hex"));
76
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,174 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { record } from "../src/protocol.js";
6
+ import { configuration } from "../src/configuration-schema.js";
7
+ import { parseCommandSeconds } from "../src/command-settings.js";
8
+ import { redGreenProcess } from "./red-green-process.js";
9
+ function git(argumentsList) {
10
+ return execFileSync("git", [...argumentsList], {
11
+ encoding: "utf8",
12
+ stdio: ["ignore", "pipe", "pipe"],
13
+ });
14
+ }
15
+ async function run(input) {
16
+ const temporary = mkdtempSync(join(tmpdir(), "cloving-red-green-"));
17
+ const tree = join(temporary, "tree");
18
+ try {
19
+ git(["worktree", "add", "--detach", tree, input.revision]);
20
+ if (input.revision !== input.head)
21
+ git([
22
+ "--literal-pathspecs",
23
+ "-C",
24
+ tree,
25
+ "restore",
26
+ "--source",
27
+ input.head,
28
+ "--worktree",
29
+ "--",
30
+ ...input.files,
31
+ ]);
32
+ const options = {
33
+ directory: tree,
34
+ signal: input.signal,
35
+ milliseconds: input.milliseconds,
36
+ };
37
+ const prepared = input.prepare === undefined
38
+ ? undefined
39
+ : await redGreenProcess(input.prepare, options);
40
+ if (prepared !== undefined && prepared.status !== 0)
41
+ return { ...prepared, prepareFailed: true };
42
+ const tested = await redGreenProcess([...input.test, ...input.files], options);
43
+ return tested;
44
+ }
45
+ finally {
46
+ git(["worktree", "remove", "--force", tree]);
47
+ rmSync(temporary, { recursive: true, force: true });
48
+ }
49
+ }
50
+ async function redGreenCommand(signal) {
51
+ if (process.argv.slice(2).includes("--help")) {
52
+ process.stdout.write('Usage: cloving red-green\n\nReads {"head":"<full commit ID>"} on standard input.\nCLOVING_RED_GREEN configures test, testFiles, prepare, assertionPattern and skipPattern.\nPrints one JSON verdict from temporary detached worktrees.\n');
53
+ return;
54
+ }
55
+ let text = "";
56
+ for await (const chunk of process.stdin)
57
+ text += String(chunk);
58
+ const head = inputHead(text);
59
+ const { settings, assertion, skip } = redGreenSettings();
60
+ const { test, testFiles: prefixes, prepare } = settings;
61
+ if (git(["cat-file", "-t", head]).trim() !== "commit")
62
+ throw new Error("invalid-red-green-input: head must be a full commit ID");
63
+ if (git(["rev-parse", "--verify", head]).trim() !== head.toLowerCase())
64
+ throw new Error("invalid-red-green-input: head must be a full commit ID");
65
+ const base = git(["merge-base", head, "refs/remotes/origin/HEAD"]).trim();
66
+ const files = changedTests({ base, head, prefixes });
67
+ if (files.length === 0) {
68
+ process.stdout.write(JSON.stringify({
69
+ verdict: "no-test-change",
70
+ base,
71
+ head,
72
+ files,
73
+ output: "",
74
+ }) + "\n");
75
+ return;
76
+ }
77
+ const milliseconds = parseCommandSeconds(process.env["CLOVING_COMMAND_SECONDS"]) * 1000;
78
+ const options = { head, files, test, prepare, signal, milliseconds };
79
+ const red = await run({ ...options, revision: base });
80
+ signal.throwIfAborted();
81
+ const green = await run({ ...options, revision: head });
82
+ signal.throwIfAborted();
83
+ const result = classify({ red, green, assertion, skip });
84
+ process.stdout.write(JSON.stringify({
85
+ ...result,
86
+ output: result.output.slice(-16000),
87
+ base,
88
+ head,
89
+ files,
90
+ }) + "\n");
91
+ }
92
+ function changedTests(input) {
93
+ return git([
94
+ "diff",
95
+ "--no-renames",
96
+ "--name-only",
97
+ "--diff-filter=AMT",
98
+ "-z",
99
+ input.base,
100
+ input.head,
101
+ ])
102
+ .split("\0")
103
+ .filter((file) => input.prefixes.some((prefix) => file.startsWith(prefix)));
104
+ }
105
+ function classify(input) {
106
+ const { red, green, assertion, skip } = input;
107
+ for (const result of [red, green])
108
+ if (result.prepareFailed === true)
109
+ return { verdict: "prepare-failed", output: result.output };
110
+ for (const result of [red, green])
111
+ if (skip.test(result.output))
112
+ return { verdict: "skipped-test", output: result.output };
113
+ if (red.status === 0)
114
+ return { verdict: "false-red", output: red.output };
115
+ if (red.status === null || !assertion.test(red.output))
116
+ return { verdict: "red-not-assertion", output: red.output };
117
+ if (green.status !== 0)
118
+ return { verdict: "green-failed", output: green.output };
119
+ return { verdict: "accepted", output: green.output };
120
+ }
121
+ function inputHead(text) {
122
+ let input;
123
+ try {
124
+ input = JSON.parse(text);
125
+ }
126
+ catch {
127
+ throw new Error("invalid-red-green-input: expected JSON with a full commit ID in head");
128
+ }
129
+ const head = record(input)?.["head"];
130
+ if (typeof head !== "string" ||
131
+ !/^(?:[a-fA-F0-9]{40}|[a-fA-F0-9]{64})$/.test(head))
132
+ throw new Error("invalid-red-green-input: head must be a full commit ID");
133
+ return head;
134
+ }
135
+ function redGreenSettings() {
136
+ try {
137
+ const value = JSON.parse(process.env["CLOVING_RED_GREEN"] ?? "null");
138
+ const settings = configuration({ version: 1, checks: { redGreen: value } }, "project").checks?.redGreen;
139
+ if (settings === undefined)
140
+ throw new Error("missing settings");
141
+ return {
142
+ settings,
143
+ assertion: new RegExp(settings.assertionPattern ?? "ERR_ASSERTION"),
144
+ skip: new RegExp(settings.skipPattern ?? "# SKIP\\b"),
145
+ };
146
+ }
147
+ catch (error) {
148
+ throw new Error("invalid-CLOVING_RED_GREEN: " +
149
+ (error instanceof Error ? error.message : String(error)), { cause: error });
150
+ }
151
+ }
152
+ const controller = new AbortController();
153
+ const interrupt = () => {
154
+ controller.abort();
155
+ };
156
+ process.on("disconnect", interrupt);
157
+ if (!process.connected)
158
+ interrupt();
159
+ process.on("SIGTERM", interrupt);
160
+ process.on("SIGINT", interrupt);
161
+ try {
162
+ await redGreenCommand(controller.signal);
163
+ }
164
+ catch (error) {
165
+ process.stderr.write((error instanceof Error ? error.message : String(error)) + "\n");
166
+ process.exitCode = 1;
167
+ }
168
+ finally {
169
+ process.off("disconnect", interrupt);
170
+ if (process.connected)
171
+ process.disconnect?.();
172
+ process.off("SIGTERM", interrupt);
173
+ process.off("SIGINT", interrupt);
174
+ }
@@ -0,0 +1,10 @@
1
+ export interface RunResult {
2
+ readonly status: number | null;
3
+ readonly output: string;
4
+ readonly prepareFailed?: boolean;
5
+ }
6
+ export declare function redGreenProcess(argumentsList: readonly string[], options: {
7
+ readonly directory: string;
8
+ readonly signal: AbortSignal;
9
+ readonly milliseconds: number;
10
+ }): Promise<RunResult>;