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
@@ -11,28 +11,13 @@ const path_1 = __importDefault(require("path"));
11
11
  const framework_manager_1 = require("./framework-manager");
12
12
  const semver_lite_1 = require("./semver-lite");
13
13
  const win_spawn_1 = require("./util/win-spawn");
14
- /**
15
- * CoreUpdateManager — the voluntary, app-global specrails-core update channel.
16
- *
17
- * The bundled framework (see FrameworkManager) is pinned to the core version
18
- * shipped inside the app build. This manager lets a user UPDATE specrails-core
19
- * independently of the desktop app: it queries npm for the latest published
20
- * version, and on an explicit request npm-installs that newer core into a temp
21
- * staging dir, materializes its framework into `~/.specrails/framework/<version>`
22
- * and atomically swaps `framework/current` to it — every project workspace that
23
- * symlinks `current/...` jumps to the new core with NO restart and NO per-project
24
- * `npx`. The previous `framework/<version>` dir is left intact (non-destructive).
25
- *
26
- * Detection is deliberately simple (KISS): npm `latest` strictly greater than the
27
- * installed `framework/current` version ⇒ an update is available. There is no
28
- * compatibility-range gate; the floor is the app-bundled core (the update only
29
- * ever moves the pointer UP — FrameworkManager.versionCheck has an anti-downgrade
30
- * guard so the next startup never reverts a manual update).
31
- *
32
- * The whole feature is gated on a bundled core being present (desktop mode); in
33
- * dev / non-desktop mode `isAvailable()` is false and projects use the legacy npx
34
- * path, so a core "update" would have no effect and the UI shows "unavailable".
35
- */
14
+ const bundled_core_1 = require("./bundled-core");
15
+ const core_runtime_1 = require("./core-runtime");
16
+ const artifact_registry_1 = require("./artifact-registry");
17
+ /** Voluntary Core updates retain the complete npm package and dependencies,
18
+ * publish its framework, then await every project refresh. Partial refreshes
19
+ * remain explicitly pending across restart and can retry without the network.
20
+ * Older packages/frameworks remain available for diagnosis and recovery. */
36
21
  exports.CORE_PACKAGE = 'specrails-core';
37
22
  const REGISTRY_URL = `https://registry.npmjs.org/${exports.CORE_PACKAGE}/latest`;
38
23
  const CHECK_TIMEOUT_MS = 10_000;
