factory-factory 0.4.0 → 0.4.2

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 (1333) hide show
  1. package/README.md +61 -344
  2. package/dist/client/assets/abnfDiagram-VRR7QNED-Bz06FkfB.js +1 -0
  3. package/dist/client/assets/arc--1iscGgN.js +1 -0
  4. package/dist/client/assets/architectureDiagram-ZJ3FMSHR-Ds-52hDI.js +36 -0
  5. package/dist/client/assets/blockDiagram-677ZJIJ3-DZwPPZmp.js +132 -0
  6. package/dist/client/assets/{c4Diagram-AAUBKEIU-fGcSJ5RM.js → c4Diagram-LMCZKHZV-RQch21fC.js} +1 -1
  7. package/dist/client/assets/channel-DFzHt_cl.js +1 -0
  8. package/dist/client/assets/{chunk-QZHKN3VN-D4NOzC69.js → chunk-2Q5K7J3B-Df6Q19iI.js} +1 -1
  9. package/dist/client/assets/{chunk-ND2GUHAM-BZ894oON.js → chunk-32BRIVSS-DlbaZtVC.js} +1 -1
  10. package/dist/client/assets/{chunk-FMBD7UC4-CkbLLRk5.js → chunk-5VM5RSS4-KgkMhS6k.js} +1 -1
  11. package/dist/client/assets/chunk-EX3LRPZG-DAfTWCem.js +231 -0
  12. package/dist/client/assets/{chunk-4BX2VUAB-BzGqW4v8.js → chunk-JWPE2WC7-D6Y5C5Yz.js} +1 -1
  13. package/dist/client/assets/chunk-MOJQB5TN-B_P4pqPC.js +88 -0
  14. package/dist/client/assets/chunk-RYQCIY6F-CzFboHFv.js +1 -0
  15. package/dist/client/assets/{chunk-727SXJPM-D6c3bkpP.js → chunk-V7JOEXUC-8yT3lW_D.js} +5 -5
  16. package/dist/client/assets/{chunk-2J33WTMH-nkRXvoLd.js → chunk-VR4S4FIN-B_QG1di-.js} +1 -1
  17. package/dist/client/assets/{chunk-55IACEB6-CKYhsw8v.js → chunk-XXDRQBXY-CzL4_omu.js} +1 -1
  18. package/dist/client/assets/classDiagram-OUVF2IWQ-IkvZ4qsF.js +1 -0
  19. package/dist/client/assets/classDiagram-v2-EOCWNBFH-IkvZ4qsF.js +1 -0
  20. package/dist/client/assets/{cose-bilkent-S5V4N54A-CPSnsVY6.js → cose-bilkent-JH36ORCC-DmZLPx5-.js} +1 -1
  21. package/dist/client/assets/cynefin-VYW2F7L2-DUejQU5F.js +166 -0
  22. package/dist/client/assets/cynefinDiagram-TSTJHNR4-BjallChk.js +62 -0
  23. package/dist/client/assets/cytoscape.esm-D3_iZ_3b.js +321 -0
  24. package/dist/client/assets/dagre-VKFMJZFB-COdOy8Kp.js +4 -0
  25. package/dist/client/assets/{diagram-KO2AKTUF-DQ50kQAK.js → diagram-FQU43EPY-C8Xk7Fyh.js} +3 -3
  26. package/dist/client/assets/diagram-G47NLZAW-B_qqKBqq.js +24 -0
  27. package/dist/client/assets/diagram-NH7WQ7WH-DS1-6fzl.js +24 -0
  28. package/dist/client/assets/diagram-OA4YK3LP-enaDU5V4.js +30 -0
  29. package/dist/client/assets/diagram-WEI45ONY-Cx6LFRuh.js +41 -0
  30. package/dist/client/assets/ebnfDiagram-CCIWWBDH-DVGMcsj6.js +1 -0
  31. package/dist/client/assets/erDiagram-Q63AITRT-DCsDfIB_.js +85 -0
  32. package/dist/client/assets/flowDiagram-23GEKE2U-C5QCjsF8.js +156 -0
  33. package/dist/client/assets/ganttDiagram-NO4QXBWP-CLvtSsZ-.js +292 -0
  34. package/dist/client/assets/gitGraphDiagram-IHSO6WYX-BS6vNDTo.js +106 -0
  35. package/dist/client/assets/{graph--OzhPTMs.js → graph-DOmOIIwC.js} +1 -1
  36. package/dist/client/assets/index-0-xpZu6O.js +689 -0
  37. package/dist/client/assets/index-CxLzpVmH.css +1 -0
  38. package/dist/client/assets/infoDiagram-FWYZ7A6U-CIuYQAoE.js +2 -0
  39. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-DrCgRttT.js → ishikawaDiagram-FXEZZL3T-DeVUVV8X.js} +5 -5
  40. package/dist/client/assets/{journeyDiagram-JHISSGLW-CcyKXKKG.js → journeyDiagram-5HDEW3XC-BsmmwqMS.js} +3 -3
  41. package/dist/client/assets/{kanban-definition-UN3LZRKU-CsGSN3QV.js → kanban-definition-HUTT4EX6-D-mDPzR0.js} +20 -20
  42. package/dist/client/assets/katex-HP8lGamR.js +257 -0
  43. package/dist/client/assets/layout-D-LzfAck.js +1 -0
  44. package/dist/client/assets/{linear-DG8wt3yl.js → linear-BSdMIO3_.js} +1 -1
  45. package/dist/client/assets/map-DxJ2ADlA.js +1 -0
  46. package/dist/client/assets/{mindmap-definition-RKZ34NQL-CD6E0vLi.js → mindmap-definition-LN4V7U3C-DOuSISM2.js} +3 -3
  47. package/dist/client/assets/{mobile-baseline-CCXJGaWY.js → mobile-baseline-C-1Tc5b_.js} +1 -1
  48. package/dist/client/assets/pegDiagram-2B236MQR-C8CMsmVK.js +1 -0
  49. package/dist/client/assets/pieDiagram-ENE6RG2P-B1UfUhQO.js +39 -0
  50. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-DV6ne1ch.js → quadrantDiagram-ABIIQ3AL-DujmyYtF.js} +3 -3
  51. package/dist/client/assets/railroadDiagram-RFXS5EU6-DaF-IoiS.js +1 -0
  52. package/dist/client/assets/{requirementDiagram-4Y6WPE33-Cly9IJv1.js → requirementDiagram-TGXJPOKE-dfpBVIPb.js} +1 -1
  53. package/dist/client/assets/sankeyDiagram-HTMAVEWB-BYCvwHlS.js +40 -0
  54. package/dist/client/assets/sequenceDiagram-DBY2YBRQ-DOdQoxUf.js +162 -0
  55. package/dist/client/assets/sizeCapture-X5ZJPWSS-9sUXw3Q8.js +1 -0
  56. package/dist/client/assets/stateDiagram-2N3HPSRC-QZszWUA_.js +1 -0
  57. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-CueME36n.js +1 -0
  58. package/dist/client/assets/swimlanes-5IMT3BWC-CtEVTRNN.js +2 -0
  59. package/dist/client/assets/swimlanesDiagram-G3AALYLV-DITFxuwY.js +8 -0
  60. package/dist/client/assets/{terminal-instance-DFzmxfzG.js → terminal-instance-nU4PXHMm.js} +1 -1
  61. package/dist/client/assets/{timeline-definition-PNZ67QCA-B7ZqNmvc.js → timeline-definition-FHXFAJF6-0fOfnsxU.js} +3 -3
  62. package/dist/client/assets/vennDiagram-L72KCM5P-5eWMXdkg.js +34 -0
  63. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-C9yV9o-K.js → wardleyDiagram-EHGQE667-BwNCxx2w.js} +4 -4
  64. package/dist/client/assets/xychartDiagram-FW5EYKEG-VIc8vlw6.js +7 -0
  65. package/dist/client/index.html +2 -2
  66. package/dist/client/working.png +0 -0
  67. package/dist/prisma/generated/browser.d.ts +4 -0
  68. package/dist/prisma/generated/browser.d.ts.map +1 -1
  69. package/dist/prisma/generated/client.d.ts +4 -0
  70. package/dist/prisma/generated/client.d.ts.map +1 -1
  71. package/dist/prisma/generated/commonInputTypes.d.ts +118 -148
  72. package/dist/prisma/generated/commonInputTypes.d.ts.map +1 -1
  73. package/dist/prisma/generated/enums.d.ts +5 -6
  74. package/dist/prisma/generated/enums.d.ts.map +1 -1
  75. package/dist/prisma/generated/enums.js +4 -5
  76. package/dist/prisma/generated/enums.js.map +1 -1
  77. package/dist/prisma/generated/internal/class.d.ts +16 -2
  78. package/dist/prisma/generated/internal/class.d.ts.map +1 -1
  79. package/dist/prisma/generated/internal/class.js +6 -6
  80. package/dist/prisma/generated/internal/class.js.map +1 -1
  81. package/dist/prisma/generated/internal/prismaNamespace.d.ts +372 -44
  82. package/dist/prisma/generated/internal/prismaNamespace.d.ts.map +1 -1
  83. package/dist/prisma/generated/internal/prismaNamespace.js +52 -34
  84. package/dist/prisma/generated/internal/prismaNamespace.js.map +1 -1
  85. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts +53 -31
  86. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts.map +1 -1
  87. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js +50 -32
  88. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js.map +1 -1
  89. package/dist/prisma/generated/models/UserSettings.d.ts +32 -1
  90. package/dist/prisma/generated/models/UserSettings.d.ts.map +1 -1
  91. package/dist/prisma/generated/models/Workspace.d.ts +1017 -2318
  92. package/dist/prisma/generated/models/Workspace.d.ts.map +1 -1
  93. package/dist/prisma/generated/models/WorkspaceAutoIteration.d.ts +524 -0
  94. package/dist/prisma/generated/models/WorkspaceAutoIteration.d.ts.map +1 -0
  95. package/dist/prisma/generated/models/WorkspaceAutoIteration.js +2 -0
  96. package/dist/prisma/generated/models/WorkspaceAutoIteration.js.map +1 -0
  97. package/dist/prisma/generated/models/WorkspacePR.d.ts +858 -0
  98. package/dist/prisma/generated/models/WorkspacePR.d.ts.map +1 -0
  99. package/dist/prisma/generated/models/WorkspacePR.js +2 -0
  100. package/dist/prisma/generated/models/WorkspacePR.js.map +1 -0
  101. package/dist/prisma/generated/models/WorkspaceRatchet.d.ts +593 -0
  102. package/dist/prisma/generated/models/WorkspaceRatchet.d.ts.map +1 -0
  103. package/dist/prisma/generated/models/WorkspaceRatchet.js +2 -0
  104. package/dist/prisma/generated/models/WorkspaceRatchet.js.map +1 -0
  105. package/dist/prisma/generated/models/WorkspaceRunScript.d.ts +631 -0
  106. package/dist/prisma/generated/models/WorkspaceRunScript.d.ts.map +1 -0
  107. package/dist/prisma/generated/models/WorkspaceRunScript.js +2 -0
  108. package/dist/prisma/generated/models/WorkspaceRunScript.js.map +1 -0
  109. package/dist/prisma/generated/models.d.ts +4 -0
  110. package/dist/prisma/generated/models.d.ts.map +1 -1
  111. package/dist/src/backend/app-context.d.ts +93 -25
  112. package/dist/src/backend/app-context.d.ts.map +1 -1
  113. package/dist/src/backend/app-context.js +148 -43
  114. package/dist/src/backend/app-context.js.map +1 -1
  115. package/dist/src/backend/app-context.test.d.ts +2 -0
  116. package/dist/src/backend/app-context.test.d.ts.map +1 -0
  117. package/dist/src/backend/app-context.test.js +51 -0
  118. package/dist/src/backend/app-context.test.js.map +1 -0
  119. package/dist/src/backend/clients/git.client.d.ts.map +1 -1
  120. package/dist/src/backend/clients/git.client.js +5 -0
  121. package/dist/src/backend/clients/git.client.js.map +1 -1
  122. package/dist/src/backend/clients/git.client.test.js +24 -0
  123. package/dist/src/backend/clients/git.client.test.js.map +1 -1
  124. package/dist/src/backend/index.js +5 -5
  125. package/dist/src/backend/index.js.map +1 -1
  126. package/dist/src/backend/interceptors/auto-branch-rename.d.ts +19 -0
  127. package/dist/src/backend/interceptors/auto-branch-rename.d.ts.map +1 -0
  128. package/dist/src/backend/interceptors/{pre-push-rename.interceptor.js → auto-branch-rename.js} +30 -41
  129. package/dist/src/backend/interceptors/auto-branch-rename.js.map +1 -0
  130. package/dist/src/backend/interceptors/branch-naming.interceptor.d.ts +4 -0
  131. package/dist/src/backend/interceptors/branch-naming.interceptor.d.ts.map +1 -0
  132. package/dist/src/backend/interceptors/branch-naming.interceptor.js +391 -0
  133. package/dist/src/backend/interceptors/branch-naming.interceptor.js.map +1 -0
  134. package/dist/src/backend/interceptors/branch-naming.interceptor.test.d.ts +2 -0
  135. package/dist/src/backend/interceptors/branch-naming.interceptor.test.d.ts.map +1 -0
  136. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js +780 -0
  137. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js.map +1 -0
  138. package/dist/src/backend/interceptors/branch-rename.utils.d.ts +1 -1
  139. package/dist/src/backend/interceptors/branch-rename.utils.d.ts.map +1 -1
  140. package/dist/src/backend/interceptors/branch-rename.utils.js +10 -14
  141. package/dist/src/backend/interceptors/branch-rename.utils.js.map +1 -1
  142. package/dist/src/backend/interceptors/index.d.ts.map +1 -1
  143. package/dist/src/backend/interceptors/index.js +2 -6
  144. package/dist/src/backend/interceptors/index.js.map +1 -1
  145. package/dist/src/backend/lib/application-error.d.ts +6 -0
  146. package/dist/src/backend/lib/application-error.d.ts.map +1 -0
  147. package/dist/src/backend/lib/application-error.js +9 -0
  148. package/dist/src/backend/lib/application-error.js.map +1 -0
  149. package/dist/src/backend/lib/application-error.test.d.ts +2 -0
  150. package/dist/src/backend/lib/application-error.test.d.ts.map +1 -0
  151. package/dist/src/backend/lib/application-error.test.js +23 -0
  152. package/dist/src/backend/lib/application-error.test.js.map +1 -0
  153. package/dist/src/backend/lib/git-helpers.d.ts +1 -7
  154. package/dist/src/backend/lib/git-helpers.d.ts.map +1 -1
  155. package/dist/src/backend/lib/git-helpers.js +4 -33
  156. package/dist/src/backend/lib/git-helpers.js.map +1 -1
  157. package/dist/src/backend/lib/git-helpers.test.js +17 -10
  158. package/dist/src/backend/lib/git-helpers.test.js.map +1 -1
  159. package/dist/src/backend/lib/process-liveness.d.ts +2 -0
  160. package/dist/src/backend/lib/process-liveness.d.ts.map +1 -0
  161. package/dist/src/backend/lib/process-liveness.js +26 -0
  162. package/dist/src/backend/lib/process-liveness.js.map +1 -0
  163. package/dist/src/backend/lib/process-liveness.test.d.ts +2 -0
  164. package/dist/src/backend/lib/process-liveness.test.d.ts.map +1 -0
  165. package/dist/src/backend/lib/process-liveness.test.js +35 -0
  166. package/dist/src/backend/lib/process-liveness.test.js.map +1 -0
  167. package/dist/src/backend/lib/session-summaries.d.ts +4 -5
  168. package/dist/src/backend/lib/session-summaries.d.ts.map +1 -1
  169. package/dist/src/backend/lib/session-summaries.js.map +1 -1
  170. package/dist/src/backend/lib/workspace-derived-state.d.ts +9 -1
  171. package/dist/src/backend/lib/workspace-derived-state.d.ts.map +1 -1
  172. package/dist/src/backend/lib/workspace-derived-state.js +6 -1
  173. package/dist/src/backend/lib/workspace-derived-state.js.map +1 -1
  174. package/dist/src/backend/lib/workspace-derived-state.test.js +11 -3
  175. package/dist/src/backend/lib/workspace-derived-state.test.js.map +1 -1
  176. package/dist/src/backend/middleware/cors.middleware.js +1 -1
  177. package/dist/src/backend/middleware/cors.middleware.js.map +1 -1
  178. package/dist/src/backend/middleware/index.d.ts +1 -1
  179. package/dist/src/backend/middleware/index.d.ts.map +1 -1
  180. package/dist/src/backend/middleware/index.js +1 -1
  181. package/dist/src/backend/middleware/index.js.map +1 -1
  182. package/dist/src/backend/middleware/middleware.test.js +1 -1
  183. package/dist/src/backend/middleware/middleware.test.js.map +1 -1
  184. package/dist/src/backend/middleware/request-logger.middleware.d.ts +1 -2
  185. package/dist/src/backend/middleware/request-logger.middleware.d.ts.map +1 -1
  186. package/dist/src/backend/middleware/request-logger.middleware.js +0 -2
  187. package/dist/src/backend/middleware/request-logger.middleware.js.map +1 -1
  188. package/dist/src/backend/orchestration/data-backup.service.d.ts.map +1 -1
  189. package/dist/src/backend/orchestration/data-backup.service.js +106 -54
  190. package/dist/src/backend/orchestration/data-backup.service.js.map +1 -1
  191. package/dist/src/backend/orchestration/data-backup.service.test.js +212 -44
  192. package/dist/src/backend/orchestration/data-backup.service.test.js.map +1 -1
  193. package/dist/src/backend/orchestration/domain-bridges.orchestrator.d.ts +28 -14
  194. package/dist/src/backend/orchestration/domain-bridges.orchestrator.d.ts.map +1 -1
  195. package/dist/src/backend/orchestration/domain-bridges.orchestrator.js +155 -109
  196. package/dist/src/backend/orchestration/domain-bridges.orchestrator.js.map +1 -1
  197. package/dist/src/backend/orchestration/domain-bridges.orchestrator.test.js +409 -160
  198. package/dist/src/backend/orchestration/domain-bridges.orchestrator.test.js.map +1 -1
  199. package/dist/src/backend/orchestration/event-coalescer.integration.test.d.ts +2 -0
  200. package/dist/src/backend/orchestration/event-coalescer.integration.test.d.ts.map +1 -0
  201. package/dist/src/backend/orchestration/event-coalescer.integration.test.js +47 -0
  202. package/dist/src/backend/orchestration/event-coalescer.integration.test.js.map +1 -0
  203. package/dist/src/backend/orchestration/event-collector-lifecycle.integration.test.d.ts +2 -0
  204. package/dist/src/backend/orchestration/event-collector-lifecycle.integration.test.d.ts.map +1 -0
  205. package/dist/src/backend/orchestration/event-collector-lifecycle.integration.test.js +34 -0
  206. package/dist/src/backend/orchestration/event-collector-lifecycle.integration.test.js.map +1 -0
  207. package/dist/src/backend/orchestration/event-collector.orchestrator.d.ts +37 -11
  208. package/dist/src/backend/orchestration/event-collector.orchestrator.d.ts.map +1 -1
  209. package/dist/src/backend/orchestration/event-collector.orchestrator.js +280 -127
  210. package/dist/src/backend/orchestration/event-collector.orchestrator.js.map +1 -1
  211. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js +377 -49
  212. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js.map +1 -1
  213. package/dist/src/backend/orchestration/health.service.js +2 -2
  214. package/dist/src/backend/orchestration/health.service.js.map +1 -1
  215. package/dist/src/backend/orchestration/linear-config.helper.js +2 -2
  216. package/dist/src/backend/orchestration/linear-config.helper.js.map +1 -1
  217. package/dist/src/backend/orchestration/reconciliation.service.d.ts +17 -4
  218. package/dist/src/backend/orchestration/reconciliation.service.d.ts.map +1 -1
  219. package/dist/src/backend/orchestration/reconciliation.service.js +42 -67
  220. package/dist/src/backend/orchestration/reconciliation.service.js.map +1 -1
  221. package/dist/src/backend/orchestration/reconciliation.service.test.js +18 -50
  222. package/dist/src/backend/orchestration/reconciliation.service.test.js.map +1 -1
  223. package/dist/src/backend/orchestration/scheduler.service.d.ts +6 -4
  224. package/dist/src/backend/orchestration/scheduler.service.d.ts.map +1 -1
  225. package/dist/src/backend/orchestration/scheduler.service.js +143 -83
  226. package/dist/src/backend/orchestration/scheduler.service.js.map +1 -1
  227. package/dist/src/backend/orchestration/scheduler.service.test.js +312 -63
  228. package/dist/src/backend/orchestration/scheduler.service.test.js.map +1 -1
  229. package/dist/src/backend/orchestration/snapshot-reconciliation.orchestrator.d.ts +22 -15
  230. package/dist/src/backend/orchestration/snapshot-reconciliation.orchestrator.d.ts.map +1 -1
  231. package/dist/src/backend/orchestration/snapshot-reconciliation.orchestrator.js +70 -101
  232. package/dist/src/backend/orchestration/snapshot-reconciliation.orchestrator.js.map +1 -1
  233. package/dist/src/backend/orchestration/snapshot-reconciliation.orchestrator.test.js +185 -27
  234. package/dist/src/backend/orchestration/snapshot-reconciliation.orchestrator.test.js.map +1 -1
  235. package/dist/src/backend/orchestration/types.d.ts +2 -2
  236. package/dist/src/backend/orchestration/types.d.ts.map +1 -1
  237. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts +7 -37
  238. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts.map +1 -1
  239. package/dist/src/backend/orchestration/workspace-archive.orchestrator.js +10 -12
  240. package/dist/src/backend/orchestration/workspace-archive.orchestrator.js.map +1 -1
  241. package/dist/src/backend/orchestration/workspace-archive.orchestrator.test.js +52 -21
  242. package/dist/src/backend/orchestration/workspace-archive.orchestrator.test.js.map +1 -1
  243. package/dist/src/backend/orchestration/workspace-children.orchestrator.d.ts.map +1 -1
  244. package/dist/src/backend/orchestration/workspace-children.orchestrator.js +11 -12
  245. package/dist/src/backend/orchestration/workspace-children.orchestrator.js.map +1 -1
  246. package/dist/src/backend/orchestration/workspace-children.orchestrator.test.js +41 -21
  247. package/dist/src/backend/orchestration/workspace-children.orchestrator.test.js.map +1 -1
  248. package/dist/src/backend/orchestration/workspace-init-issue-prompts.js +3 -3
  249. package/dist/src/backend/orchestration/workspace-init-issue-prompts.js.map +1 -1
  250. package/dist/src/backend/orchestration/workspace-init-script-pipeline.d.ts +1 -1
  251. package/dist/src/backend/orchestration/workspace-init-script-pipeline.d.ts.map +1 -1
  252. package/dist/src/backend/orchestration/workspace-init-script-pipeline.js.map +1 -1
  253. package/dist/src/backend/orchestration/workspace-init.orchestrator.d.ts.map +1 -1
  254. package/dist/src/backend/orchestration/workspace-init.orchestrator.js +36 -22
  255. package/dist/src/backend/orchestration/workspace-init.orchestrator.js.map +1 -1
  256. package/dist/src/backend/orchestration/workspace-init.orchestrator.test.js +295 -119
  257. package/dist/src/backend/orchestration/workspace-init.orchestrator.test.js.map +1 -1
  258. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.d.ts +24 -0
  259. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.d.ts.map +1 -0
  260. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.js +73 -0
  261. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.js.map +1 -0
  262. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.test.d.ts +2 -0
  263. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.test.d.ts.map +1 -0
  264. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.test.js +258 -0
  265. package/dist/src/backend/orchestration/workspace-notification-delivery.orchestrator.test.js.map +1 -0
  266. package/dist/src/backend/routers/health.router.d.ts.map +1 -1
  267. package/dist/src/backend/routers/health.router.js +5 -5
  268. package/dist/src/backend/routers/health.router.js.map +1 -1
  269. package/dist/src/backend/routers/health.router.test.js +3 -5
  270. package/dist/src/backend/routers/health.router.test.js.map +1 -1
  271. package/dist/src/backend/routers/websocket/chat-connection-registry.d.ts +15 -6
  272. package/dist/src/backend/routers/websocket/chat-connection-registry.d.ts.map +1 -1
  273. package/dist/src/backend/routers/websocket/chat-connection-registry.js +80 -33
  274. package/dist/src/backend/routers/websocket/chat-connection-registry.js.map +1 -1
  275. package/dist/src/backend/routers/websocket/chat-connection-registry.test.js +60 -21
  276. package/dist/src/backend/routers/websocket/chat-connection-registry.test.js.map +1 -1
  277. package/dist/src/backend/routers/websocket/chat.handler.d.ts.map +1 -1
  278. package/dist/src/backend/routers/websocket/chat.handler.js +13 -8
  279. package/dist/src/backend/routers/websocket/chat.handler.js.map +1 -1
  280. package/dist/src/backend/routers/websocket/chat.handler.test.js +86 -15
  281. package/dist/src/backend/routers/websocket/chat.handler.test.js.map +1 -1
  282. package/dist/src/backend/routers/websocket/context-injection.test.js +450 -14
  283. package/dist/src/backend/routers/websocket/context-injection.test.js.map +1 -1
  284. package/dist/src/backend/routers/websocket/index.d.ts +3 -3
  285. package/dist/src/backend/routers/websocket/index.d.ts.map +1 -1
  286. package/dist/src/backend/routers/websocket/index.js +3 -3
  287. package/dist/src/backend/routers/websocket/index.js.map +1 -1
  288. package/dist/src/backend/routers/websocket/log-stream-dev.handler.test.d.ts +2 -0
  289. package/dist/src/backend/routers/websocket/log-stream-dev.handler.test.d.ts.map +1 -0
  290. package/dist/src/backend/routers/websocket/{dev-logs.handler.test.js → log-stream-dev.handler.test.js} +2 -2
  291. package/dist/src/backend/routers/websocket/log-stream-dev.handler.test.js.map +1 -0
  292. package/dist/src/backend/routers/websocket/log-stream-post-run.handler.test.d.ts +2 -0
  293. package/dist/src/backend/routers/websocket/log-stream-post-run.handler.test.d.ts.map +1 -0
  294. package/dist/src/backend/routers/websocket/{post-run-logs.handler.test.js → log-stream-post-run.handler.test.js} +2 -2
  295. package/dist/src/backend/routers/websocket/log-stream-post-run.handler.test.js.map +1 -0
  296. package/dist/src/backend/routers/websocket/log-stream.handler.d.ts +8 -0
  297. package/dist/src/backend/routers/websocket/log-stream.handler.d.ts.map +1 -0
  298. package/dist/src/backend/routers/websocket/log-stream.handler.js +35 -0
  299. package/dist/src/backend/routers/websocket/log-stream.handler.js.map +1 -0
  300. package/dist/src/backend/routers/websocket/snapshots.handler.d.ts +7 -5
  301. package/dist/src/backend/routers/websocket/snapshots.handler.d.ts.map +1 -1
  302. package/dist/src/backend/routers/websocket/snapshots.handler.js +50 -23
  303. package/dist/src/backend/routers/websocket/snapshots.handler.js.map +1 -1
  304. package/dist/src/backend/routers/websocket/snapshots.handler.test.js +128 -62
  305. package/dist/src/backend/routers/websocket/snapshots.handler.test.js.map +1 -1
  306. package/dist/src/backend/routers/websocket/terminal.handler.d.ts.map +1 -1
  307. package/dist/src/backend/routers/websocket/terminal.handler.js +39 -28
  308. package/dist/src/backend/routers/websocket/terminal.handler.js.map +1 -1
  309. package/dist/src/backend/routers/websocket/terminal.handler.test.js +72 -9
  310. package/dist/src/backend/routers/websocket/terminal.handler.test.js.map +1 -1
  311. package/dist/src/backend/routers/websocket/upgrade-utils.d.ts +4 -4
  312. package/dist/src/backend/routers/websocket/upgrade-utils.d.ts.map +1 -1
  313. package/dist/src/backend/routers/websocket/upgrade-utils.js +1 -1
  314. package/dist/src/backend/routers/websocket/upgrade-utils.js.map +1 -1
  315. package/dist/src/backend/routers/websocket/upgrade-utils.test.js +39 -2
  316. package/dist/src/backend/routers/websocket/upgrade-utils.test.js.map +1 -1
  317. package/dist/src/backend/routers/websocket/websocket.integration.test.js +53 -18
  318. package/dist/src/backend/routers/websocket/websocket.integration.test.js.map +1 -1
  319. package/dist/src/backend/server.d.ts +3 -2
  320. package/dist/src/backend/server.d.ts.map +1 -1
  321. package/dist/src/backend/server.js +48 -39
  322. package/dist/src/backend/server.js.map +1 -1
  323. package/dist/src/backend/server.upgrade.test.js +212 -143
  324. package/dist/src/backend/server.upgrade.test.js.map +1 -1
  325. package/dist/src/backend/services/auto-iteration/service/auto-iteration.service.d.ts +4 -0
  326. package/dist/src/backend/services/auto-iteration/service/auto-iteration.service.d.ts.map +1 -1
  327. package/dist/src/backend/services/auto-iteration/service/auto-iteration.service.js +66 -18
  328. package/dist/src/backend/services/auto-iteration/service/auto-iteration.service.js.map +1 -1
  329. package/dist/src/backend/services/auto-iteration/service/auto-iteration.service.test.js +154 -2
  330. package/dist/src/backend/services/auto-iteration/service/auto-iteration.service.test.js.map +1 -1
  331. package/dist/src/backend/services/auto-iteration/service/bridges.d.ts +1 -0
  332. package/dist/src/backend/services/auto-iteration/service/bridges.d.ts.map +1 -1
  333. package/dist/src/backend/services/auto-iteration/service/git-ops.d.ts.map +1 -1
  334. package/dist/src/backend/services/auto-iteration/service/git-ops.integration.test.d.ts +2 -0
  335. package/dist/src/backend/services/auto-iteration/service/git-ops.integration.test.d.ts.map +1 -0
  336. package/dist/src/backend/services/auto-iteration/service/git-ops.integration.test.js +86 -0
  337. package/dist/src/backend/services/auto-iteration/service/git-ops.integration.test.js.map +1 -0
  338. package/dist/src/backend/services/auto-iteration/service/git-ops.js +67 -11
  339. package/dist/src/backend/services/auto-iteration/service/git-ops.js.map +1 -1
  340. package/dist/src/backend/services/auto-iteration/service/git-ops.test.d.ts +2 -0
  341. package/dist/src/backend/services/auto-iteration/service/git-ops.test.d.ts.map +1 -0
  342. package/dist/src/backend/services/auto-iteration/service/git-ops.test.js +59 -0
  343. package/dist/src/backend/services/auto-iteration/service/git-ops.test.js.map +1 -0
  344. package/dist/src/backend/services/auto-iteration/service/test-runner.service.d.ts.map +1 -1
  345. package/dist/src/backend/services/auto-iteration/service/test-runner.service.js +9 -8
  346. package/dist/src/backend/services/auto-iteration/service/test-runner.service.js.map +1 -1
  347. package/dist/src/backend/services/auto-iteration/service/test-runner.service.test.d.ts +2 -0
  348. package/dist/src/backend/services/auto-iteration/service/test-runner.service.test.d.ts.map +1 -0
  349. package/dist/src/backend/services/auto-iteration/service/test-runner.service.test.js +80 -0
  350. package/dist/src/backend/services/auto-iteration/service/test-runner.service.test.js.map +1 -0
  351. package/dist/src/backend/services/check-service-accessor-boundaries.test.d.ts +2 -0
  352. package/dist/src/backend/services/check-service-accessor-boundaries.test.d.ts.map +1 -0
  353. package/dist/src/backend/services/check-service-accessor-boundaries.test.js +386 -0
  354. package/dist/src/backend/services/check-service-accessor-boundaries.test.js.map +1 -0
  355. package/dist/src/backend/services/check-single-writer.test.js +359 -14
  356. package/dist/src/backend/services/check-single-writer.test.js.map +1 -1
  357. package/dist/src/backend/services/config.service.d.ts +7 -0
  358. package/dist/src/backend/services/config.service.d.ts.map +1 -1
  359. package/dist/src/backend/services/config.service.js +9 -0
  360. package/dist/src/backend/services/config.service.js.map +1 -1
  361. package/dist/src/backend/services/config.service.test.js +41 -0
  362. package/dist/src/backend/services/config.service.test.js.map +1 -1
  363. package/dist/src/backend/services/constants.d.ts +4 -0
  364. package/dist/src/backend/services/constants.d.ts.map +1 -1
  365. package/dist/src/backend/services/constants.js +4 -0
  366. package/dist/src/backend/services/constants.js.map +1 -1
  367. package/dist/src/backend/services/decision-log/index.d.ts +1 -1
  368. package/dist/src/backend/services/decision-log/index.d.ts.map +1 -1
  369. package/dist/src/backend/services/decision-log/index.js +1 -1
  370. package/dist/src/backend/services/decision-log/index.js.map +1 -1
  371. package/dist/src/backend/services/decision-log/service/decision-log-domain-exports.test.d.ts +2 -0
  372. package/dist/src/backend/services/decision-log/service/decision-log-domain-exports.test.d.ts.map +1 -0
  373. package/dist/src/backend/services/decision-log/service/decision-log-domain-exports.test.js +8 -0
  374. package/dist/src/backend/services/decision-log/service/decision-log-domain-exports.test.js.map +1 -0
  375. package/dist/src/backend/{orchestration/decision-log-query.service.d.ts → services/decision-log/service/decision-log.service.d.ts} +4 -4
  376. package/dist/src/backend/services/decision-log/service/decision-log.service.d.ts.map +1 -0
  377. package/dist/src/backend/{orchestration/decision-log-query.service.js → services/decision-log/service/decision-log.service.js} +4 -4
  378. package/dist/src/backend/services/decision-log/service/decision-log.service.js.map +1 -0
  379. package/dist/src/backend/services/decision-log/service/decision-log.service.test.d.ts +2 -0
  380. package/dist/src/backend/services/decision-log/service/decision-log.service.test.d.ts.map +1 -0
  381. package/dist/src/backend/services/decision-log/service/decision-log.service.test.js +45 -0
  382. package/dist/src/backend/services/decision-log/service/decision-log.service.test.js.map +1 -0
  383. package/dist/src/backend/services/decision-log/service/index.d.ts +2 -0
  384. package/dist/src/backend/services/decision-log/service/index.d.ts.map +1 -0
  385. package/dist/src/backend/services/decision-log/service/index.js +2 -0
  386. package/dist/src/backend/services/decision-log/service/index.js.map +1 -0
  387. package/dist/src/backend/services/env-schemas.d.ts +46 -43
  388. package/dist/src/backend/services/env-schemas.d.ts.map +1 -1
  389. package/dist/src/backend/services/env-schemas.js +3 -0
  390. package/dist/src/backend/services/env-schemas.js.map +1 -1
  391. package/dist/src/backend/services/github/service/bridges.d.ts +54 -2
  392. package/dist/src/backend/services/github/service/bridges.d.ts.map +1 -1
  393. package/dist/src/backend/services/github/service/github-cli/mappers.d.ts +5 -2
  394. package/dist/src/backend/services/github/service/github-cli/mappers.d.ts.map +1 -1
  395. package/dist/src/backend/services/github/service/github-cli/mappers.js.map +1 -1
  396. package/dist/src/backend/services/github/service/github-cli/schemas.d.ts +34 -10
  397. package/dist/src/backend/services/github/service/github-cli/schemas.d.ts.map +1 -1
  398. package/dist/src/backend/services/github/service/github-cli/schemas.js +18 -0
  399. package/dist/src/backend/services/github/service/github-cli/schemas.js.map +1 -1
  400. package/dist/src/backend/services/github/service/github-cli/types.d.ts +6 -0
  401. package/dist/src/backend/services/github/service/github-cli/types.d.ts.map +1 -1
  402. package/dist/src/backend/services/github/service/github-cli.service.d.ts +8 -7
  403. package/dist/src/backend/services/github/service/github-cli.service.d.ts.map +1 -1
  404. package/dist/src/backend/services/github/service/github-cli.service.js +39 -33
  405. package/dist/src/backend/services/github/service/github-cli.service.js.map +1 -1
  406. package/dist/src/backend/services/github/service/github-cli.service.test.js +63 -67
  407. package/dist/src/backend/services/github/service/github-cli.service.test.js.map +1 -1
  408. package/dist/src/backend/services/github/service/index.d.ts +3 -4
  409. package/dist/src/backend/services/github/service/index.d.ts.map +1 -1
  410. package/dist/src/backend/services/github/service/index.js +2 -2
  411. package/dist/src/backend/services/github/service/index.js.map +1 -1
  412. package/dist/src/backend/services/github/service/pr-fetch-coordinator.d.ts +33 -0
  413. package/dist/src/backend/services/github/service/pr-fetch-coordinator.d.ts.map +1 -0
  414. package/dist/src/backend/services/github/service/pr-fetch-coordinator.js +110 -0
  415. package/dist/src/backend/services/github/service/pr-fetch-coordinator.js.map +1 -0
  416. package/dist/src/backend/services/github/service/pr-fetch-coordinator.test.d.ts +2 -0
  417. package/dist/src/backend/services/github/service/pr-fetch-coordinator.test.d.ts.map +1 -0
  418. package/dist/src/backend/services/github/service/pr-fetch-coordinator.test.js +222 -0
  419. package/dist/src/backend/services/github/service/pr-fetch-coordinator.test.js.map +1 -0
  420. package/dist/src/backend/services/github/service/pr-snapshot.service.d.ts +25 -8
  421. package/dist/src/backend/services/github/service/pr-snapshot.service.d.ts.map +1 -1
  422. package/dist/src/backend/services/github/service/pr-snapshot.service.js +124 -24
  423. package/dist/src/backend/services/github/service/pr-snapshot.service.js.map +1 -1
  424. package/dist/src/backend/services/github/service/pr-snapshot.service.test.js +332 -27
  425. package/dist/src/backend/services/github/service/pr-snapshot.service.test.js.map +1 -1
  426. package/dist/src/backend/services/job-runner.service.d.ts +24 -0
  427. package/dist/src/backend/services/job-runner.service.d.ts.map +1 -0
  428. package/dist/src/backend/services/job-runner.service.js +168 -0
  429. package/dist/src/backend/services/job-runner.service.js.map +1 -0
  430. package/dist/src/backend/services/job-runner.service.test.d.ts +2 -0
  431. package/dist/src/backend/services/job-runner.service.test.d.ts.map +1 -0
  432. package/dist/src/backend/services/job-runner.service.test.js +498 -0
  433. package/dist/src/backend/services/job-runner.service.test.js.map +1 -0
  434. package/dist/src/backend/services/periodic-task/index.d.ts +0 -1
  435. package/dist/src/backend/services/periodic-task/index.d.ts.map +1 -1
  436. package/dist/src/backend/services/periodic-task/index.js +0 -1
  437. package/dist/src/backend/services/periodic-task/index.js.map +1 -1
  438. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.d.ts +2 -2
  439. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.d.ts.map +1 -1
  440. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.js +17 -10
  441. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.js.map +1 -1
  442. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.test.js +29 -10
  443. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.test.js.map +1 -1
  444. package/dist/src/backend/services/periodic-task/service/periodic-task.service.d.ts +107 -7
  445. package/dist/src/backend/services/periodic-task/service/periodic-task.service.d.ts.map +1 -1
  446. package/dist/src/backend/services/periodic-task/service/periodic-task.service.js +75 -51
  447. package/dist/src/backend/services/periodic-task/service/periodic-task.service.js.map +1 -1
  448. package/dist/src/backend/services/periodic-task/service/periodic-task.service.test.js +104 -8
  449. package/dist/src/backend/services/periodic-task/service/periodic-task.service.test.js.map +1 -1
  450. package/dist/src/backend/services/port.service.d.ts.map +1 -1
  451. package/dist/src/backend/services/port.service.js +4 -0
  452. package/dist/src/backend/services/port.service.js.map +1 -1
  453. package/dist/src/backend/services/port.service.test.js +9 -0
  454. package/dist/src/backend/services/port.service.test.js.map +1 -1
  455. package/dist/src/backend/services/ratchet/service/bridges.d.ts +54 -7
  456. package/dist/src/backend/services/ratchet/service/bridges.d.ts.map +1 -1
  457. package/dist/src/backend/services/ratchet/service/fixer-session.service.d.ts +6 -1
  458. package/dist/src/backend/services/ratchet/service/fixer-session.service.d.ts.map +1 -1
  459. package/dist/src/backend/services/ratchet/service/fixer-session.service.js +47 -8
  460. package/dist/src/backend/services/ratchet/service/fixer-session.service.js.map +1 -1
  461. package/dist/src/backend/services/ratchet/service/fixer-session.service.test.js +131 -29
  462. package/dist/src/backend/services/ratchet/service/fixer-session.service.test.js.map +1 -1
  463. package/dist/src/backend/services/ratchet/service/index.d.ts +3 -3
  464. package/dist/src/backend/services/ratchet/service/index.d.ts.map +1 -1
  465. package/dist/src/backend/services/ratchet/service/index.js +1 -1
  466. package/dist/src/backend/services/ratchet/service/index.js.map +1 -1
  467. package/dist/src/backend/services/ratchet/service/ratchet-active-session.helpers.d.ts +5 -1
  468. package/dist/src/backend/services/ratchet/service/ratchet-active-session.helpers.d.ts.map +1 -1
  469. package/dist/src/backend/services/ratchet/service/ratchet-active-session.helpers.js +20 -16
  470. package/dist/src/backend/services/ratchet/service/ratchet-active-session.helpers.js.map +1 -1
  471. package/dist/src/backend/services/ratchet/service/ratchet-decision-logging.helpers.d.ts +1 -1
  472. package/dist/src/backend/services/ratchet/service/ratchet-decision-logging.helpers.js +1 -1
  473. package/dist/src/backend/services/ratchet/service/ratchet-decision-logging.helpers.js.map +1 -1
  474. package/dist/src/backend/services/ratchet/service/ratchet-fixer-dispatch.helpers.d.ts +3 -0
  475. package/dist/src/backend/services/ratchet/service/ratchet-fixer-dispatch.helpers.d.ts.map +1 -1
  476. package/dist/src/backend/services/ratchet/service/ratchet-fixer-dispatch.helpers.js +52 -10
  477. package/dist/src/backend/services/ratchet/service/ratchet-fixer-dispatch.helpers.js.map +1 -1
  478. package/dist/src/backend/services/ratchet/service/ratchet-pr-state.helpers.d.ts +9 -4
  479. package/dist/src/backend/services/ratchet/service/ratchet-pr-state.helpers.d.ts.map +1 -1
  480. package/dist/src/backend/services/ratchet/service/ratchet-pr-state.helpers.js +157 -113
  481. package/dist/src/backend/services/ratchet/service/ratchet-pr-state.helpers.js.map +1 -1
  482. package/dist/src/backend/services/ratchet/service/ratchet-pr-state.helpers.test.js +357 -33
  483. package/dist/src/backend/services/ratchet/service/ratchet-pr-state.helpers.test.js.map +1 -1
  484. package/dist/src/backend/services/ratchet/service/ratchet-provider-resolver.service.d.ts.map +1 -1
  485. package/dist/src/backend/services/ratchet/service/ratchet-provider-resolver.service.js +4 -4
  486. package/dist/src/backend/services/ratchet/service/ratchet-provider-resolver.service.js.map +1 -1
  487. package/dist/src/backend/services/ratchet/service/ratchet-provider-resolver.service.test.js +14 -14
  488. package/dist/src/backend/services/ratchet/service/ratchet-provider-resolver.service.test.js.map +1 -1
  489. package/dist/src/backend/services/ratchet/service/ratchet.service.d.ts +14 -9
  490. package/dist/src/backend/services/ratchet/service/ratchet.service.d.ts.map +1 -1
  491. package/dist/src/backend/services/ratchet/service/ratchet.service.js +104 -157
  492. package/dist/src/backend/services/ratchet/service/ratchet.service.js.map +1 -1
  493. package/dist/src/backend/services/ratchet/service/ratchet.service.test.js +866 -433
  494. package/dist/src/backend/services/ratchet/service/ratchet.service.test.js.map +1 -1
  495. package/dist/src/backend/services/ratchet/service/ratchet.types.d.ts +5 -3
  496. package/dist/src/backend/services/ratchet/service/ratchet.types.d.ts.map +1 -1
  497. package/dist/src/backend/services/registry.d.ts +49 -2
  498. package/dist/src/backend/services/registry.d.ts.map +1 -1
  499. package/dist/src/backend/services/registry.js +59 -1
  500. package/dist/src/backend/services/registry.js.map +1 -1
  501. package/dist/src/backend/services/resources.integration.test.js +235 -114
  502. package/dist/src/backend/services/resources.integration.test.js.map +1 -1
  503. package/dist/src/backend/services/run-script/service/bridges.d.ts +4 -0
  504. package/dist/src/backend/services/run-script/service/bridges.d.ts.map +1 -1
  505. package/dist/src/backend/services/{factory-config.service.d.ts → run-script/service/factory-config.service.d.ts} +1 -1
  506. package/dist/src/backend/services/run-script/service/factory-config.service.d.ts.map +1 -0
  507. package/dist/src/backend/services/{factory-config.service.js → run-script/service/factory-config.service.js} +1 -1
  508. package/dist/src/backend/services/run-script/service/factory-config.service.js.map +1 -0
  509. package/dist/src/backend/services/run-script/service/factory-config.service.test.d.ts.map +1 -0
  510. package/dist/src/backend/services/{factory-config.service.test.js → run-script/service/factory-config.service.test.js} +1 -1
  511. package/dist/src/backend/services/run-script/service/factory-config.service.test.js.map +1 -0
  512. package/dist/src/backend/services/run-script/service/index.d.ts +2 -0
  513. package/dist/src/backend/services/run-script/service/index.d.ts.map +1 -1
  514. package/dist/src/backend/services/run-script/service/index.js +2 -0
  515. package/dist/src/backend/services/run-script/service/index.js.map +1 -1
  516. package/dist/src/backend/services/run-script/service/port-allocation.service.d.ts.map +1 -0
  517. package/dist/src/backend/services/run-script/service/port-allocation.service.js.map +1 -0
  518. package/dist/src/backend/services/run-script/service/port-allocation.service.test.d.ts.map +1 -0
  519. package/dist/src/backend/services/run-script/service/port-allocation.service.test.js.map +1 -0
  520. package/dist/src/backend/services/{run-script-config-persistence.service.d.ts → run-script/service/run-script-config-persistence.service.d.ts} +10 -1
  521. package/dist/src/backend/services/run-script/service/run-script-config-persistence.service.d.ts.map +1 -0
  522. package/dist/src/backend/services/{run-script-config-persistence.service.js → run-script/service/run-script-config-persistence.service.js} +46 -2
  523. package/dist/src/backend/services/run-script/service/run-script-config-persistence.service.js.map +1 -0
  524. package/dist/src/backend/services/run-script/service/run-script-config-persistence.service.test.d.ts.map +1 -0
  525. package/dist/src/backend/services/{run-script-config-persistence.service.test.js → run-script/service/run-script-config-persistence.service.test.js} +47 -0
  526. package/dist/src/backend/services/run-script/service/run-script-config-persistence.service.test.js.map +1 -0
  527. package/dist/src/backend/services/run-script/service/run-script-domain-exports.test.js +11 -1
  528. package/dist/src/backend/services/run-script/service/run-script-domain-exports.test.js.map +1 -1
  529. package/dist/src/backend/services/run-script/service/run-script-process-utils.d.ts.map +1 -1
  530. package/dist/src/backend/services/run-script/service/run-script-process-utils.js +1 -1
  531. package/dist/src/backend/services/run-script/service/run-script-process-utils.js.map +1 -1
  532. package/dist/src/backend/services/{run-script-proxy.service.d.ts → run-script/service/run-script-proxy.service.d.ts} +1 -0
  533. package/dist/src/backend/services/run-script/service/run-script-proxy.service.d.ts.map +1 -0
  534. package/dist/src/backend/services/{run-script-proxy.service.js → run-script/service/run-script-proxy.service.js} +28 -5
  535. package/dist/src/backend/services/run-script/service/run-script-proxy.service.js.map +1 -0
  536. package/dist/src/backend/services/run-script/service/run-script-proxy.service.test.d.ts.map +1 -0
  537. package/dist/src/backend/services/{run-script-proxy.service.test.js → run-script/service/run-script-proxy.service.test.js} +127 -9
  538. package/dist/src/backend/services/run-script/service/run-script-proxy.service.test.js.map +1 -0
  539. package/dist/src/backend/services/run-script/service/run-script-state-machine.service.d.ts +9 -9
  540. package/dist/src/backend/services/run-script/service/run-script-state-machine.service.d.ts.map +1 -1
  541. package/dist/src/backend/services/run-script/service/run-script-state-machine.service.js +8 -8
  542. package/dist/src/backend/services/run-script/service/run-script-state-machine.service.js.map +1 -1
  543. package/dist/src/backend/services/run-script/service/run-script-state-machine.service.test.js +125 -125
  544. package/dist/src/backend/services/run-script/service/run-script-state-machine.service.test.js.map +1 -1
  545. package/dist/src/backend/services/run-script/service/run-script.service.js +16 -16
  546. package/dist/src/backend/services/run-script/service/run-script.service.js.map +1 -1
  547. package/dist/src/backend/services/run-script/service/run-script.service.test.js +5 -5
  548. package/dist/src/backend/services/run-script/service/run-script.service.test.js.map +1 -1
  549. package/dist/src/backend/services/run-script/service/startup-script.service.d.ts.map +1 -1
  550. package/dist/src/backend/services/run-script/service/startup-script.service.js +24 -12
  551. package/dist/src/backend/services/run-script/service/startup-script.service.js.map +1 -1
  552. package/dist/src/backend/services/run-script/service/startup-script.service.test.js +100 -2
  553. package/dist/src/backend/services/run-script/service/startup-script.service.test.js.map +1 -1
  554. package/dist/src/backend/services/service-registry-check.helpers.d.ts +6 -0
  555. package/dist/src/backend/services/service-registry-check.helpers.d.ts.map +1 -0
  556. package/dist/src/backend/services/service-registry-check.helpers.js +22 -0
  557. package/dist/src/backend/services/service-registry-check.helpers.js.map +1 -0
  558. package/dist/src/backend/services/service-registry-check.test.d.ts +2 -0
  559. package/dist/src/backend/services/service-registry-check.test.d.ts.map +1 -0
  560. package/dist/src/backend/services/service-registry-check.test.js +36 -0
  561. package/dist/src/backend/services/service-registry-check.test.js.map +1 -0
  562. package/dist/src/backend/services/session/index.d.ts +0 -2
  563. package/dist/src/backend/services/session/index.d.ts.map +1 -1
  564. package/dist/src/backend/services/session/index.js +0 -2
  565. package/dist/src/backend/services/session/index.js.map +1 -1
  566. package/dist/src/backend/services/session/resources/agent-session.accessor.d.ts +4 -3
  567. package/dist/src/backend/services/session/resources/agent-session.accessor.d.ts.map +1 -1
  568. package/dist/src/backend/services/session/resources/agent-session.accessor.js +6 -15
  569. package/dist/src/backend/services/session/resources/agent-session.accessor.js.map +1 -1
  570. package/dist/src/backend/services/session/resources/agent-session.accessor.test.js +11 -15
  571. package/dist/src/backend/services/session/resources/agent-session.accessor.test.js.map +1 -1
  572. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts.map +1 -1
  573. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js +25 -9
  574. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js.map +1 -1
  575. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js +109 -26
  576. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js.map +1 -1
  577. package/dist/src/backend/services/session/service/acp/child-workspace-mcp-server.js +2 -2
  578. package/dist/src/backend/services/session/service/acp/child-workspace-mcp-server.js.map +1 -1
  579. package/dist/src/backend/services/session/service/acp/child-workspace-mcp-server.test.d.ts +2 -0
  580. package/dist/src/backend/services/session/service/acp/child-workspace-mcp-server.test.d.ts.map +1 -0
  581. package/dist/src/backend/services/session/service/acp/child-workspace-mcp-server.test.js +119 -0
  582. package/dist/src/backend/services/session/service/acp/child-workspace-mcp-server.test.js.map +1 -0
  583. package/dist/src/backend/services/session/service/bridges.d.ts +1 -0
  584. package/dist/src/backend/services/session/service/bridges.d.ts.map +1 -1
  585. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.d.ts.map +1 -1
  586. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js +4 -4
  587. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js.map +1 -1
  588. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js +4 -2
  589. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js.map +1 -1
  590. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/permission-response.handler.d.ts +2 -2
  591. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/permission-response.handler.d.ts.map +1 -1
  592. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/permission-response.handler.js +2 -2
  593. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/permission-response.handler.js.map +1 -1
  594. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/permission-response.handler.test.js +10 -25
  595. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/permission-response.handler.test.js.map +1 -1
  596. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.d.ts.map +1 -1
  597. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.js +10 -2
  598. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.js.map +1 -1
  599. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.test.js +41 -2
  600. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.test.js.map +1 -1
  601. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-model.handler.d.ts +2 -2
  602. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-model.handler.d.ts.map +1 -1
  603. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-model.handler.js +4 -4
  604. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-model.handler.js.map +1 -1
  605. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-model.handler.test.js +1 -4
  606. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-model.handler.test.js.map +1 -1
  607. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.js +2 -2
  608. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.js.map +1 -1
  609. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.test.js +2 -2
  610. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.test.js.map +1 -1
  611. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.js +2 -2
  612. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.js.map +1 -1
  613. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.test.js +2 -2
  614. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.test.js.map +1 -1
  615. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js +2 -2
  616. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js.map +1 -1
  617. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js +2 -2
  618. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js.map +1 -1
  619. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/user-input.handler.d.ts +3 -2
  620. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/user-input.handler.d.ts.map +1 -1
  621. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/user-input.handler.js +2 -2
  622. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/user-input.handler.js.map +1 -1
  623. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/user-input.handler.test.js +17 -20
  624. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/user-input.handler.test.js.map +1 -1
  625. package/dist/src/backend/services/session/service/chat/chat-message-handlers/registry.d.ts.map +1 -1
  626. package/dist/src/backend/services/session/service/chat/chat-message-handlers/registry.js +15 -6
  627. package/dist/src/backend/services/session/service/chat/chat-message-handlers/registry.js.map +1 -1
  628. package/dist/src/backend/services/session/service/chat/chat-message-handlers/types.d.ts +12 -3
  629. package/dist/src/backend/services/session/service/chat/chat-message-handlers/types.d.ts.map +1 -1
  630. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.d.ts.map +1 -1
  631. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js +21 -17
  632. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js.map +1 -1
  633. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js +23 -17
  634. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js.map +1 -1
  635. package/dist/src/backend/services/session/service/data/session-data.service.d.ts +18 -166
  636. package/dist/src/backend/services/session/service/data/session-data.service.d.ts.map +1 -1
  637. package/dist/src/backend/services/session/service/data/session-data.service.js +68 -36
  638. package/dist/src/backend/services/session/service/data/session-data.service.js.map +1 -1
  639. package/dist/src/backend/services/session/service/data/session-data.service.test.d.ts +2 -0
  640. package/dist/src/backend/services/session/service/data/session-data.service.test.d.ts.map +1 -0
  641. package/dist/src/backend/services/session/service/data/session-data.service.test.js +103 -0
  642. package/dist/src/backend/services/session/service/data/session-data.service.test.js.map +1 -0
  643. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.d.ts +9 -0
  644. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.d.ts.map +1 -1
  645. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.js +15 -5
  646. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.js.map +1 -1
  647. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.test.js +24 -13
  648. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.test.js.map +1 -1
  649. package/dist/src/backend/services/session/service/index.d.ts +9 -3
  650. package/dist/src/backend/services/session/service/index.d.ts.map +1 -1
  651. package/dist/src/backend/services/session/service/index.js +7 -3
  652. package/dist/src/backend/services/session/service/index.js.map +1 -1
  653. package/dist/src/backend/services/session/service/interceptor.bridge.d.ts.map +1 -1
  654. package/dist/src/backend/services/session/service/interceptor.bridge.js +47 -3
  655. package/dist/src/backend/services/session/service/interceptor.bridge.js.map +1 -1
  656. package/dist/src/backend/services/session/service/interceptor.bridge.test.d.ts +2 -0
  657. package/dist/src/backend/services/session/service/interceptor.bridge.test.d.ts.map +1 -0
  658. package/dist/src/backend/services/session/service/interceptor.bridge.test.js +83 -0
  659. package/dist/src/backend/services/session/service/interceptor.bridge.test.js.map +1 -0
  660. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts +15 -4
  661. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts.map +1 -1
  662. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js +63 -6
  663. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js.map +1 -1
  664. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.d.ts +2 -0
  665. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.d.ts.map +1 -0
  666. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.js +230 -0
  667. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.js.map +1 -0
  668. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.d.ts +2 -1
  669. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.d.ts.map +1 -1
  670. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.js +3 -28
  671. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.js.map +1 -1
  672. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.test.d.ts +2 -0
  673. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.test.d.ts.map +1 -0
  674. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.test.js +33 -0
  675. package/dist/src/backend/services/session/service/lifecycle/session-pr-discovery.service.test.js.map +1 -0
  676. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts +16 -0
  677. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts.map +1 -0
  678. package/dist/src/backend/services/session/service/lifecycle/session-services.js +79 -0
  679. package/dist/src/backend/services/session/service/lifecycle/session-services.js.map +1 -0
  680. package/dist/src/backend/services/session/service/lifecycle/session.config.service.d.ts.map +1 -1
  681. package/dist/src/backend/services/session/service/lifecycle/session.config.service.js +15 -4
  682. package/dist/src/backend/services/session/service/lifecycle/session.config.service.js.map +1 -1
  683. package/dist/src/backend/services/session/service/lifecycle/session.config.service.test.js +59 -6
  684. package/dist/src/backend/services/session/service/lifecycle/session.config.service.test.js.map +1 -1
  685. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts +4 -2
  686. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts.map +1 -1
  687. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js +20 -11
  688. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js.map +1 -1
  689. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js +84 -55
  690. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js.map +1 -1
  691. package/dist/src/backend/services/session/service/lifecycle/session.prompt-builder.d.ts +5 -0
  692. package/dist/src/backend/services/session/service/lifecycle/session.prompt-builder.d.ts.map +1 -1
  693. package/dist/src/backend/services/session/service/lifecycle/session.prompt-builder.js +19 -11
  694. package/dist/src/backend/services/session/service/lifecycle/session.prompt-builder.js.map +1 -1
  695. package/dist/src/backend/services/session/service/lifecycle/session.prompt-builder.test.js +32 -1
  696. package/dist/src/backend/services/session/service/lifecycle/session.prompt-builder.test.js.map +1 -1
  697. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.d.ts +2 -0
  698. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.d.ts.map +1 -0
  699. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js +142 -0
  700. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js.map +1 -0
  701. package/dist/src/backend/services/session/service/lifecycle/session.repository.d.ts +6 -3
  702. package/dist/src/backend/services/session/service/lifecycle/session.repository.d.ts.map +1 -1
  703. package/dist/src/backend/services/session/service/lifecycle/session.repository.js +3 -3
  704. package/dist/src/backend/services/session/service/lifecycle/session.repository.js.map +1 -1
  705. package/dist/src/backend/services/session/service/lifecycle/session.repository.test.js +1 -1
  706. package/dist/src/backend/services/session/service/lifecycle/session.repository.test.js.map +1 -1
  707. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts +16 -76
  708. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts.map +1 -1
  709. package/dist/src/backend/services/session/service/lifecycle/session.service.js +21 -213
  710. package/dist/src/backend/services/session/service/lifecycle/session.service.js.map +1 -1
  711. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js +176 -67
  712. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js.map +1 -1
  713. package/dist/src/backend/services/session/service/session-domain.service.d.ts.map +1 -1
  714. package/dist/src/backend/services/session/service/session-domain.service.js +2 -1
  715. package/dist/src/backend/services/session/service/session-domain.service.js.map +1 -1
  716. package/dist/src/backend/services/session/service/store/session-queue.test.js +1 -0
  717. package/dist/src/backend/services/session/service/store/session-queue.test.js.map +1 -1
  718. package/dist/src/backend/services/session/service/store/session-replay-builder.d.ts.map +1 -1
  719. package/dist/src/backend/services/session/service/store/session-replay-builder.js +2 -1
  720. package/dist/src/backend/services/session/service/store/session-replay-builder.js.map +1 -1
  721. package/dist/src/backend/services/session/service/store/session-replay-builder.test.js +13 -0
  722. package/dist/src/backend/services/session/service/store/session-replay-builder.test.js.map +1 -1
  723. package/dist/src/backend/services/session/service/store/session-runtime-machine.test.js +1 -0
  724. package/dist/src/backend/services/session/service/store/session-runtime-machine.test.js.map +1 -1
  725. package/dist/src/backend/services/session/service/store/session-store-registry.d.ts.map +1 -1
  726. package/dist/src/backend/services/session/service/store/session-store-registry.js +1 -0
  727. package/dist/src/backend/services/session/service/store/session-store-registry.js.map +1 -1
  728. package/dist/src/backend/services/session/service/store/session-store.types.d.ts +1 -0
  729. package/dist/src/backend/services/session/service/store/session-store.types.d.ts.map +1 -1
  730. package/dist/src/backend/services/session/service/store/session-transcript.d.ts +1 -0
  731. package/dist/src/backend/services/session/service/store/session-transcript.d.ts.map +1 -1
  732. package/dist/src/backend/services/session/service/store/session-transcript.js +83 -15
  733. package/dist/src/backend/services/session/service/store/session-transcript.js.map +1 -1
  734. package/dist/src/backend/services/session/service/store/session-transcript.test.js +148 -30
  735. package/dist/src/backend/services/session/service/store/session-transcript.test.js.map +1 -1
  736. package/dist/src/backend/services/session/service/store/slash-command-cache.service.d.ts.map +1 -1
  737. package/dist/src/backend/services/session/service/store/slash-command-cache.service.js +23 -18
  738. package/dist/src/backend/services/session/service/store/slash-command-cache.service.js.map +1 -1
  739. package/dist/src/backend/services/session/service/store/slash-command-cache.service.test.js +70 -19
  740. package/dist/src/backend/services/session/service/store/slash-command-cache.service.test.js.map +1 -1
  741. package/dist/src/backend/services/session/types.d.ts +25 -0
  742. package/dist/src/backend/services/session/types.d.ts.map +1 -0
  743. package/dist/src/backend/services/session/types.js +2 -0
  744. package/dist/src/backend/services/session/types.js.map +1 -0
  745. package/dist/src/backend/services/settings/index.d.ts +1 -3
  746. package/dist/src/backend/services/settings/index.d.ts.map +1 -1
  747. package/dist/src/backend/services/settings/index.js +1 -3
  748. package/dist/src/backend/services/settings/index.js.map +1 -1
  749. package/dist/src/backend/services/settings/resources/data-backup.accessor.d.ts +10 -2
  750. package/dist/src/backend/services/settings/resources/data-backup.accessor.d.ts.map +1 -1
  751. package/dist/src/backend/services/settings/resources/data-backup.accessor.js +4 -1
  752. package/dist/src/backend/services/settings/resources/data-backup.accessor.js.map +1 -1
  753. package/dist/src/backend/services/settings/resources/user-settings.accessor.d.ts +3 -1
  754. package/dist/src/backend/services/settings/resources/user-settings.accessor.d.ts.map +1 -1
  755. package/dist/src/backend/services/settings/resources/user-settings.accessor.js +10 -0
  756. package/dist/src/backend/services/settings/resources/user-settings.accessor.js.map +1 -1
  757. package/dist/src/backend/services/settings/resources/user-settings.accessor.test.d.ts +2 -0
  758. package/dist/src/backend/services/settings/resources/user-settings.accessor.test.d.ts.map +1 -0
  759. package/dist/src/backend/services/settings/resources/user-settings.accessor.test.js +24 -0
  760. package/dist/src/backend/services/settings/resources/user-settings.accessor.test.js.map +1 -0
  761. package/dist/src/backend/services/settings/service/health.service.d.ts +6 -0
  762. package/dist/src/backend/services/settings/service/health.service.d.ts.map +1 -0
  763. package/dist/src/backend/services/settings/service/health.service.js +8 -0
  764. package/dist/src/backend/services/settings/service/health.service.js.map +1 -0
  765. package/dist/src/backend/services/settings/service/health.service.test.d.ts +2 -0
  766. package/dist/src/backend/services/settings/service/health.service.test.d.ts.map +1 -0
  767. package/dist/src/backend/services/settings/service/health.service.test.js +16 -0
  768. package/dist/src/backend/services/settings/service/health.service.test.js.map +1 -0
  769. package/dist/src/backend/services/settings/service/index.d.ts +3 -0
  770. package/dist/src/backend/services/settings/service/index.d.ts.map +1 -0
  771. package/dist/src/backend/services/settings/service/index.js +3 -0
  772. package/dist/src/backend/services/settings/service/index.js.map +1 -0
  773. package/dist/src/backend/services/settings/service/settings-domain-exports.test.d.ts +2 -0
  774. package/dist/src/backend/services/settings/service/settings-domain-exports.test.d.ts.map +1 -0
  775. package/dist/src/backend/services/settings/service/settings-domain-exports.test.js +9 -0
  776. package/dist/src/backend/services/settings/service/settings-domain-exports.test.js.map +1 -0
  777. package/dist/src/backend/services/settings/service/user-settings.service.d.ts +27 -0
  778. package/dist/src/backend/services/settings/service/user-settings.service.d.ts.map +1 -0
  779. package/dist/src/backend/services/settings/service/user-settings.service.js +23 -0
  780. package/dist/src/backend/services/settings/service/user-settings.service.js.map +1 -0
  781. package/dist/src/backend/services/settings/service/user-settings.service.test.d.ts +2 -0
  782. package/dist/src/backend/services/settings/service/user-settings.service.test.d.ts.map +1 -0
  783. package/dist/src/backend/services/settings/service/user-settings.service.test.js +58 -0
  784. package/dist/src/backend/services/settings/service/user-settings.service.test.js.map +1 -0
  785. package/dist/src/backend/services/terminal/index.d.ts +0 -1
  786. package/dist/src/backend/services/terminal/index.d.ts.map +1 -1
  787. package/dist/src/backend/services/terminal/index.js +0 -1
  788. package/dist/src/backend/services/terminal/index.js.map +1 -1
  789. package/dist/src/backend/services/terminal/service/index.d.ts +2 -0
  790. package/dist/src/backend/services/terminal/service/index.d.ts.map +1 -1
  791. package/dist/src/backend/services/terminal/service/index.js +1 -0
  792. package/dist/src/backend/services/terminal/service/index.js.map +1 -1
  793. package/dist/src/backend/services/terminal/service/terminal-domain-exports.test.js +4 -1
  794. package/dist/src/backend/services/terminal/service/terminal-domain-exports.test.js.map +1 -1
  795. package/dist/src/backend/services/terminal/service/terminal-session.service.d.ts +61 -0
  796. package/dist/src/backend/services/terminal/service/terminal-session.service.d.ts.map +1 -0
  797. package/dist/src/backend/services/terminal/service/terminal-session.service.js +43 -0
  798. package/dist/src/backend/services/terminal/service/terminal-session.service.js.map +1 -0
  799. package/dist/src/backend/services/terminal/service/terminal-session.service.test.d.ts +2 -0
  800. package/dist/src/backend/services/terminal/service/terminal-session.service.test.d.ts.map +1 -0
  801. package/dist/src/backend/services/terminal/service/terminal-session.service.test.js +46 -0
  802. package/dist/src/backend/services/terminal/service/terminal-session.service.test.js.map +1 -0
  803. package/dist/src/backend/services/workspace/index.d.ts +0 -3
  804. package/dist/src/backend/services/workspace/index.d.ts.map +1 -1
  805. package/dist/src/backend/services/workspace/index.js +0 -3
  806. package/dist/src/backend/services/workspace/index.js.map +1 -1
  807. package/dist/src/backend/services/workspace/resources/project.accessor.d.ts +1 -1
  808. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.d.ts +25 -0
  809. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.d.ts.map +1 -0
  810. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.js +73 -0
  811. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.js.map +1 -0
  812. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.test.d.ts +2 -0
  813. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.test.d.ts.map +1 -0
  814. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.test.js +155 -0
  815. package/dist/src/backend/services/workspace/resources/workspace-auto-iteration.accessor.test.js.map +1 -0
  816. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.d.ts +61 -0
  817. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.d.ts.map +1 -0
  818. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.js +265 -0
  819. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.js.map +1 -0
  820. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.test.d.ts +2 -0
  821. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.test.d.ts.map +1 -0
  822. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.test.js +398 -0
  823. package/dist/src/backend/services/workspace/resources/workspace-pr.accessor.test.js.map +1 -0
  824. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.d.ts +50 -0
  825. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.d.ts.map +1 -0
  826. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.js +179 -0
  827. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.js.map +1 -0
  828. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.test.d.ts +2 -0
  829. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.test.d.ts.map +1 -0
  830. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.test.js +311 -0
  831. package/dist/src/backend/services/workspace/resources/workspace-ratchet.accessor.test.js.map +1 -0
  832. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.d.ts +33 -0
  833. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.d.ts.map +1 -0
  834. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.js +110 -0
  835. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.js.map +1 -0
  836. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.test.d.ts +2 -0
  837. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.test.d.ts.map +1 -0
  838. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.test.js +192 -0
  839. package/dist/src/backend/services/workspace/resources/workspace-run-script.accessor.test.js.map +1 -0
  840. package/dist/src/backend/services/workspace/resources/workspace.accessor.d.ts +90 -83
  841. package/dist/src/backend/services/workspace/resources/workspace.accessor.d.ts.map +1 -1
  842. package/dist/src/backend/services/workspace/resources/workspace.accessor.js +238 -221
  843. package/dist/src/backend/services/workspace/resources/workspace.accessor.js.map +1 -1
  844. package/dist/src/backend/services/workspace/resources/workspace.accessor.test.js +328 -148
  845. package/dist/src/backend/services/workspace/resources/workspace.accessor.test.js.map +1 -1
  846. package/dist/src/backend/services/workspace/service/bridges.d.ts +1 -3
  847. package/dist/src/backend/services/workspace/service/bridges.d.ts.map +1 -1
  848. package/dist/src/backend/services/workspace/service/index.d.ts +13 -2
  849. package/dist/src/backend/services/workspace/service/index.d.ts.map +1 -1
  850. package/dist/src/backend/services/workspace/service/index.js +12 -2
  851. package/dist/src/backend/services/workspace/service/index.js.map +1 -1
  852. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.d.ts +12 -2
  853. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.d.ts.map +1 -1
  854. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.js +27 -37
  855. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.js.map +1 -1
  856. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.test.js +49 -13
  857. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.test.js.map +1 -1
  858. package/dist/src/backend/services/workspace/service/lifecycle/data.service.d.ts +148 -384
  859. package/dist/src/backend/services/workspace/service/lifecycle/data.service.d.ts.map +1 -1
  860. package/dist/src/backend/services/workspace/service/lifecycle/data.service.js +29 -13
  861. package/dist/src/backend/services/workspace/service/lifecycle/data.service.js.map +1 -1
  862. package/dist/src/backend/services/workspace/service/lifecycle/data.service.test.js +21 -8
  863. package/dist/src/backend/services/workspace/service/lifecycle/data.service.test.js.map +1 -1
  864. package/dist/src/backend/services/workspace/service/lifecycle/state-machine.service.d.ts.map +1 -1
  865. package/dist/src/backend/services/workspace/service/lifecycle/state-machine.service.js +13 -23
  866. package/dist/src/backend/services/workspace/service/lifecycle/state-machine.service.js.map +1 -1
  867. package/dist/src/backend/services/workspace/service/lifecycle/state-machine.service.test.js +99 -31
  868. package/dist/src/backend/services/workspace/service/lifecycle/state-machine.service.test.js.map +1 -1
  869. package/dist/src/backend/services/workspace/service/lifecycle/workspace-auto-iteration.service.d.ts +17 -0
  870. package/dist/src/backend/services/workspace/service/lifecycle/workspace-auto-iteration.service.d.ts.map +1 -0
  871. package/dist/src/backend/services/workspace/service/lifecycle/workspace-auto-iteration.service.js +26 -0
  872. package/dist/src/backend/services/workspace/service/lifecycle/workspace-auto-iteration.service.js.map +1 -0
  873. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.d.ts +16 -0
  874. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.d.ts.map +1 -0
  875. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.js +25 -0
  876. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.js.map +1 -0
  877. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.test.d.ts +2 -0
  878. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.test.d.ts.map +1 -0
  879. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.test.js +77 -0
  880. package/dist/src/backend/services/workspace/service/lifecycle/workspace-pr-snapshot.service.test.js.map +1 -0
  881. package/dist/src/backend/services/workspace/service/lifecycle/workspace-ratchet.service.d.ts +19 -0
  882. package/dist/src/backend/services/workspace/service/lifecycle/workspace-ratchet.service.d.ts.map +1 -0
  883. package/dist/src/backend/services/workspace/service/lifecycle/workspace-ratchet.service.js +32 -0
  884. package/dist/src/backend/services/workspace/service/lifecycle/workspace-ratchet.service.js.map +1 -0
  885. package/dist/src/backend/services/workspace/service/lifecycle/workspace-relationships.service.d.ts +189 -0
  886. package/dist/src/backend/services/workspace/service/lifecycle/workspace-relationships.service.d.ts.map +1 -0
  887. package/dist/src/backend/services/workspace/service/lifecycle/workspace-relationships.service.js +11 -0
  888. package/dist/src/backend/services/workspace/service/lifecycle/workspace-relationships.service.js.map +1 -0
  889. package/dist/src/backend/services/workspace/service/lifecycle/workspace-run-script.service.d.ts +34 -0
  890. package/dist/src/backend/services/workspace/service/lifecycle/workspace-run-script.service.d.ts.map +1 -0
  891. package/dist/src/backend/services/workspace/service/lifecycle/workspace-run-script.service.js +48 -0
  892. package/dist/src/backend/services/workspace/service/lifecycle/workspace-run-script.service.js.map +1 -0
  893. package/dist/src/backend/services/workspace/service/lifecycle/workspace-state-capabilities.test.d.ts +2 -0
  894. package/dist/src/backend/services/workspace/service/lifecycle/workspace-state-capabilities.test.d.ts.map +1 -0
  895. package/dist/src/backend/services/workspace/service/lifecycle/workspace-state-capabilities.test.js +119 -0
  896. package/dist/src/backend/services/workspace/service/lifecycle/workspace-state-capabilities.test.js.map +1 -0
  897. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.d.ts +2 -0
  898. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.d.ts.map +1 -0
  899. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.js +11 -0
  900. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.js.map +1 -0
  901. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.test.d.ts +2 -0
  902. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.test.d.ts.map +1 -0
  903. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.test.js +24 -0
  904. package/dist/src/backend/services/workspace/service/pr-discovery-schedule.test.js.map +1 -0
  905. package/dist/src/backend/services/workspace/service/query/project-management.service.d.ts +11 -73
  906. package/dist/src/backend/services/workspace/service/query/project-management.service.d.ts.map +1 -1
  907. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.d.ts +276 -0
  908. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.d.ts.map +1 -0
  909. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.js +24 -0
  910. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.js.map +1 -0
  911. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.test.d.ts +2 -0
  912. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.test.d.ts.map +1 -0
  913. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.test.js +39 -0
  914. package/dist/src/backend/services/workspace/service/query/workspace-maintenance.service.test.js.map +1 -0
  915. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.d.ts +58 -0
  916. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.d.ts.map +1 -0
  917. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.js +24 -0
  918. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.js.map +1 -0
  919. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.test.d.ts +2 -0
  920. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.test.d.ts.map +1 -0
  921. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.test.js +28 -0
  922. package/dist/src/backend/services/workspace/service/query/workspace-notification.service.test.js.map +1 -0
  923. package/dist/src/backend/services/workspace/service/query/workspace-query.service.d.ts +32 -229
  924. package/dist/src/backend/services/workspace/service/query/workspace-query.service.d.ts.map +1 -1
  925. package/dist/src/backend/services/workspace/service/query/workspace-query.service.js +93 -192
  926. package/dist/src/backend/services/workspace/service/query/workspace-query.service.js.map +1 -1
  927. package/dist/src/backend/services/workspace/service/query/workspace-query.service.test.js +214 -157
  928. package/dist/src/backend/services/workspace/service/query/workspace-query.service.test.js.map +1 -1
  929. package/dist/src/backend/services/{workspace-snapshot-store.service.d.ts → workspace/service/snapshot/workspace-snapshot-store.service.d.ts} +25 -45
  930. package/dist/src/backend/services/workspace/service/snapshot/workspace-snapshot-store.service.d.ts.map +1 -0
  931. package/dist/src/backend/services/{workspace-snapshot-store.service.js → workspace/service/snapshot/workspace-snapshot-store.service.js} +144 -31
  932. package/dist/src/backend/services/workspace/service/snapshot/workspace-snapshot-store.service.js.map +1 -0
  933. package/dist/src/backend/services/workspace/service/snapshot/workspace-snapshot-store.service.test.d.ts.map +1 -0
  934. package/dist/src/backend/services/{workspace-snapshot-store.service.test.js → workspace/service/snapshot/workspace-snapshot-store.service.test.js} +229 -18
  935. package/dist/src/backend/services/workspace/service/snapshot/workspace-snapshot-store.service.test.js.map +1 -0
  936. package/dist/src/backend/services/workspace/service/state/kanban-state.d.ts +8 -21
  937. package/dist/src/backend/services/workspace/service/state/kanban-state.d.ts.map +1 -1
  938. package/dist/src/backend/services/workspace/service/state/kanban-state.js +15 -86
  939. package/dist/src/backend/services/workspace/service/state/kanban-state.js.map +1 -1
  940. package/dist/src/backend/services/workspace/service/state/kanban-state.test.js +56 -208
  941. package/dist/src/backend/services/workspace/service/state/kanban-state.test.js.map +1 -1
  942. package/dist/src/backend/services/workspace/service/workspace-domain-exports.test.js +18 -4
  943. package/dist/src/backend/services/workspace/service/workspace-domain-exports.test.js.map +1 -1
  944. package/dist/src/backend/services/workspace/service/worktree/git-clone.service.d.ts.map +1 -0
  945. package/dist/src/backend/services/{git-clone.service.js → workspace/service/worktree/git-clone.service.js} +4 -4
  946. package/dist/src/backend/services/workspace/service/worktree/git-clone.service.js.map +1 -0
  947. package/dist/src/backend/services/workspace/service/worktree/git-clone.service.test.d.ts.map +1 -0
  948. package/dist/src/backend/services/{git-clone.service.test.js → workspace/service/worktree/git-clone.service.test.js} +1 -1
  949. package/dist/src/backend/services/workspace/service/worktree/git-clone.service.test.js.map +1 -0
  950. package/dist/src/backend/services/{git-ops.service.d.ts → workspace/service/worktree/git-ops.service.d.ts} +3 -4
  951. package/dist/src/backend/services/workspace/service/worktree/git-ops.service.d.ts.map +1 -0
  952. package/dist/src/backend/services/{git-ops.service.js → workspace/service/worktree/git-ops.service.js} +55 -33
  953. package/dist/src/backend/services/workspace/service/worktree/git-ops.service.js.map +1 -0
  954. package/dist/src/backend/services/workspace/service/worktree/git-ops.service.test.d.ts.map +1 -0
  955. package/dist/src/backend/services/{git-ops.service.test.js → workspace/service/worktree/git-ops.service.test.js} +89 -16
  956. package/dist/src/backend/services/workspace/service/worktree/git-ops.service.test.js.map +1 -0
  957. package/dist/src/backend/services/workspace/service/worktree/worktree-init.test.js +28 -20
  958. package/dist/src/backend/services/workspace/service/worktree/worktree-init.test.js.map +1 -1
  959. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.d.ts.map +1 -1
  960. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.js +3 -1
  961. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.js.map +1 -1
  962. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.test.js +66 -2
  963. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.test.js.map +1 -1
  964. package/dist/src/backend/services/workspace/types.d.ts +32 -0
  965. package/dist/src/backend/services/workspace/types.d.ts.map +1 -0
  966. package/dist/src/backend/services/workspace/types.js +2 -0
  967. package/dist/src/backend/services/workspace/types.js.map +1 -0
  968. package/dist/src/backend/services/workspace-depth-triggers.integration.test.d.ts +2 -0
  969. package/dist/src/backend/services/workspace-depth-triggers.integration.test.d.ts.map +1 -0
  970. package/dist/src/backend/services/workspace-depth-triggers.integration.test.js +68 -0
  971. package/dist/src/backend/services/workspace-depth-triggers.integration.test.js.map +1 -0
  972. package/dist/src/backend/services/workspace-git-state.service.d.ts +99 -0
  973. package/dist/src/backend/services/workspace-git-state.service.d.ts.map +1 -0
  974. package/dist/src/backend/services/workspace-git-state.service.js +393 -0
  975. package/dist/src/backend/services/workspace-git-state.service.js.map +1 -0
  976. package/dist/src/backend/services/workspace-git-state.service.test.d.ts +2 -0
  977. package/dist/src/backend/services/workspace-git-state.service.test.d.ts.map +1 -0
  978. package/dist/src/backend/services/workspace-git-state.service.test.js +476 -0
  979. package/dist/src/backend/services/workspace-git-state.service.test.js.map +1 -0
  980. package/dist/src/backend/testing/check-prisma-versions-script.test.d.ts +2 -0
  981. package/dist/src/backend/testing/check-prisma-versions-script.test.d.ts.map +1 -0
  982. package/dist/src/backend/testing/check-prisma-versions-script.test.js +52 -0
  983. package/dist/src/backend/testing/check-prisma-versions-script.test.js.map +1 -0
  984. package/dist/src/backend/testing/typescript-import-test-utils.d.ts +11 -0
  985. package/dist/src/backend/testing/typescript-import-test-utils.d.ts.map +1 -0
  986. package/dist/src/backend/testing/typescript-import-test-utils.js +47 -0
  987. package/dist/src/backend/testing/typescript-import-test-utils.js.map +1 -0
  988. package/dist/src/backend/trpc/admin.router.test.js +25 -33
  989. package/dist/src/backend/trpc/admin.router.test.js.map +1 -1
  990. package/dist/src/backend/trpc/admin.trpc.d.ts +9 -9
  991. package/dist/src/backend/trpc/admin.trpc.d.ts.map +1 -1
  992. package/dist/src/backend/trpc/admin.trpc.js +14 -18
  993. package/dist/src/backend/trpc/admin.trpc.js.map +1 -1
  994. package/dist/src/backend/trpc/application-error-mapper.d.ts +4 -0
  995. package/dist/src/backend/trpc/application-error-mapper.d.ts.map +1 -0
  996. package/dist/src/backend/trpc/application-error-mapper.js +16 -0
  997. package/dist/src/backend/trpc/application-error-mapper.js.map +1 -0
  998. package/dist/src/backend/trpc/application-error-mapper.test.d.ts +2 -0
  999. package/dist/src/backend/trpc/application-error-mapper.test.d.ts.map +1 -0
  1000. package/dist/src/backend/trpc/application-error-mapper.test.js +23 -0
  1001. package/dist/src/backend/trpc/application-error-mapper.test.js.map +1 -0
  1002. package/dist/src/backend/trpc/auto-iteration.trpc.d.ts +1 -1
  1003. package/dist/src/backend/trpc/auto-iteration.trpc.d.ts.map +1 -1
  1004. package/dist/src/backend/trpc/auto-iteration.trpc.js +20 -12
  1005. package/dist/src/backend/trpc/auto-iteration.trpc.js.map +1 -1
  1006. package/dist/src/backend/trpc/auto-iteration.trpc.test.js +10 -9
  1007. package/dist/src/backend/trpc/auto-iteration.trpc.test.js.map +1 -1
  1008. package/dist/src/backend/trpc/closed-sessions.router.test.js +3 -4
  1009. package/dist/src/backend/trpc/closed-sessions.router.test.js.map +1 -1
  1010. package/dist/src/backend/trpc/closed-sessions.trpc.d.ts +5 -5
  1011. package/dist/src/backend/trpc/closed-sessions.trpc.d.ts.map +1 -1
  1012. package/dist/src/backend/trpc/closed-sessions.trpc.js +8 -4
  1013. package/dist/src/backend/trpc/closed-sessions.trpc.js.map +1 -1
  1014. package/dist/src/backend/trpc/context-dependencies.test.d.ts +2 -0
  1015. package/dist/src/backend/trpc/context-dependencies.test.d.ts.map +1 -0
  1016. package/dist/src/backend/trpc/context-dependencies.test.js +134 -0
  1017. package/dist/src/backend/trpc/context-dependencies.test.js.map +1 -0
  1018. package/dist/src/backend/trpc/decision-log.router.test.js +11 -8
  1019. package/dist/src/backend/trpc/decision-log.router.test.js.map +1 -1
  1020. package/dist/src/backend/trpc/decision-log.trpc.d.ts +3 -3
  1021. package/dist/src/backend/trpc/decision-log.trpc.d.ts.map +1 -1
  1022. package/dist/src/backend/trpc/decision-log.trpc.js +6 -7
  1023. package/dist/src/backend/trpc/decision-log.trpc.js.map +1 -1
  1024. package/dist/src/backend/trpc/github.router.test.js +12 -6
  1025. package/dist/src/backend/trpc/github.router.test.js.map +1 -1
  1026. package/dist/src/backend/trpc/github.trpc.d.ts.map +1 -1
  1027. package/dist/src/backend/trpc/github.trpc.js +11 -8
  1028. package/dist/src/backend/trpc/github.trpc.js.map +1 -1
  1029. package/dist/src/backend/trpc/index.d.ts +424 -1301
  1030. package/dist/src/backend/trpc/index.d.ts.map +1 -1
  1031. package/dist/src/backend/trpc/index.js +0 -1
  1032. package/dist/src/backend/trpc/index.js.map +1 -1
  1033. package/dist/src/backend/trpc/issue-filter.d.ts.map +1 -1
  1034. package/dist/src/backend/trpc/issue-filter.js.map +1 -1
  1035. package/dist/src/backend/trpc/linear.router.test.js +21 -21
  1036. package/dist/src/backend/trpc/linear.router.test.js.map +1 -1
  1037. package/dist/src/backend/trpc/linear.trpc.d.ts +4 -4
  1038. package/dist/src/backend/trpc/linear.trpc.d.ts.map +1 -1
  1039. package/dist/src/backend/trpc/linear.trpc.js +9 -10
  1040. package/dist/src/backend/trpc/linear.trpc.js.map +1 -1
  1041. package/dist/src/backend/trpc/periodic-task.trpc.d.ts +13 -13
  1042. package/dist/src/backend/trpc/periodic-task.trpc.d.ts.map +1 -1
  1043. package/dist/src/backend/trpc/periodic-task.trpc.js +17 -17
  1044. package/dist/src/backend/trpc/periodic-task.trpc.js.map +1 -1
  1045. package/dist/src/backend/trpc/project.router.test.js +20 -21
  1046. package/dist/src/backend/trpc/project.router.test.js.map +1 -1
  1047. package/dist/src/backend/trpc/project.trpc.d.ts +15 -77
  1048. package/dist/src/backend/trpc/project.trpc.d.ts.map +1 -1
  1049. package/dist/src/backend/trpc/project.trpc.js +32 -30
  1050. package/dist/src/backend/trpc/project.trpc.js.map +1 -1
  1051. package/dist/src/backend/trpc/session.router.test.js +33 -25
  1052. package/dist/src/backend/trpc/session.router.test.js.map +1 -1
  1053. package/dist/src/backend/trpc/session.trpc.d.ts +34 -425
  1054. package/dist/src/backend/trpc/session.trpc.d.ts.map +1 -1
  1055. package/dist/src/backend/trpc/session.trpc.js +37 -34
  1056. package/dist/src/backend/trpc/session.trpc.js.map +1 -1
  1057. package/dist/src/backend/trpc/trpc.d.ts +2 -4
  1058. package/dist/src/backend/trpc/trpc.d.ts.map +1 -1
  1059. package/dist/src/backend/trpc/trpc.js +14 -12
  1060. package/dist/src/backend/trpc/trpc.js.map +1 -1
  1061. package/dist/src/backend/trpc/trpc.test.js +58 -1
  1062. package/dist/src/backend/trpc/trpc.test.js.map +1 -1
  1063. package/dist/src/backend/trpc/user-settings.router.test.js +27 -14
  1064. package/dist/src/backend/trpc/user-settings.router.test.js.map +1 -1
  1065. package/dist/src/backend/trpc/user-settings.trpc.d.ts +6 -3
  1066. package/dist/src/backend/trpc/user-settings.trpc.d.ts.map +1 -1
  1067. package/dist/src/backend/trpc/user-settings.trpc.js +13 -14
  1068. package/dist/src/backend/trpc/user-settings.trpc.js.map +1 -1
  1069. package/dist/src/backend/trpc/workspace/children.router.test.d.ts +2 -0
  1070. package/dist/src/backend/trpc/workspace/children.router.test.d.ts.map +1 -0
  1071. package/dist/src/backend/trpc/workspace/children.router.test.js +297 -0
  1072. package/dist/src/backend/trpc/workspace/children.router.test.js.map +1 -0
  1073. package/dist/src/backend/trpc/workspace/children.trpc.d.ts +120 -0
  1074. package/dist/src/backend/trpc/workspace/children.trpc.d.ts.map +1 -0
  1075. package/dist/src/backend/trpc/workspace/children.trpc.js +187 -0
  1076. package/dist/src/backend/trpc/workspace/children.trpc.js.map +1 -0
  1077. package/dist/src/backend/trpc/workspace/composition.test.d.ts +2 -0
  1078. package/dist/src/backend/trpc/workspace/composition.test.d.ts.map +1 -0
  1079. package/dist/src/backend/trpc/workspace/composition.test.js +54 -0
  1080. package/dist/src/backend/trpc/workspace/composition.test.js.map +1 -0
  1081. package/dist/src/backend/trpc/workspace/files.router.test.js +6 -0
  1082. package/dist/src/backend/trpc/workspace/files.router.test.js.map +1 -1
  1083. package/dist/src/backend/trpc/workspace/files.trpc.d.ts.map +1 -1
  1084. package/dist/src/backend/trpc/workspace/files.trpc.js +14 -13
  1085. package/dist/src/backend/trpc/workspace/files.trpc.js.map +1 -1
  1086. package/dist/src/backend/trpc/workspace/git.router.test.js +137 -147
  1087. package/dist/src/backend/trpc/workspace/git.router.test.js.map +1 -1
  1088. package/dist/src/backend/trpc/workspace/git.trpc.d.ts +2 -2
  1089. package/dist/src/backend/trpc/workspace/git.trpc.d.ts.map +1 -1
  1090. package/dist/src/backend/trpc/workspace/git.trpc.js +53 -75
  1091. package/dist/src/backend/trpc/workspace/git.trpc.js.map +1 -1
  1092. package/dist/src/backend/trpc/workspace/ide.router.test.js +10 -9
  1093. package/dist/src/backend/trpc/workspace/ide.router.test.js.map +1 -1
  1094. package/dist/src/backend/trpc/workspace/ide.trpc.d.ts.map +1 -1
  1095. package/dist/src/backend/trpc/workspace/ide.trpc.js +4 -4
  1096. package/dist/src/backend/trpc/workspace/ide.trpc.js.map +1 -1
  1097. package/dist/src/backend/trpc/workspace/init.router.test.js +18 -41
  1098. package/dist/src/backend/trpc/workspace/init.router.test.js.map +1 -1
  1099. package/dist/src/backend/trpc/workspace/init.trpc.d.ts +60 -44
  1100. package/dist/src/backend/trpc/workspace/init.trpc.d.ts.map +1 -1
  1101. package/dist/src/backend/trpc/workspace/init.trpc.js +14 -11
  1102. package/dist/src/backend/trpc/workspace/init.trpc.js.map +1 -1
  1103. package/dist/src/backend/trpc/workspace/run-script.router.test.js +6 -10
  1104. package/dist/src/backend/trpc/workspace/run-script.router.test.js.map +1 -1
  1105. package/dist/src/backend/trpc/workspace/run-script.trpc.d.ts.map +1 -1
  1106. package/dist/src/backend/trpc/workspace/run-script.trpc.js +3 -4
  1107. package/dist/src/backend/trpc/workspace/run-script.trpc.js.map +1 -1
  1108. package/dist/src/backend/trpc/workspace/workspace-helpers.d.ts +98 -87
  1109. package/dist/src/backend/trpc/workspace/workspace-helpers.d.ts.map +1 -1
  1110. package/dist/src/backend/trpc/workspace/workspace-helpers.js +7 -8
  1111. package/dist/src/backend/trpc/workspace/workspace-helpers.js.map +1 -1
  1112. package/dist/src/backend/trpc/workspace/workspace-helpers.test.js +17 -15
  1113. package/dist/src/backend/trpc/workspace/workspace-helpers.test.js.map +1 -1
  1114. package/dist/src/backend/trpc/workspace.router.test.js +153 -282
  1115. package/dist/src/backend/trpc/workspace.router.test.js.map +1 -1
  1116. package/dist/src/backend/trpc/workspace.trpc.d.ts +1023 -781
  1117. package/dist/src/backend/trpc/workspace.trpc.d.ts.map +1 -1
  1118. package/dist/src/backend/trpc/workspace.trpc.js +53 -294
  1119. package/dist/src/backend/trpc/workspace.trpc.js.map +1 -1
  1120. package/dist/src/cli/runtime-utils.d.ts.map +1 -1
  1121. package/dist/src/cli/runtime-utils.js +4 -0
  1122. package/dist/src/cli/runtime-utils.js.map +1 -1
  1123. package/dist/src/cli/runtime-utils.test.d.ts +2 -0
  1124. package/dist/src/cli/runtime-utils.test.d.ts.map +1 -0
  1125. package/dist/src/cli/runtime-utils.test.js +28 -0
  1126. package/dist/src/cli/runtime-utils.test.js.map +1 -0
  1127. package/dist/src/shared/acp-protocol/protocol/renderer-window.d.ts +1 -0
  1128. package/dist/src/shared/acp-protocol/protocol/renderer-window.d.ts.map +1 -1
  1129. package/dist/src/shared/acp-protocol/protocol/renderer-window.js +13 -1
  1130. package/dist/src/shared/acp-protocol/protocol/renderer-window.js.map +1 -1
  1131. package/dist/src/shared/acp-protocol/protocol/websocket.d.ts +9 -0
  1132. package/dist/src/shared/acp-protocol/protocol/websocket.d.ts.map +1 -1
  1133. package/dist/src/shared/acp-protocol/protocol/websocket.js +1 -0
  1134. package/dist/src/shared/acp-protocol/protocol/websocket.js.map +1 -1
  1135. package/dist/src/shared/core/enums.d.ts +5 -0
  1136. package/dist/src/shared/core/enums.d.ts.map +1 -1
  1137. package/dist/src/shared/core/enums.js +4 -0
  1138. package/dist/src/shared/core/enums.js.map +1 -1
  1139. package/dist/src/shared/core/index.d.ts +3 -2
  1140. package/dist/src/shared/core/index.d.ts.map +1 -1
  1141. package/dist/src/shared/core/index.js +3 -2
  1142. package/dist/src/shared/core/index.js.map +1 -1
  1143. package/dist/src/shared/core/ratchet-state.d.ts +10 -0
  1144. package/dist/src/shared/core/ratchet-state.d.ts.map +1 -0
  1145. package/dist/src/shared/core/ratchet-state.js +32 -0
  1146. package/dist/src/shared/core/ratchet-state.js.map +1 -0
  1147. package/dist/src/shared/core/workspace-sidebar-status.d.ts +4 -2
  1148. package/dist/src/shared/core/workspace-sidebar-status.d.ts.map +1 -1
  1149. package/dist/src/shared/core/workspace-sidebar-status.js +11 -0
  1150. package/dist/src/shared/core/workspace-sidebar-status.js.map +1 -1
  1151. package/dist/src/shared/schemas/export-data.schema.d.ts +22 -24
  1152. package/dist/src/shared/schemas/export-data.schema.d.ts.map +1 -1
  1153. package/dist/src/shared/schemas/export-data.schema.js +4 -4
  1154. package/dist/src/shared/schemas/export-data.schema.js.map +1 -1
  1155. package/dist/src/shared/session-runtime.d.ts +6 -3
  1156. package/dist/src/shared/session-runtime.d.ts.map +1 -1
  1157. package/dist/src/shared/session-runtime.js +10 -0
  1158. package/dist/src/shared/session-runtime.js.map +1 -1
  1159. package/dist/src/shared/workspace-flow-state.d.ts +4 -2
  1160. package/dist/src/shared/workspace-flow-state.d.ts.map +1 -1
  1161. package/dist/src/shared/workspace-flow-state.js +16 -1
  1162. package/dist/src/shared/workspace-flow-state.js.map +1 -1
  1163. package/dist/src/shared/workspace-snapshot.d.ts +1020 -0
  1164. package/dist/src/shared/workspace-snapshot.d.ts.map +1 -0
  1165. package/dist/src/shared/workspace-snapshot.js +113 -0
  1166. package/dist/src/shared/workspace-snapshot.js.map +1 -0
  1167. package/dist/src/shared/workspace-status-reason.d.ts +6 -3
  1168. package/dist/src/shared/workspace-status-reason.d.ts.map +1 -1
  1169. package/dist/src/shared/workspace-status-reason.js +35 -0
  1170. package/dist/src/shared/workspace-status-reason.js.map +1 -1
  1171. package/dist/src/test-utils/application-graph.d.ts +69 -0
  1172. package/dist/src/test-utils/application-graph.d.ts.map +1 -0
  1173. package/dist/src/test-utils/application-graph.js +360 -0
  1174. package/dist/src/test-utils/application-graph.js.map +1 -0
  1175. package/dist/tsconfig.backend.tsbuildinfo +1 -1
  1176. package/package.json +91 -154
  1177. package/prisma/migrations/20260717150000_add_pr_discovery_schedule/migration.sql +8 -0
  1178. package/prisma/migrations/20260717160000_add_ratchet_review_trigger_mode/migration.sql +2 -0
  1179. package/prisma/migrations/20260726112950_drop_cached_kanban_column/migration.sql +132 -0
  1180. package/prisma/migrations/20260726135402_split_workspace_ratchet/migration.sql +149 -0
  1181. package/prisma/migrations/20260726181500_split_workspace_pr/migration.sql +142 -0
  1182. package/prisma/migrations/20260726200000_derive_ratchet_state/migration.sql +101 -0
  1183. package/prisma/migrations/20260726220000_split_workspace_run_script/migration.sql +131 -0
  1184. package/prisma/migrations/20260726230000_split_workspace_auto_iteration/migration.sql +123 -0
  1185. package/prisma/schema.prisma +225 -56
  1186. package/dist/client/assets/arc-BZLkggFv.js +0 -1
  1187. package/dist/client/assets/architectureDiagram-3BPJPVTR-0dfKDS01.js +0 -36
  1188. package/dist/client/assets/blockDiagram-GPEHLZMM-kPnqTUvo.js +0 -132
  1189. package/dist/client/assets/channel-76ipWABm.js +0 -1
  1190. package/dist/client/assets/chunk-AQP2D5EJ-Cp_aqtpa.js +0 -231
  1191. package/dist/client/assets/classDiagram-4FO5ZUOK-FuxnCTTL.js +0 -1
  1192. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-FuxnCTTL.js +0 -1
  1193. package/dist/client/assets/cytoscape.esm-DkOyvmE4.js +0 -321
  1194. package/dist/client/assets/dagre-BM42HDAG-CDuVn0gJ.js +0 -4
  1195. package/dist/client/assets/diagram-2AECGRRQ-DkPx69Lt.js +0 -43
  1196. package/dist/client/assets/diagram-5GNKFQAL-CchGithI.js +0 -10
  1197. package/dist/client/assets/diagram-LMA3HP47-hKBATxk5.js +0 -24
  1198. package/dist/client/assets/diagram-OG6HWLK6-Bga7_0RO.js +0 -24
  1199. package/dist/client/assets/erDiagram-TEJ5UH35-D26at0F2.js +0 -85
  1200. package/dist/client/assets/flowDiagram-I6XJVG4X-Qf-TRw4O.js +0 -162
  1201. package/dist/client/assets/ganttDiagram-6RSMTGT7-CmRredaI.js +0 -292
  1202. package/dist/client/assets/gitGraphDiagram-PVQCEYII-QQP7Sl8F.js +0 -106
  1203. package/dist/client/assets/index-Db00yCQO.js +0 -672
  1204. package/dist/client/assets/index-kItkrW7R.css +0 -1
  1205. package/dist/client/assets/infoDiagram-5YYISTIA-YKWLGpI0.js +0 -2
  1206. package/dist/client/assets/katex-DHMw6HUq.js +0 -257
  1207. package/dist/client/assets/layout-SsrduOYp.js +0 -1
  1208. package/dist/client/assets/pieDiagram-4H26LBE5-PIN8tU1a.js +0 -30
  1209. package/dist/client/assets/sankeyDiagram-5OEKKPKP-D2SFbF9j.js +0 -40
  1210. package/dist/client/assets/sequenceDiagram-3UESZ5HK-BsxZzGc-.js +0 -162
  1211. package/dist/client/assets/stateDiagram-AJRCARHV-DtZoFzqs.js +0 -1
  1212. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Dhf9Ohaz.js +0 -1
  1213. package/dist/client/assets/vennDiagram-CIIHVFJN-Biz7Z4hM.js +0 -34
  1214. package/dist/client/assets/wardley-L42UT6IY-B-ZjZQUx.js +0 -161
  1215. package/dist/client/assets/xychartDiagram-2RQKCTM6-DuWAbR0h.js +0 -7
  1216. package/dist/src/backend/clients/index.d.ts +0 -2
  1217. package/dist/src/backend/clients/index.d.ts.map +0 -1
  1218. package/dist/src/backend/clients/index.js +0 -2
  1219. package/dist/src/backend/clients/index.js.map +0 -1
  1220. package/dist/src/backend/interceptors/branch-rename.interceptor.d.ts +0 -3
  1221. package/dist/src/backend/interceptors/branch-rename.interceptor.d.ts.map +0 -1
  1222. package/dist/src/backend/interceptors/branch-rename.interceptor.js +0 -43
  1223. package/dist/src/backend/interceptors/branch-rename.interceptor.js.map +0 -1
  1224. package/dist/src/backend/interceptors/branch-rename.interceptor.test.d.ts +0 -2
  1225. package/dist/src/backend/interceptors/branch-rename.interceptor.test.d.ts.map +0 -1
  1226. package/dist/src/backend/interceptors/branch-rename.interceptor.test.js +0 -80
  1227. package/dist/src/backend/interceptors/branch-rename.interceptor.test.js.map +0 -1
  1228. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.d.ts +0 -6
  1229. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.d.ts.map +0 -1
  1230. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.js +0 -222
  1231. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.js.map +0 -1
  1232. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.test.d.ts +0 -2
  1233. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.test.d.ts.map +0 -1
  1234. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.test.js +0 -340
  1235. package/dist/src/backend/interceptors/pre-pr-rename.interceptor.test.js.map +0 -1
  1236. package/dist/src/backend/interceptors/pre-push-rename.interceptor.d.ts +0 -4
  1237. package/dist/src/backend/interceptors/pre-push-rename.interceptor.d.ts.map +0 -1
  1238. package/dist/src/backend/interceptors/pre-push-rename.interceptor.js.map +0 -1
  1239. package/dist/src/backend/interceptors/pre-push-rename.interceptor.test.d.ts +0 -2
  1240. package/dist/src/backend/interceptors/pre-push-rename.interceptor.test.d.ts.map +0 -1
  1241. package/dist/src/backend/interceptors/pre-push-rename.interceptor.test.js +0 -162
  1242. package/dist/src/backend/interceptors/pre-push-rename.interceptor.test.js.map +0 -1
  1243. package/dist/src/backend/orchestration/decision-log-query.service.d.ts.map +0 -1
  1244. package/dist/src/backend/orchestration/decision-log-query.service.js.map +0 -1
  1245. package/dist/src/backend/orchestration/decision-log-query.service.test.d.ts +0 -2
  1246. package/dist/src/backend/orchestration/decision-log-query.service.test.d.ts.map +0 -1
  1247. package/dist/src/backend/orchestration/decision-log-query.service.test.js +0 -44
  1248. package/dist/src/backend/orchestration/decision-log-query.service.test.js.map +0 -1
  1249. package/dist/src/backend/routers/websocket/dev-logs.handler.d.ts +0 -5
  1250. package/dist/src/backend/routers/websocket/dev-logs.handler.d.ts.map +0 -1
  1251. package/dist/src/backend/routers/websocket/dev-logs.handler.js +0 -15
  1252. package/dist/src/backend/routers/websocket/dev-logs.handler.js.map +0 -1
  1253. package/dist/src/backend/routers/websocket/dev-logs.handler.test.d.ts +0 -2
  1254. package/dist/src/backend/routers/websocket/dev-logs.handler.test.d.ts.map +0 -1
  1255. package/dist/src/backend/routers/websocket/dev-logs.handler.test.js.map +0 -1
  1256. package/dist/src/backend/routers/websocket/post-run-logs.handler.d.ts +0 -5
  1257. package/dist/src/backend/routers/websocket/post-run-logs.handler.d.ts.map +0 -1
  1258. package/dist/src/backend/routers/websocket/post-run-logs.handler.js +0 -15
  1259. package/dist/src/backend/routers/websocket/post-run-logs.handler.js.map +0 -1
  1260. package/dist/src/backend/routers/websocket/post-run-logs.handler.test.d.ts +0 -2
  1261. package/dist/src/backend/routers/websocket/post-run-logs.handler.test.d.ts.map +0 -1
  1262. package/dist/src/backend/routers/websocket/post-run-logs.handler.test.js.map +0 -1
  1263. package/dist/src/backend/services/factory-config.service.d.ts.map +0 -1
  1264. package/dist/src/backend/services/factory-config.service.js.map +0 -1
  1265. package/dist/src/backend/services/factory-config.service.test.d.ts.map +0 -1
  1266. package/dist/src/backend/services/factory-config.service.test.js.map +0 -1
  1267. package/dist/src/backend/services/git-clone.service.d.ts.map +0 -1
  1268. package/dist/src/backend/services/git-clone.service.js.map +0 -1
  1269. package/dist/src/backend/services/git-clone.service.test.d.ts.map +0 -1
  1270. package/dist/src/backend/services/git-clone.service.test.js.map +0 -1
  1271. package/dist/src/backend/services/git-ops.service.d.ts.map +0 -1
  1272. package/dist/src/backend/services/git-ops.service.js.map +0 -1
  1273. package/dist/src/backend/services/git-ops.service.test.d.ts.map +0 -1
  1274. package/dist/src/backend/services/git-ops.service.test.js.map +0 -1
  1275. package/dist/src/backend/services/github/service/pr-fetch-registry.d.ts +0 -13
  1276. package/dist/src/backend/services/github/service/pr-fetch-registry.d.ts.map +0 -1
  1277. package/dist/src/backend/services/github/service/pr-fetch-registry.js +0 -34
  1278. package/dist/src/backend/services/github/service/pr-fetch-registry.js.map +0 -1
  1279. package/dist/src/backend/services/port-allocation.service.d.ts.map +0 -1
  1280. package/dist/src/backend/services/port-allocation.service.js.map +0 -1
  1281. package/dist/src/backend/services/port-allocation.service.test.d.ts.map +0 -1
  1282. package/dist/src/backend/services/port-allocation.service.test.js.map +0 -1
  1283. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.d.ts +0 -11
  1284. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.d.ts.map +0 -1
  1285. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.js +0 -77
  1286. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.js.map +0 -1
  1287. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.test.d.ts +0 -2
  1288. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.test.d.ts.map +0 -1
  1289. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.test.js +0 -66
  1290. package/dist/src/backend/services/ratchet/service/ratchet-state-machine.test.js.map +0 -1
  1291. package/dist/src/backend/services/run-script-config-persistence.service.d.ts.map +0 -1
  1292. package/dist/src/backend/services/run-script-config-persistence.service.js.map +0 -1
  1293. package/dist/src/backend/services/run-script-config-persistence.service.test.d.ts.map +0 -1
  1294. package/dist/src/backend/services/run-script-config-persistence.service.test.js.map +0 -1
  1295. package/dist/src/backend/services/run-script-proxy.service.d.ts.map +0 -1
  1296. package/dist/src/backend/services/run-script-proxy.service.js.map +0 -1
  1297. package/dist/src/backend/services/run-script-proxy.service.test.d.ts.map +0 -1
  1298. package/dist/src/backend/services/run-script-proxy.service.test.js.map +0 -1
  1299. package/dist/src/backend/services/session/service/lifecycle/session.service.coverage.test.d.ts +0 -2
  1300. package/dist/src/backend/services/session/service/lifecycle/session.service.coverage.test.d.ts.map +0 -1
  1301. package/dist/src/backend/services/session/service/lifecycle/session.service.coverage.test.js +0 -279
  1302. package/dist/src/backend/services/session/service/lifecycle/session.service.coverage.test.js.map +0 -1
  1303. package/dist/src/backend/services/workspace/service/query/user-settings-query.service.d.ts +0 -24
  1304. package/dist/src/backend/services/workspace/service/query/user-settings-query.service.d.ts.map +0 -1
  1305. package/dist/src/backend/services/workspace/service/query/user-settings-query.service.js +0 -17
  1306. package/dist/src/backend/services/workspace/service/query/user-settings-query.service.js.map +0 -1
  1307. package/dist/src/backend/services/workspace-snapshot-store.service.d.ts.map +0 -1
  1308. package/dist/src/backend/services/workspace-snapshot-store.service.js.map +0 -1
  1309. package/dist/src/backend/services/workspace-snapshot-store.service.test.d.ts.map +0 -1
  1310. package/dist/src/backend/services/workspace-snapshot-store.service.test.js.map +0 -1
  1311. package/dist/src/backend/trpc/procedures/index.d.ts +0 -2
  1312. package/dist/src/backend/trpc/procedures/index.d.ts.map +0 -1
  1313. package/dist/src/backend/trpc/procedures/index.js +0 -2
  1314. package/dist/src/backend/trpc/procedures/index.js.map +0 -1
  1315. package/dist/src/backend/trpc/procedures/project-scoped.d.ts +0 -7
  1316. package/dist/src/backend/trpc/procedures/project-scoped.d.ts.map +0 -1
  1317. package/dist/src/backend/trpc/procedures/project-scoped.js +0 -18
  1318. package/dist/src/backend/trpc/procedures/project-scoped.js.map +0 -1
  1319. package/dist/src/backend/trpc/procedures/project-scoped.test.d.ts +0 -2
  1320. package/dist/src/backend/trpc/procedures/project-scoped.test.d.ts.map +0 -1
  1321. package/dist/src/backend/trpc/procedures/project-scoped.test.js +0 -17
  1322. package/dist/src/backend/trpc/procedures/project-scoped.test.js.map +0 -1
  1323. /package/dist/src/backend/services/{factory-config.service.test.d.ts → run-script/service/factory-config.service.test.d.ts} +0 -0
  1324. /package/dist/src/backend/services/{port-allocation.service.d.ts → run-script/service/port-allocation.service.d.ts} +0 -0
  1325. /package/dist/src/backend/services/{port-allocation.service.js → run-script/service/port-allocation.service.js} +0 -0
  1326. /package/dist/src/backend/services/{port-allocation.service.test.d.ts → run-script/service/port-allocation.service.test.d.ts} +0 -0
  1327. /package/dist/src/backend/services/{port-allocation.service.test.js → run-script/service/port-allocation.service.test.js} +0 -0
  1328. /package/dist/src/backend/services/{run-script-config-persistence.service.test.d.ts → run-script/service/run-script-config-persistence.service.test.d.ts} +0 -0
  1329. /package/dist/src/backend/services/{run-script-proxy.service.test.d.ts → run-script/service/run-script-proxy.service.test.d.ts} +0 -0
  1330. /package/dist/src/backend/services/{workspace-snapshot-store.service.test.d.ts → workspace/service/snapshot/workspace-snapshot-store.service.test.d.ts} +0 -0
  1331. /package/dist/src/backend/services/{git-clone.service.d.ts → workspace/service/worktree/git-clone.service.d.ts} +0 -0
  1332. /package/dist/src/backend/services/{git-clone.service.test.d.ts → workspace/service/worktree/git-clone.service.test.d.ts} +0 -0
  1333. /package/dist/src/backend/services/{git-ops.service.test.d.ts → workspace/service/worktree/git-ops.service.test.d.ts} +0 -0
