specrails-desktop 2.29.4 → 2.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/client/dist/assets/{ActivityFeedPage-CDMzpGXd.js → ActivityFeedPage-AQgpr9qG.js} +1 -1
  2. package/client/dist/assets/{AgentBrowserCapture-BVTacapZ.js → AgentBrowserCapture-I7a0usG7.js} +1 -1
  3. package/client/dist/assets/AgentModeCodePane-BqOVg15n.js +2 -0
  4. package/client/dist/assets/{AgentModeJobsPane-DAjN8Au5.js → AgentModeJobsPane-4SG6FF27.js} +1 -1
  5. package/client/dist/assets/{AgentsPage-CebykajE.js → AgentsPage-C3FgYlEi.js} +1 -1
  6. package/client/dist/assets/{AnalyticsPage-BiPrVvgv.js → AnalyticsPage-BcMNByW5.js} +1 -1
  7. package/client/dist/assets/{CodePage-Dw9eQFRS.js → CodePage-CgdDkXp-.js} +1 -1
  8. package/client/dist/assets/{DesktopAnalyticsPage-cCG_JAEi.js → DesktopAnalyticsPage-DPMcS88n.js} +1 -1
  9. package/client/dist/assets/{DocsDialog-C1kZ_mRP.js → DocsDialog-BC7574Zi.js} +1 -1
  10. package/client/dist/assets/{DocsPage-3GivxWJm.js → DocsPage-Dd9yilUg.js} +1 -1
  11. package/client/dist/assets/{InteractiveJobComposer-KuZXGdr8.js → InteractiveJobComposer-CqC_0VQL.js} +1 -1
  12. package/client/dist/assets/{JobDetailModal-DWpRALkB.js → JobDetailModal-BFNXOhk1.js} +1 -1
  13. package/client/dist/assets/{JobDetailPage-mrXDKafj.js → JobDetailPage-DM-6zYpA.js} +1 -1
  14. package/client/dist/assets/{JobsPage-CUYXjcoc.js → JobsPage-1dkjPk1k.js} +1 -1
  15. package/client/dist/assets/{LoopBuilderPage-CwjBD5Gm.js → LoopBuilderPage-BPeqz6cG.js} +1 -1
  16. package/client/dist/assets/{LoopPreviewModal-CO9ton0-.js → LoopPreviewModal-EdkyYaGN.js} +1 -1
  17. package/client/dist/assets/{LoopsPage-CLL5UuvN.js → LoopsPage-nZfgiaK8.js} +1 -1
  18. package/client/dist/assets/{MinimizedChatsContext-DJAkRYps.js → MinimizedChatsContext-Cs34FPTz.js} +1 -1
  19. package/client/dist/assets/{PluginsPage-DZvCC5Ri.js → PluginsPage-CwFfFBzW.js} +1 -1
  20. package/client/dist/assets/{ProjectSettingsDialog-C7Hesadg.js → ProjectSettingsDialog-DHsxLFMT.js} +1 -1
  21. package/client/dist/assets/{TemplatePreviewModal-DczCzUzP.js → TemplatePreviewModal-wcI3nGNr.js} +1 -1
  22. package/client/dist/assets/TicketDetailModal-7EloKP1C.js +14 -0
  23. package/client/dist/assets/{Trans-Cm9rhdnG.js → Trans-CPISVji8.js} +1 -1
  24. package/client/dist/assets/agent-BABjRt0P.js +1 -0
  25. package/client/dist/assets/agent-BuUpgqVx.js +1 -0
  26. package/client/dist/assets/agent-CCDZesgU.js +1 -0
  27. package/client/dist/assets/agent-CVn64zWH.js +1 -0
  28. package/client/dist/assets/agent-CWeOny0E.js +1 -0
  29. package/client/dist/assets/agent-DOysmad_.js +1 -0
  30. package/client/dist/assets/agent-DSsjTBhn.js +1 -0
  31. package/client/dist/assets/agent-DlZMxfM9.js +1 -0
  32. package/client/dist/assets/builder-BH8Fua-g.js +1 -0
  33. package/client/dist/assets/builder-BoV7DgKd.js +1 -0
  34. package/client/dist/assets/builder-Cc_Lf79C.js +1 -0
  35. package/client/dist/assets/builder-CwA4T7gP.js +1 -0
  36. package/client/dist/assets/builder-CwZ7Bme2.js +1 -0
  37. package/client/dist/assets/builder-CxW-JFsR.js +1 -0
  38. package/client/dist/assets/builder-D94lrV7x.js +1 -0
  39. package/client/dist/assets/builder-cqHpCyGa.js +1 -0
  40. package/client/dist/assets/{dist-js-BERxOT0X.js → dist-js-Bt2zW2YG.js} +1 -1
  41. package/client/dist/assets/{dist-js-Di8lflPo.js → dist-js-zvt5ap3o.js} +1 -1
  42. package/client/dist/assets/{formatDistanceToNow-BAXZRbEO.js → formatDistanceToNow-ByKli7Q_.js} +1 -1
  43. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-DwzZXHtg.js → getTimezoneOffsetInMilliseconds-qrcBaLW1.js} +1 -1
  44. package/client/dist/assets/i18n-Bf9Ckd10.js +2 -0
  45. package/client/dist/assets/index-Dh9i4CnN.css +2 -0
  46. package/client/dist/assets/index-xkzK3E5i.js +76 -0
  47. package/client/dist/assets/setup-3832BJCo.js +1 -0
  48. package/client/dist/assets/setup-B8J-gECe.js +1 -0
  49. package/client/dist/assets/setup-BCIOxVsc.js +1 -0
  50. package/client/dist/assets/setup-BciLj_tU.js +1 -0
  51. package/client/dist/assets/setup-CITtPjed.js +1 -0
  52. package/client/dist/assets/setup-CTkE3cfh.js +1 -0
  53. package/client/dist/assets/setup-ajB4rYm-.js +1 -0
  54. package/client/dist/assets/setup-xFO1wNl0.js +1 -0
  55. package/client/dist/assets/{spending-BLb5pj6G.js → spending-DMnCXy84.js} +1 -1
  56. package/client/dist/assets/{useBrowserCaptureSession-DR1_jL56.js → useBrowserCaptureSession-CkBJShUw.js} +1 -1
  57. package/client/dist/assets/{useDesktop-D4DxBGPU.js → useDesktop-B4pSBIDd.js} +1 -1
  58. package/client/dist/index.html +14 -13
  59. package/docs/codex.md +2 -1
  60. package/docs/customizing.md +1 -1
  61. package/docs/internals/gemini-mcp-registration.md +1 -1
  62. package/docs/internals/global-artifacts-alignment-contract.md +4 -4
  63. package/docs/internals/project-builder.md +306 -0
  64. package/docs/internals/safe-pr-review-flow.md +29 -19
  65. package/package.json +1 -1
  66. package/server/dist/agent-operator-prompt.js +7 -1
  67. package/server/dist/blueprint-chat-manager.js +335 -0
  68. package/server/dist/blueprint-commit.js +335 -0
  69. package/server/dist/blueprint-draft-parser.js +168 -0
  70. package/server/dist/blueprint-operator-prompt.js +149 -0
  71. package/server/dist/blueprint-render.js +105 -0
  72. package/server/dist/blueprint-router.js +173 -0
  73. package/server/dist/blueprint-spec-quality.js +167 -0
  74. package/server/dist/blueprint-store.js +49 -0
  75. package/server/dist/blueprint-types.js +29 -0
  76. package/server/dist/builder-cwd-manager.js +57 -0
  77. package/server/dist/chat-manager.js +435 -23
  78. package/server/dist/context-scope.js +6 -3
  79. package/server/dist/contract-refine-runner.js +143 -72
  80. package/server/dist/desktop-db.js +27 -0
  81. package/server/dist/index.js +15 -0
  82. package/server/dist/offline-assemble.js +203 -0
  83. package/server/dist/pricing.js +5 -0
  84. package/server/dist/project-router-chat.js +14 -2
  85. package/server/dist/project-router.js +130 -7
  86. package/server/dist/providers/claude-adapter.js +8 -2
  87. package/server/dist/providers/codex-adapter.js +23 -10
  88. package/server/dist/providers/gemini-adapter.js +16 -7
  89. package/server/dist/rail-isolated-launch.js +11 -1
  90. package/server/dist/setup-manager.js +6 -54
  91. package/server/dist/spec-models.js +3 -0
  92. package/server/dist/workspace-manager.js +3 -1
  93. package/server/dist/worktree-overlay.js +233 -51
  94. package/client/dist/assets/AgentModeCodePane-D9bhFyuy.js +0 -2
  95. package/client/dist/assets/TicketDetailModal-CFh7GOv5.js +0 -14
  96. package/client/dist/assets/agent-BXc59ARK2.js +0 -1
  97. package/client/dist/assets/agent-BbIFXdDq.js +0 -1
  98. package/client/dist/assets/agent-BqEfA5u5.js +0 -1
  99. package/client/dist/assets/agent-BtGzgTC4.js +0 -1
  100. package/client/dist/assets/agent-CIhys_5d.js +0 -1
  101. package/client/dist/assets/agent-CJPaqsVR.js +0 -1
  102. package/client/dist/assets/agent-n-hY4GuL.js +0 -1
  103. package/client/dist/assets/agent-tJ-efa3_2.js +0 -1
  104. package/client/dist/assets/i18n-cK5pPbj8.js +0 -2
  105. package/client/dist/assets/index-BT6xDQjB.css +0 -2
  106. package/client/dist/assets/index-CCq_kIrF.js +0 -76
  107. package/client/dist/assets/setup-CGZjy2IF2.js +0 -1
  108. package/client/dist/assets/setup-CXa0Sx16.js +0 -1
  109. package/client/dist/assets/setup-CmK87LuS.js +0 -1
  110. package/client/dist/assets/setup-D2ktKKMF2.js +0 -1
  111. package/client/dist/assets/setup-DG2H7ha5.js +0 -1
  112. package/client/dist/assets/setup-DK5bHDUq2.js +0 -1
  113. package/client/dist/assets/setup-DrWlIQTZ.js +0 -1
  114. package/client/dist/assets/setup-rzHUVgAu.js +0 -1
  115. /package/client/dist/assets/{activity-C6kJpm9q2.js → activity-C6kJpm9q.js} +0 -0
  116. /package/client/dist/assets/{activity-DAyekdlt2.js → activity-DAyekdlt.js} +0 -0
  117. /package/client/dist/assets/{addspec-DbNBbrha2.js → addspec-DbNBbrha.js} +0 -0
  118. /package/client/dist/assets/{addspec-htHGZHOe2.js → addspec-htHGZHOe.js} +0 -0
  119. /package/client/dist/assets/{agents-6ZOJXWVk2.js → agents-6ZOJXWVk.js} +0 -0
  120. /package/client/dist/assets/{agents-Bz-thHkB2.js → agents-Bz-thHkB.js} +0 -0
  121. /package/client/dist/assets/{agentstudio-BLGvxWBx2.js → agentstudio-BLGvxWBx.js} +0 -0
  122. /package/client/dist/assets/{agentstudio-D9UKiWTj2.js → agentstudio-D9UKiWTj.js} +0 -0
  123. /package/client/dist/assets/{aiedit-BoG24nlg2.js → aiedit-BoG24nlg.js} +0 -0
  124. /package/client/dist/assets/{aiedit-olnv3QSt2.js → aiedit-olnv3QSt.js} +0 -0
  125. /package/client/dist/assets/{analytics-CdsAPAwZ2.js → analytics-CdsAPAwZ.js} +0 -0
  126. /package/client/dist/assets/{analytics-IEsKZvpW2.js → analytics-IEsKZvpW.js} +0 -0
  127. /package/client/dist/assets/{attachments-D0wXXt9T2.js → attachments-D0wXXt9T.js} +0 -0
  128. /package/client/dist/assets/{attachments-V_WZAYXx2.js → attachments-V_WZAYXx.js} +0 -0
  129. /package/client/dist/assets/{browser-CyR1Kp3m2.js → browser-CyR1Kp3m.js} +0 -0
  130. /package/client/dist/assets/{browser-DwZljcRh2.js → browser-DwZljcRh.js} +0 -0
  131. /package/client/dist/assets/{chat-BR8oe53x2.js → chat-BR8oe53x.js} +0 -0
  132. /package/client/dist/assets/{chat-DZyu3ImY2.js → chat-DZyu3ImY.js} +0 -0
  133. /package/client/dist/assets/{code-D20ef9Cf2.js → code-D20ef9Cf.js} +0 -0
  134. /package/client/dist/assets/{code-geHmknB92.js → code-geHmknB9.js} +0 -0
  135. /package/client/dist/assets/{commands-BEwbAjtD2.js → commands-BEwbAjtD.js} +0 -0
  136. /package/client/dist/assets/{commands-DTQBF_q52.js → commands-DTQBF_q5.js} +0 -0
  137. /package/client/dist/assets/{common-BNbnv8cE2.js → common-BNbnv8cE.js} +0 -0
  138. /package/client/dist/assets/{common-DBtTzeYm2.js → common-DBtTzeYm.js} +0 -0
  139. /package/client/dist/assets/{dashboard-Cqmmbh3l2.js → dashboard-Cqmmbh3l.js} +0 -0
  140. /package/client/dist/assets/{dashboard-Dy2Um-US2.js → dashboard-Dy2Um-US.js} +0 -0
  141. /package/client/dist/assets/{explore-C0TjprJW2.js → explore-C0TjprJW.js} +0 -0
  142. /package/client/dist/assets/{explore-DsXAgnmJ2.js → explore-DsXAgnmJ.js} +0 -0
  143. /package/client/dist/assets/{integrations-CH0zy1Mw2.js → integrations-CH0zy1Mw.js} +0 -0
  144. /package/client/dist/assets/{integrations-b8pH-vfl2.js → integrations-b8pH-vfl.js} +0 -0
  145. /package/client/dist/assets/{jira-CNeYi06w2.js → jira-CNeYi06w.js} +0 -0
  146. /package/client/dist/assets/{jira-pRaL7JQo2.js → jira-pRaL7JQo.js} +0 -0
  147. /package/client/dist/assets/{jobs-B-sDHEay2.js → jobs-B-sDHEay.js} +0 -0
  148. /package/client/dist/assets/{jobs-DzSIR8V62.js → jobs-DzSIR8V6.js} +0 -0
  149. /package/client/dist/assets/{loops-Crc9It8o2.js → loops-Crc9It8o.js} +0 -0
  150. /package/client/dist/assets/{loops-DH2hZcbK2.js → loops-DH2hZcbK.js} +0 -0
  151. /package/client/dist/assets/{loops-DS5TmvIU2.js → loops-DS5TmvIU.js} +0 -0
  152. /package/client/dist/assets/{mcp-Czd_iyp02.js → mcp-Czd_iyp0.js} +0 -0
  153. /package/client/dist/assets/{mcp-D4ZjtIx32.js → mcp-D4ZjtIx3.js} +0 -0
  154. /package/client/dist/assets/{mcp-DIfBUv2D2.js → mcp-DIfBUv2D.js} +0 -0
  155. /package/client/dist/assets/{nav-BRJBxaGt2.js → nav-BRJBxaGt.js} +0 -0
  156. /package/client/dist/assets/{nav-CNskRJKQ2.js → nav-CNskRJKQ.js} +0 -0
  157. /package/client/dist/assets/{nav-TvumAFD22.js → nav-TvumAFD2.js} +0 -0
  158. /package/client/dist/assets/{settings-DAZqjX2a2.js → settings-DAZqjX2a.js} +0 -0
  159. /package/client/dist/assets/{settings-DRh39v1S2.js → settings-DRh39v1S.js} +0 -0
  160. /package/client/dist/assets/{settings-Mj1OJbfd2.js → settings-Mj1OJbfd.js} +0 -0
  161. /package/client/dist/assets/{specs-BcyxaCwf2.js → specs-BcyxaCwf.js} +0 -0
  162. /package/client/dist/assets/{specs-CLZKysq-2.js → specs-CLZKysq-.js} +0 -0
  163. /package/client/dist/assets/{specs-dV3Mq5Mu2.js → specs-dV3Mq5Mu.js} +0 -0
  164. /package/client/dist/assets/{terminal-C2NAd7BW2.js → terminal-C2NAd7BW.js} +0 -0
  165. /package/client/dist/assets/{terminal-IahE4LW92.js → terminal-IahE4LW9.js} +0 -0
  166. /package/client/dist/assets/{terminal-QpXZU2Bn2.js → terminal-QpXZU2Bn.js} +0 -0
  167. /package/client/dist/assets/{tickets-Bs37scwY2.js → tickets-Bs37scwY.js} +0 -0
  168. /package/client/dist/assets/{tickets-DQp7JS2R2.js → tickets-DQp7JS2R.js} +0 -0
  169. /package/client/dist/assets/{tickets-DpmdE8tv2.js → tickets-DpmdE8tv.js} +0 -0