@@ -47,6 +32,9 @@ class CoreUpdateManager {
47
32
  latestVersion = null;
48
33
  lastCheckedAt = null;
49
34
  updating = false;
35
+ pendingVersion = null;
36
+ migrationError = null;
37
+ reseed;
50
38
  constructor(opts = {}) {
51
39
  this.home = opts.home;
52
40
  this.broadcast = opts.broadcast;
@@ -56,31 +44,49 @@ class CoreUpdateManager {
56
44
  this.makeFrameworkFn =
57
45
  opts.makeFramework ??
58
46
  ((coreRoot) => new framework_manager_1.FrameworkManager({ home: this.home, broadcast: this.broadcast, coreRoot }));
59
- }
60
- /** A read-only FrameworkManager pointed at the bundled core. */
61
- bundledFramework() {
62
- return new framework_manager_1.FrameworkManager({ home: this.home });
47
+ this.reseed = opts.reseed ?? (async () => []);
48
+ try {
49
+ const cached = JSON.parse(fs_1.default.readFileSync(path_1.default.join((0, core_runtime_1.managedCoreRoot)(this.home), 'update-status.json'), 'utf8'));
50
+ if (typeof cached.latestVersion === 'string' && (0, semver_lite_1.isValidVersion)(cached.latestVersion) && Number.isFinite(cached.lastCheckedAt)) {
51
+ this.latestVersion = cached.latestVersion;
52
+ this.lastCheckedAt = cached.lastCheckedAt;
53
+ }
54
+ if (typeof cached.pendingVersion === 'string' && (0, semver_lite_1.isValidVersion)(cached.pendingVersion)) {
55
+ this.pendingVersion = cached.pendingVersion;
56
+ this.migrationError = typeof cached.migrationError === 'string' ? cached.migrationError : 'Core project refresh was interrupted. Retry to finish updating project files.';
57
+ }
58
+ }
59
+ catch { /* first launch / cache unavailable: installed state is independent */ }
63
60
  }
64
61
  /** True when the bundled-framework system is active (a bundled core is present). */
65
62
  isAvailable() {
66
- return this.bundledFramework().isAvailable();
63
+ return (0, core_runtime_1.getCoreRuntimeStatus)(this.home).runtime !== null || (0, framework_manager_1.readCurrentFrameworkVersion)(this.home) !== null;
67
64
  }
68
65
  /** Current status — no network (uses the cached latest from the last check). */
69
66
  getStatus() {
70
- const fm = this.bundledFramework();
71
- const bundledVersion = fm.bundledVersion();
72
- const currentVersion = (0, framework_manager_1.readCurrentFrameworkVersion)(this.home) ?? bundledVersion;
73
- const updateAvailable = this.latestVersion != null &&
74
- currentVersion != null &&
75
- (0, semver_lite_1.isNewer)(this.latestVersion, currentVersion);
67
+ const selected = (0, core_runtime_1.getCoreRuntimeStatus)(this.home);
68
+ const bundledVersion = (0, bundled_core_1.getBundledCoreVersion)();
69
+ const frameworkVersion = (0, framework_manager_1.readCurrentFrameworkVersion)(this.home);
70
+ const currentVersion = frameworkVersion ?? selected.runtime?.version ?? null;
71
+ const updateAvailable = this.pendingVersion !== null ||
72
+ this.latestVersion != null &&
73
+ currentVersion != null &&
74
+ ((0, semver_lite_1.isNewer)(this.latestVersion, currentVersion) || selected.error !== null && this.latestVersion === currentVersion) &&
75
+ Number(this.latestVersion.split('.')[0]) <= 5;
76
76
  return {
77
- available: fm.isAvailable(),
77
+ available: this.isAvailable(),
78
78
  currentVersion,
79
79
  bundledVersion,
80
80
  latestVersion: this.latestVersion,
81
81
  updateAvailable,
82
82
  updating: this.updating,
83
83
  lastCheckedAt: this.lastCheckedAt,
84
+ runtimeVersion: selected.runtime?.version ?? null,
85
+ runtimeSource: selected.runtime?.source ?? null,
86
+ frameworkVersion,
87
+ runtimeError: selected.error,
88
+ pendingVersion: this.pendingVersion,
89
+ migrationError: this.migrationError,
84
90
  };
85
91
  }
86
92
  /** Hit npm for the latest published version, cache it, return refreshed status. */
@@ -91,6 +97,7 @@ class CoreUpdateManager {
91
97
  }
92
98
  this.latestVersion = latest.trim().replace(/^[v=]+/, '');
93
99
  this.lastCheckedAt = Date.now();
100
+ this.persistStatus();
94
101
  return this.getStatus();
95
102
  }
96
103
  /**
@@ -106,12 +113,15 @@ class CoreUpdateManager {
106
113
  if (!this.isAvailable()) {
107
114
  return { ok: false, error: 'Core updates are unavailable in this build.' };
108
115
  }
109
- const requested = (targetVersion ?? this.latestVersion ?? '').trim().replace(/^[v=]+/, '');
116
+ const requested = (targetVersion ?? this.pendingVersion ?? this.latestVersion ?? '').trim().replace(/^[v=]+/, '');
110
117
  if (!requested || !(0, semver_lite_1.isValidVersion)(requested)) {
111
118
  return { ok: false, error: 'No valid target version to update to. Check for updates first.' };
112
119
  }
120
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(requested) || ![4, 5].includes(Number(requested.split('.')[0]))) {
121
+ return { ok: false, error: 'This Desktop supports Core 4 and 5. Update Desktop before installing another major version.' };
122
+ }
113
123
  const current = (0, framework_manager_1.readCurrentFrameworkVersion)(this.home);
114
- if (current && !(0, semver_lite_1.isNewer)(requested, current)) {
124
+ if (current && !(0, semver_lite_1.isNewer)(requested, current) && !(requested === current && ((0, core_runtime_1.getCoreRuntimeStatus)(this.home).error || this.pendingVersion === requested))) {
115
125
  return { ok: false, error: `Already on ${current}; ${requested} is not newer.` };
116
126
  }
117
127
  this.updating = true;
@@ -122,39 +132,112 @@ class CoreUpdateManager {
122
132
  // loader) against process.argv[1] (the literal spawn arg) — a symlinked
123
133
  // staging path makes them differ, so main() never runs and every
124
134
  // install-framework/swap-current child exits 0 as a silent no-op.
125
- const tmp = fs_1.default.mkdtempSync(path_1.default.join(realTmpDir(), 'core-update-'));
135
+ let tmp;
126
136
  try {
127
- this.npmInstallFn(`${exports.CORE_PACKAGE}@${requested}`, tmp);
128
- const coreRoot = path_1.default.join(tmp, 'node_modules', exports.CORE_PACKAGE);
137
+ tmp = fs_1.default.mkdtempSync(path_1.default.join(realTmpDir(), 'core-update-'));
138
+ const retained = path_1.default.join((0, core_runtime_1.managedCoreRoot)(this.home), requested, 'node_modules', exports.CORE_PACKAGE);
139
+ if ((0, core_runtime_1.readCoreRuntime)(retained, 'managed')?.version === requested) {
140
+ // A project migration retry can finish fully offline.
141
+ // Force JS traversal: Node 22's native directory copy mishandles Unicode
142
+ // Windows profiles (nodejs/node#61878). Both copy targets are fresh
143
+ // staging directories, so native file replacement is avoided too.
144
+ fs_1.default.cpSync(path_1.default.dirname(path_1.default.dirname(retained)), tmp, { recursive: true, filter: () => true, mode: fs_1.default.constants.COPYFILE_FICLONE });
145
+ }
146
+ else {
147
+ this.npmInstallFn(`${exports.CORE_PACKAGE}@${requested}`, tmp);
148
+ }
149
+ let coreRoot = path_1.default.join(tmp, 'node_modules', exports.CORE_PACKAGE);
129
150
  const cli = path_1.default.join(coreRoot, 'dist', 'installer', 'cli.js');
130
151
  if (!fs_1.default.existsSync(cli)) {
131
152
  throw new Error('downloaded core is missing dist/installer/cli.js');
132
153
  }
154
+ const staged = (0, core_runtime_1.readCoreRuntime)(coreRoot, 'managed');
155
+ if (!staged || staged.version !== requested)
156
+ throw new Error(`Downloaded Core version does not match requested ${requested}.`);
157
+ const installed = staged.version;
158
+ // Retain the complete npm installation, including sibling dependencies.
159
+ // Publication is a same-filesystem rename and occurs before current moves.
160
+ const store = (0, core_runtime_1.managedCoreRoot)(this.home);
161
+ fs_1.default.mkdirSync(store, { recursive: true, mode: 0o700 });
162
+ const destination = path_1.default.join(store, installed);
163
+ if ((0, core_runtime_1.readCoreRuntime)(path_1.default.join(destination, 'node_modules', exports.CORE_PACKAGE), 'managed')?.version !== installed) {
164
+ const staging = fs_1.default.mkdtempSync(path_1.default.join(store, '.stage-'));
165
+ let retainedBackup = null;
166
+ try {
167
+ fs_1.default.cpSync(tmp, staging, { recursive: true, filter: () => true, mode: fs_1.default.constants.COPYFILE_FICLONE });
168
+ try {
169
+ fs_1.default.lstatSync(destination);
170
+ const backup = fs_1.default.mkdtempSync(path_1.default.join(store, `.previous-${installed}-`));
171
+ retainedBackup = path_1.default.join(backup, 'installation');
172
+ fs_1.default.renameSync(destination, retainedBackup);
173
+ }
174
+ catch (error) {
175
+ if (error.code !== 'ENOENT')
176
+ throw error;
177
+ }
178
+ fs_1.default.renameSync(staging, destination);
179
+ }
180
+ catch (error) {
181
+ // Restore only when publication never created a destination. The
182
+ // retained previous installation is never deleted, even on success.
183
+ if (retainedBackup && !fs_1.default.existsSync(destination))
184
+ fs_1.default.renameSync(retainedBackup, destination);
185
+ throw error;
186
+ }
187
+ finally {
188
+ fs_1.default.rmSync(staging, { recursive: true, force: true });
189
+ }
190
+ }
191
+ coreRoot = path_1.default.join(destination, 'node_modules', exports.CORE_PACKAGE);
192
+ if ((0, core_runtime_1.readCoreRuntime)(coreRoot, 'managed')?.version !== installed)
193
+ throw new Error('The retained Core package could not be verified. Retry the update.');
133
194
  const fm = this.makeFrameworkFn(coreRoot);
134
- // The version actually installed (npm may resolve a dist-tag/range).
135
- const installed = fm.bundledVersion() ?? requested;
136
195
  this.emit('materializing', { version: installed });
137
196
  const providers = uniqueProviders(this.providersFn());
138
- const mat = fm.materialize(installed, providers);
139
- if (!mat.ran) {
140
- throw new Error('framework materialize did not run (no usable core)');
141
- }
142
- if (mat.errors.length > 0) {
143
- throw new Error(`framework materialize failed: ${mat.errors.map((e) => `${e.provider}: ${e.message}`).join('; ')}`);
144
- }
145
- // A materialize that reports no errors but ALSO no materialized providers
146
- // wrote nothing — swapping `current` at a nonexistent version dir would
147
- // fail downstream with a misleading "swap-current failed". Surface the
148
- // real condition instead (mirrors FrameworkManager.versionCheck's guard).
149
- if (mat.providers.length === 0) {
150
- throw new Error(`framework materialize completed without materializing any provider (requested: ${providers.join(', ') || 'none'})`);
197
+ (0, artifact_registry_1.withFileLock)(this.home, () => {
198
+ const now = (0, framework_manager_1.readCurrentFrameworkVersion)(this.home);
199
+ if (now && (0, semver_lite_1.isNewer)(now, installed))
200
+ throw new Error(`Core advanced to ${now} while downloading; refusing to downgrade.`);
201
+ const mat = fm.materialize(installed, providers);
202
+ if (!mat.ran) {
203
+ throw new Error('framework materialize did not run (no usable core)');
204
+ }
205
+ if (mat.errors.length > 0) {
206
+ throw new Error(`framework materialize failed: ${mat.errors.map((e) => `${e.provider}: ${e.message}`).join('; ')}`);
207
+ }
208
+ // A materialize that reports no errors but ALSO no materialized providers
209
+ // wrote nothing — swapping `current` at a nonexistent version dir would
210
+ // fail downstream with a misleading "swap-current failed". Surface the
211
+ // real condition instead (mirrors FrameworkManager.versionCheck's guard).
212
+ if (mat.providers.length === 0) {
213
+ throw new Error(`framework materialize completed without materializing any provider (requested: ${providers.join(', ') || 'none'})`);
214
+ }
215
+ // Recovery state is mandatory before publication. If this write fails,
216
+ // current stays on the previous usable version and no project is reseeded.
217
+ this.pendingVersion = installed;
218
+ this.migrationError = null;
219
+ this.persistStatus(true);
220
+ const swapped = fm.swapCurrentDetailed(installed);
221
+ if (!swapped.ok) {
222
+ throw new Error(`framework swap-current failed${swapped.detail ? `: ${swapped.detail}` : ''}`);
223
+ }
224
+ });
225
+ const projects = await this.reseed(installed);
226
+ const failed = projects.filter(project => project.error);
227
+ if (failed.length)
228
+ throw new Error(`Core ${installed} is installed, but project refresh failed: ${failed.map(project => `${project.projectId}: ${project.error}`).join('; ')}. Retry the update to finish refreshing project files.`);
229
+ this.pendingVersion = null;
230
+ this.migrationError = null;
231
+ // Installation is not a registry-latest check. Preserve its cached value
232
+ // and timestamp, including a known version newer than this target.
233
+ try {
234
+ this.persistStatus(true);
151
235
  }
152
- const swapped = fm.swapCurrentDetailed(installed);
153
- if (!swapped.ok) {
154
- throw new Error(`framework swap-current failed${swapped.detail ? `: ${swapped.detail}` : ''}`);
236
+ catch (error) {
237
+ this.pendingVersion = installed;
238
+ throw error;
155
239
  }
156
- this.latestVersion = installed;
157
- this.lastCheckedAt = Date.now();
240
+ this.updating = false;
158
241
  this.emit('done', { version: installed });
159
242
  // Reuse the existing app-level event so any listener that reacts to a
160
243
  // framework version bump (e.g. core-version banners) refreshes too.
@@ -163,19 +246,37 @@ class CoreUpdateManager {
163
246
  }
164
247
  catch (err) {
165
248
  const message = err instanceof Error ? err.message : String(err);
249
+ if (this.pendingVersion) {
250
+ this.migrationError = message;
251
+ this.persistStatus();
252
+ }
253
+ this.updating = false;
166
254
  this.emit('error', { message });
167
255
  return { ok: false, error: message };
168
256
  }
169
257
  finally {
170
258
  this.updating = false;
171
259
  try {
172
- fs_1.default.rmSync(tmp, { recursive: true, force: true });
260
+ if (tmp)
261
+ fs_1.default.rmSync(tmp, { recursive: true, force: true });
173
262
  }
174
263
  catch {
175
264
  /* best-effort temp cleanup */
176
265
  }
