cloving 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. package/README.md +235 -206
  2. package/dist/src/agent-harness-shell.d.ts +2 -1
  3. package/dist/src/agent-harness-shell.js +8 -5
  4. package/dist/src/authority/command-wait-checks.d.ts +8 -0
  5. package/dist/src/authority/command-wait-checks.js +88 -0
  6. package/dist/src/authority/command-wait-results.d.ts +15 -0
  7. package/dist/src/authority/command-wait-results.js +73 -0
  8. package/dist/src/authority/command-wait-view.d.ts +21 -0
  9. package/dist/src/authority/command-wait-view.js +34 -0
  10. package/dist/src/authority/command-waits.d.ts +39 -0
  11. package/dist/src/authority/command-waits.js +97 -0
  12. package/dist/src/authority/commands.js +22 -1
  13. package/dist/src/authority/dispatch-decision.d.ts +8 -0
  14. package/dist/src/authority/dispatch-decision.js +26 -1
  15. package/dist/src/authority/effect-requests.js +6 -3
  16. package/dist/src/authority/effect-results.js +4 -2
  17. package/dist/src/authority/history-state.d.ts +2 -0
  18. package/dist/src/authority/history-state.js +13 -1
  19. package/dist/src/authority/instants.d.ts +1 -0
  20. package/dist/src/authority/instants.js +21 -0
  21. package/dist/src/authority/reduction-state.d.ts +2 -0
  22. package/dist/src/authority/reduction-state.js +5 -0
  23. package/dist/src/authority/reduction.js +24 -0
  24. package/dist/src/authority/status-input.d.ts +2 -0
  25. package/dist/src/authority/status.d.ts +2 -0
  26. package/dist/src/authority/status.js +1 -0
  27. package/dist/src/authority/stop-sequence.d.ts +1 -0
  28. package/dist/src/authority/stop-sequence.js +2 -1
  29. package/dist/src/authority/transitions.d.ts +6 -4
  30. package/dist/src/authority/transitions.js +7 -0
  31. package/dist/src/broker/actor/authority-queries.d.ts +2 -1
  32. package/dist/src/broker/actor/authority-queries.js +14 -5
  33. package/dist/src/broker/actor/authority-receipts.js +1 -1
  34. package/dist/src/broker/actor/authority-settings.js +2 -2
  35. package/dist/src/broker/actor/authority-upgrades.d.ts +19 -2
  36. package/dist/src/broker/actor/authority-upgrades.js +7 -2
  37. package/dist/src/broker/actor/delivery-intents.js +2 -0
  38. package/dist/src/broker/actor/effect-delivery.d.ts +1 -1
  39. package/dist/src/broker/actor/effect-delivery.js +15 -2
  40. package/dist/src/broker/actor/effect-plan.js +11 -2
  41. package/dist/src/broker/actor/effect-queries.d.ts +4 -0
  42. package/dist/src/broker/actor/effect-queries.js +9 -1
  43. package/dist/src/broker/actor/effect-turn.js +2 -0
  44. package/dist/src/broker/actor/stop-plan.js +9 -3
  45. package/dist/src/broker/actor/stop-turn.d.ts +2 -0
  46. package/dist/src/broker/actor/stop-turn.js +24 -11
  47. package/dist/src/broker/actor/turn-draft.d.ts +1 -0
  48. package/dist/src/broker/actor/turn-draft.js +4 -0
  49. package/dist/src/broker/actor/turn-state.js +5 -1
  50. package/dist/src/broker/authority-admission.js +60 -0
  51. package/dist/src/broker/authority-restore.d.ts +2 -0
  52. package/dist/src/broker/authority-restore.js +35 -1
  53. package/dist/src/broker/command-wait-restore.d.ts +31 -0
  54. package/dist/src/broker/command-wait-restore.js +86 -0
  55. package/dist/src/broker/decision-restore.d.ts +1 -1
  56. package/dist/src/broker/decision-restore.js +1 -0
  57. package/dist/src/broker/effect-upgrade.d.ts +2 -0
  58. package/dist/src/broker/effect-upgrade.js +8 -2
  59. package/dist/src/cli/arguments.d.ts +13 -0
  60. package/dist/src/cli/arguments.js +42 -3
  61. package/dist/src/cli/chat-stores.d.ts +2 -0
  62. package/dist/src/cli/chat-stores.js +113 -0
  63. package/dist/src/cli/client.d.ts +4 -1
  64. package/dist/src/cli/client.js +9 -3
  65. package/dist/src/cli/command-requests.d.ts +17 -0
  66. package/dist/src/cli/command-requests.js +43 -0
  67. package/dist/src/cli/command-waits.d.ts +9 -0
  68. package/dist/src/cli/command-waits.js +51 -0
  69. package/dist/src/cli/commands.js +14 -41
  70. package/dist/src/cli/config-file.d.ts +2 -2
  71. package/dist/src/cli/config-file.js +4 -2
  72. package/dist/src/cli/doctor-workflows.d.ts +5 -2
  73. package/dist/src/cli/doctor-workflows.js +12 -7
  74. package/dist/src/cli/doctor.js +6 -9
  75. package/dist/src/cli/harness-probes.d.ts +2 -0
  76. package/dist/src/cli/harness-probes.js +1 -1
  77. package/dist/src/cli/harness-processes.d.ts +6 -0
  78. package/dist/src/cli/harness-processes.js +36 -0
  79. package/dist/src/cli/init-command.d.ts +9 -0
  80. package/dist/src/cli/init-command.js +52 -0
  81. package/dist/src/cli/init-prompt-command.d.ts +10 -0
  82. package/dist/src/cli/init-prompt-command.js +145 -0
  83. package/dist/src/cli/init-prompt.d.ts +35 -0
  84. package/dist/src/cli/init-prompt.js +74 -0
  85. package/dist/src/cli/init-selection.d.ts +13 -0
  86. package/dist/src/cli/init-selection.js +75 -0
  87. package/dist/src/cli/main.d.ts +2 -0
  88. package/dist/src/cli/main.js +37 -7
  89. package/dist/src/cli/mcp-server.d.ts +1 -0
  90. package/dist/src/cli/mcp-server.js +156 -0
  91. package/dist/src/cli/mcp-tools.d.ts +15 -0
  92. package/dist/src/cli/mcp-tools.js +80 -0
  93. package/dist/src/cli/render.d.ts +1 -1
  94. package/dist/src/cli/render.js +17 -30
  95. package/dist/src/cli/report-ci-jobs.d.ts +23 -0
  96. package/dist/src/cli/report-ci-jobs.js +103 -0
  97. package/dist/src/cli/report-ci-usage.d.ts +9 -0
  98. package/dist/src/cli/report-ci-usage.js +185 -0
  99. package/dist/src/cli/report-ci.d.ts +13 -0
  100. package/dist/src/cli/report-ci.js +74 -0
  101. package/dist/src/cli/report-closures.d.ts +6 -0
  102. package/dist/src/cli/report-closures.js +97 -0
  103. package/dist/src/cli/report-flow.d.ts +7 -0
  104. package/dist/src/cli/report-flow.js +122 -0
  105. package/dist/src/cli/report-github-reader.d.ts +29 -0
  106. package/dist/src/cli/report-github-reader.js +157 -0
  107. package/dist/src/cli/report-github.d.ts +15 -8
  108. package/dist/src/cli/report-github.js +131 -150
  109. package/dist/src/cli/report-operator.d.ts +3 -0
  110. package/dist/src/cli/report-operator.js +40 -0
  111. package/dist/src/cli/report-ready.d.ts +3 -0
  112. package/dist/src/cli/report-ready.js +96 -0
  113. package/dist/src/cli/report-text.d.ts +9 -0
  114. package/dist/src/cli/report-text.js +33 -0
  115. package/dist/src/cli/report.d.ts +1 -0
  116. package/dist/src/cli/report.js +104 -27
  117. package/dist/src/cli/runtime-arguments.d.ts +2 -1
  118. package/dist/src/cli/runtime-arguments.js +7 -2
  119. package/dist/src/cli/schedule-arguments.d.ts +14 -0
  120. package/dist/src/cli/schedule-arguments.js +26 -0
  121. package/dist/src/cli/schedule-render.d.ts +22 -0
  122. package/dist/src/cli/schedule-render.js +73 -0
  123. package/dist/src/cli/terminal-text.d.ts +3 -0
  124. package/dist/src/cli/terminal-text.js +28 -0
  125. package/dist/src/cli/usage-render.d.ts +1 -0
  126. package/dist/src/cli/usage-render.js +28 -1
  127. package/dist/src/cli/usage.d.ts +1 -1
  128. package/dist/src/cli/usage.js +50 -7
  129. package/dist/src/cli/watch-actions.js +2 -2
  130. package/dist/src/cli/watch-board-view.js +19 -2
  131. package/dist/src/cli/watch-board.d.ts +4 -2
  132. package/dist/src/cli/watch-board.js +53 -43
  133. package/dist/src/cli/watch-card-forms.d.ts +1 -1
  134. package/dist/src/cli/watch-card-forms.js +11 -10
  135. package/dist/src/cli/watch-client.d.ts +1 -1
  136. package/dist/src/cli/watch-client.js +2 -11
  137. package/dist/src/cli/watch-header-view.d.ts +0 -5
  138. package/dist/src/cli/watch-header-view.js +8 -20
  139. package/dist/src/cli/watch-model.d.ts +4 -1
  140. package/dist/src/cli/watch-model.js +17 -11
  141. package/dist/src/cli/watch-page.js +12 -14
  142. package/dist/src/cli/watch-phases.d.ts +12 -3
  143. package/dist/src/cli/watch-phases.js +68 -11
  144. package/dist/src/cli/watch-pr-board.js +1 -1
  145. package/dist/src/cli/watch-pr-command.d.ts +1 -0
  146. package/dist/src/cli/watch-pull-request.js +18 -8
  147. package/dist/src/cli/watch-reads.d.ts +2 -0
  148. package/dist/src/cli/watch-reads.js +17 -16
  149. package/dist/src/cli/watch-render.d.ts +14 -0
  150. package/dist/src/cli/watch-run-input.d.ts +0 -1
  151. package/dist/src/cli/watch-run-input.js +0 -13
  152. package/dist/src/cli/watch-server.js +6 -6
  153. package/dist/src/cli/watch-view.d.ts +0 -3
  154. package/dist/src/cli/watch-view.js +13 -13
  155. package/dist/src/cli.js +59 -5
  156. package/dist/src/command-wait.d.ts +4 -0
  157. package/dist/src/command-wait.js +45 -0
  158. package/dist/src/configuration-change.d.ts +7 -0
  159. package/dist/src/configuration-change.js +14 -0
  160. package/dist/src/configuration-schema.d.ts +9 -0
  161. package/dist/src/configuration-schema.js +6 -2
  162. package/dist/src/daemon/attachment-file.d.ts +6 -0
  163. package/dist/src/daemon/attachment-file.js +86 -0
  164. package/dist/src/daemon/attachments.d.ts +14 -0
  165. package/dist/src/daemon/attachments.js +17 -0
  166. package/dist/src/daemon/claude-recovery.d.ts +50 -0
  167. package/dist/src/daemon/claude-recovery.js +204 -0
  168. package/dist/src/daemon/claude-stream.d.ts +27 -0
  169. package/dist/src/daemon/claude-stream.js +63 -0
  170. package/dist/src/daemon/claude-turn.d.ts +11 -25
  171. package/dist/src/daemon/claude-turn.js +64 -81
  172. package/dist/src/daemon/claude-worker.d.ts +2 -1
  173. package/dist/src/daemon/claude-worker.js +124 -122
  174. package/dist/src/daemon/daemon-adapters.d.ts +2 -0
  175. package/dist/src/daemon/daemon-adapters.js +26 -2
  176. package/dist/src/daemon/daemon-loops.d.ts +3 -1
  177. package/dist/src/daemon/daemon-loops.js +3 -1
  178. package/dist/src/daemon/daemon.js +14 -6
  179. package/dist/src/daemon/github-attach.d.ts +2 -0
  180. package/dist/src/daemon/github-attach.js +149 -0
  181. package/dist/src/daemon/github-ci-filters.d.ts +6 -0
  182. package/dist/src/daemon/github-ci-filters.js +138 -0
  183. package/dist/src/daemon/github-ci-workflows.d.ts +10 -0
  184. package/dist/src/daemon/github-ci-workflows.js +148 -0
  185. package/dist/src/daemon/github-ci.js +22 -4
  186. package/dist/src/daemon/github-issue-methods.d.ts +2 -0
  187. package/dist/src/daemon/github-issue-methods.js +37 -2
  188. package/dist/src/daemon/github-merge-call.d.ts +2 -1
  189. package/dist/src/daemon/github-merge-call.js +10 -3
  190. package/dist/src/daemon/github-merge.js +52 -24
  191. package/dist/src/daemon/github-methods.js +12 -2
  192. package/dist/src/daemon/github-operation.d.ts +4 -0
  193. package/dist/src/daemon/github-operation.js +6 -0
  194. package/dist/src/daemon/github-worker.d.ts +4 -0
  195. package/dist/src/daemon/github-worker.js +6 -0
  196. package/dist/src/daemon/issue-queue.d.ts +1 -1
  197. package/dist/src/daemon/issue-queue.js +16 -11
  198. package/dist/src/daemon/notify-events.d.ts +6 -0
  199. package/dist/src/daemon/notify-events.js +84 -0
  200. package/dist/src/daemon/notify-journal.js +1 -1
  201. package/dist/src/daemon/notify-message.d.ts +4 -1
  202. package/dist/src/daemon/notify-message.js +36 -0
  203. package/dist/src/daemon/notify.d.ts +1 -1
  204. package/dist/src/daemon/notify.js +5 -77
  205. package/dist/src/daemon/operation-journal.d.ts +1 -0
  206. package/dist/src/daemon/queue-status.d.ts +9 -3
  207. package/dist/src/daemon/queue-status.js +17 -4
  208. package/dist/src/daemon/resume-index-lock.d.ts +13 -0
  209. package/dist/src/daemon/resume-index-lock.js +160 -0
  210. package/dist/src/daemon/resume-worktree.d.ts +7 -0
  211. package/dist/src/daemon/resume-worktree.js +29 -0
  212. package/dist/src/daemon/retrying-turn.d.ts +2 -12
  213. package/dist/src/daemon/retrying-turn.js +98 -172
  214. package/dist/src/daemon/schedule-control.d.ts +26 -0
  215. package/dist/src/daemon/schedule-control.js +110 -0
  216. package/dist/src/daemon/schedule-journal.d.ts +2 -15
  217. package/dist/src/daemon/schedule-journal.js +67 -4
  218. package/dist/src/daemon/ticket-registration.d.ts +2 -7
  219. package/dist/src/daemon/ticket-registration.js +15 -81
  220. package/dist/src/daemon/turn-attempt-journal.d.ts +38 -0
  221. package/dist/src/daemon/turn-attempt-journal.js +136 -0
  222. package/dist/src/daemon/turn-attempts.d.ts +47 -0
  223. package/dist/src/daemon/turn-attempts.js +166 -0
  224. package/dist/src/daemon/turn-report.d.ts +8 -1
  225. package/dist/src/daemon/turn-report.js +39 -0
  226. package/dist/src/daemon/work-tree.d.ts +9 -0
  227. package/dist/src/daemon/work-tree.js +20 -0
  228. package/dist/src/daemon/worker-profile.d.ts +8 -0
  229. package/dist/src/daemon/worker-profile.js +40 -0
  230. package/dist/src/daemon/workflow-schedules.d.ts +3 -0
  231. package/dist/src/daemon/workflow-schedules.js +98 -45
  232. package/dist/src/daemon/worktree-processes.d.ts +9 -0
  233. package/dist/src/daemon/worktree-processes.js +151 -0
  234. package/dist/src/doctor-report.d.ts +3 -5
  235. package/dist/src/doctor-report.js +6 -13
  236. package/dist/src/harness-start.d.ts +15 -0
  237. package/dist/src/harness-start.js +37 -0
  238. package/dist/src/host/command-wait.d.ts +38 -0
  239. package/dist/src/host/command-wait.js +115 -0
  240. package/dist/src/host/start-delivery.d.ts +5 -7
  241. package/dist/src/host/start-delivery.js +3 -15
  242. package/dist/src/host/step-bridge-host.d.ts +2 -0
  243. package/dist/src/host/step-bridge-host.js +21 -0
  244. package/dist/src/operation-identity.d.ts +1 -0
  245. package/dist/src/operation-identity.js +4 -0
  246. package/dist/src/private-config.schema.json +40 -0
  247. package/dist/src/project-config.schema.json +40 -0
  248. package/dist/src/project-settings.d.ts +2 -2
  249. package/dist/src/project-settings.js +3 -1
  250. package/dist/src/queue-spend.d.ts +4 -0
  251. package/dist/src/queue-spend.js +21 -0
  252. package/dist/src/relay/relay-service.d.ts +3 -0
  253. package/dist/src/relay/relay-service.js +5 -3
  254. package/dist/src/report-ci.d.ts +164 -0
  255. package/dist/src/report-ci.js +32 -0
  256. package/dist/src/report-effects.d.ts +15 -0
  257. package/dist/src/report-effects.js +212 -0
  258. package/dist/src/report-flow-types.d.ts +38 -0
  259. package/dist/src/report-flow-types.js +7 -0
  260. package/dist/src/report-flow.d.ts +43 -0
  261. package/dist/src/report-flow.js +193 -0
  262. package/dist/src/report-operator.d.ts +67 -0
  263. package/dist/src/report-operator.js +25 -0
  264. package/dist/src/report-quality.d.ts +221 -0
  265. package/dist/src/report-quality.js +94 -0
  266. package/dist/src/report-ready.d.ts +23 -0
  267. package/dist/src/report-ready.js +117 -0
  268. package/dist/src/report-statistics.d.ts +2 -1
  269. package/dist/src/report-statistics.js +40 -4
  270. package/dist/src/report.d.ts +588 -47
  271. package/dist/src/report.js +156 -41
  272. package/dist/src/schedule-progress.d.ts +33 -0
  273. package/dist/src/schedule-progress.js +45 -0
  274. package/dist/src/service/active-tickets.js +6 -6
  275. package/dist/src/service/command-waits.d.ts +34 -0
  276. package/dist/src/service/command-waits.js +86 -0
  277. package/dist/src/service/expiry.js +2 -0
  278. package/dist/src/service/gateway.js +3 -1
  279. package/dist/src/service/queue-spend.d.ts +6 -0
  280. package/dist/src/service/queue-spend.js +68 -0
  281. package/dist/src/service/replies.d.ts +8 -1
  282. package/dist/src/service/replies.js +11 -0
  283. package/dist/src/service/run-handlers.js +22 -2
  284. package/dist/src/service/run-metadata.d.ts +15 -1
  285. package/dist/src/service/run-metadata.js +93 -8
  286. package/dist/src/service/service.d.ts +4 -0
  287. package/dist/src/service/service.js +8 -8
  288. package/dist/src/service/submission-handler.js +18 -4
  289. package/dist/src/service/worker-port.js +9 -1
  290. package/dist/src/service/worker-totals.d.ts +2 -0
  291. package/dist/src/service/worker-totals.js +1 -1
  292. package/dist/src/step-bridge.d.ts +20 -0
  293. package/dist/src/steps/command-wait.d.ts +12 -0
  294. package/dist/src/steps/command-wait.js +26 -0
  295. package/dist/src/steps/open-decision-wait.d.ts +2 -0
  296. package/dist/src/steps/{decision-gate.js → open-decision-wait.js} +0 -7
  297. package/dist/src/steps/review-by-person.js +4 -1
  298. package/dist/src/steps/wait-for-command.d.ts +3 -0
  299. package/dist/src/steps/wait-for-command.js +17 -0
  300. package/dist/src/steps/wait-for-decision.js +1 -1
  301. package/dist/src/storage/command-wait-rows.d.ts +30 -0
  302. package/dist/src/storage/command-wait-rows.js +96 -0
  303. package/dist/src/storage/decision-snapshots.d.ts +8 -1
  304. package/dist/src/storage/decision-snapshots.js +15 -1
  305. package/dist/src/storage/domain-changes.d.ts +2 -0
  306. package/dist/src/storage/domain-changes.js +3 -0
  307. package/dist/src/storage/domain-schema.d.ts +13 -1
  308. package/dist/src/storage/domain-schema.js +31 -4
  309. package/dist/src/storage/effect-schema.d.ts +1 -0
  310. package/dist/src/storage/effect-schema.js +5 -0
  311. package/dist/src/testing-requests.d.ts +15 -0
  312. package/dist/src/testing-requests.js +93 -0
  313. package/dist/src/testing.d.ts +4 -10
  314. package/dist/src/testing.js +33 -86
  315. package/dist/src/worker-usage.d.ts +1 -0
  316. package/dist/src/worker-usage.js +3 -0
  317. package/dist/src/workflow-candidate.d.ts +1 -0
  318. package/dist/src/workflow-candidate.js +12 -1
  319. package/dist/src/workflow-contracts.d.ts +1 -0
  320. package/dist/src/workflow-contracts.js +9 -2
  321. package/dist/src/workflow-discovery.d.ts +2 -13
  322. package/dist/src/workflow-discovery.js +1 -13
  323. package/dist/src/workflow-refusals.js +0 -1
  324. package/dist/src/workflows.d.ts +1 -1
  325. package/dist/src/workflows.js +1 -1
  326. package/dist/tools/red-green-command.d.ts +1 -0
  327. package/dist/tools/red-green-command.js +174 -0
  328. package/dist/tools/red-green-process.d.ts +10 -0
  329. package/dist/tools/red-green-process.js +59 -0
  330. package/dist/tools/self-host-processes.d.ts +2 -0
  331. package/dist/tools/self-host-processes.js +32 -13
  332. package/dist/tools/self-host-project.js +13 -78
  333. package/dist/tools/self-host-runtime.js +1 -1
  334. package/dist/tools/self-host.js +42 -10
  335. package/dist/tools/template-command.d.ts +1 -0
  336. package/dist/tools/template-command.js +160 -0
  337. package/dist/tools/template-copy.d.ts +11 -0
  338. package/dist/tools/template-copy.js +130 -0
  339. package/dist/tools/template-files.d.ts +17 -0
  340. package/dist/tools/template-files.js +74 -0
  341. package/dist/tools/workflow-bundles.js +1 -0
  342. package/dist/tools/workflow-candidate-counts.js +2 -2
  343. package/dist/tools/workflow-candidate-shape.d.ts +3 -0
  344. package/dist/tools/workflow-candidate-shape.js +90 -0
  345. package/dist/tools/workflow-candidate-source.js +15 -1
  346. package/dist/tools/workflow-candidate-syntax.d.ts +2 -2
  347. package/dist/tools/workflow-candidate-syntax.js +24 -94
  348. package/dist/tools/workflow-command.js +1 -2
  349. package/dist/tools/workflow-folders.d.ts +2 -3
  350. package/dist/tools/workflow-folders.js +1 -1
  351. package/package.json +16 -8
  352. package/templates/develop/README.md +12 -0
  353. package/templates/develop/develop/flow-tests.ts +37 -0
  354. package/templates/develop/develop/pull.ts +5 -1
  355. package/templates/develop/develop/refusal-tests.ts +77 -0
  356. package/templates/develop/develop/turn.ts +2 -1
  357. package/templates/idea-to-issue/README.md +27 -0
  358. package/templates/idea-to-issue/idea-to-issue/effects.ts +52 -0
  359. package/templates/idea-to-issue/idea-to-issue/leave.ts +10 -0
  360. package/templates/idea-to-issue/idea-to-issue/next.ts +29 -0
  361. package/templates/idea-to-issue/idea-to-issue/post-person.ts +18 -0
  362. package/templates/idea-to-issue/idea-to-issue/post.ts +50 -0
  363. package/templates/idea-to-issue/idea-to-issue/queue.ts +27 -0
  364. package/templates/idea-to-issue/idea-to-issue/review.ts +42 -0
  365. package/templates/idea-to-issue/idea-to-issue/scenarios.ts +50 -0
  366. package/templates/idea-to-issue/idea-to-issue/turn.ts +40 -0
  367. package/templates/idea-to-issue/idea-to-issue.declaration.json +16 -0
  368. package/templates/idea-to-issue/idea-to-issue.test.ts +79 -0
  369. package/templates/idea-to-issue/idea-to-issue.ts +49 -0
  370. package/templates/idea-to-issue/template.json +81 -0
  371. package/templates/review-day/README.md +97 -0
  372. package/templates/review-day/review-day/critique.ts +58 -0
  373. package/templates/review-day/review-day/decision.ts +29 -0
  374. package/templates/review-day/review-day/facts.ts +69 -0
  375. package/templates/review-day/review-day/label.ts +29 -0
  376. package/templates/review-day/review-day/next.ts +28 -0
  377. package/templates/review-day/review-day/post.ts +27 -0
  378. package/templates/review-day/review-day/report.ts +98 -0
  379. package/templates/review-day/review-day/rules.ts +12 -0
  380. package/templates/review-day/review-day/scenarios.ts +76 -0
  381. package/templates/review-day/review-day/turn.ts +73 -0
  382. package/templates/review-day/review-day.declaration.json +16 -0
  383. package/templates/review-day/review-day.test.ts +362 -0
  384. package/templates/review-day/review-day.ts +37 -0
  385. package/templates/review-day/template.json +56 -0
  386. package/dist/src/cli/watch-start-view.d.ts +0 -18
  387. package/dist/src/cli/watch-start-view.js +0 -77
  388. package/dist/src/steps/decision-gate.d.ts +0 -3
  389. package/dist/workflows/develop-prd.d.ts +0 -7
  390. package/dist/workflows/develop-prd.js +0 -165
  391. package/dist/workflows/develop-ticket.d.ts +0 -9
  392. package/dist/workflows/develop-ticket.js +0 -207
  393. package/dist/workflows/issue-labels.d.ts +0 -16
  394. package/dist/workflows/issue-labels.js +0 -70
  395. package/dist/workflows/ticket-prompts.d.ts +0 -35
  396. package/dist/workflows/ticket-prompts.js +0 -147