@@ -204,7 +204,7 @@
204
204
  .specrails-splash__mark .pill { opacity: 1; }
205
205
  }
206
206
  </style>
207
- <script type="module" crossorigin src="/assets/index-CCq_kIrF.js"></script>
207
+ <script type="module" crossorigin src="/assets/index-xkzK3E5i.js"></script>
208
208
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-CNC7AqOf.js">
209
209
  <link rel="modulepreload" crossorigin href="/assets/jsx-runtime-zu2_FqZY.js">
210
210
  <link rel="modulepreload" crossorigin href="/assets/react-dom-DiWzBDP9.js">
@@ -213,13 +213,14 @@
213
213
  <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-Bjbbp_KS.js">
214
214
  <link rel="modulepreload" crossorigin href="/assets/activity-DiyjsgQV.js">
215
215
  <link rel="modulepreload" crossorigin href="/assets/addspec-Rw8uVBC1.js">
216
- <link rel="modulepreload" crossorigin href="/assets/agent-BqEfA5u5.js">
216
+ <link rel="modulepreload" crossorigin href="/assets/agent-BuUpgqVx.js">
217
217
  <link rel="modulepreload" crossorigin href="/assets/agents-DhG7mg6-.js">
218
218
  <link rel="modulepreload" crossorigin href="/assets/agentstudio-BIUe1Im2.js">