177
266
  }
178
267
  }
268
+ persistStatus(required = false) {
269
+ try {
270
+ const root = (0, core_runtime_1.managedCoreRoot)(this.home);
271
+ fs_1.default.mkdirSync(root, { recursive: true, mode: 0o700 });
272
+ (0, artifact_registry_1.atomicWrite)(path_1.default.join(root, 'update-status.json'), JSON.stringify({ latestVersion: this.latestVersion, lastCheckedAt: this.lastCheckedAt, pendingVersion: this.pendingVersion, migrationError: this.migrationError }));
273
+ }
274
+ catch (error) {
275
+ if (required)
276
+ throw new Error(`Could not persist Core update recovery state: ${error instanceof Error ? error.message : String(error)}`);
277
+ // A registry-cache failure does not erase the installed version.
278
+ }
279
+ }
179
280
  emit(phase, extra) {
180
281
  this.safeBroadcast({ type: 'core_update.progress', phase, ...extra });
181
282
  }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.coreUpdatePendingPath = void 0;
7
+ exports.assertWorkspaceCoreReady = assertWorkspaceCoreReady;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const core_runtime_1 = require("./core-runtime");
11
+ const coreUpdatePendingPath = (workspace) => path_1.default.join(workspace, '.specrails', 'core-update-pending.json');
12
+ exports.coreUpdatePendingPath = coreUpdatePendingPath;
13
+ /** Reading the project remains available; only implementation setup calls this. */
14
+ function assertWorkspaceCoreReady(workspace, home) {
15
+ let pending = fs_1.default.existsSync((0, exports.coreUpdatePendingPath)(workspace));
16
+ // If a workspace was read-only and its marker could not be created, the
17
+ // durable global migration still prevents a launch using its old seed.
18
+ try {
19
+ const target = JSON.parse(fs_1.default.readFileSync(path_1.default.join((0, core_runtime_1.managedCoreRoot)(home), 'update-status.json'), 'utf8')).pendingVersion;
20
+ if (typeof target === 'string' && target) {
21
+ const recorded = fs_1.default.readFileSync(path_1.default.join(workspace, '.specrails', 'specrails-version'), 'utf8').trim();
22
+ pending ||= recorded !== target;
23
+ }
24
+ }
25
+ catch { /* no pending global migration / uninstalled workspace */ }
26
+ if (pending) {
27
+ throw new Error('This project has an unfinished Core update. Retry the Core update in Settings before starting an implementation.');
28
+ }
29
+ }
package/server/dist/db.js CHANGED
@@ -60,6 +60,10 @@ const fs_1 = __importDefault(require("fs"));
60
60
  const path_1 = __importDefault(require("path"));
