specrails-desktop 2.36.0 → 2.42.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 (510) hide show
  1. package/README.md +94 -247
  2. package/client/dist/assets/ActivityFeedPage-DoEMumQS.js +1 -0
  3. package/client/dist/assets/AgentBrowserCapture-DIh4K0gh.js +1 -0
  4. package/client/dist/assets/AgentModeAnalyticsPane-mbq4WEoO.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-CKh7zuA4.js +2 -0
  6. package/client/dist/assets/AgentModeJobsPane-BDokPiLJ.js +1 -0
  7. package/client/dist/assets/AgentsPage-BFVAZbBj.js +87 -0
  8. package/client/dist/assets/AnalyticsPage-3Y9LuWwz.js +1 -0
  9. package/client/dist/assets/CodePage-C9KyuV_C.js +3 -0
  10. package/client/dist/assets/DesktopAnalyticsPage-Bhi54bXQ.js +1 -0
  11. package/client/dist/assets/{DocsDialog-C76VdBJC.js → DocsDialog-Dj6mt4ga.js} +2 -2
  12. package/client/dist/assets/{DocsPage-ziMlj6B6.js → DocsPage-24yWglNI.js} +1 -1
  13. package/client/dist/assets/ExportDropdown-ophapHrT.js +1 -0
  14. package/client/dist/assets/InteractiveJobComposer-C1ucmxQh.js +19 -0
  15. package/client/dist/assets/JobDetailModal-DNWmgVHD.js +1 -0
  16. package/client/dist/assets/JobDetailPage-BcajZyuu.js +1 -0
  17. package/client/dist/assets/JobsPage-B9nTZw6e.js +1 -0
  18. package/client/dist/assets/LoopBuilderPage-Oc48iOFW.js +7 -0
  19. package/client/dist/assets/LoopPreviewModal-ae0Fr6SU.js +1 -0
  20. package/client/dist/assets/LoopsPage-CUEqzUBI.js +1 -0
  21. package/client/dist/assets/MinimizedChatsContext-Deg39peL.js +1 -0
  22. package/client/dist/assets/PluginsPage-C1xtARIx.js +2 -0
  23. package/client/dist/assets/ProjectSettingsDialog-JZzrXFBN.js +1 -0
  24. package/client/dist/assets/RepositoryDeliveries-CSDsiVUz.js +5 -0
  25. package/client/dist/assets/RepositoryScopeSelector-D-RCaknl.js +1 -0
  26. package/client/dist/assets/ReviewPacketPage-x5EfGTiL.js +1 -0
  27. package/client/dist/assets/TemplatePreviewModal-BSyuZxGq.js +1 -0
  28. package/client/dist/assets/TicketDetailModalContext-DqyF0so-.js +14 -0
  29. package/client/dist/assets/Trans-SnMX4MhW.js +1 -0
  30. package/client/dist/assets/agent-BdT0SB1D.js +1 -0
  31. package/client/dist/assets/agent-Bh9K7iEt.js +1 -0
  32. package/client/dist/assets/agent-C4ibdFk9.js +1 -0
  33. package/client/dist/assets/agent-CSFY22Zo.js +1 -0
  34. package/client/dist/assets/agent-DAnqxUn8.js +1 -0
  35. package/client/dist/assets/agent-DTQiu0Mi.js +1 -0
  36. package/client/dist/assets/agent-T54WuR_Y.js +1 -0
  37. package/client/dist/assets/agent-i75WDZOA.js +1 -0
  38. package/client/dist/assets/browser-05vRDg_y.js +1 -0
  39. package/client/dist/assets/browser-Bulb-cB1.js +1 -0
  40. package/client/dist/assets/browser-C7f-Jsg1.js +1 -0
  41. package/client/dist/assets/browser-CG0PE1j_.js +1 -0
  42. package/client/dist/assets/browser-CMzaqAjo.js +1 -0
  43. package/client/dist/assets/browser-DMajYdNX.js +1 -0
  44. package/client/dist/assets/browser-DxcOHrdP.js +1 -0
  45. package/client/dist/assets/browser-ObE7xVo3.js +1 -0
  46. package/client/dist/assets/builder-765NeYTp.js +1 -0
  47. package/client/dist/assets/builder-8MG3V6ea.js +1 -0
  48. package/client/dist/assets/builder-B-1UBs5K.js +1 -0
  49. package/client/dist/assets/builder-BTiGnPT0.js +1 -0
  50. package/client/dist/assets/builder-Cx9Rz-7u.js +1 -0
  51. package/client/dist/assets/builder-DKFMELBl.js +1 -0
  52. package/client/dist/assets/builder-OcmKrC0h.js +1 -0
  53. package/client/dist/assets/builder-P9qVBXFZ.js +1 -0
  54. package/client/dist/assets/{button-CJqFnUOL.js → button-DCBqEaZl.js} +1 -1
  55. package/client/dist/assets/{code-CkUuW4m9.js → code-B37GRg9C.js} +1 -1
  56. package/client/dist/assets/{code-B0O8-p8k.js → code-BBOEZbf9.js} +1 -1
  57. package/client/dist/assets/{code-B1VLZDhS.js → code-BRm-jcyx.js} +1 -1
  58. package/client/dist/assets/{code-Bpj4Ddml.js → code-CF7w62CR.js} +1 -1
  59. package/client/dist/assets/{code-BykBZ5-e.js → code-CLuvJt2r.js} +1 -1
  60. package/client/dist/assets/{code-B86IMAfz.js → code-D0RCm59f.js} +1 -1
  61. package/client/dist/assets/code-DaZ-ZwCT.js +1 -0
  62. package/client/dist/assets/{code-cAqaqMTx.js → code-iIHkGaCL.js} +1 -1
  63. package/client/dist/assets/commands-0XjAftla.js +1 -0
  64. package/client/dist/assets/commands-BAWSYHjQ.js +1 -0
  65. package/client/dist/assets/commands-BBwowgiE.js +1 -0
  66. package/client/dist/assets/commands-BacjIZLN.js +1 -0
  67. package/client/dist/assets/commands-Bs62IAZ3.js +1 -0
  68. package/client/dist/assets/commands-CLPdh-Hh.js +1 -0
  69. package/client/dist/assets/commands-DlbFzgrp.js +1 -0
  70. package/client/dist/assets/commands-LjPSsi5F.js +1 -0
  71. package/client/dist/assets/common-B5AZNYMH.js +1 -0
  72. package/client/dist/assets/common-BFZ0c_vN.js +1 -0
  73. package/client/dist/assets/common-BrlwEnL7.js +1 -0
  74. package/client/dist/assets/common-BttQx3z4.js +1 -0
  75. package/client/dist/assets/common-CM65k_-e.js +1 -0
  76. package/client/dist/assets/common-D4mvFFlu.js +1 -0
  77. package/client/dist/assets/common-TZ9Ny96S.js +1 -0
  78. package/client/dist/assets/common-nFpSF4GC.js +1 -0
  79. package/client/dist/assets/dashboard-B14uYXkG.js +1 -0
  80. package/client/dist/assets/dashboard-BK_mez-z.js +1 -0
  81. package/client/dist/assets/dashboard-BfBir_6o.js +1 -0
  82. package/client/dist/assets/dashboard-Bt0cCzeG.js +1 -0
  83. package/client/dist/assets/dashboard-BwkjxI6H.js +1 -0
  84. package/client/dist/assets/{dashboard-C8WOEiPu.js → dashboard-C3bDKf7s.js} +1 -1
  85. package/client/dist/assets/dashboard-CdvjGfog.js +1 -0
  86. package/client/dist/assets/dashboard-fRrP0Kv0.js +1 -0
  87. package/client/dist/assets/dialog-SRw01_QU.js +45 -0
  88. package/client/dist/assets/dist-js-CJ_2XX1Y.js +1 -0
  89. package/client/dist/assets/formatDistanceToNow-DSj5DprF.js +1 -0
  90. package/client/dist/assets/getTimezoneOffsetInMilliseconds-YhD8rIEs.js +1 -0
  91. package/client/dist/assets/index-DwAmL7m5.css +2 -0
  92. package/client/dist/assets/index-EzrkUb8B.js +76 -0
  93. package/client/dist/assets/{jira-api-B1-MUehg.js → jira-api-CSCqGr-Q.js} +1 -1
  94. package/client/dist/assets/{jobs-CN_zv4Gr.js → jobs-B8rrzfyZ.js} +1 -1
  95. package/client/dist/assets/{jobs-DUlWSYq2.js → jobs-BS6RTurf.js} +1 -1
  96. package/client/dist/assets/{jobs-Bpks6doU.js → jobs-BTT0Bsks.js} +1 -1
  97. package/client/dist/assets/{jobs-Bgq-PZkC.js → jobs-CezU69AE.js} +1 -1
  98. package/client/dist/assets/{jobs-SwYS2M-B.js → jobs-ClFJae0s.js} +1 -1
  99. package/client/dist/assets/{jobs-BAls3bWq.js → jobs-DJSFIFVy.js} +1 -1
  100. package/client/dist/assets/{jobs-DhxKgWFq.js → jobs-DhgGr8vm.js} +1 -1
  101. package/client/dist/assets/{jobs-B_TeSQxb.js → jobs-DzMjUcaN.js} +1 -1
  102. package/client/dist/assets/mcp-6rMoZfEO.js +1 -0
  103. package/client/dist/assets/mcp-BD8sL5mP.js +1 -0
  104. package/client/dist/assets/mcp-BkcA3I5s.js +1 -0
  105. package/client/dist/assets/mcp-CCpSelOh.js +1 -0
  106. package/client/dist/assets/mcp-CUAN6ORK.js +1 -0
  107. package/client/dist/assets/mcp-CqbRxRTE.js +1 -0
  108. package/client/dist/assets/mcp-DP2YfVhO.js +1 -0
  109. package/client/dist/assets/mcp-DUVQamDz.js +1 -0
  110. package/client/dist/assets/narration-B-ynWTpn.js +1 -0
  111. package/client/dist/assets/narration-Bj_laZwg.js +1 -0
  112. package/client/dist/assets/narration-BxHpmMv_.js +1 -0
  113. package/client/dist/assets/narration-C1Px0-0j.js +1 -0
  114. package/client/dist/assets/narration-CAXhGbzM.js +1 -0
  115. package/client/dist/assets/narration-DQ1l_Kq6.js +1 -0
  116. package/client/dist/assets/narration-XISXwqZd.js +1 -0
  117. package/client/dist/assets/narration-kwzfglQo.js +1 -0
  118. package/client/dist/assets/nav-1-Hh2kOz.js +1 -0
  119. package/client/dist/assets/nav-B34QkESm.js +1 -0
  120. package/client/dist/assets/nav-Bap_UXAq.js +1 -0
  121. package/client/dist/assets/{nav-pYuXq7sf.js → nav-BwxXIB24.js} +1 -1
  122. package/client/dist/assets/nav-Ch-zHBG5.js +1 -0
  123. package/client/dist/assets/nav-CygC_WN-.js +1 -0
  124. package/client/dist/assets/{nav-iwjCg5FR.js → nav-KnnLQtOQ.js} +1 -1
  125. package/client/dist/assets/nav-m00uxYUG.js +1 -0
  126. package/client/dist/assets/packet-BQ2tXfJI.js +1 -0
  127. package/client/dist/assets/packet-BdpOJ1Cy.js +1 -0
  128. package/client/dist/assets/packet-C3Ievb9R.js +1 -0
  129. package/client/dist/assets/packet-C4zJIG0Y.js +1 -0
  130. package/client/dist/assets/packet-Cx-zRuWn.js +1 -0
  131. package/client/dist/assets/packet-DUTGX52M.js +1 -0
  132. package/client/dist/assets/packet-DhsKtUPB.js +1 -0
  133. package/client/dist/assets/packet-z3LJ8Zyy.js +1 -0
  134. package/client/dist/assets/project-repositories-DYhTvwQ1.js +1 -0
  135. package/client/dist/assets/provider-capabilities-B-wOHfLP.js +1 -0
  136. package/client/dist/assets/settings-BBt2xtDQ.js +1 -0
  137. package/client/dist/assets/settings-BJB9RKJj.js +1 -0
  138. package/client/dist/assets/settings-CHwQR2rB.js +1 -0
  139. package/client/dist/assets/settings-CrMi3kSi.js +1 -0
  140. package/client/dist/assets/settings-DKyhDGD1.js +1 -0
  141. package/client/dist/assets/settings-Deupji9c.js +1 -0
  142. package/client/dist/assets/settings-DqRMmwAM.js +1 -0
  143. package/client/dist/assets/settings-J4rj6vo-.js +1 -0
  144. package/client/dist/assets/setup-2xGgu4Sk.js +1 -0
  145. package/client/dist/assets/setup-Ba11l2JA.js +1 -0
  146. package/client/dist/assets/setup-BzW4kMok.js +1 -0
  147. package/client/dist/assets/setup-COzwYhkJ.js +1 -0
  148. package/client/dist/assets/setup-CRVULhtF.js +1 -0
  149. package/client/dist/assets/setup-DgvlxwpE.js +1 -0
  150. package/client/dist/assets/setup-Dw_wYeEY.js +1 -0
  151. package/client/dist/assets/setup-ZPtEeJPW.js +1 -0
  152. package/client/dist/assets/spending-BqbdPm_4.js +1 -0
  153. package/client/dist/assets/square-Ci-PFcFe.js +1 -0
  154. package/client/dist/assets/{terminal-BFRZnMPP.js → terminal-BAzpHtpY.js} +1 -1
  155. package/client/dist/assets/{terminal-IahE4LW9.js → terminal-C7dHeR3x.js} +1 -1
  156. package/client/dist/assets/{terminal-tITa81iz.js → terminal-CMla5Qsz.js} +1 -1
  157. package/client/dist/assets/{terminal-BZiIu3rf.js → terminal-CdNXH-n9.js} +1 -1
  158. package/client/dist/assets/{terminal-ScjyXNxQ.js → terminal-Cw5bZsmV.js} +1 -1
  159. package/client/dist/assets/{terminal-C2NAd7BW.js → terminal-DFUP4eNv.js} +1 -1
  160. package/client/dist/assets/{terminal-d7GadXbN.js → terminal-DGqljeK_.js} +1 -1
  161. package/client/dist/assets/{terminal-QpXZU2Bn.js → terminal-cU70NodO.js} +1 -1
  162. package/client/dist/assets/useDesktop-RSMTyy7V.js +1 -0
  163. package/client/dist/assets/useSharedWebSocket-BOdS5_Iz.js +2 -0
  164. package/client/dist/index.html +34 -35
  165. package/docs/README.md +3 -0
  166. package/docs/agent-live-steering.md +57 -0
  167. package/docs/ci-cd.md +57 -0
  168. package/docs/code-explorer.md +48 -0
  169. package/docs/codex.md +12 -1
  170. package/docs/features/detachable-mission-windows.md +59 -0
  171. package/docs/guide/de/integrations/5-mcp-server.md +13 -9
  172. package/docs/guide/de/integrations/6-agent-chat.md +1 -0
  173. package/docs/guide/en/integrations/5-mcp-server.md +13 -9
  174. package/docs/guide/en/integrations/6-agent-chat.md +1 -0
  175. package/docs/guide/es/integrations/5-mcp-server.md +13 -9
  176. package/docs/guide/es/integrations/6-agent-chat.md +1 -0
  177. package/docs/guide/fr/integrations/5-mcp-server.md +13 -9
  178. package/docs/guide/fr/integrations/6-agent-chat.md +1 -0
  179. package/docs/guide/it/integrations/5-mcp-server.md +13 -9
  180. package/docs/guide/it/integrations/6-agent-chat.md +1 -0
  181. package/docs/guide/ja/integrations/5-mcp-server.md +13 -9
  182. package/docs/guide/ja/integrations/6-agent-chat.md +1 -0
  183. package/docs/guide/pt/integrations/5-mcp-server.md +13 -9
  184. package/docs/guide/pt/integrations/6-agent-chat.md +1 -0
  185. package/docs/guide/zh/integrations/5-mcp-server.md +13 -9
  186. package/docs/guide/zh/integrations/6-agent-chat.md +1 -0
  187. package/docs/internals/README.md +1 -0
  188. package/docs/internals/browser-capture-performance.md +55 -3
  189. package/docs/internals/browser-login-popups.md +45 -0
  190. package/docs/internals/browser-native-retina-audit.md +39 -0
  191. package/docs/internals/ci-cd-audit.md +39 -0
  192. package/docs/internals/core-runtime-updates.md +68 -0
  193. package/docs/internals/embedded-browser-native-webview-evaluation.md +2 -0
  194. package/docs/internals/implementation-reliability-audit.md +93 -0
  195. package/docs/internals/interactive-jobs.md +110 -6
  196. package/docs/internals/mcp-mission-audit.md +76 -0
  197. package/docs/internals/project-builder.md +404 -42
  198. package/docs/internals/safe-pr-review-flow.md +52 -10
  199. package/docs/internals/startup-recovery-audit.md +58 -0
  200. package/docs/mcp.md +74 -41
  201. package/docs/mission-processes.md +59 -0
  202. package/docs/multi-repo-projects.md +51 -0
  203. package/docs/platforms/windows-parity.md +70 -0
  204. package/docs/platforms/windows.md +13 -11
  205. package/docs/running-pipelines.md +14 -0
  206. package/mcp-bridge/dist/specrails-mcp.js +7186 -0
  207. package/package.json +9 -4
  208. package/server/dist/agent-chat-manager.js +537 -68
  209. package/server/dist/agent-chat-router.js +87 -6
  210. package/server/dist/agent-context-resolver.js +129 -25
  211. package/server/dist/agent-defaults.js +19 -11
  212. package/server/dist/agent-input-store.js +206 -0
  213. package/server/dist/agent-mcp-config.js +54 -15
  214. package/server/dist/agent-operator-prompt.js +255 -65
  215. package/server/dist/agent-spec-framing.js +202 -0
  216. package/server/dist/agent-steering.js +218 -0
  217. package/server/dist/agent-store.js +153 -0
  218. package/server/dist/api-not-found.js +8 -0
  219. package/server/dist/background-process-control.js +164 -0
  220. package/server/dist/background-process-service.js +61 -0
  221. package/server/dist/background-process-store.js +242 -0
  222. package/server/dist/background-windows-bootstrap.js +238 -0
  223. package/server/dist/blueprint-chat-manager.js +476 -62
  224. package/server/dist/blueprint-commit.js +21 -1
  225. package/server/dist/blueprint-draft-parser.js +122 -17
  226. package/server/dist/blueprint-generation.js +238 -0
  227. package/server/dist/blueprint-operator-prompt.js +91 -63
  228. package/server/dist/blueprint-router.js +55 -3
  229. package/server/dist/blueprint-spec-fixtures.js +85 -0
  230. package/server/dist/blueprint-spec-quality.js +57 -16
  231. package/server/dist/blueprint-store.js +92 -1
  232. package/server/dist/browser-capture-manager.js +86 -30
  233. package/server/dist/browser-playwright.js +168 -45
  234. package/server/dist/browser-viewport.js +26 -0
  235. package/server/dist/chat-manager.js +23 -15
  236. package/server/dist/chromium-archive.cjs +83 -0
  237. package/server/dist/chromium-resolver.js +72 -64
  238. package/server/dist/code-activity.js +145 -0
  239. package/server/dist/code-explorer-router.js +714 -114
  240. package/server/dist/core-compat.js +28 -1
  241. package/server/dist/core-execution.js +111 -0
  242. package/server/dist/core-package.js +4 -16
  243. package/server/dist/core-runtime.js +202 -0
  244. package/server/dist/core-update-manager.js +161 -60
  245. package/server/dist/core-update-state.js +29 -0
  246. package/server/dist/db.js +66 -35
  247. package/server/dist/delivery-evidence.js +142 -39
  248. package/server/dist/desktop-db.js +239 -43
  249. package/server/dist/desktop-router.js +109 -9
  250. package/server/dist/dev-ports.js +8 -3
  251. package/server/dist/file-provenance.js +34 -22
  252. package/server/dist/file-story-manager.js +62 -64
  253. package/server/dist/file-story.js +118 -35
  254. package/server/dist/file-summary-generator.js +46 -5
  255. package/server/dist/file-summary-manager.js +355 -106
  256. package/server/dist/framework-manager.js +98 -110
  257. package/server/dist/framework-reseed.js +76 -10
  258. package/server/dist/git-diagnostics.js +2 -2
  259. package/server/dist/headroom-manager.js +38 -3
  260. package/server/dist/host-control.js +52 -0
  261. package/server/dist/index.js +43 -2
  262. package/server/dist/interactive-job-session.js +149 -16
  263. package/server/dist/internal-api.js +48 -0
  264. package/server/dist/jira/jira-materializer.js +1 -0
  265. package/server/dist/json-tolerant.js +174 -0
  266. package/server/dist/legacy-migration.js +4 -2
  267. package/server/dist/loop-command-catalog.js +83 -21
  268. package/server/dist/loop-constants.js +6 -1
  269. package/server/dist/loop-decider.js +11 -1
  270. package/server/dist/loop-executors.js +194 -45
  271. package/server/dist/loop-factory.js +11 -4
  272. package/server/dist/loop-graph.js +90 -4
  273. package/server/dist/loop-run-manager.js +301 -69
  274. package/server/dist/loop-shell-invocation.js +21 -0
  275. package/server/dist/loop-step-idle.js +55 -0
  276. package/server/dist/loop-templates.js +31 -7
  277. package/server/dist/mcp/agent-capability.js +37 -4
  278. package/server/dist/mcp/guide.js +149 -36
  279. package/server/dist/mcp/mcp-admin-router.js +20 -4
  280. package/server/dist/mcp/mcp-server.js +89 -43
  281. package/server/dist/mcp/mcp-tiers.js +6 -5
  282. package/server/dist/mcp/mcp-token.js +3 -1
  283. package/server/dist/mcp/resources.js +5 -4
  284. package/server/dist/mcp/tools/catalog.js +4 -0
  285. package/server/dist/mcp/tools/code.js +125 -14
  286. package/server/dist/mcp/tools/context.js +153 -0
  287. package/server/dist/mcp/tools/git.js +15 -5
  288. package/server/dist/mcp/tools/jobs.js +84 -71
  289. package/server/dist/mcp/tools/loops.js +17 -6
  290. package/server/dist/mcp/tools/meta.js +74 -27
  291. package/server/dist/mcp/tools/mission.js +29 -0
  292. package/server/dist/mcp/tools/projects.js +22 -6
  293. package/server/dist/mcp/tools/rails.js +48 -11
  294. package/server/dist/mcp/tools/specs.js +86 -14
  295. package/server/dist/mcp/tools/support.js +1 -1
  296. package/server/dist/mcp/tools/types.js +117 -43
  297. package/server/dist/mcp/tools/watch.js +144 -51
  298. package/server/dist/milestone-chain-store.js +164 -0
  299. package/server/dist/milestone-chain.js +487 -0
  300. package/server/dist/milestone-progress.js +348 -0
  301. package/server/dist/mobile/mobile-devices.js +10 -7
  302. package/server/dist/mobile/mobile-gateway.js +2 -0
  303. package/server/dist/mobile/mobile-missions.js +214 -0
  304. package/server/dist/mobile/mobile-redact.js +17 -3
  305. package/server/dist/mobile/mobile-router.js +46 -4
  306. package/server/dist/mobile/mobile-ws.js +49 -4
  307. package/server/dist/multi-repo-bases.js +56 -0
  308. package/server/dist/multi-repo-checkout.js +101 -0
  309. package/server/dist/multi-repo-delivery.js +100 -0
  310. package/server/dist/multi-repo-execution-store.js +65 -0
  311. package/server/dist/multi-repo-execution.js +453 -0
  312. package/server/dist/offline-assemble.js +10 -8
  313. package/server/dist/openspec-runtime-plugin-commands.json +18 -0
  314. package/server/dist/openspec-runtime-plugin.js +120 -0
  315. package/server/dist/plugins/codex-spawn.js +49 -0
  316. package/server/dist/plugins/serena/manifest.js +2 -2
  317. package/server/dist/plugins/serena/templates/instructions.md +16 -0
  318. package/server/dist/pr-publisher.js +1 -1
  319. package/server/dist/profiles-router.js +3 -18
  320. package/server/dist/project-code-discovery.js +142 -0
  321. package/server/dist/project-git.js +19 -21
  322. package/server/dist/project-registry.js +272 -20
  323. package/server/dist/project-repositories.js +157 -0
  324. package/server/dist/project-repository-provenance.js +31 -0
  325. package/server/dist/project-router-background-processes.js +105 -0
  326. package/server/dist/project-router-git.js +75 -16
  327. package/server/dist/project-router-jobs.js +2 -126
  328. package/server/dist/project-router-loop-runs.js +72 -2
  329. package/server/dist/project-router-tickets.js +53 -8
  330. package/server/dist/project-router.js +239 -38
  331. package/server/dist/provider-detection.js +18 -6
  332. package/server/dist/provider-limit.js +64 -0
  333. package/server/dist/providers/claude-adapter.js +26 -1
  334. package/server/dist/providers/claude-background-tasks.js +18 -0
  335. package/server/dist/providers/claude-live-session.js +383 -0
  336. package/server/dist/providers/codex-adapter.js +19 -0
  337. package/server/dist/providers/codex-live-session.js +545 -0
  338. package/server/dist/providers/gemini-adapter.js +1 -0
  339. package/server/dist/providers/live-session-types.js +12 -0
  340. package/server/dist/providers/live-session.js +13 -0
  341. package/server/dist/providers/terminal-result.js +24 -0
  342. package/server/dist/queue-manager.js +2 -0
  343. package/server/dist/rail-isolated-launch.js +141 -16
  344. package/server/dist/rail-pr-decision.js +364 -99
  345. package/server/dist/rail-pr-store.js +24 -10
  346. package/server/dist/rail-worktree-release.js +34 -2
  347. package/server/dist/rail-worktrees-store.js +4 -4
  348. package/server/dist/rails-router.js +249 -55
  349. package/server/dist/repo-lock.js +92 -17
  350. package/server/dist/review-packet.js +23 -13
  351. package/server/dist/schemas/file-summary.v1.json +59 -59
  352. package/server/dist/schemas/profile.v1.json +137 -137
  353. package/server/dist/setup-manager.js +52 -8
  354. package/server/dist/shell-integration/bash-shim.bash +45 -0
  355. package/server/dist/shell-integration/fish-shim.fish +36 -0
  356. package/server/dist/shell-integration/powershell-shim.ps1 +48 -0
  357. package/server/dist/shell-integration/zsh-shim.zsh +47 -0
  358. package/server/dist/smash-runner.js +1 -0
  359. package/server/dist/spawn-lifecycle.js +44 -27
  360. package/server/dist/spec-contract-prompt.js +143 -0
  361. package/server/dist/spec-draft-parser.js +6 -1
  362. package/server/dist/spec-models.js +1 -0
  363. package/server/dist/stuck-run-detector.js +1 -0
  364. package/server/dist/terminal-manager.js +21 -1
  365. package/server/dist/terminal-shell-integration.js +4 -1
  366. package/server/dist/transient-children.js +619 -230
  367. package/server/dist/util/cli-prompt.js +78 -5
  368. package/server/dist/util/sqlite-migrations.js +20 -0
  369. package/server/dist/util/win-spawn.js +18 -3
  370. package/server/dist/util/windows-provider-shell.js +29 -0
  371. package/server/dist/verification-sentinel.js +14 -0
  372. package/server/dist/vitest-setup.js +2 -0
  373. package/server/dist/windows-job-supervisor.js +133 -0
  374. package/server/dist/workspace-manager.js +9 -30
  375. package/server/dist/worktree-manager.js +17 -2
  376. package/server/dist/worktree-overlay.js +3 -1
  377. package/client/dist/assets/ActivityFeedPage-tAQcWs1R.js +0 -1
  378. package/client/dist/assets/AgentBrowserCapture-BX8BNae5.js +0 -1
  379. package/client/dist/assets/AgentModeAnalyticsPane-BVAqCL0K.js +0 -2
  380. package/client/dist/assets/AgentModeCodePane-9XfqQc9c.js +0 -2
  381. package/client/dist/assets/AgentModeJobsPane-BCjLsPhc.js +0 -1
  382. package/client/dist/assets/AgentsPage-C8J_42sm.js +0 -87
  383. package/client/dist/assets/AnalyticsPage-BtQuyfJm.js +0 -1
  384. package/client/dist/assets/CodePage-BZ2MwdCu.js +0 -2
  385. package/client/dist/assets/DesktopAnalyticsPage-Bh_29V4g.js +0 -1
  386. package/client/dist/assets/ExportDropdown-BrtfWzEG.js +0 -1
  387. package/client/dist/assets/InteractiveJobComposer-BCZiSfzc.js +0 -19
  388. package/client/dist/assets/JobDetailModal-NAJmuYIT.js +0 -1
  389. package/client/dist/assets/JobDetailPage-fE0ZycUX.js +0 -1
  390. package/client/dist/assets/JobsPage-z5M_HVJG.js +0 -1
  391. package/client/dist/assets/LoopBuilderPage-j1FQS47c.js +0 -7
  392. package/client/dist/assets/LoopPreviewModal-DhDJjdCe.js +0 -1
  393. package/client/dist/assets/LoopsPage-Ll7zORZb.js +0 -1
  394. package/client/dist/assets/MinimizedChatsContext-Bd3OqR_S.js +0 -1
  395. package/client/dist/assets/PluginsPage-DN4kGvJt.js +0 -2
  396. package/client/dist/assets/ProjectSettingsDialog-OgsgxjzF.js +0 -1
  397. package/client/dist/assets/ReviewPacketPage-DFog4sXn.js +0 -1
  398. package/client/dist/assets/TemplatePreviewModal-L2XxqiOD.js +0 -1
  399. package/client/dist/assets/TicketDetailModal-CsF-AWZX.js +0 -14
  400. package/client/dist/assets/Trans-Bsz9_gyq.js +0 -1
  401. package/client/dist/assets/agent-BQgKv_2F.js +0 -1
  402. package/client/dist/assets/agent-C1NRDJqL.js +0 -1
  403. package/client/dist/assets/agent-CLNleiy6.js +0 -1
  404. package/client/dist/assets/agent-CmceArkQ.js +0 -1
  405. package/client/dist/assets/agent-D3UxQ3SR.js +0 -1
  406. package/client/dist/assets/agent-DB1JeE64.js +0 -1
  407. package/client/dist/assets/agent-DXkHcQ-D.js +0 -1
  408. package/client/dist/assets/agent-DjJJJ2M8.js +0 -1
  409. package/client/dist/assets/auth-D_5bQv5W.js +0 -1
  410. package/client/dist/assets/ban-E4EaZe3a.js +0 -1
  411. package/client/dist/assets/bot-DzuuuC5R.js +0 -1
  412. package/client/dist/assets/browser-BgqHDMXP.js +0 -1
  413. package/client/dist/assets/browser-CsKJtrpv.js +0 -1
  414. package/client/dist/assets/browser-Cu58EpZU.js +0 -1
  415. package/client/dist/assets/browser-CvXOJm-j.js +0 -1
  416. package/client/dist/assets/browser-DOrFh1c7.js +0 -1
  417. package/client/dist/assets/browser-DQv_Jo7x.js +0 -1
  418. package/client/dist/assets/browser-LPgxvJu2.js +0 -1
  419. package/client/dist/assets/browser-eL-3MIEI.js +0 -1
  420. package/client/dist/assets/builder-0IWtosUm.js +0 -1
  421. package/client/dist/assets/builder-Bf2N6XWn.js +0 -1
  422. package/client/dist/assets/builder-BnqaaBkT.js +0 -1
  423. package/client/dist/assets/builder-BxxfGgAO.js +0 -1
  424. package/client/dist/assets/builder-C5IIgWsq.js +0 -1
  425. package/client/dist/assets/builder-CHuahItu.js +0 -1
  426. package/client/dist/assets/builder-CjWosXL_.js +0 -1
  427. package/client/dist/assets/builder-CpCIOoKj.js +0 -1
  428. package/client/dist/assets/code-CHPkOkDS.js +0 -1
  429. package/client/dist/assets/commands-6tYQih5m.js +0 -1
  430. package/client/dist/assets/commands-BK2QinXN.js +0 -1
  431. package/client/dist/assets/commands-C2Nca76h.js +0 -1
  432. package/client/dist/assets/commands-CKdLihNX.js +0 -1
  433. package/client/dist/assets/commands-CYIBTAlc.js +0 -1
  434. package/client/dist/assets/commands-CjUtYJIM.js +0 -1
  435. package/client/dist/assets/commands-Cq_u_8y9.js +0 -1
  436. package/client/dist/assets/commands-D_ZHYQug.js +0 -1
  437. package/client/dist/assets/common-6MLbD-jM.js +0 -1
  438. package/client/dist/assets/common-BKGEu4rm.js +0 -1
  439. package/client/dist/assets/common-BNbnv8cE.js +0 -1
  440. package/client/dist/assets/common-BqGWtDbt.js +0 -1
  441. package/client/dist/assets/common-DBtTzeYm.js +0 -1
  442. package/client/dist/assets/common-DEW3Nr8n.js +0 -1
  443. package/client/dist/assets/common-DHsv9N5d.js +0 -1
  444. package/client/dist/assets/common-mkrEcW-Y.js +0 -1
  445. package/client/dist/assets/dashboard-B9GhC5ku.js +0 -1
  446. package/client/dist/assets/dashboard-CBnv6zOI.js +0 -1
  447. package/client/dist/assets/dashboard-CaP0fU87.js +0 -1
  448. package/client/dist/assets/dashboard-D4ZZ4x84.js +0 -1
  449. package/client/dist/assets/dashboard-DmuTQBX8.js +0 -1
  450. package/client/dist/assets/dashboard-DynLYj3d.js +0 -1
  451. package/client/dist/assets/dashboard-zj4gGFJ1.js +0 -1
  452. package/client/dist/assets/dialog-CdWCb02j.js +0 -45
  453. package/client/dist/assets/dist-bVqfYmby.js +0 -1
  454. package/client/dist/assets/dist-js-CWeUdf5Z.js +0 -1
  455. package/client/dist/assets/formatDistanceToNow-DQCvMgUc.js +0 -1
  456. package/client/dist/assets/getTimezoneOffsetInMilliseconds-C16cxSe1.js +0 -1
  457. package/client/dist/assets/git-refresh-wTW0rO0Z.js +0 -1
  458. package/client/dist/assets/i18n-hnX2DPmo.js +0 -2
  459. package/client/dist/assets/index-DIhMEAVw.js +0 -66
  460. package/client/dist/assets/index-DwygcU7s.css +0 -2
  461. package/client/dist/assets/mcp-B8X8wBLu.js +0 -1
  462. package/client/dist/assets/mcp-C2AojZZN.js +0 -1
  463. package/client/dist/assets/mcp-D8BT78_L.js +0 -1
  464. package/client/dist/assets/mcp-Df5RWBNk.js +0 -1
  465. package/client/dist/assets/mcp-Dj7BgMQW.js +0 -1
  466. package/client/dist/assets/mcp-Q7iT7rcz.js +0 -1
  467. package/client/dist/assets/mcp-YlvNvJbW.js +0 -1
  468. package/client/dist/assets/mcp-mKFpTSRb.js +0 -1
  469. package/client/dist/assets/narration-4Z2VUhZw.js +0 -1
  470. package/client/dist/assets/narration-BGuiEDj3.js +0 -1
  471. package/client/dist/assets/narration-BuSoqRmv.js +0 -1
  472. package/client/dist/assets/narration-CB9t29fy.js +0 -1
  473. package/client/dist/assets/narration-CU3x8FHD.js +0 -1
  474. package/client/dist/assets/narration-CWeXhfP5.js +0 -1
  475. package/client/dist/assets/narration-DRaDnhT5.js +0 -1
  476. package/client/dist/assets/narration-DtEbQgCw.js +0 -1
  477. package/client/dist/assets/nav-ApJVg3xu.js +0 -1
  478. package/client/dist/assets/nav-BKNky1Bg.js +0 -1
  479. package/client/dist/assets/nav-C6vWWKYx.js +0 -1
  480. package/client/dist/assets/nav-CXJ6Aw89.js +0 -1
  481. package/client/dist/assets/nav-CsmFBIDf.js +0 -1
  482. package/client/dist/assets/nav-DjkmQJjd.js +0 -1
  483. package/client/dist/assets/packet-B1IxaL5f.js +0 -1
  484. package/client/dist/assets/packet-Bh5kkbiB.js +0 -1
  485. package/client/dist/assets/packet-By8fqYxW.js +0 -1
  486. package/client/dist/assets/packet-CZdvA44E.js +0 -1
  487. package/client/dist/assets/packet-CrjHRQCP.js +0 -1
  488. package/client/dist/assets/packet-Dwi2pTtx.js +0 -1
  489. package/client/dist/assets/packet-_-ZIa0IR.js +0 -1
  490. package/client/dist/assets/packet-n0W6E1Hn.js +0 -1
  491. package/client/dist/assets/play-DNlrawbS.js +0 -1
  492. package/client/dist/assets/provider-capabilities-Doej9lfa.js +0 -1
  493. package/client/dist/assets/settings-Bd0x4TBa.js +0 -1
  494. package/client/dist/assets/settings-C1-7cXzC.js +0 -1
  495. package/client/dist/assets/settings-CAFaT38b.js +0 -1
  496. package/client/dist/assets/settings-Ct3v1nW2.js +0 -1
  497. package/client/dist/assets/settings-DHh7_IF5.js +0 -1
  498. package/client/dist/assets/settings-DQXAmdcv.js +0 -1
  499. package/client/dist/assets/settings-guzlhpup.js +0 -1
  500. package/client/dist/assets/settings-sZfTPMEH.js +0 -1
  501. package/client/dist/assets/setup-3jWaoVUO.js +0 -1
  502. package/client/dist/assets/setup-BjgRneb2.js +0 -1
  503. package/client/dist/assets/setup-BzFIjv-f.js +0 -1
  504. package/client/dist/assets/setup-C4VfQsxE.js +0 -1
  505. package/client/dist/assets/setup-DfUkpDbN.js +0 -1
  506. package/client/dist/assets/setup-ECe90Pmu.js +0 -1
  507. package/client/dist/assets/setup-dlqC4Jmm.js +0 -1
  508. package/client/dist/assets/setup-jwfLMiM2.js +0 -1
  509. package/client/dist/assets/spending-EC9xqio-.js +0 -1
  510. package/client/dist/assets/useDesktop-BQVt8lFd.js +0 -1