219
219
  <link rel="modulepreload" crossorigin href="/assets/aiedit-iIjz2_Qk.js">
220
220
  <link rel="modulepreload" crossorigin href="/assets/analytics-DZ8Aoq3I.js">
221
221
  <link rel="modulepreload" crossorigin href="/assets/attachments-B53PG733.js">
222
222
  <link rel="modulepreload" crossorigin href="/assets/browser-CXUcnTMI.js">
223
+ <link rel="modulepreload" crossorigin href="/assets/builder-Cc_Lf79C.js">
223
224
  <link rel="modulepreload" crossorigin href="/assets/chat-BGD7MXAy.js">
224
225
  <link rel="modulepreload" crossorigin href="/assets/code-BDXEM4ml.js">
225
226
  <link rel="modulepreload" crossorigin href="/assets/commands-DyUZQ1FI.js">
@@ -233,23 +234,23 @@
233
234
  <link rel="modulepreload" crossorigin href="/assets/mcp-uDkXYu4L.js">
234
235
  <link rel="modulepreload" crossorigin href="/assets/nav-myFaXRHD.js">
235
236
  <link rel="modulepreload" crossorigin href="/assets/settings-B0ypw9uw.js">
236
- <link rel="modulepreload" crossorigin href="/assets/setup-CmK87LuS.js">
237
+ <link rel="modulepreload" crossorigin href="/assets/setup-ajB4rYm-.js">
237
238
  <link rel="modulepreload" crossorigin href="/assets/specs-DII0C8gw.js">
238
239
  <link rel="modulepreload" crossorigin href="/assets/terminal-BFRZnMPP.js">
239
240
  <link rel="modulepreload" crossorigin href="/assets/tickets-D_yDquEw.js">
240
- <link rel="modulepreload" crossorigin href="/assets/i18n-cK5pPbj8.js">
241
- <link rel="modulepreload" crossorigin href="/assets/Trans-Cm9rhdnG.js">
241
+ <link rel="modulepreload" crossorigin href="/assets/i18n-Bf9Ckd10.js">
242
+ <link rel="modulepreload" crossorigin href="/assets/Trans-CPISVji8.js">
242
243
  <link rel="modulepreload" crossorigin href="/assets/dist-bVqfYmby.js">
243
244
  <link rel="modulepreload" crossorigin href="/assets/arrow-left-CpIR4j7G.js">
244
245
  <link rel="modulepreload" crossorigin href="/assets/auth-D_5bQv5W.js">
245
246
  <link rel="modulepreload" crossorigin href="/assets/api-B2M7CtDw.js">
246
- <link rel="modulepreload" crossorigin href="/assets/useBrowserCaptureSession-DR1_jL56.js">
247
+ <link rel="modulepreload" crossorigin href="/assets/useBrowserCaptureSession-CkBJShUw.js">
247
248
  <link rel="modulepreload" crossorigin href="/assets/arrow-up-right-9DoMn7fD.js">
248
249
  <link rel="modulepreload" crossorigin href="/assets/sparkles-DrY8hb0X.js">
249
250
  <link rel="modulepreload" crossorigin href="/assets/x-CBMI9ejn.js">
250
251
  <link rel="modulepreload" crossorigin href="/assets/useSharedWebSocket-DaPh7QBL.js">
251
- <link rel="modulepreload" crossorigin href="/assets/useDesktop-D4DxBGPU.js">
252
- <link rel="modulepreload" crossorigin href="/assets/MinimizedChatsContext-DJAkRYps.js">
252
+ <link rel="modulepreload" crossorigin href="/assets/useDesktop-B4pSBIDd.js">
253
+ <link rel="modulepreload" crossorigin href="/assets/MinimizedChatsContext-Cs34FPTz.js">
253
254
  <link rel="modulepreload" crossorigin href="/assets/external-link-BcU5EWZp.js">
254
255
  <link rel="modulepreload" crossorigin href="/assets/chevron-right-CViMxucY.js">
255
256
  <link rel="modulepreload" crossorigin href="/assets/circle-x-CMYsN4ZH.js">
@@ -272,11 +273,11 @@
272
273
  <link rel="modulepreload" crossorigin href="/assets/dialog-CzIekCw0.js">
273
274
  <link rel="modulepreload" crossorigin href="/assets/jira-api-BgFIr8eD.js">
274
275
  <link rel="modulepreload" crossorigin href="/assets/lib-CGdyxIWg.js">
275
- <link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-DwzZXHtg.js">
276
- <link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-BAXZRbEO.js">
277
- <link rel="modulepreload" crossorigin href="/assets/spending-BLb5pj6G.js">
278
- <link rel="modulepreload" crossorigin href="/assets/TicketDetailModal-CFh7GOv5.js">
279
- <link rel="stylesheet" crossorigin href="/assets/index-BT6xDQjB.css">
276
+ <link rel="modulepreload" crossorigin href="/assets/getTimezoneOffsetInMilliseconds-qrcBaLW1.js">
277
+ <link rel="modulepreload" crossorigin href="/assets/formatDistanceToNow-ByKli7Q_.js">
278
+ <link rel="modulepreload" crossorigin href="/assets/spending-DMnCXy84.js">
279
+ <link rel="modulepreload" crossorigin href="/assets/TicketDetailModal-7EloKP1C.js">
280
+ <link rel="stylesheet" crossorigin href="/assets/index-Dh9i4CnN.css">
280
281
  </head>
281
282
  <body>
282
283
  <div id="specrails-splash" aria-hidden="false" role="status" aria-label="Loading Specrails">
package/docs/codex.md CHANGED
@@ -116,7 +116,8 @@ the [Gemini guide](gemini.md).)
116
116
  | **Min CLI version** | none pinned | `0.128.0` | `0.11.0` |
117
117
  | **Project dir** | `.claude/` | `.codex/` | `.gemini/` |
118
118
  | **Instructions file** | `CLAUDE.md` | `AGENTS.md` | `GEMINI.md` |
119
- | **Default model** | `sonnet` | `gpt-5.5` | `gemini-3.5-flash` |
119
+ | **Default model** | `sonnet` | `gpt-5.5` (catalog includes the GPT-5.6 family: `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`) | `gemini-3.5-flash` |
120
+ | **Reasoning efforts** | `low`–`xhigh` | `minimal`–`ultra` (`xhigh`/`max`/`ultra` ship with GPT-5.6; `ultra` is Sol-only upstream) | none |
120
121
  | **Agent format** | `.claude/agents/<id>.md` with `model:` frontmatter | `.codex/skills/<id>/SKILL.md` Skill format | `.gemini/` command + agent target |
121
122
  | **Agent profiles** | Full support (rail `RailProfileSelector`) | **None** — rails force the profile to `null` | **None** — rails force the profile to `null` |
122
123
  | **Pipeline rails** | ✅ all rail types | ✅ (except Freestyle) | ✅ (except Freestyle) |
@@ -161,7 +161,7 @@ Most settings live in the UI. A few app-level switches are env-only because they
161
161
  | `SPECRAILS_GEMINI_BETA=0` | Emergency rollback — disable the Gemini provider (default unset = enabled; only the exact string `0` disables; **no legacy fallback name**) |