61
61
  const better_sqlite3_1 = __importDefault(require("better-sqlite3"));
62
62
  const secure_fs_1 = require("./util/secure-fs");
63
+ const sqlite_migrations_1 = require("./util/sqlite-migrations");
64
+ const multi_repo_execution_store_1 = require("./multi-repo-execution-store");
65
+ const project_repository_provenance_1 = require("./project-repository-provenance");
66
+ const file_story_1 = require("./file-story");
63
67
  /** Backfill only terminal Safe-PR rows whose ticket ownership is unambiguous.
64
68
  * Eligibility is intentionally deferred to the outbox drainer, which freezes
65
69
  * only candidates still parked at on_review in the external ticket JSON. */
@@ -1501,6 +1505,45 @@ const MIGRATIONS = [
1501
1505
  db.exec(`ALTER TABLE rail_pr_deliveries ADD COLUMN revision_of TEXT`);
1502
1506
  }
1503
1507
  },
1508
+ // Migration 58: premium-milestone-progress — server-durable milestone launch
1509
+ // chains. One row per sequential "Launch Milestone": the chunk plan, the
1510
+ // chunk in flight (rail / runs / delivery), the branch the next chunk stacks
1511
+ // on, and a CAS status. The partial unique index enforces ONE non-terminal
1512
+ // chain per milestone. Additive; nothing else references it.
1513
+ (db) => {
1514
+ db.exec(`
1515
+ CREATE TABLE IF NOT EXISTS milestone_launch_chains (
1516
+ id TEXT PRIMARY KEY,
1517
+ milestone_n INTEGER NOT NULL,
1518
+ milestone_id TEXT NOT NULL,
1519
+ mode TEXT NOT NULL,
1520
+ chunks TEXT NOT NULL DEFAULT '[]',
1521
+ next_chunk INTEGER NOT NULL DEFAULT 0,
1522
+ current_rail_index INTEGER,
1523
+ current_run_ids TEXT NOT NULL DEFAULT '[]',
1524
+ current_delivery_id TEXT,
1525
+ integration_branch TEXT,
1526
+ head_branch TEXT,
1527
+ status TEXT NOT NULL DEFAULT 'running',
1528
+ pause_reason TEXT,
1529
+ launched TEXT NOT NULL DEFAULT '[]',
1530
+ last_run_outcome TEXT,
1531
+ auto_advance INTEGER NOT NULL DEFAULT 1,
1532
+ retry_chunk INTEGER,
1533
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
1534
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
1535
+ );
1536
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_milestone_chains_active
1537
+ ON milestone_launch_chains(milestone_n) WHERE status IN ('running','waiting','paused','awaiting_approval');
1538
+ CREATE INDEX IF NOT EXISTS idx_milestone_chains_status ON milestone_launch_chains(status);
1539
+ `);
1540
+ },
1541
+ // Migration 59: coordinated repository execution and grouped delivery.
1542
+ multi_repo_execution_store_1.migrateMultiRepoExecution,
1543
+ // Migration 60: repository-scoped provenance and code identity.
1544
+ project_repository_provenance_1.migrateRepositoryProvenance,
1545
+ // Migration 61: version and evidence identity of recorded-change explanations.
1546
+ file_story_1.migrateFileStoryMetadata,
1504
1547
  ];
