specrails-desktop 2.57.0 → 2.58.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 (336) hide show
  1. package/README.md +1 -1
  2. package/cli/dist/args.js +10 -4
  3. package/cli/dist/help.js +1 -1
  4. package/client/dist/assets/{ActivityFeedPage-DdMNmVBk.js → ActivityFeedPage-BQGppIR0.js} +1 -1
  5. package/client/dist/assets/{AgentBrowserCapture-CeerUf--.js → AgentBrowserCapture-PI-cxyxi.js} +1 -1
  6. package/client/dist/assets/{AgentModeAnalyticsPane-BjGb-8rh.js → AgentModeAnalyticsPane-mQsOqdXx.js} +2 -2
  7. package/client/dist/assets/{AgentModeCodePane-8XRkQEo_.js → AgentModeCodePane-BL-vpmlY.js} +2 -2
  8. package/client/dist/assets/{AgentModeJobsPane-BN3dgJOA.js → AgentModeJobsPane-DR_06oqH.js} +1 -1
  9. package/client/dist/assets/AgentsPage-BKIKjm_1.js +118 -0
  10. package/client/dist/assets/{AnalyticsPage-CyfhYiBn.js → AnalyticsPage-069npRVo.js} +1 -1
  11. package/client/dist/assets/{CodePage-C2Nf-Q5W.js → CodePage-Alitorrv.js} +2 -2
  12. package/client/dist/assets/DesktopAnalyticsPage-qtWAPM47.js +1 -0
  13. package/client/dist/assets/{DocsDialog-Br3bczCp.js → DocsDialog-DwL6rgcT.js} +1 -1
  14. package/client/dist/assets/{DocsPage-BcDr2cBm.js → DocsPage-BA1hiJZ-.js} +1 -1
  15. package/client/dist/assets/{ExportDropdown-BVGKEgkd.js → ExportDropdown-BcMpvV89.js} +1 -1
  16. package/client/dist/assets/InteractiveJobComposer-B3H1YHr6.js +19 -0
  17. package/client/dist/assets/JobDetailModal-XZQ9eAAy.js +1 -0
  18. package/client/dist/assets/{JobDetailPage-vBbNeRuz.js → JobDetailPage-B-SIaq_q.js} +1 -1
  19. package/client/dist/assets/{JobsPage-CJCtPhIz.js → JobsPage-iQXAv2mx.js} +1 -1
  20. package/client/dist/assets/LoopBuilderPage-B_gbFAHn.js +1 -0
  21. package/client/dist/assets/LoopPreviewModal-je5pSCf-.js +1 -0
  22. package/client/dist/assets/LoopsPage-BZwcY5cu.js +2 -0
  23. package/client/dist/assets/{MinimizedChatsContext-D3QzgV0B.js → MinimizedChatsContext-CNx_0bZu.js} +1 -1
  24. package/client/dist/assets/{PluginsPage-DOpTV75p.js → PluginsPage-DFocnoHX.js} +2 -2
  25. package/client/dist/assets/{ProjectSettingsDialog-Dkwrn0z1.js → ProjectSettingsDialog-Bh3S6EBB.js} +1 -1
  26. package/client/dist/assets/{RepositoryDeliveries-Du2UCf1a.js → RepositoryDeliveries-BiwJuS8r.js} +1 -1
  27. package/client/dist/assets/{RepositoryScopeSelector-hPGZNRBm.js → RepositoryScopeSelector-C2mvWBQ6.js} +1 -1
  28. package/client/dist/assets/{ReviewPacketPage-DcOq-UTA.js → ReviewPacketPage-CrAf-WDy.js} +2 -2
  29. package/client/dist/assets/TemplatePreviewModal-C7DordpN.js +1 -0
  30. package/client/dist/assets/{TicketDetailModalContext-DAK-NoK6.js → TicketDetailModalContext-B-DySpKL.js} +1 -1
  31. package/client/dist/assets/{Trans-DEbgkuAY.js → Trans-D3AfDylf.js} +1 -1
  32. package/client/dist/assets/agentRuntime-BeUb3g9g.js +1 -0
  33. package/client/dist/assets/agentRuntime-Bg9tbmqm.js +1 -0
  34. package/client/dist/assets/agentRuntime-D-GJVGkj.js +1 -0
  35. package/client/dist/assets/agentRuntime-D2__CVI_.js +1 -0
  36. package/client/dist/assets/agentRuntime-D35dGVOP.js +1 -0
  37. package/client/dist/assets/agentRuntime-DGz_IqKW.js +1 -0
  38. package/client/dist/assets/agentRuntime-O1liDt6G.js +1 -0
  39. package/client/dist/assets/agentRuntime-hInH9Fzj.js +1 -0
  40. package/client/dist/assets/agentstudio-B2CTtQgZ.js +1 -0
  41. package/client/dist/assets/agentstudio-BWAKeGgB.js +1 -0
  42. package/client/dist/assets/{agentstudio-DR7doNBV.js → agentstudio-Cja8Sjcp.js} +1 -1
  43. package/client/dist/assets/agentstudio-DtfPcr99.js +1 -0
  44. package/client/dist/assets/agentstudio-QdAJIDef.js +1 -0
  45. package/client/dist/assets/agentstudio-X70BKf6u.js +1 -0
  46. package/client/dist/assets/agentstudio-fLXpRGIi.js +1 -0
  47. package/client/dist/assets/agentstudio-qXKqVnWS.js +1 -0
  48. package/client/dist/assets/{analytics-jzCkOec3.js → analytics-B1DIQd4m.js} +1 -1
  49. package/client/dist/assets/{analytics-CoEzWPx4.js → analytics-BAIZ9vE_.js} +1 -1
  50. package/client/dist/assets/{analytics-DjLlE7jS.js → analytics-BTqgmgBd.js} +1 -1
  51. package/client/dist/assets/analytics-BWynXPEm.js +1 -0
  52. package/client/dist/assets/{analytics-nZIjjtCc.js → analytics-DGcUsQ44.js} +1 -1
  53. package/client/dist/assets/{analytics-BNgZ1Nj7.js → analytics-GnSnpJ4W.js} +1 -1
  54. package/client/dist/assets/{analytics-DFh5lSOl.js → analytics-TWgYaoO0.js} +1 -1
  55. package/client/dist/assets/{analytics-DZp-DS2U.js → analytics-kJy6Tuko.js} +1 -1
  56. package/client/dist/assets/builder-C0FyvM83.js +1 -0
  57. package/client/dist/assets/builder-C1neIYGR.js +1 -0
  58. package/client/dist/assets/builder-CvnFm0im.js +1 -0
  59. package/client/dist/assets/builder-CzW5ovyI.js +1 -0
  60. package/client/dist/assets/builder-D06L7iPy.js +1 -0
  61. package/client/dist/assets/builder-DGETbtZj.js +1 -0
  62. package/client/dist/assets/builder-DXEJUlC0.js +1 -0
  63. package/client/dist/assets/builder-DyQperXU.js +1 -0
  64. package/client/dist/assets/common-BCK7c9Pv.js +1 -0
  65. package/client/dist/assets/common-C0BOdeNi.js +1 -0
  66. package/client/dist/assets/common-C4Yvvhp6.js +1 -0
  67. package/client/dist/assets/{common-BrlwEnL7.js → common-DmsclwjP.js} +1 -1
  68. package/client/dist/assets/common-DpLWwgI1.js +1 -0
  69. package/client/dist/assets/common-OCvl3c8t.js +1 -0
  70. package/client/dist/assets/common-c0aujEZC.js +1 -0
  71. package/client/dist/assets/common-od8WThlf.js +1 -0
  72. package/client/dist/assets/dashboard-B1ndRHuC.js +1 -0
  73. package/client/dist/assets/dashboard-BX7KXiqA.js +1 -0
  74. package/client/dist/assets/dashboard-B_BSNxcN.js +1 -0
  75. package/client/dist/assets/dashboard-CCeFN7-V.js +1 -0
  76. package/client/dist/assets/dashboard-CRl0WtFJ.js +1 -0
  77. package/client/dist/assets/dashboard-XGdQ5mjw.js +1 -0
  78. package/client/dist/assets/dashboard-_oltA6Jd.js +1 -0
  79. package/client/dist/assets/dashboard-bAuf5KVk.js +1 -0
  80. package/client/dist/assets/{formatDistanceToNow-Cc2u5FwQ.js → formatDistanceToNow-aBss1Nai.js} +1 -1
  81. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-BHm2U-w1.js → getTimezoneOffsetInMilliseconds-DOGj1Mfz.js} +1 -1
  82. package/client/dist/assets/index-BS7KCWkV.css +2 -0
  83. package/client/dist/assets/{index-Cxb288Rj.js → index-C4Mv6tV6.js} +32 -32
  84. package/client/dist/assets/{jira-api-DrGxiD9p.js → jira-api-BYbEPlkr.js} +1 -1
  85. package/client/dist/assets/{jobs-DSp9DoD_.js → jobs-6_-r75yC.js} +1 -1
  86. package/client/dist/assets/{jobs-C6qmvUAi.js → jobs-B1yqyId5.js} +1 -1
  87. package/client/dist/assets/{jobs-DoFaEl9g.js → jobs-BFuV7ttH.js} +1 -1
  88. package/client/dist/assets/{jobs-DTQO08nJ.js → jobs-BKa8LuwA.js} +1 -1
  89. package/client/dist/assets/{jobs-DrF8Do8s.js → jobs-BKiK-ZWh.js} +1 -1
  90. package/client/dist/assets/{jobs-DLHdDkHc.js → jobs-DmKV7b7d.js} +1 -1
  91. package/client/dist/assets/{jobs-C9j2PuwW.js → jobs-KG4Sn12X.js} +1 -1
  92. package/client/dist/assets/{jobs-Dpw6AEOP.js → jobs-pPv4KWjb.js} +1 -1
  93. package/client/dist/assets/loop-layout-BF8xMiNv.js +7 -0
  94. package/client/dist/assets/loops-B4_D-nbg.js +1 -0
  95. package/client/dist/assets/loops-BJDY9FTh.js +1 -0
  96. package/client/dist/assets/loops-BO3V8244.js +1 -0
  97. package/client/dist/assets/loops-C-hZK8pf.js +1 -0
  98. package/client/dist/assets/loops-CDOWZ0ci.js +1 -0
  99. package/client/dist/assets/loops-Dv849ckF.js +1 -0
  100. package/client/dist/assets/loops-Q9LIgGkq.js +1 -0
  101. package/client/dist/assets/loops-eUnCfLjC.js +1 -0
  102. package/client/dist/assets/{project-repositories-9Wzs0coQ.js → project-repositories-CSnfzUpg.js} +1 -1
  103. package/client/dist/assets/settings-3o4Ntzi5.js +1 -0
  104. package/client/dist/assets/settings-B97jlkwD.js +1 -0
  105. package/client/dist/assets/settings-BTaT_bsS.js +1 -0
  106. package/client/dist/assets/settings-CT8w7WiC.js +1 -0
  107. package/client/dist/assets/settings-Cvr8_tvh.js +1 -0
  108. package/client/dist/assets/settings-Dn1qgAC8.js +1 -0
  109. package/client/dist/assets/settings-EShDdg4J.js +1 -0
  110. package/client/dist/assets/settings-tfLhQZRN.js +1 -0
  111. package/client/dist/assets/{setup-Dwzt5nO1.js → setup-CBMu3tqH.js} +1 -1
  112. package/client/dist/assets/{setup-B5FHuxvB.js → setup-CPh-e6J2.js} +1 -1
  113. package/client/dist/assets/{setup-DpqzaEEA.js → setup-CRKLp_ke.js} +1 -1
  114. package/client/dist/assets/{setup-CROyqD5N.js → setup-Cy302v4J.js} +1 -1
  115. package/client/dist/assets/{setup-C9Fy9PKQ.js → setup-D681vTPf.js} +1 -1
  116. package/client/dist/assets/{setup-BihzXadx.js → setup-DYK0VBkE.js} +1 -1
  117. package/client/dist/assets/{setup-BtC6Hg4K.js → setup-KAuiIiVs.js} +1 -1
  118. package/client/dist/assets/{setup-zLEDnG7N.js → setup-OGX-oCfI.js} +1 -1
  119. package/client/dist/assets/{spending-T5GM7wpn.js → spending-HBSk-6jv.js} +1 -1
  120. package/client/dist/assets/{useDesktop-QogiN0zR.js → useDesktop-0unHDBJJ.js} +1 -1
  121. package/client/dist/assets/useRuntimeRuns-BN1rhyEm.js +1 -0
  122. package/client/dist/assets/{useSharedWebSocket-Dz-HI6De.js → useSharedWebSocket-BZMrX4jr.js} +2 -2
  123. package/client/dist/index.html +25 -25
  124. package/docs/agent-live-steering.md +16 -0
  125. package/docs/cli.md +7 -7
  126. package/docs/codex.md +2 -2
  127. package/docs/customizing.md +1 -1
  128. package/docs/gemini.md +1 -1
  129. package/docs/getting-started.md +1 -1
  130. package/docs/guide/de/integrations/7-local-engines.md +1 -1
  131. package/docs/guide/de/pipeline/1-rails-and-jobs.md +8 -6
  132. package/docs/guide/de/pipeline/2-the-job-detail-view.md +11 -2
  133. package/docs/guide/de/pipeline/4-picking-an-engine-per-rail.md +2 -2
  134. package/docs/guide/de/pipeline/5-the-loop-builder.md +24 -5
  135. package/docs/guide/en/integrations/7-local-engines.md +1 -1
  136. package/docs/guide/en/pipeline/1-rails-and-jobs.md +8 -6
  137. package/docs/guide/en/pipeline/2-the-job-detail-view.md +11 -2
  138. package/docs/guide/en/pipeline/4-picking-an-engine-per-rail.md +2 -2
  139. package/docs/guide/en/pipeline/5-the-loop-builder.md +34 -5
  140. package/docs/guide/en/settings/3-pipeline-telemetry-and-diagnostics.md +1 -1
  141. package/docs/guide/es/integrations/7-local-engines.md +1 -1
  142. package/docs/guide/es/pipeline/1-rails-and-jobs.md +8 -6
  143. package/docs/guide/es/pipeline/2-the-job-detail-view.md +11 -2
  144. package/docs/guide/es/pipeline/4-picking-an-engine-per-rail.md +2 -2
  145. package/docs/guide/es/pipeline/5-the-loop-builder.md +30 -5
  146. package/docs/guide/es/settings/3-pipeline-telemetry-and-diagnostics.md +1 -2
  147. package/docs/guide/fr/integrations/7-local-engines.md +1 -1
  148. package/docs/guide/fr/pipeline/1-rails-and-jobs.md +8 -6
  149. package/docs/guide/fr/pipeline/2-the-job-detail-view.md +11 -2
  150. package/docs/guide/fr/pipeline/4-picking-an-engine-per-rail.md +2 -2
  151. package/docs/guide/fr/pipeline/5-the-loop-builder.md +24 -5
  152. package/docs/guide/it/integrations/7-local-engines.md +1 -1
  153. package/docs/guide/it/pipeline/1-rails-and-jobs.md +8 -6
  154. package/docs/guide/it/pipeline/2-the-job-detail-view.md +11 -2
  155. package/docs/guide/it/pipeline/4-picking-an-engine-per-rail.md +2 -2
  156. package/docs/guide/it/pipeline/5-the-loop-builder.md +24 -5
  157. package/docs/guide/ja/integrations/7-local-engines.md +1 -1
  158. package/docs/guide/ja/pipeline/1-rails-and-jobs.md +8 -6
  159. package/docs/guide/ja/pipeline/2-the-job-detail-view.md +11 -2
  160. package/docs/guide/ja/pipeline/4-picking-an-engine-per-rail.md +2 -2
  161. package/docs/guide/ja/pipeline/5-the-loop-builder.md +24 -5
  162. package/docs/guide/pt/integrations/7-local-engines.md +1 -1
  163. package/docs/guide/pt/pipeline/1-rails-and-jobs.md +8 -6
  164. package/docs/guide/pt/pipeline/2-the-job-detail-view.md +11 -2
  165. package/docs/guide/pt/pipeline/4-picking-an-engine-per-rail.md +2 -2
  166. package/docs/guide/pt/pipeline/5-the-loop-builder.md +24 -5
  167. package/docs/guide/zh/integrations/7-local-engines.md +1 -1
  168. package/docs/guide/zh/pipeline/1-rails-and-jobs.md +8 -6
  169. package/docs/guide/zh/pipeline/2-the-job-detail-view.md +11 -2
  170. package/docs/guide/zh/pipeline/4-picking-an-engine-per-rail.md +2 -2
  171. package/docs/guide/zh/pipeline/5-the-loop-builder.md +24 -5
  172. package/docs/internals/api-reference.md +2 -2
  173. package/docs/internals/ci-performance.md +71 -0
  174. package/docs/internals/companion-rails-as-loops-contract.md +4 -3
  175. package/docs/internals/configuration.md +3 -3
  176. package/docs/internals/core-runtime-updates.md +3 -2
  177. package/docs/internals/interactive-jobs.md +2 -2
  178. package/docs/internals/loop-step-log-explorer.md +42 -0
  179. package/docs/internals/mission-rail-cards.md +1 -1
  180. package/docs/internals/operations-runbook.md +22 -0
  181. package/docs/internals/programmatic-agent-runtime.md +130 -7
  182. package/docs/internals/project-builder.md +35 -16
  183. package/docs/internals/source-map.md +48 -2
  184. package/docs/kimi.md +2 -2
  185. package/docs/local-providers.md +1 -1
  186. package/docs/running-pipelines.md +26 -18
  187. package/docs/tracking-cost.md +10 -1
  188. package/package.json +2 -1
  189. package/server/dist/core-compat.js +8 -3
  190. package/server/dist/core-execution.js +49 -3
  191. package/server/dist/db/migrations.js +101 -0
  192. package/server/dist/desktop-db.js +28 -0
  193. package/server/dist/desktop-router.js +2 -0
  194. package/server/dist/mcp/guide.js +6 -3
  195. package/server/dist/mcp/tools/jobs.js +14 -1
  196. package/server/dist/mcp/tools/loops.js +17 -8
  197. package/server/dist/mcp/tools/rails.js +3 -3
  198. package/server/dist/modules/agent-runtime/runtime/agent-runtime-bridge.js +313 -9
  199. package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls-router.js +34 -1
  200. package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +203 -21
  201. package/server/dist/modules/agent-runtime/runtime/agent-runtime-effective-config.js +46 -1
  202. package/server/dist/modules/agent-runtime/runtime/agent-runtime-engines.js +10 -0
  203. package/server/dist/modules/agent-runtime/runtime/agent-runtime-loader.js +68 -3
  204. package/server/dist/modules/agent-runtime/runtime/agent-runtime-metrics.js +16 -8
  205. package/server/dist/modules/agent-runtime/runtime/agent-runtime-package-gc.js +81 -0
  206. package/server/dist/modules/agent-runtime/runtime/agent-runtime-package-lock.js +37 -0
  207. package/server/dist/modules/agent-runtime/runtime/agent-runtime-package.js +30 -24
  208. package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-host.js +87 -0
  209. package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-quarantine.js +231 -0
  210. package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention-records.js +32 -0
  211. package/server/dist/modules/agent-runtime/runtime/agent-runtime-retention.js +102 -0
  212. package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings-router.js +12 -7
  213. package/server/dist/modules/agent-runtime/runtime/agent-runtime-settings.js +57 -13
  214. package/server/dist/modules/agents/runtime/agent-catalog.js +32 -0
  215. package/server/dist/modules/agents/runtime/agent-refine-manager.js +12 -0
  216. package/server/dist/modules/agents/runtime/agent-role-descriptor.js +64 -0
  217. package/server/dist/modules/agents/runtime/profiles-router.js +26 -34
  218. package/server/dist/modules/builder/runtime/milestone-chain.js +121 -73
  219. package/server/dist/modules/delivery/runtime/definition-fork.js +158 -0
  220. package/server/dist/modules/delivery/runtime/delivery-evidence.js +56 -10
  221. package/server/dist/modules/delivery/runtime/isolated-settlement-reconstruction.js +99 -0
  222. package/server/dist/modules/delivery/runtime/isolated-settlement-store.js +53 -0
  223. package/server/dist/modules/delivery/runtime/multi-repo-execution-store.js +3 -1
  224. package/server/dist/modules/delivery/runtime/rail-isolated-launch.js +388 -203
  225. package/server/dist/modules/delivery/runtime/rail-launch-parser.js +3 -2
  226. package/server/dist/modules/delivery/runtime/rail-pr-store.js +6 -1
  227. package/server/dist/modules/delivery/runtime/rails-router.js +66 -10
  228. package/server/dist/modules/execution/runtime/queue-manager.js +11 -2
  229. package/server/dist/modules/loops/runtime/builtin-loops.js +70 -0
  230. package/server/dist/modules/loops/runtime/definition-cancellation.js +31 -0
  231. package/server/dist/modules/loops/runtime/legacy-launch-telemetry.js +78 -0
  232. package/server/dist/modules/loops/runtime/loop-command-catalog.js +13 -12
  233. package/server/dist/modules/loops/runtime/loop-compat.js +219 -0
  234. package/server/dist/modules/loops/runtime/loop-core-factory.js +42 -0
  235. package/server/dist/modules/loops/runtime/loop-definition-controls.js +48 -0
  236. package/server/dist/modules/loops/runtime/loop-definition-events.js +145 -0
  237. package/server/dist/modules/loops/runtime/loop-definition-recovery.js +106 -0
  238. package/server/dist/modules/loops/runtime/loop-definition-run.js +36 -0
  239. package/server/dist/modules/loops/runtime/loop-definition.js +246 -0
  240. package/server/dist/modules/loops/runtime/loop-effect.js +10 -1
  241. package/server/dist/modules/loops/runtime/loop-executors.js +63 -3
  242. package/server/dist/modules/loops/runtime/loop-factory.js +35 -15
  243. package/server/dist/modules/loops/runtime/loop-graph.js +84 -8
  244. package/server/dist/modules/loops/runtime/loop-migration.js +36 -0
  245. package/server/dist/modules/loops/runtime/loop-preview.js +10 -0
  246. package/server/dist/modules/loops/runtime/loop-run-manager.js +206 -13
  247. package/server/dist/modules/loops/runtime/loop-runs-store.js +271 -7
  248. package/server/dist/modules/loops/runtime/loop-templates.js +205 -45
  249. package/server/dist/modules/loops/runtime/loops-router.js +246 -29
  250. package/server/dist/modules/loops/runtime/loops-store.js +140 -20
  251. package/server/dist/modules/missions/runtime/agent-operator-prompt.js +14 -11
  252. package/server/dist/modules/specs/runtime/spec-addenda.js +28 -0
  253. package/server/dist/project-registry.js +87 -51
  254. package/server/dist/project-router-jobs.js +17 -6
  255. package/server/dist/project-router-loop-runs.js +200 -0
  256. package/server/dist/project-router-spending.js +17 -0
  257. package/server/dist/project-router.js +8 -6
  258. package/server/dist/schemas/agent-runtime.schema.json +93 -1
  259. package/server/dist/schemas/workflow-definition.schema.json +312 -0
  260. package/server/dist/vitest-setup.js +22 -0
  261. package/server/dist/worktree-overlay.js +1 -1
  262. package/client/dist/assets/AgentsPage-DfHWgP_x.js +0 -87
  263. package/client/dist/assets/DesktopAnalyticsPage-tc0p4jJW.js +0 -1
  264. package/client/dist/assets/InteractiveJobComposer-DFPpdg2D.js +0 -19
  265. package/client/dist/assets/JobDetailModal-BdOv-l-M.js +0 -1
  266. package/client/dist/assets/LoopBuilderPage-BemvUOxy.js +0 -7
  267. package/client/dist/assets/LoopPreviewModal-C6hNAeYN.js +0 -1
  268. package/client/dist/assets/LoopsPage-BZr0quvh.js +0 -1
  269. package/client/dist/assets/TemplatePreviewModal-Cejcquah.js +0 -1
  270. package/client/dist/assets/agentRuntime-BUimiX__.js +0 -1
  271. package/client/dist/assets/agentRuntime-BcN97sAB.js +0 -1
  272. package/client/dist/assets/agentRuntime-Ckyg-ahT.js +0 -1
  273. package/client/dist/assets/agentRuntime-DJCwyX4t.js +0 -1
  274. package/client/dist/assets/agentRuntime-DVzPJOl0.js +0 -1
  275. package/client/dist/assets/agentRuntime-V9AZTG3c.js +0 -1
  276. package/client/dist/assets/agentRuntime-bAAjw1vw.js +0 -1
  277. package/client/dist/assets/agentRuntime-v4qKAYH-.js +0 -1
  278. package/client/dist/assets/agentstudio-BNUth0uj.js +0 -1
  279. package/client/dist/assets/agentstudio-BNdB5hkr.js +0 -1
  280. package/client/dist/assets/agentstudio-C7-bt8FO.js +0 -1
  281. package/client/dist/assets/agentstudio-C8uNchpV.js +0 -1
  282. package/client/dist/assets/agentstudio-CKabyMlg.js +0 -1
  283. package/client/dist/assets/agentstudio-CN2pokQu.js +0 -1
  284. package/client/dist/assets/agentstudio-y2dVDXeM.js +0 -1
  285. package/client/dist/assets/analytics-BlypLLR6.js +0 -1
  286. package/client/dist/assets/builder-765NeYTp.js +0 -1
  287. package/client/dist/assets/builder-8MG3V6ea.js +0 -1
  288. package/client/dist/assets/builder-B-1UBs5K.js +0 -1
  289. package/client/dist/assets/builder-BTiGnPT0.js +0 -1
  290. package/client/dist/assets/builder-Cx9Rz-7u.js +0 -1
  291. package/client/dist/assets/builder-DKFMELBl.js +0 -1
  292. package/client/dist/assets/builder-OcmKrC0h.js +0 -1
  293. package/client/dist/assets/builder-P9qVBXFZ.js +0 -1
  294. package/client/dist/assets/common-B5AZNYMH.js +0 -1
  295. package/client/dist/assets/common-BFZ0c_vN.js +0 -1
  296. package/client/dist/assets/common-BttQx3z4.js +0 -1
  297. package/client/dist/assets/common-CM65k_-e.js +0 -1
  298. package/client/dist/assets/common-D4mvFFlu.js +0 -1
  299. package/client/dist/assets/common-TZ9Ny96S.js +0 -1
  300. package/client/dist/assets/common-nFpSF4GC.js +0 -1
  301. package/client/dist/assets/dashboard-B9uWNGO2.js +0 -1
  302. package/client/dist/assets/dashboard-BIwmO9wc.js +0 -1
  303. package/client/dist/assets/dashboard-BV4vOZkR.js +0 -1
  304. package/client/dist/assets/dashboard-Bf6MPVsM.js +0 -1
  305. package/client/dist/assets/dashboard-C8klWVvZ.js +0 -1
  306. package/client/dist/assets/dashboard-D1ORhPF0.js +0 -1
  307. package/client/dist/assets/dashboard-DSubvs9x.js +0 -1
  308. package/client/dist/assets/dashboard-px_vlRx_.js +0 -1
  309. package/client/dist/assets/index-C51x61Bz.css +0 -2
  310. package/client/dist/assets/loops-B7YpGizO.js +0 -1
  311. package/client/dist/assets/loops-BomD2J-h.js +0 -1
  312. package/client/dist/assets/loops-BrQHSQw3.js +0 -1
  313. package/client/dist/assets/loops-BvBwvTPA.js +0 -1
  314. package/client/dist/assets/loops-CODk6Slz.js +0 -1
  315. package/client/dist/assets/loops-DC6k-Ym-.js +0 -1
  316. package/client/dist/assets/loops-DSEg8-uY.js +0 -1
  317. package/client/dist/assets/loops-DhCoXD_v.js +0 -1
  318. package/client/dist/assets/settings-BsehKpty.js +0 -1
  319. package/client/dist/assets/settings-CQWob3SB.js +0 -1
  320. package/client/dist/assets/settings-CgCl0hzy.js +0 -1
  321. package/client/dist/assets/settings-DLRLd22L.js +0 -1
  322. package/client/dist/assets/settings-Dn5prAFl.js +0 -1
  323. package/client/dist/assets/settings-HZSu-K4l.js +0 -1
  324. package/client/dist/assets/settings-p4OVIyRh.js +0 -1
  325. package/client/dist/assets/settings-rddaldUp.js +0 -1
  326. package/client/dist/assets/useRuntimeRuns-Dqmz9fS9.js +0 -1
  327. package/docs/guide/de/pipeline/3-batch-implement-and-multi-feature.md +0 -84
  328. package/docs/guide/en/pipeline/3-batch-implement-and-multi-feature.md +0 -126
  329. package/docs/guide/es/pipeline/3-batch-implement-and-multi-feature.md +0 -114
  330. package/docs/guide/fr/pipeline/3-batch-implement-and-multi-feature.md +0 -84
  331. package/docs/guide/it/pipeline/3-batch-implement-and-multi-feature.md +0 -84
  332. package/docs/guide/ja/pipeline/3-batch-implement-and-multi-feature.md +0 -84
  333. package/docs/guide/pt/pipeline/3-batch-implement-and-multi-feature.md +0 -84
  334. package/docs/guide/zh/pipeline/3-batch-implement-and-multi-feature.md +0 -84
  335. package/server/dist/modules/loops/runtime/loop-templates-ported.js +0 -675
  336. /package/client/dist/assets/{LoopBuilderPage-DtxX27Jz.css → loop-layout-DtxX27Jz.css} +0 -0
