gsd-pi 2.81.0 → 2.82.0-dev.ed17d078d

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 (503) hide show
  1. package/README.md +60 -30
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  4. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  5. package/dist/resources/extensions/gsd/auto/loop.js +111 -8
  6. package/dist/resources/extensions/gsd/auto/orchestrator.js +113 -6
  7. package/dist/resources/extensions/gsd/auto/phases.js +199 -97
  8. package/dist/resources/extensions/gsd/auto/run-unit.js +66 -3
  9. package/dist/resources/extensions/gsd/auto/session.js +9 -0
  10. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +43 -0
  11. package/dist/resources/extensions/gsd/auto-dashboard.js +182 -178
  12. package/dist/resources/extensions/gsd/auto-dispatch.js +14 -11
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -1
  14. package/dist/resources/extensions/gsd/auto-prompts.js +11 -3
  15. package/dist/resources/extensions/gsd/auto-recovery.js +6 -181
  16. package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -0
  17. package/dist/resources/extensions/gsd/auto-start.js +20 -23
  18. package/dist/resources/extensions/gsd/auto-unit-closeout.js +33 -5
  19. package/dist/resources/extensions/gsd/auto-verification.js +12 -6
  20. package/dist/resources/extensions/gsd/auto-worktree.js +8 -0
  21. package/dist/resources/extensions/gsd/auto.js +386 -106
  22. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +13 -6
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +13 -2
  24. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +4 -8
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  26. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -1
  27. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +4 -10
  28. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +9 -0
  29. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  30. package/dist/resources/extensions/gsd/context-store.js +112 -0
  31. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  32. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  33. package/dist/resources/extensions/gsd/doctor-git-checks.js +41 -6
  34. package/dist/resources/extensions/gsd/git-service.js +2 -1
  35. package/dist/resources/extensions/gsd/gsd-db.js +7 -23
  36. package/dist/resources/extensions/gsd/health-widget-core.js +1 -1
  37. package/dist/resources/extensions/gsd/health-widget.js +4 -10
  38. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  39. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  40. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  41. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  42. package/dist/resources/extensions/gsd/markdown-renderer.js +6 -96
  43. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  44. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  45. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  46. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  47. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  48. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  49. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  50. package/dist/resources/extensions/gsd/native-git-bridge.js +14 -14
  51. package/dist/resources/extensions/gsd/notification-overlay.js +35 -40
  52. package/dist/resources/extensions/gsd/parallel-merge.js +53 -30
  53. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +25 -33
  54. package/dist/resources/extensions/gsd/prompts/complete-slice.md +14 -12
  55. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
  56. package/dist/resources/extensions/gsd/prompts/discuss.md +20 -2
  57. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  58. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  59. package/dist/resources/extensions/gsd/recovery-classification.js +15 -1
  60. package/dist/resources/extensions/gsd/session-lock.js +40 -0
  61. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +131 -0
  62. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +247 -0
  63. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +50 -0
  64. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +87 -0
  65. package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +50 -0
  66. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +124 -0
  67. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +32 -0
  68. package/dist/resources/extensions/gsd/state-reconciliation/errors.js +41 -0
  69. package/dist/resources/extensions/gsd/state-reconciliation/index.js +99 -0
  70. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +24 -0
  71. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +43 -0
  72. package/dist/resources/extensions/gsd/state-reconciliation/types.js +3 -0
  73. package/dist/resources/extensions/gsd/state-reconciliation.js +5 -26
  74. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  75. package/dist/resources/extensions/gsd/tui/render-kit.js +74 -0
  76. package/dist/resources/extensions/gsd/watch/header-renderer.js +92 -69
  77. package/dist/resources/extensions/gsd/watch/splash-palette.js +10 -0
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  79. package/dist/resources/extensions/gsd/worktree-lifecycle.js +722 -316
  80. package/dist/resources/extensions/gsd/worktree-telemetry.js +3 -1
  81. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  82. package/dist/web/standalone/.next/BUILD_ID +1 -1
  83. package/dist/web/standalone/.next/app-path-routes-manifest.json +6 -6
  84. package/dist/web/standalone/.next/build-manifest.json +3 -3
  85. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  86. package/dist/web/standalone/.next/required-server-files.json +3 -3
  87. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  88. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  98. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  126. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  146. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  156. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  162. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  178. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  182. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/index.html +1 -1
  192. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  193. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  194. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  195. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  196. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  197. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  198. package/dist/web/standalone/.next/server/app/page.js +2 -2
  199. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app-paths-manifest.json +6 -6
  201. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  202. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  203. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/middleware.js +2 -2
  205. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  207. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  208. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  209. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  210. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  211. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  212. package/dist/web/standalone/.next/static/chunks/app/page-752f1e2ebdaa3e45.js +1 -0
  213. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  214. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  215. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  216. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  217. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  218. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  219. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  220. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  221. package/dist/web/standalone/server.js +1 -1
  222. package/dist/welcome-screen.d.ts +0 -7
  223. package/dist/welcome-screen.js +60 -69
  224. package/package.json +3 -2
  225. package/packages/daemon/package.json +2 -2
  226. package/packages/mcp-server/README.md +2 -0
  227. package/packages/mcp-server/package.json +2 -2
  228. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  229. package/packages/native/package.json +1 -1
  230. package/packages/pi-agent-core/package.json +1 -1
  231. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  232. package/packages/pi-ai/dist/index.d.ts +2 -2
  233. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  234. package/packages/pi-ai/dist/index.js +1 -1
  235. package/packages/pi-ai/dist/index.js.map +1 -1
  236. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  237. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  238. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  239. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  240. package/packages/pi-ai/package.json +1 -1
  241. package/packages/pi-ai/src/index.ts +7 -2
  242. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  243. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  244. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  245. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts +2 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts.map +1 -0
  248. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js +47 -0
  249. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js.map +1 -0
  250. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +76 -9
  251. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts +2 -0
  253. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts.map +1 -0
  254. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js +40 -0
  255. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js.map +1 -0
  256. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +0 -1
  257. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -1
  258. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +30 -29
  259. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
  260. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +10 -3
  261. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -1
  262. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  263. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -13
  264. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  265. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -3
  266. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  267. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +58 -3
  268. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  269. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +2 -2
  270. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
  271. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +12 -6
  272. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  273. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  274. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +14 -41
  275. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  276. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +0 -1
  277. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  278. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +86 -82
  279. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  280. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts +35 -0
  281. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts.map +1 -0
  282. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js +152 -0
  283. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js.map +1 -0
  284. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts +16 -0
  285. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts.map +1 -0
  286. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js +73 -0
  287. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js.map +1 -0
  288. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +1 -1
  289. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  290. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +12 -8
  291. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
  293. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
  294. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
  295. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
  296. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +105 -1
  298. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  299. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +27 -26
  301. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +9 -6
  303. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
  304. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  305. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  306. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  307. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  308. package/packages/pi-coding-agent/package.json +1 -1
  309. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  310. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/assistant-message-design.test.ts +56 -0
  311. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +113 -9
  312. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/user-message-design.test.ts +48 -0
  313. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +10 -3
  314. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +43 -42
  315. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -14
  316. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +64 -3
  317. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +13 -7
  318. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +15 -42
  319. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -104
  320. package/packages/pi-coding-agent/src/modes/interactive/components/transcript-design.ts +196 -0
  321. package/packages/pi-coding-agent/src/modes/interactive/components/tui-style-kit.ts +94 -0
  322. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +14 -9
  323. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-highlight.test.ts +23 -0
  324. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +106 -1
  325. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +27 -26
  326. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +9 -6
  327. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  328. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  329. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +14 -1
  330. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  331. package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
  332. package/packages/pi-tui/dist/overlay-layout.js +9 -6
  333. package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
  334. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  335. package/packages/pi-tui/dist/tui.js +5 -0
  336. package/packages/pi-tui/dist/tui.js.map +1 -1
  337. package/packages/pi-tui/package.json +1 -1
  338. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +20 -1
  339. package/packages/pi-tui/src/overlay-layout.ts +10 -7
  340. package/packages/pi-tui/src/tui.ts +6 -0
  341. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  342. package/packages/rpc-client/package.json +1 -1
  343. package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
  344. package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
  345. package/pkg/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
  346. package/pkg/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
  347. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  348. package/pkg/dist/modes/interactive/theme/theme.js +105 -1
  349. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  350. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  351. package/pkg/dist/modes/interactive/theme/themes.js +27 -26
  352. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  353. package/pkg/package.json +1 -1
  354. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  355. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  356. package/src/resources/extensions/gsd/auto/contracts.ts +46 -11
  357. package/src/resources/extensions/gsd/auto/loop-deps.ts +9 -5
  358. package/src/resources/extensions/gsd/auto/loop.ts +113 -9
  359. package/src/resources/extensions/gsd/auto/orchestrator.ts +118 -6
  360. package/src/resources/extensions/gsd/auto/phases.ts +158 -19
  361. package/src/resources/extensions/gsd/auto/run-unit.ts +69 -4
  362. package/src/resources/extensions/gsd/auto/session.ts +10 -0
  363. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +82 -0
  364. package/src/resources/extensions/gsd/auto-dashboard.ts +230 -183
  365. package/src/resources/extensions/gsd/auto-dispatch.ts +15 -1
  366. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -1
  367. package/src/resources/extensions/gsd/auto-prompts.ts +11 -3
  368. package/src/resources/extensions/gsd/auto-recovery.ts +7 -209
  369. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  370. package/src/resources/extensions/gsd/auto-start.ts +22 -22
  371. package/src/resources/extensions/gsd/auto-unit-closeout.ts +51 -0
  372. package/src/resources/extensions/gsd/auto-verification.ts +12 -6
  373. package/src/resources/extensions/gsd/auto-worktree.ts +8 -0
  374. package/src/resources/extensions/gsd/auto.ts +424 -106
  375. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +21 -6
  376. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +12 -2
  377. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +5 -8
  378. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  379. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -1
  380. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +4 -10
  381. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +12 -0
  382. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  383. package/src/resources/extensions/gsd/context-store.ts +120 -1
  384. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  385. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  386. package/src/resources/extensions/gsd/doctor-git-checks.ts +44 -6
  387. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  388. package/src/resources/extensions/gsd/git-service.ts +2 -0
  389. package/src/resources/extensions/gsd/gsd-db.ts +7 -23
  390. package/src/resources/extensions/gsd/health-widget-core.ts +1 -1
  391. package/src/resources/extensions/gsd/health-widget.ts +6 -10
  392. package/src/resources/extensions/gsd/journal.ts +2 -0
  393. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  394. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  395. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  396. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  397. package/src/resources/extensions/gsd/markdown-renderer.ts +10 -96
  398. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  399. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  400. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  401. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  402. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  403. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  404. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  405. package/src/resources/extensions/gsd/native-git-bridge.ts +14 -13
  406. package/src/resources/extensions/gsd/notification-overlay.ts +50 -46
  407. package/src/resources/extensions/gsd/parallel-merge.ts +61 -34
  408. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +33 -35
  409. package/src/resources/extensions/gsd/prompts/complete-slice.md +14 -12
  410. package/src/resources/extensions/gsd/prompts/discuss-headless.md +20 -2
  411. package/src/resources/extensions/gsd/prompts/discuss.md +20 -2
  412. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  413. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  414. package/src/resources/extensions/gsd/recovery-classification.ts +18 -1
  415. package/src/resources/extensions/gsd/session-lock.ts +41 -0
  416. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +172 -0
  417. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +337 -0
  418. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +69 -0
  419. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +109 -0
  420. package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +68 -0
  421. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +185 -0
  422. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +46 -0
  423. package/src/resources/extensions/gsd/state-reconciliation/errors.ts +67 -0
  424. package/src/resources/extensions/gsd/state-reconciliation/index.ts +142 -0
  425. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +27 -0
  426. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +60 -0
  427. package/src/resources/extensions/gsd/state-reconciliation/types.ts +83 -0
  428. package/src/resources/extensions/gsd/state-reconciliation.ts +21 -53
  429. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  430. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +1 -1
  431. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +99 -0
  432. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +729 -176
  433. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +408 -4
  434. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +291 -4
  435. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +20 -5
  436. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +18 -0
  437. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +68 -0
  438. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  439. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  440. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +28 -1
  441. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  442. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  443. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  444. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +20 -2
  445. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +44 -0
  446. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  447. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  448. package/src/resources/extensions/gsd/tests/header-renderer.test.ts +40 -0
  449. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +10 -0
  450. package/src/resources/extensions/gsd/tests/health-widget.test.ts +14 -4
  451. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  452. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +26 -0
  453. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  454. package/src/resources/extensions/gsd/tests/integration/integration-proof.test.ts +1 -1
  455. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  456. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +116 -24
  457. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -1
  458. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  459. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  460. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  461. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  462. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1 -1
  463. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  464. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +46 -11
  465. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  466. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  467. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +78 -41
  468. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +44 -0
  469. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +12 -217
  470. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +38 -6
  471. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  472. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +2 -2
  473. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +1 -1
  474. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +32 -1
  475. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  476. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +7 -3
  477. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +6 -3
  478. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  479. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +24 -0
  480. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +65 -58
  481. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +952 -0
  482. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -0
  483. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +121 -1
  484. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +66 -0
  485. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +83 -0
  486. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +6 -0
  487. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +158 -58
  488. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +572 -118
  489. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +59 -2
  490. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +18 -0
  491. package/src/resources/extensions/gsd/tui/render-kit.ts +109 -0
  492. package/src/resources/extensions/gsd/watch/header-renderer.ts +121 -79
  493. package/src/resources/extensions/gsd/watch/splash-palette.ts +11 -0
  494. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  495. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  496. package/src/resources/extensions/gsd/worktree-lifecycle.ts +1151 -524
  497. package/src/resources/extensions/gsd/worktree-telemetry.ts +7 -2
  498. package/dist/web/standalone/.next/static/chunks/app/page-200592a7f3baf579.js +0 -1
  499. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  500. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  501. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +0 -1544
  502. /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → YEvjuT-fsFfYQhDSWtueS}/_buildManifest.js +0 -0
  503. /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → YEvjuT-fsFfYQhDSWtueS}/_ssgManifest.js +0 -0