@@ -3,26 +3,29 @@ import { SERVICE_TIMEOUT_MS } from '../../../../backend/services/constants.js';
3
3
  import { CIStatus, RatchetState, SessionStatus } from '../../../../shared/core/index.js';
4
4
  import { unsafeCoerce } from '../../../../test-utils/unsafe-coerce.js';
5
5
  vi.mock('@/backend/services/workspace', () => ({
6
- workspaceAccessor: {
6
+ workspaceDataService: {
7
7
  findById: vi.fn(),
8
- findWithPRsForRatchet: vi.fn(),
9
- findForRatchetById: vi.fn(),
10
- update: vi.fn(),
11
- transitionRatchetStateIfEnabled: vi.fn(),
12
- settleRatchetIdleWhileDisabled: vi.fn(),
13
- recordRatchetDispatchIfEnabled: vi.fn(),
14
- adoptRatchetActiveSessionIfEnabled: vi.fn(),
15
- recordRatchetSessionEnd: vi.fn(),
8
+ },
9
+ workspaceRatchetService: {
10
+ findCandidates: vi.fn(),
11
+ findCandidateById: vi.fn(),
12
+ clearActiveSession: vi.fn(),
13
+ enable: vi.fn(),
14
+ disable: vi.fn(),
15
+ recordCheckIfEnabled: vi.fn(),
16
+ recordDispatchIfEnabled: vi.fn(),
17
+ adoptActiveSessionIfEnabled: vi.fn(),
18
+ recordSessionEnd: vi.fn(),
16
19
  },
17
20
  }));
18
21
  vi.mock('@/backend/services/session', () => ({
19
- agentSessionAccessor: {
20
- findById: vi.fn(),
21
- findByWorkspaceId: vi.fn(),
22
+ sessionDataService: {
23
+ findAgentSessionById: vi.fn(),
24
+ findAgentSessionsByWorkspaceId: vi.fn(),
22
25
  },
23
26
  }));
24
27
  vi.mock('@/backend/services/settings', () => ({
25
- userSettingsAccessor: {
28
+ userSettingsService: {
26
29
  get: vi.fn(),
27
30
  getDefaultSessionProvider: vi.fn(),
28
31
  },
@@ -40,15 +43,16 @@ vi.mock('@/backend/services/logger.service', () => ({
40
43
  error: vi.fn(),
41
44
  }),
42
45
  }));
43
- import { agentSessionAccessor } from '../../../../backend/services/session/index.js';
44
- import { userSettingsAccessor } from '../../../../backend/services/settings/index.js';
45
- import { workspaceAccessor } from '../../../../backend/services/workspace/index.js';
46
+ import { userSettingsService } from '../../../../backend/services/settings/index.js';
47
+ import { workspaceDataService, workspaceRatchetService } from '../../../../backend/services/workspace/index.js';
46
48
  import { fixerSessionService } from './fixer-session.service.js';
47
- import { RATCHET_STATE_CHANGED, RATCHET_TOGGLED, ratchetService, } from './ratchet.service.js';
49
+ import { RATCHET_DISPATCH_CHANGED, RATCHET_STATE_CHANGED, RATCHET_TOGGLED, ratchetService, } from './ratchet.service.js';
48
50
  import { buildRatchetingLogContext } from './ratchet-decision-logging.helpers.js';
49
51
  import { computeDispatchSnapshotKey, computeLatestReviewActivityAtMs, determineRatchetState, } from './ratchet-pr-state.helpers.js';
50
52
  const mockSessionBridge = {
53
+ findSessionById: vi.fn(),
51
54
  findSessionsByWorkspaceId: vi.fn(),
55
+ acquireFixerSession: vi.fn(),
52
56
  isSessionRunning: vi.fn(),
53
57
  isSessionWorking: vi.fn(),
54
58
  stopSession: vi.fn(),
@@ -57,47 +61,70 @@ const mockSessionBridge = {
57
61
  sendSessionMessage: vi.fn(),
58
62
  injectCommittedUserMessage: vi.fn(),
59
63
  };
64
+ const mockWorkspaceBridge = {
65
+ findFixerContext: vi.fn(),
66
+ recordSessionEnd: vi.fn(),
67
+ };
68
+ const fakeCoordinator = { cooldownActive: false, inFlightSkips: 0 };
60
69
  const mockGitHubBridge = {
61
70
  extractPRInfo: vi.fn(),
62
71
  getPRFullDetails: vi.fn(),
63
72
  getReviewComments: vi.fn(),
64
73
  getResolvedReviewCommentIds: vi.fn(),
65
74
  computeCIStatus: vi.fn(),
75
+ computePRState: vi.fn(() => 'OPEN'),
66
76
  getAuthenticatedUsername: vi.fn(),
67
77
  fetchAndComputePRState: vi.fn(),
68
- isRecentlyFetched: vi.fn(),
69
- isFetchInFlight: vi.fn(),
70
- startFetch: vi.fn(),
71
- registerFetch: vi.fn(),
72
- cancelFetch: vi.fn(),
78
+ coordinatePrFetch: vi.fn(async (_workspaceId, fetch, options) => {
79
+ if (fakeCoordinator.inFlightSkips > 0) {
80
+ fakeCoordinator.inFlightSkips -= 1;
81
+ return { status: 'skipped', reason: 'recently_fetched' };
82
+ }
83
+ if (fakeCoordinator.cooldownActive && !options?.ignoreCooldown) {
84
+ return { status: 'skipped', reason: 'recently_fetched' };
85
+ }
86
+ return { status: 'fetched', value: await fetch() };
87
+ }),
73
88
  };
74
89
  const mockSnapshotBridge = {
75
- recordCIObservation: vi.fn(),
90
+ recordPrObservation: vi.fn(),
76
91
  recordCINotification: vi.fn(),
77
92
  recordReviewCheck: vi.fn(),
78
93
  };
94
+ function setRatchetShuttingDown(shuttingDown) {
95
+ const controller = new AbortController();
96
+ if (shuttingDown) {
97
+ controller.abort();
98
+ }
99
+ const internals = unsafeCoerce(ratchetService);
100
+ internals.runSignal = controller.signal;
101
+ internals.stopped = shuttingDown;
102
+ }
79
103
  describe('ratchet service (state-change + idle dispatch)', () => {
80
104
  beforeEach(() => {
81
105
  vi.clearAllMocks();
82
- unsafeCoerce(ratchetService).isShuttingDown = false;
106
+ fakeCoordinator.cooldownActive = false;
107
+ fakeCoordinator.inFlightSkips = 0;
108
+ setRatchetShuttingDown(false);
83
109
  unsafeCoerce(ratchetService).workspaceCheckTimeoutMs =
84
110
  SERVICE_TIMEOUT_MS.ratchetWorkspaceCheck;
85
111
  ratchetService.configure({
86
112
  session: mockSessionBridge,
87
113
  github: mockGitHubBridge,
88
114
  snapshot: mockSnapshotBridge,
115
+ workspace: mockWorkspaceBridge,
89
116
  });
90
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled).mockResolvedValue(true);
91
- vi.mocked(workspaceAccessor.settleRatchetIdleWhileDisabled).mockResolvedValue(true);
92
- vi.mocked(workspaceAccessor.recordRatchetDispatchIfEnabled).mockResolvedValue(true);
93
- vi.mocked(workspaceAccessor.adoptRatchetActiveSessionIfEnabled).mockResolvedValue(true);
94
- vi.mocked(workspaceAccessor.recordRatchetSessionEnd).mockResolvedValue(true);
95
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
117
+ vi.mocked(workspaceRatchetService.recordCheckIfEnabled).mockResolvedValue(true);
118
+ vi.mocked(workspaceRatchetService.recordDispatchIfEnabled).mockResolvedValue(true);
119
+ vi.mocked(workspaceRatchetService.adoptActiveSessionIfEnabled).mockResolvedValue(true);
120
+ vi.mocked(workspaceRatchetService.recordSessionEnd).mockResolvedValue(true);
121
+ vi.mocked(mockWorkspaceBridge.recordSessionEnd).mockResolvedValue(true);
122
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
96
123
  vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
97
124
  vi.mocked(mockGitHubBridge.getAuthenticatedUsername).mockResolvedValue(null);
98
125
  vi.mocked(mockGitHubBridge.getResolvedReviewCommentIds).mockResolvedValue(new Set());
99
126
  vi.mocked(mockSessionBridge.isSessionWorking).mockReturnValue(false);
100
- vi.mocked(userSettingsAccessor.get).mockResolvedValue({
127
+ vi.mocked(userSettingsService.get).mockResolvedValue({
101
128
  id: 'settings-1',
102
129
  userId: 'default',
103
130
  preferredIde: 'cursor',
@@ -108,6 +135,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
108
135
  cachedSlashCommands: null,
109
136
  ratchetEnabled: false,
110
137
  ratchetReplyToPrComments: true,
138
+ ratchetReviewTriggerMode: 'CHANGES_REQUESTED',
111
139
  defaultSessionProvider: 'CLAUDE',
112
140
  defaultClaudeModel: 'sonnet',
113
141
  defaultCodexModel: 'default',
@@ -118,25 +146,33 @@ describe('ratchet service (state-change + idle dispatch)', () => {
118
146
  createdAt: new Date('2026-01-01T00:00:00.000Z'),
119
147
  updatedAt: new Date('2026-01-01T00:00:00.000Z'),
120
148
  });
121
- vi.mocked(userSettingsAccessor.getDefaultSessionProvider).mockResolvedValue('CLAUDE');
149
+ vi.mocked(userSettingsService.getDefaultSessionProvider).mockResolvedValue('CLAUDE');
150
+ });
151
+ it('records session completion through the configured workspace bridge', async () => {
152
+ await ratchetService.recordSessionEnd('workspace-1', 'session-1', 'COMPLETED');
153
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith('workspace-1', 'session-1', 'COMPLETED');
154
+ expect(workspaceRatchetService.recordSessionEnd).not.toHaveBeenCalled();
122
155
  });
123
156
  afterEach(() => {
124
157
  ratchetService.removeAllListeners();
125
158
  });
126
159
  it('checks workspaces and processes each', async () => {
127
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue([
160
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue([
128
161
  {
129
162
  id: 'ws-1',
130
163
  prUrl: 'https://github.com/example/repo/pull/1',
131
164
  prNumber: 1,
132
165
  prState: 'OPEN',
166
+ prReviewState: null,
167
+ prHasMergeConflict: false,
133
168
  prCiStatus: CIStatus.UNKNOWN,
134
169
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
135
170
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
136
171
  ratchetEnabled: true,
137
172
  ratchetState: RatchetState.IDLE,
173
+ ratchetLastCheckedAt: null,
138
174
  ratchetActiveSessionId: null,
139
- ratchetLastCiRunId: null,
175
+ ratchetDispatchSnapshotKey: null,
140
176
  prReviewLastCheckedAt: null,
141
177
  ratchetDispatchOutcome: null,
142
178
  ratchetDispatchRetryCount: 0,
@@ -157,17 +193,18 @@ describe('ratchet service (state-change + idle dispatch)', () => {
157
193
  prUrl: 'https://github.com/example/repo/pull/2',
158
194
  prNumber: 2,
159
195
  prState: 'OPEN',
196
+ prReviewState: null,
197
+ prHasMergeConflict: false,
160
198
  prCiStatus: CIStatus.UNKNOWN,
161
199
  ratchetEnabled: false,
162
200
  ratchetState: RatchetState.IDLE,
163
201
  ratchetActiveSessionId: null,
164
- ratchetLastCiRunId: null,
202
+ ratchetDispatchSnapshotKey: null,
165
203
  prReviewLastCheckedAt: null,
166
204
  ratchetDispatchOutcome: null,
167
205
  ratchetDispatchRetryCount: 0,
168
206
  };
169
207
  const fetchPRStateSpy = vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState');
170
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
171
208
  const triggerSpy = vi.spyOn(unsafeCoerce(ratchetService), 'triggerFixer');
172
209
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
173
210
  expect(fetchPRStateSpy).not.toHaveBeenCalled();
@@ -176,70 +213,37 @@ describe('ratchet service (state-change + idle dispatch)', () => {
176
213
  action: { type: 'DISABLED', reason: 'Workspace ratcheting disabled' },
177
214
  newState: RatchetState.IDLE,
178
215
  });
179
- expect(workspaceAccessor.settleRatchetIdleWhileDisabled).toHaveBeenCalledWith('ws-disabled', RatchetState.IDLE);
216
+ expect(workspaceRatchetService.recordCheckIfEnabled).not.toHaveBeenCalled();
180
217
  });
181
- it('settles a disabled workspace to IDLE via CAS and emits an accurate fromState', async () => {
218
+ it('reports a disabled workspace as IDLE without writing anything', async () => {
182
219
  const workspace = {
183
220
  id: 'ws-disabled-settle',
184
221
  prUrl: 'https://github.com/example/repo/pull/2',
185
222
  prNumber: 2,
186
223
  prState: 'OPEN',
224
+ prReviewState: null,
225
+ prHasMergeConflict: false,
187
226
  prCiStatus: CIStatus.UNKNOWN,
188
227
  ratchetEnabled: false,
189
- ratchetState: RatchetState.CI_FAILED,
228
+ ratchetState: RatchetState.IDLE,
190
229
  ratchetActiveSessionId: null,
191
- ratchetLastCiRunId: null,
230
+ ratchetDispatchSnapshotKey: null,
192
231
  prReviewLastCheckedAt: null,
193
232
  ratchetDispatchOutcome: null,
194
233
  ratchetDispatchRetryCount: 0,
195
234
  };
196
- vi.mocked(workspaceAccessor.settleRatchetIdleWhileDisabled).mockResolvedValue(true);
197
235
  const events = [];
198
236
  ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
199
237
  events.push(event);
200
238
  });
201
239
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
202
- expect(workspaceAccessor.settleRatchetIdleWhileDisabled).toHaveBeenCalledWith('ws-disabled-settle', RatchetState.CI_FAILED);
203
240
  expect(result).toMatchObject({
204
- previousState: RatchetState.CI_FAILED,
241
+ previousState: RatchetState.IDLE,
205
242
  newState: RatchetState.IDLE,
206
243
  action: { type: 'DISABLED' },
207
244
  });
208
- expect(events).toEqual([
209
- {
210
- workspaceId: 'ws-disabled-settle',
211
- fromState: RatchetState.CI_FAILED,
212
- toState: RatchetState.IDLE,
213
- },
214
- ]);
215
- });
216
- it('does not emit for a disabled workspace when the settle CAS loses', async () => {
217
- const workspace = {
218
- id: 'ws-disabled-settle-race',
219
- prUrl: 'https://github.com/example/repo/pull/2',
220
- prNumber: 2,
221
- prState: 'OPEN',
222
- prCiStatus: CIStatus.UNKNOWN,
223
- ratchetEnabled: false,
224
- ratchetState: RatchetState.CI_FAILED,
225
- ratchetActiveSessionId: null,
226
- ratchetLastCiRunId: null,
227
- prReviewLastCheckedAt: null,
228
- ratchetDispatchOutcome: null,
229
- ratchetDispatchRetryCount: 0,
230
- };
231
- vi.mocked(workspaceAccessor.settleRatchetIdleWhileDisabled).mockResolvedValue(false);
232
- const events = [];
233
- ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
234
- events.push(event);
235
- });
236
- const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
237
- expect(result).toMatchObject({
238
- previousState: RatchetState.CI_FAILED,
239
- newState: RatchetState.CI_FAILED,
240
- action: { type: 'DISABLED' },
241
- });
242
245
  expect(events).toEqual([]);
246
+ expect(workspaceRatchetService.recordCheckIfEnabled).not.toHaveBeenCalled();
243
247
  });
244
248
  it('does not dispatch when workspace is not idle', async () => {
245
249
  const workspace = {
@@ -247,11 +251,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
247
251
  prUrl: 'https://github.com/example/repo/pull/3',
248
252
  prNumber: 3,
249
253
  prState: 'OPEN',
254
+ prReviewState: null,
255
+ prHasMergeConflict: false,
250
256
  prCiStatus: CIStatus.UNKNOWN,
251
257
  ratchetEnabled: true,
252
258
  ratchetState: RatchetState.IDLE,
253
259
  ratchetActiveSessionId: null,
254
- ratchetLastCiRunId: null,
260
+ ratchetDispatchSnapshotKey: null,
255
261
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
256
262
  ratchetDispatchOutcome: null,
257
263
  ratchetDispatchRetryCount: 0,
@@ -274,8 +280,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
274
280
  },
275
281
  ],
276
282
  });
277
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
278
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([
283
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([
279
284
  {
280
285
  id: 'chat-1',
281
286
  workflow: 'default-followup',
@@ -298,11 +303,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
298
303
  prUrl: 'https://github.com/example/repo/pull/4',
299
304
  prNumber: 4,
300
305
  prState: 'OPEN',
306
+ prReviewState: null,
307
+ prHasMergeConflict: false,
301
308
  prCiStatus: CIStatus.UNKNOWN,
302
309
  ratchetEnabled: true,
303
310
  ratchetState: RatchetState.IDLE,
304
311
  ratchetActiveSessionId: null,
305
- ratchetLastCiRunId: '2026-01-01T00:00:00Z',
312
+ ratchetDispatchSnapshotKey: '2026-01-01T00:00:00Z',
306
313
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
307
314
  ratchetDispatchOutcome: null,
308
315
  ratchetDispatchRetryCount: 0,
@@ -316,8 +323,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
316
323
  prState: 'OPEN',
317
324
  prNumber: 4,
318
325
  });
319
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
320
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
326
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
321
327
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
322
328
  status: 'started',
323
329
  sessionId: 'ratchet-session',
@@ -327,16 +333,12 @@ describe('ratchet service (state-change + idle dispatch)', () => {
327
333
  expect(result).toMatchObject({
328
334
  action: { type: 'TRIGGERED_FIXER' },
329
335
  });
330
- expect(workspaceAccessor.recordRatchetDispatchIfEnabled).toHaveBeenCalledWith('ws-change', {
336
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith('ws-change', {
331
337
  sessionId: 'ratchet-session',
332
338
  snapshotKey: '2026-01-02T00:00:00Z',
333
339
  retryCount: 0,
334
340
  });
335
- const finalUpdatePayload = vi
336
- .mocked(workspaceAccessor.transitionRatchetStateIfEnabled)
337
- .mock.calls.at(-1)?.[2];
338
- expect(finalUpdatePayload).not.toHaveProperty('ratchetLastCiRunId');
339
- expect(finalUpdatePayload).not.toHaveProperty('prReviewLastCheckedAt');
341
+ expect(workspaceRatchetService.recordCheckIfEnabled).toHaveBeenLastCalledWith('ws-change', expect.any(Date));
340
342
  expect(mockSnapshotBridge.recordReviewCheck).toHaveBeenCalledWith('ws-change', expect.any(Date));
341
343
  });
342
344
  it('stops fixer and returns disabled when active-session recording loses disable race', async () => {
@@ -345,11 +347,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
345
347
  prUrl: 'https://github.com/example/repo/pull/40',
346
348
  prNumber: 40,
347
349
  prState: 'OPEN',
350
+ prReviewState: null,
351
+ prHasMergeConflict: false,
348
352
  prCiStatus: CIStatus.UNKNOWN,
349
353
  ratchetEnabled: true,
350
354
  ratchetState: RatchetState.IDLE,
351
355
  ratchetActiveSessionId: null,
352
- ratchetLastCiRunId: 'old-snapshot',
356
+ ratchetDispatchSnapshotKey: 'old-snapshot',
353
357
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
354
358
  ratchetDispatchOutcome: null,
355
359
  ratchetDispatchRetryCount: 0,
@@ -363,27 +367,32 @@ describe('ratchet service (state-change + idle dispatch)', () => {
363
367
  prState: 'OPEN',
364
368
  prNumber: 40,
365
369
  });
366
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
370
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
367
371
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
368
372
  status: 'started',
369
373
  sessionId: 'raced-session',
370
374
  promptSent: true,
371
375
  });
372
- vi.mocked(workspaceAccessor.recordRatchetDispatchIfEnabled).mockResolvedValue(false);
373
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled).mockResolvedValue(false);
374
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
376
+ vi.mocked(workspaceRatchetService.recordDispatchIfEnabled).mockResolvedValue(false);
377
+ vi.mocked(workspaceRatchetService.recordCheckIfEnabled).mockResolvedValue(false);
378
+ vi.mocked(workspaceDataService.findById).mockResolvedValue({
375
379
  id: 'ws-disable-race-dispatch',
376
380
  ratchetEnabled: false,
377
381
  });
378
382
  vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
383
+ const events = [];
384
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
385
+ events.push(event);
386
+ });
379
387
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
380
388
  expect(result).toMatchObject({
381
389
  newState: RatchetState.IDLE,
382
390
  action: { type: 'DISABLED', reason: 'Workspace ratcheting disabled' },
383
391
  });
384
- expect(workspaceAccessor.recordRatchetDispatchIfEnabled).toHaveBeenCalledWith('ws-disable-race-dispatch', { sessionId: 'raced-session', snapshotKey: 'new-snapshot', retryCount: 0 });
392
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith('ws-disable-race-dispatch', { sessionId: 'raced-session', snapshotKey: 'new-snapshot', retryCount: 0 });
385
393
  expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('raced-session');
386
394
  expect(mockSnapshotBridge.recordReviewCheck).not.toHaveBeenCalled();
395
+ expect(events).toHaveLength(0);
387
396
  });
388
397
  it('dispatches when session is running but idle', async () => {
389
398
  const workspace = {
@@ -391,11 +400,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
391
400
  prUrl: 'https://github.com/example/repo/pull/44',
392
401
  prNumber: 44,
393
402
  prState: 'OPEN',
403
+ prReviewState: null,
404
+ prHasMergeConflict: false,
394
405
  prCiStatus: CIStatus.UNKNOWN,
395
406
  ratchetEnabled: true,
396
407
  ratchetState: RatchetState.IDLE,
397
408
  ratchetActiveSessionId: null,
398
- ratchetLastCiRunId: '2026-01-01T00:00:00Z',
409
+ ratchetDispatchSnapshotKey: '2026-01-01T00:00:00Z',
399
410
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
400
411
  ratchetDispatchOutcome: null,
401
412
  ratchetDispatchRetryCount: 0,
@@ -409,8 +420,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
409
420
  prState: 'OPEN',
410
421
  prNumber: 44,
411
422
  });
412
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
413
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([
423
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([
414
424
  {
415
425
  id: 'chat-idle-1',
416
426
  workflow: 'default-followup',
@@ -437,11 +447,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
437
447
  prUrl: 'https://github.com/example/repo/pull/5',
438
448
  prNumber: 5,
439
449
  prState: 'OPEN',
450
+ prReviewState: null,
451
+ prHasMergeConflict: false,
440
452
  prCiStatus: CIStatus.UNKNOWN,
441
453
  ratchetEnabled: true,
442
454
  ratchetState: RatchetState.READY,
443
455
  ratchetActiveSessionId: null,
444
- ratchetLastCiRunId: '2026-01-02T00:00:00Z',
456
+ ratchetDispatchSnapshotKey: '2026-01-02T00:00:00Z',
445
457
  prReviewLastCheckedAt: new Date('2026-01-02T00:00:00Z'),
446
458
  ratchetDispatchOutcome: null,
447
459
  ratchetDispatchRetryCount: 0,
@@ -455,8 +467,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
455
467
  prState: 'OPEN',
456
468
  prNumber: 5,
457
469
  });
458
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
459
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
470
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
460
471
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
461
472
  expect(result).toMatchObject({
462
473
  action: {
@@ -464,7 +475,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
464
475
  reason: 'PR state unchanged since last ratchet dispatch',
465
476
  },
466
477
  });
467
- expect(agentSessionAccessor.findByWorkspaceId).not.toHaveBeenCalled();
478
+ expect(mockSessionBridge.findSessionsByWorkspaceId).not.toHaveBeenCalled();
468
479
  expect(mockSnapshotBridge.recordReviewCheck).not.toHaveBeenCalled();
469
480
  });
470
481
  it('does not dispatch repeatedly for unchanged CHANGES_REQUESTED state', async () => {
@@ -473,11 +484,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
473
484
  prUrl: 'https://github.com/example/repo/pull/55',
474
485
  prNumber: 55,
475
486
  prState: 'OPEN',
487
+ prReviewState: null,
488
+ prHasMergeConflict: false,
476
489
  prCiStatus: CIStatus.UNKNOWN,
477
490
  ratchetEnabled: true,
478
491
  ratchetState: RatchetState.REVIEW_PENDING,
479
492
  ratchetActiveSessionId: null,
480
- ratchetLastCiRunId: 'ci:SUCCESS|changes-requested:1735776000000',
493
+ ratchetDispatchSnapshotKey: 'ci:SUCCESS|changes-requested:1735776000000',
481
494
  prReviewLastCheckedAt: new Date('2026-01-02T00:00:00Z'),
482
495
  ratchetDispatchOutcome: null,
483
496
  ratchetDispatchRetryCount: 0,
@@ -500,8 +513,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
500
513
  },
501
514
  ],
502
515
  });
503
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
504
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
516
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
505
517
  const triggerSpy = vi.spyOn(unsafeCoerce(ratchetService), 'triggerFixer');
506
518
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
507
519
  expect(triggerSpy).not.toHaveBeenCalled();
@@ -511,19 +523,21 @@ describe('ratchet service (state-change + idle dispatch)', () => {
511
523
  reason: 'PR state unchanged since last ratchet dispatch',
512
524
  },
513
525
  });
514
- expect(agentSessionAccessor.findByWorkspaceId).not.toHaveBeenCalled();
526
+ expect(mockSessionBridge.findSessionsByWorkspaceId).not.toHaveBeenCalled();
515
527
  });
516
- it('does not dispatch for CHANGES_REQUESTED when there are no PR review comments', async () => {
528
+ it('dispatches for CHANGES_REQUESTED when there are no PR review comments', async () => {
517
529
  const workspace = {
518
530
  id: 'ws-review-no-comments',
519
531
  prUrl: 'https://github.com/example/repo/pull/56',
520
532
  prNumber: 56,
521
533
  prState: 'OPEN',
534
+ prReviewState: null,
535
+ prHasMergeConflict: false,
522
536
  prCiStatus: CIStatus.UNKNOWN,
523
537
  ratchetEnabled: true,
524
538
  ratchetState: RatchetState.REVIEW_PENDING,
525
539
  ratchetActiveSessionId: null,
526
- ratchetLastCiRunId: 'ci:SUCCESS|changes-requested:old',
540
+ ratchetDispatchSnapshotKey: 'ci:SUCCESS|changes-requested:old',
527
541
  prReviewLastCheckedAt: new Date('2026-01-02T00:00:00Z'),
528
542
  ratchetDispatchOutcome: null,
529
543
  ratchetDispatchRetryCount: 0,
@@ -538,34 +552,44 @@ describe('ratchet service (state-change + idle dispatch)', () => {
538
552
  prNumber: 56,
539
553
  reviewComments: [],
540
554
  });
541
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
542
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
555
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
556
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
557
+ status: 'started',
558
+ sessionId: 'ratchet-session',
559
+ promptSent: true,
560
+ });
543
561
  const triggerSpy = vi.spyOn(unsafeCoerce(ratchetService), 'triggerFixer');
544
562
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
545
- expect(triggerSpy).not.toHaveBeenCalled();
563
+ expect(triggerSpy).toHaveBeenCalled();
546
564
  expect(result).toMatchObject({
547
- action: {
548
- type: 'WAITING',
549
- reason: 'No CI failures or PR review comments to address',
550
- },
565
+ action: { type: 'TRIGGERED_FIXER' },
551
566
  });
552
567
  });
553
- it('dispatches for PR review summary feedback without changes requested', async () => {
568
+ it('dispatches for PR review summary feedback in all-feedback mode', async () => {
554
569
  const workspace = {
555
570
  id: 'ws-review-summary',
556
571
  prUrl: 'https://github.com/example/repo/pull/57',
557
572
  prNumber: 57,
558
573
  prState: 'OPEN',
574
+ prReviewState: null,
575
+ prHasMergeConflict: false,
559
576
  prCiStatus: CIStatus.UNKNOWN,
560
577
  ratchetEnabled: true,
561
578
  ratchetState: RatchetState.READY,
562
579
  ratchetActiveSessionId: null,
563
- ratchetLastCiRunId: 'ci:SUCCESS|no-changes-requested:old|merge:clean',
580
+ ratchetDispatchSnapshotKey: 'ci:SUCCESS|no-changes-requested:old|merge:clean',
564
581
  prReviewLastCheckedAt: new Date('2026-01-02T00:00:00Z'),
565
582
  ratchetDispatchOutcome: null,
566
583
  ratchetDispatchRetryCount: 0,
567
584
  };
568
- vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({
585
+ vi.mocked(userSettingsService.get).mockResolvedValue({
586
+ ratchetReviewTriggerMode: 'ALL_REVIEW_FEEDBACK',
587
+ ratchetReplyToPrComments: true,
588
+ ratchetPermissions: 'YOLO',
589
+ });
590
+ const fetchPRStateSpy = vi
591
+ .spyOn(unsafeCoerce(ratchetService), 'fetchPRState')
592
+ .mockResolvedValue({
569
593
  ciStatus: CIStatus.SUCCESS,
570
594
  snapshotKey: 'ci:SUCCESS|no-changes-requested:1767315600000|merge:clean',
571
595
  hasChangesRequested: false,
@@ -584,8 +608,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
584
608
  },
585
609
  ],
586
610
  });
587
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
588
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
611
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
589
612
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
590
613
  status: 'started',
591
614
  sessionId: 'ratchet-session',
@@ -597,6 +620,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
597
620
  newState: RatchetState.READY,
598
621
  });
599
622
  expect(fixerSessionService.acquireAndDispatch).toHaveBeenCalled();
623
+ expect(fetchPRStateSpy).toHaveBeenCalledWith(workspace, null, expect.objectContaining({ reviewTriggerMode: 'ALL_REVIEW_FEEDBACK' }), expect.any(AbortSignal));
600
624
  });
601
625
  it('treats closed PR as IDLE and does not dispatch', () => {
602
626
  const state = determineRatchetState({
@@ -615,11 +639,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
615
639
  prUrl: 'https://github.com/example/repo/pull/7',
616
640
  prNumber: 7,
617
641
  prState: 'OPEN',
642
+ prReviewState: null,
643
+ prHasMergeConflict: false,
618
644
  prCiStatus: CIStatus.UNKNOWN,
619
645
  ratchetEnabled: true,
620
646
  ratchetState: RatchetState.IDLE,
621
647
  ratchetActiveSessionId: null,
622
- ratchetLastCiRunId: null,
648
+ ratchetDispatchSnapshotKey: null,
623
649
  prReviewLastCheckedAt: null,
624
650
  ratchetDispatchOutcome: null,
625
651
  ratchetDispatchRetryCount: 0,
@@ -642,8 +668,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
642
668
  },
643
669
  ],
644
670
  });
645
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
646
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
671
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
647
672
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
648
673
  status: 'started',
649
674
  sessionId: 'ratchet-session',
@@ -655,9 +680,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
655
680
  expect(result).toMatchObject({
656
681
  action: { type: 'ERROR', error: 'Failed to deliver initial ratchet prompt' },
657
682
  });
658
- expect(workspaceAccessor.update).toHaveBeenCalledWith(workspace.id, {
659
- ratchetActiveSessionId: null,
660
- });
683
+ expect(workspaceRatchetService.clearActiveSession).toHaveBeenCalledWith(workspace.id, 'ratchet-session');
661
684
  expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('ratchet-session');
662
685
  });
663
686
  it('routes an aborted prompt-failure path through orphan cleanup', async () => {
@@ -669,7 +692,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
669
692
  sessionId: 'aborted-prompt-session',
670
693
  promptSent: false,
671
694
  });
672
- vi.mocked(workspaceAccessor.update).mockImplementation(() => new Promise((resolve) => {
695
+ vi.mocked(workspaceRatchetService.clearActiveSession).mockImplementation(() => new Promise((resolve) => {
673
696
  finishPointerClear = () => resolve({});
674
697
  }));
675
698
  vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
@@ -683,13 +706,30 @@ describe('ratchet service (state-change + idle dispatch)', () => {
683
706
  reviewComments: [],
684
707
  hasMergeConflict: false,
685
708
  }, 0, controller.signal);
686
- await vi.waitFor(() => expect(workspaceAccessor.update).toHaveBeenCalled());
709
+ await vi.waitFor(() => expect(workspaceRatchetService.clearActiveSession).toHaveBeenCalled());
687
710
  controller.abort(timeoutError);
688
711
  finishPointerClear();
689
712
  await expect(trigger).rejects.toBe(timeoutError);
690
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith('ws-aborted-prompt', 'aborted-prompt-session', 'COMPLETED');
713
+ expect(workspaceRatchetService.recordSessionEnd).toHaveBeenCalledWith('ws-aborted-prompt', 'aborted-prompt-session', 'COMPLETED');
691
714
  expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('aborted-prompt-session');
692
715
  });
716
+ it('publishes a dispatch invalidation after lifecycle settlement', async () => {
717
+ const events = [];
718
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
719
+ events.push(event);
720
+ });
721
+ await ratchetService.recordSessionEnd('ws-1', 'session-1', 'DIED');
722
+ expect(events).toContainEqual({ workspaceId: 'ws-1' });
723
+ });
724
+ it('does not publish a dispatch change when lifecycle settlement loses its CAS', async () => {
725
+ const events = [];
726
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
727
+ events.push(event);
728
+ });
729
+ vi.mocked(mockWorkspaceBridge.recordSessionEnd).mockResolvedValue(false);
730
+ await ratchetService.recordSessionEnd('ws-1', 'session-1', 'DIED');
731
+ expect(events).toHaveLength(0);
732
+ });
693
733
  it('does not dispatch on a clean PR with no new review activity', async () => {
694
734
  const recentCheck = new Date();
695
735
  const workspace = {
@@ -697,11 +737,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
697
737
  prUrl: 'https://github.com/example/repo/pull/8',
698
738
  prNumber: 8,
699
739
  prState: 'OPEN',
740
+ prReviewState: null,
741
+ prHasMergeConflict: false,
700
742
  prCiStatus: CIStatus.UNKNOWN,
701
743
  ratchetEnabled: true,
702
744
  ratchetState: RatchetState.READY,
703
745
  ratchetActiveSessionId: null,
704
- ratchetLastCiRunId: '2026-01-01T00:00:00Z',
746
+ ratchetDispatchSnapshotKey: '2026-01-01T00:00:00Z',
705
747
  prReviewLastCheckedAt: recentCheck,
706
748
  ratchetDispatchOutcome: null,
707
749
  ratchetDispatchRetryCount: 0,
@@ -715,8 +757,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
715
757
  prState: 'OPEN',
716
758
  prNumber: 8,
717
759
  });
718
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
719
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
760
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
720
761
  const triggerSpy = vi.spyOn(unsafeCoerce(ratchetService), 'triggerFixer');
721
762
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
722
763
  expect(triggerSpy).not.toHaveBeenCalled();
@@ -727,6 +768,58 @@ describe('ratchet service (state-change + idle dispatch)', () => {
727
768
  },
728
769
  });
729
770
  });
771
+ it('dispatches stale actionable review comments when the PR snapshot changes', async () => {
772
+ const recentCheck = new Date();
773
+ const workspace = {
774
+ id: 'ws-stale-actionable-review',
775
+ prUrl: 'https://github.com/example/repo/pull/43',
776
+ prNumber: 43,
777
+ prState: 'OPEN',
778
+ prReviewState: null,
779
+ prHasMergeConflict: false,
780
+ prCiStatus: CIStatus.FAILURE,
781
+ ratchetEnabled: true,
782
+ ratchetState: RatchetState.CI_FAILED,
783
+ ratchetActiveSessionId: null,
784
+ ratchetDispatchSnapshotKey: 'failed-snapshot',
785
+ prReviewLastCheckedAt: recentCheck,
786
+ ratchetDispatchOutcome: 'COMPLETED',
787
+ ratchetDispatchRetryCount: 0,
788
+ };
789
+ vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({
790
+ ciStatus: CIStatus.SUCCESS,
791
+ snapshotKey: 'successful-snapshot',
792
+ hasChangesRequested: false,
793
+ hasMergeConflict: false,
794
+ latestReviewActivityAtMs: recentCheck.getTime() - 1000,
795
+ statusCheckRollup: null,
796
+ prState: 'OPEN',
797
+ prNumber: 43,
798
+ reviewComments: [
799
+ {
800
+ author: 'reviewer',
801
+ body: 'Please handle this edge case.',
802
+ path: 'src/example.ts',
803
+ line: 10,
804
+ url: 'https://github.com/example/repo/pull/43#discussion_r1',
805
+ },
806
+ ],
807
+ });
808
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
809
+ status: 'started',
810
+ sessionId: 'ratchet-review-session',
811
+ promptSent: true,
812
+ });
813
+ const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
814
+ expect(result).toMatchObject({
815
+ action: { type: 'TRIGGERED_FIXER', sessionId: 'ratchet-review-session' },
816
+ });
817
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith('ws-stale-actionable-review', {
818
+ sessionId: 'ratchet-review-session',
819
+ snapshotKey: 'successful-snapshot',
820
+ retryCount: 0,
821
+ });
822
+ });
730
823
  it('returns disabled when stale check-state update loses disable race', async () => {
731
824
  const recentCheck = new Date();
732
825
  const workspace = {
@@ -734,11 +827,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
734
827
  prUrl: 'https://github.com/example/repo/pull/41',
735
828
  prNumber: 41,
736
829
  prState: 'OPEN',
830
+ prReviewState: null,
831
+ prHasMergeConflict: false,
737
832
  prCiStatus: CIStatus.UNKNOWN,
738
833
  ratchetEnabled: true,
739
834
  ratchetState: RatchetState.READY,
740
835
  ratchetActiveSessionId: null,
741
- ratchetLastCiRunId: 'old-snapshot',
836
+ ratchetDispatchSnapshotKey: 'old-snapshot',
742
837
  prReviewLastCheckedAt: recentCheck,
743
838
  ratchetDispatchOutcome: null,
744
839
  ratchetDispatchRetryCount: 0,
@@ -752,12 +847,12 @@ describe('ratchet service (state-change + idle dispatch)', () => {
752
847
  prState: 'OPEN',
753
848
  prNumber: 41,
754
849
  });
755
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled).mockResolvedValue(false);
756
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
850
+ vi.mocked(workspaceRatchetService.recordCheckIfEnabled).mockResolvedValue(false);
851
+ vi.mocked(workspaceDataService.findById).mockResolvedValue({
757
852
  id: 'ws-disable-race-state',
758
853
  ratchetEnabled: false,
759
854
  });
760
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
855
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
761
856
  const events = [];
762
857
  ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
763
858
  events.push(event);
@@ -770,55 +865,117 @@ describe('ratchet service (state-change + idle dispatch)', () => {
770
865
  expect(events).toHaveLength(0);
771
866
  ratchetService.removeAllListeners();
772
867
  });
773
- it('reports a concurrent state change without emitting when the check loses the CAS while still enabled', async () => {
774
- const recentCheck = new Date();
868
+ it('persists PR state and review decision the check observed, not just CI', async () => {
775
869
  const workspace = {
776
- id: 'ws-superseded',
777
- prUrl: 'https://github.com/example/repo/pull/42',
778
- prNumber: 42,
870
+ id: 'ws-observation',
871
+ prUrl: 'https://github.com/example/repo/pull/44',
872
+ prNumber: 44,
779
873
  prState: 'OPEN',
780
- prCiStatus: CIStatus.UNKNOWN,
874
+ prReviewState: null,
875
+ prHasMergeConflict: false,
876
+ prCiStatus: CIStatus.SUCCESS,
781
877
  ratchetEnabled: true,
782
- ratchetState: RatchetState.CI_RUNNING,
878
+ ratchetState: RatchetState.READY,
783
879
  ratchetActiveSessionId: null,
784
- ratchetLastCiRunId: 'old-snapshot',
785
- prReviewLastCheckedAt: recentCheck,
880
+ ratchetDispatchSnapshotKey: 'observation-snapshot',
881
+ prReviewLastCheckedAt: null,
786
882
  ratchetDispatchOutcome: null,
787
883
  ratchetDispatchRetryCount: 0,
788
884
  };
789
885
  vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({
790
886
  ciStatus: CIStatus.SUCCESS,
791
- snapshotKey: 'new-snapshot',
792
- hasChangesRequested: false,
793
- latestReviewActivityAtMs: recentCheck.getTime() - 1000,
887
+ snapshotKey: 'observation-snapshot',
888
+ hasChangesRequested: true,
889
+ hasMergeConflict: false,
890
+ latestReviewActivityAtMs: null,
794
891
  statusCheckRollup: null,
795
892
  prState: 'OPEN',
796
- prNumber: 42,
893
+ cachedPrState: 'CHANGES_REQUESTED',
894
+ reviewDecision: 'CHANGES_REQUESTED',
895
+ prNumber: 44,
896
+ reviewComments: [],
797
897
  });
798
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled).mockResolvedValue(false);
799
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
800
- id: 'ws-superseded',
898
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
899
+ await unsafeCoerce(ratchetService).processWorkspace(workspace);
900
+ expect(mockSnapshotBridge.recordPrObservation).toHaveBeenCalledWith(expect.objectContaining({
901
+ workspaceId: 'ws-observation',
902
+ prState: 'CHANGES_REQUESTED',
903
+ reviewState: 'CHANGES_REQUESTED',
904
+ ciStatus: CIStatus.SUCCESS,
905
+ hasMergeConflict: false,
906
+ }));
907
+ });
908
+ it('persists a merge observed by the check even when CI status is unchanged', async () => {
909
+ const workspace = {
910
+ id: 'ws-merged',
911
+ prUrl: 'https://github.com/example/repo/pull/45',
912
+ prNumber: 45,
913
+ prState: 'OPEN',
914
+ prReviewState: null,
915
+ prHasMergeConflict: false,
916
+ prCiStatus: CIStatus.SUCCESS,
801
917
  ratchetEnabled: true,
802
- ratchetState: RatchetState.IDLE,
803
- });
804
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
805
- const events = [];
806
- ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
807
- events.push(event);
918
+ ratchetState: RatchetState.READY,
919
+ ratchetActiveSessionId: null,
920
+ ratchetDispatchSnapshotKey: 'merged-snapshot',
921
+ prReviewLastCheckedAt: null,
922
+ ratchetDispatchOutcome: null,
923
+ ratchetDispatchRetryCount: 0,
924
+ };
925
+ vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({
926
+ ciStatus: CIStatus.SUCCESS,
927
+ snapshotKey: 'merged-snapshot',
928
+ hasChangesRequested: false,
929
+ hasMergeConflict: false,
930
+ latestReviewActivityAtMs: null,
931
+ statusCheckRollup: null,
932
+ prState: 'MERGED',
933
+ cachedPrState: 'MERGED',
934
+ reviewDecision: null,
935
+ prNumber: 45,
936
+ reviewComments: [],
808
937
  });
809
- const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
810
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).toHaveBeenCalledWith('ws-superseded', RatchetState.CI_RUNNING, expect.objectContaining({ ratchetState: RatchetState.READY }));
811
- expect(result).toMatchObject({
812
- previousState: RatchetState.CI_RUNNING,
813
- newState: RatchetState.CI_RUNNING,
814
- action: {
815
- type: 'WAITING',
816
- reason: 'Ratchet state changed concurrently during this check; re-evaluating next cycle',
817
- },
938
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
939
+ await unsafeCoerce(ratchetService).processWorkspace(workspace);
940
+ expect(mockSnapshotBridge.recordPrObservation).toHaveBeenCalledWith(expect.objectContaining({ workspaceId: 'ws-merged', prState: 'MERGED' }));
941
+ });
942
+ it('persists a newly observed merge conflict even when CI status is unchanged', async () => {
943
+ const workspace = {
944
+ id: 'ws-conflict',
945
+ prUrl: 'https://github.com/example/repo/pull/43',
946
+ prNumber: 43,
947
+ prState: 'OPEN',
948
+ prReviewState: null,
949
+ prHasMergeConflict: false,
950
+ prCiStatus: CIStatus.SUCCESS,
951
+ ratchetEnabled: true,
952
+ ratchetState: RatchetState.READY,
953
+ ratchetActiveSessionId: null,
954
+ ratchetDispatchSnapshotKey: 'conflict-snapshot',
955
+ prReviewLastCheckedAt: null,
956
+ ratchetDispatchOutcome: null,
957
+ ratchetDispatchRetryCount: 0,
958
+ };
959
+ vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({
960
+ ciStatus: CIStatus.SUCCESS,
961
+ snapshotKey: 'conflict-snapshot',
962
+ hasChangesRequested: false,
963
+ hasMergeConflict: true,
964
+ latestReviewActivityAtMs: null,
965
+ statusCheckRollup: null,
966
+ prState: 'OPEN',
967
+ cachedPrState: 'OPEN',
968
+ reviewDecision: null,
969
+ prNumber: 43,
970
+ reviewComments: [],
818
971
  });
819
- expect(events).toHaveLength(0);
820
- expect(mockSnapshotBridge.recordCIObservation).not.toHaveBeenCalled();
821
- expect(mockSnapshotBridge.recordReviewCheck).not.toHaveBeenCalled();
972
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
973
+ await unsafeCoerce(ratchetService).processWorkspace(workspace);
974
+ expect(mockSnapshotBridge.recordPrObservation).toHaveBeenCalledWith(expect.objectContaining({
975
+ workspaceId: 'ws-conflict',
976
+ ciStatus: CIStatus.SUCCESS,
977
+ hasMergeConflict: true,
978
+ }));
822
979
  });
823
980
  it('redispatches with an incremented retry count when the previous fixer died', async () => {
824
981
  const workspace = {
@@ -826,11 +983,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
826
983
  prUrl: 'https://github.com/example/repo/pull/13',
827
984
  prNumber: 13,
828
985
  prState: 'OPEN',
986
+ prReviewState: null,
987
+ prHasMergeConflict: false,
829
988
  prCiStatus: CIStatus.FAILURE,
830
989
  ratchetEnabled: true,
831
990
  ratchetState: RatchetState.CI_FAILED,
832
991
  ratchetActiveSessionId: null,
833
- ratchetLastCiRunId: 'same-snapshot',
992
+ ratchetDispatchSnapshotKey: 'same-snapshot',
834
993
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
835
994
  ratchetDispatchOutcome: 'DIED',
836
995
  ratchetDispatchRetryCount: 1,
@@ -844,8 +1003,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
844
1003
  prState: 'OPEN',
845
1004
  prNumber: 13,
846
1005
  });
847
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
848
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
1006
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
849
1007
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
850
1008
  status: 'started',
851
1009
  sessionId: 'retry-session',
@@ -855,7 +1013,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
855
1013
  expect(result).toMatchObject({
856
1014
  action: { type: 'TRIGGERED_FIXER' },
857
1015
  });
858
- expect(workspaceAccessor.recordRatchetDispatchIfEnabled).toHaveBeenCalledWith('ws-died-retry', {
1016
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith('ws-died-retry', {
859
1017
  sessionId: 'retry-session',
860
1018
  snapshotKey: 'same-snapshot',
861
1019
  retryCount: 2,
@@ -868,11 +1026,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
868
1026
  prUrl: 'https://github.com/example/repo/pull/14',
869
1027
  prNumber: 14,
870
1028
  prState: 'OPEN',
1029
+ prReviewState: null,
1030
+ prHasMergeConflict: false,
871
1031
  prCiStatus: CIStatus.SUCCESS,
872
1032
  ratchetEnabled: true,
873
1033
  ratchetState: RatchetState.READY,
874
1034
  ratchetActiveSessionId: null,
875
- ratchetLastCiRunId: 'clean-snapshot',
1035
+ ratchetDispatchSnapshotKey: 'clean-snapshot',
876
1036
  prReviewLastCheckedAt: checkedAt,
877
1037
  ratchetDispatchOutcome: 'DIED',
878
1038
  ratchetDispatchRetryCount: 0,
@@ -887,22 +1047,26 @@ describe('ratchet service (state-change + idle dispatch)', () => {
887
1047
  prNumber: 14,
888
1048
  reviewComments: [],
889
1049
  });
890
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
891
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
1050
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
892
1051
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
893
1052
  status: 'started',
894
1053
  sessionId: 'retry-session',
895
1054
  promptSent: true,
896
1055
  });
1056
+ const events = [];
1057
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
1058
+ events.push(event);
1059
+ });
897
1060
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
898
1061
  expect(result).toMatchObject({
899
1062
  action: { type: 'TRIGGERED_FIXER' },
900
1063
  });
901
- expect(workspaceAccessor.recordRatchetDispatchIfEnabled).toHaveBeenCalledWith('ws-died-clean', {
1064
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith('ws-died-clean', {
902
1065
  sessionId: 'retry-session',
903
1066
  snapshotKey: 'clean-snapshot',
904
1067
  retryCount: 1,
905
1068
  });
1069
+ expect(events).toContainEqual({ workspaceId: 'ws-died-clean' });
906
1070
  });
907
1071
  it('stops retrying a died fixer once the retry budget is exhausted', async () => {
908
1072
  const workspace = {
@@ -910,11 +1074,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
910
1074
  prUrl: 'https://github.com/example/repo/pull/15',
911
1075
  prNumber: 15,
912
1076
  prState: 'OPEN',
1077
+ prReviewState: null,
1078
+ prHasMergeConflict: false,
913
1079
  prCiStatus: CIStatus.FAILURE,
914
1080
  ratchetEnabled: true,
915
1081
  ratchetState: RatchetState.CI_FAILED,
916
1082
  ratchetActiveSessionId: null,
917
- ratchetLastCiRunId: 'same-snapshot',
1083
+ ratchetDispatchSnapshotKey: 'same-snapshot',
918
1084
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
919
1085
  ratchetDispatchOutcome: 'DIED',
920
1086
  ratchetDispatchRetryCount: 3,
@@ -928,7 +1094,6 @@ describe('ratchet service (state-change + idle dispatch)', () => {
928
1094
  prState: 'OPEN',
929
1095
  prNumber: 15,
930
1096
  });
931
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
932
1097
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
933
1098
  expect(result).toMatchObject({
934
1099
  action: {
@@ -944,11 +1109,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
944
1109
  prUrl: 'https://github.com/example/repo/pull/16',
945
1110
  prNumber: 16,
946
1111
  prState: 'OPEN',
1112
+ prReviewState: null,
1113
+ prHasMergeConflict: false,
947
1114
  prCiStatus: CIStatus.FAILURE,
948
1115
  ratchetEnabled: true,
949
1116
  ratchetState: RatchetState.CI_FAILED,
950
1117
  ratchetActiveSessionId: null,
951
- ratchetLastCiRunId: 'old-snapshot',
1118
+ ratchetDispatchSnapshotKey: 'old-snapshot',
952
1119
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
953
1120
  ratchetDispatchOutcome: 'DIED',
954
1121
  ratchetDispatchRetryCount: 3,
@@ -962,8 +1129,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
962
1129
  prState: 'OPEN',
963
1130
  prNumber: 16,
964
1131
  });
965
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
966
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
1132
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
967
1133
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
968
1134
  status: 'started',
969
1135
  sessionId: 'fresh-session',
@@ -973,7 +1139,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
973
1139
  expect(result).toMatchObject({
974
1140
  action: { type: 'TRIGGERED_FIXER' },
975
1141
  });
976
- expect(workspaceAccessor.recordRatchetDispatchIfEnabled).toHaveBeenCalledWith('ws-died-changed', { sessionId: 'fresh-session', snapshotKey: 'new-snapshot', retryCount: 0 });
1142
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith('ws-died-changed', { sessionId: 'fresh-session', snapshotKey: 'new-snapshot', retryCount: 0 });
977
1143
  });
978
1144
  it('does not redispatch when the previous fixer completed and PR state is unchanged', async () => {
979
1145
  const workspace = {
@@ -981,11 +1147,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
981
1147
  prUrl: 'https://github.com/example/repo/pull/17',
982
1148
  prNumber: 17,
983
1149
  prState: 'OPEN',
1150
+ prReviewState: null,
1151
+ prHasMergeConflict: false,
984
1152
  prCiStatus: CIStatus.FAILURE,
985
1153
  ratchetEnabled: true,
986
1154
  ratchetState: RatchetState.CI_FAILED,
987
1155
  ratchetActiveSessionId: null,
988
- ratchetLastCiRunId: 'same-snapshot',
1156
+ ratchetDispatchSnapshotKey: 'same-snapshot',
989
1157
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
990
1158
  ratchetDispatchOutcome: 'COMPLETED',
991
1159
  ratchetDispatchRetryCount: 2,
@@ -999,7 +1167,6 @@ describe('ratchet service (state-change + idle dispatch)', () => {
999
1167
  prState: 'OPEN',
1000
1168
  prNumber: 17,
1001
1169
  });
1002
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
1003
1170
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
1004
1171
  expect(result).toMatchObject({
1005
1172
  action: {
@@ -1009,22 +1176,71 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1009
1176
  });
1010
1177
  expect(fixerSessionService.acquireAndDispatch).not.toHaveBeenCalled();
1011
1178
  });
1179
+ it('dispatches a fresh fixer after switching PRs with identical merge-conflict state', async () => {
1180
+ const previousSnapshotKey = computeDispatchSnapshotKey(123, CIStatus.SUCCESS, false, null, null, true);
1181
+ const currentSnapshotKey = computeDispatchSnapshotKey(456, CIStatus.SUCCESS, false, null, null, true);
1182
+ expect(previousSnapshotKey).toBe('pr:123|ci:SUCCESS|no-changes-requested:none|merge:conflict');
1183
+ expect(currentSnapshotKey).toBe('pr:456|ci:SUCCESS|no-changes-requested:none|merge:conflict');
1184
+ const workspace = {
1185
+ id: 'ws-pr-switch-merge-conflict',
1186
+ prUrl: 'https://github.com/example/repo/pull/456',
1187
+ prNumber: 456,
1188
+ prState: 'OPEN',
1189
+ prReviewState: null,
1190
+ prHasMergeConflict: false,
1191
+ prCiStatus: CIStatus.SUCCESS,
1192
+ ratchetEnabled: true,
1193
+ ratchetState: RatchetState.MERGE_CONFLICT,
1194
+ ratchetActiveSessionId: null,
1195
+ ratchetDispatchSnapshotKey: previousSnapshotKey,
1196
+ prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
1197
+ ratchetDispatchOutcome: 'COMPLETED',
1198
+ ratchetDispatchRetryCount: 2,
1199
+ };
1200
+ vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({
1201
+ ciStatus: CIStatus.SUCCESS,
1202
+ snapshotKey: currentSnapshotKey,
1203
+ hasChangesRequested: false,
1204
+ hasMergeConflict: true,
1205
+ latestReviewActivityAtMs: null,
1206
+ statusCheckRollup: null,
1207
+ prState: 'OPEN',
1208
+ prNumber: 456,
1209
+ reviewComments: [],
1210
+ });
1211
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
1212
+ status: 'started',
1213
+ sessionId: 'fresh-pr-session',
1214
+ promptSent: true,
1215
+ });
1216
+ const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
1217
+ expect(result).toMatchObject({
1218
+ action: { type: 'TRIGGERED_FIXER' },
1219
+ });
1220
+ expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalledWith(workspace.id, {
1221
+ sessionId: 'fresh-pr-session',
1222
+ snapshotKey: currentSnapshotKey,
1223
+ retryCount: 0,
1224
+ });
1225
+ });
1012
1226
  it('settles the dispatch as DIED when ratchet session process is not running', async () => {
1013
1227
  const workspace = {
1014
1228
  id: 'ws-stale-active',
1015
1229
  prUrl: 'https://github.com/example/repo/pull/9',
1016
1230
  prNumber: 9,
1017
1231
  prState: 'OPEN',
1232
+ prReviewState: null,
1233
+ prHasMergeConflict: false,
1018
1234
  prCiStatus: CIStatus.UNKNOWN,
1019
1235
  ratchetEnabled: true,
1020
1236
  ratchetState: RatchetState.CI_FAILED,
1021
1237
  ratchetActiveSessionId: 'ratchet-session',
1022
- ratchetLastCiRunId: 'previous-snapshot-key',
1238
+ ratchetDispatchSnapshotKey: 'previous-snapshot-key',
1023
1239
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
1024
1240
  ratchetDispatchOutcome: 'RUNNING',
1025
1241
  ratchetDispatchRetryCount: 0,
1026
1242
  };
1027
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue({
1243
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
1028
1244
  id: 'ratchet-session',
1029
1245
  provider: 'CLAUDE',
1030
1246
  status: SessionStatus.RUNNING,
@@ -1032,7 +1248,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1032
1248
  vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(false);
1033
1249
  const result = await unsafeCoerce(ratchetService).checkActiveFixerSession(workspace);
1034
1250
  expect(result).toEqual({ kind: 'settled', outcome: 'DIED' });
1035
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith(workspace.id, 'ratchet-session', 'DIED');
1251
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith(workspace.id, 'ratchet-session', 'DIED');
1036
1252
  });
1037
1253
  it('settles the dispatch as DIED when ratchet session is not found in database', async () => {
1038
1254
  const workspace = {
@@ -1040,19 +1256,26 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1040
1256
  prUrl: 'https://github.com/example/repo/pull/12',
1041
1257
  prNumber: 12,
1042
1258
  prState: 'OPEN',
1259
+ prReviewState: null,
1260
+ prHasMergeConflict: false,
1043
1261
  prCiStatus: CIStatus.UNKNOWN,
1044
1262
  ratchetEnabled: true,
1045
1263
  ratchetState: RatchetState.CI_FAILED,
1046
1264
  ratchetActiveSessionId: 'deleted-session',
1047
- ratchetLastCiRunId: 'previous-snapshot-key',
1265
+ ratchetDispatchSnapshotKey: 'previous-snapshot-key',
1048
1266
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
1049
1267
  ratchetDispatchOutcome: 'RUNNING',
1050
1268
  ratchetDispatchRetryCount: 0,
1051
1269
  };
1052
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue(null);
1270
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue(null);
1271
+ const events = [];
1272
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
1273
+ events.push(event);
1274
+ });
1053
1275
  const result = await unsafeCoerce(ratchetService).checkActiveFixerSession(workspace);
1054
1276
  expect(result).toEqual({ kind: 'settled', outcome: 'DIED' });
1055
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith(workspace.id, 'deleted-session', 'DIED');
1277
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith(workspace.id, 'deleted-session', 'DIED');
1278
+ expect(events).toEqual([{ workspaceId: 'ws-missing-session' }]);
1056
1279
  });
1057
1280
  it('reports a concurrent end when another path already settled the dispatch', async () => {
1058
1281
  const workspace = {
@@ -1060,19 +1283,26 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1060
1283
  prUrl: 'https://github.com/example/repo/pull/12',
1061
1284
  prNumber: 12,
1062
1285
  prState: 'OPEN',
1286
+ prReviewState: null,
1287
+ prHasMergeConflict: false,
1063
1288
  prCiStatus: CIStatus.UNKNOWN,
1064
1289
  ratchetEnabled: true,
1065
1290
  ratchetState: RatchetState.CI_FAILED,
1066
1291
  ratchetActiveSessionId: 'finished-session',
1067
- ratchetLastCiRunId: 'previous-snapshot-key',
1292
+ ratchetDispatchSnapshotKey: 'previous-snapshot-key',
1068
1293
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
1069
1294
  ratchetDispatchOutcome: 'RUNNING',
1070
1295
  ratchetDispatchRetryCount: 0,
1071
1296
  };
1072
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue(null);
1073
- vi.mocked(workspaceAccessor.recordRatchetSessionEnd).mockResolvedValue(false);
1297
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue(null);
1298
+ vi.mocked(mockWorkspaceBridge.recordSessionEnd).mockResolvedValue(false);
1299
+ const events = [];
1300
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
1301
+ events.push(event);
1302
+ });
1074
1303
  const result = await unsafeCoerce(ratchetService).checkActiveFixerSession(workspace);
1075
1304
  expect(result).toEqual({ kind: 'ended_concurrently' });
1305
+ expect(events).toHaveLength(0);
1076
1306
  });
1077
1307
  it('waits for the next cycle when the fixer ends concurrently mid-check', async () => {
1078
1308
  const workspace = {
@@ -1080,11 +1310,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1080
1310
  prUrl: 'https://github.com/example/repo/pull/12',
1081
1311
  prNumber: 12,
1082
1312
  prState: 'OPEN',
1313
+ prReviewState: null,
1314
+ prHasMergeConflict: false,
1083
1315
  prCiStatus: CIStatus.UNKNOWN,
1084
1316
  ratchetEnabled: true,
1085
1317
  ratchetState: RatchetState.CI_FAILED,
1086
1318
  ratchetActiveSessionId: 'finished-session',
1087
- ratchetLastCiRunId: 'previous-snapshot-key',
1319
+ ratchetDispatchSnapshotKey: 'previous-snapshot-key',
1088
1320
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
1089
1321
  ratchetDispatchOutcome: 'RUNNING',
1090
1322
  ratchetDispatchRetryCount: 0,
@@ -1098,9 +1330,8 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1098
1330
  prState: 'OPEN',
1099
1331
  prNumber: 12,
1100
1332
  });
1101
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue(null);
1102
- vi.mocked(workspaceAccessor.recordRatchetSessionEnd).mockResolvedValue(false);
1103
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
1333
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue(null);
1334
+ vi.mocked(mockWorkspaceBridge.recordSessionEnd).mockResolvedValue(false);
1104
1335
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
1105
1336
  expect(result).toMatchObject({
1106
1337
  action: {
@@ -1116,16 +1347,18 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1116
1347
  prUrl: 'https://github.com/example/repo/pull/11',
1117
1348
  prNumber: 11,
1118
1349
  prState: 'OPEN',
1350
+ prReviewState: null,
1351
+ prHasMergeConflict: false,
1119
1352
  prCiStatus: CIStatus.UNKNOWN,
1120
1353
  ratchetEnabled: true,
1121
1354
  ratchetState: RatchetState.CI_FAILED,
1122
1355
  ratchetActiveSessionId: 'ratchet-session',
1123
- ratchetLastCiRunId: 'snapshot-key',
1356
+ ratchetDispatchSnapshotKey: 'snapshot-key',
1124
1357
  prReviewLastCheckedAt: new Date('2026-01-01T00:00:00Z'),
1125
1358
  ratchetDispatchOutcome: 'RUNNING',
1126
1359
  ratchetDispatchRetryCount: 0,
1127
1360
  };
1128
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue({
1361
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
1129
1362
  id: 'ratchet-session',
1130
1363
  provider: 'CLAUDE',
1131
1364
  status: SessionStatus.RUNNING,
@@ -1135,7 +1368,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1135
1368
  vi.mocked(mockSessionBridge.stopSession).mockResolvedValue();
1136
1369
  const result = await unsafeCoerce(ratchetService).checkActiveFixerSession(workspace);
1137
1370
  expect(result).toEqual({ kind: 'settled', outcome: 'COMPLETED' });
1138
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith(workspace.id, 'ratchet-session', 'COMPLETED');
1371
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith(workspace.id, 'ratchet-session', 'COMPLETED');
1139
1372
  expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('ratchet-session');
1140
1373
  });
1141
1374
  it('decides to trigger fixer when context is actionable', async () => {
@@ -1186,11 +1419,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1186
1419
  prUrl: 'https://github.com/example/repo/pull/10',
1187
1420
  prNumber: 10,
1188
1421
  prState: 'OPEN',
1422
+ prReviewState: null,
1423
+ prHasMergeConflict: false,
1189
1424
  prCiStatus: CIStatus.UNKNOWN,
1190
1425
  ratchetEnabled: true,
1191
1426
  ratchetState: RatchetState.READY,
1192
1427
  ratchetActiveSessionId: null,
1193
- ratchetLastCiRunId: 'prev-snapshot',
1428
+ ratchetDispatchSnapshotKey: 'prev-snapshot',
1194
1429
  prReviewLastCheckedAt: null,
1195
1430
  ratchetDispatchOutcome: null,
1196
1431
  ratchetDispatchRetryCount: 0,
@@ -1218,11 +1453,19 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1218
1453
  it('ignores review activity authored by the authenticated user', () => {
1219
1454
  const latestActivity = computeLatestReviewActivityAtMs({
1220
1455
  reviews: [
1221
- { submittedAt: '2026-01-02T00:00:00Z', author: { login: 'ratchet-bot' } },
1222
- { submittedAt: '2026-01-01T00:00:00Z', author: { login: 'reviewer' } },
1456
+ {
1457
+ submittedAt: '2026-01-02T00:00:00Z',
1458
+ author: { login: 'ratchet-bot' },
1459
+ state: 'CHANGES_REQUESTED',
1460
+ },
1461
+ {
1462
+ submittedAt: '2026-01-01T00:00:00Z',
1463
+ author: { login: 'reviewer' },
1464
+ state: 'CHANGES_REQUESTED',
1465
+ },
1223
1466
  ],
1224
1467
  comments: [{ updatedAt: '2026-01-02T01:00:00Z', author: { login: 'ratchet-bot' } }],
1225
- }, [{ updatedAt: '2026-01-01T02:00:00Z', author: { login: 'reviewer2' } }], 'ratchet-bot');
1468
+ }, [{ updatedAt: '2026-01-01T02:00:00Z', author: { login: 'reviewer2' } }], 'ratchet-bot', 'CHANGES_REQUESTED');
1226
1469
  expect(latestActivity).toBe(new Date('2026-01-01T02:00:00Z').getTime());
1227
1470
  });
1228
1471
  describe('determineRatchetState', () => {
@@ -1285,7 +1528,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1285
1528
  });
1286
1529
  });
1287
1530
  describe('computeDispatchSnapshotKey', () => {
1288
- const callComputeSnapshotKey = (ciStatus, hasChangesRequested, latestReviewActivityAtMs, statusChecks) => computeDispatchSnapshotKey(ciStatus, hasChangesRequested, latestReviewActivityAtMs, statusChecks);
1531
+ const callComputeSnapshotKey = (ciStatus, hasChangesRequested, latestReviewActivityAtMs, statusChecks) => computeDispatchSnapshotKey(123, ciStatus, hasChangesRequested, latestReviewActivityAtMs, statusChecks);
1289
1532
  it('includes CI status in key for non-failure states', () => {
1290
1533
  const key = callComputeSnapshotKey(CIStatus.SUCCESS, false, null, null);
1291
1534
  expect(key).toContain('ci:SUCCESS');
@@ -1329,36 +1572,60 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1329
1572
  });
1330
1573
  });
1331
1574
  describe('computeLatestReviewActivityAtMs edge cases', () => {
1332
- const callCompute = (prDetails, reviewComments, authenticatedUsername) => computeLatestReviewActivityAtMs(prDetails, reviewComments, authenticatedUsername);
1575
+ const callCompute = (prDetails, reviewComments, authenticatedUsername) => computeLatestReviewActivityAtMs(prDetails, reviewComments, authenticatedUsername, 'CHANGES_REQUESTED');
1333
1576
  it('returns null when there are no reviews or comments', () => {
1334
1577
  expect(callCompute({ reviews: [], comments: [] }, [], null)).toBeNull();
1335
1578
  });
1336
1579
  it('returns null when all activity is from the authenticated user', () => {
1337
1580
  expect(callCompute({
1338
- reviews: [{ submittedAt: '2026-01-01T00:00:00Z', author: { login: 'me' } }],
1581
+ reviews: [
1582
+ {
1583
+ submittedAt: '2026-01-01T00:00:00Z',
1584
+ author: { login: 'me' },
1585
+ state: 'CHANGES_REQUESTED',
1586
+ },
1587
+ ],
1339
1588
  comments: [{ updatedAt: '2026-01-02T00:00:00Z', author: { login: 'me' } }],
1340
1589
  }, [{ updatedAt: '2026-01-03T00:00:00Z', author: { login: 'me' } }], 'me')).toBeNull();
1341
1590
  });
1342
1591
  it('does not filter when authenticatedUsername is null', () => {
1343
1592
  const result = callCompute({
1344
- reviews: [{ submittedAt: '2026-01-01T00:00:00Z', author: { login: 'bot' } }],
1593
+ reviews: [
1594
+ {
1595
+ submittedAt: '2026-01-01T00:00:00Z',
1596
+ author: { login: 'bot' },
1597
+ state: 'CHANGES_REQUESTED',
1598
+ },
1599
+ ],
1345
1600
  comments: [],
1346
1601
  }, [], null);
1347
1602
  expect(result).toBe(new Date('2026-01-01T00:00:00Z').getTime());
1348
1603
  });
1349
- it('ignores reviews that have null submittedAt', () => {
1604
+ it('ignores reviews with null submittedAt and ordinary PR comments', () => {
1350
1605
  const result = callCompute({
1351
- reviews: [{ submittedAt: null, author: { login: 'reviewer' } }],
1606
+ reviews: [
1607
+ {
1608
+ submittedAt: null,
1609
+ author: { login: 'reviewer' },
1610
+ state: 'CHANGES_REQUESTED',
1611
+ },
1612
+ ],
1352
1613
  comments: [{ updatedAt: '2026-01-02T00:00:00Z', author: { login: 'commenter' } }],
1353
1614
  }, [], null);
1354
- expect(result).toBe(new Date('2026-01-02T00:00:00Z').getTime());
1615
+ expect(result).toBeNull();
1355
1616
  });
1356
1617
  it('returns the latest timestamp across all sources', () => {
1357
1618
  const result = callCompute({
1358
- reviews: [{ submittedAt: '2026-01-01T00:00:00Z', author: { login: 'a' } }],
1619
+ reviews: [
1620
+ {
1621
+ submittedAt: '2026-01-01T00:00:00Z',
1622
+ author: { login: 'a' },
1623
+ state: 'CHANGES_REQUESTED',
1624
+ },
1625
+ ],
1359
1626
  comments: [{ updatedAt: '2026-01-03T00:00:00Z', author: { login: 'b' } }],
1360
1627
  }, [{ updatedAt: '2026-01-02T00:00:00Z', author: { login: 'c' } }], null);
1361
- expect(result).toBe(new Date('2026-01-03T00:00:00Z').getTime());
1628
+ expect(result).toBe(new Date('2026-01-02T00:00:00Z').getTime());
1362
1629
  });
1363
1630
  });
1364
1631
  describe('processWorkspace error handling', () => {
@@ -1370,13 +1637,12 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1370
1637
  ratchetEnabled: true,
1371
1638
  ratchetState: RatchetState.IDLE,
1372
1639
  ratchetActiveSessionId: null,
1373
- ratchetLastCiRunId: null,
1640
+ ratchetDispatchSnapshotKey: null,
1374
1641
  prReviewLastCheckedAt: null,
1375
1642
  ratchetDispatchOutcome: null,
1376
1643
  ratchetDispatchRetryCount: 0,
1377
1644
  };
1378
1645
  vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue(null);
1379
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
1380
1646
  const result = await unsafeCoerce(ratchetService).processWorkspace(workspace);
1381
1647
  expect(result).toMatchObject({
1382
1648
  action: { type: 'ERROR', error: 'Failed to fetch PR state' },
@@ -1391,7 +1657,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1391
1657
  ratchetEnabled: true,
1392
1658
  ratchetState: RatchetState.IDLE,
1393
1659
  ratchetActiveSessionId: null,
1394
- ratchetLastCiRunId: null,
1660
+ ratchetDispatchSnapshotKey: null,
1395
1661
  prReviewLastCheckedAt: null,
1396
1662
  ratchetDispatchOutcome: null,
1397
1663
  ratchetDispatchRetryCount: 0,
@@ -1402,10 +1668,10 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1402
1668
  action: { type: 'WAITING', reason: 'recently_fetched' },
1403
1669
  newState: RatchetState.IDLE,
1404
1670
  });
1405
- expect(workspaceAccessor.update).not.toHaveBeenCalled();
1671
+ expect(workspaceRatchetService.clearActiveSession).not.toHaveBeenCalled();
1406
1672
  });
1407
1673
  it('returns WAITING when shutting down', async () => {
1408
- unsafeCoerce(ratchetService).isShuttingDown = true;
1674
+ setRatchetShuttingDown(true);
1409
1675
  const workspace = {
1410
1676
  id: 'ws-shutdown',
1411
1677
  prUrl: 'https://github.com/example/repo/pull/1',
@@ -1413,7 +1679,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1413
1679
  ratchetEnabled: true,
1414
1680
  ratchetState: RatchetState.CI_FAILED,
1415
1681
  ratchetActiveSessionId: null,
1416
- ratchetLastCiRunId: null,
1682
+ ratchetDispatchSnapshotKey: null,
1417
1683
  prReviewLastCheckedAt: null,
1418
1684
  ratchetDispatchOutcome: null,
1419
1685
  ratchetDispatchRetryCount: 0,
@@ -1433,7 +1699,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1433
1699
  ratchetEnabled: true,
1434
1700
  ratchetState: RatchetState.CI_FAILED,
1435
1701
  ratchetActiveSessionId: null,
1436
- ratchetLastCiRunId: null,
1702
+ ratchetDispatchSnapshotKey: null,
1437
1703
  prReviewLastCheckedAt: null,
1438
1704
  ratchetDispatchOutcome: null,
1439
1705
  ratchetDispatchRetryCount: 0,
@@ -1449,18 +1715,41 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1449
1715
  });
1450
1716
  describe('checkAllWorkspaces', () => {
1451
1717
  it('returns zero counts when shutting down', async () => {
1452
- unsafeCoerce(ratchetService).isShuttingDown = true;
1718
+ setRatchetShuttingDown(true);
1453
1719
  const result = await ratchetService.checkAllWorkspaces();
1454
1720
  expect(result).toEqual({ checked: 0, stateChanges: 0, actionsTriggered: 0, results: [] });
1455
1721
  });
1456
1722
  it('returns zero counts when no workspaces have PRs', async () => {
1457
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue([]);
1723
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue([]);
1458
1724
  const result = await ratchetService.checkAllWorkspaces();
1459
1725
  expect(result).toEqual({ checked: 0, stateChanges: 0, actionsTriggered: 0, results: [] });
1460
1726
  });
1727
+ it('loads the global review trigger mode once for the whole batch', async () => {
1728
+ const workspaces = ['ws-one', 'ws-two'].map((id) => ({
1729
+ id,
1730
+ prUrl: `https://github.com/example/repo/pull/${id}`,
1731
+ prNumber: 1,
1732
+ prState: 'OPEN',
1733
+ prReviewState: null,
1734
+ prHasMergeConflict: false,
1735
+ prCiStatus: CIStatus.UNKNOWN,
1736
+ ratchetEnabled: true,
1737
+ ratchetState: RatchetState.IDLE,
1738
+ ratchetActiveSessionId: null,
1739
+ ratchetDispatchSnapshotKey: null,
1740
+ prReviewLastCheckedAt: null,
1741
+ ratchetDispatchOutcome: null,
1742
+ ratchetDispatchRetryCount: 0,
1743
+ }));
1744
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue(workspaces);
1745
+ vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue({ skipped: true, reason: 'recently_fetched' });
1746
+ const result = await ratchetService.checkAllWorkspaces();
1747
+ expect(result.checked).toBe(2);
1748
+ expect(userSettingsService.get).toHaveBeenCalledTimes(1);
1749
+ });
1461
1750
  it('returns an error result when one workspace check times out', async () => {
1462
1751
  unsafeCoerce(ratchetService).workspaceCheckTimeoutMs = 5;
1463
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue([
1752
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue([
1464
1753
  {
1465
1754
  id: 'ws-timeout',
1466
1755
  prUrl: 'https://github.com/example/repo/pull/1',
@@ -1472,7 +1761,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1472
1761
  ratchetEnabled: true,
1473
1762
  ratchetState: RatchetState.IDLE,
1474
1763
  ratchetActiveSessionId: null,
1475
- ratchetLastCiRunId: null,
1764
+ ratchetDispatchSnapshotKey: null,
1476
1765
  prReviewLastCheckedAt: null,
1477
1766
  ratchetDispatchOutcome: null,
1478
1767
  ratchetDispatchRetryCount: 0,
@@ -1488,7 +1777,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1488
1777
  ratchetEnabled: true,
1489
1778
  ratchetState: RatchetState.CI_RUNNING,
1490
1779
  ratchetActiveSessionId: null,
1491
- ratchetLastCiRunId: null,
1780
+ ratchetDispatchSnapshotKey: null,
1492
1781
  prReviewLastCheckedAt: null,
1493
1782
  ratchetDispatchOutcome: null,
1494
1783
  ratchetDispatchRetryCount: 0,
@@ -1530,18 +1819,20 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1530
1819
  prUrl: `https://github.com/example/repo/pull/${index + 1}`,
1531
1820
  prNumber: index + 1,
1532
1821
  prState: 'OPEN',
1822
+ prReviewState: null,
1823
+ prHasMergeConflict: false,
1533
1824
  prCiStatus: CIStatus.UNKNOWN,
1534
1825
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
1535
1826
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
1536
1827
  ratchetEnabled: true,
1537
1828
  ratchetState: RatchetState.IDLE,
1538
1829
  ratchetActiveSessionId: null,
1539
- ratchetLastCiRunId: null,
1830
+ ratchetDispatchSnapshotKey: null,
1540
1831
  prReviewLastCheckedAt: null,
1541
1832
  ratchetDispatchOutcome: null,
1542
1833
  ratchetDispatchRetryCount: 0,
1543
1834
  }));
1544
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue(workspaces);
1835
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue(workspaces);
1545
1836
  const processWorkspaceSpy = vi.spyOn(unsafeCoerce(ratchetService), 'processWorkspace');
1546
1837
  processWorkspaceSpy.mockImplementation((workspace, _opts, signal) => {
1547
1838
  if (workspace.id !== 'ws-queued-timeout-3') {
@@ -1557,7 +1848,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1557
1848
  });
1558
1849
  });
1559
1850
  const result = await ratchetService.checkAllWorkspaces();
1560
- expect(processWorkspaceSpy).toHaveBeenCalledWith(expect.objectContaining({ id: 'ws-queued-timeout-3' }), undefined, expect.any(AbortSignal), expect.any(Function));
1851
+ expect(processWorkspaceSpy).toHaveBeenCalledWith(expect.objectContaining({ id: 'ws-queued-timeout-3' }), undefined, expect.any(AbortSignal), expect.any(Function), 'CHANGES_REQUESTED');
1561
1852
  expect(result.results[3]).toMatchObject({
1562
1853
  workspaceId: 'ws-queued-timeout-3',
1563
1854
  action: { type: 'WAITING', reason: 'ran after queue' },
@@ -1572,7 +1863,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1572
1863
  ratchetEnabled: true,
1573
1864
  ratchetState: RatchetState.IDLE,
1574
1865
  };
1575
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue([workspace]);
1866
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue([workspace]);
1576
1867
  let finishCommit;
1577
1868
  const commitBarrier = new Promise((resolve) => {
1578
1869
  finishCommit = resolve;
@@ -1603,18 +1894,20 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1603
1894
  prUrl: `https://github.com/example/repo/pull/${index + 1}`,
1604
1895
  prNumber: index + 1,
1605
1896
  prState: 'OPEN',
1897
+ prReviewState: null,
1898
+ prHasMergeConflict: false,
1606
1899
  prCiStatus: CIStatus.UNKNOWN,
1607
1900
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
1608
1901
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
1609
1902
  ratchetEnabled: true,
1610
1903
  ratchetState: RatchetState.IDLE,
1611
1904
  ratchetActiveSessionId: null,
1612
- ratchetLastCiRunId: null,
1905
+ ratchetDispatchSnapshotKey: null,
1613
1906
  prReviewLastCheckedAt: null,
1614
1907
  ratchetDispatchOutcome: null,
1615
1908
  ratchetDispatchRetryCount: 0,
1616
1909
  }));
1617
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue(workspaces);
1910
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue(workspaces);
1618
1911
  let active = 0;
1619
1912
  let maximumActive = 0;
1620
1913
  let releaseChecks;
@@ -1659,98 +1952,10 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1659
1952
  vi.useRealTimers();
1660
1953
  });
1661
1954
  });
1662
- describe('markPrClosed', () => {
1955
+ describe('check abort safety', () => {
1663
1956
  afterEach(() => {
1664
1957
  ratchetService.removeAllListeners();
1665
1958
  });
1666
- it('resets ratchet state to IDLE via CAS on fromState and emits a state change', async () => {
1667
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
1668
- id: 'ws-closed',
1669
- ratchetEnabled: true,
1670
- ratchetState: RatchetState.CI_FAILED,
1671
- });
1672
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled).mockResolvedValue(true);
1673
- const stateEvents = [];
1674
- ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
1675
- stateEvents.push(event);
1676
- });
1677
- await ratchetService.markPrClosed('ws-closed');
1678
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).toHaveBeenCalledWith('ws-closed', RatchetState.CI_FAILED, {
1679
- ratchetState: RatchetState.IDLE,
1680
- ratchetLastCheckedAt: expect.any(Date),
1681
- });
1682
- expect(stateEvents).toEqual([
1683
- {
1684
- workspaceId: 'ws-closed',
1685
- fromState: RatchetState.CI_FAILED,
1686
- toState: RatchetState.IDLE,
1687
- },
1688
- ]);
1689
- });
1690
- it('emits the fromState that actually won the compare-and-swap after losing a race', async () => {
1691
- vi.mocked(workspaceAccessor.findById)
1692
- .mockResolvedValueOnce({
1693
- id: 'ws-closed',
1694
- ratchetEnabled: true,
1695
- ratchetState: RatchetState.CI_FAILED,
1696
- })
1697
- .mockResolvedValueOnce({
1698
- id: 'ws-closed',
1699
- ratchetEnabled: true,
1700
- ratchetState: RatchetState.CI_RUNNING,
1701
- });
1702
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled)
1703
- .mockResolvedValueOnce(false)
1704
- .mockResolvedValueOnce(true);
1705
- const stateEvents = [];
1706
- ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
1707
- stateEvents.push(event);
1708
- });
1709
- await ratchetService.markPrClosed('ws-closed');
1710
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).toHaveBeenNthCalledWith(1, 'ws-closed', RatchetState.CI_FAILED, expect.objectContaining({ ratchetState: RatchetState.IDLE }));
1711
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).toHaveBeenNthCalledWith(2, 'ws-closed', RatchetState.CI_RUNNING, expect.objectContaining({ ratchetState: RatchetState.IDLE }));
1712
- expect(stateEvents).toEqual([
1713
- {
1714
- workspaceId: 'ws-closed',
1715
- fromState: RatchetState.CI_RUNNING,
1716
- toState: RatchetState.IDLE,
1717
- },
1718
- ]);
1719
- });
1720
- it('is a no-op when ratchet state is already IDLE', async () => {
1721
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
1722
- id: 'ws-closed',
1723
- ratchetEnabled: true,
1724
- ratchetState: RatchetState.IDLE,
1725
- });
1726
- await ratchetService.markPrClosed('ws-closed');
1727
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).not.toHaveBeenCalled();
1728
- });
1729
- it('does not emit when ratcheting was disabled concurrently', async () => {
1730
- vi.mocked(workspaceAccessor.findById)
1731
- .mockResolvedValueOnce({
1732
- id: 'ws-closed',
1733
- ratchetEnabled: true,
1734
- ratchetState: RatchetState.CI_FAILED,
1735
- })
1736
- .mockResolvedValueOnce({
1737
- id: 'ws-closed',
1738
- ratchetEnabled: false,
1739
- ratchetState: RatchetState.IDLE,
1740
- });
1741
- vi.mocked(workspaceAccessor.transitionRatchetStateIfEnabled).mockResolvedValue(false);
1742
- const stateEvents = [];
1743
- ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
1744
- stateEvents.push(event);
1745
- });
1746
- await ratchetService.markPrClosed('ws-closed');
1747
- expect(stateEvents).toEqual([]);
1748
- });
1749
- it('is a no-op when workspace is not found', async () => {
1750
- vi.mocked(workspaceAccessor.findById).mockResolvedValue(null);
1751
- await ratchetService.markPrClosed('ws-missing');
1752
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).not.toHaveBeenCalled();
1753
- });
1754
1959
  it('does not continue to side effects after a timed-out await completes', async () => {
1755
1960
  unsafeCoerce(ratchetService).workspaceCheckTimeoutMs = 5;
1756
1961
  const workspace = {
@@ -1758,18 +1963,20 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1758
1963
  prUrl: 'https://github.com/example/repo/pull/1',
1759
1964
  prNumber: 1,
1760
1965
  prState: 'OPEN',
1966
+ prReviewState: null,
1967
+ prHasMergeConflict: false,
1761
1968
  prCiStatus: CIStatus.FAILURE,
1762
1969
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
1763
1970
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
1764
1971
  ratchetEnabled: true,
1765
1972
  ratchetState: RatchetState.IDLE,
1766
1973
  ratchetActiveSessionId: null,
1767
- ratchetLastCiRunId: null,
1974
+ ratchetDispatchSnapshotKey: null,
1768
1975
  prReviewLastCheckedAt: null,
1769
1976
  ratchetDispatchOutcome: null,
1770
1977
  ratchetDispatchRetryCount: 0,
1771
1978
  };
1772
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue([workspace]);
1979
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue([workspace]);
1773
1980
  let releaseFetch;
1774
1981
  const fetchBarrier = new Promise((resolve) => {
1775
1982
  releaseFetch = resolve;
@@ -1794,18 +2001,18 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1794
2001
  releaseFetch();
1795
2002
  await new Promise((resolve) => setTimeout(resolve, 0));
1796
2003
  expect(finishSpy).not.toHaveBeenCalled();
1797
- expect(workspaceAccessor.transitionRatchetStateIfEnabled).not.toHaveBeenCalled();
2004
+ expect(workspaceRatchetService.recordCheckIfEnabled).not.toHaveBeenCalled();
1798
2005
  });
1799
2006
  });