@@ -36,7 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.PrContinuationIsolationError = void 0;
37
37
  exports.buildSpecSnapshot = buildSpecSnapshot;
38
38
  exports.launchIsolatedRail = launchIsolatedRail;
39
+ exports.reattachIsolatedSettlement = reattachIsolatedSettlement;
39
40
  exports.reconcileRailWorktrees = reconcileRailWorktrees;
41
+ const isolated_settlement_reconstruction_1 = require("./isolated-settlement-reconstruction");
42
+ const legacy_launch_telemetry_1 = require("../../loops/runtime/legacy-launch-telemetry");
40
43
  /**
41
44
  * Isolated (worktree-per-ticket) rail launch — the live wiring that turns the
42
45
  * tested building blocks (worktree-manager, merge-manager, rail-merge-orchestrator,
@@ -73,6 +76,9 @@ const pr_naming_1 = require("./pr-naming");
73
76
  const jira_db_1 = require("../../../jira/jira-db");
74
77
  const db_1 = require("../../../db");
75
78
  const delivery_evidence_1 = require("./delivery-evidence");
79
+ const isolated_settlement_store_1 = require("./isolated-settlement-store");
80
+ const loop_runs_store_1 = require("../../loops/runtime/loop-runs-store");
81
+ const loop_definition_recovery_1 = require("../../loops/runtime/loop-definition-recovery");
76
82
  const integration_branch_1 = require("../../../integration-branch");
77
83
  const repo_lock_1 = require("../../../repo-lock");
78
84
  const project_repositories_1 = require("../../../project-repositories");
@@ -359,7 +365,7 @@ async function captureSettlementIgnoredPaths(git, worktreePath, overlayExcludes)
359
365
  }
360
366
  }
361
367
  function branchRecords(results) {
362
- return results.flatMap((result) => result.run.ticketIds.map((ticketId) => ({
368
+ return results.flatMap((result) => (result.run.ticketIds.length ? result.run.ticketIds : [result.run.ticketId]).map((ticketId) => ({
363
369
  ticketId,
364
370
  branch: result.run.handle.branch,
365
371
  // Legacy consumers interpret this as delivery eligibility, not engine truth.
@@ -392,6 +398,209 @@ function getRailName(ctx, railIndex) {
392
398
  return null;
393
399
  }
394
400
  }
401
+ /** Shared by fresh execution and durable reattachment; one owner of Git settlement. */
402
+ function createIsolatedRunSettlement(ports) {
403
+ const { git, baseRepo, overlaySourceRoot, overlayFallbackRoots, overlayProviderDir, overlayInstructions, isAborted, onFinished, recordRunProvenance, markWorktree, hasRuntimeRequest, commitMessage } = ports;
404
+ return async (a, enginePromise) => {
405
+ let actualOutcome = 'failed';
406
+ let engineFailure;
407
+ let stallReason;
408
+ try {
409
+ const result = await enginePromise;
410
+ actualOutcome = result.outcome;
411
+ stallReason = result.stallReason;
412
+ }
413
+ catch (err) {
414
+ engineFailure = errorDetail(err);
415
+ console.error(`[rail-isolated] loop run ${a.runId} rejected: ${engineFailure}`);
416
+ }
417
+ if (ports.ownsRun && !ports.ownsRun(a.runId))
418
+ throw new Error('Fork owns the original settlement');
419
+ if (isAborted?.()) {
420
+ // The coordinator never started. Do not commit incidental edits or
421
+ // release a mount borrowed from a previous delivery while rolling back.
422
+ const finalSha = await readHeadSha(git, a.handle.worktreePath);
423
+ if (a.worktreeOwnership !== 'created')
424
+ markWorktree(a, 'needs-review');
425
+ return {
426
+ run: a, implementationOutcome: 'failed', deliveryOutcome: 'not_started',
427
+ initialSha: a.initialSha, finalSha, changed: Boolean(finalSha && a.initialSha !== finalSha),
428
+ safeToRelease: a.worktreeOwnership === 'created' && finalSha !== null && finalSha === a.initialSha,
429
+ };
430
+ }
431
+ const implementationOutcome = actualOutcome === 'success' ? 'succeeded' : 'failed';
432
+ recordRunProvenance(a);
433
+ let callbackFailure;
434
+ try {
435
+ onFinished(a.runId, actualOutcome, stallReason);
436
+ }
437
+ catch (err) {
438
+ callbackFailure = errorDetail(err);
439
+ console.error(`[rail-isolated] terminal callback failed for ${a.runId}: ${callbackFailure}`);
440
+ }
441
+ // A paused/interrupted programmatic workflow owns unfinished changes and
442
+ // verification evidence in this exact mount. Keep them available for
443
+ // explicit Core recovery, including a currently clean worktree.
444
+ if (implementationOutcome === 'failed' && hasRuntimeRequest(a.runId)) {
445
+ markWorktree(a, 'needs-review');
446
+ return {
447
+ run: a, implementationOutcome, deliveryOutcome: 'blocked',
448
+ initialSha: a.initialSha, finalSha: await readHeadSha(git, a.handle.worktreePath),
449
+ failureCode: 'settlement_interrupted',
450
+ failureDetail: 'Programmatic workflow needs attention. Resume it from Agent Runtime settings in its original worktree.',
451
+ safeToRelease: false,
452
+ };
453
+ }
454
+ // The engine may have edited a copied overlay file. Re-authenticate only
455
+ // automatic-cleanup authority. Allocation-time paths remain conservative
456
+ // NEVER-COMMIT exclusions, while a modified copy is preserved in the
457
+ // worktree instead of being silently staged or deleted.
458
+ if (a.overlayCleanupEvidence.length > 0) {
459
+ a.overlayCleanupEvidence = (0, worktree_overlay_1.revalidateOverlayCleanupEvidence)({
460
+ worktreePath: a.handle.worktreePath,
461
+ sourceRoot: overlaySourceRoot,
462
+ fallbackSourceRoots: overlayFallbackRoots,
463
+ providerDir: overlayProviderDir,
464
+ instructionsFilename: overlayInstructions,
465
+ }, a.overlayCleanupEvidence);
466
+ }
467
+ // Warm-dependency links are re-proven against the base repo (their source),
468
+ // not the framework overlay source. A link the engine replaced with a real
469
+ // install loses its authorization here and correctly preserves the worktree.
470
+ if (a.warmLinkEvidence.length > 0) {
471
+ a.warmLinkEvidence = (0, worktree_node_modules_1.authenticateWarmNodeModulesLinks)(baseRepo, a.handle.worktreePath)
472
+ .filter((live) => a.warmLinkEvidence.some((prior) => prior.path === live.path && prior.kind === live.kind && prior.digest === live.digest));
473
+ }
474
+ let commit;
475
+ try {
476
+ commit = await (0, worktree_manager_1.commitWorktreeAndVerify)(git, a.handle.worktreePath, commitMessage(a, implementationOutcome === 'failed'), a.overlayExcludes);
477
+ }
478
+ catch (err) {
479
+ commit = { staged: false, committed: false, clean: false, dirty: [], error: errorDetail(err) };
480
+ }
481
+ if (!commit.clean) {
482
+ const detail = commitFailureSummary(commit);
483
+ console.error(`[rail-isolated] run ${a.runId} left an unsafe worktree: ${detail}`);
484
+ markWorktree(a, 'needs-review');
485
+ return {
486
+ run: a,
487
+ implementationOutcome,
488
+ deliveryOutcome: 'blocked',
489
+ initialSha: a.initialSha,
490
+ finalSha: await readHeadSha(git, a.handle.worktreePath),
491
+ failureCode: 'commit_failed',
492
+ failureDetail: detail,
493
+ safeToRelease: false,
494
+ };
495
+ }
496
+ // The worktree is proven clean THIS instant — freeze the ignored-path set
497
+ // as the settlement snapshot. Everything ignored right now is run residue
498
+ // by construction (the worktree is app-created and deliverables are
499
+ // committed); anything ignored that appears LATER preserves the worktree.
500
+ a.settlementIgnoredPaths = a.automaticRelease === false ? null : await captureSettlementIgnoredPaths(git, a.handle.worktreePath, a.overlayExcludes);
501
+ let finalSha = null;
502
+ let verificationFailure;
503
+ if (a.continuationTarget) {
504
+ try {
505
+ finalSha = await verifyContinuationWorktree(git, baseRepo, a.handle, a.continuationTarget);
506
+ }
507
+ catch (err) {
508
+ verificationFailure = errorDetail(err);
509
+ }
510
+ }
511
+ else {
512
+ finalSha = await readHeadSha(git, a.handle.worktreePath);
513
+ }
514
+ const changed = commit.committed || Boolean(a.initialSha && finalSha && a.initialSha !== finalSha);
515
+ // A successful commit without a provable object is not retryable or safe to
516
+ // detach. A continuation additionally requires HEAD == refs/heads/<branch>.
517
+ if (verificationFailure || !finalSha || (!commit.committed && a.branchOwnership === 'created' && !a.initialSha)) {
518
+ const detail = verificationFailure
519
+ ?? (!finalSha
520
+ ? 'the final HEAD object could not be verified'
521
+ : 'the initial HEAD object was not captured, so a no-change result cannot be proven');
522
+ console.error(`[rail-isolated] run ${a.runId} finished on an unverified ref: ${detail}`);
523
+ markWorktree(a, 'needs-review');
524
+ return {
525
+ run: a,
526
+ implementationOutcome,
527
+ deliveryOutcome: 'blocked',
528
+ initialSha: a.initialSha,
529
+ finalSha,
530
+ changed,
531
+ failureCode: 'branch_verification_failed',
532
+ failureDetail: detail,
533
+ safeToRelease: false,
534
+ };
535
+ }
536
+ // Failed clean units remain `building` until aggregate cleanup removes the
537
+ // mount and atomically terminalizes the ledger as failed. Marking them
538
+ // terminal first would make idempotent cleanup skip a still-mounted path.
539
+ const ledgerFailure = implementationOutcome === 'succeeded'
540
+ ? (markWorktree(a, 'built') ?? undefined)
541
+ : undefined;
542
+ const settlementFailure = callbackFailure ?? ledgerFailure;
543
+ if (settlementFailure) {
544
+ markWorktree(a, 'needs-review');
545
+ return {
546
+ run: a,
547
+ implementationOutcome,
548
+ deliveryOutcome: 'blocked',
549
+ initialSha: a.initialSha,
550
+ finalSha,
551
+ changed,
552
+ failureCode: 'settlement_interrupted',
553
+ failureDetail: settlementFailure,
554
+ safeToRelease: false,
555
+ };
556
+ }
557
+ if (implementationOutcome === 'failed') {
558
+ return {
559
+ run: a,
560
+ implementationOutcome,
561
+ deliveryOutcome: 'not_started',
562
+ initialSha: a.initialSha,
563
+ finalSha,
564
+ changed,
565
+ ...(engineFailure ? { failureDetail: engineFailure } : {}),
566
+ safeToRelease: a.automaticRelease !== false,
567
+ };
568
+ }
569
+ // A resumed non-PR branch may already contain reviewable commits from an
570
+ // earlier attempt, even when this iteration itself did not add one.
571
+ let resumedBranchDelta = null;
572
+ if (!changed && a.continuationTarget === null && a.branchOwnership === 'preexisting') {
573
+ resumedBranchDelta = await branchHasDelta(git, a.handle.worktreePath, a.baseRef);
574
+ if (resumedBranchDelta === null) {
575
+ const detail = `cannot prove whether resumed branch ${a.handle.branch} has commits ahead of ${a.baseRef}`;
576
+ markWorktree(a, 'needs-review');
577
+ return {
578
+ run: a,
579
+ implementationOutcome,
580
+ deliveryOutcome: 'blocked',
581
+ initialSha: a.initialSha,
582
+ finalSha,
583
+ changed,
584
+ failureCode: 'branch_verification_failed',
585
+ failureDetail: detail,
586
+ safeToRelease: false,
587
+ };
588
+ }
589
+ }
590
+ const noChanges = !changed && Boolean(a.initialSha && finalSha && a.initialSha === finalSha && (a.continuationTarget !== null ||
591
+ a.branchOwnership === 'created' ||
592
+ resumedBranchDelta === false));
593
+ return {
594
+ run: a,
595
+ implementationOutcome,
596
+ deliveryOutcome: noChanges ? 'no_changes' : 'ready',
597
+ initialSha: a.initialSha,
598
+ finalSha,
599
+ changed,
600
+ safeToRelease: a.automaticRelease !== false,
601
+ };
602
+ };
603
+ }
395
604
  async function launchIsolatedRail(input, io = {}) {
396
605
  const { ctx, railIndex, ticketIds, loopId, loopName, loopGraph, provider, model, effort, deciderEngine } = input;
397
606
  let expectedRepositoryBaseSha = input.repositoryExecution?.expectedBaseSha;
@@ -936,6 +1145,19 @@ async function launchIsolatedRail(input, io = {}) {
936
1145
  throw err;
937
1146
  }
938
1147
  }