@@ -16,7 +16,7 @@
16
16
  * call the same body during its internal `mergeAndEnterNext` recursion without
17
17
  * a circular reference. Both classes share the body until the Resolver retires.
18
18
  */
19
- import { existsSync, unlinkSync } from "node:fs";
19
+ import { existsSync, readFileSync, unlinkSync } from "node:fs";
20
20
  import { randomUUID } from "node:crypto";
21
21
  import { join } from "node:path";
22
22
  import { debugLog } from "./debug-logger.js";
@@ -26,8 +26,28 @@ import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare, } from "./
26
26
  import { claimMilestoneLease, refreshMilestoneLease, releaseMilestoneLease, } from "./db/milestone-leases.js";
27
27
  import { MergeConflictError } from "./git-service.js";
28
28
  import { getCollapseCadence, getMilestoneResquash, resquashMilestoneOnMain, } from "./slice-cadence.js";
29
- import { loadEffectiveGSDPreferences } from "./preferences.js";
29
+ // ADR-016 phase 2 / C3 (#5626): cache + preferences + path helpers inlined
30
+ // as direct imports. They are leaf-level functions that do not vary across
31
+ // callers — production wiring previously injected them via deps; the seam
32
+ // added type churn without enabling test variation.
33
+ import { loadEffectiveGSDPreferences, getIsolationMode } from "./preferences.js";
34
+ import { invalidateAllCaches } from "./cache.js";
35
+ import { resolveMilestoneFile } from "./paths.js";
30
36
  import { createWorkspace, scopeMilestone } from "./workspace.js";