@@ -11,23 +11,36 @@ function attemptOf(row) {
11
11
  const closed = "NOT EXISTS (SELECT 1 FROM deliveries x WHERE x.operation_id = o.operation_id AND x.outcome IS NULL)";
12
12
  const reported = "EXISTS (SELECT 1 FROM stopped_operations s WHERE s.operation_id = o.operation_id)";
13
13
  const finalStatuses = "'refused', 'completed', 'failed', 'stopped', 'cancelled'";
14
+ const waitStopSequence = "(SELECT min(e.sequence) FROM command_waits w JOIN events e ON e.run_id = w.run_id WHERE w.check_operation = o.operation_id AND w.state != 'waiting' AND (e.type IN ('run.completed', 'run.failed', 'run.cancelled') OR (json_extract(e.payload, '$.waitName') = w.name AND (e.type = 'command-wait.expired' OR (e.type = 'command-wait.opened' AND round(unixepoch(e.recorded_at, 'subsec') * 1000) >= w.deadline)))))";
14
15
  export async function stopping(connection, runId) {
15
- const rows = await connection.all(`SELECT o.operation_id, o.kind, d.effect_id, coalesce(a.purpose, 'dispatch') AS purpose, d.outcome, d.staged_sequence, p.status, p.receipt, ${reported} AS stopped FROM outbox o JOIN deliveries d ON d.operation_id = o.operation_id LEFT JOIN dispatch_attempts a ON a.effect_id = d.effect_id LEFT JOIN operations p ON p.operation_id = o.operation_id WHERE o.run_id = ? AND ((coalesce(p.status, '') NOT IN (${finalStatuses}) AND NOT ${reported}) OR NOT ${closed}) ORDER BY d.staged_sequence, d.rowid`, [runId]);
16
+ const rows = await connection.all(`SELECT o.operation_id, o.kind, d.effect_id, coalesce(a.purpose, 'dispatch') AS purpose, d.outcome, d.staged_sequence, p.status, p.receipt, ${reported} AS stopped, ${waitStopSequence} AS stop_sequence FROM outbox o JOIN deliveries d ON d.operation_id = o.operation_id LEFT JOIN dispatch_attempts a ON a.effect_id = d.effect_id LEFT JOIN operations p ON p.operation_id = o.operation_id WHERE o.run_id = ? AND ((coalesce(p.status, '') NOT IN (${finalStatuses}) AND NOT ${reported}) OR NOT ${closed}) ORDER BY d.staged_sequence, d.rowid`, [runId]);
16
17
  const grouped = Map.groupBy(rows, (row) => text(row, "operation_id"));
17
18
  return {
18
- subjects: [...grouped].map(([operationId, attempts]) => ({
19
- operationId,
20
- kind: text(attempts[0], "kind"),
21
- attempts: attempts.map(attemptOf),
22
- status: optionalText(attempts[0], "status"),
23
- receipt: optionalText(attempts[0], "receipt"),
24
- stopped: integer(attempts[0], "stopped") === 1,
25
- })),
19
+ subjects: [...grouped].map(([operationId, attempts]) => {
20
+ const stopSequence = optionalInteger(attempts[0], "stop_sequence");
21
+ return {
22
+ operationId,
23
+ kind: text(attempts[0], "kind"),
24
+ attempts: attempts.map(attemptOf),
25
+ status: optionalText(attempts[0], "status"),
26
+ receipt: optionalText(attempts[0], "receipt"),
27
+ stopped: integer(attempts[0], "stopped") === 1,
28
+ ...(stopSequence === null ? {} : { stopSequence }),
29
+ };
30
+ }),
26
31
  cancelledSequence: optionalInteger(await connection.get("SELECT max(sequence) AS sequence FROM events WHERE run_id = ? AND type = 'run.cancelled'", [runId]), "sequence"),
27
32
  };
28
33
  }
29
34
  export function turnStopping(connection, scope) {
30
- const dropping = scope.status === "cancelled" || scope.kind === "cancel-run";
35
+ const endingWait = Object.values(scope.commandWaits ?? {}).some((wait) => wait.checkOperation !== null &&
36
+ (wait.state !== "waiting" ||
37
+ [
38
+ "expire-command-wait",
39
+ "open-command-wait",
40
+ "complete-run",
41
+ "fail-run",
42
+ ].includes(scope.kind)));
43
+ const dropping = scope.status === "cancelled" || scope.kind === "cancel-run" || endingWait;
31
44
  return dropping
32
45
  ? stopping(connection, scope.runId)
33
46
  : Promise.resolve(noStopping);
@@ -37,6 +50,6 @@ const last = "(SELECT d.outcome FROM deliveries d WHERE d.operation_id = o.opera
37
50
  const effectDue = `o.kind = 'protected-effect' AND p.status NOT IN (${finalStatuses}) AND (p.receipt IS NULL OR ?1 = 1 OR p.status IN ('recovering', 'blocked'))`;
38
51
  const runDue = `o.kind != 'protected-effect' AND NOT ${reported} AND ((${sent} AND ?1 = 1) OR (NOT ${sent} AND ${last} NOT IN ('withheld', 'blocked', 'absent')))`;
39
52
  export async function droppedRuns(connection, rule) {
40
- const rows = await connection.all(`SELECT r.run_id FROM runs r WHERE r.status = 'cancelled' AND EXISTS (SELECT 1 FROM outbox o LEFT JOIN operations p ON p.operation_id = o.operation_id WHERE o.run_id = r.run_id AND ${closed} AND ((${effectDue}) OR (${runDue}))) ORDER BY r.run_id LIMIT ?2`, [rule.stopSupported ? 1 : 0, rule.limit]);
53
+ const rows = await connection.all(`SELECT r.run_id FROM runs r WHERE EXISTS (SELECT 1 FROM outbox o LEFT JOIN operations p ON p.operation_id = o.operation_id WHERE o.run_id = r.run_id AND (r.status = 'cancelled' OR EXISTS (SELECT 1 FROM command_waits w WHERE w.run_id = r.run_id AND w.check_operation = o.operation_id AND w.state != 'waiting')) AND ${closed} AND ((${effectDue}) OR (${runDue}))) ORDER BY r.run_id LIMIT ?2`, [rule.stopSupported ? 1 : 0, rule.limit]);
41
54
  return rows.map((row) => text(row, "run_id"));
42
55
  }
@@ -11,6 +11,7 @@ export interface ChangeDraft {
11
11
  readonly released: readonly Row[];
12
12
  readonly closed: readonly Row[];
13
13
  readonly gates: readonly Row[];
14
+ readonly waits: readonly Row[];
14
15
  readonly details: readonly Row[];
15
16
  readonly documents: readonly Row[];
16
17
  readonly budgets: readonly Row[];
@@ -1,3 +1,4 @@
1
+ import { commandWaitRows } from "../command-wait-restore.js";
1
2
  import { evidenceOf } from "../../authority/operations.js";
2
3
  import { detailRecord, gateRecord } from "../decision-restore.js";
3
4
  import { effectRows } from "../effect-restore.js";
@@ -23,6 +24,7 @@ export function unchangedAt(sequence, closed) {
23
24
  released: [],
24
25
  closed,
25
26
  gates: [],
27
+ waits: [],
26
28
  details: [],
27
29
  documents: [],
28
30
  ...unchangedEffects,
@@ -78,6 +80,7 @@ export function draft(result, extra) {
78
80
  closed,
79
81
  documents,
80
82
  ...effectDraft(result, before),
83
+ waits: commandWaitRows(changedEntries(state.commandWaits ?? {}, before.commandWaits ?? {})).map(columns),
81
84
  gates: changed(state.gates, before.gates).map((gate) => columns(gateRecord(gate))),
82
85
  details: changed(state.details, before.details).map((details) => columns(detailRecord(details))),
83
86
  previous: before.sequence,
@@ -127,6 +130,7 @@ export function merged(first, later) {
127
130
  released: joined("released"),
128
131
  closed: joined("closed"),
129
132
  gates: joined("gates"),
133
+ waits: joined("waits"),
130
134
  details: joined("details"),
131
135
  documents: joined("documents"),
132
136
  budgets: joined("budgets"),
@@ -1,3 +1,4 @@
1
+ import { readCommandWaits } from "../command-wait-restore.js";
1
2
  import { parseCommand } from "../../authority/commands.js";
2
3
  import { readExecutionManifest } from "../../execution-pin.js";
3
4
  import { operationScope } from "../../operation-identity.js";
@@ -92,7 +93,7 @@ export async function recoverableIntents(connection) {
92
93
  return [...intents, ...(await uncertainEffects(connection))];
93
94
  }
94
95
  async function uncertainEffects(connection) {
95
- const rows = await connection.all("SELECT p.operation_id, p.run_id, r.workflow FROM operations p JOIN runs r ON r.run_id = p.run_id WHERE p.status IN ('recovering', 'blocked') AND r.status != 'cancelled' AND NOT EXISTS (SELECT 1 FROM deliveries d WHERE d.operation_id = p.operation_id AND d.outcome IS NULL) ORDER BY p.requested_sequence LIMIT ?", [recoveryBatch]);
96
+ const rows = await connection.all("SELECT p.operation_id, p.run_id, r.workflow FROM operations p JOIN runs r ON r.run_id = p.run_id WHERE p.status IN ('recovering', 'blocked') AND r.status != 'cancelled' AND NOT EXISTS (SELECT 1 FROM command_waits w WHERE w.check_operation = p.operation_id AND w.state != 'waiting') AND NOT EXISTS (SELECT 1 FROM deliveries d WHERE d.operation_id = p.operation_id AND d.outcome IS NULL) ORDER BY p.requested_sequence LIMIT ?", [recoveryBatch]);
96
97
  const intents = [];
97
98
  for (const row of rows)
98
99
  intents.push(await pendingEffect(connection, {
@@ -161,8 +162,10 @@ async function turnState(connection, scope) {
161
162
  sql: "SELECT operation_id, kind, run_id, created_sequence, released_sequence FROM outbox WHERE run_id = ? AND released_sequence IS NULL ORDER BY created_sequence, rowid",
162
163
  parameters: [runId],
163
164
  });
165
+ const waits = await readCommandWaits(connection, runId);
164
166
  return {
165
167
  state: {
168
+ ...waits,
166
169
  sequence,
167
170
  runs: { ...near.runs, ...(stored ? { [runId]: stored } : {}) },
168
171
  commands: recorded ? { [commandId]: recorded } : {},
@@ -178,6 +181,7 @@ async function turnState(connection, scope) {
178
181
  runId,
179
182
  status: stored?.status,
180
183
  kind: scope.kind,
184
+ ...waits,
181
185
  }),
182
186
  runId,
183
187
  created: recorded
@@ -1,3 +1,5 @@
1
+ import { commandWaitKey } from "../authority/command-waits.js";
2
+ import { documentDigest } from "./decision-restore.js";
1
3
  import { parseCommand } from "../authority/commands.js";
2
4
  import { executionCapabilities, readExecutionManifest, resumePinned, } from "../execution-pin.js";
3
5
  import { effectInputDigest } from "./effect-restore.js";
@@ -41,6 +43,61 @@ const commandEffect = (command, context) => {
41
43
  return { kind: "rejected", reason: "command-not-declared" };
42
44
  return unpinned(command, context);
43
45
  };
46
+ function waitMatches(command, wait) {
47
+ return (command.target === wait.command &&
48
+ command.maximumWaitSeconds === wait.maximumWaitSeconds &&
49
+ command.pollEverySeconds === wait.pollEverySeconds);
50
+ }
51
+ const commandWait = (command, context) => {
52
+ const pin = context.state.runs[command.runId]?.workflow ?? "";
53
+ const declaration = context.definitions.get(pin);
54
+ const name = String(command.waitName);
55
+ const waits = declaration?.commandWaits ?? {};
56
+ const wait = Object.hasOwn(waits, name) ? waits[name] : undefined;
57
+ if (wait === undefined || !waitMatches(command, wait))
58
+ return { kind: "rejected", reason: "command-wait-not-declared" };
59
+ return unpinned(command, context);
60
+ };
61
+ function declaredWaitCheck(command, context) {
62
+ const pin = context.state.runs[command.runId]?.workflow ?? "";
63
+ const declarations = context.definitions.get(pin)?.commandWaits ?? {};
64
+ const name = String(command.waitName);
65
+ const declaration = Object.hasOwn(declarations, name)
66
+ ? declarations[name]
67
+ : undefined;
68
+ const wait = context.state.commandWaits?.[commandWaitKey(command.runId, name)];
69
+ if (declaration === undefined || wait === undefined)
70
+ return undefined;
71
+ return waitMatches({
72
+ ...command,
73
+ target: wait.command,
74
+ maximumWaitSeconds: wait.maximumWaitSeconds,
75
+ pollEverySeconds: wait.pollEverySeconds,
76
+ }, declaration)
77
+ ? wait
78
+ : undefined;
79
+ }
80
+ const commandWaitCheck = (command, context) => {
81
+ const wait = declaredWaitCheck(command, context);
82
+ if (wait === undefined)
83
+ return { kind: "rejected", reason: "command-wait-not-declared" };
84
+ const computed = effectInputDigest({
85
+ capability: "command",
86
+ method: `run:${wait.command}`,
87
+ target: wait.command,
88
+ payload: wait.inputDigest,
89
+ });
90
+ return command.inputDigest === computed
91
+ ? unpinned(command, context)
92
+ : { kind: "rejected", reason: "input-digest-mismatch" };
93
+ };
94
+ const commandReport = (command, context) => {
95
+ const output = command.commandOutput;
96
+ if (output !== undefined &&
97
+ (command.worker !== "command" || documentDigest(output) !== command.result))
98
+ return { kind: "rejected", reason: "command-output-mismatch" };
99
+ return unpinned(command, context);
100
+ };
44
101
  const handoffEffect = (command, context) => {
45
102
  if (command.capability !== "handoff")
46
103
  return unpinned(command, context);
@@ -67,6 +124,9 @@ const effectRequest = (command, context) => {
67
124
  };
68
125
  const rules = {
69
126
  "request-effect": effectRequest,
127
+ "check-command-wait": commandWaitCheck,
128
+ "report-effect": commandReport,
129
+ "open-command-wait": commandWait,
70
130
  "admit-run": admission,
71
131
  "decide-gate": resumption,
72
132
  "resume-run": resumption,
@@ -1,3 +1,4 @@
1
+ import type { CommandWaitRow } from "../storage/command-wait-rows.ts";
1
2
  import type { AuthorityEvent, AuthorityState, EffectIntent, RunState } from "../authority/reducer.ts";
2
3
  import type { DecisionRows, DocumentRow } from "../storage/decision-rows.ts";
3
4
  import type { EffectRows } from "../storage/effect-rows.ts";
@@ -7,6 +8,7 @@ import type { ExecutionManifest } from "../workflow-admission.ts";
7
8
  export type RestoredSnapshot = AuthoritySnapshot & Partial<DecisionRows> & {
8
9
  readonly documents?: readonly DocumentRow[];
9
10
  readonly effects?: EffectRows;
11
+ readonly waits?: readonly CommandWaitRow[];
10
12
  };
11
13
  export type Restoration = {
12
14
  readonly kind: "restored";
@@ -1,3 +1,4 @@
1
+ import { commandWaitState, commandWaitRows, commandOutputsIntact, } from "./command-wait-restore.js";
1
2
  import { isDeepStrictEqual } from "node:util";
2
3
  import { parseEvent } from "../authority/events.js";
3
4
  import { rebuildHistory } from "../authority/history.js";
@@ -51,6 +52,7 @@ export function restore(snapshot) {
51
52
  isDeepStrictEqual(Object.fromEntries(snapshot.runs.map((row) => [row.runId, row.sequence])), history.runSequences),
52
53
  ],
53
54
  ["definitions", definitionsIntact(definitions, state)],
55
+ ...waitChecks(snapshot, { state, definitions, events }),
54
56
  ...decisionChecks(snapshot, state),
55
57
  ...(snapshot.effects === undefined
56
58
  ? []
@@ -68,6 +70,21 @@ export function restore(snapshot) {
68
70
  };
69
71
  return { kind: "restored", state, events, definitions };
70
72
  }
73
+ function waitChecks(snapshot, context) {
74
+ const { state, definitions } = context;
75
+ return [
76
+ ["command-outputs", commandOutputsIntact(context.events)],
77
+ ["command-waits", commandWaitsIntact(definitions, state)],
78
+ ...(snapshot.waits === undefined
79
+ ? []
80
+ : [
81
+ [
82
+ "waits",
83
+ isDeepStrictEqual(snapshot.waits, commandWaitRows(state.commandWaits ?? {})),
84
+ ],
85
+ ]),
86
+ ];
87
+ }
71
88
  function decisionChecks(snapshot, state) {
72
89
  const { gates, details, documents } = snapshot;
73
90
  if (gates === undefined || details === undefined || documents === undefined)
@@ -95,12 +112,29 @@ function definitionsIntact(definitions, state) {
95
112
  const used = new Set(Object.values(state.runs).map((run) => run.workflow));
96
113
  return ([...definitions].every(([pin, manifest]) => workflowPin(manifest) === pin && used.has(pin)) && [...used].every((pin) => definitions.has(pin)));
97
114
  }
115
+ function commandWaitsIntact(definitions, state) {
116
+ return Object.values(state.commandWaits ?? {}).every((wait) => {
117
+ const pin = state.runs[wait.runId]?.workflow ?? "";
118
+ const waits = definitions.get(pin)?.commandWaits ?? {};
119
+ const permission = Object.hasOwn(waits, wait.name)
120
+ ? waits[wait.name]
121
+ : undefined;
122
+ return (permission !== undefined &&
123
+ permission.command === wait.command &&
124
+ permission.maximumWaitSeconds === wait.maximumWaitSeconds &&
125
+ permission.pollEverySeconds === wait.pollEverySeconds);
126
+ });
127
+ }
98
128
  const projectionParts = ["sequence", "runs", "commands", "intents"];
99
129
  function text(error) {
100
130
  return String(Reflect.get(Object(error), "message"));
101
131
  }
102
132
  export function currentState(snapshot) {
103
- return { ...projectedState(snapshot), ...decisionState(snapshot) };
133
+ return {
134
+ ...projectedState(snapshot),
135
+ ...decisionState(snapshot),
136
+ ...commandWaitState(snapshot.waits ?? []),
137
+ };
104
138
  }
105
139
  export function projectedState(snapshot) {
106
140
  return {
@@ -0,0 +1,31 @@
1
+ import type { Database } from "solid-objects";
2
+ import { type CommandWaitsState } from "../authority/command-waits.ts";
3
+ import type { AuthorityEvent } from "../authority/transitions.ts";
4
+ import type { DispatchRead } from "../authority/dispatch-decision.ts";
5
+ import type { AuthorityState } from "../authority/transitions.ts";
6
+ import { type CommandWaitRow } from "../storage/command-wait-rows.ts";
7
+ import type { SqlConnection } from "../storage/domain-rows.ts";
8
+ export declare function commandWaitRows(waits: CommandWaitsState): readonly CommandWaitRow[];
9
+ export declare function commandWaitState(rows: readonly CommandWaitRow[]): {
10
+ readonly commandWaits?: CommandWaitsState;
11
+ };
12
+ export declare function readCommandWaits(connection: SqlConnection, runId: string): Promise<{
13
+ readonly commandWaits?: CommandWaitsState;
14
+ }>;
15
+ export declare function upgradeCommandWaits(database: Database, state: AuthorityState): Promise<void>;
16
+ export declare function upgradeCommandWaitChecks(database: Database): Promise<void>;
17
+ export declare function commandWaitDeadline(waits: CommandWaitsState, operationId: string): {
18
+ readonly commandDeadline?: string;
19
+ };
20
+ export declare function dispatchCommandWait(connection: SqlConnection, check: {
21
+ readonly runId: string;
22
+ readonly workflow: string;
23
+ readonly operationId: string;
24
+ }): Promise<Pick<DispatchRead, "commandWait">>;
25
+ export declare function commandOutputsIntact(events: readonly AuthorityEvent[]): boolean;
26
+ export declare function readCommandWaitDeadline(connection: SqlConnection, scope: {
27
+ readonly runId: string;
28
+ readonly operationId: string;
29
+ }): Promise<{
30
+ readonly commandDeadline?: string;
31
+ }>;
@@ -0,0 +1,86 @@
1
+ import { commandWaitKey, } from "../authority/command-waits.js";
2
+ import { operationPhase } from "../operation-identity.js";
3
+ import { documentDigest } from "./decision-restore.js";
4
+ import { readExecutionManifest } from "../execution-pin.js";
5
+ import { text } from "../storage/domain-rows.js";
6
+ import { commandWaitStatements, loadCommandWaits, } from "../storage/command-wait-rows.js";
7
+ import { checkedVersion, waitingVersion, upgradeSchema, } from "../storage/domain-schema.js";
8
+ export function commandWaitRows(waits) {
9
+ return Object.values(waits).sort((left, right) => {
10
+ if (left.runId !== right.runId)
11
+ return left.runId < right.runId ? -1 : 1;
12
+ return left.name < right.name ? -1 : 1;
13
+ });
14
+ }
15
+ export function commandWaitState(rows) {
16
+ const waits = {};
17
+ for (const row of rows) {
18
+ const state = [
19
+ "waiting",
20
+ "timed-out",
21
+ "cancelled",
22
+ "completed",
23
+ "failed",
24
+ ];
25
+ const kind = state.find((candidate) => candidate === row.state);
26
+ if (kind === undefined)
27
+ throw new Error("invalid-command-wait-state");
28
+ waits[commandWaitKey(row.runId, row.name)] = { ...row, state: kind };
29
+ }
30
+ return rows.length === 0 ? {} : { commandWaits: waits };
31
+ }
32
+ export async function readCommandWaits(connection, runId) {
33
+ return commandWaitState(await loadCommandWaits(connection, { runId }));
34
+ }
35
+ export async function upgradeCommandWaits(database, state) {
36
+ await database.transaction(async (connection) => {
37
+ await upgradeSchema(connection, waitingVersion);
38
+ for (const statement of commandWaitStatements(commandWaitRows(state.commandWaits ?? {}), { version: "opened" }))
39
+ await connection.run(statement.sql, statement.parameters);
40
+ });
41
+ }
42
+ export async function upgradeCommandWaitChecks(database) {
43
+ await database.transaction(async (connection) => {
44
+ await upgradeSchema(connection, checkedVersion);
45
+ });
46
+ }
47
+ export function commandWaitDeadline(waits, operationId) {
48
+ const wait = Object.values(waits).find((wait) => wait.checkOperation === operationId);
49
+ return wait === undefined
50
+ ? {}
51
+ : { commandDeadline: new Date(wait.deadline).toISOString() };
52
+ }
53
+ export async function dispatchCommandWait(connection, check) {
54
+ if (operationPhase(check.operationId) !== "command-wait")
55
+ return {};
56
+ const state = await readCommandWaits(connection, check.runId);
57
+ const wait = Object.values(state.commandWaits ?? {}).find((wait) => wait.checkOperation === check.operationId);
58
+ if (wait === undefined)
59
+ return {};
60
+ const row = await connection.get("SELECT manifest FROM definitions WHERE pin = ?", [check.workflow]);
61
+ const declarations = readExecutionManifest(JSON.parse(text(row, "manifest"))).commandWaits ?? {};
62
+ const permission = Object.hasOwn(declarations, wait.name)
63
+ ? declarations[wait.name]
64
+ : undefined;
65
+ return { commandWait: { wait, declared: declaredWait(wait, permission) } };
66
+ }
67
+ function declaredWait(wait, permission) {
68
+ return (permission !== undefined &&
69
+ permission.command === wait.command &&
70
+ permission.maximumWaitSeconds === wait.maximumWaitSeconds &&
71
+ permission.pollEverySeconds === wait.pollEverySeconds);
72
+ }
73
+ export function commandOutputsIntact(events) {
74
+ return events.every((event) => {
75
+ const output = event.payload["commandOutput"];
76
+ return (output === undefined ||
77
+ (event.payload["worker"] === "command" &&
78
+ documentDigest(output) === event.payload["result"]));
79
+ });
80
+ }
81
+ export async function readCommandWaitDeadline(connection, scope) {
82
+ if (operationPhase(scope.operationId) !== "command-wait")
83
+ return {};
84
+ const state = await readCommandWaits(connection, scope.runId);
85
+ return commandWaitDeadline(state.commandWaits ?? {}, scope.operationId);
86
+ }
@@ -2,7 +2,7 @@ import type { RunDetails } from "../authority/details.ts";
2
2
  import { type GateState } from "../authority/gates.ts";
3
3
  import type { AuthorityState } from "../authority/reducer.ts";
4
4
  import type { DecisionRows, DetailRow, DocumentRow, GateRow } from "../storage/decision-rows.ts";
5
- export type DecisionState = Pick<AuthorityState, "gates" | "details" | "questions">;
5
+ export type DecisionState = Pick<AuthorityState, "gates" | "details" | "questions" | "commandWaits">;
6
6
  export declare function documentDigest(body: string): string;
7
7
  export declare function gateRecord(gate: GateState): GateRow;
8
8
  export declare function detailRecord(details: RunDetails): DetailRow;
@@ -141,6 +141,7 @@ export function decisionRows(state) {
141
141
  }
142
142
  export function referencedDigests(state) {
143
143
  return [
144
+ ...Object.values(state.commandWaits ?? {}).map((wait) => wait.inputDigest),
144
145
  ...Object.values(state.gates).flatMap((gate) => [
145
146
  gate.prompt,
146
147
  gate.outcome?.answer ?? null,
@@ -1,3 +1,5 @@
1
1
  import type { Database } from "solid-objects";
2
2
  import type { AuthorityState } from "../authority/reducer.ts";
3
3
  export declare function upgradeEffects(database: Database, state: AuthorityState): Promise<void>;
4
+ export declare function upgradeSpendingIndexes(database: Database): Promise<void>;
5
+ export declare function upgradeEventIndexes(database: Database): Promise<void>;
@@ -1,9 +1,15 @@
1
- import { currentVersion, upgradeSchema } from "../storage/domain-schema.js";
1
+ import { currentVersion, effectedVersion, spendingVersion, upgradeSchema, } from "../storage/domain-schema.js";
2
2
  import { persistEffects } from "../storage/effect-changes.js";
3
3
  import { effectRows } from "./effect-restore.js";
4
4
  export async function upgradeEffects(database, state) {
5
5
  await database.transaction(async (connection) => {
6
- await upgradeSchema(connection, currentVersion);
6
+ await upgradeSchema(connection, effectedVersion);
7
7
  await persistEffects(connection, effectRows(state.effects, []));
8
8
  });
9
9
  }
10
+ export async function upgradeSpendingIndexes(database) {
11
+ await database.transaction((connection) => upgradeSchema(connection, spendingVersion));
12
+ }
13
+ export async function upgradeEventIndexes(database) {
14
+ await database.transaction((connection) => upgradeSchema(connection, currentVersion));
15
+ }
@@ -9,6 +9,19 @@ export type Invocation = {
9
9
  readonly kind: "help";
10
10
  } | {
11
11
  readonly kind: "version";
12
+ } | {
13
+ readonly kind: "mcp";
14
+ } | {
15
+ readonly kind: "mcp-help";
16
+ } | {
17
+ readonly kind: "init-prompt";
18
+ } | {
19
+ readonly kind: "init";
20
+ readonly harness: string | undefined;
21
+ } | {
22
+ readonly kind: "init-prompt-help";
23
+ } | {
24
+ readonly kind: "init-help";
12
25
  } | {
13
26
  readonly kind: "doctor-help";
14
27
  } | {
@@ -26,6 +26,7 @@ const options = {
26
26
  follow: { type: "boolean" },
27
27
  since: { type: "string" },
28
28
  repository: { type: "string" },
29
+ harness: { type: "string" },
29
30
  };
30
31
  const textOptions = ["--prompt", "--text"];
31
32
  function knownFlag(token) {
@@ -83,8 +84,9 @@ function selectedInvocation(given) {
83
84
  const [command, ...remaining] = positionals;
84
85
  if (command === undefined)
85
86
  return topLevel(values);
86
- if (command === "doctor")
87
- return doctorInvocation(values, remaining);
87
+ const inspection = inspectionInvocation(command, { values, remaining });
88
+ if (inspection !== undefined)
89
+ return inspection;
88
90
  if (command === "workflows" && remaining[0] === "validate")
89
91
  return validation(values, remaining.slice(1));
90
92
  const outcome = runtimeInvocation({
@@ -96,6 +98,43 @@ function selectedInvocation(given) {
96
98
  return { kind: "usage-error", code: outcome, json };
97
99
  return outcome;
98
100
  }
101
+ function inspectionInvocation(command, input) {
102
+ const { values, remaining } = input;
103
+ if (command === "init") {
104
+ if (unexpected(values, ["harness", "help"]))
105
+ return { kind: "usage-error", code: "unexpected-option", json: false };
106
+ if (remaining.length > 0)
107
+ return { kind: "usage-error", code: "unexpected-argument", json: false };
108
+ return values.help
109
+ ? { kind: "init-help" }
110
+ : { kind: "init", harness: values.harness };
111
+ }
112
+ if (command === "doctor")
113
+ return doctorInvocation(values, remaining);
114
+ if (command === "mcp")
115
+ return mcpInvocation(values, remaining);
116
+ if (command === "init-prompt")
117
+ return promptInvocation(values, remaining);
118
+ return undefined;
119
+ }
120
+ function mcpInvocation(values, remaining) {
121
+ if (unexpected(values, ["help"]))
122
+ return { kind: "usage-error", code: "unexpected-option", json: false };
123
+ if (remaining.length > 0)
124
+ return { kind: "usage-error", code: "unexpected-argument", json: false };
125
+ return { kind: values.help ? "mcp-help" : "mcp" };
126
+ }
127
+ function promptInvocation(values, remaining) {
128
+ if (unexpected(values, ["help"]))
129
+ return {
130
+ kind: "usage-error",
131
+ code: "unexpected-option",
132
+ json: values.json === true,
133
+ };
134
+ if (remaining.length > 0)
135
+ return { kind: "usage-error", code: "unexpected-argument", json: false };
136
+ return { kind: values.help ? "init-prompt-help" : "init-prompt" };
137
+ }
99
138
  function doctorInvocation(values, remaining) {
100
139
  const json = values.json === true;
101
140
  if (unexpected(values, ["help", "json"]))
@@ -134,7 +173,7 @@ function knownCommand(positionals) {
134
173
  return true;
135
174
  if (command === "workflows")
136
175
  return subcommand === "validate";
137
- if (command === "doctor")
176
+ if (["doctor", "init-prompt", "init", "mcp"].includes(command))
138
177
  return true;
139
178
  return isRuntimeCommand(command);
140
179
  }
@@ -0,0 +1,2 @@
1
+ import type { ChatStore } from "./init-prompt.ts";
2
+ export declare function chatStores(home: string | undefined): Promise<readonly ChatStore[]>;
@@ -0,0 +1,113 @@
1
+ import { lstat, opendir } from "node:fs/promises";
2
+ import { isAbsolute, join } from "node:path";
3
+ const knownStores = [
4
+ {
5
+ name: "Claude Code",
6
+ folder: ".claude/projects",
7
+ format: "JSONL",
8
+ documentation: "https://code.claude.com/docs/en/claude-directory",
9
+ },
10
+ {
11
+ name: "Codex",
12
+ folder: ".codex/sessions",
13
+ format: "JSONL, including zstd-compressed .jsonl.zst (read with zstd -dc)",
14
+ documentation: "https://learn.chatgpt.com/docs/security/sdk",
15
+ },
16
+ {
17
+ name: "Gemini CLI",
18
+ folder: ".gemini",
19
+ format: "JSON conversation history and checkpoints under tmp/<project>/",
20
+ documentation: "https://geminicli.com/docs/cli/checkpointing/",
21
+ },
22
+ {
23
+ name: "opencode",
24
+ folder: ".local/share/opencode",
25
+ format: "SQLite (opencode.db); older versions use JSON files under storage/",
26
+ documentation: "https://docs.opencode.ai/docs/troubleshooting/",
27
+ },
28
+ {
29
+ name: "grok",
30
+ folder: ".grok",
31
+ format: "JSONL ACP events in sessions/<encoded-cwd>/<session-id>/updates.jsonl; raw messages in chat_history.jsonl; JSON metadata",
32
+ documentation: "https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-pager/docs/user-guide/17-sessions.md",
33
+ },
34
+ {
35
+ name: "Cursor Agent",
36
+ folder: ".cursor",
37
+ format: "JSONL under projects/<project>/agent-transcripts/ (older transcripts may be text)",
38
+ documentation: "https://forum.cursor.com/t/accessing-the-full-agent-transcript-in-cursor/157311/5",
39
+ },
40
+ ];
41
+ async function metadata(path) {
42
+ return lstat(path).catch((error) => {
43
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
44
+ return null;
45
+ throw error;
46
+ });
47
+ }
48
+ async function* storeFiles(path) {
49
+ const pending = [path];
50
+ for (let folder = pending.pop(); folder !== undefined; folder = pending.pop()) {
51
+ const directory = await opendir(folder);
52
+ for await (const entry of directory) {
53
+ const file = join(folder, entry.name);
54
+ if (entry.isDirectory())
55
+ pending.push(file);
56
+ if (!entry.isFile())
57
+ continue;
58
+ const info = await metadata(file);
59
+ if (info?.isFile())
60
+ yield info;
61
+ }
62
+ }
63
+ }
64
+ async function measure(path) {
65
+ let fileCount = 0;
66
+ let totalBytes = 0;
67
+ let oldest = null;
68
+ let newest = null;
69
+ for await (const info of storeFiles(path)) {
70
+ fileCount += 1;
71
+ totalBytes += info.size;
72
+ oldest = Math.min(oldest ?? info.mtimeMs, info.mtimeMs);
73
+ newest = Math.max(newest ?? info.mtimeMs, info.mtimeMs);
74
+ }
75
+ return {
76
+ fileCount,
77
+ totalBytes,
78
+ oldestFileDate: oldest === null ? null : new Date(oldest).toISOString(),
79
+ newestFileDate: newest === null ? null : new Date(newest).toISOString(),
80
+ };
81
+ }
82
+ async function inspectStore(path) {
83
+ try {
84
+ if (!(await metadata(path))?.isDirectory())
85
+ return null;
86
+ return await measure(path);
87
+ }
88
+ catch (error) {
89
+ return {
90
+ unavailable: error instanceof Error &&
91
+ "code" in error &&
92
+ typeof error.code === "string"
93
+ ? error.code
94
+ : "scan-failed",
95
+ fileCount: null,
96
+ totalBytes: null,
97
+ oldestFileDate: null,
98
+ newestFileDate: null,
99
+ };
100
+ }
101
+ }
102
+ export async function chatStores(home) {
103
+ if (!home || !isAbsolute(home))
104
+ return [];
105
+ const stores = [];
106
+ for (const { folder, ...store } of knownStores) {
107
+ const path = join(home, folder);
108
+ const facts = await inspectStore(path);
109
+ if (facts)
110
+ stores.push({ ...store, path, ...facts });
111
+ }
112
+ return stores;
113
+ }