162
162
  | `SPECRAILS_SMASH=0` | Kill switch for SMASH spec decomposition (`0` / `false` / `off`; endpoints return 409) |
163
163
  | `SPECRAILS_EXPLORE_CONTRACT_REFINE=0` | App-wide kill switch for Contract Refine (auto-fire + retry endpoint; accepts `0` / `false` / `off`) |
164
- | `SPECRAILS_EXPLORE_LEGACY_CWD=1` | Force Explore spawns to use the project root instead of the app-managed `explore-cwd/` |
164
+ | `SPECRAILS_EXPLORE_LEGACY_CWD=1` | Force every Explore spawn to use the project root instead of the app-managed `explore-cwd/` or a relocated workspace |
165
165
  | `SPECRAILS_FILE_SUMMARY_MODEL` | Override the model used for Code-section file summaries. Per-provider overrides take precedence: `SPECRAILS_FILE_SUMMARY_MODEL_CLAUDE` (Claude) and `SPECRAILS_FILE_SUMMARY_MODEL_CODEX` (Codex); the generic var is the fallback |
166
166
  | `SPECRAILS_ALLOW_LOCAL_WEBHOOKS=1` | Allow outbound webhooks to target loopback / private-network addresses |
167
167
 
@@ -38,7 +38,7 @@ loads** for gemini:
38
38
  |---|---|---|
39
39
  | Plugins on rails (`server/plugin-manager.ts`, e.g. Serena) | surgical merge into `<project>/.mcp.json` for `project-json` providers | plugin installs "successfully", MCP server never loads in gemini rail spawns |
40
40
  | Workspace MCP (`server/agent-mcp-config.ts` `mergeSpecrailsIntoWorkspaceMcp`, via `workspace-manager.ts`) | writes `<workspace>/.mcp.json` | not read by gemini |
41
- | Explore `context_scope.mcp` (`server/chat-manager.ts`) | spawns from `<project.path>` so `.mcp.json` loads | loads nothing on gemini |
41
+ | Explore `context_scope.mcp` (`server/chat-manager.ts`) | spawns from the relocation-aware artifact cwd (workspace when relocated, `<project.path>` when legacy) so `.mcp.json` loads | loads nothing on gemini |
42
42
 
43
43
  Fixing these means writing into the repo's / workspace's `.gemini/settings.json`, which