37
+ // ADR-016 phase 2 / C1 (#5624): file-system + git-CLI leaf primitives
38
+ // inlined as direct imports rather than injected through `WorktreeLifecycleDeps`.
39
+ // These four symbols (`readFileSync` from node:fs, `getCurrentBranch` and
40
+ // `autoCommitCurrentBranch` from `./worktree.js`, `nativeCheckoutBranch` from
41
+ // `./native-git-bridge.js`) are leaf-level primitives — no environment varies
42
+ // across callers — so the dependency-injection seam they used to inhabit was
43
+ // adding type churn without enabling any test variation.
44
+ import { autoCommitCurrentBranch, getCurrentBranch, } from "./worktree.js";
45
+ import { nativeCheckoutBranch } from "./native-git-bridge.js";
46
+ // ADR-016 phase 2 / C2 (#5625): worktree-manager helpers inlined from
47
+ // `./auto-worktree.js`. These seven functions are not real seams — Lifecycle
48
+ // is the only Module that calls them, and they live alongside the Module's
49
+ // other primitives in `auto-worktree.ts`.
50
+ import { autoWorktreeBranch, createAutoWorktree, enterAutoWorktree, enterBranchModeForMilestone, getAutoWorktreePath, isInAutoWorktree, teardownAutoWorktree, } from "./auto-worktree.js";
31
51
  /**
32
52
  * Internal sentinel — thrown by `_mergeBranchMode` when it has already
33
53
  * emitted a user-visible error. The outer `mergeAndExit` catches the type
@@ -59,6 +79,85 @@ function isValidMilestoneId(milestoneId) {
59
79
  function invalidMilestoneIdError(milestoneId) {
60
80
  return new Error(`Invalid milestoneId: ${milestoneId} — contains path separators or traversal`);
61
81
  }
82
+ function primitiveOverrides(deps) {
83
+ return deps;
84
+ }
85
+ function readLifecycleFile(deps, path) {
86
+ return primitiveOverrides(deps).readFileSync?.(path, "utf-8") ??
87
+ readFileSync(path, "utf-8");
88
+ }
89
+ function currentLifecycleBranch(deps, basePath) {
90
+ return primitiveOverrides(deps).getCurrentBranch?.(basePath) ??
91
+ getCurrentBranch(basePath);
92
+ }
93
+ function checkoutLifecycleBranch(deps, basePath, branch) {
94
+ const checkoutBranch = primitiveOverrides(deps).checkoutBranch;
95
+ if (checkoutBranch) {
96
+ checkoutBranch(basePath, branch);
97
+ return;
98
+ }
99
+ nativeCheckoutBranch(basePath, branch);
100
+ }
101
+ function autoCommitLifecycleBranch(deps, basePath, unitType, unitId) {
102
+ return primitiveOverrides(deps).autoCommitCurrentBranch?.(basePath, unitType, unitId) ?? autoCommitCurrentBranch(basePath, unitType, unitId);
103
+ }
104
+ // ADR-016 phase 2 / C2-inlined worktree-manager primitives — helpers that
105
+ // honour the structural-typing override pattern so legacy test fixtures keep
106
+ // working without rewriting them onto real-git fixtures.
107
+ function lifecycleIsInAutoWorktree(deps, basePath) {
108
+ return primitiveOverrides(deps).isInAutoWorktree?.(basePath) ??
109
+ isInAutoWorktree(basePath);
110
+ }
111
+ function lifecycleAutoWorktreeBranch(deps, milestoneId) {
112
+ return primitiveOverrides(deps).autoWorktreeBranch?.(milestoneId) ??
113
+ autoWorktreeBranch(milestoneId);
114
+ }
115
+ function lifecycleTeardownAutoWorktree(deps, basePath, milestoneId, opts) {
116
+ const override = primitiveOverrides(deps).teardownAutoWorktree;
117
+ if (override) {
118
+ override(basePath, milestoneId, opts);
119
+ return;
120
+ }
121
+ teardownAutoWorktree(basePath, milestoneId, opts);
122
+ }
123
+ function lifecycleCreateAutoWorktree(deps, basePath, milestoneId) {
124
+ return primitiveOverrides(deps).createAutoWorktree?.(basePath, milestoneId) ??
125
+ createAutoWorktree(basePath, milestoneId);
126
+ }
127
+ function lifecycleEnterAutoWorktree(deps, basePath, milestoneId) {
128
+ return primitiveOverrides(deps).enterAutoWorktree?.(basePath, milestoneId) ??
129
+ enterAutoWorktree(basePath, milestoneId);
130
+ }
131
+ function lifecycleEnterBranchMode(deps, basePath, milestoneId) {
132
+ const override = primitiveOverrides(deps).enterBranchModeForMilestone;
133
+ if (override) {
134
+ override(basePath, milestoneId);
135
+ return;
136
+ }
137
+ enterBranchModeForMilestone(basePath, milestoneId);
138
+ }
139
+ // ADR-016 phase 2 / C3-inlined cache + preferences + path helpers.
140
+ function lifecycleGetIsolationMode(deps, basePath) {
141
+ return primitiveOverrides(deps).getIsolationMode?.(basePath) ??
142
+ getIsolationMode(basePath);
143
+ }
144
+ function lifecycleInvalidateAllCaches(deps) {
145
+ const override = primitiveOverrides(deps).invalidateAllCaches;
146
+ if (override) {
147
+ override();
148
+ return;
149
+ }
150
+ invalidateAllCaches();
151
+ }
152
+ function lifecycleResolveMilestoneFile(deps, basePath, milestoneId, fileType) {
153
+ return primitiveOverrides(deps).resolveMilestoneFile?.(basePath, milestoneId, fileType) ?? resolveMilestoneFile(basePath, milestoneId, fileType);
154
+ }
155
+ function lifecycleLoadPreferences(deps, basePath) {
156
+ const override = primitiveOverrides(deps).loadEffectiveGSDPreferences;
157
+ if (override)
158
+ return override(basePath);
159
+ return loadEffectiveGSDPreferences(basePath);
160
+ }
62
161
  /**
63
162
  * Throwing variant used by the merge/exit paths that surface failures via
64
163
  * the typed `ExitResult` (callers wrap the throw → cause). The enter path
@@ -193,7 +292,7 @@ export function _enterMilestoneCore(s, deps, milestoneId, ctx) {
193
292
  // Handles the case where originalBasePath is falsy and basePath is itself
194
293
  // a worktree path — prevents double-nested worktree paths (#3729).
195
294
  const basePath = resolveWorktreeProjectRoot(s.basePath, s.originalBasePath);
196
- const mode = deps.getIsolationMode(basePath);
295
+ const mode = getIsolationMode(basePath);
197
296
  if (mode === "none") {
198
297
  debugLog("WorktreeLifecycle", {
199
298
  action: "enterMilestone",
@@ -231,12 +330,12 @@ export function _enterMilestoneCore(s, deps, milestoneId, ctx) {
231
330
  // ── Branch mode: create/checkout milestone branch, stay in project root ──
232
331
  if (mode === "branch") {
233
332
  try {
234
- deps.enterBranchModeForMilestone(basePath, milestoneId);
333
+ lifecycleEnterBranchMode(deps, basePath, milestoneId);
235
334
  // basePath does not change — no worktree, no chdir.
236
335
  // Rebuild GitService so the new HEAD is reflected, then flush any
237
336
  // path-keyed caches that may have been populated before the checkout.
238
337
  rebuildGitService(s, deps);
239
- deps.invalidateAllCaches();
338
+ invalidateAllCaches();
240
339
  debugLog("WorktreeLifecycle", {
241
340
  action: "enterMilestone",
242
341
  milestoneId,
@@ -269,17 +368,17 @@ export function _enterMilestoneCore(s, deps, milestoneId, ctx) {
269
368
  }
270
369
  // ── Worktree mode ────────────────────────────────────────────────────────
271
370
  try {
272
- const existingPath = deps.getAutoWorktreePath(basePath, milestoneId);
371
+ const existingPath = (primitiveOverrides(deps).getAutoWorktreePath ?? getAutoWorktreePath)(basePath, milestoneId);
273
372
  let wtPath;
274
373
  if (existingPath) {
275
- wtPath = deps.enterAutoWorktree(basePath, milestoneId);
374
+ wtPath = lifecycleEnterAutoWorktree(deps, basePath, milestoneId);
276
375
  }
277
376
  else {
278
- wtPath = deps.createAutoWorktree(basePath, milestoneId);
377
+ wtPath = lifecycleCreateAutoWorktree(deps, basePath, milestoneId);
279
378
  }
280
379
  s.basePath = wtPath;
281
380
  rebuildGitService(s, deps);
282
- deps.invalidateAllCaches();
381
+ invalidateAllCaches();
283
382
  // Per ADR-016: Lifecycle calls Projection on entry, before any Unit
284
383
  // dispatches. Build a temporary scope from the new basePath; callers may
285
384
  // later set s.scope via their own rebuildScope hook (the two are
@@ -354,9 +453,411 @@ export function _enterMilestoneCore(s, deps, milestoneId, ctx) {
354
453
  return { ok: false, reason: "creation-failed", cause: err };
355
454
  }
356
455
  }
456
+ /**
457
+ * Resolve the basePath to adopt on resume from a paused session.
458
+ *
459
+ * Returns `persistedWorktreePath` when the path is non-null and exists on
460
+ * disk; otherwise falls back to `base`. Used by
461
+ * `WorktreeLifecycle.resumeFromPausedSession` (#5621). Exported as a pure
462
+ * function so unit tests can exercise the path-resolution logic without
463
+ * constructing a `WorktreeLifecycle` instance.
464
+ *
465
+ * The optional `pathExists` parameter exists only for tests that need to
466
+ * substitute a stub for `existsSync`.
467
+ */
468
+ export function resolvePausedResumeBasePath(base, persistedWorktreePath, pathExists = existsSync) {
469
+ return persistedWorktreePath && pathExists(persistedWorktreePath)
470
+ ? persistedWorktreePath
471
+ : base;
472
+ }
357
473
  function rebuildGitService(s, deps) {
358
- const gitConfig = deps.loadEffectiveGSDPreferences()?.preferences?.git ?? {};
359
- s.gitService = new deps.GitServiceImpl(s.basePath, gitConfig);
474
+ // ADR-016 phase 2 / C4 (#5627): the gitConfig load and constructor
475
+ // construction live behind `gitServiceFactory`. Lifecycle no longer
476
+ // sees the constructor shape, the gitConfig type, or the unknown→
477
+ // GitService cast.
478
+ s.gitService = deps.gitServiceFactory(s.basePath);
479
+ }
480
+ // ─── Session-less merge entry (ADR-016 phase 2 / A1) ─────────────────────
481
+ /**
482
+ * Worktree-mode merge body. Session-less — operates on a `MergeContext`.
483
+ *
484
+ * On error: emits the "worktree-merge-failed" journal event, notifies the
485
+ * user, cleans up stale `SQUASH_MSG` / `MERGE_HEAD` / `MERGE_MSG` files
486
+ * (#1389), and chdirs back to project root before rethrowing. Session-side
487
+ * cleanup (`restoreToProjectRoot`, `gitService` rebuild) is the caller's
488
+ * responsibility.
489
+ */
490
+ function _mergeWorktreeModeImpl(deps, mctx) {
491
+ const { originalBasePath, worktreeBasePath, milestoneId, notify } = mctx;
492
+ if (!originalBasePath) {
493
+ debugLog("WorktreeLifecycle", {
494
+ action: "mergeAndExit",
495
+ milestoneId,
496
+ mode: "worktree",
497
+ skipped: true,
498
+ reason: "missing-original-base",
499
+ });
500
+ return {
501
+ merged: false,
502
+ mode: "worktree",
503
+ codeFilesChanged: false,
504
+ pushed: false,
505
+ };
506
+ }
507
+ try {
508
+ // ADR-016: final projection before teardown. Replaces the legacy
509
+ // syncWorktreeStateBack(originalBase, basePath, milestoneId) call.
510
+ const finalScope = scopeMilestone(createWorkspace(worktreeBasePath), milestoneId);
511
+ const { synced } = deps.worktreeProjection.finalizeProjectionForMerge(finalScope);
512
+ if (synced.length > 0) {
513
+ debugLog("WorktreeLifecycle", {
514
+ action: "mergeAndExit",
515
+ milestoneId,
516
+ phase: "reverse-sync",
517
+ synced: synced.length,
518
+ });
519
+ }
520
+ // Resolve roadmap — try project root first, then worktree path as
521
+ // fallback. The worktree may hold the only copy when state-back
522
+ // projection silently dropped it or .gsd/ is not symlinked. Without
523
+ // the fallback, a missing roadmap triggers bare teardown which
524
+ // deletes the branch and orphans all milestone commits (#1573).
525
+ let roadmapPath = resolveMilestoneFile(originalBasePath, milestoneId, "ROADMAP");
526
+ if (!roadmapPath &&
527
+ !isSamePathPhysical(worktreeBasePath, originalBasePath)) {
528
+ roadmapPath = resolveMilestoneFile(worktreeBasePath, milestoneId, "ROADMAP");
529
+ if (roadmapPath) {
530
+ debugLog("WorktreeLifecycle", {
531
+ action: "mergeAndExit",
532
+ milestoneId,
533
+ phase: "roadmap-fallback",
534
+ note: "resolved from worktree path",
535
+ });
536
+ }
537
+ }
538
+ if (!roadmapPath) {
539
+ // No roadmap at either location — teardown but PRESERVE the branch
540
+ // so commits are not orphaned (#1573).
541
+ lifecycleTeardownAutoWorktree(deps, originalBasePath, milestoneId, {
542
+ preserveBranch: true,
543
+ });
544
+ notify(`Exited worktree for ${milestoneId} (no roadmap found — branch preserved for manual merge).`, "warning");
545
+ return {
546
+ merged: false,
547
+ mode: "worktree",
548
+ codeFilesChanged: false,
549
+ pushed: false,
550
+ };
551
+ }
552
+ const roadmapContent = readLifecycleFile(deps, roadmapPath);
553
+ const mergeResult = deps.mergeMilestoneToMain(originalBasePath, milestoneId, roadmapContent);
554
+ // #2945 Bug 3: mergeMilestoneToMain performs best-effort worktree
555
+ // cleanup internally (step 12), but it can silently fail on Windows
556
+ // or when the worktree directory is locked. Perform a secondary
557
+ // teardown here to ensure the worktree is properly cleaned up.
558
+ // Idempotent — if already removed, teardownAutoWorktree no-ops.
559
+ try {
560
+ lifecycleTeardownAutoWorktree(deps, originalBasePath, milestoneId);
561
+ }
562
+ catch {
563
+ // Best-effort — primary cleanup in mergeMilestoneToMain may have
564
+ // already removed the worktree.
565
+ }
566
+ if (mergeResult.codeFilesChanged) {
567
+ notify(`Milestone ${milestoneId} merged to main.${mergeResult.pushed ? " Pushed to remote." : ""}`, "info");
568
+ }
569
+ else {
570
+ // #1906 — milestone produced only .gsd/ metadata. Surface
571
+ // clearly so the user knows the milestone is not truly complete.
572
+ notify(`WARNING: Milestone ${milestoneId} merged to main but contained NO code changes — only .gsd/ metadata files. ` +
573
+ `The milestone summary may describe planned work that was never implemented. ` +
574
+ `Review the milestone output and re-run if code is missing.`, "warning");
575
+ }
576
+ return {
577
+ merged: true,
578
+ mode: "worktree",
579
+ codeFilesChanged: mergeResult.codeFilesChanged,
580
+ pushed: mergeResult.pushed,
581
+ commitMessage: mergeResult.commitMessage,
582
+ };
583
+ }
584
+ catch (err) {
585
+ const msg = err instanceof Error ? err.message : String(err);
586
+ debugLog("WorktreeLifecycle", {
587
+ action: "mergeAndExit",
588
+ milestoneId,
589
+ result: "error",
590
+ error: msg,
591
+ fallback: "chdir-to-project-root",
592
+ });
593
+ emitJournalEvent(originalBasePath || worktreeBasePath, {
594
+ ts: new Date().toISOString(),
595
+ flowId: randomUUID(),
596
+ seq: 0,
597
+ eventType: "worktree-merge-failed",
598
+ data: { milestoneId, error: msg },
599
+ });
600
+ // Surface a clear, actionable error. Worktree and milestone branch
601
+ // are intentionally preserved — nothing has been deleted. User can
602
+ // retry /gsd dispatch complete-milestone or merge manually once the
603
+ // underlying issue is fixed (#1668, #1891).
604
+ notify(`Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry with \`/gsd dispatch complete-milestone\` or merge manually.`, "warning");
605
+ // Clean up stale merge state left by failed squash-merge (#1389)
606
+ try {
607
+ const gitDir = join(originalBasePath || worktreeBasePath, ".git");
608
+ for (const f of ["SQUASH_MSG", "MERGE_HEAD", "MERGE_MSG"]) {
609
+ const p = join(gitDir, f);
610
+ if (existsSync(p))
611
+ unlinkSync(p);
612
+ }
613
+ }
614
+ catch {
615
+ /* best-effort */
616
+ }
617
+ // Error recovery: chdir back to project root only when no real worktree
618
+ // path is available. Session-side cleanup (restoreToProjectRoot,
619
+ // gitService rebuild) is the caller's responsibility.
620
+ if (originalBasePath && !worktreeBasePath) {
621
+ try {
622
+ process.chdir(originalBasePath);
623
+ }
624
+ catch {
625
+ /* best-effort */
626
+ }
627
+ }
628
+ // Re-throw: MergeConflictError stops the auto loop (#2330);
629
+ // non-conflict errors must also propagate so broken states are
630
+ // diagnosable (#4380).
631
+ throw err;
632
+ }
633
+ }
634
+ /**
635
+ * Branch-mode merge body. Session-less.
636
+ *
637
+ * Session-side `gitService` rebuild after HEAD changes is the caller's
638
+ * responsibility. The branch-mode `UserNotifiedError` sentinel still flows
639
+ * through unchanged so the outer caller can suppress duplicate toasts.
640
+ */
641
+ function _mergeBranchModeImpl(deps, mctx) {
642
+ const { worktreeBasePath, milestoneId, notify } = mctx;
643
+ try {
644
+ const currentBranch = currentLifecycleBranch(deps, worktreeBasePath);
645
+ const milestoneBranch = lifecycleAutoWorktreeBranch(deps, milestoneId);
646
+ if (currentBranch !== milestoneBranch) {
647
+ // #5538-followup: previous behaviour was to silently `return false`
648
+ // when HEAD wasn't on the milestone branch — that let the loop
649
+ // advance with the milestone's commits stranded on the branch.
650
+ // Attempt recovery by force-checking-out the milestone branch; if
651
+ // that fails, throw so the caller pauses auto-mode and the user
652
+ // sees the failure instead of a silent merge skip.
653
+ debugLog("WorktreeLifecycle", {
654
+ action: "mergeAndExit",
655
+ milestoneId,
656
+ mode: "branch",
657
+ recovery: "checkout-milestone-branch",
658
+ currentBranch,
659
+ milestoneBranch,
660
+ });
661
+ try {
662
+ checkoutLifecycleBranch(deps, worktreeBasePath, milestoneBranch);
663
+ }
664
+ catch (checkoutErr) {
665
+ const checkoutMsg = checkoutErr instanceof Error
666
+ ? checkoutErr.message
667
+ : String(checkoutErr);
668
+ notify(`Cannot merge milestone ${milestoneId}: working tree is on ${currentBranch} and checkout to ${milestoneBranch} failed (${checkoutMsg}). Resolve manually and run /gsd auto to resume.`, "error");
669
+ throw new UserNotifiedError(checkoutMsg, checkoutErr);
670
+ }
671
+ const reverify = currentLifecycleBranch(deps, worktreeBasePath);
672
+ if (reverify !== milestoneBranch) {
673
+ const reverifyMsg = `branch checkout to ${milestoneBranch} reported success but current branch is ${reverify}`;
674
+ notify(`Cannot merge milestone ${milestoneId}: ${reverifyMsg}. Resolve manually and run /gsd auto to resume.`, "error");
675
+ throw new UserNotifiedError(reverifyMsg);
676
+ }
677
+ }
678
+ const roadmapPath = resolveMilestoneFile(worktreeBasePath, milestoneId, "ROADMAP");
679
+ if (!roadmapPath) {
680
+ debugLog("WorktreeLifecycle", {
681
+ action: "mergeAndExit",
682
+ milestoneId,
683
+ mode: "branch",
684
+ skipped: true,
685
+ reason: "no-roadmap",
686
+ });
687
+ return {
688
+ merged: false,
689
+ mode: "branch",
690
+ codeFilesChanged: false,
691
+ pushed: false,
692
+ };
693
+ }
694
+ const roadmapContent = readLifecycleFile(deps, roadmapPath);
695
+ const mergeResult = deps.mergeMilestoneToMain(worktreeBasePath, milestoneId, roadmapContent);
696
+ if (mergeResult.codeFilesChanged) {
697
+ notify(`Milestone ${milestoneId} merged (branch mode).${mergeResult.pushed ? " Pushed to remote." : ""}`, "info");
698
+ }
699
+ else {
700
+ notify(`WARNING: Milestone ${milestoneId} merged (branch mode) but contained NO code changes — only .gsd/ metadata. ` +
701
+ `Review the milestone output and re-run if code is missing.`, "warning");
702
+ }
703
+ debugLog("WorktreeLifecycle", {
704
+ action: "mergeAndExit",
705
+ milestoneId,
706
+ mode: "branch",
707
+ result: "success",
708
+ });
709
+ return {
710
+ merged: true,
711
+ mode: "branch",
712
+ codeFilesChanged: mergeResult.codeFilesChanged,
713
+ pushed: mergeResult.pushed,
714
+ commitMessage: mergeResult.commitMessage,
715
+ };
716
+ }
717
+ catch (err) {
718
+ const msg = err instanceof Error ? err.message : String(err);
719
+ debugLog("WorktreeLifecycle", {
720
+ action: "mergeAndExit",
721
+ milestoneId,
722
+ mode: "branch",
723
+ result: "error",
724
+ error: msg,
725
+ });
726
+ if (!(err instanceof UserNotifiedError)) {
727
+ notify(`Milestone merge failed (branch mode): ${msg}`, "warning");
728
+ }
729
+ // Re-throw all errors so callers can apply their own recovery (#4380).
730
+ throw err;
731
+ }
732
+ }
733
+ /**
734
+ * Session-less merge entry (ADR-016 phase 2 / A1, issue #5618).
735
+ *
736
+ * Runs the worktree-mode or branch-mode merge body without touching session
737
+ * state. Used directly by `parallel-merge.ts` and indirectly (via
738
+ * `_mergeAndExit`) by the single-loop path. Caller is responsible for any
739
+ * session-side cleanup based on the returned `mode`.
740
+ *
741
+ * **CWD anchor**: anchors `process.cwd()` at `originalBasePath` before
742
+ * non-worktree merge paths to mirror the single-loop guard against ENOENT
743
+ * after teardown (de73fb43d). Worktree-mode merge paths keep the real
744
+ * worktree as cwd because `mergeMilestoneToMain()` infers source worktree
745
+ * state from `process.cwd()`. Best-effort; silent on failure.
746
+ *
747
+ * **Failure handling**: `MergeConflictError` and other unrecoverable errors
748
+ * propagate to the caller. The caller is responsible for any state restore
749
+ * (single-loop callers re-`chdir` and `restoreToProjectRoot`; parallel
750
+ * callers surface to the user as a `MergeResult` with `success: false`).
751
+ */
752
+ export function mergeMilestoneStandalone(deps, mctx) {
753
+ const { originalBasePath, worktreeBasePath, milestoneId, notify } = mctx;
754
+ validateMilestoneId(milestoneId);
755
+ if (mctx.isolationDegraded) {
756
+ if (originalBasePath) {
757
+ try {
758
+ process.chdir(originalBasePath);
759
+ }
760
+ catch (err) {
761
+ debugLog("WorktreeLifecycle", {
762
+ action: "mergeAndExit",
763
+ phase: "pre-merge-chdir-failed",
764
+ milestoneId,
765
+ originalBasePath,
766
+ error: err instanceof Error ? err.message : String(err),
767
+ });
768
+ }
769
+ }
770
+ debugLog("WorktreeLifecycle", {
771
+ action: "mergeAndExit",
772
+ milestoneId,
773
+ skipped: true,
774
+ reason: "isolation-degraded",
775
+ });
776
+ notify(`Skipping worktree merge for ${milestoneId} — isolation was degraded (worktree creation failed earlier). Work is on the current branch.`, "info");
777
+ return {
778
+ merged: false,
779
+ mode: "skipped",
780
+ codeFilesChanged: false,
781
+ pushed: false,
782
+ };
783
+ }
784
+ const mode = getIsolationMode(originalBasePath || worktreeBasePath);
785
+ debugLog("WorktreeLifecycle", {
786
+ action: "mergeAndExit",
787
+ milestoneId,
788
+ mode,
789
+ basePath: worktreeBasePath,
790
+ });
791
+ emitJournalEvent(originalBasePath || worktreeBasePath, {
792
+ ts: new Date().toISOString(),
793
+ flowId: randomUUID(),
794
+ seq: 0,
795
+ eventType: "worktree-merge-start",
796
+ data: { milestoneId, mode },
797
+ });
798
+ // #2625: If we are physically inside an auto-worktree, we MUST merge
799
+ // regardless of the current isolation config. This prevents data loss
800
+ // when the default isolation mode changes between versions.
801
+ const inWorktree = lifecycleIsInAutoWorktree(deps, worktreeBasePath) && Boolean(originalBasePath);
802
+ if (mode === "none" && !inWorktree) {
803
+ debugLog("WorktreeLifecycle", {
804
+ action: "mergeAndExit",
805
+ milestoneId,
806
+ skipped: true,
807
+ reason: "mode-none",
808
+ });
809
+ // Anchor cwd at project root before the early return so subsequent
810
+ // process.cwd() calls after the skip don't ENOENT if we were inside a
811
+ // worktree directory that gets torn down later. Best-effort.
812
+ if (originalBasePath) {
813
+ try {
814
+ process.chdir(originalBasePath);
815
+ }
816
+ catch {
817
+ /* best-effort */
818
+ }
819
+ }
820
+ return {
821
+ merged: false,
822
+ mode: "skipped",
823
+ codeFilesChanged: false,
824
+ pushed: false,
825
+ };
826
+ }
827
+ // Set cwd to the correct anchor before dispatching to mode implementations.
828
+ // Worktree mode / in-worktree override must run from the live worktree so
829
+ // mergeMilestoneToMain can find worktree-local state; branch mode runs from
830
+ // the original project root. Best-effort for synthetic test paths.
831
+ const targetCwd = mode === "worktree" || inWorktree
832
+ ? worktreeBasePath
833
+ : originalBasePath;
834
+ if (targetCwd) {
835
+ try {
836
+ process.chdir(targetCwd);
837
+ }
838
+ catch (err) {
839
+ debugLog("WorktreeLifecycle", {
840
+ action: "mergeAndExit",
841
+ phase: "pre-merge-chdir-failed",
842
+ milestoneId,
843
+ targetCwd,
844
+ error: err instanceof Error ? err.message : String(err),
845
+ });
846
+ }
847
+ }
848
+ if (mode === "worktree" || inWorktree) {
849
+ return _mergeWorktreeModeImpl(deps, mctx);
850
+ }
851
+ if (mode === "branch") {
852
+ return _mergeBranchModeImpl(deps, mctx);
853
+ }
854
+ // Defensive fallback — should not reach here given the mode-none guard above.
855
+ return {
856
+ merged: false,
857
+ mode: "skipped",
858
+ codeFilesChanged: false,
859
+ pushed: false,
860
+ };
360
861
  }