1505
1548
  function applyMigrations(db) {
1506
1549
  // Ensure the migrations table exists (migration 1 creates it, but we need
@@ -1511,26 +1554,7 @@ function applyMigrations(db) {
1511
1554
  applied_at TEXT NOT NULL DEFAULT (datetime('now'))
1512
1555
  )
1513
1556
  `);
1514
- const appliedVersions = new Set(db.prepare('SELECT version FROM schema_migrations').all()
1515
- .map((r) => r.version));
1516
- for (let i = 0; i < MIGRATIONS.length; i++) {
1517
- const version = i + 1;
1518
- if (!appliedVersions.has(version)) {
1519
- // M8: run each migration body and its version INSERT atomically. SQLite DDL
1520
- // is transactional, so a crash/failure mid-migration now rolls back the
1521
- // whole body instead of leaving a half-applied schema with the version
1522
- // unrecorded — which under the old code re-ran a bare `ALTER TABLE ADD
1523
- // COLUMN` on next startup and bricked it forever with 'duplicate column
1524
- // name'. With this, a failed migration leaves nothing applied and re-runs
1525
- // cleanly. (Pre-existing half-applied DBs are contained by the per-project
1526
- // load isolation in project-registry.ts — one bad DB no longer kills the app.)
1527
- const tx = db.transaction(() => {
1528
- MIGRATIONS[i](db);
1529
- db.prepare('INSERT OR IGNORE INTO schema_migrations (version) VALUES (?)').run(version);
1530
- });
1531
- tx();
1532
- }
1533
- }
1557
+ (0, sqlite_migrations_1.applyNumberedMigrations)(db, MIGRATIONS);
1534
1558
  }