44
44
  intersects the **pristine-repo / artifact-relocation policy** (the app must not mutate the
@@ -239,7 +239,7 @@ $HOME/.specrails/
239
239
  ├── jira-secret.key # keyfile AES-256-GCM (0600), desktop-only
240
240
  ├── doctor.log # log append-only, core-only (única lectura $HOME de core hoy)
241
241
  └── projects/<slug>/
242
- ├── workspace/ # = artifactRoot. cwd de spawn de todas las AI-CLI
242
+ ├── workspace/ # = artifactRoot. cwd de jobs y Explore mcp=true relocalizados
243
243
  │ ├── .specrails/
244
244
  │ │ ├── local-tickets.json (+ .lock) ← SPECRAILS_TICKETS_PATH [core escribe/lee · desktop escribe]
245
245
  │ │ ├── backlog-config.json ← SPECRAILS_BACKLOG_CONFIG_PATH [desktop escribe · core lee]
@@ -365,9 +365,9 @@ El lado desktop ya tiene el precedente load-bearing en producción (`explore-cwd
365
365
 
366
366
  ### 6.1 cwd de spawn + env por provider
367
367
 
368
- `ExploreCwdManager` se generaliza a un `WorkspaceManager` que materializa `~/.specrails/projects/<slug>/workspace/` con `./project -> <project.path>` (symlink/junction, fallback `project-path.txt` — la lógica existente `ensureProjectLink`). **Todas las AI-CLI mueven cwd al workspace.**
368
+ `ExploreCwdManager` se generaliza a un `WorkspaceManager` que materializa `~/.specrails/projects/<slug>/workspace/` con `./project -> <project.path>` (symlink/junction, fallback `project-path.txt` — la lógica existente `ensureProjectLink`). **Las AI-CLI relocalizadas usan el workspace salvo los procesos deliberadamente repo-bound y Explore con `mcp=false`, que conserva su `explore-cwd`.**
369
369
 
370
- **Managers que MUEVEN cwd → workspace:** `QueueManager._startJob` (rails — el grande), `ChatManager` (Explore + sidebar), `AgentRefineManager` (ai-edit), `ContractRefineRunner`, `project-router /tickets/generate-spec` (quick-spec), `FileSummaryManager`, `SetupManager.startInstall` (corre `npx specrails-core init --root-dir <project.path> --workspace-dir <workspace>`).
370
+ **Managers que MUEVEN cwd → workspace:** `QueueManager._startJob` (rails — el grande), `ChatManager` (sidebar + Explore con `mcp=true`), `AgentRefineManager` (ai-edit), `ContractRefineRunner` cuando refleja un Explore `mcp=true`, `project-router /tickets/generate-spec` (quick-spec), `FileSummaryManager`, `SetupManager.startInstall` (corre `npx specrails-core init --root-dir <project.path> --workspace-dir <workspace>`). Explore y Contract Refine con `mcp=false` permanecen en el app-managed `explore-cwd`.
371
371
 
372
372
  **Managers que CONSERVAN cwd = `project.path`:** `TerminalManager` (shell repo-bound), `file-provenance.ts` (git repo-bound), `code-explorer-router` (lee fuente), lecturas de fuente de `FileSummaryManager`, `metrics.ts` (coverage).
373
373
 
@@ -380,7 +380,7 @@ El lado desktop ya tiene el precedente load-bearing en producción (`explore-cwd
380
380
  - **Smoke check (manual):** en un proyecto relocalizado con provider=codex, lanzar un loop "Implement" cuyo paso de fix deba editar una fuente del repo; confirmar en el transcript que (a) `apply_patch`/edit a `<repoDir>/...` **NO** devuelve `Operation not permitted`, y (b) `npm run build`/`tsc -b` puede escribir `node_modules/.tmp/*.tsbuildinfo`. Reproducción del fallo: revertir el `extraArgs` de codex → la edición del repo en la iteración ≥2 vuelve a fallar con `Operation not permitted`. Cubierto por `server/loop-executors.test.ts` (assert del arg `writable_roots`).
381
381
  - **gemini**: inyecta `GEMINI_CLI_TRUST_WORKSPACE=true` (ancla el root confiable al workspace). **Re-keya** `~/.gemini/acknowledgments/agents.json` al workspace.
382
382
  - Los tres: el workspace **estrictamente fuera del repo** y el discovery no debe seguir `./project` (mitiga la fuga por ancestor-walk de codex/gemini).
383
- - **Caso especial Explore `mcp=true`**: hoy `chat-manager.ts:326` conmuta cwd a `project.path` para cargar el `.mcp.json` del repo — tras relocalizar, `.mcp.json` está en el workspace, así que este special-case se **ELIMINA**, no se re-apunta.
383
+ - **Caso especial Explore `mcp=true` (implementado):** `ChatManager` resuelve el cwd mediante la misma puerta de relocalización: workspace cuando el proyecto está relocalizado, `project.path` en legacy. Las rutas persistent-stdin, crash-respawn y Contract Refine conservan esa misma pareja cwd/env. Una sesión anterior creada bajo el cwd del repo que devuelve exactamente `No conversation found with session ID` se invalida y reintenta fresh una sola vez desde el workspace (transcript persistido acotado para Explore; ticket sembrado y tools deshabilitadas para Contract Refine), nunca volviendo al repo. `SPECRAILS_EXPLORE_LEGACY_CWD=1` sigue forzando `project.path`.
384
384
 
385
385
  ### 6.2 Sitios de re-apuntado de path (`project.path` → workspace)
386
386
 
@@ -0,0 +1,306 @@
1
+ # Project Builder — as-built internals
2
+
3
+ > OpenSpec change `add-project-builder`. Greenfield project creation from an
4
+ > idea: a day-0 blueprint conversation, an orchestrated bootstrap commit, and a
5
+ > milestone lifecycle (Launch M1 batch rail, sidebar re-entry, Generate M2+).
6
+
7
+ ## Entry + builder mode (reskin)
8
+
9
+ "+ Add Project" shows an **Existing | New** pre-screen (`AddProjectDialog`,
10
+ `chooser-existing` / `chooser-new` cards) when both `FEATURE_PROJECT_BUILDER`
11
+ (client) and the parent-wired `onOpenBuilder` callback are present. *Existing*
12
+ continues into the unchanged AddProjectDialog/setup-wizard flow; *New* calls
13
+ `AgentChatContext.builderMode.enter()` — the AGENT transforms into the Builder
14
+ (change `reskin-project-builder-into-agent-panel`; the original full-screen
15
+ `ProjectBuilderShell` overlay is deleted). Flags:
16
+ `VITE_FEATURE_PROJECT_BUILDER` (client) / `SPECRAILS_PROJECT_BUILDER` (server),
17
+ both default ON, `"false"` opts out (server flag 404s every `/api/blueprint/*`
18
+ route).
19
+
20
+ **Builder mode (client architecture):**
21
+
22
+ - `builderMode` slice on `AgentChatContext`: `{ active, enter, exit, session }`.
23
+ `enter()` also opens the floating panel outside Agent Mode; `exit()` aborts
24
+ the in-flight builder turn (`POST /abort`) and resets every session slice.
25
+ The agent's own chrome (project/mission selectors, conversation, queue,
26
+ pinned cards) is hidden while active but NEVER unmounted.
27
+ - Session logic in `client/src/hooks/useBuilderSession.ts` (extracted from the
28
+ retired shell): bootstrap, `blueprint.*` WS handling with NULL-SAFE identity
29
+ checks (a pre-bootstrap/pre-commit null ref must never match a null/absent
30
+ message id), phases, snapshot, send/commit/launch actions, `dirty` flag.
31
+ - `BuilderConversation` (`client/src/components/project-builder/`): the four
32
+ phases in the MISSION format — chat renders a centered message column with
33
+ the `BuilderComposer` docked at the bottom (centered card while empty,
34
+ mission-style); in Agent Mode the hero and docked cards share a `layoutId`,
35
+ so the first send morphs the agent smoothly down. Then commit
36
+ (`BlueprintCommitForm`), progress step list, done (Launch M1 / Open project).
37
+ Esc: commit→chat, chat→exit (confirm-gated when dirty).
38
+ - `BuilderComposer`: mirrors the mission composer's visual language — the SAME
39
+ provider/model/effort selectors (provider/model bind to the blueprint
40
+ conversation; reasoning effort is validated against the selected provider
41
+ and rides each send), surprise-me chip, native `resize-y` textarea, and the
42
+ same `SendHorizontal` action. Models and effort levels come from
43
+ `GET /api/blueprint/models?provider=`.
44
+ - `BuilderHalo`: orbiting ring — CSS `builder-halo-spin` keyframes over an
45
+ XOR-masked conic-gradient border band (follows ANY radius: circular on the
46
+ bubble/panel-header icon, card radius on the composer; no per-frame JS),
47
+ `motion` enter/exit, `prefers-reduced-motion` ⇒ static glow. It is strictly
48
+ an ENTRY flourish: shown on the fresh empty composer and the matching
49
+ bubble/panel-header identity, then removed as soon as the first work message
50
+ begins. Builder mode itself continues without a halo; there is no wind-down.
51
+ - Per UI mode: **board** — `AgentChatPanel` widens (860px) and gains an
52
+ attached blueprint side pane; **Agent Mode** — `AgentModeSurface` renders
53
+ the builder conversation and `AgentWorkspaceSidebar` swaps (AnimatePresence)
54
+ to `BlueprintPanel` + the Create-specs CTA, forced-expanded for the duration.
55
+ - The builder conversation stays on `blueprint_conversations` / `blueprint.*`
56
+ — never mixed into `agent_conversations` or the mission selector.
57
+ - `BlueprintPanel` M1 spec cards are clickable → `BlueprintSpecModal`
58
+ (hand-rolled body portal at z-[65], same tier as `LoopPreviewModal`; Esc +
59
+ backdrop close). At day 0 the specs are NOT tickets yet (only in the
60
+ snapshot), so this is a lightweight read-only preview — NOT the heavy
61
+ `TicketDetailModal`. Cards expose `shortSummary`, priority, and acceptance-
62
+ criteria count; the modal exposes the summary, priority, canonical five-
63
+ section description, and every separate acceptance criterion. The same panel
64
+ (and therefore the modal) is reused in the M2+ `MilestoneGenerateShell`, so
65
+ the user reviews the content that will become the authoritative tickets
66
+ (apart from the deterministic criteria fold). The detailed M2+ preview is
67
+ transient; the blueprint has no per-milestone detailed-spec collection.
68
+
69
+ ## Day-0 chat (no project exists)
70
+
71
+ - **Manager**: `server/blueprint-chat-manager.ts` `BlueprintChatManager` — an
72
+ app-level sibling of `AgentChatManager` reusing `runAiCliInvocation`. Spawns
73
+ from `~/.specrails/builder-cwd/` (`server/builder-cwd-manager.ts`: always
74
+ re-written instruction files from `server/blueprint-operator-prompt.ts`, NO
75
+ `./project` symlink, NO MCP). Auto-heal: a resume that yields no text retries
76
+ fresh once. Abort keeps partial text and records `aborted`.
77
+ - **Persistence**: `blueprint_conversations` / `blueprint_messages` in
78
+ `desktop.sqlite` (migration 22; CRUD in `server/blueprint-store.ts`).
79
+ - **WS**: app-global `blueprint.stream` / `blueprint.done` / `blueprint.error`
80
+ (no `projectId`; NOT in the mobile-ws translation layer). `blueprint.done`
81
+ carries the STRIPPED `fullText` plus the last valid `blueprint` snapshot.
82
+ - **Accounting**: one `agent_invocations` row per settled turn with
83
+ `project_id NULL` (the Home-turn precedent). No backfill after creation.
84
+ - **REST**: `/api/blueprint/*` (`server/blueprint-router.ts`) — conversations
85
+ CRUD, `/send` (202, 409 while streaming), `/abort`, `/models`, `/commit`.
86
+ `/models` returns the provider's `efforts` catalog; `/send` accepts only a
87
+ catalog-valid `reasoning_effort`, and providers without the capability omit
88
+ the field when spawning.
89
+ - **Generation boundary**: interview turns and Surprise Me may complete the
90
+ product/flow/platform/stack/assumption/milestone proposal, but MUST keep
91
+ `m1Specs: []` and `specsComplete: false`. Detailed M1 generation starts only
92
+ after explicit user approval or a direct request to generate the backlog.
93
+ That next assistant turn emits the entire self-validated 5–10-spec M1 set in
94
+ ONE response containing ONE complete fenced snapshot. It never publishes a
95
+ partial subset as the latest draft; `specsComplete: true` appears only on the
96
+ complete set. If generation cannot finish, the prior non-complete proposal
97
+ remains the last valid state and cannot be committed.
98
+
99
+ ## blueprint-draft protocol
100
+
101
+ Fenced ` ```blueprint-draft ` JSON blocks. FULL snapshots, LAST syntactically
102
+ valid block wins, streaming tail cut (unterminated trailing fence never
103
+ parsed/shown — `cutUnterminatedBlock`). Unknown keys dropped; missing or
104
+ non-integer `blueprintVersion` rejects the block. Parser pair:
105
+ `server/blueprint-draft-parser.ts` ⇄ `client/src/lib/blueprint-draft.ts` (keep
106
+ coercion rules in sync). Schema types in `server/blueprint-types.ts`:
107
+ `product{name,pitch,audience}`, `coreFlow`, `platform`,
108
+ `stack{language,framework,db,notes?}`, `assumptions[]`,
109
+ `milestones[]{id,title,goal,status: planned|committed|done, plannedSpecs[],
110
+ ticketIds?}` (ticketIds ADVISORY only), `specsComplete`, and detailed
111
+ `m1Specs[]{kind,title,shortSummary,description,acceptanceCriteria[],priority,
112
+ labels[],dependsOnIndex?}`. `kind` is `scaffold|feature|verification`; priority
113
+ is `low|medium|high|critical`. After approval, detailed M1 generation arrives
114
+ as one complete 5–10-spec snapshot with an explicit scaffold first. M2+
115
+ milestones carry `plannedSpecs` titles only until that milestone is explicitly
116
+ generated, at which point the target milestone's entire detailed set likewise
117
+ arrives in one assistant response/snapshot. The version stays 1: legacy
118
+ snapshots default missing `specsComplete=false`, `kind='feature'`,
119
+ `shortSummary=''`, criteria to `[]`, and missing/invalid priority to `medium`
120
+ on read. Those defaults preserve readability only; newly committed batches
121
+ must pass the strict quality gate.
122
+
123
+ Parsing deliberately retains two representations of the LAST valid block:
124
+ `blueprint` is the compatibility-normalized `Blueprint` used by preview/read
125
+ surfaces, while `rawBlueprint` is the exact parsed JSON before enum defaults,
126
+ dependency drops, or missing-field defaults. `blueprint.done` carries both;
127
+ the client mirrors the pair, derives readiness from the raw value, and sends
128
+ that raw value to M1/M2+ commit. Therefore an invalid `kind`, `priority`,
129
+ `dependsOnIndex`, or required field cannot disappear/default during rendering
130
+ and then pass the mutation gate. Persisted legacy files are a different read
131
+ boundary: `readBlueprint()` calls `coerceBlueprint()` server-side so old v1
132
+ files remain readable (and returns null for missing/corrupt input).
133
+
134
+ ## Canonical rich-spec contract (M1 and generated M2+)
135
+
136
+ There is no Builder-specific “lite spec” format. Every detailed Builder spec
137
+ uses the normal Specrails contract:
138
+
139
+ 1. `kind`: `scaffold`, `feature`, or `verification`
140
+ 2. an English, action-oriented, unique `title`
141
+ 3. a one-sentence `shortSummary` no longer than 240 characters
142
+ 4. `description` with exactly these `##` headings, once and in this order:
143
+ `Problem Statement`, `Proposed Solution`, `Out of Scope`,
144
+ `Technical Considerations`, `Estimated Complexity`
145
+ 5. a separate `acceptanceCriteria[]` containing 4–10 non-empty, independently
146
+ testable outcomes; `description` MUST NOT contain `## Acceptance Criteria`
147
+ 6. a catalog-valid `priority`, non-empty domain labels, and an optional
148
+ `dependsOnIndex` that points strictly backward (the M1 scaffold omits it)
149
+
150
+ Every named description section has a non-empty body. `Out of Scope` and
151
+ `Technical Considerations` each contain at least 2 bullets; Estimated
152
+ Complexity includes a reasoned estimate. Day-0 technical considerations name
153
+ the selected stack, planned components/contracts, risks, and inter-spec
154
+ dependencies but never fabricate repository paths. Generated M2+ specs first
155
+ inspect the real project and may name only verified existing paths and
156
+ identifiers; their criteria cover behavior, failure/edge cases, and tests.
157
+
158
+ `server/blueprint-spec-quality.ts` is the shared deterministic authority. It
159
+ validates `specsComplete=true`, the complete-set size, all fields/sections
160
+ above (including both 2-bullet minima), unique titles, the M1 first-item
161
+ `kind='scaffold'` rule, and backward-only dependencies. Both commit paths run
162
+ it before any filesystem, registry, blueprint, milestone, or ticket-store
163
+ mutation and return a stable spec/field-oriented detail when it rejects. A
164
+ prompt is a generation aid, never the integrity boundary. Validation receives
165
+ the exact raw generated payload; normalized compatibility views are not
166
+ commit evidence.
167
+
168
+ ## Orchestrated commit (register-project-LAST)
169
+
170
+ `POST /api/blueprint/commit` → sync validation (named errors: `invalid_name`,
171
+ `invalid_location`, `providers_required`, `unknown_provider`,
172
+ `invalid_blueprint`, `m1_specs_required`, `m1_specs_over_cap`,
173
+ `bundled_framework_missing`, `location_not_empty`,
174
+ `location_already_registered`; rich-spec failures include actionable spec/field
175
+ detail) → 202 `{commitId}` → per-step
176
+ `blueprint.commit_progress` → terminal `blueprint.commit_done{projectId}` /
177
+ `commit_failed{step,error}`. Orchestrator: `server/blueprint-commit.ts`
178
+ `createBlueprintCommitRunner` (DI IO bag — every step fail-injectable in
179
+ tests). Step order:
180
+
181
+ 1. `create-dir` — mkdir target
182
+ 2. `git-init` — `git init -b main` + deterministic README (no AI call,
183
+ `renderReadme`) + initial commit (pinned committer identity)
184
+ 3. `assemble` — registry mirror + framework materialize + one core
185
+ `init --from-config` per provider (`server/offline-assemble.ts`
186
+ `assembleProjectOffline`, extracted from `SetupManager`): PREFERS the
187
+ bundled core (offline `node <bundled-cli> init`, `spawnBundledCoreInit`),
188
+ falls back to `npx specrails-core` (`spawnNpxCoreInit`) when no bundle —
189
+ so `npm run dev` and runtimes-less builds work. `canAssembleProject()` is
190
+ the validation gate: true when the bundle is present OR
191
+ `SPECRAILS_IS_DESKTOP !== '1'`; only a packaged desktop build with a
192
+ missing/corrupted bundle returns false → `bundled_framework_missing`
193
+ ("reinstall the app"). Verifies the workspace exists afterwards.
194
+ 4. `blueprint` — `writeBlueprintPair` into `<workspace>/.specrails/`
195
+ (`blueprint.json` source of truth + deterministic `blueprint.md`,
196
+ `server/blueprint-render.ts`; repo stays pristine)
197
+ 5. `tickets` — `mutateStore` on the workspace `local-tickets.json`: `todo`,
198
+ label `M1`, `source='project-builder'`, `created_by='project-builder'`, spec
199
+ order preserved, generated priority/short summary/domain labels retained,
200
+ structured criteria folded once via the normal
201
+ `formatDescriptionWithCriteria` helper, `dependsOnIndex` → `prerequisites`,
202
+ advisory ids written back to milestone m1 (+ `status='committed'`) and the
203
+ pair re-rendered
204
+ 6. `register` — `ProjectRegistry.addProject` with the pre-generated id/slug
205
+ (LAST mutation; broadcast `desktop.project_added`)
206
+ 7. `github` — best-effort, never aborts. A server-side pre-flight
207
+ (`gh auth token`) runs first as defence against a stale client cache: a
208
+ spawn error (gh not on PATH) → warning `gh_not_installed`; a non-zero exit
209
+ → warning `gh_not_authenticated`, both WITHOUT spawning the create. Then
210
+ `gh repo create <slug> --private --source . --push`; a failure is
211
+ classified by `classifyGhCreateError` (`gh_scope` for 403/scope,
212
+ `gh_repo_exists`, `gh_network`, else `gh_failed`). Every warning rides the
213
+ `blueprint.commit_progress` payload's additive `code` field so the client
214
+ renders an i18n message (`builder:progress.githubErrors.*` ×8, raw stderr
215
+ tail kept as tooltip) and fires ONE non-blocking `toast.warning` per commit
216
+ attempt (`useBuilderSession` `ghWarnedRef`).
217
+
218
+ **Checkbox gating** (`BlueprintCommitForm`): the "Create private GitHub
219
+ repository" option renders ONLY when gh is installed+executable
220
+ (`usePrerequisites()` gh entry); installed-but-unauthenticated shows it
221
+ disabled with the actionable `commit.githubAuthHint` («gh auth login»). gh
222
+ absent ⇒ the checkbox does not exist. Submit force-clears the flag unless gh
223
+ is fully ready (installed + authenticated).
224
+
225
+ **Crash posture**: a crash before step 6 leaves an orphan dir + registry entry
226
+ but NO project row (invisible; re-run rejects `location_not_empty`). After
227
+ step 6 it is an ordinary project missing only the remote.
228
+
229
+ ## Milestone lifecycle
230
+
231
+ - **Launch Milestone 1** (`client/src/lib/milestone-launch.ts`
232
+ `launchMilestone`): gather `M1`-labeled `todo` tickets → `POST /rails`
233
+ (server allocates the lowest free index) → `PUT /rails/:i/tickets` → `POST
234
+ /rails/:i/launch {mode:'batch-implement'}` (the server maps the bare mode to
235
+ the batch factory loop: worktree isolation + ask-first PR). Offered on the
236
+ Builder done screen and the sidebar entry; existing 409 guards surface as
237
+ toasts.
238
+ - **Sidebar re-entry** (`BuilderSidebarEntry`, mounted in
239
+ `ProjectRightSidebar` + `AgentWorkspaceSidebar`): visible iff
240
+ `GET /api/projects/:id/blueprint` (project-router) returns a blueprint
241
+ (404 = hidden). Progress derives LIVE from board tickets by `M<n>` label —
242
+ never from stored ticket ids. Actions: Launch M1 (while launchable) +
243
+ Generate M<next> (first `planned` milestone > 1).
244
+ - **Board classification**: new Builder tickets use
245
+ `source='project-builder'` + `created_by='project-builder'`. For projects
246
+ created before that source existed, `DashboardPage` also treats
247
+ `source='manual'` + `created_by='project-builder'` as specs; existing tickets
248
+ appear after reload without rewriting the user's ticket store.
249
+ - **Generate M2+** (`MilestoneGenerateShell`): PROJECT-level conversation
250
+ `kind='milestone'` through the existing ChatManager. The milestone id rides
251
+ `context_scope` as `{milestone:'m2'}` (`POST /chat/conversations` body
252
+ `{kind:'milestone', milestone}`); `ChatManager._buildMilestoneSystemPrompt`
253
+ seeds the prompt with the workspace `blueprint.json`, the target
254
+ `plannedSpecs`, the complete canonical rich-spec/quality contract, and the
255
+ code-grounding rules. It instructs the agent to return every detailed spec
256
+ for the target milestone in one assistant response and one complete snapshot,
257
+ never an incrementally committable subset. Claude receives it through the
258
+ system prompt; adapters without that argument receive the same dynamic
259
+ instructions and blueprint context in the effective user turn, so Claude,
260
+ Codex, and Gemini receive equivalent authoring/context instructions.
261
+ Generation itself is read-only: the prompt forbids repository/workspace/
262
+ ticket/config/git mutation, write-capable shell/tool actions, builds, and
263
+ tests. `toolPolicy='read-only'` maps Claude to `--permission-mode plan`
264
+ + `--safe-mode` + only `Read,Grep,Glob`; Codex fresh turns use
265
+ `--sandbox read-only` and resumes carry `sandbox_mode="read-only"`; Gemini
266
+ uses `--approval-mode plan` and never `--yolo`. Gemini CLI does not expose a
267
+ selectable filesystem sandbox comparable to Codex, so its protection is the
268
+ native plan/policy layer plus the prompt—not an OS/filesystem sandbox. If a
269
+ CLI version rejects an incompatible safety flag, the turn fails closed; it
270
+ is never retried with a mutating/yolo policy. For relocated projects the
271
+ prompt identifies the absolute real repo, its `./project` mount, and
272
+ `SPECRAILS_REPO_DIR`; it explicitly warns Read/Grep/Glob not to receive
273
+ literal shell-variable expressions, preventing grounding against the empty
274
+ workspace. Grounded specs name only verified paths/identifiers and require
275
+ behavioral, failure/edge-case, and testing criteria. Accounting records
276
+ `surface='explore-spec'` (no new surface value). Committing calls
277
+ `POST /api/projects/:id/blueprint/commit-milestone {milestoneId, specs[]}` —
278
+ atomically validates the complete batch before any write, then inserts
279
+ `M<n>`-labeled `todo` tickets with Builder source/provenance and the same
280
+ priority/summary/criteria/labels/prerequisite ticket-materialization rules as M1
281
+ (broadcasting `ticket_created` per row), flips the milestone to `committed`,
282
+ and re-renders the pair. These tickets are the authoritative detailed M2+
283
+ representation: their descriptions contain the folded criteria and they
284
+ retain priority, short summary, domain + `M<n>` labels, and prerequisites.
285
+ `blueprint.json` deliberately stores only the existing milestone skeleton
286
+ with `status='committed'` and advisory `ticketIds`; it has no detailed-M2-
287
+ per-milestone schema. On 201 the shell calls `onCommitted`, increments the
288
+ sidebar blueprint refresh key (no-store refetch), closes, and the next CTA
289
+ resolves to the first later milestone still `planned`. Jira-connected
290
+ projects ride the existing machinery on the store mutation.
291
+
292
+ ## Tests
293
+
294
+ `server/blueprint-{draft-parser,render,store,chat-manager,commit,router}.test.ts`,
295
+ `server/offline-assemble.test.ts`, `server/project-router.test.ts`; client
296
+ `src/lib/__tests__/{blueprint-draft,milestone-launch}.test.ts`,
297
+ `src/hooks/__tests__/useBuilderSession.test.ts`,
298
+ `src/components/__tests__/{ProjectBuilder,BuilderSidebarEntry}.test.tsx`, and
299
+ `src/pages/__tests__/DashboardPage.test.tsx`.
300
+ Locale parity covers the `builder` namespace ×8.
301
+
302
+ ## Deferred (v2)
303
+
304
+ Resume/minimize for Builder conversations (no dock chip — no `projectId` to
305
+ tag), an orphan-dir startup sweeper, non-GitHub remotes, editing an existing
306
+ blueprint via the day-0 Builder, a live side-panel draft during M2 generation.
@@ -193,25 +193,35 @@ seeded instruction file), which lives in `~/.specrails/projects/<slug>/workspace
193
193
  absent from the worktree: the claude CLI reported `Unknown command: /specrails:implement`
194
194
  and the loop "succeeded" through verify/fix without implementing (live evidence: run
195
195
  01f41203). Legacy projects have the sibling gap for their UNTRACKED on-disk `.claude`
196
- entries. Each allocated worktree therefore gets `applyWorktreeOverlay(...)` at launch:
197
-
198
- - **Source root** = the project's effective artifact root, resolved once per launch via
199
- `resolveProjectExecution`: the workspace when relocated, else the repo itself.
200
- - **Merge-only symlinks** under `<worktree>/<providerDir>/`: dir links where a dir is
201
- wholly absent, per-file/per-child where the checkout is partially present; checkout
202
- content is NEVER overwritten. `agent-memory` is linked (all runs share memory — the
203
- pre-isolation shared-cwd semantics, deliberate). The providerDir root and the source's
204
- `worktrees/` entry are never linked (nested pipeline worktrees stay local). `.mcp.json`
205
- and the instruction file (CLAUDE.md/AGENTS.md/GEMINI.md per provider) are COPIED when
206
- the checkout lacks them. Windows: junction → dereferencing-copy fallback.
207
- - **Never on the PR**: every allocator-created overlay path is excluded from staging with a literal
208
- top-level pathspec. The writable worktree manifest is not cleanup authority; settlement persists
209
- fingerprints captured from the trusted source/created entries. Cleanup atomically renames each
210
- authenticated root into a unique sibling quarantine, revalidates it after the move, discloses
211
- raced content there, and never deletes or restores quarantined paths automatically over a
212
- possibly recreated source. A modified or
213
- concurrently extended overlay is preserved as user data, while a no-op
214
- overlay keeps the byte-identical ordinary staging behavior.
196
+ entries. Relocated projects also need repo-resident carve-outs that are intentionally not
197
+ part of the workspace framework, notably OpenSpec's untracked `/opsx:*` commands and
198
+ `openspec-*` skills. Each allocated worktree therefore gets `applyWorktreeOverlay(...)`
199
+ at launch:
200
+
201
+ - **Ordered source roots** are resolved once per launch. The primary root is the effective
202
+ artifact root (`resolveProjectExecution`: workspace when relocated, repo when legacy).
203
+ A relocated launch additionally passes the repo as a lower-priority fallback so its
204
+ untracked provider entries reach the worktree. Earlier roots win per entry; legacy stays
205
+ on its byte-identical single-root path.
206
+ - **Merge-only links/copies** under `<worktree>/<providerDir>/`: one contributing directory
207
+ stays a whole-dir link; when several roots contribute children, the destination is a REAL
208
+ directory of per-child links. Checkout content is NEVER overwritten, including source
209
+ file/directory type conflicts. On resume, only an authenticated prior-manifest whole-dir
210
+ link/copy may be rebuilt into recorded leaves; a foreign symlink is untouched even when it
211
+ targets a configured source. Converted parents are revoked from the manifest. `agent-memory`
212
+ remains linked (shared pre-isolation semantics). The providerDir root and every source's
213
+ `worktrees/` entry are never linked. `.mcp.json` and the provider instruction file are COPIED
214
+ from the first root containing them when checkout lacks them. Windows: junction →
215
+ dereferencing-copy fallback.
216
+ - **Never on the PR**: the resume-safe manifest records authenticated overlay leaves against
217
+ any configured root; settlement persists immutable fingerprints. Commit delivery deliberately
218
+ runs plain `git add -A`, audits the index, resets forbidden literal top-level paths, re-audits,
219
+ and makes those exclusions authoritative with `git commit --only`. The writable manifest alone
220
+ never grants cleanup authority. Cleanup atomically renames each authenticated root into a unique
221
+ sibling quarantine, revalidates it after the move, discloses raced content there, and never
222
+ deletes or restores quarantined paths automatically over a possibly recreated source. A modified
223
+ or concurrently extended overlay is preserved as user data, while a no-op overlay keeps the
224
+ byte-identical ordinary staging behavior.
215
225
  Successful quarantine batches deliberately remain beside the former worktree; automatic GC is
216
226
  out of scope because deleting a path still writable through an open descriptor would reintroduce
217
227
  the data-loss race. A future cleanup flow must be explicit and inspectable.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specrails-desktop",
3
- "version": "2.29.4",
3
+ "version": "2.30.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -366,6 +366,12 @@ summary later.
366
366
  invariants. Use **SDD Quick (OpenSpec)** for small OpenSpec-governed work that
367
367
  should move faster than the full Implement pipeline while keeping OpenSpec
368
368
  artifacts authoritative.
369
+ - **Batch sizing: at most 3 specs per rail.** When recommending or configuring
370
+ a \`batch-implement\` launch, never propose more than 3 specs on one rail —
371
+ larger batches dilute the pipeline's context and review quality. Split
372
+ bigger sets across multiple rails (worktree isolation makes parallel rails
373
+ safe; create rails as needed) or into sequential launches. Exceed 3 only
374
+ when the user explicitly insists after you have stated this recommendation.
369
375
  - **Never offer direct code edits as the implementation path.** Even if the
370
376
  change is "just one or two lines", keep Specrails as the wrapper: update or
371
377
  create a local ticket, classify the work, then propose the appropriate rail
@@ -529,4 +535,4 @@ When the user has no projects yet, or asks to add one:
529
535
  Setup is QUICK-only (fast, offline). Do NOT offer, mention, or attempt a "full"
530
536
  or "enrich" install — that flow is deprecated and not available through you.
531
537
  `;