361
862
  // ─── Module class ────────────────────────────────────────────────────────
362
863
  /**
@@ -401,7 +902,11 @@ export class WorktreeLifecycle {
401
902
  if (opts.merge) {
402
903
  try {
403
904
  const merged = this._mergeAndExit(milestoneId, ctx);
404
- return { ok: true, merged, codeFilesChanged: false };
905
+ return {
906
+ ok: true,
907
+ merged: merged.merged,
908
+ codeFilesChanged: merged.codeFilesChanged,
909
+ };
405
910
  }
406
911
  catch (err) {
407
912
  if (err instanceof MergeConflictError) {
@@ -435,7 +940,7 @@ export class WorktreeLifecycle {
435
940
  let merged = false;
436
941
  let mergeThrew = false;
437
942
  try {
438
- merged = this._mergeAndExit(currentMilestoneId, ctx);
943
+ merged = this._mergeAndExit(currentMilestoneId, ctx).merged;
439
944
  }
440
945
  catch (err) {
441
946
  if (err instanceof UserNotifiedError)
@@ -470,7 +975,7 @@ export class WorktreeLifecycle {
470
975
  // ── Private — exit without merge ─────────────────────────────────────
471
976
  _exitWithoutMerge(milestoneId, ctx, opts) {
472
977
  validateMilestoneId(milestoneId);
473
- if (!this.deps.isInAutoWorktree(this.s.basePath)) {
978
+ if (!lifecycleIsInAutoWorktree(this.deps, this.s.basePath)) {
474
979
  debugLog("WorktreeLifecycle", {
475
980
  action: "exitMilestone",
476
981
  milestoneId,
@@ -485,7 +990,7 @@ export class WorktreeLifecycle {
485
990
  basePath: this.s.basePath,
486
991
  });
487
992
  try {
488
- this.deps.autoCommitCurrentBranch(this.s.basePath, "stop", milestoneId);
993
+ autoCommitLifecycleBranch(this.deps, this.s.basePath, "stop", milestoneId);
489
994
  }
490
995
  catch (err) {
491
996
  debugLog("WorktreeLifecycle", {
@@ -494,7 +999,7 @@ export class WorktreeLifecycle {
494
999
  phase: "auto-commit-failed",
495
1000
  error: err instanceof Error ? err.message : String(err),
496
1001
  });
497
- ctx.notify(`Auto-commit before exiting ${milestoneId} failed: ${err instanceof Error ? err.message : String(err)}. Branch ${this.deps.autoWorktreeBranch(milestoneId)} is preserved for recovery.`, "warning");
1002
+ ctx.notify(`Auto-commit before exiting ${milestoneId} failed: ${err instanceof Error ? err.message : String(err)}. Branch ${lifecycleAutoWorktreeBranch(this.deps, milestoneId)} is preserved for recovery.`, "warning");
498
1003
  }
499
1004
  if (this.s.originalBasePath) {
500
1005
  try {
@@ -508,12 +1013,12 @@ export class WorktreeLifecycle {
508
1013
  originalBasePath: this.s.originalBasePath,
509
1014
  error: err instanceof Error ? err.message : String(err),
510
1015
  });
511
- ctx.notify(`Could not leave milestone worktree before cleanup: ${err instanceof Error ? err.message : String(err)}. Branch ${this.deps.autoWorktreeBranch(milestoneId)} is preserved for recovery.`, "warning");
1016
+ ctx.notify(`Could not leave milestone worktree before cleanup: ${err instanceof Error ? err.message : String(err)}. Branch ${lifecycleAutoWorktreeBranch(this.deps, milestoneId)} is preserved for recovery.`, "warning");
512
1017
  }
513
1018
  }
514
1019
  let teardownFailed = false;
515
1020
  try {
516
- this.deps.teardownAutoWorktree(this.s.originalBasePath, milestoneId, {
1021
+ lifecycleTeardownAutoWorktree(this.deps, this.s.originalBasePath, milestoneId, {
517
1022
  preserveBranch: opts.preserveBranch ?? false,
518
1023
  });
519
1024
  }
@@ -525,7 +1030,7 @@ export class WorktreeLifecycle {
525
1030
  phase: "teardown-failed",
526
1031
  error: err instanceof Error ? err.message : String(err),
527
1032
  });
528
- ctx.notify(`Worktree cleanup failed for ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. Branch ${this.deps.autoWorktreeBranch(milestoneId)} is preserved for recovery.`, "warning");
1033
+ ctx.notify(`Worktree cleanup failed for ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. Branch ${lifecycleAutoWorktreeBranch(this.deps, milestoneId)} is preserved for recovery.`, "warning");
529
1034
  }
530
1035
  this.restoreToProjectRoot();
531
1036
  debugLog("WorktreeLifecycle", {
@@ -542,91 +1047,56 @@ export class WorktreeLifecycle {
542
1047
  /**
543
1048
  * Merge the completed milestone branch back to main and exit the worktree.
544
1049
  *
545
- * - **worktree mode**: reads the roadmap, runs squash merge, projects
546
- * final state back via Projection.finalizeProjectionForMerge, tears
547
- * down the worktree, restores `s.basePath`. Falls back to bare
548
- * teardown (preserving the branch) if no roadmap exists.
549
- * - **branch mode**: validates HEAD is on the milestone branch (recovers
550
- * via checkout if not), merges, rebuilds GitService.
551
- * - **none**: no-op unless physically inside an auto-worktree (#2625).
1050
+ * Session-bound wrapper around `mergeMilestoneStandalone`. Builds a
1051
+ * `MergeContext` from `this.s`, layers session-side bookkeeping on top of
1052
+ * the result:
1053
+ *
1054
+ * - resquash-on-merge using `s.milestoneStartShas`
1055
+ * - merge-completion telemetry (duration)
1056
+ * - mode-specific session restore: worktree-mode `restoreToProjectRoot`,
1057
+ * branch-mode → `gitService` rebuild
552
1058
  *
553
- * Returns true when an actual squash-merge ran. Throws MergeConflictError
554
- * (and other non-recoverable errors) for callers to handle.
1059
+ * Returns the session-less merge result. Errors propagate after
1060
+ * `restoreToProjectRoot()` runs so callers always receive a consistent
1061
+ * session.
555
1062
  */