1800
2007
  describe('checkWorkspaceById', () => {
1801
2008
  it('returns null when shutting down', async () => {
1802
- unsafeCoerce(ratchetService).isShuttingDown = true;
2009
+ setRatchetShuttingDown(true);
1803
2010
  const result = await ratchetService.checkWorkspaceById('ws-1');
1804
2011
  expect(result).toBeNull();
1805
2012
  });
1806
2013
  it('returns null when workspace not found', async () => {
1807
- unsafeCoerce(ratchetService).isShuttingDown = false;
1808
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(null);
2014
+ setRatchetShuttingDown(false);
2015
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(null);
1809
2016
  const result = await ratchetService.checkWorkspaceById('ws-nonexistent');
1810
2017
  expect(result).toBeNull();
1811
2018
  });
@@ -1815,22 +2022,24 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1815
2022
  prUrl: 'https://github.com/example/repo/pull/9',
1816
2023
  prNumber: 9,
1817
2024
  prState: 'OPEN',
2025
+ prReviewState: null,
2026
+ prHasMergeConflict: false,
1818
2027
  prCiStatus: CIStatus.UNKNOWN,
1819
2028
  ratchetEnabled: true,
1820
2029
  ratchetState: RatchetState.CI_RUNNING,
1821
2030
  ratchetActiveSessionId: null,
1822
- ratchetLastCiRunId: null,
2031
+ ratchetDispatchSnapshotKey: null,
1823
2032
  prReviewLastCheckedAt: null,
1824
2033
  ratchetDispatchOutcome: null,
1825
2034
  ratchetDispatchRetryCount: 0,
1826
2035
  };
1827
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(workspace);
2036
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(workspace);
1828
2037
  vi.mocked(mockGitHubBridge.extractPRInfo).mockReturnValue({
1829
2038
  owner: 'example',
1830
2039
  repo: 'repo',
1831
2040
  number: 9,
1832
2041
  });
1833
- vi.mocked(mockGitHubBridge.isRecentlyFetched).mockReturnValue(true);
2042
+ fakeCoordinator.cooldownActive = true;
1834
2043
  const result = await ratchetService.checkWorkspaceById('ws-cooldown');
1835
2044
  expect(result?.action).toEqual({ type: 'WAITING', reason: 'recently_fetched' });
1836
2045
  expect(mockGitHubBridge.getPRFullDetails).not.toHaveBeenCalled();
@@ -1841,25 +2050,28 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1841
2050
  prUrl: 'https://github.com/example/repo/pull/9',
1842
2051
  prNumber: 9,
1843
2052
  prState: 'OPEN',
2053
+ prReviewState: null,
2054
+ prHasMergeConflict: false,
1844
2055
  prCiStatus: CIStatus.UNKNOWN,
1845
2056
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
1846
2057
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
1847
2058
  ratchetEnabled: true,
1848
2059
  ratchetState: RatchetState.CI_RUNNING,
1849
2060
  ratchetActiveSessionId: null,
1850
- ratchetLastCiRunId: null,
2061
+ ratchetDispatchSnapshotKey: null,
1851
2062
  prReviewLastCheckedAt: null,
1852
2063
  ratchetDispatchOutcome: null,
1853
2064
  ratchetDispatchRetryCount: 0,
1854
2065
  };
