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
@@ -5,17 +5,32 @@
5
5
  // BUILDER_SYSTEM_PROMPT is the compact equivalent passed via --system-prompt
6
6
  // to providers that support a dedicated system-prompt argument.
7
7
  //
8
+ // The spec DEPTH bar comes from the shared premium contract
9
+ // (spec-contract-prompt.ts) so the Builder, M2+ generation and the agent's
10
+ // super-spec mode never drift; generation itself is app-driven and batched
11
+ // (blueprint-generation.ts) so depth is no longer capped by one reply's
12
+ // output budget.
13
+ //
8
14
  // BYTE-STABILITY CONTRACT: both constants must stay static (no timestamps, no
9
- // interpolation, no live data) so provider prompt caching can work on turn 2+.
15
+ // interpolation of live data) so provider prompt caching can work on turn 2+.
10
16
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.BUILDER_SYSTEM_PROMPT = exports.BUILDER_INSTRUCTIONS = void 0;
17
+ exports.SPEC_DEPTH_FLOORS = exports.SINGLE_RESPONSE_MODE_LINE = exports.BUILDER_SYSTEM_PROMPT = exports.BUILDER_INSTRUCTIONS = void 0;
18
+ exports.buildSnapshotRepairPrompt = buildSnapshotRepairPrompt;
19
+ const spec_contract_prompt_1 = require("./spec-contract-prompt");
20
+ Object.defineProperty(exports, "SPEC_DEPTH_FLOORS", { enumerable: true, get: function () { return spec_contract_prompt_1.SPEC_DEPTH_FLOORS; } });
21
+ const blueprint_generation_1 = require("./blueprint-generation");
22
+ const EXAMPLE_SPEC_JSON = JSON.stringify(spec_contract_prompt_1.PREMIUM_SCAFFOLD_EXAMPLE, null, 2)
23
+ .split('\n')
24
+ .map((line, i) => (i === 0 ? line : ` ${line}`))
25
+ .join('\n');
12
26
  exports.BUILDER_INSTRUCTIONS = `# Specrails Project Builder
13
27
 
14
28
  You are the Specrails Project Builder: a conversational product architect in
15
29
  Specrails Desktop. Help the user turn a raw idea into an approved project
16
- blueprint and then a rich Milestone-1 backlog. No project or repository exists
17
- yet: you cannot inspect code, claim that paths exist, or create anything on
18
- disk. The app performs the commit after the user reviews the result.
30
+ blueprint and then a rich, premium Milestone-1 backlog. No project or
31
+ repository exists yet: you cannot inspect code, claim that paths exist, or
32
+ create anything on disk. The app performs the commit after the user reviews
33
+ the result.
19
34
 
20
35
  Conversational prose follows the user's language. Every generated spec field
21
36
  (\`title\`, \`shortSummary\`, \`description\`, \`acceptanceCriteria\`, and
@@ -29,10 +44,21 @@ blocks. Rules:
29
44
  - Every block is a FULL snapshot of the whole blueprint, never a delta. The
30
45
  app uses the last valid block, so re-emit everything currently known.
31
46
  - Emit at most one block per message and put it at the END of the message.
32
- - Use valid JSON: double quotes, no comments, and no trailing commas.
47
+ Nothing follows the closing fence.
48
+ - The fence language is EXACTLY \`blueprint-draft\` (\`\`\`blueprint-draft).
49
+ A \`\`\`json fence or a bare fence is NOT a snapshot: the app would show the
50
+ raw JSON in the chat and the panel would stay empty.
51
+ - Use strictly valid JSON: double quotes only, no comments, no trailing
52
+ commas, no nested code fences inside the block, and no prose inside it.
53
+ - Inside JSON strings escape every newline as \\n and every double quote as
54
+ \\". Markdown descriptions therefore travel as ONE line per string.
55
+ - The app validates every block. If it tells you a block was rejected or cut
56
+ off, answer with ONLY the complete corrected snapshot — no prose.
57
+ - When nothing changed since your last accepted snapshot you may omit the
58
+ block; the app keeps the last accepted state.
33
59
  - All keys below are required except \`stack.notes\` and \`dependsOnIndex\`:
34
60
 
35
- \`\`\`json
61
+ \`\`\`blueprint-draft
36
62
  {
37
63
  "blueprintVersion": 1,
38
64
  "product": { "name": "...", "pitch": "...", "audience": "..." },
@@ -46,19 +72,16 @@ blocks. Rules:
46
72
  ],
47
73
  "specsComplete": false,
48
74
  "m1Specs": [
49
- {
50
- "kind": "scaffold",
51
- "title": "Scaffold the runnable project foundation",
52
- "shortSummary": "Initialize a reproducible foundation that every later walking-skeleton slice can build on.",
53
- "description": "## Problem Statement\\nThe approved project has a README and selected stack, but contributors cannot yet install, run, or verify a working application.\\n\\n## Proposed Solution\\nInitialize the selected stack with deterministic development, build, test, and CI commands while preserving the existing README.\\n\\n## Out of Scope\\n- Product features beyond a minimal runnable shell\\n- Production deployment and environment provisioning\\n\\n## Technical Considerations\\n- Pin compatible runtime and dependency versions for reproducible setup\\n- Keep the first application and test boundaries minimal but executable\\n\\n## Estimated Complexity\\nMedium - the foundation must align local development, automated tests, and CI without implementing product behavior.",
54
- "acceptanceCriteria": ["A clean checkout installs successfully with the documented command.", "The development command starts the minimal application without an error.", "The automated test command passes from a clean checkout.", "CI executes the build and test commands on every proposed change."],
55
- "priority": "high",
56
- "labels": ["M1", "foundation"]
57
- }
75
+ ${EXAMPLE_SPEC_JSON}
58
76
  ]
59
77
  }
60
78
  \`\`\`
61
79
 
80
+ The example above is the mandatory first spec at the DEPTH every spec must
81
+ reach: a narrative problem, a journey plus the five \`###\` sub-blocks, three
82
+ placed exclusions, labelled technical bullets and six behavioural criteria.
83
+ Shorter is a defect, not a style.
84
+
62
85
  ## Interview and approval gate
63
86
 
64
87
  - Ask questions for at most three assistant turns. After that, propose concrete
@@ -83,36 +106,9 @@ blocks. Rules:
83
106
  - If an idea is beyond the walking skeleton, park it as an M2+ title rather
84
107
  than expanding M1 into a waterfall.
85
108
 
86
- ## Canonical detailed-spec contract
87
-
88
- Every detailed M1 spec must satisfy all of these rules:
89
-
90
- - \`kind\` is exactly \`scaffold\`, \`feature\`, or \`verification\`.
91
- - \`title\` is concise, action-oriented, unique in the batch, and in English.
92
- - \`shortSummary\` is one useful English sentence, no more than 240 characters.
93
- - \`description\` is English markdown with exactly these five \`##\` headings,
94
- once each and in this order:
95
- 1. \`## Problem Statement\` — who has the problem, when, why it matters, and
96
- the user or product value of solving it.
97
- 2. \`## Proposed Solution\` — the concrete behavior and planned components or
98
- contracts that deliver the thin outcome.
99
- 3. \`## Out of Scope\` — at least two honest bullets naming adjacent work that
100
- this spec deliberately defers.
101
- 4. \`## Technical Considerations\` — at least two bullets covering the selected stack,
102
- planned components and interfaces, risks, invariants, testing strategy,
103
- and dependencies on earlier specs where relevant.
104
- 5. \`## Estimated Complexity\` — Low, Medium, High, or Very High plus one
105
- sentence explaining what drives the estimate.
106
- - Do NOT put an \`## Acceptance Criteria\` heading in \`description\`. Criteria
107
- live only in the separate \`acceptanceCriteria\` array; the app folds them
108
- into the ticket deterministically at commit time.
109
- - \`acceptanceCriteria\` contains 4–10 non-empty, independent, testable outcomes,
110
- not implementation steps. Cover the intended behavior, observable failure or
111
- edge cases, and relevant automated verification.
112
- - \`priority\` is exactly \`low\`, \`medium\`, \`high\`, or \`critical\` and
113
- reflects delivery urgency/risk rather than implementation complexity.
114
- - \`labels\` includes \`M1\` plus at least one concise domain label such as
115
- \`frontend\`, \`api\`, \`data\`, \`auth\`, \`testing\`, or \`infra\`.
109
+ ## Canonical detailed-spec contract (premium bar)
110
+
111
+ ${(0, spec_contract_prompt_1.premiumSpecContract)('day0')}
116
112
  - \`m1Specs[0]\` is \`kind: "scaffold"\` and omits \`dependsOnIndex\`. It
117
113
  states that the repository already contains a README and defines concrete
118
114
  install, run, test, and CI outcomes for the selected stack.
@@ -120,30 +116,62 @@ Every detailed M1 spec must satisfy all of these rules:
120
116
  item, and the integer must point strictly backward to an earlier array index.
121
117
  Never point forward or to the same spec.
122
118
 
119
+ ## Generation protocol (app-driven batches)
120
+
121
+ Premium depth does not fit ten specs in one reply, so the APP drives
122
+ generation in turns. Follow the phase the app is in:
123
+
124
+ 1. **Outline** — right after approval, emit ONE \`blueprint-draft\` FULL
125
+ snapshot listing the whole 5–10-spec walking skeleton with every spec's
126
+ \`kind\`, \`title\`, \`shortSummary\`, \`priority\`, \`labels\` and
127
+ \`dependsOnIndex\` decided, but \`description: ""\` and
128
+ \`acceptanceCriteria: []\` for every spec, and \`specsComplete: false\`.
129
+ One line of prose at most ("Outline ready — writing the specs next.").
130
+ 2. **Detail turns** — the app then sends \`APP CONTINUE\` naming
131
+ ${blueprint_generation_1.SPECS_PER_DETAIL_TURN} specs by index and title. Reply with ONE fenced
132
+ \`spec-detail\` block per named spec and nothing else:
133
+ \`{ "index": <0-based m1Specs index>, "spec": { …the complete premium spec… } }\`.
134
+ Write each spec at full depth; keep the outline's kind, priority, labels and
135
+ dependency. Do not re-emit the blueprint-draft block on detail turns.
136
+ 3. **Audit turn** — when the app sends \`APP AUDIT\`, audit the whole batch
137
+ against the contract and reply with ONE fenced \`spec-audit\` block:
138
+ \`{ "specsComplete": true|false, "issues": [...], "fixes": [ { "index": n, "spec": {…} } ] }\`
139
+ — complete corrected specs in \`fixes\`, \`specsComplete: true\` only when
140
+ every spec passes.
141
+ 4. **Corrections** — when the app lists audit problems, reply with
142
+ \`spec-detail\` blocks for ONLY the affected specs (complete corrected spec
143
+ objects), never the whole snapshot.
144
+
145
+ Only when the app says \`GENERATION MODE: single response\` (a provider that
146
+ cannot resume sessions) do you instead emit every spec complete inside one
147
+ \`blueprint-draft\` snapshot in one reply, prioritising per-spec depth over
148
+ prose, with \`specsComplete: true\` only after self-auditing all of them. If a
149
+ complete audited batch cannot be produced in that mode, keep
150
+ \`m1Specs: []\` and \`specsComplete: false\` and explain what blocked it.
151
+
123
152
  ## Day-0 grounding
124
153
 
125
154
  There is no codebase to inspect. Ground technical details in the selected
126
155
  stack, planned components, intended contracts/data shapes, known risks, and
127
- inter-spec dependencies. Never invent or imply existing repository paths,
128
- filenames, modules, functions, identifiers, migrations, or test files.
129
-
130
- ## Generation and completion
131
-
132
- - After approval, generate the complete 5–10-spec walking skeleton in one
133
- response and one FULL snapshot. Never expose a partial detailed-spec batch as
134
- a result the user must manually ask you to continue.
135
- - If the complete batch cannot fit or cannot pass validation, keep
136
- \`m1Specs: []\` and \`specsComplete: false\`, explain what prevented completion,
137
- and wait for a fresh generation request. Do not emit partially generated specs.
138
- - Before setting \`specsComplete: true\`, self-audit the entire snapshot: schema
139
- fields, exact five headings and order, English content, 4–10 criteria, valid
140
- priority, M1 plus domain labels, unique titles, scaffold first, and strictly
141
- backward dependencies. Set it true only if every spec passes. If anything
142
- fails, keep it false and repair the snapshot first.
156
+ inter-spec dependencies. Label every module, path, table or route you
157
+ introduce as planned; never invent or imply an EXISTING repository path,
158
+ filename, module, function, identifier, migration, or test file.
143
159
 
144
160
  ## Boundaries
145
161
 
146
162
  - Never claim to have created files, repositories, tickets, or projects.
147
163
  - Never include secrets, real API keys, or license-restricted code in specs.
148
164
  `;