556
1063
  _mergeAndExit(milestoneId, ctx) {
557
- validateMilestoneId(milestoneId);
558
- // Anchor cwd at the project root before any merge work. Some merge
559
- // paths (mergeMilestoneToMain, slice-cadence) chdir explicitly; others
560
- // (branch-mode, isolation-degraded skip) do not. If the worktree dir
561
- // is later torn down while cwd still points into it, every subsequent
562
- // process.cwd() throws ENOENT — which after de73fb43d surfaces as a
563
- // session-failed cancel and (in headless mode) terminates the whole
564
- // gsd process. Best-effort: silent on failure so synthetic test paths
565
- // still pass.
566
- if (this.s.originalBasePath) {
567
- try {
568
- process.chdir(this.s.originalBasePath);
569
- }
570
- catch (err) {
571
- debugLog("WorktreeLifecycle", {
572
- action: "mergeAndExit",
573
- phase: "pre-merge-chdir-failed",
574
- milestoneId,
575
- originalBasePath: this.s.originalBasePath,
576
- error: err instanceof Error ? err.message : String(err),
577
- });
578
- }
579
- }
580
1064
  // #4764 — telemetry: record start timestamp so we can emit merge duration.
581
1065
  const mergeStartedAt = new Date().toISOString();
582
1066
  const mergeStartMs = Date.now();
583
- if (this.s.isolationDegraded) {
584
- debugLog("WorktreeLifecycle", {
585
- action: "mergeAndExit",
586
- milestoneId,
587
- skipped: true,
588
- reason: "isolation-degraded",
589
- });
590
- ctx.notify(`Skipping worktree merge for ${milestoneId} — isolation was degraded (worktree creation failed earlier). Work is on the current branch.`, "info");
591
- return false;
592
- }
593
- const mode = this.deps.getIsolationMode(this.s.originalBasePath || this.s.basePath);
594
- debugLog("WorktreeLifecycle", {
595
- action: "mergeAndExit",
596
- milestoneId,
597
- mode,
598
- basePath: this.s.basePath,
599
- });
600
- emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
601
- ts: new Date().toISOString(),
602
- flowId: randomUUID(),
603
- seq: 0,
604
- eventType: "worktree-merge-start",
605
- data: { milestoneId, mode },
606
- });
607
- // #2625: If we are physically inside an auto-worktree, we MUST merge
608
- // regardless of the current isolation config. This prevents data loss
609
- // when the default isolation mode changes between versions.
610
- const inWorktree = this.deps.isInAutoWorktree(this.s.basePath) && this.s.originalBasePath;
611
- if (mode === "none" && !inWorktree) {
612
- debugLog("WorktreeLifecycle", {
613
- action: "mergeAndExit",
1067
+ let result;
1068
+ try {
1069
+ result = mergeMilestoneStandalone(this.deps, {
1070
+ originalBasePath: this.s.originalBasePath,
1071
+ worktreeBasePath: this.s.basePath,
614
1072
  milestoneId,
615
- skipped: true,
616
- reason: "mode-none",
1073
+ isolationDegraded: this.s.isolationDegraded,
1074
+ notify: ctx.notify,
617
1075
  });
618
- return false;
619
- }
620
- let actuallyMerged = false;
621
- if (mode === "worktree" || inWorktree) {
622
- actuallyMerged = this._mergeWorktreeMode(milestoneId, ctx);
623
1076
  }
624
- else if (mode === "branch") {
625
- actuallyMerged = this._mergeBranchMode(milestoneId, ctx);
1077
+ catch (err) {
1078
+ // Standalone has already done its session-less cleanup
1079
+ // (chdir, SQUASH_MSG cleanup, journal event). Layer session-side
1080
+ // restore on top so callers get a consistent session.
1081
+ this.restoreToProjectRoot();
1082
+ throw err;
626
1083
  }
627
- if (!actuallyMerged) {
1084
+ if (!result.merged) {
1085
+ // Skip / no-roadmap / mode-none paths. milestoneStartShas housekeeping
1086
+ // is unconditional; mode-specific session restore happens for
1087
+ // worktree-mode (preserve-branch path tore down the worktree, so
1088
+ // basePath must restore) and not for branch-mode (no basePath change).
628
1089
  this.s.milestoneStartShas.delete(milestoneId);
629
- return false;
1090
+ if (result.mode === "worktree") {
1091
+ this.restoreToProjectRoot();
1092
+ debugLog("WorktreeLifecycle", {
1093
+ action: "mergeAndExit",
1094
+ milestoneId,
1095
+ result: "done",
1096
+ basePath: this.s.basePath,
1097
+ });
1098
+ }
1099
+ return result;
630
1100
  }
631
1101
  // #4765 — when collapse_cadence=slice AND milestone_resquash=true, the
632
1102
  // N per-slice commits on main should be collapsed into one milestone
@@ -635,11 +1105,11 @@ export class WorktreeLifecycle {
635
1105
  try {
636
1106
  const startSha = this.s.milestoneStartShas.get(milestoneId);
637
1107
  if (startSha) {
638
- const prefs = loadEffectiveGSDPreferences(this.s.originalBasePath || this.s.basePath)?.preferences;
1108
+ const prefs = lifecycleLoadPreferences(this.deps, this.s.originalBasePath || this.s.basePath)?.preferences;
639
1109
  if (getCollapseCadence(prefs) === "slice" &&
640
1110
  getMilestoneResquash(prefs)) {
641
- const result = resquashMilestoneOnMain(this.s.originalBasePath || this.s.basePath, milestoneId, startSha);
642
- if (result.resquashed) {
1111
+ const resquashResult = resquashMilestoneOnMain(this.s.originalBasePath || this.s.basePath, milestoneId, startSha);
1112
+ if (resquashResult.resquashed) {
643
1113
  ctx.notify(`slice-cadence: re-squashed slice commits for ${milestoneId} into a single milestone commit.`, "info");
644
1114
  }
645
1115
  }
@@ -672,232 +1142,27 @@ export class WorktreeLifecycle {
672
1142
  : String(telemetryErr),
673
1143
  });
674
1144
  }
675
- return true;
676
- }
677
- /** Worktree-mode merge body. Returns true when an actual squash-merge ran. */
678
- _mergeWorktreeMode(milestoneId, ctx) {
679
- const originalBase = this.s.originalBasePath;
680
- if (!originalBase) {
681
- debugLog("WorktreeLifecycle", {
682
- action: "mergeAndExit",
683
- milestoneId,
684
- mode: "worktree",
685
- skipped: true,
686
- reason: "missing-original-base",
687
- });
688
- return false;
689
- }
690
- let merged = false;
691
- try {
692
- // ADR-016: final projection before teardown. Replaces the legacy
693
- // syncWorktreeStateBack(originalBase, basePath, milestoneId) call.
694
- const finalScope = scopeMilestone(createWorkspace(this.s.basePath), milestoneId);
695
- const { synced } = this.deps.worktreeProjection.finalizeProjectionForMerge(finalScope);
696
- if (synced.length > 0) {
697
- debugLog("WorktreeLifecycle", {
698
- action: "mergeAndExit",
699
- milestoneId,
700
- phase: "reverse-sync",
701
- synced: synced.length,
702
- });
703
- }
704
- // Resolve roadmap — try project root first, then worktree path as
705
- // fallback. The worktree may hold the only copy when state-back
706
- // projection silently dropped it or .gsd/ is not symlinked. Without
707
- // the fallback, a missing roadmap triggers bare teardown which
708
- // deletes the branch and orphans all milestone commits (#1573).
709
- let roadmapPath = this.deps.resolveMilestoneFile(originalBase, milestoneId, "ROADMAP");
710
- if (!roadmapPath &&
711
- !isSamePathPhysical(this.s.basePath, originalBase)) {
712
- roadmapPath = this.deps.resolveMilestoneFile(this.s.basePath, milestoneId, "ROADMAP");
713
- if (roadmapPath) {
714
- debugLog("WorktreeLifecycle", {
715
- action: "mergeAndExit",
716
- milestoneId,
717
- phase: "roadmap-fallback",
718
- note: "resolved from worktree path",
719
- });
720
- }
721
- }
722
- if (roadmapPath) {
723
- const roadmapContent = this.deps.readFileSync(roadmapPath, "utf-8");
724
- const mergeResult = this.deps.mergeMilestoneToMain(originalBase, milestoneId, roadmapContent);
725
- merged = true;
726
- // #2945 Bug 3: mergeMilestoneToMain performs best-effort worktree
727
- // cleanup internally (step 12), but it can silently fail on Windows
728
- // or when the worktree directory is locked. Perform a secondary
729
- // teardown here to ensure the worktree is properly cleaned up.
730
- // Idempotent — if already removed, teardownAutoWorktree no-ops.
731
- try {
732
- this.deps.teardownAutoWorktree(originalBase, milestoneId);
733
- }
734
- catch {
735
- // Best-effort — primary cleanup in mergeMilestoneToMain may have
736
- // already removed the worktree.
737
- }
738
- if (mergeResult.codeFilesChanged) {
739
- ctx.notify(`Milestone ${milestoneId} merged to main.${mergeResult.pushed ? " Pushed to remote." : ""}`, "info");
740
- }
741
- else {
742
- // #1906 — milestone produced only .gsd/ metadata. Surface
743
- // clearly so the user knows the milestone is not truly complete.
744
- ctx.notify(`WARNING: Milestone ${milestoneId} merged to main but contained NO code changes — only .gsd/ metadata files. ` +
745
- `The milestone summary may describe planned work that was never implemented. ` +
746
- `Review the milestone output and re-run if code is missing.`, "warning");
747
- }
748
- }
749
- else {
750
- // No roadmap at either location — teardown but PRESERVE the branch
751
- // so commits are not orphaned (#1573).
752
- this.deps.teardownAutoWorktree(originalBase, milestoneId, {
753
- preserveBranch: true,
754
- });
755
- ctx.notify(`Exited worktree for ${milestoneId} (no roadmap found — branch preserved for manual merge).`, "warning");
756
- }
757
- }
758
- catch (err) {
759
- const msg = err instanceof Error ? err.message : String(err);
1145
+ // Mode-specific session restore.
1146
+ if (result.mode === "worktree") {
1147
+ this.restoreToProjectRoot();
760
1148
  debugLog("WorktreeLifecycle", {
761
1149
  action: "mergeAndExit",
762
1150
  milestoneId,
763
- result: "error",
764
- error: msg,
765
- fallback: "chdir-to-project-root",
1151
+ result: "done",
1152
+ basePath: this.s.basePath,
766
1153
  });
767
- emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
768
- ts: new Date().toISOString(),
769
- flowId: randomUUID(),
770
- seq: 0,
771
- eventType: "worktree-merge-failed",
772
- data: { milestoneId, error: msg },
773
- });
774
- // Surface a clear, actionable error. Worktree and milestone branch
775
- // are intentionally preserved — nothing has been deleted. User can
776
- // retry /gsd dispatch complete-milestone or merge manually once the
777
- // underlying issue is fixed (#1668, #1891).
778
- ctx.notify(`Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry with \`/gsd dispatch complete-milestone\` or merge manually.`, "warning");
779
- // Clean up stale merge state left by failed squash-merge (#1389)
780
- try {
781
- const gitDir = join(originalBase || this.s.basePath, ".git");
782
- for (const f of ["SQUASH_MSG", "MERGE_HEAD", "MERGE_MSG"]) {
783
- const p = join(gitDir, f);
784
- if (existsSync(p))
785
- unlinkSync(p);
786
- }
787
- }
788
- catch {
789
- /* best-effort */
790
- }
791
- // Error recovery: always restore to project root
792
- if (originalBase) {
793
- try {
794
- process.chdir(originalBase);
795
- }
796
- catch {
797
- /* best-effort */
798
- }
799
- }
800
- // Restore state before re-throwing so callers always get a
801
- // consistent session (#4380).
802
- this.restoreToProjectRoot();
803
- // Re-throw: MergeConflictError stops the auto loop (#2330);
804
- // non-conflict errors must also propagate so broken states are
805
- // diagnosable (#4380).
806
- throw err;
807
1154
  }
808
- // Always restore basePath and rebuild — whether merge succeeded or failed
809
- this.restoreToProjectRoot();
810
- debugLog("WorktreeLifecycle", {
811
- action: "mergeAndExit",
812
- milestoneId,
813
- result: "done",
814
- basePath: this.s.basePath,
815
- });
816
- return merged;
817
- }
818
- /** Branch-mode merge body. Returns true when a merge actually ran. */
819
- _mergeBranchMode(milestoneId, ctx) {
820
- try {
821
- const currentBranch = this.deps.getCurrentBranch(this.s.basePath);
822
- const milestoneBranch = this.deps.autoWorktreeBranch(milestoneId);
823
- if (currentBranch !== milestoneBranch) {
824
- // #5538-followup: previous behaviour was to silently `return false`
825
- // when HEAD wasn't on the milestone branch — that let the loop
826
- // advance with the milestone's commits stranded on the branch.
827
- // Attempt recovery by force-checking-out the milestone branch; if
828
- // that fails, throw so the caller pauses auto-mode and the user
829
- // sees the failure instead of a silent merge skip.
830
- debugLog("WorktreeLifecycle", {
831
- action: "mergeAndExit",
832
- milestoneId,
833
- mode: "branch",
834
- recovery: "checkout-milestone-branch",
835
- currentBranch,
836
- milestoneBranch,
837
- });
838
- try {
839
- this.deps.checkoutBranch(this.s.basePath, milestoneBranch);
840
- }
841
- catch (checkoutErr) {
842
- const checkoutMsg = checkoutErr instanceof Error
843
- ? checkoutErr.message
844
- : String(checkoutErr);
845
- ctx.notify(`Cannot merge milestone ${milestoneId}: working tree is on ${currentBranch} and checkout to ${milestoneBranch} failed (${checkoutMsg}). Resolve manually and run /gsd auto to resume.`, "error");
846
- throw new UserNotifiedError(checkoutMsg, checkoutErr);
847
- }
848
- const reverify = this.deps.getCurrentBranch(this.s.basePath);
849
- if (reverify !== milestoneBranch) {
850
- const reverifyMsg = `branch checkout to ${milestoneBranch} reported success but current branch is ${reverify}`;
851
- ctx.notify(`Cannot merge milestone ${milestoneId}: ${reverifyMsg}. Resolve manually and run /gsd auto to resume.`, "error");
852
- throw new UserNotifiedError(reverifyMsg);
853
- }
854
- }
855
- const roadmapPath = this.deps.resolveMilestoneFile(this.s.basePath, milestoneId, "ROADMAP");
856
- if (!roadmapPath) {
857
- debugLog("WorktreeLifecycle", {
858
- action: "mergeAndExit",
859
- milestoneId,
860
- mode: "branch",
861
- skipped: true,
862
- reason: "no-roadmap",
863
- });
864
- return false;
865
- }
866
- const roadmapContent = this.deps.readFileSync(roadmapPath, "utf-8");
867
- const mergeResult = this.deps.mergeMilestoneToMain(this.s.basePath, milestoneId, roadmapContent);
1155
+ else if (result.mode === "branch") {
868
1156
  // Rebuild GitService after merge (branch HEAD changed)
869
1157
  rebuildGitService(this.s, this.deps);
870
- if (mergeResult.codeFilesChanged) {
871
- ctx.notify(`Milestone ${milestoneId} merged (branch mode).${mergeResult.pushed ? " Pushed to remote." : ""}`, "info");
872
- }
873
- else {
874
- ctx.notify(`WARNING: Milestone ${milestoneId} merged (branch mode) but contained NO code changes — only .gsd/ metadata. ` +
875
- `Review the milestone output and re-run if code is missing.`, "warning");
876
- }
877
- debugLog("WorktreeLifecycle", {
878
- action: "mergeAndExit",
879
- milestoneId,
880
- mode: "branch",
881
- result: "success",
882
- });
883
- return true;
884
- }
885
- catch (err) {
886
- const msg = err instanceof Error ? err.message : String(err);
887
- debugLog("WorktreeLifecycle", {
888
- action: "mergeAndExit",
889
- milestoneId,
890
- mode: "branch",
891
- result: "error",
892
- error: msg,
893
- });
894
- if (!(err instanceof UserNotifiedError)) {
895
- ctx.notify(`Milestone merge failed (branch mode): ${msg}`, "warning");
896
- }
897
- // Re-throw all errors so callers can apply their own recovery (#4380).
898
- throw err;
899
1158
  }
1159
+ return result;
900
1160
  }
1161
+ // ── Removed: _mergeWorktreeMode / _mergeBranchMode bodies ────────────
1162
+ // The merge bodies moved to file-scope `_mergeWorktreeModeImpl` and
1163
+ // `_mergeBranchModeImpl`, callable from the session-less
1164
+ // `mergeMilestoneStandalone` entry. The previous private methods are
1165
+ // gone; `_mergeAndExit` above is the only session-bound caller.
901
1166
  /**
902
1167
  * Fall back to branch-mode for `milestoneId` after a failed worktree
903
1168
  * creation, marking the session's isolation as degraded.
@@ -920,9 +1185,9 @@ export class WorktreeLifecycle {
920
1185
  }
921
1186
  const basePath = resolveWorktreeProjectRoot(this.s.basePath, this.s.originalBasePath);
922
1187
  try {
923
- this.deps.enterBranchModeForMilestone(basePath, milestoneId);
1188
+ lifecycleEnterBranchMode(this.deps, basePath, milestoneId);
924
1189
  rebuildGitService(this.s, this.deps);
925
- this.deps.invalidateAllCaches();
1190
+ invalidateAllCaches();
926
1191
  this.s.isolationDegraded = true;
927
1192
  ctx.notify(`Switched to branch milestone/${milestoneId} (isolation degraded).`, "info");
928
1193
  }
@@ -945,7 +1210,148 @@ export class WorktreeLifecycle {
945
1210
  return;
946
1211
  this.s.basePath = this.s.originalBasePath;
947
1212
  rebuildGitService(this.s, this.deps);
948
- this.deps.invalidateAllCaches();
1213
+ invalidateAllCaches();
1214
+ }
1215
+ /**
1216
+ * Adopt a session root (ADR-016 phase 2 / B2, issue #5620).
1217
+ *
1218
+ * Sole owner of `s.basePath` mutation for bootstrap-class transitions:
1219
+ * initial session start, paused-resume entry (before persisted-state
1220
+ * consultation), and hook-trigger session activation. Defensive about
1221
+ * `s.originalBasePath`:
1222
+ *
1223
+ * - When `originalBase` is explicit: overwrite.
1224
+ * - Otherwise, set `s.originalBasePath` only if it is currently empty —
1225
+ * resume paths that already restored `s.originalBasePath` from paused
1226
+ * metadata keep their value.
1227
+ *
1228
+ * Does NOT chdir; callers that need cwd alignment with the new basePath
1229
+ * are responsible for it. Does NOT rebuild `s.gitService` — callers that
1230
+ * mutate `s.basePath` to a non-project-root path (e.g. a worktree on a
1231
+ * subsequent milestone enter) go through `enterMilestone`, which handles
1232
+ * the rebuild.
1233
+ */
1234
+ adoptSessionRoot(base, originalBase) {
1235
+ this.s.basePath = base;
1236
+ if (originalBase !== undefined) {
1237
+ this.s.originalBasePath = originalBase;
1238
+ }
1239
+ else if (!this.s.originalBasePath) {
1240
+ this.s.originalBasePath = base;
1241
+ }
1242
+ }
1243
+ /**
1244
+ * Resume from a paused session (ADR-016 phase 2 / B3, issue #5621).
1245
+ *
1246
+ * Adopts `persistedWorktreePath` as `s.basePath` when the path is
1247
+ * non-null and exists on disk; otherwise falls back to `base`. Mirrors
1248
+ * the resume guard at `auto.ts:2164` — a stale or removed worktree
1249
+ * directory must not strand the resumed session in an invalid root.
1250
+ *
1251
+ * Folds in the body of the legacy `_resolvePausedResumeBasePathForTest`
1252
+ * helper (see `resolvePausedResumeBasePath` below). After this verb
1253
+ * lands the helper is deleted from `auto.ts` per the slice-7 closure
1254
+ * decision to retire `_*ForTest` suffixes from production paths.
1255
+ *
1256
+ * Like `adoptSessionRoot`, this is a pure session-state mutation — no
1257
+ * chdir, no git service rebuild, no cache invalidation.
1258
+ */
1259
+ resumeFromPausedSession(base, persistedWorktreePath) {
1260
+ this.s.basePath = resolvePausedResumeBasePath(base, persistedWorktreePath);
1261
+ }
1262
+ /**
1263
+ * Adopt an orphan worktree for a bootstrap-time merge (ADR-016 phase 2 / B4,
1264
+ * issue #5622).
1265
+ *
1266
+ * Owns the swap-run-revert protocol that bootstrap previously open-coded:
1267
+ *
1268
+ * 1. Snapshot prior `s.basePath` and `s.originalBasePath`.
1269
+ * 2. Resolve `getAutoWorktreePath(base, milestoneId) ?? base` before
1270
+ * mutating session state, then set `s.originalBasePath = base` and
1271
+ * `s.basePath` to the resolved path.
1272
+ * 3. Invoke the caller-supplied `run` callback under the swap.
1273
+ * 4. On `!result.merged`: revert to `base` and `chdir(base)` so the
1274
+ * caller can return early without leaving the session in a half-
1275
+ * swapped state.
1276
+ * 5. On `result.merged && !s.active`: revert to the snapshotted prior
1277
+ * paths (the orphan merge succeeded but bootstrap chose not to keep
1278
+ * the session active).
1279
+ * 6. On `result.merged && s.active`: leave the swap in place — the
1280
+ * loop will continue from the worktree path.
1281
+ *
1282
+ * The callback shape forces every caller through the same revert
1283
+ * protocol; an open-coded swap that forgets to revert on failure was the
1284
+ * original bug pattern this verb is designed to prevent.
1285
+ */
1286
+ adoptOrphanWorktree(milestoneId, base, run) {
1287
+ validateMilestoneId(milestoneId);
1288
+ const priorBasePath = this.s.basePath;
1289
+ const priorOriginalBasePath = this.s.originalBasePath;
1290
+ const restorePriorPaths = (phase) => {
1291
+ this.s.basePath = priorBasePath || base;
1292
+ this.s.originalBasePath = priorOriginalBasePath || base;
1293
+ try {
1294
+ process.chdir(this.s.originalBasePath || base);
1295
+ }
1296
+ catch (err) {
1297
+ debugLog("WorktreeLifecycle", {
1298
+ action: "adoptOrphanWorktree",
1299
+ phase,
1300
+ base: this.s.originalBasePath || base,
1301
+ error: err instanceof Error ? err.message : String(err),
1302
+ });
1303
+ }
1304
+ };
1305
+ let adoptedBasePath;
1306
+ try {
1307
+ const wtPathFn = primitiveOverrides(this.deps).getAutoWorktreePath ?? getAutoWorktreePath;
1308
+ adoptedBasePath = wtPathFn(base, milestoneId) ?? base;
1309
+ }
1310
+ catch (err) {
1311
+ restorePriorPaths("rollback-resolve-worktree-failed");
1312
+ throw err;
1313
+ }
1314
+ // Swap into the orphan worktree.
1315
+ this.s.originalBasePath = base;
1316
+ this.s.basePath = adoptedBasePath;
1317
+ let result;
1318
+ try {
1319
+ result = run();
1320
+ }
1321
+ catch (err) {
1322
+ restorePriorPaths("rollback-run-failed");
1323
+ throw err;
1324
+ }
1325
+ if (!result.merged) {
1326
+ // Failed orphan merge — revert to project root so the caller can
1327
+ // safely return early without leaving the session in an invalid
1328
+ // basePath. Mirror the chdir that bootstrap performed inline.
1329
+ this.s.basePath = base;
1330
+ this.s.originalBasePath = base;
1331
+ try {
1332
+ process.chdir(base);
1333
+ }
1334
+ catch (err) {
1335
+ debugLog("WorktreeLifecycle", {
1336
+ action: "adoptOrphanWorktree",
1337
+ phase: "revert-chdir-failed",
1338
+ base,
1339
+ error: err instanceof Error ? err.message : String(err),
1340
+ });
1341
+ }
1342
+ return result;
1343
+ }
1344
+ if (!this.s.active) {
1345
+ // Merge succeeded but the session was not (re)activated — restore
1346
+ // the snapshotted paths so the calling context resumes where it
1347
+ // was, with the orphan branch now merged on main.
1348
+ this.s.basePath = priorBasePath || base;
1349
+ this.s.originalBasePath = priorOriginalBasePath || base;
1350
+ }
1351
+ // else: merged && active — leave the swap; the loop continues from
1352
+ // the worktree path. Subsequent milestone enters mutate `s.basePath`
1353
+ // through their own Lifecycle verbs.
1354
+ return result;
949
1355
  }
950
1356
  /** True if `milestoneId` is the session's currently-active milestone. */
951
1357
  isInMilestone(milestoneId) {