1855
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(workspace);
2066
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(workspace);
1856
2067
  vi.mocked(mockGitHubBridge.extractPRInfo).mockReturnValue({
1857
2068
  owner: 'example',
1858
2069
  repo: 'repo',
1859
2070
  number: 9,
1860
2071
  });
1861
- vi.mocked(mockGitHubBridge.isRecentlyFetched).mockReturnValue(true);
2072
+ fakeCoordinator.cooldownActive = true;
1862
2073
  vi.mocked(mockGitHubBridge.getPRFullDetails).mockResolvedValue({
2074
+ isDraft: false,
1863
2075
  state: 'OPEN',
1864
2076
  number: 9,
1865
2077
  url: 'https://github.com/example/repo/pull/9',
@@ -1884,25 +2096,28 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1884
2096
  prUrl: 'https://github.com/example/repo/pull/9',
1885
2097
  prNumber: 9,
1886
2098
  prState: 'OPEN',
2099
+ prReviewState: null,
2100
+ prHasMergeConflict: false,
1887
2101
  prCiStatus: CIStatus.UNKNOWN,
1888
2102
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
1889
2103
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
1890
2104
  ratchetEnabled: true,
1891
2105
  ratchetState: RatchetState.CI_RUNNING,
1892
2106
  ratchetActiveSessionId: null,
1893
- ratchetLastCiRunId: null,
2107
+ ratchetDispatchSnapshotKey: null,
1894
2108
  prReviewLastCheckedAt: null,
1895
2109
  ratchetDispatchOutcome: null,
1896
2110
  ratchetDispatchRetryCount: 0,
1897
2111
  };
1898
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(workspace);
2112
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(workspace);
1899
2113
  vi.mocked(mockGitHubBridge.extractPRInfo).mockReturnValue({
1900
2114
  owner: 'example',
1901
2115
  repo: 'repo',
1902
2116
  number: 9,
1903
2117
  });
1904
- vi.mocked(mockGitHubBridge.isFetchInFlight).mockReturnValueOnce(true).mockReturnValue(false);
2118
+ fakeCoordinator.inFlightSkips = 1;
1905
2119
  vi.mocked(mockGitHubBridge.getPRFullDetails).mockResolvedValue({
2120
+ isDraft: false,
1906
2121
  state: 'OPEN',
1907
2122
  number: 9,
1908
2123
  url: 'https://github.com/example/repo/pull/9',
@@ -1927,48 +2142,52 @@ describe('ratchet service (state-change + idle dispatch)', () => {
1927
2142
  prUrl: 'https://github.com/example/repo/pull/9',
1928
2143
  prNumber: 9,
1929
2144
  prState: 'OPEN',
2145
+ prReviewState: null,
2146
+ prHasMergeConflict: false,
1930
2147
  prCiStatus: CIStatus.UNKNOWN,
1931
2148
  ratchetEnabled: true,
1932
2149
  ratchetState: RatchetState.CI_RUNNING,
1933
2150
  ratchetActiveSessionId: null,
1934
- ratchetLastCiRunId: null,
2151
+ ratchetDispatchSnapshotKey: null,
1935
2152
  prReviewLastCheckedAt: null,
1936
2153
  ratchetDispatchOutcome: null,
1937
2154
  ratchetDispatchRetryCount: 0,
1938
2155
  };
1939
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(workspace);
2156
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(workspace);
1940
2157
  vi.mocked(mockGitHubBridge.extractPRInfo).mockReturnValue({
1941
2158
  owner: 'example',
1942
2159
  repo: 'repo',
1943
2160
  number: 9,
1944
2161
  });
1945
- vi.mocked(mockGitHubBridge.isFetchInFlight).mockReturnValue(true);
2162
+ fakeCoordinator.inFlightSkips = Number.POSITIVE_INFINITY;
1946
2163
  const result = await ratchetService.checkWorkspaceById('ws-rerun-cap', {
1947
2164
  bypassPrFetchCooldown: true,
1948
2165
  });
1949
2166
  expect(result?.action).toEqual({ type: 'WAITING', reason: 'recently_fetched' });
1950
- expect(mockGitHubBridge.isFetchInFlight).toHaveBeenCalledTimes(2);
2167
+ expect(mockGitHubBridge.coordinatePrFetch).toHaveBeenCalledTimes(2);
1951
2168
  expect(mockGitHubBridge.getPRFullDetails).not.toHaveBeenCalled();
1952
2169
  });
1953
2170
  it('deduplicates concurrent checks for the same workspace', async () => {
1954
- unsafeCoerce(ratchetService).isShuttingDown = false;
2171
+ setRatchetShuttingDown(false);
1955
2172
  const workspace = {
1956
2173
  id: 'ws-1',
1957
2174
  prUrl: 'https://github.com/example/repo/pull/1',
1958
2175
  prNumber: 1,
1959
2176
  prState: 'OPEN',
2177
+ prReviewState: null,
2178
+ prHasMergeConflict: false,
1960
2179
  prCiStatus: CIStatus.UNKNOWN,
1961
2180
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
1962
2181
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
1963
2182
  ratchetEnabled: true,
1964
2183
  ratchetState: RatchetState.IDLE,
1965
2184
  ratchetActiveSessionId: null,
1966
- ratchetLastCiRunId: null,
2185
+ ratchetDispatchSnapshotKey: null,
1967
2186
  prReviewLastCheckedAt: null,
1968
2187
  ratchetDispatchOutcome: null,
1969
2188
  ratchetDispatchRetryCount: 0,
1970
2189
  };
1971
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(workspace);
2190
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(workspace);
1972
2191
  let releaseCheck;
1973
2192
  const checkBarrier = new Promise((resolve) => {
1974
2193
  releaseCheck = () => resolve();
@@ -2009,8 +2228,8 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2009
2228
  makeWorkspace('blocker-3'),
2010
2229
  makeWorkspace('queued-target'),
2011
2230
  ];
2012
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue(workspaces);
2013
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(workspaces[3]);
2231
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue(workspaces);
2232
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(workspaces[3]);
2014
2233
  let releaseBlockers;
2015
2234
  const blockerBarrier = new Promise((resolve) => {
2016
2235
  releaseBlockers = resolve;
@@ -2057,7 +2276,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2057
2276
  makeWorkspace('timed-out-3'),
2058
2277
  makeWorkspace('after-timeouts'),
2059
2278
  ];
2060
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue(workspaces);
2279
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue(workspaces);
2061
2280
  let finishCleanup;
2062
2281
  const cleanupBarrier = new Promise((resolve) => {
2063
2282
  finishCleanup = resolve;
@@ -2103,8 +2322,8 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2103
2322
  makeWorkspace('batch-blocker-3'),
2104
2323
  ];
2105
2324
  const directWorkspace = makeWorkspace('direct-target');
2106
- vi.mocked(workspaceAccessor.findWithPRsForRatchet).mockResolvedValue(batchWorkspaces);
2107
- vi.mocked(workspaceAccessor.findForRatchetById).mockResolvedValue(directWorkspace);
2325
+ vi.mocked(workspaceRatchetService.findCandidates).mockResolvedValue(batchWorkspaces);
2326
+ vi.mocked(workspaceRatchetService.findCandidateById).mockResolvedValue(directWorkspace);
2108
2327
  let releaseBatch;
2109
2328
  const batchBarrier = new Promise((resolve) => {
2110
2329
  releaseBatch = resolve;
@@ -2138,7 +2357,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2138
2357
  describe('decideRatchetAction edge cases', () => {
2139
2358
  const callDecide = (context) => unsafeCoerce(ratchetService).decideRatchetAction(context);
2140
2359
  const arrangeOtherWorkingSession = () => {
2141
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([
2360
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([
2142
2361
  { id: 'other-session' },
2143
2362
  ]);
2144
2363
  vi.mocked(mockSessionBridge.isSessionWorking).mockReturnValue(true);
@@ -2268,7 +2487,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2268
2487
  expect(result).toEqual({ kind: 'none' });
2269
2488
  });
2270
2489
  it('settles a non-RUNNING session as COMPLETED when its status is not FAILED', async () => {
2271
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue({
2490
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
2272
2491
  id: 'completed-session',
2273
2492
  provider: 'CLAUDE',
2274
2493
  status: SessionStatus.IDLE,
@@ -2278,10 +2497,10 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2278
2497
  ratchetActiveSessionId: 'completed-session',
2279
2498
  });
2280
2499
  expect(result).toEqual({ kind: 'settled', outcome: 'COMPLETED' });
2281
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith('ws-3', 'completed-session', 'COMPLETED');
2500
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith('ws-3', 'completed-session', 'COMPLETED');
2282
2501
  });
2283
2502
  it('settles a FAILED session as DIED', async () => {
2284
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue({
2503
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
2285
2504
  id: 'failed-session',
2286
2505
  provider: 'CLAUDE',
2287
2506
  status: SessionStatus.FAILED,
@@ -2291,27 +2510,63 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2291
2510
  ratchetActiveSessionId: 'failed-session',
2292
2511
  });
2293
2512
  expect(result).toEqual({ kind: 'settled', outcome: 'DIED' });
2294
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith('ws-4', 'failed-session', 'DIED');
2513
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith('ws-4', 'failed-session', 'DIED');
2295
2514
  });
2296
- it('settles a provider-mismatched session as DIED and stops it', async () => {
2297
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue({
2515
+ it('waits for provider-mismatch cleanup before returning a successful settlement', async () => {
2516
+ let finishStop;
2517
+ const pendingStop = new Promise((resolve) => {
2518
+ finishStop = resolve;
2519
+ });
2520
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
2298
2521
  id: 'codex-session',
2299
2522
  provider: 'CODEX',
2300
2523
  status: SessionStatus.RUNNING,
2301
2524
  });
2302
- vi.mocked(mockSessionBridge.stopSession).mockResolvedValue();
2303
- const result = await callCheckActiveFixerSession({
2525
+ vi.mocked(mockSessionBridge.stopSession).mockReturnValue(pendingStop);
2526
+ const check = callCheckActiveFixerSession({
2304
2527
  id: 'ws-5',
2305
2528
  ratchetActiveSessionId: 'codex-session',
2306
2529
  defaultSessionProvider: 'WORKSPACE_DEFAULT',
2307
2530
  ratchetSessionProvider: 'WORKSPACE_DEFAULT',
2308
2531
  });
2309
- expect(result).toEqual({ kind: 'settled', outcome: 'DIED' });
2310
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith('ws-5', 'codex-session', 'DIED');
2311
- expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('codex-session');
2532
+ await vi.waitFor(() => expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('codex-session'));
2533
+ await expect(Promise.race([check, Promise.resolve({ kind: 'cleanup_pending' })])).resolves.toEqual({ kind: 'cleanup_pending' });
2534
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith('ws-5', 'codex-session', 'DIED');
2535
+ finishStop();
2536
+ await expect(check).resolves.toEqual({ kind: 'settled', outcome: 'DIED' });
2537
+ });
2538
+ it('returns cancellation without waiting for provider-mismatch cleanup', async () => {
2539
+ const controller = new AbortController();
2540
+ const timeoutError = new Error('Workspace check timed out');
2541
+ let finishStop;
2542
+ const pendingStop = new Promise((resolve) => {
2543
+ finishStop = resolve;
2544
+ });
2545
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
2546
+ id: 'codex-session',
2547
+ provider: 'CODEX',
2548
+ status: SessionStatus.RUNNING,
2549
+ });
2550
+ vi.mocked(mockSessionBridge.stopSession).mockReturnValue(pendingStop);
2551
+ vi.mocked(mockWorkspaceBridge.recordSessionEnd).mockImplementation(() => {
2552
+ controller.abort(timeoutError);
2553
+ return Promise.resolve(true);
2554
+ });
2555
+ const check = unsafeCoerce(ratchetService).checkActiveFixerSession({
2556
+ id: 'ws-provider-mismatch-cancelled-after-settlement',
2557
+ ratchetActiveSessionId: 'codex-session',
2558
+ defaultSessionProvider: 'WORKSPACE_DEFAULT',
2559
+ ratchetSessionProvider: 'WORKSPACE_DEFAULT',
2560
+ }, controller.signal);
2561
+ const outcome = check.then((value) => ({ status: 'resolved', value }), (error) => ({ status: 'rejected', error }));
2562
+ await vi.waitFor(() => expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('codex-session'));
2563
+ await expect(Promise.race([outcome, Promise.resolve({ status: 'pending' })])).resolves.toEqual({ status: 'rejected', error: timeoutError });
2564
+ expect(mockWorkspaceBridge.recordSessionEnd).toHaveBeenCalledWith('ws-provider-mismatch-cancelled-after-settlement', 'codex-session', 'DIED');
2565
+ finishStop();
2566
+ await pendingStop;
2312
2567
  });
2313
2568
  it('returns the active fixer when the session is working', async () => {
2314
- vi.mocked(agentSessionAccessor.findById).mockResolvedValue({
2569
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue({
2315
2570
  id: 'working-session',
2316
2571
  provider: 'CLAUDE',
2317
2572
  status: SessionStatus.RUNNING,
@@ -2326,28 +2581,111 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2326
2581
  kind: 'active',
2327
2582
  action: { type: 'FIXER_ACTIVE', sessionId: 'working-session' },
2328
2583
  });
2329
- expect(workspaceAccessor.recordRatchetSessionEnd).not.toHaveBeenCalled();
2584
+ expect(mockWorkspaceBridge.recordSessionEnd).not.toHaveBeenCalled();
2330
2585
  });
2331
2586
  it('does not settle or stop a fixer after cancellation during session lookup', async () => {
2332
2587
  const controller = new AbortController();
2333
2588
  const timeoutError = new Error('Workspace check timed out');
2334
2589
  let finishLookup;
2335
- vi.mocked(agentSessionAccessor.findById).mockImplementation(() => new Promise((resolve) => {
2590
+ vi.mocked(mockSessionBridge.findSessionById).mockImplementation(() => new Promise((resolve) => {
2336
2591
  finishLookup = resolve;
2337
2592
  }));
2338
2593
  const check = unsafeCoerce(ratchetService).checkActiveFixerSession({
2339
2594
  id: 'ws-cancelled-active-check',
2340
2595
  ratchetActiveSessionId: 'session-1',
2341
2596
  }, controller.signal);
2342
- await vi.waitFor(() => expect(agentSessionAccessor.findById).toHaveBeenCalled());
2597
+ await vi.waitFor(() => expect(mockSessionBridge.findSessionById).toHaveBeenCalled());
2343
2598
  controller.abort(timeoutError);
2344
2599
  finishLookup(null);
2345
2600
  await expect(check).rejects.toBe(timeoutError);
2346
- expect(workspaceAccessor.recordRatchetSessionEnd).not.toHaveBeenCalled();
2601
+ expect(mockWorkspaceBridge.recordSessionEnd).not.toHaveBeenCalled();
2347
2602
  expect(mockSessionBridge.stopSession).not.toHaveBeenCalled();
2348
2603
  });
2604
+ it('publishes a successful fallback settlement before observing post-write cancellation', async () => {
2605
+ const controller = new AbortController();
2606
+ const timeoutError = new Error('Workspace check timed out');
2607
+ vi.mocked(mockSessionBridge.findSessionById).mockResolvedValue(null);
2608
+ vi.mocked(mockWorkspaceBridge.recordSessionEnd).mockImplementation(() => {
2609
+ controller.abort(timeoutError);
2610
+ return Promise.resolve(true);
2611
+ });
2612
+ const events = [];
2613
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
2614
+ events.push(event);
2615
+ });
2616
+ const check = unsafeCoerce(ratchetService).checkActiveFixerSession({
2617
+ id: 'ws-cancelled-after-settlement',
2618
+ ratchetActiveSessionId: 'session-1',
2619
+ ratchetDispatchRetryCount: 2,
2620
+ }, controller.signal);
2621
+ await expect(check).rejects.toBe(timeoutError);
2622
+ expect(events).toEqual([{ workspaceId: 'ws-cancelled-after-settlement' }]);
2623
+ });
2349
2624
  });
2350
2625
  describe('triggerFixer error handling', () => {
2626
+ it('commits the workspace check as soon as the fixer session starts', async () => {
2627
+ const commitSideEffects = vi.fn();
2628
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockImplementation(async (input) => {
2629
+ await input.afterStart?.({ sessionId: 'started-session', prompt: 'prompt' });
2630
+ return {
2631
+ status: 'started',
2632
+ sessionId: 'started-session',
2633
+ promptSent: true,
2634
+ };
2635
+ });
2636
+ const result = await unsafeCoerce(ratchetService).triggerFixer({
2637
+ id: 'ws-started-dispatch',
2638
+ prUrl: 'https://github.com/example/repo/pull/20',
2639
+ }, {
2640
+ ciStatus: CIStatus.FAILURE,
2641
+ snapshotKey: 'failed:20',
2642
+ prNumber: 20,
2643
+ reviewComments: [],
2644
+ hasMergeConflict: false,
2645
+ }, 0, new AbortController().signal, commitSideEffects);
2646
+ expect(result).toMatchObject({
2647
+ type: 'TRIGGERED_FIXER',
2648
+ sessionId: 'started-session',
2649
+ });
2650
+ expect(commitSideEffects).toHaveBeenCalledTimes(2);
2651
+ });
2652
+ it('cleans up a fixer when cancellation races with session startup', async () => {
2653
+ const controller = new AbortController();
2654
+ const timeoutError = new Error('Workspace check timed out');
2655
+ const commitSideEffects = vi.fn();
2656
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockImplementation(async (input) => {
2657
+ controller.abort(timeoutError);
2658
+ try {
2659
+ await input.afterStart?.({ sessionId: 'cancelled-startup-session', prompt: 'prompt' });
2660
+ return {
2661
+ status: 'started',
2662
+ sessionId: 'cancelled-startup-session',
2663
+ promptSent: true,
2664
+ };
2665
+ }
2666
+ catch (error) {
2667
+ return {
2668
+ status: 'error',
2669
+ error: error instanceof Error ? error.message : String(error),
2670
+ };
2671
+ }
2672
+ });
2673
+ vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2674
+ const trigger = unsafeCoerce(ratchetService).triggerFixer({
2675
+ id: 'ws-cancelled-startup',
2676
+ prUrl: 'https://github.com/example/repo/pull/20',
2677
+ }, {
2678
+ ciStatus: CIStatus.FAILURE,
2679
+ snapshotKey: 'failed:20',
2680
+ prNumber: 20,
2681
+ reviewComments: [],
2682
+ hasMergeConflict: false,
2683
+ }, 0, controller.signal, commitSideEffects);
2684
+ await expect(trigger).rejects.toBe(timeoutError);
2685
+ expect(commitSideEffects).toHaveBeenCalledTimes(1);
2686
+ expect(workspaceRatchetService.recordSessionEnd).toHaveBeenCalledWith('ws-cancelled-startup', 'cancelled-startup-session', 'COMPLETED');
2687
+ expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('cancelled-startup-session');
2688
+ });
2351
2689
  it('does not clean up a fixer after its dispatch record is persisted', async () => {
2352
2690
  const controller = new AbortController();
2353
2691
  const timeoutError = new Error('Workspace check timed out');
@@ -2357,11 +2695,15 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2357
2695
  sessionId: 'cancelled-session',
2358
2696
  promptSent: true,
2359
2697
  });
2360
- vi.mocked(workspaceAccessor.recordRatchetDispatchIfEnabled).mockImplementation(() => new Promise((resolve) => {
2698
+ vi.mocked(workspaceRatchetService.recordDispatchIfEnabled).mockImplementation(() => new Promise((resolve) => {
2361
2699
  finishRecord = resolve;
2362
2700
  }));
2363
2701
  vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2364
2702
  const commitSideEffects = vi.fn();
2703
+ const events = [];
2704
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
2705
+ events.push(event);
2706
+ });
2365
2707
  const trigger = unsafeCoerce(ratchetService).triggerFixer({
2366
2708
  id: 'ws-cancelled-dispatch',
2367
2709
  prUrl: 'https://github.com/example/repo/pull/20',
@@ -2372,13 +2714,138 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2372
2714
  reviewComments: [],
2373
2715
  hasMergeConflict: false,
2374
2716
  }, 0, controller.signal, commitSideEffects);
2375
- await vi.waitFor(() => expect(workspaceAccessor.recordRatchetDispatchIfEnabled).toHaveBeenCalled());
2717
+ await vi.waitFor(() => expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalled());
2376
2718
  controller.abort(timeoutError);
2377
2719
  finishRecord(true);
2378
2720
  await expect(trigger).rejects.toBe(timeoutError);
2379
2721
  expect(commitSideEffects).toHaveBeenCalledTimes(1);
2380
- expect(workspaceAccessor.recordRatchetSessionEnd).not.toHaveBeenCalled();
2722
+ expect(workspaceRatchetService.recordSessionEnd).not.toHaveBeenCalled();
2723
+ expect(mockSessionBridge.stopSession).not.toHaveBeenCalled();
2724
+ expect(events).toEqual([{ workspaceId: 'ws-cancelled-dispatch' }]);
2725
+ });
2726
+ it('persists and commits a dispatch while prompt completion is pending', async () => {
2727
+ let finishPrompt;
2728
+ const promptCompletion = new Promise((resolve) => {
2729
+ finishPrompt = resolve;
2730
+ });
2731
+ let finishRecord;
2732
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2733
+ status: 'started',
2734
+ sessionId: 'pending-prompt-session',
2735
+ promptSent: true,
2736
+ promptCompletion,
2737
+ });
2738
+ vi.mocked(workspaceRatchetService.recordDispatchIfEnabled).mockImplementation(() => new Promise((resolve) => {
2739
+ finishRecord = resolve;
2740
+ }));
2741
+ const commitSideEffects = vi.fn();
2742
+ const trigger = unsafeCoerce(ratchetService).triggerFixer({
2743
+ id: 'ws-pending-prompt',
2744
+ prUrl: 'https://github.com/example/repo/pull/20',
2745
+ }, {
2746
+ ciStatus: CIStatus.FAILURE,
2747
+ snapshotKey: 'failed:20',
2748
+ prNumber: 20,
2749
+ reviewComments: [],
2750
+ hasMergeConflict: false,
2751
+ }, 0, new AbortController().signal, commitSideEffects);
2752
+ await vi.waitFor(() => expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalled());
2753
+ expect(commitSideEffects).toHaveBeenCalledTimes(1);
2754
+ finishRecord(true);
2755
+ await expect(trigger).resolves.toMatchObject({ type: 'TRIGGERED_FIXER' });
2756
+ expect(commitSideEffects).toHaveBeenCalledTimes(1);
2757
+ finishPrompt(true);
2758
+ await promptCompletion;
2759
+ });
2760
+ it('settles a persisted dispatch as died when prompt completion fails', async () => {
2761
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2762
+ status: 'started',
2763
+ sessionId: 'failed-prompt-session',
2764
+ promptSent: true,
2765
+ promptCompletion: Promise.resolve(false),
2766
+ });
2767
+ vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2768
+ const events = [];
2769
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
2770
+ events.push(event);
2771
+ });
2772
+ const result = await unsafeCoerce(ratchetService).triggerFixer({
2773
+ id: 'ws-failed-prompt',
2774
+ prUrl: 'https://github.com/example/repo/pull/20',
2775
+ }, {
2776
+ ciStatus: CIStatus.FAILURE,
2777
+ snapshotKey: 'failed:20',
2778
+ prNumber: 20,
2779
+ reviewComments: [],
2780
+ hasMergeConflict: false,
2781
+ }, 0);
2782
+ expect(result).toMatchObject({ type: 'TRIGGERED_FIXER' });
2783
+ await vi.waitFor(() => expect(workspaceRatchetService.recordSessionEnd).toHaveBeenCalledWith('ws-failed-prompt', 'failed-prompt-session', 'DIED'));
2784
+ expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('failed-prompt-session');
2785
+ await vi.waitFor(() => expect(events).toEqual([
2786
+ { workspaceId: 'ws-failed-prompt' },
2787
+ { workspaceId: 'ws-failed-prompt' },
2788
+ ]));
2789
+ });
2790
+ it('does not stop a newer active session when an old prompt completion fails', async () => {
2791
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2792
+ status: 'started',
2793
+ sessionId: 'stale-prompt-session',
2794
+ promptSent: true,
2795
+ promptCompletion: Promise.resolve(false),
2796
+ });
2797
+ vi.mocked(workspaceRatchetService.recordSessionEnd).mockResolvedValue(false);
2798
+ vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2799
+ const events = [];
2800
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
2801
+ events.push(event);
2802
+ });
2803
+ await unsafeCoerce(ratchetService).triggerFixer({
2804
+ id: 'ws-replaced-prompt',
2805
+ prUrl: 'https://github.com/example/repo/pull/20',
2806
+ }, {
2807
+ ciStatus: CIStatus.FAILURE,
2808
+ snapshotKey: 'failed:20',
2809
+ prNumber: 20,
2810
+ reviewComments: [],
2811
+ hasMergeConflict: false,
2812
+ }, 0);
2813
+ await vi.waitFor(() => expect(workspaceRatchetService.recordSessionEnd).toHaveBeenCalled());
2381
2814
  expect(mockSessionBridge.stopSession).not.toHaveBeenCalled();
2815
+ expect(events).toEqual([{ workspaceId: 'ws-replaced-prompt' }]);
2816
+ });
2817
+ it('cleans up a fixer through prompt failure after its dispatch record is persisted', async () => {
2818
+ const controller = new AbortController();
2819
+ const timeoutError = new Error('Workspace check timed out');
2820
+ let finishRecord;
2821
+ vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2822
+ status: 'started',
2823
+ sessionId: 'cancelled-session',
2824
+ promptSent: true,
2825
+ promptCompletion: Promise.resolve(false),
2826
+ });
2827
+ vi.mocked(workspaceRatchetService.recordDispatchIfEnabled).mockImplementation(() => new Promise((resolve) => {
2828
+ finishRecord = resolve;
2829
+ }));
2830
+ vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2831
+ const commitSideEffects = vi.fn();
2832
+ const trigger = unsafeCoerce(ratchetService).triggerFixer({
2833
+ id: 'ws-cancelled-dispatch',
2834
+ prUrl: 'https://github.com/example/repo/pull/20',
2835
+ }, {
2836
+ ciStatus: CIStatus.FAILURE,
2837
+ snapshotKey: 'failed:20',
2838
+ prNumber: 20,
2839
+ reviewComments: [],
2840
+ hasMergeConflict: false,
2841
+ }, 0, controller.signal, commitSideEffects);
2842
+ await vi.waitFor(() => expect(workspaceRatchetService.recordDispatchIfEnabled).toHaveBeenCalled());
2843
+ controller.abort(timeoutError);
2844
+ finishRecord(true);
2845
+ await expect(trigger).rejects.toBe(timeoutError);
2846
+ expect(commitSideEffects).toHaveBeenCalledTimes(1);
2847
+ await vi.waitFor(() => expect(workspaceRatchetService.recordSessionEnd).toHaveBeenCalledWith('ws-cancelled-dispatch', 'cancelled-session', 'DIED'));
2848
+ expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('cancelled-session');
2382
2849
  });
2383
2850
  it('cleans up a started fixer when dispatch persistence fails', async () => {
2384
2851
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
@@ -2386,8 +2853,8 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2386
2853
  sessionId: 'unrecorded-session',
2387
2854
  promptSent: true,
2388
2855
  });
2389
- vi.mocked(workspaceAccessor.recordRatchetDispatchIfEnabled).mockRejectedValue(new Error('dispatch record write failed'));
2390
- vi.mocked(workspaceAccessor.recordRatchetSessionEnd).mockRejectedValue(new Error('dispatch cleanup write failed'));
2856
+ vi.mocked(workspaceRatchetService.recordDispatchIfEnabled).mockRejectedValue(new Error('dispatch record write failed'));
2857
+ vi.mocked(workspaceRatchetService.recordSessionEnd).mockRejectedValue(new Error('dispatch cleanup write failed'));
2391
2858
  vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2392
2859
  const commitSideEffects = vi.fn();
2393
2860
  const result = await unsafeCoerce(ratchetService).triggerFixer({
@@ -2402,7 +2869,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2402
2869
  }, 0, new AbortController().signal, commitSideEffects);
2403
2870
  expect(result).toMatchObject({ type: 'ERROR', error: 'dispatch record write failed' });
2404
2871
  expect(commitSideEffects).toHaveBeenCalledTimes(1);
2405
- expect(workspaceAccessor.recordRatchetSessionEnd).toHaveBeenCalledWith('ws-unrecorded-dispatch', 'unrecorded-session', 'COMPLETED');
2872
+ expect(workspaceRatchetService.recordSessionEnd).toHaveBeenCalledWith('ws-unrecorded-dispatch', 'unrecorded-session', 'COMPLETED');
2406
2873
  expect(mockSessionBridge.stopSession).toHaveBeenCalledWith('unrecorded-session');
2407
2874
  });
2408
2875
  it('handles acquireAndDispatch throwing an error', async () => {
@@ -2413,7 +2880,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2413
2880
  ratchetEnabled: true,
2414
2881
  ratchetState: RatchetState.CI_FAILED,
2415
2882
  ratchetActiveSessionId: null,
2416
- ratchetLastCiRunId: null,
2883
+ ratchetDispatchSnapshotKey: null,
2417
2884
  prReviewLastCheckedAt: null,
2418
2885
  ratchetDispatchOutcome: null,
2419
2886
  ratchetDispatchRetryCount: 0,
@@ -2436,16 +2903,19 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2436
2903
  ratchetEnabled: true,
2437
2904
  ratchetState: RatchetState.CI_FAILED,
2438
2905
  ratchetActiveSessionId: null,
2439
- ratchetLastCiRunId: null,
2906
+ ratchetDispatchSnapshotKey: null,
2440
2907
  prReviewLastCheckedAt: null,
2441
2908
  ratchetDispatchOutcome: null,
2442
- ratchetDispatchRetryCount: 0,
2909
+ ratchetDispatchRetryCount: 2,
2443
2910
  };
2444
2911
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2445
2912
  status: 'already_active',
2446
2913
  sessionId: 'existing-session',
2447
2914
  });
2448
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2915
+ const events = [];
2916
+ ratchetService.on(RATCHET_DISPATCH_CHANGED, (event) => {
2917
+ events.push(event);
2918
+ });
2449
2919
  const result = await unsafeCoerce(ratchetService).triggerFixer(workspace, {
2450
2920
  ciStatus: CIStatus.FAILURE,
2451
2921
  prNumber: 21,
@@ -2454,8 +2924,9 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2454
2924
  type: 'FIXER_ACTIVE',
2455
2925
  sessionId: 'existing-session',
2456
2926
  });
2457
- expect(workspaceAccessor.adoptRatchetActiveSessionIfEnabled).toHaveBeenCalledWith('ws-already-active', 'existing-session');
2458
- expect(workspaceAccessor.recordRatchetDispatchIfEnabled).not.toHaveBeenCalled();
2927
+ expect(workspaceRatchetService.adoptActiveSessionIfEnabled).toHaveBeenCalledWith('ws-already-active', 'existing-session');
2928
+ expect(workspaceRatchetService.recordDispatchIfEnabled).not.toHaveBeenCalled();
2929
+ expect(events).toEqual([{ workspaceId: 'ws-already-active' }]);
2459
2930
  });