149
- exports.BUILDER_SYSTEM_PROMPT = `You are the Specrails Project Builder. No repository exists yet. Converse in the user's language, but write every spec field in English. Emit at most one final fenced blueprint-draft JSON FULL snapshot per message. Before explicit blueprint approval or a direct request to generate specs, populate product/coreFlow/platform/stack/assumptions/milestones but ALWAYS keep m1Specs: [] and specsComplete: false; "surprise me" also proposes dimensions only and asks for approval. After approval generate the complete 5-10-spec M1 walking skeleton in one response and one FULL snapshot; never expose a partial batch the user must ask you to continue. If the complete batch cannot be produced and audited, emit m1Specs: [] with specsComplete: false instead of partial specs. Each spec has kind (scaffold|feature|verification), unique action-oriented title, one-sentence shortSummary <=240 characters, description with exactly these headings once and in order: ## Problem Statement, ## Proposed Solution, ## Out of Scope, ## Technical Considerations, ## Estimated Complexity; Out of Scope and Technical Considerations each contain at least two bullets; a separate acceptanceCriteria array of 4-10 independent testable outcomes (never an ## Acceptance Criteria section in description); priority low|medium|high|critical; labels including M1 and a domain label; and optional dependsOnIndex pointing strictly backward. m1Specs[0] is kind scaffold, omits dependsOnIndex, notes the repo already contains a README, and defines install/run/test/CI outcomes. Ground day-0 content in the selected stack, planned components/contracts, risks, tests, and spec dependencies; NEVER invent paths or existing identifiers. M2+ keeps title-only plannedSpecs. Set specsComplete true only after auditing all fields, five headings, English content, 4-10 criteria, labels, scaffold, unique titles, and dependencies. Never claim disk changes.`;
165
+ exports.BUILDER_SYSTEM_PROMPT = `You are the Specrails Project Builder. No repository exists yet. Converse in the user's language, but write every spec field in English. Emit at most one final fenced blueprint-draft JSON FULL snapshot per message (fence language EXACTLY blueprint-draft — never json or a bare fence), at the END of the message, as strictly valid JSON (double quotes, newlines inside strings escaped as \\n, inner quotes escaped as \\", no trailing commas, no comments, no nested code fences, nothing after the closing fence). If the app reports that a block was rejected or cut off, reply with ONLY the complete corrected block. Before explicit blueprint approval or a direct request to generate specs, populate product/coreFlow/platform/stack/assumptions/milestones but ALWAYS keep m1Specs: [] and specsComplete: false; "surprise me" also proposes dimensions only and asks for approval. Generation is app-driven and batched: after approval emit ONE blueprint-draft OUTLINE snapshot (all 5-10 M1 specs with kind/title/shortSummary/priority/labels/dependsOnIndex, description "" and acceptanceCriteria [] for every spec, specsComplete false); when the app sends APP CONTINUE reply with one fenced spec-detail block per named spec ({ "index": n, "spec": {...complete spec...} }) and nothing else; when it sends APP AUDIT reply with one fenced spec-audit block ({ "specsComplete", "issues", "fixes": [{ "index", "spec" }] }); when it lists audit problems reply with spec-detail blocks for the affected specs only. Only if the app says GENERATION MODE: single response, emit every spec complete in one blueprint-draft snapshot with specsComplete true after self-auditing all of them, else m1Specs: [] with specsComplete: false. ${(0, spec_contract_prompt_1.premiumSpecContractCompact)('day0')} m1Specs[0] is kind scaffold, omits dependsOnIndex, notes the repo already contains a README, and defines install/run/test/CI outcomes; dependsOnIndex points strictly backward. M2+ keeps title-only plannedSpecs. Never claim disk changes.`;
166
+ const REPAIR_PROMPTS = {
167
+ invalid_json: `APP CHECK: your last blueprint-draft block was REJECTED because it is not valid JSON. Re-emit the COMPLETE snapshot now — every field, every spec — as strictly valid JSON: double quotes only, newlines inside strings escaped as \\n, inner double quotes escaped as \\", no trailing commas, no comments, no nested code fences, nothing after the closing fence. Reply with ONLY the block.`,
168
+ truncated: `APP CHECK: your last blueprint-draft block was CUT OFF before its closing fence — the reply exceeded the output limit, so the app received nothing. Do NOT shorten the specs. If this was the outline, re-emit the complete outline snapshot (every spec with an empty description and empty criteria). If you were writing detailed specs in one reply, re-emit the snapshot with empty bodies as an OUTLINE and let the app request the detail spec by spec. Reply with ONLY the block, no prose.`,
169
+ quality: `APP CHECK: the deterministic audit rejected the batch. Fix EXACTLY the problems listed below — enrich, never trim — and reply with ONE fenced spec-detail block per AFFECTED spec ({ "index": <0-based m1Specs index>, "spec": { …the complete corrected spec… } }) and nothing else; do not re-emit the whole snapshot. Untouched specs keep their current content.`,
170
+ };
171
+ function buildSnapshotRepairPrompt(kind, detail) {
172
+ const base = REPAIR_PROMPTS[kind];
173
+ const trimmed = detail.trim();
174
+ return trimmed ? `${base}\n\nDetails:\n${trimmed}` : base;
175
+ }
176
+ /** Appended to the per-turn prompt for providers that cannot resume sessions. */
177
+ exports.SINGLE_RESPONSE_MODE_LINE = 'GENERATION MODE: single response — this provider cannot resume sessions, so when you generate the specs emit every spec complete inside ONE blueprint-draft snapshot.';
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isProjectBuilderEnabled = isProjectBuilderEnabled;
4
4
  exports.createBlueprintRouter = createBlueprintRouter;