@@ -7,6 +7,7 @@ const auth_1 = require("../auth");
7
7
  const mobile_redact_1 = require("./mobile-redact");
8
8
  const mobile_auth_1 = require("./mobile-auth");
9
9
  const mobile_devices_1 = require("./mobile-devices");
10
+ const mobile_missions_1 = require("./mobile-missions");
10
11
  // The gateway's authenticated REST surface: the `/v1/*` allow-list. Each route
11
12
  // forwards, in-process, via a REAL loopback HTTP request to
12
13
  // http://127.0.0.1:<desktopPort> with the master token injected server-side as
@@ -34,7 +35,7 @@ function createMobileRouter(deps) {
34
35
  // any `:pid`-scoped route is gated against the device's granted project set.
35
36
  // An unrestricted device (null grant ⇒ all projects, legacy default) passes
36
37
  // through; a scoped device gets 403 for any project it wasn't granted. The
37
- // bare `GET /v1/projects` list has no `:pid` and is unaffected.
38
+ // bare `GET /v1/projects` list applies the same grant when shaping its response.
38
39
  v1.use('/projects/:pid', (req, res, next) => {
39
40
  const pid = seg(req.params.pid);
40
41
  if (!PID_RE.test(pid)) {
@@ -56,11 +57,12 @@ function createMobileRouter(deps) {
56
57
  });
57
58
  /** Forward to the internal desktop API with the master token injected; redact
58
59
  * the JSON response. `internalPath` is built from validated params only. */
59
- async function forward(res, method, internalPath, query, body) {
60
+ async function forward(res, method, internalPath, query, body, transform) {
60
61
  const url = internalBase + internalPath + (query ? `?${query}` : '');
61
62
  try {
62
63
  const init = {
63
64
  method,
65
+ signal: AbortSignal.timeout(30_000),
64
66
  headers: {
65
67
  'x-desktop-token': (0, auth_1.loadOrGenerateToken)(),
66
68
  ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}),
@@ -79,7 +81,7 @@ function createMobileRouter(deps) {
79
81
  res.status(502).json({ error: 'Bad upstream response' });
80
82
  return;
81
83
  }
82
- res.status(upstream.status).json((0, mobile_redact_1.redact)(json));
84
+ res.status(upstream.status).json((0, mobile_redact_1.redact)(upstream.ok && transform ? transform(json) : json));
83
85
  }
84
86
  catch {
85
87
  res.status(502).json({ error: 'Server unreachable' });
@@ -102,7 +104,13 @@ function createMobileRouter(deps) {
102
104
  // —— Reads ——
103
105
  v1.get('/projects', (req, res) => {
104
106
  // Exact GET /api/projects is served by the desktop router (mounted at /api).
105
- void forward(res, 'GET', '/api/projects', '');
107
+ const allowed = (0, mobile_devices_1.getAllowedProjects)(deps.db, req.mobileDevice.id);
108
+ void forward(res, 'GET', '/api/projects', '', undefined, json => {
109
+ if (allowed === null || !json || typeof json !== 'object')
110
+ return json;
111
+ const value = json;
112
+ return { ...value, projects: value.projects?.filter(project => project.id && allowed.has(project.id)) ?? [] };
113
+ });
106
114
  });
107
115
  const projectReads = [
108
116
  ['/projects/:pid/tickets', '/tickets'],
@@ -164,6 +172,13 @@ function createMobileRouter(deps) {
164
172
  narrowed.priority = b.priority;
165
173
  if (typeof b.title === 'string')
166
174
  narrowed.title = b.title;
175
+ if (b.repositoryIds !== undefined) {
176
+ if (!Array.isArray(b.repositoryIds) || !b.repositoryIds.length || b.repositoryIds.length > 30 || b.repositoryIds.some(id => typeof id !== 'string' || !PID_RE.test(id))) {
177
+ res.status(400).json({ error: 'Invalid repository selection' });
178
+ return;
179
+ }
180
+ narrowed.repositoryIds = b.repositoryIds;
181
+ }
167
182
  void forward(res, 'PATCH', `/api/projects/${encodeURIComponent(pid)}/tickets/${encodeURIComponent(tid)}`, '', narrowed);
168
183
  });
169
184
  v1.delete('/projects/:pid/tickets/:tid', (req, res) => {
@@ -206,6 +221,13 @@ function createMobileRouter(deps) {
206
221
  }
207
222
  if (typeof b.interactive === 'boolean')
208
223
  narrowed.interactive = b.interactive;
224
+ if (b.repositoryIds !== undefined) {
225
+ if (!Array.isArray(b.repositoryIds) || !b.repositoryIds.length || b.repositoryIds.length > 30 || b.repositoryIds.some(id => typeof id !== 'string' || !PID_RE.test(id))) {
226
+ res.status(400).json({ error: 'Invalid repository selection' });
227
+ return;
228
+ }
229
+ narrowed.repositoryIds = b.repositoryIds;
230
+ }
209
231
  void forward(res, 'POST', `/api/projects/${encodeURIComponent(pid)}/rails/${encodeURIComponent(i)}/launch`, '', narrowed);
210
232
  });
211
233
  v1.put('/projects/:pid/rails/:i/engine', (req, res) => {
@@ -382,6 +404,12 @@ function createMobileRouter(deps) {
382
404
  narrowed.acceptanceCriteria = b.acceptanceCriteria.filter((x) => typeof x === 'string');
383
405
  void forward(res, 'POST', `/api/projects/${encodeURIComponent(pid)}/tickets/from-draft`, '', narrowed);
384
406
  });
407
+ v1.use((0, mobile_missions_1.createMobileMissionsRouter)({ db: deps.db, upstream: async (method, path, body) => {
408
+ const response = await fetch(internalBase + path, { method, signal: AbortSignal.timeout(30_000),
409
+ headers: { 'x-desktop-token': (0, auth_1.loadOrGenerateToken)(), ...(body !== undefined ? { 'Content-Type': 'application/json' } : {}) },
410
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}) });
411
+ return { status: response.status, json: await response.json() };
412
+ } }));
385
413
  router.use('/v1', v1);
386
414
  // Any unmatched gateway path → 404 JSON (NEVER falls through to a SPA handler;
387
415
  // the gateway serves JSON/WS only, no static client).
@@ -393,6 +421,20 @@ function createMobileRouter(deps) {
393
421
  /** The allow-list, exported for the CI drift test (asserts each entry resolves
394
422
  * against a real internal route, and that no traversal param is accepted). */
395
423
  exports.MOBILE_ALLOWLIST = [
424
+ { method: 'GET', path: '/v1/capabilities' },
425
+ { method: 'GET', path: '/v1/mission-models' },
426
+ { method: 'GET', path: '/v1/projects/:pid/repositories' },
427
+ { method: 'GET', path: '/v1/projects/:pid/missions' },
428
+ { method: 'POST', path: '/v1/projects/:pid/missions' },
429
+ { method: 'GET', path: '/v1/projects/:pid/missions/:cid' },
430
+ { method: 'POST', path: '/v1/projects/:pid/missions/:cid/send' },
431
+ { method: 'POST', path: '/v1/projects/:pid/missions/:cid/abort' },
432
+ { method: 'PATCH', path: '/v1/projects/:pid/missions/:cid/queue/:queueId' },
433
+ { method: 'DELETE', path: '/v1/projects/:pid/missions/:cid/queue/:queueId' },
434
+ { method: 'POST', path: '/v1/projects/:pid/missions/:cid/queue/:queueId/steer' },
435
+ { method: 'GET', path: '/v1/projects/:pid/missions/:cid/processes' },
436
+ { method: 'GET', path: '/v1/projects/:pid/missions/:cid/processes/:processId/logs' },
437
+ { method: 'DELETE', path: '/v1/projects/:pid/missions/:cid/processes/:processId' },
396
438
  { method: 'GET', path: '/v1/projects' },
397
439
  { method: 'GET', path: '/v1/projects/:pid/tickets' },
398
440
  { method: 'GET', path: '/v1/projects/:pid/tickets/:tid' },
@@ -16,6 +16,24 @@ const LOG_LINE_MAX = 2048;
16
16
  const LOG_BATCH_MAX = 60;
17
17
  function topicFor(type) {
18
18
  switch (type) {
19
+ case 'agent_stream':
20
+ case 'agent_done':
21
+ case 'agent_partial':
22
+ case 'agent_error':
23
+ case 'agent_title':
24
+ case 'agent_tool':
25
+ case 'agent_tool_result':
26
+ case 'agent_queued':
27
+ case 'agent_dequeued':
28
+ case 'agent_steered':
29
+ case 'agent_queue_edited':
30
+ case 'agent_queue_removed':
31
+ case 'agent_queue_cleared':
32
+ case 'agent_input_receipt':
33
+ case 'agent_pr_decision':
34
+ case 'background_process.started':
35
+ case 'background_process.updated':
36
+ case 'background_process.exited': return 'missions';
19
37
  case 'queue': return 'queue';
20
38
  case 'phase': return 'phase';
21
39
  case 'ticket_created':
@@ -107,9 +125,11 @@ class MobileWsBridge {
107
125
  // it so a companion can only subscribe to projects it was granted. Default
108
126
  // resolver (undefined) keeps every device unrestricted — byte-identical legacy.
109
127
  _allowedProjectsFor;
128
+ _missionProjectFor;
110
129
  constructor(opts = {}) {
111
130
  this._clock = opts.clock ?? (() => Date.now());
112
131
  this._allowedProjectsFor = opts.allowedProjectsFor ?? (() => null);
132
+ this._missionProjectFor = opts.missionProjectFor ?? (() => null);
113
133
  }
114
134
  start() {
115
135
  if (this._unsub)
@@ -227,10 +247,34 @@ class MobileWsBridge {
227
247
  if (!topic)
228
248
  return;
229
249
  for (const s of this._socks) {
250
+ const allowed = this._allowedProjectsFor(s.deviceId);
251
+ if (topic === 'missions') {
252
+ if (allowed !== null || !s.topics.has('missions'))
253
+ continue;
254
+ const value = msg;
255
+ const process = value.process;
256
+ const cid = typeof value.conversationId === 'string' ? value.conversationId : process?.chatId;
257
+ const projectId = cid ? this._missionProjectFor(cid) : null;
258
+ if (!projectId || !s.projects.has(projectId))
259
+ continue;
260
+ // The snapshot is authoritative. Events are bounded invalidation hints;
261
+ // never stream tool inputs/results or arbitrary provider payloads.
262
+ this.send(s, (0, mobile_redact_1.redact)({ type: 'mission.updated', projectId, conversationId: cid, event: msg.type, timestamp: value.timestamp,
263
+ ...(msg.type === 'agent_stream' ? { delta: String(value.delta ?? '').slice(0, 16_000) } : {}),
264
+ ...(msg.type === 'agent_tool' ? { tool: String(value.tool ?? '').slice(0, 100) } : {}) }));
265
+ continue;
266
+ }
230
267
  if (topic === 'hub') {
231
268
  // App-level (project registry): always forward, but translate to the
232
269
  // legacy mobile wire types and redact (the projects payload carries `path`).
233
- this.send(s, (0, mobile_redact_1.redact)(toMobileWire(msg)));
270
+ if (msg.type === 'desktop.projects')
271
+ this.send(s, (0, mobile_redact_1.redact)(toMobileWire({ ...msg, projects: allowed === null ? msg.projects : msg.projects.filter(project => allowed.has(project.id)) })));
272
+ else if (msg.type === 'desktop.project_added') {
273
+ if (allowed === null || allowed.has(msg.project.id))
274
+ this.send(s, (0, mobile_redact_1.redact)(toMobileWire(msg)));
275
+ }
276
+ else if (msg.type === 'desktop.project_removed' && (allowed === null || allowed.has(msg.projectId)))
277
+ this.send(s, (0, mobile_redact_1.redact)(toMobileWire(msg)));
234
278
  continue;
235
279
  }
236
280
  // App-level desktop budget alert: deliver to subscribers holding the
@@ -239,12 +283,12 @@ class MobileWsBridge {
239
283
  // topic gate like every other delivery — a device that never subscribed to
240
284
  // `alerts` (topics init empty) must NOT receive these frames (BUG-MOBILE-05).
241
285
  if (topic === 'alerts' && msg.type === 'desktop_daily_budget_exceeded') {
242
- if (s.topics.has('alerts'))
286
+ if (allowed === null && s.topics.has('alerts'))
243
287
  this.send(s, (0, mobile_redact_1.redact)(toMobileWire(msg)));
244
288
  continue;
245
289
  }
246
290
  const projectId = msg.projectId;
247
- if (!projectId || !s.projects.has(projectId))
291
+ if (!projectId || !s.projects.has(projectId) || (allowed !== null && !allowed.has(projectId)))
248
292
  continue;
249
293
  if (topic === 'jobtail') {
250
294
  if (!s.watchedJob || s.watchedJob.projectId !== projectId)
@@ -278,7 +322,8 @@ class MobileWsBridge {
278
322
  flushLogs() {
279
323
  for (const s of this._socks) {
280
324
  for (const [jobId, lines] of s.logBuf) {
281
- if (lines.length === 0)
325
+ const allowed = this._allowedProjectsFor(s.deviceId);
326
+ if (lines.length === 0 || !s.watchedJob || s.watchedJob.jobId !== jobId || (allowed !== null && !allowed.has(s.watchedJob.projectId)))
282
327
  continue;
283
328
  const dropped = s.logDropped.get(jobId) ?? 0;
284
329
  this.send(s, { type: 'log_batch', jobId, lines, dropped });
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveRepositoryDeliveryBases = resolveRepositoryDeliveryBases;
4
+ const rail_pr_store_1 = require("./rail-pr-store");
5
+ const project_repositories_1 = require("./project-repositories");
6
+ /** Resolve the latest delivered head per member from durable milestone history.
7
+ * The request names deliveries, never invents a repository/branch/SHA binding. */
8
+ async function resolveRepositoryDeliveryBases(db, project, selectedIds, value, git) {
9
+ const repositoryBaseBranches = {};
10
+ const repositoryBaseShas = {};
11
+ if (value === undefined)
12
+ return { repositoryBaseBranches, repositoryBaseShas };
13
+ if (!Array.isArray(value) || value.length === 0 || value.length > 100 || value.some((id) => typeof id !== 'string' || id.length === 0 || id.length > 200)) {
14
+ throw new Error('baseDeliveryIds must be a nonempty list of at most 100 delivery IDs');
15
+ }
16
+ const primary = (0, project_repositories_1.resolveProjectRepository)(project);
17
+ const bases = new Map();
18
+ for (const id of value) {
19
+ const row = (0, rail_pr_store_1.getPrDelivery)(db, id);
20
+ if (!row || row.parent_delivery_id)
21
+ throw new Error(`Unknown base delivery: ${id}`);
22
+ const snapshot = (0, rail_pr_store_1.toPrDeliverySnapshot)(row);
23
+ if (!['on_review', 'no_changes', 'pr_draft', 'pr_ready', 'completed', 'merged'].includes(snapshot.decision))
24
+ throw new Error(`Base delivery ${id} is not ready`);
25
+ const entries = snapshot.repositoryDeliveries?.length ? snapshot.repositoryDeliveries : [{
26
+ repositoryId: primary.id, decision: snapshot.decision, baseBranch: snapshot.baseBranch,
27
+ branch: snapshot.branch ?? (snapshot.branches.length === 1 ? snapshot.branches[0].branch : null),
28
+ deliverySha: snapshot.deliverySha ?? (snapshot.branches.length === 1 ? snapshot.branches[0].finalSha : null),
29
+ }];
30
+ for (const entry of entries) {
31
+ if (!selectedIds.includes(entry.repositoryId) || ['no_changes', 'completed'].includes(entry.decision))
32
+ continue;
33
+ const branch = entry.decision === 'merged' ? (('integrationBranch' in entry && entry.integrationBranch) || entry.baseBranch) : entry.branch;
34
+ if (!branch || !entry.deliverySha)
35
+ throw new Error(`Base delivery ${id} has no verified head for repository ${entry.repositoryId}`);
36
+ bases.set(entry.repositoryId, { branch, sha: entry.deliverySha, merged: entry.decision === 'merged' });
37
+ }
38
+ }
39
+ for (const [id, base] of bases) {
40
+ const repository = (0, project_repositories_1.resolveProjectRepository)(project, id);
41
+ const result = await git.run(['rev-parse', '--verify', `refs/heads/${base.branch}`], repository.path);
42
+ const head = result.stdout.trim();
43
+ if (result.code !== 0 || !/^[a-f0-9]{40,64}$/i.test(head))
44
+ throw new Error(`Missing base branch ${base.branch} in ${repository.name}`);
45
+ if (base.merged) {
46
+ const contains = await git.run(['merge-base', '--is-ancestor', base.sha, head], repository.path);
47
+ if (contains.code !== 0)
48
+ throw new Error(`Integrated base no longer contains the delivered commit in ${repository.name}`);
49
+ }
50
+ else if (head !== base.sha)
51
+ throw new Error(`Delivered base branch changed in ${repository.name}`);
52
+ repositoryBaseBranches[id] = base.branch;
53
+ repositoryBaseShas[id] = head;
54
+ }
55
+ return { repositoryBaseBranches, repositoryBaseShas };
56
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.repositoryDeliveryCheckoutTarget = repositoryDeliveryCheckoutTarget;
4
+ exports.checkoutRepositoryDelivery = checkoutRepositoryDelivery;
5
+ const ids_1 = require("./ids");
6
+ const repo_lock_1 = require("./repo-lock");
7
+ const project_git_1 = require("./project-git");
8
+ const rail_pr_store_1 = require("./rail-pr-store");
9
+ const rail_worktree_release_1 = require("./rail-worktree-release");
10
+ const multi_repo_execution_1 = require("./multi-repo-execution");
11
+ const agent_chat_registry_1 = require("./agent-chat-registry");
12
+ function repositoryDeliveryCheckoutTarget(delivery) {
13
+ if (delivery.branch && delivery.deliverySha)
14
+ return { branch: delivery.branch, sha: delivery.deliverySha };
15
+ const units = delivery.branches;
16
+ if (!delivery.branch && !delivery.deliverySha && units.length === 1 && units[0].succeeded && units[0].finalSha &&
17
+ units[0].deliveryOutcome !== 'blocked' && units[0].deliveryOutcome !== 'no_changes' &&
18
+ units[0].implementationOutcome !== 'failed' && units[0].changed !== false) {
19
+ return { branch: units[0].branch, sha: units[0].finalSha };
20
+ }
21
+ return null;
22
+ }
23
+ function checkoutBlock(delivery) {
24
+ if ((0, rail_pr_store_1.isTerminalPrDecision)(delivery.decision) || ['building', 'implementation_failed', 'no_changes'].includes(delivery.decision))
25
+ return 'delivery is not awaiting implementation review';
26
+ if (delivery.deliveryOutcome === 'blocked')
27
+ return 'delivery is blocked and has no safely checkoutable result';
28
+ const target = repositoryDeliveryCheckoutTarget(delivery);
29
+ return target && /^[0-9a-f]{40,64}$/i.test(target.sha) ? null : 'delivery has no verified commit available for checkout';
30
+ }
31
+ /** Checkout changes one physical checkout; it never accepts a shared spec. */
32
+ async function checkoutRepositoryDelivery(deps, input) {
33
+ const parent = (0, rail_pr_store_1.getPrDelivery)(deps.db, input.prDeliveryId);
34
+ if (!parent || parent.parent_delivery_id || !parent.execution_manifest)
35
+ return { status: 404, body: { error: 'Unknown repository delivery group' } };
36
+ const children = (0, multi_repo_execution_1.listRepositoryDeliveries)(deps.db, parent.id);
37
+ if (!input.repositoryId && children.length !== 1)
38
+ return { status: 400, body: { error: 'repositoryId_required', detail: 'Choose the repository checkout to move.' } };
39
+ const child = input.repositoryId ? children.find((row) => row.repository_id === input.repositoryId) : children[0];
40
+ if (!child?.repository_path)
41
+ return { status: 404, body: { error: 'Unknown repository in this delivery' } };
42
+ return (0, repo_lock_1.withRepoLock)(child.repository_path, async () => {
43
+ deps.assertAdmission?.();
44
+ const active = (0, rail_pr_store_1.getActivePrDeliveryByRail)(deps.db, parent.rail_index);
45
+ if (active?.id !== parent.id)
46
+ return { status: 409, body: { error: 'stale_decision', currentPrDeliveryId: active?.id ?? null } };
47
+ if (active.operation_token)
48
+ return { status: 409, body: { error: 'operation_in_progress' } };
49
+ const token = (0, ids_1.newId)();
50
+ if (!(0, rail_pr_store_1.claimPrDeliveryOperation)(deps.db, parent.id, active.decision, 'checkout', token))
51
+ return { status: 409, body: { error: 'operation_in_progress' } };
52
+ const perform = async () => {
53
+ const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, child.id);
54
+ const snap = (0, rail_pr_store_1.toPrDeliverySnapshot)(current);
55
+ const block = checkoutBlock(snap);
56
+ if (block)
57
+ return { status: 409, body: { error: 'checkout_not_deliverable', detail: block } };
58
+ const target = repositoryDeliveryCheckoutTarget(snap);
59
+ const cleanliness = await (0, project_git_1.inspectProjectCheckoutCleanliness)(child.repository_path);
60
+ if (!cleanliness.ok)
61
+ return { status: 409, body: { error: 'checkout_safety_unknown', detail: cleanliness.detail } };
62
+ const git = await (0, project_git_1.getProjectGitInfo)(child.repository_path);
63
+ if (!git.git)
64
+ return { status: 409, body: { error: 'checkout_unavailable', detail: 'repository is not a Git checkout' } };
65
+ const cleanupWarnings = await (0, rail_worktree_release_1.releaseRailWorktrees)({
66
+ db: deps.db, git: deps.git, repoDir: child.repository_path, worktreeIds: snap.worktreeIds,
67
+ expectedHeadByBranch: (0, rail_worktree_release_1.durableBranchHeads)(snap.branches), overlayEvidenceByBranch: (0, rail_worktree_release_1.durableOverlayCleanupEvidence)(snap.branches),
68
+ settlementIgnoredByBranch: (0, rail_worktree_release_1.durableSettlementIgnoredPaths)(snap.branches),
69
+ onSafetyArchive: (archive) => {
70
+ if (!(0, rail_pr_store_1.appendPrDeliverySafetyArchive)(deps.db, child.id, archive))
71
+ throw new Error(`delivery ${child.id} disappeared while recording safety archive`);
72
+ },
73
+ });
74
+ (0, rail_pr_store_1.transitionDecision)(deps.db, child.id, current.decision, current.decision, {
75
+ cleanupWarnings,
76
+ ...(cleanupWarnings.length ? { statusCode: 'cleanup_incomplete' } : snap.statusCode === 'cleanup_incomplete' ? { statusCode: null } : {}),
77
+ });
78
+ const stillActive = (0, rail_pr_store_1.getActivePrDeliveryByRail)(deps.db, parent.rail_index);
79
+ const outcome = stillActive?.id === parent.id
80
+ ? await (0, project_git_1.checkoutProjectReviewBranch)(child.repository_path, target.branch, target.sha)
81
+ : { ok: false, error: 'The delivery generation changed before checkout.' };
82
+ if (!outcome.ok)
83
+ return { status: 409, body: { error: 'checkout_failed', detail: [outcome.error, ...cleanupWarnings].join('\n') } };
84
+ return { status: 200, body: { ok: true, repositoryId: child.repository_id, branch: target.branch,
85
+ git: await (0, project_git_1.getProjectGitInfo)(child.repository_path), cleanupWarnings } };
86
+ };
87
+ let result;
88
+ try {
89
+ result = await perform();
90
+ }
91
+ finally {
92
+ (0, rail_pr_store_1.releasePrDeliveryOperation)(deps.db, parent.id, token);
93
+ }
94
+ const updated = (0, multi_repo_execution_1.refreshRepositoryDeliveryGroup)(deps.db, parent.id);
95
+ const parentSnap = (0, rail_pr_store_1.toPrDeliverySnapshot)(updated);
96
+ deps.broadcast((0, rail_pr_store_1.toRailPrStateMessage)(deps.project.id, parentSnap));
97
+ if (updated.origin_conversation_id)
98
+ (deps.agentChat ?? agent_chat_registry_1.getAgentChatManager)()?.updatePrDecisionCard(updated.origin_conversation_id, (0, rail_pr_store_1.toPrDecisionCardEnvelope)(deps.project.id, parentSnap));
99
+ return { status: result.status, body: { ...result.body, snapshot: parentSnap } };
100
+ });
101
+ }
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executeRepositoryGroupDecision = executeRepositoryGroupDecision;
4
+ const ids_1 = require("./ids");
5
+ const repo_lock_1 = require("./repo-lock");
6
+ const workspace_resolution_1 = require("./workspace-resolution");
7
+ const agent_chat_registry_1 = require("./agent-chat-registry");
8
+ const rail_pr_decision_1 = require("./rail-pr-decision");
9
+ const rail_pr_store_1 = require("./rail-pr-store");
10
+ const rail_pr_ticket_effects_1 = require("./rail-pr-ticket-effects");
11
+ const multi_repo_execution_1 = require("./multi-repo-execution");
12
+ const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
13
+ function completed(row) {
14
+ return row.decision === 'merged' || row.decision === 'completed';
15
+ }
16
+ /** A parent operation has one lease; each child keeps its own existing safe Git
17
+ * decision and immutable SHA. A retry never reintegrates accepted repositories. */
18
+ async function executeRepositoryGroupDecision(deps, input) {
19
+ const parent = (0, rail_pr_store_1.getPrDelivery)(deps.db, input.prDeliveryId);
20
+ if (!parent || !(0, multi_repo_execution_store_1.readExecutionManifest)(parent.execution_manifest))
21
+ return { status: 404, body: { error: 'Unknown repository delivery group' } };
22
+ if (parent.decision !== input.expectedDecision)
23
+ return { status: 409, body: { error: 'stale_decision', current: parent.decision } };
24
+ if (['building', 'merged', 'completed', 'discarded', 'superseded'].includes(parent.decision)) {
25
+ return { status: 409, body: { error: 'stale_decision', current: parent.decision, reason: 'illegal_action' } };
26
+ }
27
+ const children = (0, multi_repo_execution_1.listRepositoryDeliveries)(deps.db, parent.id);
28
+ const selected = input.repositoryId ? children.filter((row) => row.repository_id === input.repositoryId) : children;
29
+ if (selected.length === 0)
30
+ return { status: 404, body: { error: 'Unknown repository in this delivery' } };
31
+ if ((input.action === 'discard' || input.action === 'dismiss') && children.some(completed)) {
32
+ return { status: 409, body: { error: 'partial_delivery_already_integrated', detail: 'Complete the remaining repository deliveries; already accepted commits cannot be discarded.' } };
33
+ }
34
+ const steps = selected.flatMap((row) => {
35
+ if (completed(row) || row.decision === 'discarded' || row.decision === 'superseded')
36
+ return [];
37
+ const action = row.decision === 'no_changes' &&
38
+ ['merge-local', 'poll-merge', 'acknowledge-no-changes'].includes(input.action)
39
+ ? 'acknowledge-no-changes' : input.action;
40
+ return (0, rail_pr_decision_1.actionAllowed)(action, row) ? [{ row, action }] : [];
41
+ });
42
+ if (steps.length === 0)
43
+ return { status: 409, body: { error: 'stale_decision', current: parent.decision, reason: 'illegal_action' } };
44
+ const token = (0, ids_1.newId)();
45
+ if (!(0, rail_pr_store_1.claimPrDeliveryOperation)(deps.db, parent.id, parent.decision, input.action, token)) {
46
+ return { status: 409, body: { error: 'operation_in_progress', current: (0, rail_pr_store_1.getPrDelivery)(deps.db, parent.id)?.decision } };
47
+ }
48
+ const outcomes = [];
49
+ try {
50
+ deps.assertAdmission?.();
51
+ const ticketFile = deps.ticketFile ?? (0, workspace_resolution_1.resolveProjectExecution)(deps.project).ticketsPath;
52
+ for (const { row, action } of steps) {
53
+ const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, row.id);
54
+ if (!current || current.parent_delivery_id !== parent.id || current.repository_path !== row.repository_path) {
55
+ outcomes.push({ repositoryId: row.repository_id, status: 409, body: { error: 'repository_delivery_changed' } });
56
+ continue;
57
+ }
58
+ const childDeps = {
59
+ ...deps, project: { ...deps.project, path: row.repository_path }, ticketFile,
60
+ repositoryChildOf: parent.id, repositoryIntegrationBranch: (0, multi_repo_execution_store_1.readExecutionManifest)(row.execution_manifest)?.repositories.find((repo) => repo.repositoryId === row.repository_id)?.integrationBranch, jiraSyncManager: undefined, broadcast: () => { }, agentChat: () => null,
61
+ };
62
+ try {
63
+ const perform = () => (0, rail_pr_decision_1.executePrDecision)(childDeps, { prDeliveryId: row.id, action, expectedDecision: current.decision });
64
+ // merge-local already locks around its checkout resolution and merge.
65
+ const result = action === 'merge-local' ? await perform() : await (0, repo_lock_1.withRepoLock)(row.repository_path, perform);
66
+ outcomes.push({ repositoryId: row.repository_id, ...result });
67
+ }
68
+ catch (error) {
69
+ outcomes.push({ repositoryId: row.repository_id, status: 500, body: { error: 'repository_delivery_failed', detail: error instanceof Error ? error.message : String(error) } });
70
+ }
71
+ (0, multi_repo_execution_1.refreshRepositoryDeliveryGroup)(deps.db, parent.id);
72
+ }
73
+ const aggregate = (0, multi_repo_execution_1.refreshRepositoryDeliveryGroup)(deps.db, parent.id);
74
+ if (aggregate.decision === 'merged' || aggregate.decision === 'completed' || aggregate.decision === 'discarded') {
75
+ const effect = (0, rail_pr_ticket_effects_1.applyRailPrTicketEffect)(deps, aggregate.id);
76
+ if (!effect.ok)
77
+ (0, rail_pr_store_1.transitionClaimedDecision)(deps.db, aggregate.id, aggregate.decision, aggregate.decision, token, {
78
+ statusCode: 'cleanup_incomplete', cleanupWarnings: [`Shared ticket update pending: ${effect.error ?? 'unknown error'}`],
79
+ });
80
+ }
81
+ const failure = outcomes.find((outcome) => outcome.status >= 400);
82
+ return {
83
+ status: failure?.status ?? 200,
84
+ body: { ok: !failure, ...(failure ? { error: 'repository_delivery_incomplete', detail: outcomes.filter((outcome) => outcome.status >= 400).map((outcome) => `${outcome.repositoryId}: ${outcome.body.detail ?? outcome.body.error ?? 'delivery failed'}`).join('; ') } : {}), repositories: outcomes,
85
+ decision: aggregate.decision, prState: aggregate.pr_state, statusCode: aggregate.status_code, statusDetail: aggregate.status_detail,
86
+ snapshot: (0, rail_pr_store_1.toPrDeliverySnapshot)((0, rail_pr_store_1.getPrDelivery)(deps.db, parent.id)), delivery: (0, rail_pr_store_1.toPrDeliverySnapshot)((0, rail_pr_store_1.getPrDelivery)(deps.db, parent.id)) },
87
+ };
88
+ }
89
+ finally {
90
+ (0, rail_pr_store_1.releasePrDeliveryOperation)(deps.db, parent.id, token);
91
+ const current = (0, rail_pr_store_1.getPrDelivery)(deps.db, parent.id);
92
+ if (current) {
93
+ const snap = (0, rail_pr_store_1.toPrDeliverySnapshot)(current);
94
+ deps.broadcast((0, rail_pr_store_1.toRailPrStateMessage)(deps.project.id, snap));
95
+ if (current.origin_conversation_id) {
96
+ (deps.agentChat ?? agent_chat_registry_1.getAgentChatManager)()?.updatePrDecisionCard(current.origin_conversation_id, (0, rail_pr_store_1.toPrDecisionCardEnvelope)(deps.project.id, snap));
97
+ }
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.migrateMultiRepoExecution = migrateMultiRepoExecution;
4
+ exports.readExecutionManifest = readExecutionManifest;
5
+ exports.getRepositoryExecutionReferences = getRepositoryExecutionReferences;
6
+ exports.executionManifestPrompt = executionManifestPrompt;
7
+ /** Project DB migration 59. Desktop membership is migrated separately. */
8
+ function migrateMultiRepoExecution(db) {
9
+ db.exec(`
10
+ ALTER TABLE rail_pr_deliveries ADD COLUMN parent_delivery_id TEXT REFERENCES rail_pr_deliveries(id);
11
+ ALTER TABLE rail_pr_deliveries ADD COLUMN repository_id TEXT;
12
+ ALTER TABLE rail_pr_deliveries ADD COLUMN repository_path TEXT;
13
+ ALTER TABLE rail_pr_deliveries ADD COLUMN execution_manifest TEXT;
14
+ ALTER TABLE rail_pr_deliveries ADD COLUMN repository_deliveries TEXT NOT NULL DEFAULT '[]';
15
+ ALTER TABLE rail_worktrees ADD COLUMN repository_id TEXT;
16
+ ALTER TABLE rail_worktrees ADD COLUMN repository_path TEXT;
17
+ ALTER TABLE loop_runs ADD COLUMN execution_manifest TEXT;
18
+ CREATE INDEX idx_rail_pr_delivery_parent ON rail_pr_deliveries(parent_delivery_id);
19
+ CREATE INDEX idx_rail_worktree_repository ON rail_worktrees(repository_id);
20
+ DROP INDEX IF EXISTS idx_rail_pr_deliveries_one_active_per_rail;
21
+ CREATE UNIQUE INDEX idx_rail_pr_deliveries_one_active_per_rail
22
+ ON rail_pr_deliveries(rail_index)
23
+ WHERE parent_delivery_id IS NULL AND decision NOT IN ('completed','merged','discarded','superseded');
24
+ `);
25
+ }
26
+ function readExecutionManifest(raw) {
27
+ if (!raw)
28
+ return null;
29
+ try {
30
+ const value = JSON.parse(raw);
31
+ if (value.version !== 1 || !Array.isArray(value.repositories) || !Array.isArray(value.selectedRepositoryIds))
32
+ return null;
33
+ return value;
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ function getRepositoryExecutionReferences(db, repositoryId) {
40
+ const deliveries = db.prepare(`SELECT id, parent_delivery_id, run_ids FROM rail_pr_deliveries
41
+ WHERE repository_id = ? AND decision NOT IN ('completed','merged','discarded','superseded')`).all(repositoryId);
42
+ const worktrees = db.prepare(`SELECT run_id FROM rail_worktrees WHERE repository_id = ?
43
+ AND merge_state NOT IN ('merged','failed','released')`).all(repositoryId);
44
+ const runs = db.prepare("SELECT id, execution_manifest FROM loop_runs WHERE status <> 'completed' AND execution_manifest IS NOT NULL").all();
45
+ const manifestRunIds = runs.filter((row) => readExecutionManifest(row.execution_manifest)?.selectedRepositoryIds.includes(repositoryId)).map((row) => row.id);
46
+ return {
47
+ runIds: [...new Set([...worktrees.flatMap((row) => row.run_id ? [row.run_id] : []), ...manifestRunIds])],
48
+ deliveryIds: [...new Set(deliveries.map((row) => row.parent_delivery_id ?? row.id))],
49
+ };
50
+ }
51
+ function executionManifestPrompt(manifest) {
52
+ if (!manifest)
53
+ return '';
54
+ return [
55
+ 'Specrails repository execution scope (immutable for this run):',
56
+ JSON.stringify({
57
+ projectId: manifest.projectId,
58
+ artifactRepositoryId: manifest.artifactRepositoryId,
59
+ repositories: manifest.repositories.map((repo) => ({ repositoryId: repo.repositoryId, name: repo.name, path: repo.worktreePath, branch: repo.branch, base: repo.baseSha })),
60
+ }),
61
+ 'This is ONE shared spec and ONE coordinated implementation. Complete and verify every required repository and their shared contracts before reporting success.',
62
+ 'Edit only the listed worktree paths. The original checkouts are not implementation targets. Keep the shared backlog in the project artifact workspace; keep OpenSpec changes in its designated artifact repository.',
63
+ 'Specrails owns Git delivery: do not merge, push, delete branches or open pull requests yourself.',
64
+ ].join('\n');
65
+ }