2460
2931
  it('stops already-active fixer when active-session recording loses disable race', async () => {
2461
2932
  const workspace = {
@@ -2465,7 +2936,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2465
2936
  ratchetEnabled: true,
2466
2937
  ratchetState: RatchetState.CI_FAILED,
2467
2938
  ratchetActiveSessionId: null,
2468
- ratchetLastCiRunId: null,
2939
+ ratchetDispatchSnapshotKey: null,
2469
2940
  prReviewLastCheckedAt: null,
2470
2941
  ratchetDispatchOutcome: null,
2471
2942
  ratchetDispatchRetryCount: 0,
@@ -2474,7 +2945,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2474
2945
  status: 'already_active',
2475
2946
  sessionId: 'existing-session',
2476
2947
  });
2477
- vi.mocked(workspaceAccessor.adoptRatchetActiveSessionIfEnabled).mockResolvedValue(false);
2948
+ vi.mocked(workspaceRatchetService.adoptActiveSessionIfEnabled).mockResolvedValue(false);
2478
2949
  vi.mocked(mockSessionBridge.isSessionRunning).mockReturnValue(true);
2479
2950
  const result = await unsafeCoerce(ratchetService).triggerFixer(workspace, {
2480
2951
  ciStatus: CIStatus.FAILURE,
@@ -2494,7 +2965,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2494
2965
  ratchetEnabled: true,
2495
2966
  ratchetState: RatchetState.CI_FAILED,
2496
2967
  ratchetActiveSessionId: null,
2497
- ratchetLastCiRunId: null,
2968
+ ratchetDispatchSnapshotKey: null,
2498
2969
  prReviewLastCheckedAt: null,
2499
2970
  ratchetDispatchOutcome: null,
2500
2971
  ratchetDispatchRetryCount: 0,
@@ -2518,12 +2989,13 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2518
2989
  ratchetService.removeAllListeners();
2519
2990
  });
2520
2991
  it('emits ratchet_toggled when enabling workspace ratcheting', async () => {
2521
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
2992
+ vi.mocked(workspaceDataService.findById).mockResolvedValue({
2522
2993
  id: 'ws-enable',
2523
2994
  ratchetState: RatchetState.CI_FAILED,
2524
2995
  ratchetActiveSessionId: null,
2996
+ ratchetDispatchOutcome: 'DIED',
2997
+ ratchetDispatchRetryCount: 2,
2525
2998
  });
2526
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2527
2999
  const toggledEvents = [];
2528
3000
  const stateEvents = [];
2529
3001
  ratchetService.on(RATCHET_TOGGLED, (event) => {
@@ -2533,7 +3005,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2533
3005
  stateEvents.push(event);
2534
3006
  });
2535
3007
  await ratchetService.setWorkspaceRatcheting('ws-enable', true);
2536
- expect(workspaceAccessor.update).toHaveBeenCalledWith('ws-enable', { ratchetEnabled: true });
3008
+ expect(workspaceRatchetService.enable).toHaveBeenCalledWith('ws-enable');
2537
3009
  expect(toggledEvents).toEqual([
2538
3010
  {
2539
3011
  workspaceId: 'ws-enable',
@@ -2544,12 +3016,11 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2544
3016
  expect(stateEvents).toHaveLength(0);
2545
3017
  });
2546
3018
  it('emits ratchet_toggled and ratchet_state_changed when disabling non-idle workspace', async () => {
2547
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
3019
+ vi.mocked(workspaceDataService.findById).mockResolvedValue({
2548
3020
  id: 'ws-disable',
2549
3021
  ratchetState: RatchetState.CI_RUNNING,
2550
3022
  ratchetActiveSessionId: null,
2551
3023
  });
2552
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2553
3024
  const toggledEvents = [];
2554
3025
  const stateEvents = [];
2555
3026
  ratchetService.on(RATCHET_TOGGLED, (event) => {
@@ -2559,14 +3030,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2559
3030
  stateEvents.push(event);
2560
3031
  });
2561
3032
  await ratchetService.setWorkspaceRatcheting('ws-disable', false);
2562
- expect(workspaceAccessor.update).toHaveBeenCalledWith('ws-disable', {
2563
- ratchetEnabled: false,
2564
- ratchetState: RatchetState.IDLE,
2565
- ratchetActiveSessionId: null,
2566
- ratchetLastCiRunId: null,
2567
- ratchetDispatchOutcome: null,
2568
- ratchetDispatchRetryCount: 0,
2569
- });
3033
+ expect(workspaceRatchetService.disable).toHaveBeenCalledWith('ws-disable');
2570
3034
  expect(stateEvents).toEqual([
2571
3035
  {
2572
3036
  workspaceId: 'ws-disable',
@@ -2583,12 +3047,11 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2583
3047
  ]);
2584
3048
  });
2585
3049
  it('stops ratchet workflow sessions found after disabling workspace', async () => {
2586
- vi.mocked(workspaceAccessor.findById).mockResolvedValue({
3050
+ vi.mocked(workspaceDataService.findById).mockResolvedValue({
2587
3051
  id: 'ws-disable-raced-session',
2588
3052
  ratchetState: RatchetState.CI_RUNNING,
2589
3053
  ratchetActiveSessionId: null,
2590
3054
  });
2591
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2592
3055
  vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([
2593
3056
  {
2594
3057
  id: 'raced-ratchet-session',
@@ -2612,33 +3075,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2612
3075
  afterEach(() => {
2613
3076
  ratchetService.removeAllListeners();
2614
3077
  });
2615
- it('emits ratchet_state_changed when disabled workspace state changes', async () => {
2616
- const workspace = {
2617
- id: 'ws-disabled-change',
2618
- prUrl: 'https://github.com/example/repo/pull/30',
2619
- prNumber: 30,
2620
- ratchetEnabled: false,
2621
- ratchetState: RatchetState.CI_FAILED,
2622
- ratchetActiveSessionId: null,
2623
- ratchetLastCiRunId: null,
2624
- prReviewLastCheckedAt: null,
2625
- ratchetDispatchOutcome: null,
2626
- ratchetDispatchRetryCount: 0,
2627
- };
2628
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2629
- const events = [];
2630
- ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
2631
- events.push(event);
2632
- });
2633
- await unsafeCoerce(ratchetService).processWorkspace(workspace);
2634
- expect(events).toHaveLength(1);
2635
- expect(events[0]).toEqual({
2636
- workspaceId: 'ws-disabled-change',
2637
- fromState: RatchetState.CI_FAILED,
2638
- toState: RatchetState.IDLE,
2639
- });
2640
- });
2641
- it('does NOT emit when disabled workspace state is already IDLE', async () => {
3078
+ it('never emits from a check on a disabled workspace', async () => {
2642
3079
  const workspace = {
2643
3080
  id: 'ws-disabled-idle',
2644
3081
  prUrl: 'https://github.com/example/repo/pull/31',
@@ -2646,12 +3083,11 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2646
3083
  ratchetEnabled: false,
2647
3084
  ratchetState: RatchetState.IDLE,
2648
3085
  ratchetActiveSessionId: null,
2649
- ratchetLastCiRunId: null,
3086
+ ratchetDispatchSnapshotKey: null,
2650
3087
  prReviewLastCheckedAt: null,
2651
3088
  ratchetDispatchOutcome: null,
2652
3089
  ratchetDispatchRetryCount: 0,
2653
3090
  };
2654
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2655
3091
  const events = [];
2656
3092
  ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
2657
3093
  events.push(event);
@@ -2667,7 +3103,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2667
3103
  ratchetEnabled: true,
2668
3104
  ratchetState: RatchetState.IDLE,
2669
3105
  ratchetActiveSessionId: null,
2670
- ratchetLastCiRunId: null,
3106
+ ratchetDispatchSnapshotKey: null,
2671
3107
  prReviewLastCheckedAt: null,
2672
3108
  ratchetDispatchOutcome: null,
2673
3109
  ratchetDispatchRetryCount: 0,
@@ -2681,8 +3117,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2681
3117
  prState: 'OPEN',
2682
3118
  prNumber: 32,
2683
3119
  });
2684
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2685
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
3120
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
2686
3121
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2687
3122
  status: 'started',
2688
3123
  sessionId: 'ratchet-session-32',
@@ -2709,7 +3144,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2709
3144
  ratchetEnabled: true,
2710
3145
  ratchetState: RatchetState.CI_FAILED,
2711
3146
  ratchetActiveSessionId: null,
2712
- ratchetLastCiRunId: null,
3147
+ ratchetDispatchSnapshotKey: null,
2713
3148
  prReviewLastCheckedAt: null,
2714
3149
  ratchetDispatchOutcome: null,
2715
3150
  ratchetDispatchRetryCount: 0,
@@ -2723,8 +3158,7 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2723
3158
  prState: 'OPEN',
2724
3159
  prNumber: 33,
2725
3160
  });
2726
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2727
- vi.mocked(agentSessionAccessor.findByWorkspaceId).mockResolvedValue([]);
3161
+ vi.mocked(mockSessionBridge.findSessionsByWorkspaceId).mockResolvedValue([]);
2728
3162
  vi.mocked(fixerSessionService.acquireAndDispatch).mockResolvedValue({
2729
3163
  status: 'started',
2730
3164
  sessionId: 'ratchet-session-33',
@@ -2745,13 +3179,12 @@ describe('ratchet service (state-change + idle dispatch)', () => {
2745
3179
  ratchetEnabled: true,
2746
3180
  ratchetState: RatchetState.CI_FAILED,
2747
3181
  ratchetActiveSessionId: null,
2748
- ratchetLastCiRunId: null,
3182
+ ratchetDispatchSnapshotKey: null,
2749
3183
  prReviewLastCheckedAt: null,
2750
3184
  ratchetDispatchOutcome: null,
2751
3185
  ratchetDispatchRetryCount: 0,
2752
3186
  };
2753
3187
  vi.spyOn(unsafeCoerce(ratchetService), 'fetchPRState').mockResolvedValue(null);
2754
- vi.mocked(workspaceAccessor.update).mockResolvedValue({});
2755
3188
  const events = [];
2756
3189
  ratchetService.on(RATCHET_STATE_CHANGED, (event) => {
2757
3190
  events.push(event);