5
5
  const express_1 = require("express");
6
+ const blueprint_chat_manager_1 = require("./blueprint-chat-manager");
6
7
  const providers_1 = require("./providers");
7
8
  const runtime_1 = require("./providers/runtime");
8
9
  const blueprint_store_1 = require("./blueprint-store");
10
+ const blueprint_spec_quality_1 = require("./blueprint-spec-quality");
9
11
  function isProjectBuilderEnabled() {
10
12
  return process.env.SPECRAILS_PROJECT_BUILDER !== 'false';
11
13
  }
@@ -48,7 +50,15 @@ function createBlueprintRouter(deps) {
48
50
  efforts: toolPolicy ? (0, providers_1.reasoningEffortsForModel)(adapter, model) : [],
49
51
  });
50
52
  });
51
- router.get('/conversations', (_req, res) => {
53
+ // `?resumable=1` → the "continue where you left off" list: unfinished
54
+ // (never committed) conversations with at least one assistant reply, each
55
+ // with a snapshot summary — the durable answer to "I closed the panel and
56
+ // lost the specs".
57
+ router.get('/conversations', (req, res) => {
58
+ if (req.query.resumable === '1' || req.query.resumable === 'true') {
59
+ res.json({ conversations: (0, blueprint_store_1.listResumableBlueprintConversations)(desktopDb) });
60
+ return;
61
+ }
52
62
  res.json({ conversations: (0, blueprint_store_1.listBlueprintConversations)(desktopDb) });
53
63
  });
54
64
  router.post('/conversations', (req, res) => {
@@ -62,13 +72,36 @@ function createBlueprintRouter(deps) {
62
72
  const conversation = (0, blueprint_store_1.createBlueprintConversation)(desktopDb, { provider, model });
63
73
  res.status(201).json({ conversation });
64
74
  });
75
+ // Rehydration payload: transcript (block-only replies hidden — their raw
76
+ // payload stays in the row), the persisted snapshot pair, and its status so
77
+ // the panel resumes with the same readiness/repair affordances it had live.
65
78
  router.get('/conversations/:id', (req, res) => {
66
79
  const conversation = (0, blueprint_store_1.getBlueprintConversation)(desktopDb, String(req.params.id));
67
80
  if (!conversation) {
68
81
  res.status(404).json({ error: 'conversation not found' });
69
82
  return;
70
83
  }
71
- res.json({ conversation, messages: (0, blueprint_store_1.listBlueprintMessages)(desktopDb, conversation.id) });
84
+ const snapshot = (0, blueprint_store_1.getBlueprintSnapshot)(desktopDb, conversation.id);
85
+ const audit = snapshot.rawBlueprint !== null ? (0, blueprint_spec_quality_1.auditRawBlueprintForM1)(snapshot.rawBlueprint) : null;
86
+ const messages = (0, blueprint_store_1.listBlueprintMessages)(desktopDb, conversation.id)
87
+ .filter((m) => m.content.trim() !== '')
88
+ .map(({ raw_content: _raw, ...rest }) => rest);
89
+ res.json({
90
+ conversation,
91
+ messages,
92
+ blueprint: snapshot.blueprint,
93
+ rawBlueprint: snapshot.rawBlueprint,
94
+ snapshot: snapshot.issue
95
+ ? { status: 'rejected', reason: snapshot.issue.reason, detail: snapshot.issue.detail }
96
+ : snapshot.blueprint
97
+ ? {
98
+ status: 'accepted',
99
+ claimsComplete: audit?.claimsComplete ?? false,
100
+ ...(audit && audit.claimsComplete && !audit.valid ? { qualityIssues: audit.issues } : {}),
101
+ }
102
+ : { status: 'none' },
103
+ snapshotUpdatedAt: snapshot.snapshotUpdatedAt,
104
+ });
72
105
  });
73
106
  router.patch('/conversations/:id', (req, res) => {
74
107
  const conversation = (0, blueprint_store_1.getBlueprintConversation)(desktopDb, String(req.params.id));
@@ -177,9 +210,28 @@ function createBlueprintRouter(deps) {
177
210
  }
178
211
  reasoningEffort = body.reasoning_effort;
179
212
  }
180
- void manager.sendMessage(conversation.id, text, { model, reasoningEffort });
213
+ const intent = blueprint_chat_manager_1.BUILDER_TURN_INTENTS.includes(String(body.intent)) ? body.intent : undefined;
214
+ void manager.sendMessage(conversation.id, text, { model, reasoningEffort, ...(intent ? { intent } : {}) });
181
215
  res.status(202).json({ accepted: true });
182
216
  });
217
+ // Manual snapshot repair: re-ask the Builder for its last block (rejected
218
+ // JSON / cut-off reply) or for the audit fixes (claimed complete, gate
219
+ // disagreed). 202 + `blueprint.repairing` → streams → `blueprint.done`.
220
+ router.post('/conversations/:id/repair-snapshot', async (req, res) => {
221
+ const conversation = (0, blueprint_store_1.getBlueprintConversation)(desktopDb, String(req.params.id));
222
+ if (!conversation) {
223
+ res.status(404).json({ error: 'conversation not found' });
224
+ return;
225
+ }
226
+ const rawEffort = (req.body ?? {}).reasoningEffort;
227
+ const outcome = await manager.repairSnapshot(conversation.id, typeof rawEffort === 'string' ? { reasoningEffort: rawEffort } : {});
228
+ if (!outcome.ok) {
229
+ const status = outcome.reason === 'unknown_conversation' ? 404 : 409;
230
+ res.status(status).json({ error: outcome.reason });
231
+ return;
232
+ }
233
+ res.status(202).json({ accepted: true, kind: outcome.kind });
234
+ });
183
235
  router.post('/conversations/:id/abort', (req, res) => {
184
236
  const conversation = (0, blueprint_store_1.getBlueprintConversation)(desktopDb, String(req.params.id));
185
237
  if (!conversation) {
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ // Premium-bar spec fixtures shared by the server test suites (and usable by
3
+ // any seam that needs a gate-valid detailed spec). Every builder here passes
4
+ // `analyzeBuilderSpecBatch` under the raised floors (spec-contract-prompt.ts
5
+ // SPEC_DEPTH_FLOORS): narrative problem, sub-blocked solution, ≥3 / ≥5
6
+ // bullets, reasoned complexity, 6 behavioural criteria.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.premiumDescription = premiumDescription;
9
+ exports.premiumCriteria = premiumCriteria;
10
+ exports.premiumSpec = premiumSpec;
11
+ function premiumDescription(opts = {}) {
12
+ const subject = opts.subject ?? 'the core workflow';
13
+ return [
14
+ '## Problem Statement',
15
+ `A home cook who opens the product today has no way to complete ${subject} end to end: the flow stops at the first screen, nothing is persisted, and nobody can tell whether the slice works. It matters now because every later Milestone-1 spec is defined as building on this slice.${opts.readme ? ' The repository already contains a README and nothing else.' : ''} A good outcome is a thin, runnable, tested version of ${subject} that a contributor can run from a clean checkout and a reviewer can exercise without guidance.`,
16
+ '',
17
+ '## Proposed Solution',
18
+ `1. The user opens the entry screen for ${subject} and sees the empty state.`,
19
+ '2. They provide the minimal input and submit it.',
20
+ '3. The system validates, persists and echoes the result on the next screen.',
21
+ '4. Reloading shows the persisted state; invalid input shows an inline error.',
22
+ '',
23
+ '### User experience',
24
+ 'One screen with an empty state, a loading indicator while saving, an inline validation error and a success confirmation; the last successful input is remembered between visits.',
25
+ '',
26
+ '### Data model',
27
+ '`Entry { id: uuid, createdAt: timestamp, payload: text (1–500 chars), status: "draft" | "saved" }` (planned).',
28
+ '',
29
+ '### Interfaces & contracts',
30
+ '`POST /entries { payload } → 201 { id, status }`; `400 { error: "invalid_payload" }` on validation failure; `GET /entries/:id → 200 | 404` (planned).',
31
+ '',
32
+ '### Planned modules',
33
+ '- `src/features/entries/EntryScreen (planned)` — the screen and its states.',
34
+ '- `src/features/entries/entries-api (planned)` — request/response handling and validation.',
35
+ '- `src/data/entries-repository (planned)` — persistence behind one interface.',
36
+ '',
37
+ '### Key decisions',
38
+ '- Persist through a repository interface instead of calling the database from the screen, so later slices swap storage without touching UI; rejected: direct calls for speed.',
39
+ '- Validate on both client and server; rejected: server-only validation because the empty-state UX needs immediate feedback.',
40
+ '',
41
+ '## Out of Scope',
42
+ '- Social sharing and collaboration — deferred to the "Sharing" milestone because it needs accounts first.',
43
+ '- Advanced personalization and recommendations — deferred to a later milestone once real usage data exists.',
44
+ '- Offline editing — deferred; the walking skeleton assumes connectivity.',
45
+ '',
46
+ '## Technical Considerations',
47
+ '- **Architecture**: screen → api module → repository; no cross-layer imports.',
48
+ '- **Data & contracts**: the `Entry` shape and the `/entries` routes are frozen for later slices.',
49
+ '- **Failure handling & edge cases**: empty payload, over-long payload and a missing id each produce a distinct, tested error.',
50
+ '- **Security & privacy**: input is length-limited and escaped before rendering; no personal data beyond the payload.',
51
+ '- **Testing strategy**: unit — validation rules; integration — repository round trip; e2e — submit, reload, error path.',
52
+ '- **Dependencies**: builds on the scaffold spec; nothing else.',
53
+ '- **Risks & mitigations**: persistence schema churn — mitigated by the repository interface.',
54
+ '',
55
+ '## Estimated Complexity',
56
+ 'Medium — the slice crosses UI, validation and persistence; the main uncertainty is the persistence setup on the chosen stack.',
57
+ ].join('\n');
58
+ }
59
+ function premiumCriteria(seed = '') {
60
+ const s = seed ? ` (${seed})` : '';
61
+ return [
62
+ `Given a clean checkout, when the user completes the primary happy path${s}, then the result is persisted and shown on the next screen.`,
63
+ `When the user submits an empty or over-long payload${s}, then an inline validation error names the rule and nothing is persisted.`,
64
+ `When the user opens the screen with no data${s}, then a deliberate empty state explains what to do first.`,
65
+ `When the page reloads after a successful save${s}, then the persisted state is shown again without re-entry.`,
66
+ `When the persistence layer is unavailable${s}, then the user sees an actionable error and the input is kept for retry.`,
67
+ `Automated unit, integration and end-to-end tests cover the happy path, the validation error and the persistence failure${s}, and pass in CI.`,
68
+ ];
69
+ }
70
+ function premiumSpec(index, over = {}) {
71
+ const scaffold = index === 0;
72
+ return {
73
+ kind: scaffold ? 'scaffold' : 'feature',
74
+ title: scaffold ? 'Scaffold the runnable project foundation' : `Deliver workflow slice ${index}`,
75
+ shortSummary: scaffold
76
+ ? 'Turn the README-only repository into an installable, runnable, testable application shell with CI.'
77
+ : `Deliver an independently testable workflow slice ${index} on top of the scaffold.`,
78
+ description: premiumDescription({ readme: scaffold, subject: scaffold ? 'the project foundation' : `workflow slice ${index}` }),
79
+ acceptanceCriteria: premiumCriteria(scaffold ? 'foundation' : `slice ${index}`),
80
+ priority: 'medium',
81
+ labels: ['M1', scaffold ? 'foundation' : 'workflow'],
82
+ ...(index > 0 ? { dependsOnIndex: index - 1 } : {}),
83
+ ...over,
84
+ };
85
+ }
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BUILDER_SPEC_HEADINGS = void 0;
3
+ exports.M1_BATCH_QUALITY_OPTIONS = exports.BUILDER_SPEC_HEADINGS = void 0;
4
4
  exports.analyzeBuilderSpecBatch = analyzeBuilderSpecBatch;
5
5
  exports.firstBuilderSpecQualityDetail = firstBuilderSpecQualityDetail;
6
6
  exports.analyzeTypedBuilderSpecs = analyzeTypedBuilderSpecs;
7
+ exports.auditRawBlueprintForM1 = auditRawBlueprintForM1;
8
+ exports.formatQualityIssuesForModel = formatQualityIssuesForModel;
9
+ const spec_contract_prompt_1 = require("./spec-contract-prompt");
7
10
  exports.BUILDER_SPEC_HEADINGS = [
8
11
  'Problem Statement',
9
12
  'Proposed Solution',
@@ -14,8 +17,14 @@ exports.BUILDER_SPEC_HEADINGS = [
14
17
  const VALID_KINDS = new Set(['scaffold', 'feature', 'verification']);
15
18
  const VALID_PRIORITIES = new Set(['critical', 'high', 'medium', 'low']);
16
19
  const PLACEHOLDER_CRITERION = /^(?:todo|tbd|n\/?a|works?|test(?: it)?|criterion|acceptance criterion)$/i;
17
- function issue(issues, specIndex, field, code, message) {
18
- issues.push({ specIndex, field, code, message });
20
+ function issue(issues, specIndex, field, code, message, params = {}) {
21
+ issues.push({
22
+ specIndex,
23
+ field,
24
+ code,
25
+ message,
26
+ params: specIndex === null ? params : { n: specIndex + 1, ...params },
27
+ });
19
28
  }
20
29
  function record(value) {
21
30
  return value && typeof value === 'object' && !Array.isArray(value)
@@ -51,7 +60,7 @@ function analyzeBuilderSpecBatch(input, options) {
51
60
  issue(issues, null, 'specsComplete', 'batch_incomplete', 'generation is not marked complete');
52
61
  }
53
62
  if (input.specs.length < options.minSpecs || input.specs.length > options.maxSpecs) {
54
- issue(issues, null, 'm1Specs', 'spec_count', `requires ${options.minSpecs}-${options.maxSpecs} specs (received ${input.specs.length})`);
63
+ issue(issues, null, 'm1Specs', 'spec_count', `requires ${options.minSpecs}-${options.maxSpecs} specs (received ${input.specs.length})`, { min: options.minSpecs, max: options.maxSpecs, count: input.specs.length });
55
64
  }
56
65
  const titles = new Map();
57
66
  input.specs.forEach((raw, specIndex) => {
@@ -79,7 +88,7 @@ function analyzeBuilderSpecBatch(input, options) {
79
88
  const key = normalized(title);
80
89
  const prior = titles.get(key);
81
90
  if (prior !== undefined) {
82
- issue(issues, specIndex, 'title', 'duplicate', `spec ${humanIndex} duplicates spec ${prior + 1} title`);
91
+ issue(issues, specIndex, 'title', 'duplicate', `spec ${humanIndex} duplicates spec ${prior + 1} title`, { other: prior + 1 });
83
92
  }
84
93
  else {
85
94
  titles.set(key, specIndex);
@@ -101,10 +110,10 @@ function analyzeBuilderSpecBatch(input, options) {
101
110
  : [];
102
111
  const normalizedLabels = new Set(labels.map((value) => value.toUpperCase()));
103
112
  if (!normalizedLabels.has(label)) {
104
- issue(issues, specIndex, 'labels', 'milestone_label', `spec ${humanIndex} labels must include ${label}`);
113
+ issue(issues, specIndex, 'labels', 'milestone_label', `spec ${humanIndex} labels must include ${label}`, { label });
105
114
  }
106
115
  if (labels.every((value) => value.toUpperCase() === label)) {
107
- issue(issues, specIndex, 'labels', 'domain_label', `spec ${humanIndex} requires at least one domain label besides ${label}`);
116
+ issue(issues, specIndex, 'labels', 'domain_label', `spec ${humanIndex} requires at least one domain label besides ${label}`, { label });
108
117
  }
109
118
  const description = typeof spec.description === 'string' ? spec.description.trim() : '';
110
119
  const sections = h2Sections(description);
@@ -115,13 +124,22 @@ function analyzeBuilderSpecBatch(input, options) {
115
124
  }
116
125
  for (const heading of exports.BUILDER_SPEC_HEADINGS) {
117
126
  if (!(sections.bodies.get(heading) ?? '').trim()) {
118
- issue(issues, specIndex, 'description', 'empty_section', `spec ${humanIndex} section "${heading}" cannot be empty`);
127
+ issue(issues, specIndex, 'description', 'empty_section', `spec ${humanIndex} section "${heading}" cannot be empty`, { heading });
128
+ }
129
+ }
130
+ // Depth floors (premium-milestone-progress D8): a restated title is not a
131
+ // problem statement and two lines are not a solution. Character floors are
132
+ // crude but deterministic — the prompt carries the real bar.
133
+ for (const [heading, min] of [['Problem Statement', spec_contract_prompt_1.SPEC_DEPTH_FLOORS.problemMinChars], ['Proposed Solution', spec_contract_prompt_1.SPEC_DEPTH_FLOORS.solutionMinChars]]) {
134
+ const body = (sections.bodies.get(heading) ?? '').trim();
135
+ if (body && body.length < min) {
136
+ issue(issues, specIndex, 'description', 'section_depth', `spec ${humanIndex} section "${heading}" is too thin (needs at least ${min} characters)`, { heading, min });
119
137
  }
120
138
  }
121
- for (const heading of ['Out of Scope', 'Technical Considerations']) {
139
+ for (const [heading, min] of [['Out of Scope', spec_contract_prompt_1.SPEC_DEPTH_FLOORS.outOfScopeMinBullets], ['Technical Considerations', spec_contract_prompt_1.SPEC_DEPTH_FLOORS.technicalMinBullets]]) {
122
140
  const body = sections.bodies.get(heading) ?? '';
123
- if (body && bulletCount(body) < 2) {
124
- issue(issues, specIndex, 'description', 'section_bullets', `spec ${humanIndex} section "${heading}" requires at least two bullets`);
141
+ if (body && bulletCount(body) < min) {
142
+ issue(issues, specIndex, 'description', 'section_bullets', `spec ${humanIndex} section "${heading}" requires at least ${min} bullets`, { heading, min });
125
143
  }
126
144
  }
127
145
  const complexity = sections.bodies.get('Estimated Complexity') ?? '';
@@ -132,19 +150,19 @@ function analyzeBuilderSpecBatch(input, options) {
132
150
  issue(issues, specIndex, 'description', 'scaffold_readme', 'spec 1 must note that the repository already contains a README');
133
151
  }
134
152
  const criteria = Array.isArray(spec.acceptanceCriteria) ? spec.acceptanceCriteria : [];
135
- if (criteria.length < 4 || criteria.length > 10) {
136
- issue(issues, specIndex, 'acceptanceCriteria', 'criteria_count', `spec ${humanIndex} acceptanceCriteria requires 4-10 items`);
153
+ if (criteria.length < spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criteriaMin || criteria.length > spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criteriaMax) {
154
+ issue(issues, specIndex, 'acceptanceCriteria', 'criteria_count', `spec ${humanIndex} acceptanceCriteria requires ${spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criteriaMin}-${spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criteriaMax} items`, { count: criteria.length, min: spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criteriaMin, max: spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criteriaMax });
137
155
  }
138
156
  const seenCriteria = new Set();
139
157
  criteria.forEach((criterion, criterionIndex) => {
140
158
  const text = typeof criterion === 'string' ? criterion.trim() : '';
141
- if (text.length < 10 || PLACEHOLDER_CRITERION.test(text)) {
142
- issue(issues, specIndex, 'acceptanceCriteria', 'criterion_quality', `spec ${humanIndex} criterion ${criterionIndex + 1} must be a concrete testable outcome`);
159
+ if (text.length < spec_contract_prompt_1.SPEC_DEPTH_FLOORS.criterionMinChars || PLACEHOLDER_CRITERION.test(text)) {
160
+ issue(issues, specIndex, 'acceptanceCriteria', 'criterion_quality', `spec ${humanIndex} criterion ${criterionIndex + 1} must be a concrete testable outcome`, { criterion: criterionIndex + 1 });
143
161
  return;
144
162
  }
145
163
  const key = normalized(text);
146
164
  if (seenCriteria.has(key)) {
147
- issue(issues, specIndex, 'acceptanceCriteria', 'duplicate_criterion', `spec ${humanIndex} criterion ${criterionIndex + 1} is duplicated`);
165
+ issue(issues, specIndex, 'acceptanceCriteria', 'duplicate_criterion', `spec ${humanIndex} criterion ${criterionIndex + 1} is duplicated`, { criterion: criterionIndex + 1 });
148
166
  }
149
167
  seenCriteria.add(key);
150
168
  });
@@ -165,3 +183,26 @@ function firstBuilderSpecQualityDetail(report) {
165
183
  function analyzeTypedBuilderSpecs(specs, specsComplete, options) {
166
184
  return analyzeBuilderSpecBatch({ specs, specsComplete }, options);
167
185
  }
186
+ /** M1 walking-skeleton gate options — the single definition shared by the
187
+ * commit validator, the chat manager's post-turn audit, and the router. */
188
+ exports.M1_BATCH_QUALITY_OPTIONS = {
189
+ milestoneLabel: 'M1',
190
+ minSpecs: 5,
191
+ maxSpecs: 10,
192
+ requireScaffold: true,
193
+ };
194
+ /** Audit an exact raw snapshot payload against the M1 gate. Tolerant of any
195
+ * input shape (a non-object simply fails the gate). */
196
+ function auditRawBlueprintForM1(rawBlueprint) {
197
+ const source = record(rawBlueprint);
198
+ const specs = Array.isArray(source?.m1Specs) ? source.m1Specs : [];
199
+ const report = analyzeBuilderSpecBatch({ specsComplete: source?.specsComplete, specs }, exports.M1_BATCH_QUALITY_OPTIONS);
200
+ return { ...report, claimsComplete: source?.specsComplete === true };
201
+ }
202
+ /** Compact, model-facing rendering of the audit (one line per issue). */
203
+ function formatQualityIssuesForModel(issues, max = 40) {
204
+ const lines = issues.slice(0, max).map((i) => `- ${i.message}`);
205
+ if (issues.length > max)
206
+ lines.push(`- …and ${issues.length - max} more`);
207
+ return lines.join('\n');
208
+ }