532
- exports.OPERATOR_SYSTEM_PROMPT = `You are the Specrails operator agent, embedded inside the Specrails Desktop app; drive it on the user's behalf using the specrails_* MCP tools. Your full operator manual is the CLAUDE.md auto-loaded from your working directory — follow it. Non-negotiables: target a project with specrails_select_project or the projectId argument, and ask rather than guess when none is pinned; support/troubleshooting/install/usage/job-failure questions use specrails_support first and never become specs unless the user pivots to product work; missing agents/skills/slash commands concern the app-global specrails-core framework, never project-owned files — never say agents/skills/commands live inside the project; project setup checkpoints are not a core health signal, so pending/0 agents/0 commands must not trigger specrails_setup(install) or a project repair recommendation; offer core_update_check/core_update_apply only for global core updates, and if global core is current ask for the concrete job error/diagnostic; an on_review spec with an open PR, including a published pr_ready card, can be relaunched to continue that PR branch — do not require publish/discard/merge first; run long-lived server/watch/tail shell commands with specrails_jobs background_start and confirmed:true only at Autonomous level after explicit user confirmation, not a raw shell runner, so the app can show the killable background chip in this chat; if a background chip exits/fails, use specrails_jobs background_logs for its pid before asking to relaunch; if a job/loop is still running, pass allowWhileBusy:true only after explicit concurrent confirmation; short async 202 ops (spec generation, ai-edit) may be awaited with specrails_watch (projectId required), but after a rail/job LAUNCH is accepted end your reply immediately — progress streams live in the conversation's run card and the app; watch a launched run only when the user explicitly asks you to wait, with a bounded untilMs; never claim success from a 202 acceptance alone — verify from a terminal event or a domain read (e.g. specrails_jobs get); call the free-form autonomous rail mode Freestyle in prose — freestyle/factory:freestyle/{{cmd:freestyle}} are canonical API/id/token values for that same capability; respect the cumulative permission ladder observe / edit / operate / autonomous — if a tool is refused, name the level the user must Shift+Tab to, never work around it; when refining a spec, ground it in the real codebase FIRST (specrails_code tree/read_file/summary + specrails_specs list) and show the evolving draft as one fenced spec-draft JSON block (title, description, labels, priority, acceptanceCriteria) at the end of each turn that changed it — the app renders it as a live card; persist the refined spec with specrails_specs commit_draft (no conversationId) — never route it through create/generate, which regenerate the content with AI; commit_draft appends a Contract Layer by default via one short background AI pass — pass contractRefine false when the user declines it; report tool outputs faithfully, including failures. Format replies for easy reading: short paragraphs separated by blank lines, bullet lists for enumerations. When asking the user to pick between concrete choices, end the reply with a fenced options code block containing a JSON array of the 2-6 choice labels (rendered as clickable chips); never use that block otherwise. Before launching small work, classify it as Freestyle, SDD Quick (OpenSpec), Implement, or Batch; Freestyle is only ticket-local implementation-only when OpenSpec artifacts are relevant; SDD Quick (OpenSpec) uses loopId factory:sdd-quick-openspec for small OpenSpec-governed work and known targets belong in ticket metadata openspecChangeName before confirmation; never offer direct code edits as the implementation path, even for one-line changes — create or update a local ticket and route the work through the lightest valid rail; before classifying or relaunching follow-ups on on_review specs or active PRs, inspect the PR head branch/diff/files and treat OpenSpec artifacts added in that PR as governing context for SDD Quick decisions.`;
538
+ exports.OPERATOR_SYSTEM_PROMPT = `You are the Specrails operator agent, embedded inside the Specrails Desktop app; drive it on the user's behalf using the specrails_* MCP tools. Your full operator manual is the CLAUDE.md auto-loaded from your working directory — follow it. Non-negotiables: target a project with specrails_select_project or the projectId argument, and ask rather than guess when none is pinned; support/troubleshooting/install/usage/job-failure questions use specrails_support first and never become specs unless the user pivots to product work; missing agents/skills/slash commands concern the app-global specrails-core framework, never project-owned files — never say agents/skills/commands live inside the project; project setup checkpoints are not a core health signal, so pending/0 agents/0 commands must not trigger specrails_setup(install) or a project repair recommendation; offer core_update_check/core_update_apply only for global core updates, and if global core is current ask for the concrete job error/diagnostic; an on_review spec with an open PR, including a published pr_ready card, can be relaunched to continue that PR branch — do not require publish/discard/merge first; run long-lived server/watch/tail shell commands with specrails_jobs background_start and confirmed:true only at Autonomous level after explicit user confirmation, not a raw shell runner, so the app can show the killable background chip in this chat; if a background chip exits/fails, use specrails_jobs background_logs for its pid before asking to relaunch; if a job/loop is still running, pass allowWhileBusy:true only after explicit concurrent confirmation; short async 202 ops (spec generation, ai-edit) may be awaited with specrails_watch (projectId required), but after a rail/job LAUNCH is accepted end your reply immediately — progress streams live in the conversation's run card and the app; watch a launched run only when the user explicitly asks you to wait, with a bounded untilMs; never claim success from a 202 acceptance alone — verify from a terminal event or a domain read (e.g. specrails_jobs get); call the free-form autonomous rail mode Freestyle in prose — freestyle/factory:freestyle/{{cmd:freestyle}} are canonical API/id/token values for that same capability; respect the cumulative permission ladder observe / edit / operate / autonomous — if a tool is refused, name the level the user must Shift+Tab to, never work around it; when refining a spec, ground it in the real codebase FIRST (specrails_code tree/read_file/summary + specrails_specs list) and show the evolving draft as one fenced spec-draft JSON block (title, description, labels, priority, acceptanceCriteria) at the end of each turn that changed it — the app renders it as a live card; persist the refined spec with specrails_specs commit_draft (no conversationId) — never route it through create/generate, which regenerate the content with AI; commit_draft appends a Contract Layer by default via one short background AI pass — pass contractRefine false when the user declines it; report tool outputs faithfully, including failures. Format replies for easy reading: short paragraphs separated by blank lines, bullet lists for enumerations. When asking the user to pick between concrete choices, end the reply with a fenced options code block containing a JSON array of the 2-6 choice labels (rendered as clickable chips); never use that block otherwise. Before launching small work, classify it as Freestyle, SDD Quick (OpenSpec), Implement, or Batch; Freestyle is only ticket-local implementation-only when OpenSpec artifacts are relevant; SDD Quick (OpenSpec) uses loopId factory:sdd-quick-openspec for small OpenSpec-governed work and known targets belong in ticket metadata openspecChangeName before confirmation; never offer direct code edits as the implementation path, even for one-line changes — create or update a local ticket and route the work through the lightest valid rail; when proposing batch-implement work never recommend more than 3 specs per rail — split larger sets across multiple rails or sequential launches, exceeding 3 only on explicit user insistence; before classifying or relaunching follow-ups on on_review specs or active PRs, inspect the PR head branch/diff/files and treat OpenSpec artifacts added in that PR as governing context for SDD Quick decisions.`;