gsd-pi 2.80.0-dev.fbe7c8c6f → 2.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (786) hide show
  1. package/README.md +35 -59
  2. package/dist/claude-cli-check.d.ts +30 -0
  3. package/dist/claude-cli-check.js +18 -7
  4. package/dist/cli.js +0 -19
  5. package/dist/headless-query.d.ts +10 -0
  6. package/dist/headless-query.js +6 -4
  7. package/dist/loader-entrypoint.d.ts +8 -0
  8. package/dist/loader-entrypoint.js +27 -0
  9. package/dist/loader.js +2 -11
  10. package/dist/mcp-server.d.ts +1 -0
  11. package/dist/mcp-server.js +6 -3
  12. package/dist/resources/.managed-resources-content-hash +1 -1
  13. package/dist/resources/extensions/claude-code-cli/readiness.js +18 -7
  14. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +40 -3
  15. package/dist/resources/extensions/github-sync/sync.js +4 -1
  16. package/dist/resources/extensions/gsd/auto/contracts.js +2 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +103 -9
  18. package/dist/resources/extensions/gsd/auto/orchestrator.js +48 -4
  19. package/dist/resources/extensions/gsd/auto/phases.js +282 -132
  20. package/dist/resources/extensions/gsd/auto/resolve.js +29 -0
  21. package/dist/resources/extensions/gsd/auto/run-unit.js +22 -30
  22. package/dist/resources/extensions/gsd/auto/session.js +9 -1
  23. package/dist/resources/extensions/gsd/auto/unit-runner-events.js +7 -0
  24. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
  25. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
  26. package/dist/resources/extensions/gsd/auto-dashboard.js +18 -0
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
  29. package/dist/resources/extensions/gsd/auto-post-unit.js +112 -78
  30. package/dist/resources/extensions/gsd/auto-prompts.js +103 -16
  31. package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
  32. package/dist/resources/extensions/gsd/auto-start.js +252 -14
  33. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  34. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  35. package/dist/resources/extensions/gsd/auto-worktree.js +229 -336
  36. package/dist/resources/extensions/gsd/auto.js +264 -86
  37. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +122 -11
  38. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +44 -37
  39. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -10
  40. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +30 -20
  41. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +4 -1
  42. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +6 -4
  43. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +5 -3
  44. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +1 -1
  45. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +330 -54
  46. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +4 -0
  47. package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
  48. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +19 -2
  49. package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
  50. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  51. package/dist/resources/extensions/gsd/commands-config.js +1 -1
  52. package/dist/resources/extensions/gsd/commands-eval-review.js +2 -2
  53. package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
  54. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  55. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  56. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  57. package/dist/resources/extensions/gsd/db/unit-dispatches.js +92 -0
  58. package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
  59. package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
  60. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
  61. package/dist/resources/extensions/gsd/git-service.js +73 -5
  62. package/dist/resources/extensions/gsd/gsd-db.js +46 -13
  63. package/dist/resources/extensions/gsd/guided-flow.js +119 -42
  64. package/dist/resources/extensions/gsd/health-widget.js +3 -0
  65. package/dist/resources/extensions/gsd/init-wizard.js +4 -1
  66. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  67. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  68. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  69. package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
  70. package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
  71. package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
  72. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  73. package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
  74. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  75. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +22 -17
  76. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  77. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
  78. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  79. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  80. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  81. package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  82. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  83. package/dist/resources/extensions/gsd/quick.js +34 -2
  84. package/dist/resources/extensions/gsd/recovery-classification.js +94 -0
  85. package/dist/resources/extensions/gsd/slice-cadence.js +45 -2
  86. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +15 -9
  87. package/dist/resources/extensions/gsd/state-reconciliation.js +27 -0
  88. package/dist/resources/extensions/gsd/tool-contract.js +50 -0
  89. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -7
  90. package/dist/resources/extensions/gsd/tools/complete-task.js +1 -1
  91. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  92. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  93. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  94. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  95. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  96. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  97. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  98. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  99. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  100. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  101. package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
  102. package/dist/resources/extensions/gsd/worktree-lifecycle.js +958 -0
  103. package/dist/resources/extensions/gsd/worktree-safety.js +119 -0
  104. package/dist/resources/extensions/gsd/worktree-state-projection.js +317 -0
  105. package/dist/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  106. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  107. package/dist/web/standalone/.next/BUILD_ID +1 -1
  108. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  109. package/dist/web/standalone/.next/build-manifest.json +4 -4
  110. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  111. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  112. package/dist/web/standalone/.next/required-server-files.json +3 -3
  113. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  114. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  124. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  140. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  152. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  172. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  182. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  188. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  204. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  205. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  208. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  217. package/dist/web/standalone/.next/server/app/index.html +1 -1
  218. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  219. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  220. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  221. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  222. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  223. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  224. package/dist/web/standalone/.next/server/app/page.js +2 -2
  225. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  226. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  227. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  228. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  229. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  230. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  231. package/dist/web/standalone/.next/server/middleware.js +3 -3
  232. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  233. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  234. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  235. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  236. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  237. package/dist/web/standalone/.next/static/chunks/8359.e059d86b255fce1c.js +10 -0
  238. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  239. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  240. package/dist/web/standalone/.next/static/chunks/app/page-200592a7f3baf579.js +1 -0
  241. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  242. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  243. package/dist/web/standalone/.next/static/chunks/{webpack-0481f1221120a7c6.js → webpack-de742b64187e13fe.js} +1 -1
  244. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  245. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  246. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  247. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  248. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  249. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  250. package/dist/web/standalone/server.js +1 -1
  251. package/dist/welcome-screen.d.ts +2 -0
  252. package/dist/welcome-screen.js +9 -7
  253. package/package.json +3 -3
  254. package/packages/daemon/package.json +2 -2
  255. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  256. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  257. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  258. package/packages/mcp-server/package.json +2 -2
  259. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  260. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  261. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  262. package/packages/native/package.json +1 -1
  263. package/packages/native/tsconfig.tsbuildinfo +1 -1
  264. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  265. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  266. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  267. package/packages/pi-agent-core/dist/agent.d.ts +9 -2
  268. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  269. package/packages/pi-agent-core/dist/agent.js +43 -11
  270. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  271. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  272. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  273. package/packages/pi-agent-core/dist/index.js +2 -0
  274. package/packages/pi-agent-core/dist/index.js.map +1 -1
  275. package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
  276. package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
  277. package/packages/pi-agent-core/dist/token-audit.js +221 -0
  278. package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
  279. package/packages/pi-agent-core/dist/types.d.ts +31 -0
  280. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  281. package/packages/pi-agent-core/dist/types.js.map +1 -1
  282. package/packages/pi-agent-core/package.json +1 -1
  283. package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
  284. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  285. package/packages/pi-agent-core/src/agent.ts +52 -11
  286. package/packages/pi-agent-core/src/index.ts +2 -0
  287. package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
  288. package/packages/pi-agent-core/src/token-audit.ts +287 -0
  289. package/packages/pi-agent-core/src/types.ts +26 -10
  290. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  291. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +35 -13
  292. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  293. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +21 -11
  294. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -1
  295. package/packages/pi-ai/dist/providers/anthropic.d.ts +7 -0
  296. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  297. package/packages/pi-ai/dist/providers/anthropic.js +9 -7
  298. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  299. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +23 -14
  300. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -1
  301. package/packages/pi-ai/dist/types.d.ts +2 -0
  302. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  303. package/packages/pi-ai/dist/types.js.map +1 -1
  304. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +48 -21
  305. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
  306. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +22 -21
  307. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -1
  308. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +22 -21
  309. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -1
  310. package/packages/pi-ai/package.json +1 -1
  311. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +39 -25
  312. package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -22
  313. package/packages/pi-ai/src/providers/anthropic.ts +22 -9
  314. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +34 -21
  315. package/packages/pi-ai/src/types.ts +3 -0
  316. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +56 -22
  317. package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +24 -28
  318. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +24 -28
  319. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  320. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +36 -1
  321. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  322. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +30 -1
  323. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  324. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +21 -2
  325. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  326. package/packages/pi-coding-agent/dist/core/agent-session.js +94 -16
  327. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  328. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +6 -2
  329. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  331. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  333. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  334. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  335. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  336. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  337. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  338. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  339. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  341. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  343. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  345. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
  348. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
  350. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/core/extensions/runner.js +6 -6
  352. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +5 -3
  354. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  355. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +60 -4
  356. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  357. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  358. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
  359. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
  361. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
  362. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
  363. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
  364. package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
  365. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/core/sdk.js +81 -4
  367. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  369. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  370. package/packages/pi-coding-agent/dist/core/settings-manager.js +25 -0
  371. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  372. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
  373. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
  375. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  376. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
  377. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  378. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +22 -56
  379. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -1
  380. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +1 -0
  381. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  382. package/packages/pi-coding-agent/dist/core/tools/bash.js +3 -1
  383. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  384. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts +2 -0
  385. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  386. package/packages/pi-coding-agent/dist/core/tools/edit.js +12 -1
  387. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  388. package/packages/pi-coding-agent/dist/core/tools/find.d.ts +2 -0
  389. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  390. package/packages/pi-coding-agent/dist/core/tools/find.js +14 -6
  391. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  392. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts +2 -0
  393. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  394. package/packages/pi-coding-agent/dist/core/tools/grep.js +12 -3
  395. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  396. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts +2 -0
  397. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts.map +1 -1
  398. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js +3 -1
  399. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js.map +1 -1
  400. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -1
  401. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  402. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
  403. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  404. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts +2 -0
  405. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  406. package/packages/pi-coding-agent/dist/core/tools/ls.js +10 -3
  407. package/packages/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  408. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -0
  409. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  410. package/packages/pi-coding-agent/dist/core/tools/read.js +3 -1
  411. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  412. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js +7 -62
  413. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js.map +1 -1
  414. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts +2 -0
  415. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts.map +1 -0
  416. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js +115 -0
  417. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js.map +1 -0
  418. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts +19 -0
  419. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts.map +1 -0
  420. package/packages/pi-coding-agent/dist/core/tools/tool-target.js +20 -0
  421. package/packages/pi-coding-agent/dist/core/tools/tool-target.js.map +1 -0
  422. package/packages/pi-coding-agent/dist/core/tools/write.d.ts +4 -0
  423. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  424. package/packages/pi-coding-agent/dist/core/tools/write.js +9 -1
  425. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  426. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +89 -2
  427. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  428. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +25 -1
  429. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  430. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  431. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  432. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +155 -7
  433. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  434. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  435. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +31 -6
  436. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  437. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +71 -0
  438. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  439. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
  440. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  441. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +25 -3
  442. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  443. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
  444. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  445. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  446. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  447. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +28 -0
  448. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  449. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  450. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  451. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  452. package/packages/pi-coding-agent/package.json +1 -1
  453. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +40 -1
  454. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +40 -1
  455. package/packages/pi-coding-agent/src/core/agent-session.ts +102 -16
  456. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +6 -2
  457. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  458. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  459. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  460. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  461. package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
  462. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +5 -3
  463. package/packages/pi-coding-agent/src/core/extensions/runner.ts +8 -5
  464. package/packages/pi-coding-agent/src/core/extensions/types.ts +63 -2
  465. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
  466. package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
  467. package/packages/pi-coding-agent/src/core/sdk.ts +92 -4
  468. package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
  469. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
  470. package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
  471. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +22 -66
  472. package/packages/pi-coding-agent/src/core/tools/bash.ts +4 -1
  473. package/packages/pi-coding-agent/src/core/tools/edit.ts +13 -1
  474. package/packages/pi-coding-agent/src/core/tools/find.ts +15 -6
  475. package/packages/pi-coding-agent/src/core/tools/grep.ts +13 -3
  476. package/packages/pi-coding-agent/src/core/tools/hashline-read.ts +4 -1
  477. package/packages/pi-coding-agent/src/core/tools/index.ts +8 -0
  478. package/packages/pi-coding-agent/src/core/tools/ls.ts +11 -3
  479. package/packages/pi-coding-agent/src/core/tools/read.ts +4 -1
  480. package/packages/pi-coding-agent/src/core/tools/spawn-shell-windows.test.ts +11 -72
  481. package/packages/pi-coding-agent/src/core/tools/tool-target-metadata.test.ts +127 -0
  482. package/packages/pi-coding-agent/src/core/tools/tool-target.ts +48 -0
  483. package/packages/pi-coding-agent/src/core/tools/write.ts +14 -2
  484. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +119 -2
  485. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +31 -1
  486. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +180 -7
  487. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +75 -0
  488. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +39 -8
  489. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +27 -3
  490. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +1 -1
  491. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +31 -0
  492. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  493. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  494. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  495. package/packages/pi-tui/dist/tui.js +18 -8
  496. package/packages/pi-tui/dist/tui.js.map +1 -1
  497. package/packages/pi-tui/package.json +1 -1
  498. package/packages/pi-tui/src/tui.ts +20 -8
  499. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  500. package/packages/rpc-client/README.md +7 -0
  501. package/packages/rpc-client/package.json +1 -1
  502. package/pkg/package.json +1 -1
  503. package/src/resources/extensions/claude-code-cli/readiness.ts +25 -7
  504. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +42 -3
  505. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +67 -0
  506. package/src/resources/extensions/github-sync/sync.ts +8 -1
  507. package/src/resources/extensions/github-sync/tests/sync-source.test.ts +6 -18
  508. package/src/resources/extensions/gsd/auto/contracts.ts +19 -2
  509. package/src/resources/extensions/gsd/auto/loop-deps.ts +10 -11
  510. package/src/resources/extensions/gsd/auto/loop.ts +134 -16
  511. package/src/resources/extensions/gsd/auto/orchestrator.ts +52 -4
  512. package/src/resources/extensions/gsd/auto/phases.ts +393 -200
  513. package/src/resources/extensions/gsd/auto/resolve.ts +42 -1
  514. package/src/resources/extensions/gsd/auto/run-unit.ts +28 -30
  515. package/src/resources/extensions/gsd/auto/session.ts +9 -1
  516. package/src/resources/extensions/gsd/auto/types.ts +3 -0
  517. package/src/resources/extensions/gsd/auto/unit-runner-events.ts +15 -0
  518. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
  519. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
  520. package/src/resources/extensions/gsd/auto-dashboard.ts +20 -0
  521. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
  522. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
  523. package/src/resources/extensions/gsd/auto-post-unit.ts +124 -80
  524. package/src/resources/extensions/gsd/auto-prompts.ts +112 -15
  525. package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
  526. package/src/resources/extensions/gsd/auto-start.ts +319 -19
  527. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  528. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  529. package/src/resources/extensions/gsd/auto-worktree.ts +259 -360
  530. package/src/resources/extensions/gsd/auto.ts +324 -88
  531. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +147 -11
  532. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +45 -37
  533. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +36 -10
  534. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +32 -19
  535. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +5 -1
  536. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +7 -4
  537. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +6 -3
  538. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +1 -1
  539. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +379 -54
  540. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +4 -0
  541. package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
  542. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -2
  543. package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
  544. package/src/resources/extensions/gsd/commands/handlers/ops.ts +4 -2
  545. package/src/resources/extensions/gsd/commands-config.ts +1 -1
  546. package/src/resources/extensions/gsd/commands-eval-review.ts +2 -2
  547. package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
  548. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  549. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  550. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  551. package/src/resources/extensions/gsd/db/unit-dispatches.ts +107 -0
  552. package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
  553. package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
  554. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
  555. package/src/resources/extensions/gsd/git-service.ts +87 -10
  556. package/src/resources/extensions/gsd/gsd-db.ts +50 -13
  557. package/src/resources/extensions/gsd/guided-flow.ts +148 -49
  558. package/src/resources/extensions/gsd/health-widget.ts +3 -0
  559. package/src/resources/extensions/gsd/init-wizard.ts +5 -1
  560. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  561. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  562. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  563. package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
  564. package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
  565. package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
  566. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  567. package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
  568. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  569. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  570. package/src/resources/extensions/gsd/prompts/complete-milestone.md +22 -17
  571. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  572. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  573. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  574. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  575. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  576. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  577. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  578. package/src/resources/extensions/gsd/quick.ts +37 -2
  579. package/src/resources/extensions/gsd/recovery-classification.ts +122 -0
  580. package/src/resources/extensions/gsd/slice-cadence.ts +49 -2
  581. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +23 -9
  582. package/src/resources/extensions/gsd/state-reconciliation.ts +57 -0
  583. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +59 -89
  584. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +47 -172
  585. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +0 -35
  586. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +35 -9
  587. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +543 -40
  588. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +80 -59
  589. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +119 -2
  590. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +3 -47
  591. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +76 -18
  592. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +175 -11
  593. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +54 -95
  594. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +67 -26
  595. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
  596. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +32 -30
  597. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +32 -128
  598. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +20 -54
  599. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +20 -30
  600. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +17 -29
  601. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +142 -0
  602. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +21 -39
  603. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +15 -24
  604. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +44 -29
  605. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +39 -51
  606. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +159 -213
  607. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +15 -32
  608. package/src/resources/extensions/gsd/tests/browser-teardown.test.ts +0 -41
  609. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
  610. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +34 -27
  611. package/src/resources/extensions/gsd/tests/cmux.test.ts +51 -53
  612. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +39 -61
  613. package/src/resources/extensions/gsd/tests/commands-config.test.ts +26 -19
  614. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  615. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
  616. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  617. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
  618. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +29 -33
  619. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +45 -108
  620. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  621. package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
  622. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +90 -31
  623. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  624. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +18 -10
  625. package/src/resources/extensions/gsd/tests/cwd-fallback-hardening.test.ts +138 -0
  626. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +4 -68
  627. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +6 -11
  628. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +14 -65
  629. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +44 -37
  630. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +58 -40
  631. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +25 -15
  632. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  633. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +35 -17
  634. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +16 -21
  635. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +15 -82
  636. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  637. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  638. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
  639. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
  640. package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +151 -0
  641. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +2 -20
  642. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +18 -26
  643. package/src/resources/extensions/gsd/tests/init-skip-git.test.ts +9 -12
  644. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +40 -0
  645. package/src/resources/extensions/gsd/tests/integration/commands-eval-review.integration.test.ts +4 -2
  646. package/src/resources/extensions/gsd/tests/integration/git-locale.test.ts +31 -20
  647. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +66 -0
  648. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +0 -47
  649. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +60 -202
  650. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +13 -56
  651. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +248 -10
  652. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
  653. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
  654. package/src/resources/extensions/gsd/tests/lazy-pi-tui-import.test.ts +44 -6
  655. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  656. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +21 -35
  657. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +37 -7
  658. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  659. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  660. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +267 -0
  661. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +88 -98
  662. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +70 -278
  663. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
  664. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +37 -30
  665. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +32 -28
  666. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +44 -9
  667. package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +144 -0
  668. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
  669. package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
  670. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +24 -37
  671. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +9 -24
  672. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +95 -75
  673. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  674. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  675. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +36 -22
  676. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +36 -30
  677. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
  678. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +74 -4
  679. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +20 -22
  680. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +130 -32
  681. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +18 -36
  682. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +35 -73
  683. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +76 -138
  684. package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
  685. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  686. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +70 -106
  687. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +59 -161
  688. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +33 -29
  689. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +22 -196
  690. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +23 -93
  691. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  692. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +50 -79
  693. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +27 -13
  694. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +151 -251
  695. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +41 -29
  696. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +58 -69
  697. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +32 -164
  698. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +57 -41
  699. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +91 -0
  700. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  701. package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
  702. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +14 -9
  703. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
  704. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +222 -0
  705. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +44 -42
  706. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  707. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +56 -24
  708. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +51 -11
  709. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +23 -0
  710. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +66 -50
  711. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +68 -107
  712. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +115 -42
  713. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +21 -77
  714. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +25 -116
  715. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +21 -57
  716. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +29 -76
  717. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +33 -24
  718. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +39 -30
  719. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  720. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +55 -0
  721. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -4
  722. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +35 -40
  723. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +48 -46
  724. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +14 -102
  725. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +78 -232
  726. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +32 -35
  727. package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
  728. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
  729. package/src/resources/extensions/gsd/tests/token-profile.test.ts +84 -309
  730. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
  731. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +134 -341
  732. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +210 -0
  733. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  734. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +8 -25
  735. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +80 -1
  736. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  737. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -99
  738. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +43 -36
  739. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -444
  740. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
  741. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +44 -189
  742. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -57
  743. package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
  744. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  745. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
  746. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +21 -44
  747. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +27 -26
  748. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +14 -13
  749. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +50 -31
  750. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +434 -0
  751. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -18
  752. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
  753. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
  754. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +373 -76
  755. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +327 -0
  756. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +120 -0
  757. package/src/resources/extensions/gsd/tests/write-gate.test.ts +40 -1
  758. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -13
  759. package/src/resources/extensions/gsd/tool-contract.ts +82 -0
  760. package/src/resources/extensions/gsd/tools/complete-milestone.ts +14 -15
  761. package/src/resources/extensions/gsd/tools/complete-task.ts +1 -1
  762. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  763. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  764. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  765. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  766. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  767. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  768. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  769. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  770. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  771. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  772. package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
  773. package/src/resources/extensions/gsd/worktree-lifecycle.ts +1255 -0
  774. package/src/resources/extensions/gsd/worktree-safety.ts +282 -0
  775. package/src/resources/extensions/gsd/worktree-state-projection.ts +404 -0
  776. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +86 -40
  777. package/src/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  778. package/dist/resources/extensions/gsd/worktree-resolver.js +0 -733
  779. package/dist/web/standalone/.next/static/chunks/8336.631939fb583761fa.js +0 -10
  780. package/dist/web/standalone/.next/static/chunks/app/page-fab3ebb85b006001.js +0 -1
  781. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  782. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  783. package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +0 -434
  784. package/src/resources/extensions/gsd/worktree-resolver.ts +0 -909
  785. /package/dist/web/standalone/.next/static/{yTuahMMuJzVnsov5PreWl → drLMkgfHQ8lzS229_HWYR}/_buildManifest.js +0 -0
  786. /package/dist/web/standalone/.next/static/{yTuahMMuJzVnsov5PreWl → drLMkgfHQ8lzS229_HWYR}/_ssgManifest.js +0 -0