1535
1559
  // ─── Public API ──────────────────────────────────────────────────────────────
1536
1560
  function initDb(dbPath) {
@@ -1540,21 +1564,27 @@ function initDb(dbPath) {
1540
1564
  (0, secure_fs_1.secureDir)(dir); // H-13: owner-only data dir
1541
1565
  }
1542
1566
  const db = new better_sqlite3_1.default(dbPath);
1543
- db.pragma('journal_mode = WAL');
1544
- db.pragma('foreign_keys = ON');
1545
- // Under load, QueueManager / ChatManager / FileSummaryManager write the same
1546
- // per-project DB concurrently with /analytics reads. Wait up to 5s on a lock
1547
- // instead of throwing SQLITE_BUSY, and cap the WAL so a long checkpoint can't
1548
- // grow it without bound.
1549
- db.pragma('busy_timeout = 5000');
1550
- db.pragma('journal_size_limit = 10000000'); // ~10 MB
1551
- applyMigrations(db);
1552
- // H-13: restrict the db + its WAL sidecars to 0600 (jobs.sqlite holds chat
1553
- // transcripts and verbatim terminal command history). After migrations the
1554
- // WAL/SHM files exist, so this covers them too.
1555
- (0, secure_fs_1.secureDbFile)(dbPath);
1556
- // Orphan sweep: cancel any in-flight proposals from a previous server session
1557
- db.prepare("UPDATE proposals SET status = 'cancelled', updated_at = ? WHERE status IN ('exploring', 'refining')").run(new Date().toISOString());
1567
+ try {
1568
+ db.pragma('journal_mode = WAL');
1569
+ db.pragma('foreign_keys = ON');
1570
+ // Under load, QueueManager / ChatManager / FileSummaryManager write the same
1571
+ // per-project DB concurrently with /analytics reads. Wait up to 5s on a lock
1572
+ // instead of throwing SQLITE_BUSY, and cap the WAL so a long checkpoint can't
1573
+ // grow it without bound.
1574
+ db.pragma('busy_timeout = 5000');
1575
+ db.pragma('journal_size_limit = 10000000'); // ~10 MB
1576
+ applyMigrations(db);
1577
+ // H-13: restrict the db + its WAL sidecars to 0600 (jobs.sqlite holds chat
1578
+ // transcripts and verbatim terminal command history). After migrations the
1579
+ // WAL/SHM files exist, so this covers them too.
1580
+ (0, secure_fs_1.secureDbFile)(dbPath);
1581
+ // Orphan sweep: cancel any in-flight proposals from a previous server session
1582
+ db.prepare("UPDATE proposals SET status = 'cancelled', updated_at = ? WHERE status IN ('exploring', 'refining')").run(new Date().toISOString());
1583
+ }
1584
+ catch (err) {
1585
+ db.close();
1586
+ throw err;
1587
+ }
1558
1588
  return db;
1559
1589
  }
1560
1590
  function createJob(db, job) {
@@ -2008,6 +2038,7 @@ exports.DEFAULT_FREESTYLE_PRE_PROMPT = [
2008
2038
  'Work independently until the feature is done: write the code, the tests, update docs as needed, and make sure everything builds and the test suite passes.',
2009
2039
  'Do NOT follow any structured architect/developer/reviewer pipeline — use your own judgement and the repo conventions.',
2010
2040
  'Never ask for confirmation; there is no human to answer. Choose the recommended option and proceed.',
2041
+ 'Run every command in the FOREGROUND and wait for it to finish — never background a command (no run_in_background, no trailing `&`): your reply ends the step and anything still running is killed with its output lost.',
2011
2042
  ].join('\n');
2012
2043
  exports.WORKTREE_ENV_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
2013
2044
  const WORKTREE_ENV_MAX_NAMES = 64;