1148
+ // Durable admission is part of allocation: if any snapshot cannot commit,
1149
+ // roll back the entire batch before releasing the repository lock or
1150
+ // starting Core. The same ownership-aware cleanup handles that failure.
1151
+ if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph))
1152
+ ctx.db.transaction(() => {
1153
+ for (const run of allocated)
1154
+ (0, isolated_settlement_store_1.saveIsolatedSettlementSnapshot)(ctx.db, {
1155
+ version: 1, projectId: ctx.project.id, deliveryId: prDeliveryId, baseRepo,
1156
+ overlaySourceRoot, overlayFallbackRoots, overlayProviderDir, overlayInstructions,
1157
+ commitMessage: worktreeCommitMessage(ctx, run.ticketId, run.runId),
1158
+ partialCommitMessage: worktreeCommitMessage(ctx, run.ticketId, run.runId, true), run,
1159
+ });
1160
+ })();
939
1161
  }
940
1162
  catch (err) {
941
1163
  for (const a of allocated) {
@@ -997,7 +1219,7 @@ async function launchIsolatedRail(input, io = {}) {
997
1219
  return;
998
1220
  provenanceRecorded.add(a.runId);
999
1221
  try {
1000
- recordProvenance({
1222
+ const record = () => recordProvenance({
1001
1223
  db: ctx.db,
1002
1224
  projectId: ctx.project.id,
1003
1225
  runId: a.runId,
@@ -1007,6 +1229,10 @@ async function launchIsolatedRail(input, io = {}) {
1007
1229
  snapshot: a.provenanceSnapshot,
1008
1230
  broadcast: (msg) => ctx.broadcast(msg),
1009
1231
  });
1232
+ if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph))
1233
+ (0, isolated_settlement_store_1.recordIsolatedProvenanceOnce)(ctx.db, prDeliveryId, a.runId, record);
1234
+ else
1235
+ record();
1010
1236
  }
1011
1237
  catch (err) {
1012
1238
  console.warn(`[rail-isolated] provenance recording failed for ${a.runId}: ${errorDetail(err)}`);
@@ -1022,206 +1248,15 @@ async function launchIsolatedRail(input, io = {}) {
1022
1248
  return errorDetail(err);
1023
1249
  }
1024
1250
  };
1025
- /** This function resolves for every allocated unit. Engine truth is captured
1026
- * before any delivery effect and is the only value sent to the deferred
1027
- * terminal callback. Delivery failures become blocked evidence instead. */
1028
- const settleAllocatedRun = async (a, enginePromise) => {
1029
- let actualOutcome = 'failed';
1030
- let engineFailure;
1031
- let stallReason;
1032
- try {
1033
- const result = await enginePromise;
1034
- actualOutcome = result.outcome;
1035
- stallReason = result.stallReason;
1036
- }
1037
- catch (err) {
1038
- engineFailure = errorDetail(err);
1039
- console.error(`[rail-isolated] loop run ${a.runId} rejected: ${engineFailure}`);
1040
- }
1041
- if (input.repositoryExecution?.isAborted?.()) {
1042
- // The coordinator never started. Do not commit incidental edits or
1043
- // release a mount borrowed from a previous delivery while rolling back.
1044
- const finalSha = await readHeadSha(git, a.handle.worktreePath);
1045
- if (a.worktreeOwnership !== 'created')
1046
- markWorktree(a, 'needs-review');
1047
- return {
1048
- run: a, implementationOutcome: 'failed', deliveryOutcome: 'not_started',
1049
- initialSha: a.initialSha, finalSha, changed: Boolean(finalSha && a.initialSha !== finalSha),
1050
- safeToRelease: a.worktreeOwnership === 'created' && finalSha !== null && finalSha === a.initialSha,
1051
- };
1052
- }
1053
- const implementationOutcome = actualOutcome === 'success' ? 'succeeded' : 'failed';
1054
- recordRunProvenance(a);
1055
- let callbackFailure;
1056
- try {
1057
- ctx.onLoopRunFinished(a.runId, actualOutcome, { ...runFinishedOpts, ...(stallReason ? { stallReason } : {}) });
1058
- }
1059
- catch (err) {
1060
- callbackFailure = errorDetail(err);
1061
- console.error(`[rail-isolated] terminal callback failed for ${a.runId}: ${callbackFailure}`);
1062
- }
1063
- // A paused/interrupted programmatic workflow owns unfinished changes and
1064
- // verification evidence in this exact mount. Keep them available for
1065
- // explicit Core recovery, including a currently clean worktree.
1066
- if (implementationOutcome === 'failed' && (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(input.runtimeStateProject ?? ctx.project, a.runId)) {
1067
- markWorktree(a, 'needs-review');
1068
- return {
1069
- run: a, implementationOutcome, deliveryOutcome: 'blocked',
1070
- initialSha: a.initialSha, finalSha: await readHeadSha(git, a.handle.worktreePath),
1071
- failureCode: 'settlement_interrupted',
1072
- failureDetail: 'Programmatic workflow needs attention. Resume it from Agent Runtime settings in its original worktree.',
1073
- safeToRelease: false,
1074
- };
1075
- }
1076
- // The engine may have edited a copied overlay file. Re-authenticate only
1077
- // automatic-cleanup authority. Allocation-time paths remain conservative
1078
- // NEVER-COMMIT exclusions, while a modified copy is preserved in the
1079
- // worktree instead of being silently staged or deleted.
1080
- if (a.overlayCleanupEvidence.length > 0) {
1081
- a.overlayCleanupEvidence = (0, worktree_overlay_1.revalidateOverlayCleanupEvidence)({
1082
- worktreePath: a.handle.worktreePath,
1083
- sourceRoot: overlaySourceRoot,
1084
- fallbackSourceRoots: overlayFallbackRoots,
1085
- providerDir: overlayProviderDir,
1086
- instructionsFilename: overlayInstructions,
1087
- }, a.overlayCleanupEvidence);
1088
- }
1089
- // Warm-dependency links are re-proven against the base repo (their source),
1090
- // not the framework overlay source. A link the engine replaced with a real
1091
- // install loses its authorization here and correctly preserves the worktree.
1092
- if (a.warmLinkEvidence.length > 0) {
1093
- a.warmLinkEvidence = (0, worktree_node_modules_1.authenticateWarmNodeModulesLinks)(baseRepo, a.handle.worktreePath)
1094
- .filter((live) => a.warmLinkEvidence.some((prior) => prior.path === live.path && prior.kind === live.kind && prior.digest === live.digest));
1095
- }
1096
- let commit;
1097
- try {
1098
- commit = await (0, worktree_manager_1.commitWorktreeAndVerify)(git, a.handle.worktreePath, worktreeCommitMessage(ctx, a.ticketId, a.runId, implementationOutcome === 'failed'), a.overlayExcludes);
1099
- }
1100
- catch (err) {
1101
- commit = { staged: false, committed: false, clean: false, dirty: [], error: errorDetail(err) };
1102
- }
1103
- if (!commit.clean) {
1104
- const detail = commitFailureSummary(commit);
1105
- console.error(`[rail-isolated] run ${a.runId} left an unsafe worktree: ${detail}`);
1106
- markWorktree(a, 'needs-review');
1107
- return {
1108
- run: a,
1109
- implementationOutcome,
1110
- deliveryOutcome: 'blocked',
1111
- initialSha: a.initialSha,
1112
- finalSha: await readHeadSha(git, a.handle.worktreePath),
1113
- failureCode: 'commit_failed',
1114
- failureDetail: detail,
1115
- safeToRelease: false,
1116
- };
1117
- }
1118
- // The worktree is proven clean THIS instant — freeze the ignored-path set
1119
- // as the settlement snapshot. Everything ignored right now is run residue
1120
- // by construction (the worktree is app-created and deliverables are
1121
- // committed); anything ignored that appears LATER preserves the worktree.
1122
- a.settlementIgnoredPaths = await captureSettlementIgnoredPaths(git, a.handle.worktreePath, a.overlayExcludes);
1123
- let finalSha = null;
1124
- let verificationFailure;
1125
- if (a.continuationTarget) {
1126
- try {
1127
- finalSha = await verifyContinuationWorktree(git, baseRepo, a.handle, a.continuationTarget);
1128
- }
1129
- catch (err) {
1130
- verificationFailure = errorDetail(err);
1131
- }
1132
- }
1133
- else {
1134
- finalSha = await readHeadSha(git, a.handle.worktreePath);
1135
- }
1136
- const changed = commit.committed || Boolean(a.initialSha && finalSha && a.initialSha !== finalSha);
1137
- // A successful commit without a provable object is not retryable or safe to
1138
- // detach. A continuation additionally requires HEAD == refs/heads/<branch>.
1139
- if (verificationFailure || !finalSha || (!commit.committed && a.branchOwnership === 'created' && !a.initialSha)) {
1140
- const detail = verificationFailure
1141
- ?? (!finalSha
1142
- ? 'the final HEAD object could not be verified'
1143
- : 'the initial HEAD object was not captured, so a no-change result cannot be proven');
1144
- console.error(`[rail-isolated] run ${a.runId} finished on an unverified ref: ${detail}`);
1145
- markWorktree(a, 'needs-review');
1146
- return {
1147
- run: a,
1148
- implementationOutcome,
1149
- deliveryOutcome: 'blocked',
1150
- initialSha: a.initialSha,
1151
- finalSha,
1152
- changed,
1153
- failureCode: 'branch_verification_failed',
1154
- failureDetail: detail,
1155
- safeToRelease: false,
1156
- };
1157
- }
1158
- // Failed clean units remain `building` until aggregate cleanup removes the
1159
- // mount and atomically terminalizes the ledger as failed. Marking them
1160
- // terminal first would make idempotent cleanup skip a still-mounted path.
1161
- const ledgerFailure = implementationOutcome === 'succeeded'
1162
- ? (markWorktree(a, 'built') ?? undefined)
1163
- : undefined;
1164
- const settlementFailure = callbackFailure ?? ledgerFailure;
1165
- if (settlementFailure) {
1166
- markWorktree(a, 'needs-review');
1167
- return {
1168
- run: a,
1169
- implementationOutcome,
1170
- deliveryOutcome: 'blocked',
1171
- initialSha: a.initialSha,
1172
- finalSha,
1173
- changed,
1174
- failureCode: 'settlement_interrupted',
1175
- failureDetail: settlementFailure,
1176
- safeToRelease: false,
1177
- };
1178
- }
1179
- if (implementationOutcome === 'failed') {
1180
- return {
1181
- run: a,
1182
- implementationOutcome,
1183
- deliveryOutcome: 'not_started',
1184
- initialSha: a.initialSha,
1185
- finalSha,
1186
- changed,
1187
- ...(engineFailure ? { failureDetail: engineFailure } : {}),
1188
- safeToRelease: true,
1189
- };
1190
- }
1191
- // A resumed non-PR branch may already contain reviewable commits from an
1192
- // earlier attempt, even when this iteration itself did not add one.
1193
- let resumedBranchDelta = null;
1194
- if (!changed && a.continuationTarget === null && a.branchOwnership === 'preexisting') {
1195
- resumedBranchDelta = await branchHasDelta(git, a.handle.worktreePath, a.baseRef);
1196
- if (resumedBranchDelta === null) {
1197
- const detail = `cannot prove whether resumed branch ${a.handle.branch} has commits ahead of ${a.baseRef}`;
1198
- markWorktree(a, 'needs-review');
1199
- return {
1200
- run: a,
1201
- implementationOutcome,
1202
- deliveryOutcome: 'blocked',
1203
- initialSha: a.initialSha,
1204
- finalSha,
1205
- changed,
1206
- failureCode: 'branch_verification_failed',
1207
- failureDetail: detail,
1208
- safeToRelease: false,
1209
- };
1210
- }
1211
- }
1212
- const noChanges = !changed && Boolean(a.initialSha && finalSha && a.initialSha === finalSha && (a.continuationTarget !== null ||
1213
- a.branchOwnership === 'created' ||
1214
- resumedBranchDelta === false));
1215
- return {
1216
- run: a,
1217
- implementationOutcome,
1218
- deliveryOutcome: noChanges ? 'no_changes' : 'ready',
1219
- initialSha: a.initialSha,
1220
- finalSha,
1221
- changed,
1222
- safeToRelease: true,
1223
- };
1224
- };
1251
+ const settleAllocatedRun = createIsolatedRunSettlement({
1252
+ git, baseRepo, overlaySourceRoot, overlayFallbackRoots, overlayProviderDir, overlayInstructions,
1253
+ isAborted: input.repositoryExecution?.isAborted,
1254
+ ownsRun: runId => !(0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, runId),
1255
+ onFinished: (runId, outcome, stallReason) => ctx.onLoopRunFinished(runId, outcome, { ...runFinishedOpts, ...(stallReason ? { stallReason } : {}) }),
1256
+ recordRunProvenance, markWorktree,
1257
+ hasRuntimeRequest: runId => (0, agent_runtime_paths_1.hasAgentRuntimeRequest)(input.runtimeStateProject ?? ctx.project, runId),
1258
+ commitMessage: (run, partial) => worktreeCommitMessage(ctx, run.ticketId, run.runId, partial),
1259
+ });
1225
1260
  const runPromises = [];
1226
1261
  for (const a of allocated) {
1227
1262
  ctx.railLoopRuns.set(a.runId, {
@@ -1259,7 +1294,14 @@ async function launchIsolatedRail(input, io = {}) {
1259
1294
  ...(input.followUp ? { followUp: { id: input.followUp.id, version: input.followUp.version, hash: input.followUp.hash, openspecChangeName: input.followUp.openspecChangeName ?? null, briefing: (0, pr_follow_up_1.renderFollowUpBriefing)(input.followUp, { prNumber: launchContinuation?.prNumber ?? input.explicitPrTarget?.prNumber ?? null, ticketIds }) } } : {}),
1260
1295
  ...(claimedAddenda.briefing ? { addenda: { ids: claimedAddenda.snapshot.map((e) => e.id), briefing: claimedAddenda.briefing } } : {}),
1261
1296
  });
1262
- runPromises.push(settleAllocatedRun(a, enginePromise));
1297
+ runPromises.push(settleAllocatedRun(a, enginePromise).then(async (result) => {
1298
+ if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph))
1299
+ (0, isolated_settlement_store_1.saveIsolatedSettlementResult)(ctx.db, prDeliveryId, result);
1300
+ if (prDeliveryId && (0, loop_graph_1.isDefinitionGraph)(loopGraph) && allocated.some(unit => (0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, unit.runId))) {
1301
+ await reattachIsolatedSettlement(ctx, prDeliveryId, a.runId).catch(error => console.error('[rail-isolated] fork sibling settlement remains pending:', error));
1302
+ }
1303
+ return result;
1304
+ }));
1263
1305
  try {
1264
1306
  ctx.jiraSyncManager.onRailLaunch(a.ticketIds, a.runId);
1265
1307
  }
@@ -1282,6 +1324,8 @@ async function launchIsolatedRail(input, io = {}) {
1282
1324
  // the draft PR is only created when the user clicks [Create PR] on the
1283
1325
  // pr-decision endpoint (ask-first, safe-pr-review-flow).
1284
1326
  const guardedRunPromises = runPromises.map((promise, index) => promise.catch(async (err) => {
1327
+ if ((0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, allocated[index].runId))
1328
+ return { run: allocated[index], implementationOutcome: 'failed', deliveryOutcome: 'not_started', initialSha: allocated[index].initialSha, finalSha: null, safeToRelease: false };
1285
1329
  const run = allocated[index];
1286
1330
  const detail = errorDetail(err);
1287
1331
  console.error(`[rail-isolated] unexpected settlement rejection for ${run.runId}: ${detail}`);
@@ -1300,6 +1344,8 @@ async function launchIsolatedRail(input, io = {}) {
1300
1344
  };
1301
1345
  }));
1302
1346
  void Promise.all(guardedRunPromises).then(async (settledResults) => {
1347
+ if (allocated.some(unit => (0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, unit.runId)))
1348
+ return;
1303
1349
  // Cardinality is invariant: one structured result per allocated unit. The
1304
1350
  // guarded promises above synthesize a blocked record for any rejection.
1305
1351
  let results = settledResults;
@@ -1590,9 +1636,12 @@ async function launchIsolatedRail(input, io = {}) {
1590
1636
  const harvest = io.harvestEvidence ?? delivery_evidence_1.harvestDeliveryEvidence;
1591
1637
  let settleEvidence;
1592
1638
  try {
1639
+ const definitionEvidence = await (0, loop_definition_recovery_1.probeDefinitionRuns)({ db: ctx.db, cwd: ctx.project.path, env: process.env }, results.filter(result => (0, loop_runs_store_1.getLoopRun)(ctx.db, result.run.runId)?.engine_version === 2).map(result => result.run.runId), true);
1593
1640
  settleEvidence = harvest({ readEvents: (runId) => (0, db_1.getJobEvents)(ctx.db, runId) }, results.map((result) => ({
1594
1641
  ticketId: result.run.ticketId,
1595
1642
  runId: result.run.runId,
1643
+ definitionStatus: definitionEvidence.get(result.run.runId),
1644
+ reviewerStepId: (0, loop_runs_store_1.readDefinitionRun)(ctx.db, result.run.runId)?.request.graph.config.reviewerStepId,
1596
1645
  worktreePath: result.run.handle.worktreePath,
1597
1646
  loopId,
1598
1647
  // Programmatic-runtime evidence (host-run verification, reviewer
@@ -1674,6 +1723,8 @@ async function launchIsolatedRail(input, io = {}) {
1674
1723
  succeeded: result.deliveryOutcome === 'ready',
1675
1724
  })));
1676
1725
  try {
1726
+ if (branches.length)
1727
+ (0, legacy_launch_telemetry_1.recordLegacyLaunch)(ctx.db, { kind: 'merge_back', projectId: ctx.project.id, runId: allocated[0]?.runId });
1677
1728
  const outcomes = await (0, rail_merge_orchestrator_1.runMergeBack)({
1678
1729
  git, executor: (0, loop_executors_1.createLoopExecutors)(), baseDir: baseRepo,
1679
1730
  provider, model, effort, ...(deciderEngine ? { deciderEngine } : {}), constants, branches,
@@ -1723,6 +1774,8 @@ async function launchIsolatedRail(input, io = {}) {
1723
1774
  console.error('[rail-isolated] merge-back failed:', err);
1724
1775
  }
1725
1776
  }).catch(async (err) => {
1777
+ if (allocated.some(unit => (0, loop_runs_store_1.readDefinitionForkTarget)(ctx.db, unit.runId)))
1778
+ return;
1726
1779
  const detail = errorDetail(err);
1727
1780
  console.error(`[rail-isolated] aggregate settlement failed: ${detail}`);
1728
1781
  if (!prMode || !prDeliveryId || (0, rail_pr_store_1.getPrDelivery)(ctx.db, prDeliveryId)?.decision !== 'building')
@@ -1750,6 +1803,138 @@ async function launchIsolatedRail(input, io = {}) {
1750
1803
  }).finally(() => { input.repositoryExecution?.onSettled(prDeliveryId); });
1751
1804
  return allocated.map((a) => a.runId);
1752
1805
  }
1806
+ /** Git and Node can spell the same Windows directory differently. Compare its
1807
+ * filesystem identity instead of case-folding paths (which could admit another
1808
+ * repository on a case-sensitive volume). */
1809
+ function sameRecoveryDirectory(observed, expected) {
1810
+ const left = fs.statSync(observed, { bigint: true });
1811
+ const right = fs.statSync(expected, { bigint: true });
1812
+ if (!left.isDirectory() || !right.isDirectory())
1813
+ return false;
1814
+ if (left.ino !== 0n && right.ino !== 0n)
1815
+ return left.dev === right.dev && left.ino === right.ino;
1816
+ return fs.realpathSync.native(observed) === fs.realpathSync.native(expected);
1817
+ }
1818
+ /** Reattach the original allocation after Core finishes; no new generation or remote effects. */
1819
+ async function reattachIsolatedSettlement(ctx, deliveryId, runId, io = {}) {
1820
+ const frozen = (0, loop_runs_store_1.readDefinitionRun)(ctx.db, runId);
1821
+ if (!frozen || frozen.row.project_id !== ctx.project.id || frozen.row.status !== 'completed')
1822
+ throw new Error('Recovered workflow must finish before delivery settlement');
1823
+ (0, isolated_settlement_reconstruction_1.ensureIsolatedSettlementSnapshot)(ctx.db, ctx.project.id, runId);
1824
+ const ids = ctx.db.prepare('SELECT delivery_id FROM definition_delivery_settlements WHERE project_id=? AND run_id=? ORDER BY delivery_id')
1825
+ .all(ctx.project.id, runId);
1826
+ const rows = ids.map(({ delivery_id }) => (0, rail_pr_store_1.getPrDelivery)(ctx.db, delivery_id));
1827
+ if (!rows.length || rows.some(row => !row) || !rows.some(row => row.id === deliveryId || row.parent_delivery_id === deliveryId))
1828
+ throw new Error('Original isolated settlement is unavailable');
1829
+ const deliveries = rows.filter((row) => !!row);
1830
+ if (deliveries.some(row => ['discarded', 'superseded', 'merged', 'completed'].includes(row.decision)))
1831
+ throw new Error('Delivery ownership has already changed');
1832
+ const parents = [...new Set(deliveries.map(row => row.parent_delivery_id).filter((id) => !!id))];
1833
+ const allRows = [...deliveries, ...parents.map(id => (0, rail_pr_store_1.getPrDelivery)(ctx.db, id)).filter((row) => !!row)];
1834
+ if (allRows.some(row => ['discarded', 'superseded', 'merged', 'completed'].includes(row.decision)))
1835
+ throw new Error('Delivery group ownership has already changed');
1836
+ const pending = deliveries.filter(row => !['on_review', 'no_changes', 'pr_draft', 'pr_ready'].includes(row.decision));
1837
+ if (!pending.length) {
1838
+ ctx.onLoopRunFinished(runId, frozen.row.final_outcome ?? 'failed', { ticketCompletionStatus: 'on_review' });
1839
+ return;
1840
+ }
1841
+ // Terminal admission must not depend on optional, potentially large evidence
1842
+ // outputs. A failed full-status harvest stays visible in the review packet.
1843
+ const probe = await (0, loop_definition_recovery_1.probeDefinitionRun)({ db: ctx.db, cwd: ctx.project.path, env: process.env }, runId);
1844
+ if (probe.status === 'unavailable' || probe.lease?.active || ['paused', 'running'].includes(probe.status))
1845
+ throw new Error('Core has not provided an inactive terminal result');
1846
+ const policy = frozen.metadata.definition?.delivery;
1847
+ const accepted = probe.status === 'succeeded' && probe.completion?.ok === true && (policy?.requiresVerified === false || probe.completion.verified);
1848
+ const outcome = accepted ? 'success' : probe.status === 'cancelled' ? 'stopped' : 'blocked';
1849
+ const token = (0, ids_1.newId)();
1850
+ const execution = (0, loop_runs_store_1.claimDefinitionExecution)(ctx.db, runId, { owner: token, repositoryMounts: (0, loop_runs_store_1.definitionRepositoryMounts)(frozen.request) });
1851
+ if (!execution.ok)
1852
+ throw new Error(`runtime_run_active: ${execution.conflictingRunId}`);
1853
+ const claimed = [];
1854
+ const git = io.git ?? worktree_manager_1.defaultGitRunner;
1855
+ try {
1856
+ ctx.db.transaction(() => {
1857
+ for (const row of allRows) {
1858
+ if (!(0, rail_pr_store_1.claimPrDeliveryOperation)(ctx.db, row.id, row.decision, 'recover-and-retry', token))
1859
+ throw new Error('Delivery is busy; retry settlement');
1860
+ claimed.push(row);
1861
+ }
1862
+ })();
1863
+ for (const row of pending) {
1864
+ const records = (0, isolated_settlement_store_1.readIsolatedSettlementRecords)(ctx.db, ctx.project.id, row.id);
1865
+ const record = records.find(item => item.snapshot.run.runId === runId);
1866
+ if (!record)
1867
+ throw new Error('Original isolated allocation is missing');
1868
+ const snapshot = record.snapshot, run = snapshot.run;
1869
+ const ledger = (0, rail_worktrees_store_1.getRailWorktree)(ctx.db, run.ledgerId);
1870
+ if (!ledger || ledger.run_id !== runId || ledger.branch !== run.handle.branch || ledger.worktree_path !== run.handle.worktreePath)
1871
+ throw new Error('Worktree ownership changed before recovery');
1872
+ const context = frozen.metadata.context ?? (frozen.metadata.contextPath ? JSON.parse(fs.readFileSync(frozen.metadata.contextPath, 'utf8')) : null);
1873
+ if (!context || context.runId !== runId || !Array.isArray(context.repositories) || !context.repositories.some((repo) => repo.path && fs.realpathSync(repo.path) === fs.realpathSync(run.handle.worktreePath)))
1874
+ throw new Error('Recovered mount is outside the frozen Core context');
1875
+ if (snapshot.reconstructedFrom) {
1876
+ const repository = frozen.request.executionManifest?.repositories.find(item => item.worktreeId === run.ledgerId);
1877
+ const common = await git.run(['rev-parse', '--path-format=absolute', '--git-common-dir'], run.handle.worktreePath);
1878
+ if (!repository || common.code !== 0 || !common.stdout.trim() ||
1879
+ !sameRecoveryDirectory(common.stdout.trim(), repository.gitCommonDir))
1880
+ throw new Error('Recovered worktree belongs to another Git repository');
1881
+ }
1882
+ const branch = await git.run(['branch', '--show-current'], run.handle.worktreePath);
1883
+ if (branch.code !== 0 || branch.stdout.trim() !== run.handle.branch)
1884
+ throw new Error('Recovered worktree is on another branch');
1885
+ const settle = createIsolatedRunSettlement({
1886
+ ...snapshot, git,
1887
+ onFinished: () => { },
1888
+ hasRuntimeRequest: () => true,
1889
+ commitMessage: (_run, partial) => partial ? snapshot.partialCommitMessage : snapshot.commitMessage,
1890
+ markWorktree: (unit, state) => (0, rail_worktrees_store_1.updateRailWorktreeState)(ctx.db, unit.ledgerId, state) ? null : 'Worktree ledger disappeared',
1891
+ recordRunProvenance: unit => (0, isolated_settlement_store_1.recordIsolatedProvenanceOnce)(ctx.db, row.id, runId, () => (io.recordProvenance ?? file_story_1.recordLoopRunProvenance)({
1892
+ db: ctx.db, projectId: ctx.project.id, runId, ticketId: unit.ticketId, repoDir: unit.handle.worktreePath,
1893
+ repositoryId: row.repository_id ?? undefined, snapshot: unit.provenanceSnapshot, broadcast: message => ctx.broadcast(message),
1894
+ })),
1895
+ });
1896
+ const result = await settle(run, Promise.resolve({ runId, outcome }));
1897
+ (0, isolated_settlement_store_1.saveIsolatedSettlementResult)(ctx.db, row.id, result);
1898
+ const latest = (0, isolated_settlement_store_1.readIsolatedSettlementRecords)(ctx.db, ctx.project.id, row.id);
1899
+ const results = latest.flatMap(item => item.result ? [item.result] : []);
1900
+ const complete = results.length === latest.length;
1901
+ const successes = results.filter(item => item.implementationOutcome === 'succeeded');
1902
+ const ready = results.filter(item => item.deliveryOutcome === 'ready');
1903
+ const noChanges = complete && results.every(item => item.deliveryOutcome === 'no_changes');
1904
+ const partial = complete && ready.length > 0 && results.some(item => item.implementationOutcome !== 'succeeded' || !['ready', 'no_changes'].includes(item.deliveryOutcome));
1905
+ const next = !complete ? 'building' : !successes.length ? 'implementation_failed' : row.pr_url ? 'pr_failed' : ready.length ? 'on_review' : noChanges ? 'no_changes' : 'pr_failed';
1906
+ const definitionEvidence = await (0, loop_definition_recovery_1.probeDefinitionRuns)({ db: ctx.db, cwd: ctx.project.path, env: process.env }, results.map(item => item.run.runId), true);
1907
+ const evidence = (io.harvestEvidence ?? delivery_evidence_1.harvestDeliveryEvidence)({ readEvents: id => (0, db_1.getJobEvents)(ctx.db, id) }, results.map(item => ({
1908
+ ticketId: item.run.ticketId, runId: item.run.runId, worktreePath: item.run.handle.worktreePath,
1909
+ definitionStatus: definitionEvidence.get(item.run.runId),
1910
+ reviewerStepId: (0, loop_runs_store_1.readDefinitionRun)(ctx.db, item.run.runId)?.request.graph.config.reviewerStepId,
1911
+ })));
1912
+ if (!(0, rail_pr_store_1.transitionClaimedDecision)(ctx.db, row.id, row.decision, next, token, {
1913
+ branches: branchRecords(results), runIds: latest.map(item => item.snapshot.run.runId), worktreeIds: latest.map(item => item.snapshot.run.ledgerId),
1914
+ implementationOutcome: !complete ? 'unknown' : successes.length === results.length ? 'succeeded' : successes.length ? 'partially_succeeded' : 'failed',
1915
+ deliveryOutcome: !complete ? 'not_started' : row.pr_url && ready.length ? 'retryable_failure' : ready.length ? (partial ? 'partial' : 'ready') : noChanges ? 'no_changes' : 'blocked',
1916
+ statusCode: !complete ? 'restart_pending' : row.pr_url && ready.length ? 'settlement_interrupted' : ready.length ? (partial ? 'partial_success' : 'ready_for_review') : noChanges ? 'no_changes' : 'implementation_failed',
1917
+ statusDetail: row.pr_url && ready.length ? 'Recovered local work is ready. Retry delivery to update the existing PR.' : null,
1918
+ ...(results.length === 1 ? { deliverySha: results[0].finalSha } : {}), settleEvidence: evidence,
1919
+ }))
1920
+ throw new Error('Delivery changed during recovered settlement');
1921
+ }
1922
+ const { refreshRepositoryDeliveryGroup } = await Promise.resolve().then(() => __importStar(require('./multi-repo-execution')));
1923
+ for (const parent of parents)
1924
+ refreshRepositoryDeliveryGroup(ctx.db, parent);
1925
+ ctx.onLoopRunFinished(runId, outcome, { ticketCompletionStatus: 'on_review' });
1926
+ }
1927
+ finally {
1928
+ for (const row of claimed)
1929
+ (0, rail_pr_store_1.releasePrDeliveryOperation)(ctx.db, row.id, token);
1930
+ execution.release();
1931
+ }
1932
+ for (const row of allRows) {
1933
+ const latest = (0, rail_pr_store_1.getPrDelivery)(ctx.db, row.id);
1934
+ if (latest)
1935
+ ctx.broadcast((0, rail_pr_store_1.toRailPrStateMessage)(ctx.project.id, (0, rail_pr_store_1.toPrDeliverySnapshot)(latest)));
1936
+ }
1937
+ }
1753
1938
  function recoveryOverlayExcludes(repoDir, worktreePath) {
1754
1939
  try {
1755
1940
  const parsed = JSON.parse(fs.readFileSync(path.join(worktreePath, worktree_overlay_1.OVERLAY_MANIFEST), 'utf8'));