@@ -0,0 +1,1255 @@
1
+ // GSD-2 — Worktree Lifecycle module: owns milestone entry/exit lifecycle behind a small, typed Interface.
2
+ /**
3
+ * Worktree Lifecycle module — first-class Module for worktree create/enter/exit/merge.
4
+ *
5
+ * Per ADR-016, this Module is the sole owner of:
6
+ * - `s.basePath` mutation across the session
7
+ * - `process.chdir()` discipline for worktree transitions (delegated to
8
+ * `enterAutoWorktree`/`createAutoWorktree`, which chdir internally)
9
+ * - milestone lease coordination (claim/refresh/release fencing tokens)
10
+ *
11
+ * Phase 1 of the migration ships only `enterMilestone`. The remaining verbs
12
+ * (`exitMilestone`, `degradeToBranchMode`, `restoreToProjectRoot`, queries) are
13
+ * extracted from `WorktreeResolver` in subsequent slices.
14
+ *
15
+ * The implementation lives in `_enterMilestoneCore` so `WorktreeResolver` can
16
+ * call the same body during its internal `mergeAndEnterNext` recursion without
17
+ * a circular reference. Both classes share the body until the Resolver retires.
18
+ */
19
+
20
+ import { existsSync, unlinkSync } from "node:fs";
21
+ import { randomUUID } from "node:crypto";
22
+ import { join } from "node:path";
23
+
24
+ import type { AutoSession } from "./auto/session.js";
25
+ import { debugLog } from "./debug-logger.js";
26
+ import { emitJournalEvent } from "./journal.js";
27
+ import { emitWorktreeCreated, emitWorktreeMerged } from "./worktree-telemetry.js";
28
+ import {
29
+ resolveWorktreeProjectRoot,
30
+ normalizeWorktreePathForCompare,
31
+ } from "./worktree-root.js";
32
+ import {
33
+ claimMilestoneLease,
34
+ refreshMilestoneLease,
35
+ releaseMilestoneLease,
36
+ } from "./db/milestone-leases.js";
37
+ import { MergeConflictError } from "./git-service.js";
38
+ import {
39
+ getCollapseCadence,
40
+ getMilestoneResquash,
41
+ resquashMilestoneOnMain,
42
+ } from "./slice-cadence.js";
43
+ import { loadEffectiveGSDPreferences } from "./preferences.js";
44
+ import type { WorktreeStateProjection } from "./worktree-state-projection.js";
45
+ import { createWorkspace, scopeMilestone } from "./workspace.js";
46
+
47
+ // ─── Types ───────────────────────────────────────────────────────────────
48
+
49
+ export interface NotifyCtx {
50
+ notify: (
51
+ msg: string,
52
+ level?: "info" | "warning" | "error" | "success",
53
+ ) => void;
54
+ }
55
+
56
+ /**
57
+ * Dependencies the Worktree Lifecycle Module needs from auto-mode wiring.
58
+ *
59
+ * Structurally a subset of `WorktreeResolverDeps`. `WorktreeResolver` can pass
60
+ * its own deps where these are expected — TypeScript's structural typing
61
+ * handles the narrowing.
62
+ *
63
+ * TODO(#5586): collapse this to the ADR target dep set after the resolver
64
+ * recursion retires; shrinking it now would force a parallel migration.
65
+ */
66
+ export interface WorktreeLifecycleDeps {
67
+ // ── Entry / branch-mode setup ────────────────────────────────────────
68
+ enterAutoWorktree: (basePath: string, milestoneId: string) => string;
69
+ createAutoWorktree: (basePath: string, milestoneId: string) => string;
70
+ enterBranchModeForMilestone: (basePath: string, milestoneId: string) => void;
71
+ getAutoWorktreePath: (basePath: string, milestoneId: string) => string | null;
72
+ getIsolationMode: (basePath?: string) => "worktree" | "branch" | "none";
73
+
74
+ // ── Cache + git service rebuild ──────────────────────────────────────
75
+ invalidateAllCaches: () => void;
76
+ GitServiceImpl: new (basePath: string, gitConfig: unknown) => unknown;
77
+ loadEffectiveGSDPreferences: () =>
78
+ | { preferences?: { git?: Record<string, unknown> } }
79
+ | undefined;
80
+
81
+ // ── State Projection Module (ADR-016 one-way edge) ───────────────────
82
+ /**
83
+ * State Projection Module called by Lifecycle on enter/exit transitions.
84
+ * Per ADR-016 the dependency direction is one-way: Lifecycle → Projection.
85
+ */
86
+ worktreeProjection: WorktreeStateProjection;
87
+
88
+ // ── Exit / merge / teardown ──────────────────────────────────────────
89
+ isInAutoWorktree: (basePath: string) => boolean;
90
+ autoCommitCurrentBranch: (
91
+ basePath: string,
92
+ reason: string,
93
+ milestoneId: string,
94
+ ) => void;
95
+ autoWorktreeBranch: (milestoneId: string) => string;
96
+ teardownAutoWorktree: (
97
+ basePath: string,
98
+ milestoneId: string,
99
+ opts?: { preserveBranch?: boolean },
100
+ ) => void;
101
+ mergeMilestoneToMain: (
102
+ basePath: string,
103
+ milestoneId: string,
104
+ roadmapContent: string,
105
+ ) => { pushed: boolean; codeFilesChanged: boolean };
106
+ getCurrentBranch: (basePath: string) => string;
107
+ /**
108
+ * Force-checkout the named branch in `basePath`. Required by the branch-mode
109
+ * merge path when HEAD has been moved off the milestone branch — silently
110
+ * skipping the merge would strand the milestone's commits.
111
+ */
112
+ checkoutBranch: (basePath: string, branch: string) => void;
113
+ resolveMilestoneFile: (
114
+ basePath: string,
115
+ milestoneId: string,
116
+ fileType: string,
117
+ ) => string | null;
118
+ /**
119
+ * Roadmap file reader. Injected so unit tests can substitute fixture
120
+ * content without touching the filesystem; production wiring passes
121
+ * `node:fs.readFileSync`.
122
+ */
123
+ readFileSync: (path: string, encoding: string) => string;
124
+ }
125
+
126
+ /**
127
+ * Internal sentinel — thrown by `_mergeBranchMode` when it has already
128
+ * emitted a user-visible error. The outer `mergeAndExit` catches the type
129
+ * and skips its own warning toast to avoid duplicate notifications.
130
+ */
131
+ class UserNotifiedError extends Error {
132
+ readonly cause?: unknown;
133
+
134
+ constructor(message: string, cause?: unknown) {
135
+ super(message);
136
+ this.name = "UserNotifiedError";
137
+ this.cause = cause;
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Compare two paths for physical identity, tolerating trailing slashes,
143
+ * symlink differences, and case variations on case-insensitive volumes.
144
+ *
145
+ * Used in place of string `===` / `!==` wherever one operand may be
146
+ * realpath-normalised and the other may not be (e.g. raw caller-supplied
147
+ * basePath vs. realpath-normalised projectRoot).
148
+ */
149
+ function isSamePathPhysical(a: string, b: string): boolean {
150
+ return normalizeWorktreePathForCompare(a) === normalizeWorktreePathForCompare(b);
151
+ }
152
+
153
+ export type EnterResult =
154
+ | { ok: true; mode: "worktree" | "branch" | "none"; path: string }
155
+ | {
156
+ ok: false;
157
+ reason:
158
+ | "isolation-degraded"
159
+ | "lease-conflict"
160
+ | "creation-failed"
161
+ | "invalid-milestone-id";
162
+ cause?: unknown;
163
+ };
164
+
165
+ export type ExitResult =
166
+ | { ok: true; merged: boolean; codeFilesChanged: boolean }
167
+ | { ok: false; reason: "merge-conflict" | "teardown-failed"; cause?: unknown };
168
+
169
+ // ─── Validation ──────────────────────────────────────────────────────────
170
+
171
+ function isValidMilestoneId(milestoneId: string): boolean {
172
+ return !/[\/\\]|\.\./.test(milestoneId);
173
+ }
174
+
175
+ function invalidMilestoneIdError(milestoneId: string): Error {
176
+ return new Error(
177
+ `Invalid milestoneId: ${milestoneId} — contains path separators or traversal`,
178
+ );
179
+ }
180
+
181
+ /**
182
+ * Throwing variant used by the merge/exit paths that surface failures via
183
+ * the typed `ExitResult` (callers wrap the throw → cause). The enter path
184
+ * uses `isValidMilestoneId` + the typed result directly.
185
+ */
186
+ function validateMilestoneId(milestoneId: string): void {
187
+ if (!isValidMilestoneId(milestoneId)) {
188
+ throw invalidMilestoneIdError(milestoneId);
189
+ }
190
+ }
191
+
192
+ // ─── Implementation core ─────────────────────────────────────────────────
193
+
194
+ /**
195
+ * Shared implementation of milestone entry. Called by both
196
+ * `WorktreeLifecycle.enterMilestone` and the legacy
197
+ * `WorktreeResolver.mergeAndEnterNext` internal recursion until the Resolver
198
+ * retires (slice #5587).
199
+ *
200
+ * Side effects (preserved from the original `WorktreeResolver.enterMilestone`):
201
+ * - mutates `s.milestoneLeaseToken` on lease claim/release/refresh
202
+ * - mutates `s.basePath` on successful worktree entry
203
+ * - mutates `s.gitService` (rebuilt against the new base path)
204
+ * - mutates `s.isolationDegraded` on hard failure of branch/worktree setup
205
+ * - emits journal events: worktree-skip, worktree-enter, worktree-create-failed
206
+ * - emits worktree-created telemetry on successful entry
207
+ * - notifies the caller via `ctx.notify` for every user-visible outcome
208
+ */
209
+ export function _enterMilestoneCore(
210
+ s: AutoSession,
211
+ deps: WorktreeLifecycleDeps,
212
+ milestoneId: string,
213
+ ctx: NotifyCtx,
214
+ ): EnterResult {
215
+ if (!isValidMilestoneId(milestoneId)) {
216
+ debugLog("WorktreeLifecycle", {
217
+ action: "enterMilestone",
218
+ milestoneId,
219
+ rejected: "invalid-milestone-id",
220
+ });
221
+ return {
222
+ ok: false,
223
+ reason: "invalid-milestone-id",
224
+ cause: invalidMilestoneIdError(milestoneId),
225
+ };
226
+ }
227
+
228
+ if (s.isolationDegraded) {
229
+ debugLog("WorktreeLifecycle", {
230
+ action: "enterMilestone",
231
+ milestoneId,
232
+ skipped: true,
233
+ reason: "isolation-degraded",
234
+ });
235
+ return { ok: false, reason: "isolation-degraded" };
236
+ }
237
+
238
+ // Phase B: claim a milestone lease before any worktree mutation. Two
239
+ // workers cannot enter the same milestone concurrently. Best-effort:
240
+ // skip if no worker registered (single-worker fallback) or DB
241
+ // unavailable; reuse existing lease if we already hold it on this
242
+ // milestone (re-entry within the same session).
243
+ if (s.workerId) {
244
+ if (
245
+ s.currentMilestoneId === milestoneId &&
246
+ s.milestoneLeaseToken !== null
247
+ ) {
248
+ const refreshed = refreshMilestoneLease(
249
+ s.workerId,
250
+ milestoneId,
251
+ s.milestoneLeaseToken,
252
+ );
253
+ if (refreshed) {
254
+ debugLog("WorktreeLifecycle", {
255
+ action: "enterMilestone",
256
+ milestoneId,
257
+ leaseRefreshed: true,
258
+ fencingToken: s.milestoneLeaseToken,
259
+ });
260
+ } else {
261
+ debugLog("WorktreeLifecycle", {
262
+ action: "enterMilestone",
263
+ milestoneId,
264
+ staleLeaseToken: s.milestoneLeaseToken,
265
+ });
266
+ s.milestoneLeaseToken = null;
267
+ }
268
+ }
269
+
270
+ // If we held a different milestone, release it first so other
271
+ // workers don't have to wait for TTL.
272
+ if (
273
+ s.currentMilestoneId &&
274
+ s.currentMilestoneId !== milestoneId &&
275
+ s.milestoneLeaseToken !== null
276
+ ) {
277
+ try {
278
+ releaseMilestoneLease(
279
+ s.workerId,
280
+ s.currentMilestoneId,
281
+ s.milestoneLeaseToken,
282
+ );
283
+ } catch (err) {
284
+ debugLog("WorktreeLifecycle", {
285
+ action: "enterMilestone",
286
+ milestoneId,
287
+ releasePriorLeaseError:
288
+ err instanceof Error ? err.message : String(err),
289
+ });
290
+ }
291
+ s.milestoneLeaseToken = null;
292
+ }
293
+
294
+ if (s.milestoneLeaseToken === null) {
295
+ try {
296
+ const claim = claimMilestoneLease(s.workerId, milestoneId);
297
+ if (claim.ok) {
298
+ s.milestoneLeaseToken = claim.token;
299
+ debugLog("WorktreeLifecycle", {
300
+ action: "enterMilestone",
301
+ milestoneId,
302
+ leaseAcquired: true,
303
+ fencingToken: claim.token,
304
+ expiresAt: claim.expiresAt,
305
+ });
306
+ } else {
307
+ // Lease held by another worker — fail loud so the user can
308
+ // see the conflict instead of silently double-running.
309
+ const msg = `Milestone ${milestoneId} is held by worker ${claim.byWorker} until ${claim.expiresAt}.`;
310
+ debugLog("WorktreeLifecycle", {
311
+ action: "enterMilestone",
312
+ milestoneId,
313
+ leaseHeldByOther: claim.byWorker,
314
+ expiresAt: claim.expiresAt,
315
+ });
316
+ ctx.notify(
317
+ `${msg} Another auto-mode worker is active. Stop it before entering ${milestoneId}.`,
318
+ "error",
319
+ );
320
+ return { ok: false, reason: "lease-conflict" };
321
+ }
322
+ } catch (err) {
323
+ // DB unavailable or other error — log and fall through to the
324
+ // pre-Phase-B single-worker behavior so a fresh project before
325
+ // DB init still works.
326
+ debugLog("WorktreeLifecycle", {
327
+ action: "enterMilestone",
328
+ milestoneId,
329
+ leaseError: err instanceof Error ? err.message : String(err),
330
+ });
331
+ }
332
+ }
333
+ }
334
+
335
+ // Resolve the project root for worktree operations via shared helper.
336
+ // Handles the case where originalBasePath is falsy and basePath is itself
337
+ // a worktree path — prevents double-nested worktree paths (#3729).
338
+ const basePath = resolveWorktreeProjectRoot(s.basePath, s.originalBasePath);
339
+ const mode = deps.getIsolationMode(basePath);
340
+
341
+ if (mode === "none") {
342
+ debugLog("WorktreeLifecycle", {
343
+ action: "enterMilestone",
344
+ milestoneId,
345
+ skipped: true,
346
+ reason: "isolation-disabled",
347
+ });
348
+ emitJournalEvent(s.originalBasePath || s.basePath, {
349
+ ts: new Date().toISOString(),
350
+ flowId: randomUUID(),
351
+ seq: 0,
352
+ eventType: "worktree-skip",
353
+ data: { milestoneId, reason: "isolation-disabled" },
354
+ });
355
+ return { ok: true, mode: "none", path: basePath };
356
+ }
357
+
358
+ debugLog("WorktreeLifecycle", {
359
+ action: "enterMilestone",
360
+ milestoneId,
361
+ mode,
362
+ basePath,
363
+ });
364
+
365
+ if (
366
+ mode === "worktree" &&
367
+ s.currentMilestoneId === milestoneId &&
368
+ s.basePath !== basePath
369
+ ) {
370
+ debugLog("WorktreeLifecycle", {
371
+ action: "enterMilestone",
372
+ milestoneId,
373
+ mode: "worktree",
374
+ result: "already-entered",
375
+ wtPath: s.basePath,
376
+ });
377
+ return { ok: true, mode: "worktree", path: s.basePath };
378
+ }
379
+
380
+ // ── Branch mode: create/checkout milestone branch, stay in project root ──
381
+ if (mode === "branch") {
382
+ try {
383
+ deps.enterBranchModeForMilestone(basePath, milestoneId);
384
+ // basePath does not change — no worktree, no chdir.
385
+ // Rebuild GitService so the new HEAD is reflected, then flush any
386
+ // path-keyed caches that may have been populated before the checkout.
387
+ rebuildGitService(s, deps);
388
+ deps.invalidateAllCaches();
389
+ debugLog("WorktreeLifecycle", {
390
+ action: "enterMilestone",
391
+ milestoneId,
392
+ mode: "branch",
393
+ result: "success",
394
+ });
395
+ emitJournalEvent(basePath, {
396
+ ts: new Date().toISOString(),
397
+ flowId: randomUUID(),
398
+ seq: 0,
399
+ eventType: "worktree-skip",
400
+ data: { milestoneId, reason: "branch-mode-no-worktree" },
401
+ });
402
+ ctx.notify(`Switched to branch milestone/${milestoneId}.`, "info");
403
+ return { ok: true, mode: "branch", path: basePath };
404
+ } catch (err) {
405
+ const msg = err instanceof Error ? err.message : String(err);
406
+ debugLog("WorktreeLifecycle", {
407
+ action: "enterMilestone",
408
+ milestoneId,
409
+ mode: "branch",
410
+ result: "error",
411
+ error: msg,
412
+ });
413
+ ctx.notify(
414
+ `Branch isolation setup for ${milestoneId} failed: ${msg}. Continuing on current branch.`,
415
+ "warning",
416
+ );
417
+ s.isolationDegraded = true;
418
+ return { ok: false, reason: "creation-failed", cause: err };
419
+ }
420
+ }
421
+
422
+ // ── Worktree mode ────────────────────────────────────────────────────────
423
+ try {
424
+ const existingPath = deps.getAutoWorktreePath(basePath, milestoneId);
425
+ let wtPath: string;
426
+
427
+ if (existingPath) {
428
+ wtPath = deps.enterAutoWorktree(basePath, milestoneId);
429
+ } else {
430
+ wtPath = deps.createAutoWorktree(basePath, milestoneId);
431
+ }
432
+
433
+ s.basePath = wtPath;
434
+ rebuildGitService(s, deps);
435
+ deps.invalidateAllCaches();
436
+
437
+ // Per ADR-016: Lifecycle calls Projection on entry, before any Unit
438
+ // dispatches. Build a temporary scope from the new basePath; callers may
439
+ // later set s.scope via their own rebuildScope hook (the two are
440
+ // independent — this scope is only used to drive the projection rules).
441
+ try {
442
+ const enterScope = scopeMilestone(createWorkspace(wtPath), milestoneId);
443
+ deps.worktreeProjection.projectRootToWorktree(enterScope);
444
+ } catch (projErr) {
445
+ // Non-fatal: projection failures must not block worktree entry.
446
+ // The pre-dispatch path in auto/phases.ts performs the same projection
447
+ // on every iteration, so a transient failure here self-heals on the
448
+ // next loop pass.
449
+ debugLog("WorktreeLifecycle", {
450
+ action: "enterMilestone",
451
+ phase: "projection-on-enter",
452
+ error: projErr instanceof Error ? projErr.message : String(projErr),
453
+ });
454
+ }
455
+
456
+ debugLog("WorktreeLifecycle", {
457
+ action: "enterMilestone",
458
+ milestoneId,
459
+ result: "success",
460
+ wtPath,
461
+ });
462
+ emitJournalEvent(s.originalBasePath || s.basePath, {
463
+ ts: new Date().toISOString(),
464
+ flowId: randomUUID(),
465
+ seq: 0,
466
+ eventType: "worktree-enter",
467
+ data: { milestoneId, wtPath, created: !existingPath },
468
+ });
469
+ // #4764 — record creation/enter as a lifecycle event so the telemetry
470
+ // aggregator can pair it with the eventual worktree-merged event.
471
+ try {
472
+ emitWorktreeCreated(s.originalBasePath || s.basePath, milestoneId, {
473
+ reason: existingPath ? "enter-milestone" : "create-milestone",
474
+ });
475
+ } catch (telemetryErr) {
476
+ debugLog("WorktreeLifecycle", {
477
+ action: "enterMilestone",
478
+ phase: "telemetry-emit",
479
+ error:
480
+ telemetryErr instanceof Error
481
+ ? telemetryErr.message
482
+ : String(telemetryErr),
483
+ });
484
+ }
485
+ ctx.notify(`Entered worktree for ${milestoneId} at ${wtPath}`, "info");
486
+ return { ok: true, mode: "worktree", path: wtPath };
487
+ } catch (err) {
488
+ const msg = err instanceof Error ? err.message : String(err);
489
+ debugLog("WorktreeLifecycle", {
490
+ action: "enterMilestone",
491
+ milestoneId,
492
+ result: "error",
493
+ error: msg,
494
+ });
495
+ emitJournalEvent(s.originalBasePath || s.basePath, {
496
+ ts: new Date().toISOString(),
497
+ flowId: randomUUID(),
498
+ seq: 0,
499
+ eventType: "worktree-create-failed",
500
+ data: { milestoneId, error: msg, fallback: "project-root" },
501
+ });
502
+ ctx.notify(
503
+ `Auto-worktree creation for ${milestoneId} failed: ${msg}. Continuing in project root.`,
504
+ "warning",
505
+ );
506
+ // Degrade isolation for the rest of this session so mergeAndExit
507
+ // doesn't try to merge a nonexistent worktree branch (#2483)
508
+ s.isolationDegraded = true;
509
+ // Do NOT update s.basePath — stay in project root
510
+ return { ok: false, reason: "creation-failed", cause: err };
511
+ }
512
+ }
513
+
514
+ function rebuildGitService(
515
+ s: AutoSession,
516
+ deps: WorktreeLifecycleDeps,
517
+ ): void {
518
+ const gitConfig =
519
+ deps.loadEffectiveGSDPreferences()?.preferences?.git ?? {};
520
+ s.gitService = new deps.GitServiceImpl(
521
+ s.basePath,
522
+ gitConfig,
523
+ ) as AutoSession["gitService"];
524
+ }
525
+
526
+ // ─── Module class ────────────────────────────────────────────────────────
527
+
528
+ /**
529
+ * Worktree Lifecycle module instance.
530
+ *
531
+ * Constructed once per auto-mode session. Holds the session reference so
532
+ * verbs can mutate `s.basePath` and related coordination state directly
533
+ * without round-tripping through callers.
534
+ */
535
+ export class WorktreeLifecycle {
536
+ private readonly s: AutoSession;
537
+ private readonly deps: WorktreeLifecycleDeps;
538
+
539
+ constructor(s: AutoSession, deps: WorktreeLifecycleDeps) {
540
+ this.s = s;
541
+ this.deps = deps;
542
+ }
543
+
544
+ /**
545
+ * Enter or create the auto-worktree for `milestoneId`. Idempotent if
546
+ * already in this milestone (lease refreshed; basePath unchanged).
547
+ *
548
+ * Returns a typed `EnterResult` describing the outcome. Callers may
549
+ * ignore the result if they read `s.basePath` directly afterwards
550
+ * (legacy behaviour); new callers should branch on the result.
551
+ */
552
+ enterMilestone(milestoneId: string, ctx: NotifyCtx): EnterResult {
553
+ return _enterMilestoneCore(this.s, this.deps, milestoneId, ctx);
554
+ }
555
+
556
+ /**
557
+ * Exit the current worktree. With `opts.merge === true`, runs the full
558
+ * merge-and-teardown path (worktree-mode or branch-mode auto-detected).
559
+ * With `opts.merge === false`, runs auto-commit and teardown without
560
+ * merging to main.
561
+ *
562
+ * Returns a typed `ExitResult`. `MergeConflictError` is surfaced as
563
+ * `{ ok: false, reason: "merge-conflict", cause }` instead of thrown,
564
+ * giving callers a typed branch for the expected failure path.
565
+ * Unexpected failures (filesystem, git permissions, etc.) are wrapped
566
+ * as `{ ok: false, reason: "teardown-failed", cause }` so callers always
567
+ * receive a discriminated union — no exceptions for any expected outcome.
568
+ */
569
+ exitMilestone(
570
+ milestoneId: string,
571
+ opts: { merge: boolean; preserveBranch?: boolean },
572
+ ctx: NotifyCtx,
573
+ ): ExitResult {
574
+ if (opts.merge) {
575
+ try {
576
+ const merged = this._mergeAndExit(milestoneId, ctx);
577
+ return { ok: true, merged, codeFilesChanged: false };
578
+ } catch (err) {
579
+ if (err instanceof MergeConflictError) {
580
+ return { ok: false, reason: "merge-conflict", cause: err };
581
+ }
582
+ return { ok: false, reason: "teardown-failed", cause: err };
583
+ }
584
+ }
585
+ try {
586
+ this._exitWithoutMerge(milestoneId, ctx, {
587
+ preserveBranch: opts.preserveBranch,
588
+ });
589
+ return { ok: true, merged: false, codeFilesChanged: false };
590
+ } catch (err) {
591
+ return { ok: false, reason: "teardown-failed", cause: err };
592
+ }
593
+ }
594
+
595
+ /**
596
+ * Milestone transition: merge the current milestone, then enter the next
597
+ * one. Pattern used when the loop detects that the active milestone has
598
+ * changed (current completed, next is now active). Caller is responsible
599
+ * for re-deriving state between the merge and the enter.
600
+ */
601
+ mergeAndEnterNext(
602
+ currentMilestoneId: string,
603
+ nextMilestoneId: string,
604
+ ctx: NotifyCtx,
605
+ ): void {
606
+ debugLog("WorktreeLifecycle", {
607
+ action: "mergeAndEnterNext",
608
+ currentMilestoneId,
609
+ nextMilestoneId,
610
+ });
611
+ let merged = false;
612
+ let mergeThrew = false;
613
+ try {
614
+ merged = this._mergeAndExit(currentMilestoneId, ctx);
615
+ } catch (err) {
616
+ if (err instanceof UserNotifiedError) throw err;
617
+ mergeThrew = true;
618
+ // _mergeAndExit emits a warning and restores state on failure during
619
+ // merge/cleanup. If it throws before recovery runs (e.g. validation,
620
+ // emitJournalEvent), basePath isn't restored — re-throw so we don't
621
+ // enter the next milestone with the current one unmerged.
622
+ const projectRoot = resolveWorktreeProjectRoot(
623
+ this.s.basePath,
624
+ this.s.originalBasePath,
625
+ );
626
+ if (this.s.basePath !== projectRoot) throw err;
627
+ // Otherwise: merge attempted, failed cleanly with state restored.
628
+ // The loop intentionally continues to the next milestone — the
629
+ // failed milestone's branch is preserved for manual recovery.
630
+ }
631
+ if (!merged && !mergeThrew && !this.s.isolationDegraded) {
632
+ // _mergeAndExit returned without attempting a merge (no roadmap
633
+ // → preserveBranch path) and state is restored. The current
634
+ // milestone was deliberately NOT merged; halt before entering the
635
+ // next so we don't silently strand commits on the preserved
636
+ // branch. (#5602 halt-on-no-merge regression coverage.)
637
+ //
638
+ // mergeThrew=true means a merge was attempted but failed — that
639
+ // path proceeds (existing test "enters next even if merge fails").
640
+ // isolationDegraded=true means the loop intentionally continues
641
+ // without merging — that path proceeds too.
642
+ throw new Error(
643
+ `Cannot enter milestone ${nextMilestoneId} because ${currentMilestoneId} was not merged`,
644
+ );
645
+ }
646
+ _enterMilestoneCore(this.s, this.deps, nextMilestoneId, ctx);
647
+ }
648
+
649
+ // ── Private — exit without merge ─────────────────────────────────────
650
+
651
+ private _exitWithoutMerge(
652
+ milestoneId: string,
653
+ ctx: NotifyCtx,
654
+ opts: { preserveBranch?: boolean },
655
+ ): void {
656
+ validateMilestoneId(milestoneId);
657
+ if (!this.deps.isInAutoWorktree(this.s.basePath)) {
658
+ debugLog("WorktreeLifecycle", {
659
+ action: "exitMilestone",
660
+ milestoneId,
661
+ skipped: true,
662
+ reason: "not-in-worktree",
663
+ });
664
+ return;
665
+ }
666
+
667
+ debugLog("WorktreeLifecycle", {
668
+ action: "exitMilestone",
669
+ milestoneId,
670
+ basePath: this.s.basePath,
671
+ });
672
+
673
+ try {
674
+ this.deps.autoCommitCurrentBranch(this.s.basePath, "stop", milestoneId);
675
+ } catch (err) {
676
+ debugLog("WorktreeLifecycle", {
677
+ action: "exitMilestone",
678
+ milestoneId,
679
+ phase: "auto-commit-failed",
680
+ error: err instanceof Error ? err.message : String(err),
681
+ });
682
+ ctx.notify(
683
+ `Auto-commit before exiting ${milestoneId} failed: ${err instanceof Error ? err.message : String(err)}. Branch ${this.deps.autoWorktreeBranch(milestoneId)} is preserved for recovery.`,
684
+ "warning",
685
+ );
686
+ }
687
+
688
+ if (this.s.originalBasePath) {
689
+ try {
690
+ process.chdir(this.s.originalBasePath);
691
+ } catch (err) {
692
+ debugLog("WorktreeLifecycle", {
693
+ action: "exitMilestone",
694
+ milestoneId,
695
+ phase: "pre-teardown-chdir-failed",
696
+ originalBasePath: this.s.originalBasePath,
697
+ error: err instanceof Error ? err.message : String(err),
698
+ });
699
+ ctx.notify(
700
+ `Could not leave milestone worktree before cleanup: ${err instanceof Error ? err.message : String(err)}. Branch ${this.deps.autoWorktreeBranch(milestoneId)} is preserved for recovery.`,
701
+ "warning",
702
+ );
703
+ }
704
+ }
705
+
706
+ let teardownFailed = false;
707
+ try {
708
+ this.deps.teardownAutoWorktree(this.s.originalBasePath, milestoneId, {
709
+ preserveBranch: opts.preserveBranch ?? false,
710
+ });
711
+ } catch (err) {
712
+ teardownFailed = true;
713
+ debugLog("WorktreeLifecycle", {
714
+ action: "exitMilestone",
715
+ milestoneId,
716
+ phase: "teardown-failed",
717
+ error: err instanceof Error ? err.message : String(err),
718
+ });
719
+ ctx.notify(
720
+ `Worktree cleanup failed for ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. Branch ${this.deps.autoWorktreeBranch(milestoneId)} is preserved for recovery.`,
721
+ "warning",
722
+ );
723
+ }
724
+
725
+ this.restoreToProjectRoot();
726
+ debugLog("WorktreeLifecycle", {
727
+ action: "exitMilestone",
728
+ milestoneId,
729
+ result: "done",
730
+ basePath: this.s.basePath,
731
+ });
732
+ ctx.notify(
733
+ teardownFailed
734
+ ? `Worktree exit for ${milestoneId} needs manual cleanup.`
735
+ : `Exited worktree for ${milestoneId}`,
736
+ teardownFailed ? "warning" : "info",
737
+ );
738
+ }
739
+
740
+ // ── Private — merge and exit (worktree-mode or branch-mode) ──────────
741
+
742
+ /**
743
+ * Merge the completed milestone branch back to main and exit the worktree.
744
+ *
745
+ * - **worktree mode**: reads the roadmap, runs squash merge, projects
746
+ * final state back via Projection.finalizeProjectionForMerge, tears
747
+ * down the worktree, restores `s.basePath`. Falls back to bare
748
+ * teardown (preserving the branch) if no roadmap exists.
749
+ * - **branch mode**: validates HEAD is on the milestone branch (recovers
750
+ * via checkout if not), merges, rebuilds GitService.
751
+ * - **none**: no-op unless physically inside an auto-worktree (#2625).
752
+ *
753
+ * Returns true when an actual squash-merge ran. Throws MergeConflictError
754
+ * (and other non-recoverable errors) for callers to handle.
755
+ */
756
+ private _mergeAndExit(milestoneId: string, ctx: NotifyCtx): boolean {
757
+ validateMilestoneId(milestoneId);
758
+
759
+ // Anchor cwd at the project root before any merge work. Some merge
760
+ // paths (mergeMilestoneToMain, slice-cadence) chdir explicitly; others
761
+ // (branch-mode, isolation-degraded skip) do not. If the worktree dir
762
+ // is later torn down while cwd still points into it, every subsequent
763
+ // process.cwd() throws ENOENT — which after de73fb43d surfaces as a
764
+ // session-failed cancel and (in headless mode) terminates the whole
765
+ // gsd process. Best-effort: silent on failure so synthetic test paths
766
+ // still pass.
767
+ if (this.s.originalBasePath) {
768
+ try {
769
+ process.chdir(this.s.originalBasePath);
770
+ } catch (err) {
771
+ debugLog("WorktreeLifecycle", {
772
+ action: "mergeAndExit",
773
+ phase: "pre-merge-chdir-failed",
774
+ milestoneId,
775
+ originalBasePath: this.s.originalBasePath,
776
+ error: err instanceof Error ? err.message : String(err),
777
+ });
778
+ }
779
+ }
780
+
781
+ // #4764 — telemetry: record start timestamp so we can emit merge duration.
782
+ const mergeStartedAt = new Date().toISOString();
783
+ const mergeStartMs = Date.now();
784
+
785
+ if (this.s.isolationDegraded) {
786
+ debugLog("WorktreeLifecycle", {
787
+ action: "mergeAndExit",
788
+ milestoneId,
789
+ skipped: true,
790
+ reason: "isolation-degraded",
791
+ });
792
+ ctx.notify(
793
+ `Skipping worktree merge for ${milestoneId} — isolation was degraded (worktree creation failed earlier). Work is on the current branch.`,
794
+ "info",
795
+ );
796
+ return false;
797
+ }
798
+
799
+ const mode = this.deps.getIsolationMode(
800
+ this.s.originalBasePath || this.s.basePath,
801
+ );
802
+ debugLog("WorktreeLifecycle", {
803
+ action: "mergeAndExit",
804
+ milestoneId,
805
+ mode,
806
+ basePath: this.s.basePath,
807
+ });
808
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
809
+ ts: new Date().toISOString(),
810
+ flowId: randomUUID(),
811
+ seq: 0,
812
+ eventType: "worktree-merge-start",
813
+ data: { milestoneId, mode },
814
+ });
815
+
816
+ // #2625: If we are physically inside an auto-worktree, we MUST merge
817
+ // regardless of the current isolation config. This prevents data loss
818
+ // when the default isolation mode changes between versions.
819
+ const inWorktree =
820
+ this.deps.isInAutoWorktree(this.s.basePath) && this.s.originalBasePath;
821
+
822
+ if (mode === "none" && !inWorktree) {
823
+ debugLog("WorktreeLifecycle", {
824
+ action: "mergeAndExit",
825
+ milestoneId,
826
+ skipped: true,
827
+ reason: "mode-none",
828
+ });
829
+ return false;
830
+ }
831
+
832
+ let actuallyMerged = false;
833
+ if (mode === "worktree" || inWorktree) {
834
+ actuallyMerged = this._mergeWorktreeMode(milestoneId, ctx);
835
+ } else if (mode === "branch") {
836
+ actuallyMerged = this._mergeBranchMode(milestoneId, ctx);
837
+ }
838
+
839
+ if (!actuallyMerged) {
840
+ this.s.milestoneStartShas.delete(milestoneId);
841
+ return false;
842
+ }
843
+
844
+ // #4765 — when collapse_cadence=slice AND milestone_resquash=true, the
845
+ // N per-slice commits on main should be collapsed into one milestone
846
+ // commit. Done AFTER the primary merge-and-teardown so the branch and
847
+ // worktree are already cleaned up; we operate on main directly.
848
+ try {
849
+ const startSha = this.s.milestoneStartShas.get(milestoneId);
850
+ if (startSha) {
851
+ const prefs = loadEffectiveGSDPreferences(
852
+ this.s.originalBasePath || this.s.basePath,
853
+ )?.preferences;
854
+ if (
855
+ getCollapseCadence(prefs) === "slice" &&
856
+ getMilestoneResquash(prefs)
857
+ ) {
858
+ const result = resquashMilestoneOnMain(
859
+ this.s.originalBasePath || this.s.basePath,
860
+ milestoneId,
861
+ startSha,
862
+ );
863
+ if (result.resquashed) {
864
+ ctx.notify(
865
+ `slice-cadence: re-squashed slice commits for ${milestoneId} into a single milestone commit.`,
866
+ "info",
867
+ );
868
+ }
869
+ }
870
+ this.s.milestoneStartShas.delete(milestoneId);
871
+ }
872
+ } catch (err) {
873
+ debugLog("WorktreeLifecycle", {
874
+ action: "mergeAndExit",
875
+ milestoneId,
876
+ phase: "resquash",
877
+ error: err instanceof Error ? err.message : String(err),
878
+ });
879
+ }
880
+
881
+ // #4764 — record merge completion. Only reaches here when an actual
882
+ // merge ran; failure paths throw out before this point.
883
+ try {
884
+ emitWorktreeMerged(
885
+ this.s.originalBasePath || this.s.basePath,
886
+ milestoneId,
887
+ {
888
+ reason: "milestone-complete",
889
+ startedAt: mergeStartedAt,
890
+ durationMs: Date.now() - mergeStartMs,
891
+ },
892
+ );
893
+ } catch (telemetryErr) {
894
+ debugLog("WorktreeLifecycle", {
895
+ action: "mergeAndExit",
896
+ phase: "telemetry-emit",
897
+ error:
898
+ telemetryErr instanceof Error
899
+ ? telemetryErr.message
900
+ : String(telemetryErr),
901
+ });
902
+ }
903
+ return true;
904
+ }
905
+
906
+ /** Worktree-mode merge body. Returns true when an actual squash-merge ran. */
907
+ private _mergeWorktreeMode(milestoneId: string, ctx: NotifyCtx): boolean {
908
+ const originalBase = this.s.originalBasePath;
909
+ if (!originalBase) {
910
+ debugLog("WorktreeLifecycle", {
911
+ action: "mergeAndExit",
912
+ milestoneId,
913
+ mode: "worktree",
914
+ skipped: true,
915
+ reason: "missing-original-base",
916
+ });
917
+ return false;
918
+ }
919
+
920
+ let merged = false;
921
+ try {
922
+ // ADR-016: final projection before teardown. Replaces the legacy
923
+ // syncWorktreeStateBack(originalBase, basePath, milestoneId) call.
924
+ const finalScope = scopeMilestone(
925
+ createWorkspace(this.s.basePath),
926
+ milestoneId,
927
+ );
928
+ const { synced } =
929
+ this.deps.worktreeProjection.finalizeProjectionForMerge(finalScope);
930
+ if (synced.length > 0) {
931
+ debugLog("WorktreeLifecycle", {
932
+ action: "mergeAndExit",
933
+ milestoneId,
934
+ phase: "reverse-sync",
935
+ synced: synced.length,
936
+ });
937
+ }
938
+
939
+ // Resolve roadmap — try project root first, then worktree path as
940
+ // fallback. The worktree may hold the only copy when state-back
941
+ // projection silently dropped it or .gsd/ is not symlinked. Without
942
+ // the fallback, a missing roadmap triggers bare teardown which
943
+ // deletes the branch and orphans all milestone commits (#1573).
944
+ let roadmapPath = this.deps.resolveMilestoneFile(
945
+ originalBase,
946
+ milestoneId,
947
+ "ROADMAP",
948
+ );
949
+ if (
950
+ !roadmapPath &&
951
+ !isSamePathPhysical(this.s.basePath, originalBase)
952
+ ) {
953
+ roadmapPath = this.deps.resolveMilestoneFile(
954
+ this.s.basePath,
955
+ milestoneId,
956
+ "ROADMAP",
957
+ );
958
+ if (roadmapPath) {
959
+ debugLog("WorktreeLifecycle", {
960
+ action: "mergeAndExit",
961
+ milestoneId,
962
+ phase: "roadmap-fallback",
963
+ note: "resolved from worktree path",
964
+ });
965
+ }
966
+ }
967
+
968
+ if (roadmapPath) {
969
+ const roadmapContent = this.deps.readFileSync(roadmapPath, "utf-8");
970
+ const mergeResult = this.deps.mergeMilestoneToMain(
971
+ originalBase,
972
+ milestoneId,
973
+ roadmapContent,
974
+ );
975
+ merged = true;
976
+
977
+ // #2945 Bug 3: mergeMilestoneToMain performs best-effort worktree
978
+ // cleanup internally (step 12), but it can silently fail on Windows
979
+ // or when the worktree directory is locked. Perform a secondary
980
+ // teardown here to ensure the worktree is properly cleaned up.
981
+ // Idempotent — if already removed, teardownAutoWorktree no-ops.
982
+ try {
983
+ this.deps.teardownAutoWorktree(originalBase, milestoneId);
984
+ } catch {
985
+ // Best-effort — primary cleanup in mergeMilestoneToMain may have
986
+ // already removed the worktree.
987
+ }
988
+
989
+ if (mergeResult.codeFilesChanged) {
990
+ ctx.notify(
991
+ `Milestone ${milestoneId} merged to main.${mergeResult.pushed ? " Pushed to remote." : ""}`,
992
+ "info",
993
+ );
994
+ } else {
995
+ // #1906 — milestone produced only .gsd/ metadata. Surface
996
+ // clearly so the user knows the milestone is not truly complete.
997
+ ctx.notify(
998
+ `WARNING: Milestone ${milestoneId} merged to main but contained NO code changes — only .gsd/ metadata files. ` +
999
+ `The milestone summary may describe planned work that was never implemented. ` +
1000
+ `Review the milestone output and re-run if code is missing.`,
1001
+ "warning",
1002
+ );
1003
+ }
1004
+ } else {
1005
+ // No roadmap at either location — teardown but PRESERVE the branch
1006
+ // so commits are not orphaned (#1573).
1007
+ this.deps.teardownAutoWorktree(originalBase, milestoneId, {
1008
+ preserveBranch: true,
1009
+ });
1010
+ ctx.notify(
1011
+ `Exited worktree for ${milestoneId} (no roadmap found — branch preserved for manual merge).`,
1012
+ "warning",
1013
+ );
1014
+ }
1015
+ } catch (err) {
1016
+ const msg = err instanceof Error ? err.message : String(err);
1017
+ debugLog("WorktreeLifecycle", {
1018
+ action: "mergeAndExit",
1019
+ milestoneId,
1020
+ result: "error",
1021
+ error: msg,
1022
+ fallback: "chdir-to-project-root",
1023
+ });
1024
+ emitJournalEvent(this.s.originalBasePath || this.s.basePath, {
1025
+ ts: new Date().toISOString(),
1026
+ flowId: randomUUID(),
1027
+ seq: 0,
1028
+ eventType: "worktree-merge-failed",
1029
+ data: { milestoneId, error: msg },
1030
+ });
1031
+ // Surface a clear, actionable error. Worktree and milestone branch
1032
+ // are intentionally preserved — nothing has been deleted. User can
1033
+ // retry /gsd dispatch complete-milestone or merge manually once the
1034
+ // underlying issue is fixed (#1668, #1891).
1035
+ ctx.notify(
1036
+ `Milestone merge failed: ${msg}. Your worktree and milestone branch are preserved — retry with \`/gsd dispatch complete-milestone\` or merge manually.`,
1037
+ "warning",
1038
+ );
1039
+
1040
+ // Clean up stale merge state left by failed squash-merge (#1389)
1041
+ try {
1042
+ const gitDir = join(originalBase || this.s.basePath, ".git");
1043
+ for (const f of ["SQUASH_MSG", "MERGE_HEAD", "MERGE_MSG"]) {
1044
+ const p = join(gitDir, f);
1045
+ if (existsSync(p)) unlinkSync(p);
1046
+ }
1047
+ } catch {
1048
+ /* best-effort */
1049
+ }
1050
+
1051
+ // Error recovery: always restore to project root
1052
+ if (originalBase) {
1053
+ try {
1054
+ process.chdir(originalBase);
1055
+ } catch {
1056
+ /* best-effort */
1057
+ }
1058
+ }
1059
+
1060
+ // Restore state before re-throwing so callers always get a
1061
+ // consistent session (#4380).
1062
+ this.restoreToProjectRoot();
1063
+ // Re-throw: MergeConflictError stops the auto loop (#2330);
1064
+ // non-conflict errors must also propagate so broken states are
1065
+ // diagnosable (#4380).
1066
+ throw err;
1067
+ }
1068
+
1069
+ // Always restore basePath and rebuild — whether merge succeeded or failed
1070
+ this.restoreToProjectRoot();
1071
+ debugLog("WorktreeLifecycle", {
1072
+ action: "mergeAndExit",
1073
+ milestoneId,
1074
+ result: "done",
1075
+ basePath: this.s.basePath,
1076
+ });
1077
+ return merged;
1078
+ }
1079
+
1080
+ /** Branch-mode merge body. Returns true when a merge actually ran. */
1081
+ private _mergeBranchMode(milestoneId: string, ctx: NotifyCtx): boolean {
1082
+ try {
1083
+ const currentBranch = this.deps.getCurrentBranch(this.s.basePath);
1084
+ const milestoneBranch = this.deps.autoWorktreeBranch(milestoneId);
1085
+
1086
+ if (currentBranch !== milestoneBranch) {
1087
+ // #5538-followup: previous behaviour was to silently `return false`
1088
+ // when HEAD wasn't on the milestone branch — that let the loop
1089
+ // advance with the milestone's commits stranded on the branch.
1090
+ // Attempt recovery by force-checking-out the milestone branch; if
1091
+ // that fails, throw so the caller pauses auto-mode and the user
1092
+ // sees the failure instead of a silent merge skip.
1093
+ debugLog("WorktreeLifecycle", {
1094
+ action: "mergeAndExit",
1095
+ milestoneId,
1096
+ mode: "branch",
1097
+ recovery: "checkout-milestone-branch",
1098
+ currentBranch,
1099
+ milestoneBranch,
1100
+ });
1101
+ try {
1102
+ this.deps.checkoutBranch(this.s.basePath, milestoneBranch);
1103
+ } catch (checkoutErr) {
1104
+ const checkoutMsg =
1105
+ checkoutErr instanceof Error
1106
+ ? checkoutErr.message
1107
+ : String(checkoutErr);
1108
+ ctx.notify(
1109
+ `Cannot merge milestone ${milestoneId}: working tree is on ${currentBranch} and checkout to ${milestoneBranch} failed (${checkoutMsg}). Resolve manually and run /gsd auto to resume.`,
1110
+ "error",
1111
+ );
1112
+ throw new UserNotifiedError(checkoutMsg, checkoutErr);
1113
+ }
1114
+
1115
+ const reverify = this.deps.getCurrentBranch(this.s.basePath);
1116
+ if (reverify !== milestoneBranch) {
1117
+ const reverifyMsg = `branch checkout to ${milestoneBranch} reported success but current branch is ${reverify}`;
1118
+ ctx.notify(
1119
+ `Cannot merge milestone ${milestoneId}: ${reverifyMsg}. Resolve manually and run /gsd auto to resume.`,
1120
+ "error",
1121
+ );
1122
+ throw new UserNotifiedError(reverifyMsg);
1123
+ }
1124
+ }
1125
+
1126
+ const roadmapPath = this.deps.resolveMilestoneFile(
1127
+ this.s.basePath,
1128
+ milestoneId,
1129
+ "ROADMAP",
1130
+ );
1131
+ if (!roadmapPath) {
1132
+ debugLog("WorktreeLifecycle", {
1133
+ action: "mergeAndExit",
1134
+ milestoneId,
1135
+ mode: "branch",
1136
+ skipped: true,
1137
+ reason: "no-roadmap",
1138
+ });
1139
+ return false;
1140
+ }
1141
+
1142
+ const roadmapContent = this.deps.readFileSync(roadmapPath, "utf-8");
1143
+ const mergeResult = this.deps.mergeMilestoneToMain(
1144
+ this.s.basePath,
1145
+ milestoneId,
1146
+ roadmapContent,
1147
+ );
1148
+
1149
+ // Rebuild GitService after merge (branch HEAD changed)
1150
+ rebuildGitService(this.s, this.deps);
1151
+
1152
+ if (mergeResult.codeFilesChanged) {
1153
+ ctx.notify(
1154
+ `Milestone ${milestoneId} merged (branch mode).${mergeResult.pushed ? " Pushed to remote." : ""}`,
1155
+ "info",
1156
+ );
1157
+ } else {
1158
+ ctx.notify(
1159
+ `WARNING: Milestone ${milestoneId} merged (branch mode) but contained NO code changes — only .gsd/ metadata. ` +
1160
+ `Review the milestone output and re-run if code is missing.`,
1161
+ "warning",
1162
+ );
1163
+ }
1164
+ debugLog("WorktreeLifecycle", {
1165
+ action: "mergeAndExit",
1166
+ milestoneId,
1167
+ mode: "branch",
1168
+ result: "success",
1169
+ });
1170
+ return true;
1171
+ } catch (err) {
1172
+ const msg = err instanceof Error ? err.message : String(err);
1173
+ debugLog("WorktreeLifecycle", {
1174
+ action: "mergeAndExit",
1175
+ milestoneId,
1176
+ mode: "branch",
1177
+ result: "error",
1178
+ error: msg,
1179
+ });
1180
+ if (!(err instanceof UserNotifiedError)) {
1181
+ ctx.notify(`Milestone merge failed (branch mode): ${msg}`, "warning");
1182
+ }
1183
+ // Re-throw all errors so callers can apply their own recovery (#4380).
1184
+ throw err;
1185
+ }
1186
+ }
1187
+
1188
+ /**
1189
+ * Fall back to branch-mode for `milestoneId` after a failed worktree
1190
+ * creation, marking the session's isolation as degraded.
1191
+ *
1192
+ * Currently delegates to `enterBranchModeForMilestone` from auto-worktree.
1193
+ * Idempotent: subsequent calls in a degraded session are no-ops.
1194
+ *
1195
+ * Issue #5587 ships this as a thin adapter; the body extraction joins the
1196
+ * other merge-logic move-out in a follow-up cleanup slice.
1197
+ */
1198
+ degradeToBranchMode(milestoneId: string, ctx: NotifyCtx): void {
1199
+ if (this.s.isolationDegraded) {
1200
+ debugLog("WorktreeLifecycle", {
1201
+ action: "degradeToBranchMode",
1202
+ milestoneId,
1203
+ skipped: true,
1204
+ reason: "already-degraded",
1205
+ });
1206
+ return;
1207
+ }
1208
+ const basePath = resolveWorktreeProjectRoot(
1209
+ this.s.basePath,
1210
+ this.s.originalBasePath,
1211
+ );
1212
+ try {
1213
+ this.deps.enterBranchModeForMilestone(basePath, milestoneId);
1214
+ rebuildGitService(this.s, this.deps);
1215
+ this.deps.invalidateAllCaches();
1216
+ this.s.isolationDegraded = true;
1217
+ ctx.notify(
1218
+ `Switched to branch milestone/${milestoneId} (isolation degraded).`,
1219
+ "info",
1220
+ );
1221
+ } catch (err) {
1222
+ const msg = err instanceof Error ? err.message : String(err);
1223
+ ctx.notify(
1224
+ `Branch isolation setup for ${milestoneId} failed: ${msg}. Continuing on current branch.`,
1225
+ "warning",
1226
+ );
1227
+ this.s.isolationDegraded = true;
1228
+ }
1229
+ }
1230
+
1231
+ /**
1232
+ * Restore `s.basePath` to `s.originalBasePath` and rebuild `s.gitService`.
1233
+ * No-op when `originalBasePath` is empty (fresh sessions).
1234
+ *
1235
+ * Used by error/cleanup paths that need the session to behave as if the
1236
+ * worktree was never entered. Does NOT teardown the worktree directory —
1237
+ * callers that need teardown go through `exitMilestone({ merge: false })`.
1238
+ */
1239
+ restoreToProjectRoot(): void {
1240
+ if (!this.s.originalBasePath) return;
1241
+ this.s.basePath = this.s.originalBasePath;
1242
+ rebuildGitService(this.s, this.deps);
1243
+ this.deps.invalidateAllCaches();
1244
+ }
1245
+
1246
+ /** True if `milestoneId` is the session's currently-active milestone. */
1247
+ isInMilestone(milestoneId: string): boolean {
1248
+ return this.s.currentMilestoneId === milestoneId;
1249
+ }
1250
+
1251
+ /** The active milestone id, or `null` if no milestone is active. */
1252
+ getCurrentMilestoneIfAny(): string | null {
1253
+ return this.s.currentMilestoneId;
1254
+ }
1255
+ }