gsd-pi 2.65.0 → 2.66.0-dev.6c91c1f

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 (458) hide show
  1. package/dist/mcp-server.js +6 -2
  2. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  3. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  4. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +2 -0
  5. package/dist/resources/extensions/gsd/auto/loop.js +2 -2
  6. package/dist/resources/extensions/gsd/auto/phases.js +48 -5
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  8. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  9. package/dist/resources/extensions/gsd/auto/types.js +2 -0
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  15. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  16. package/dist/resources/extensions/gsd/auto-start.js +175 -12
  17. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  18. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  19. package/dist/resources/extensions/gsd/auto.js +21 -15
  20. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  22. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  23. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -1
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  27. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -2
  29. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  31. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  32. package/dist/resources/extensions/gsd/detection.js +1 -1
  33. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  35. package/dist/resources/extensions/gsd/doctor.js +2 -1
  36. package/dist/resources/extensions/gsd/files.js +17 -0
  37. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  38. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  39. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  40. package/dist/resources/extensions/gsd/index.js +1 -1
  41. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  42. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  43. package/dist/resources/extensions/gsd/notification-overlay.js +1 -1
  44. package/dist/resources/extensions/gsd/notification-widget.js +2 -1
  45. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
  46. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  47. package/dist/resources/extensions/gsd/pre-execution-checks.js +26 -5
  48. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  49. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  50. package/dist/resources/extensions/gsd/preferences.js +9 -2
  51. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  52. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  53. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  54. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  56. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  57. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  59. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  60. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  61. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  62. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  63. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  64. package/dist/resources/extensions/gsd/quick.js +19 -15
  65. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  66. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  67. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  68. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  69. package/dist/resources/extensions/gsd/state.js +115 -28
  70. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  71. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  72. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  73. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  74. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  75. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  76. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  77. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  78. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  79. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  80. package/dist/resources/extensions/gsd/undo.js +3 -2
  81. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  82. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  83. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  84. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  85. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  86. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  87. package/dist/resources/extensions/gsd/worktree.js +9 -0
  88. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  89. package/dist/resources/extensions/subagent/agents.js +19 -5
  90. package/dist/web/standalone/.next/BUILD_ID +1 -1
  91. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  92. package/dist/web/standalone/.next/build-manifest.json +4 -4
  93. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  94. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  95. package/dist/web/standalone/.next/required-server-files.json +3 -3
  96. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  97. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  99. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  107. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  111. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  113. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  135. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  155. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  165. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  171. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  187. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  189. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  191. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/index.html +1 -1
  201. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  202. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  203. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  204. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  205. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  206. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  207. package/dist/web/standalone/.next/server/app/page.js +2 -2
  208. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  210. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  211. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  212. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  213. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/middleware.js +2 -2
  215. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  217. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  218. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  219. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  220. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  221. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  222. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  223. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  227. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  228. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  229. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  230. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  231. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  232. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  233. package/dist/web/standalone/server.js +1 -1
  234. package/package.json +1 -1
  235. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  237. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  239. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  240. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  241. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  246. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  247. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  249. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  251. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  252. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  253. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  255. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  256. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  257. package/packages/pi-coding-agent/package.json +1 -1
  258. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  259. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  260. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  261. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  263. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  265. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  266. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  267. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  268. package/packages/pi-tui/dist/components/image.js +4 -0
  269. package/packages/pi-tui/dist/components/image.js.map +1 -1
  270. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  271. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  272. package/packages/pi-tui/dist/components/image.test.js +32 -0
  273. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  274. package/packages/pi-tui/src/components/image.test.ts +36 -0
  275. package/packages/pi-tui/src/components/image.ts +5 -0
  276. package/pkg/package.json +1 -1
  277. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  278. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  279. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
  280. package/src/resources/extensions/gsd/auto/loop.ts +2 -2
  281. package/src/resources/extensions/gsd/auto/phases.ts +68 -3
  282. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  283. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  284. package/src/resources/extensions/gsd/auto/types.ts +5 -0
  285. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  286. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  287. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  288. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  289. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  290. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  291. package/src/resources/extensions/gsd/auto-start.ts +188 -10
  292. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  293. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  294. package/src/resources/extensions/gsd/auto.ts +19 -8
  295. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  296. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  297. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  298. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  299. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  300. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
  301. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  302. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  303. package/src/resources/extensions/gsd/commands/handlers/core.ts +26 -2
  304. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  305. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  306. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  307. package/src/resources/extensions/gsd/detection.ts +1 -1
  308. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  309. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  310. package/src/resources/extensions/gsd/doctor.ts +2 -1
  311. package/src/resources/extensions/gsd/files.ts +19 -0
  312. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  313. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  314. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  315. package/src/resources/extensions/gsd/index.ts +1 -0
  316. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  317. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  318. package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
  319. package/src/resources/extensions/gsd/notification-widget.ts +2 -1
  320. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
  321. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  322. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -7
  323. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  324. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  325. package/src/resources/extensions/gsd/preferences.ts +9 -2
  326. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  327. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  328. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  329. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  330. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  331. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  332. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  333. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  334. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  335. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  336. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  337. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  338. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  339. package/src/resources/extensions/gsd/quick.ts +20 -15
  340. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  341. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  342. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  343. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  344. package/src/resources/extensions/gsd/state.ts +115 -26
  345. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  346. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  347. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  348. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  349. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  350. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  351. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  352. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  353. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  354. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  355. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  356. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  357. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  358. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  359. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
  360. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  361. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
  362. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  363. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  364. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  365. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  366. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  367. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  368. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  369. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  370. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  371. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  372. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
  373. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  374. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  375. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
  376. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  377. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  378. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  379. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  380. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +284 -20
  381. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  382. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  383. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  384. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  385. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  386. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  387. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  388. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  389. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  390. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  391. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  392. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  393. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  394. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  395. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  396. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  397. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  398. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  399. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  400. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  401. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  402. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  403. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  404. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  405. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  406. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  407. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  408. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  409. package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
  410. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  411. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  412. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  413. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  414. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  415. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  416. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  417. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  418. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  419. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  420. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  421. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  422. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  423. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  424. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  425. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  426. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  427. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  428. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  429. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  430. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  431. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  432. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  433. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  434. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  435. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  436. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  437. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  438. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  439. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  440. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  441. package/src/resources/extensions/gsd/types.ts +4 -0
  442. package/src/resources/extensions/gsd/undo.ts +3 -2
  443. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  444. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  445. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  446. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  447. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  448. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  449. package/src/resources/extensions/gsd/worktree.ts +10 -0
  450. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  451. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  452. package/src/resources/extensions/subagent/agents.ts +30 -6
  453. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  454. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  455. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  456. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  457. /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → _X1i-S7l1jZfb7lmIZozb}/_buildManifest.js +0 -0
  458. /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → _X1i-S7l1jZfb7lmIZozb}/_ssgManifest.js +0 -0
@@ -9,6 +9,7 @@
9
9
 
10
10
  import type { ExtensionContext } from "@gsd/pi-coding-agent";
11
11
  import { parseUnitId } from "./unit-id.js";
12
+ import { appendEvent } from "./workflow-events.js";
12
13
  import { atomicWriteSync } from "./atomic-write.js";
13
14
  import { clearParseCache } from "./files.js";
14
15
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
@@ -60,13 +61,12 @@ export { resolveExpectedArtifactPath, diagnoseExpectedArtifact };
60
61
  * in the git history. Uses `git log --name-only` to inspect all commits on the
61
62
  * current branch that touch files outside `.gsd/`.
62
63
  *
63
- * Returns true if at least one non-`.gsd/` file was committed, false otherwise.
64
- * Non-fatal: returns true on git errors to avoid blocking the pipeline when
65
- * running outside a git repo (e.g., tests).
64
+ * Returns "present" if implementation files found, "absent" if only .gsd/ files,
65
+ * "unknown" if git is unavailable or check failed (callers decide how to handle).
66
66
  */
67
- export function hasImplementationArtifacts(basePath: string): boolean {
67
+ export function hasImplementationArtifacts(basePath: string): "present" | "absent" | "unknown" {
68
68
  try {
69
- // Verify we're in a git repo — fail open if not
69
+ // Verify we're in a git repo
70
70
  try {
71
71
  execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
72
72
  cwd: basePath,
@@ -75,7 +75,7 @@ export function hasImplementationArtifacts(basePath: string): boolean {
75
75
  });
76
76
  } catch (e) {
77
77
  logWarning("recovery", `git rev-parse check failed: ${(e as Error).message}`);
78
- return true;
78
+ return "unknown";
79
79
  }
80
80
 
81
81
  // Strategy: check `git diff --name-only` against the merge-base with the
@@ -85,19 +85,19 @@ export function hasImplementationArtifacts(basePath: string): boolean {
85
85
  const mainBranch = detectMainBranch(basePath);
86
86
  const changedFiles = getChangedFilesSinceBranch(basePath, mainBranch);
87
87
 
88
- // No files changed at all — fail open (could be detached HEAD, single-
88
+ // No files changed at all — unknown (could be detached HEAD, single-
89
89
  // commit repo, or other edge case where git diff returns nothing).
90
- if (changedFiles.length === 0) return true;
90
+ if (changedFiles.length === 0) return "unknown";
91
91
 
92
92
  // Filter out .gsd/ files — only implementation files count.
93
93
  // If every changed file is under .gsd/, the milestone produced no
94
94
  // implementation code (#1703).
95
95
  const implFiles = changedFiles.filter(f => !f.startsWith(".gsd/") && !f.startsWith(".gsd\\"));
96
- return implFiles.length > 0;
96
+ return implFiles.length > 0 ? "present" : "absent";
97
97
  } catch (e) {
98
- // Non-fatal — if git operations fail, don't block the pipeline
98
+ // Non-fatal — if git operations fail, return unknown so callers can decide
99
99
  logWarning("recovery", `implementation artifact check failed: ${(e as Error).message}`);
100
- return true;
100
+ return "unknown";
101
101
  }
102
102
  }
103
103
 
@@ -286,7 +286,7 @@ export function verifyExpectedArtifact(
286
286
  if (!hasCheckboxTask && !hasHeadingTask) return false;
287
287
  }
288
288
 
289
- // execute-task: DB status is authoritative. Fall back to heading-style plan
289
+ // execute-task: DB status is authoritative. Fall back to checked-checkbox
290
290
  // detection when the DB is unavailable (unmigrated projects).
291
291
  if (unitType === "execute-task") {
292
292
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
@@ -297,20 +297,22 @@ export function verifyExpectedArtifact(
297
297
  if (dbTask.status !== "complete" && dbTask.status !== "done") return false;
298
298
  } else if (!isDbAvailable()) {
299
299
  // LEGACY: Pre-migration fallback for projects without DB.
300
- // Fall back to plan heading check (format detection, not reconciliation).
301
- // Heading-style entries (### T01 --) count as verified because the
302
- // summary file existence (checked above) is the real signal.
300
+ // Require a CHECKED checkbox a bare heading or unchecked checkbox
301
+ // does not prove gsd_complete_task ran. Summary file on disk alone
302
+ // is not sufficient evidence (could be a rogue write) (#3607).
303
303
  const planAbs = resolveSliceFile(base, mid, sid, "PLAN");
304
304
  if (planAbs && existsSync(planAbs)) {
305
305
  const planContent = readFileSync(planAbs, "utf-8");
306
306
  const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
307
- const hdRe = new RegExp(`^#{2,4}\\s+${escapedTid}\\s*(?:--|—|:)`, "m");
308
307
  const cbRe = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
309
- if (!hdRe.test(planContent) && !cbRe.test(planContent)) return false;
308
+ if (!cbRe.test(planContent)) return false;
309
+ } else {
310
+ return false; // no plan file → cannot verify
310
311
  }
312
+ } else {
313
+ // DB available but task row not found — completion tool never ran (#3607)
314
+ return false;
311
315
  }
312
- // else: DB available but task not found — summary file exists (checked above),
313
- // so treat as verified (task may not be imported yet)
314
316
  }
315
317
  }
316
318
 
@@ -392,7 +394,7 @@ export function verifyExpectedArtifact(
392
394
  // A milestone with only .gsd/ plan files and zero implementation code is
393
395
  // not genuinely complete — the LLM wrote plan files but skipped actual work.
394
396
  if (unitType === "complete-milestone") {
395
- if (!hasImplementationArtifacts(base)) return false;
397
+ if (hasImplementationArtifacts(base) === "absent") return false;
396
398
  }
397
399
 
398
400
  return true;
@@ -429,11 +431,15 @@ export function writeBlockerPlaceholder(
429
431
  // re-derives the same unit indefinitely (#2531, #2653).
430
432
  if (isDbAvailable()) {
431
433
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
434
+ const ts = new Date().toISOString();
432
435
  if (unitType === "execute-task" && mid && sid && tid) {
433
- try { updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString()); } catch (e) { logWarning("recovery", `updateTaskStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
436
+ try { updateTaskStatus(mid, sid, tid, "complete", ts); } catch (e) { logWarning("recovery", `updateTaskStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
437
+ // Append event so worktree reconciliation can replay this recovery completion
438
+ try { appendEvent(base, { cmd: "complete-task", params: { milestoneId: mid, sliceId: sid, taskId: tid }, ts, actor: "system", trigger_reason: "blocker-placeholder-recovery" }); } catch (e) { logWarning("recovery", `appendEvent failed for task recovery: ${e instanceof Error ? e.message : String(e)}`); }
434
439
  }
435
440
  if (unitType === "complete-slice" && mid && sid) {
436
- try { updateSliceStatus(mid, sid, "complete", new Date().toISOString()); } catch (e) { logWarning("recovery", `updateSliceStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
441
+ try { updateSliceStatus(mid, sid, "complete", ts); } catch (e) { logWarning("recovery", `updateSliceStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
442
+ try { appendEvent(base, { cmd: "complete-slice", params: { milestoneId: mid, sliceId: sid }, ts, actor: "system", trigger_reason: "blocker-placeholder-recovery" }); } catch (e) { logWarning("recovery", `appendEvent failed for slice recovery: ${e instanceof Error ? e.message : String(e)}`); }
437
443
  }
438
444
  }
439
445
 
@@ -495,7 +501,7 @@ export function reconcileMergeState(
495
501
  if (conflictedFiles.length === 0) {
496
502
  // All conflicts resolved — finalize the merge/squash commit
497
503
  try {
498
- const commitSha = nativeCommit(basePath, ""); // --no-edit equivalent: use empty message placeholder
504
+ const commitSha = nativeCommit(basePath, "chore(gsd): reconcile merge state");
499
505
  if (commitSha) {
500
506
  const mode = hasMergeHead ? "merge" : "squash commit";
501
507
  ctx.ui.notify(`Finalized leftover ${mode} from prior session.`, "info");
@@ -44,6 +44,13 @@ import {
44
44
  nativeInit,
45
45
  nativeAddAll,
46
46
  nativeCommit,
47
+ nativeGetCurrentBranch,
48
+ nativeDetectMainBranch,
49
+ nativeCheckoutBranch,
50
+ nativeBranchList,
51
+ nativeBranchListMerged,
52
+ nativeBranchDelete,
53
+ nativeWorktreeRemove,
47
54
  } from "./native-git-bridge.js";
48
55
  import { GitServiceImpl } from "./git-service.js";
49
56
  import {
@@ -53,6 +60,7 @@ import {
53
60
  } from "./worktree.js";
54
61
  import { getAutoWorktreePath, isInAutoWorktree } from "./auto-worktree.js";
55
62
  import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
63
+ import { worktreePath as getWorktreeDir, isInsideWorktreesDir } from "./worktree-manager.js";
56
64
  import { initMetrics } from "./metrics.js";
57
65
  import { initRoutingHistory } from "./routing-history.js";
58
66
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
@@ -73,6 +81,7 @@ import {
73
81
  existsSync,
74
82
  mkdirSync,
75
83
  readdirSync,
84
+ rmSync,
76
85
  statSync,
77
86
  unlinkSync,
78
87
  } from "node:fs";
@@ -99,11 +108,8 @@ export interface BootstrapDeps {
99
108
  * concurrent session detected). Returns true when ready to dispatch.
100
109
  */
101
110
 
102
- /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
103
- * Prevents the recursive dialog loop described in #1348 where
104
- * bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
105
- * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
106
- let _consecutiveCompleteBootstraps = 0;
111
+ // Guard constant for consecutive bootstrap attempts that found phase === "complete".
112
+ // Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
107
113
  const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
108
114
 
109
115
  export async function openProjectDbIfPresent(basePath: string): Promise<void> {
@@ -117,6 +123,123 @@ export async function openProjectDbIfPresent(basePath: string): Promise<void> {
117
123
  }
118
124
  }
119
125
 
126
+ /**
127
+ * Audit for orphaned milestone branches at bootstrap.
128
+ *
129
+ * After a milestone completes, the teardown step (merge branch → main,
130
+ * delete branch, remove worktree) runs as a post-completion engine step.
131
+ * If the session ends between completion and teardown, the branch and
132
+ * worktree are orphaned — the DB says "complete" so auto-mode won't
133
+ * re-enter the milestone, and the teardown is never retried.
134
+ *
135
+ * This audit runs on every fresh bootstrap to catch that gap:
136
+ * 1. Lists all local `milestone/*` branches.
137
+ * 2. For each, checks if the milestone's DB status is "complete".
138
+ * 3. If the branch is already merged into main → deletes the branch
139
+ * and cleans up any orphaned worktree directory (safe, no data loss).
140
+ * 4. If the branch is NOT merged → preserves it and warns the user
141
+ * so they can merge manually (data safety first).
142
+ *
143
+ * Returns a summary of actions taken for the caller to surface via notify.
144
+ */
145
+ export function auditOrphanedMilestoneBranches(
146
+ basePath: string,
147
+ isolationMode: "worktree" | "branch" | "none",
148
+ ): { recovered: string[]; warnings: string[] } {
149
+ const recovered: string[] = [];
150
+ const warnings: string[] = [];
151
+
152
+ // Skip in none mode — no milestone branches are created
153
+ if (isolationMode === "none") return { recovered, warnings };
154
+
155
+ // Skip if DB not available — can't determine completion status
156
+ if (!isDbAvailable()) return { recovered, warnings };
157
+
158
+ let milestoneBranches: string[];
159
+ try {
160
+ milestoneBranches = nativeBranchList(basePath, "milestone/*");
161
+ } catch {
162
+ // git branch list failed — skip audit
163
+ return { recovered, warnings };
164
+ }
165
+
166
+ if (milestoneBranches.length === 0) return { recovered, warnings };
167
+
168
+ // Detect main branch for merge-check
169
+ let mainBranch: string;
170
+ try {
171
+ mainBranch = nativeDetectMainBranch(basePath);
172
+ } catch {
173
+ mainBranch = "main";
174
+ }
175
+
176
+ // Get branches already merged into main
177
+ let mergedBranches: Set<string>;
178
+ try {
179
+ mergedBranches = new Set(nativeBranchListMerged(basePath, mainBranch, "milestone/*"));
180
+ } catch {
181
+ mergedBranches = new Set();
182
+ }
183
+
184
+ for (const branch of milestoneBranches) {
185
+ const milestoneId = branch.replace(/^milestone\//, "");
186
+ const milestone = getMilestone(milestoneId);
187
+
188
+ // Only audit completed milestones
189
+ if (!milestone || milestone.status !== "complete") continue;
190
+
191
+ const isMerged = mergedBranches.has(branch);
192
+
193
+ if (isMerged) {
194
+ // Branch is merged — safe to delete branch and clean up worktree dir
195
+ try {
196
+ nativeBranchDelete(basePath, branch, true);
197
+ recovered.push(`Deleted merged branch ${branch} for completed milestone ${milestoneId}.`);
198
+ } catch (err) {
199
+ warnings.push(`Failed to delete merged branch ${branch}: ${err instanceof Error ? err.message : String(err)}`);
200
+ }
201
+
202
+ // Clean up orphaned worktree directory if it exists
203
+ const wtDir = getWorktreeDir(basePath, milestoneId);
204
+ if (existsSync(wtDir)) {
205
+ // Try git worktree remove first (handles registered worktrees)
206
+ try {
207
+ nativeWorktreeRemove(basePath, wtDir, true);
208
+ } catch (e) {
209
+ // Not a registered worktree — expected for orphaned dirs
210
+ logWarning("engine", `worktree remove failed (expected for orphaned dirs): ${e instanceof Error ? e.message : String(e)}`);
211
+ }
212
+
213
+ // If the directory still exists after git worktree remove (either it
214
+ // wasn't registered or the remove was a noop), fall back to direct
215
+ // filesystem removal — but only inside .gsd/worktrees/ for safety (#2365).
216
+ if (existsSync(wtDir)) {
217
+ if (isInsideWorktreesDir(basePath, wtDir)) {
218
+ try {
219
+ rmSync(wtDir, { recursive: true, force: true });
220
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
221
+ } catch (err2) {
222
+ warnings.push(`Failed to remove worktree directory for ${milestoneId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
223
+ }
224
+ } else {
225
+ warnings.push(`Orphaned worktree directory for ${milestoneId} is outside .gsd/worktrees/ — skipping removal for safety.`);
226
+ }
227
+ } else {
228
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
229
+ }
230
+ }
231
+ } else {
232
+ // Branch is NOT merged — preserve for safety, warn the user
233
+ warnings.push(
234
+ `Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
235
+ `This may contain unmerged work. Merge manually or run \`/gsd health --fix\` to resolve.`,
236
+ );
237
+ }
238
+ }
239
+
240
+ return { recovered, warnings };
241
+ }
242
+
120
243
  export async function bootstrapAutoSession(
121
244
  s: AutoSession,
122
245
  ctx: ExtensionCommandContext,
@@ -300,6 +423,26 @@ export async function bootstrapAutoSession(
300
423
  // derivation (queue-order, task status) works on a cold start (#2841).
301
424
  await openProjectDbIfPresent(base);
302
425
 
426
+ // ── Orphaned milestone branch audit ──
427
+ // Catches completed milestones whose teardown (merge + branch delete)
428
+ // was lost due to session ending between completion and teardown.
429
+ // Must run after DB open and before worktree entry.
430
+ try {
431
+ const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode());
432
+ for (const msg of auditResult.recovered) {
433
+ ctx.ui.notify(`Orphan audit: ${msg}`, "info");
434
+ }
435
+ for (const msg of auditResult.warnings) {
436
+ ctx.ui.notify(`Orphan audit: ${msg}`, "warning");
437
+ }
438
+ if (auditResult.recovered.length > 0) {
439
+ debugLog("orphan-audit", { recovered: auditResult.recovered, warnings: auditResult.warnings });
440
+ }
441
+ } catch (err) {
442
+ // Non-fatal — the audit is defensive, never block bootstrap
443
+ logWarning("bootstrap", `orphaned milestone branch audit failed: ${err instanceof Error ? err.message : String(err)}`);
444
+ }
445
+
303
446
  let state = await deriveState(base);
304
447
 
305
448
  // Stale worktree state recovery (#654)
@@ -389,9 +532,9 @@ export async function bootstrapAutoSession(
389
532
  // Guard against recursive dialog loop (#1348):
390
533
  // If we've entered this branch multiple times in quick succession,
391
534
  // the discuss workflow isn't producing a milestone. Break the cycle.
392
- _consecutiveCompleteBootstraps++;
393
- if (_consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
394
- _consecutiveCompleteBootstraps = 0;
535
+ s.consecutiveCompleteBootstraps++;
536
+ if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
537
+ s.consecutiveCompleteBootstraps = 0;
395
538
  ctx.ui.notify(
396
539
  "All milestones are complete and the discussion didn't produce a new one. " +
397
540
  "Run /gsd to start a new milestone manually.",
@@ -410,7 +553,7 @@ export async function bootstrapAutoSession(
410
553
  postState.phase !== "complete" &&
411
554
  postState.phase !== "pre-planning"
412
555
  ) {
413
- _consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
556
+ s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
414
557
  state = postState;
415
558
  } else if (
416
559
  postState.activeMilestone &&
@@ -489,7 +632,7 @@ export async function bootstrapAutoSession(
489
632
  }
490
633
 
491
634
  // Successfully resolved an active milestone — reset the re-entry guard
492
- _consecutiveCompleteBootstraps = 0;
635
+ s.consecutiveCompleteBootstraps = 0;
493
636
 
494
637
  // ── Initialize session state ──
495
638
  s.active = true;
@@ -528,6 +671,22 @@ export async function bootstrapAutoSession(
528
671
  setActiveMilestoneId(base, s.currentMilestoneId);
529
672
  }
530
673
 
674
+ // Guard against stale milestone branch when isolation:none (#3613).
675
+ // A prior session with isolation:branch/worktree may have left HEAD on
676
+ // milestone/<MID>. Auto-checkout back to the integration branch.
677
+ if (getIsolationMode() === "none" && nativeIsRepo(base)) {
678
+ try {
679
+ const currentBranch = nativeGetCurrentBranch(base);
680
+ if (currentBranch.startsWith("milestone/")) {
681
+ const integrationBranch = nativeDetectMainBranch(base);
682
+ nativeCheckoutBranch(base, integrationBranch);
683
+ logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
684
+ }
685
+ } catch (err) {
686
+ logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
687
+ }
688
+ }
689
+
531
690
  // ── Auto-worktree setup ──
532
691
  s.originalBasePath = base;
533
692
 
@@ -614,6 +773,25 @@ export async function bootstrapAutoSession(
614
773
  };
615
774
  }
616
775
 
776
+ // Apply worker model override from parallel orchestrator (#worker-model).
777
+ // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
778
+ // is configured, so parallel milestone workers use a cheaper model than the
779
+ // coordinator session (e.g. Haiku for execution, Sonnet for planning).
780
+ const workerModelOverride = process.env.GSD_WORKER_MODEL;
781
+ if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
782
+ const availableModels = ctx.modelRegistry.getAvailable();
783
+ const { resolveModelId } = await import("./auto-model-selection.js");
784
+ const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
785
+ if (overrideModel) {
786
+ const ok = await pi.setModel(overrideModel, { persist: false });
787
+ if (ok) {
788
+ // Update start model so all subsequent units use this as the baseline
789
+ s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
790
+ ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
791
+ }
792
+ }
793
+ }
794
+
617
795
  // Snapshot installed skills
618
796
  if (resolveSkillDiscoveryMode() !== "off") {
619
797
  snapshotSkills();
@@ -102,3 +102,13 @@ export function isToolInvocationError(errorMsg: string): boolean {
102
102
  if (!errorMsg) return false;
103
103
  return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
104
104
  }
105
+
106
+ /**
107
+ * Returns true if the error message indicates the tool was skipped because
108
+ * a queued user message interrupted the turn (#3595). Retrying will produce
109
+ * the same skip, so the unit should be paused rather than retried.
110
+ */
111
+ export function isQueuedUserMessageSkip(errorMsg: string): boolean {
112
+ if (!errorMsg) return false;
113
+ return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
114
+ }
@@ -188,8 +188,10 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
188
188
  try {
189
189
  unlinkSync(file);
190
190
  } catch (err) {
191
- /* non-fatal — file may not exist */
192
- logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
191
+ // ENOENT is expected — file may not exist (#3597)
192
+ if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
193
+ logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
194
+ }
193
195
  }
194
196
  }
195
197
 
@@ -218,8 +220,11 @@ function clearProjectRootStateFiles(basePath: string, milestoneId: string): void
218
220
  try {
219
221
  unlinkSync(join(basePath, f));
220
222
  } catch (err) {
221
- /* non-fatal */
222
- logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
223
+ // ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
224
+ const code = (err as NodeJS.ErrnoException).code;
225
+ if (code !== "ENOENT" && code !== "EISDIR") {
226
+ logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
227
+ }
223
228
  }
224
229
  }
225
230
  }
@@ -770,6 +775,9 @@ export function syncWorktreeStateBack(
770
775
  .map((d) => d.name);
771
776
 
772
777
  for (const mid of wtMilestones) {
778
+ // Skip the current milestone being merged — its files are already in the
779
+ // milestone branch and would conflict with the squash merge (#3641).
780
+ if (mid === milestoneId) continue;
773
781
  syncMilestoneDir(wtGsd, mainGsd, mid, synced);
774
782
  }
775
783
  } catch (err) {
@@ -1049,12 +1057,20 @@ export function createAutoWorktree(
1049
1057
  reuseExistingBranch: true,
1050
1058
  });
1051
1059
  } else {
1052
- // Fresh start — create branch from integration branch
1060
+ // Fresh start — create branch from integration branch.
1061
+ // Use the same 3-tier fallback as mergeMilestoneToMain (#3461):
1062
+ // 1. META.json integration branch (explicit per-milestone override)
1063
+ // 2. git.main_branch preference (user's configured working branch)
1064
+ // 3. nativeDetectMainBranch (origin/HEAD auto-detection)
1065
+ // Without tier 2, projects with main_branch=dev but origin/HEAD→master
1066
+ // would fork worktrees from the wrong (stale) branch.
1053
1067
  const integrationBranch =
1054
1068
  readIntegrationBranch(basePath, milestoneId) ?? undefined;
1069
+ const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
1070
+ const startPoint = integrationBranch ?? gitPrefs?.main_branch ?? undefined;
1055
1071
  info = createWorktree(basePath, milestoneId, {
1056
1072
  branch,
1057
- startPoint: integrationBranch,
1073
+ startPoint,
1058
1074
  });
1059
1075
  }
1060
1076
 
@@ -1453,8 +1469,13 @@ export function mergeMilestoneToMain(
1453
1469
  originalBasePath_,
1454
1470
  milestoneId,
1455
1471
  );
1472
+ // Validate prefs.main_branch exists before using it — a stale preference
1473
+ // (e.g. "master" when repo uses "main") causes merge failure (#3589).
1474
+ const validatedPrefBranch = prefs.main_branch && nativeBranchExists(originalBasePath_, prefs.main_branch)
1475
+ ? prefs.main_branch
1476
+ : undefined;
1456
1477
  const mainBranch =
1457
- integrationBranch ?? prefs.main_branch ?? nativeDetectMainBranch(originalBasePath_);
1478
+ integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
1458
1479
 
1459
1480
  // Remove transient project-root state files before any branch or merge
1460
1481
  // operation. Untracked milestone metadata can otherwise block squash merges.
@@ -76,6 +76,7 @@ import {
76
76
  hasInteractiveToolInFlight,
77
77
  clearInFlightTools,
78
78
  isToolInvocationError,
79
+ isQueuedUserMessageSkip,
79
80
  } from "./auto-tool-tracking.js";
80
81
  import { closeoutUnit } from "./auto-unit-closeout.js";
81
82
  import { recoverTimedOutUnit } from "./auto-timeout-recovery.js";
@@ -397,7 +398,7 @@ export function markToolEnd(toolCallId: string): void {
397
398
  */
398
399
  export function recordToolInvocationError(toolName: string, errorMsg: string): void {
399
400
  if (!s.active) return;
400
- if (isToolInvocationError(errorMsg)) {
401
+ if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
401
402
  s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
402
403
  }
403
404
  }
@@ -1140,9 +1141,9 @@ export async function startAuto(
1140
1141
  s.stepMode = meta.stepMode ?? requestedStepMode;
1141
1142
  s.autoStartTime = meta.autoStartTime || Date.now();
1142
1143
  s.paused = true;
1143
- try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1144
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1145
- }
1144
+ // Don't delete pause file yet defer until lock is acquired.
1145
+ // If lock fails, the file must survive for retry.
1146
+ s.pausedSessionFile = pausedPath;
1146
1147
  ctx.ui.notify(
1147
1148
  `Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`,
1148
1149
  "info",
@@ -1166,10 +1167,9 @@ export async function startAuto(
1166
1167
  s.stepMode = meta.stepMode ?? requestedStepMode;
1167
1168
  s.autoStartTime = meta.autoStartTime || Date.now();
1168
1169
  s.paused = true;
1169
- // Clean up the persisted file — we're consuming it
1170
- try { unlinkSync(pausedPath); } catch (err) { /* non-fatal */
1171
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1172
- }
1170
+ // Don't delete pause file yet defer until lock is acquired.
1171
+ // If lock fails, the file must survive for retry.
1172
+ s.pausedSessionFile = pausedPath;
1173
1173
  ctx.ui.notify(
1174
1174
  `Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`,
1175
1175
  "info",
@@ -1186,10 +1186,21 @@ export async function startAuto(
1186
1186
  if (s.paused) {
1187
1187
  const resumeLock = acquireSessionLock(base);
1188
1188
  if (!resumeLock.acquired) {
1189
+ // Reset paused state so isAutoPaused() doesn't stick true after lock failure.
1190
+ // Pause file is preserved on disk for retry — not deleted.
1191
+ s.paused = false;
1189
1192
  ctx.ui.notify(`Cannot resume: ${resumeLock.reason}`, "error");
1190
1193
  return;
1191
1194
  }
1192
1195
 
1196
+ // Lock acquired — now safe to delete the pause file
1197
+ if (s.pausedSessionFile) {
1198
+ try { unlinkSync(s.pausedSessionFile); } catch (err) {
1199
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
1200
+ }
1201
+ s.pausedSessionFile = null;
1202
+ }
1203
+
1193
1204
  s.paused = false;
1194
1205
  s.active = true;
1195
1206
  s.verbose = verboseMode;
@@ -95,12 +95,24 @@ export async function handleAgentEnd(
95
95
  return;
96
96
  }
97
97
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
98
- const errorDetail = "errorMessage" in lastMsg && lastMsg.errorMessage ? `: ${lastMsg.errorMessage}` : "";
99
- const errorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
98
+ // #3588: errorMessage can be useless (e.g. "success") while the real error
99
+ // is in the assistant message text content. Fall back to content when
100
+ // errorMessage looks uninformative.
101
+ const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
102
+ const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
103
+ // #3588: When errorMessage is uninformative, extract the real error from
104
+ // the assistant message text content for display purposes only.
105
+ // Classification still uses rawErrorMsg to avoid false positives from prose.
106
+ let displayMsg = rawErrorMsg;
107
+ if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
108
+ const textBlock = lastMsg.content.find((b: any) => b.type === "text" && b.text);
109
+ if (textBlock) displayMsg = (textBlock as any).text.slice(0, 300);
110
+ }
111
+ const errorDetail = displayMsg ? `: ${displayMsg}` : "";
100
112
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
101
113
 
102
- // ── 1. Classify ──────────────────────────────────────────────────────
103
- const cls = classifyError(errorMsg, explicitRetryAfterMs);
114
+ // ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
115
+ const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
104
116
 
105
117
  // Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
106
118
  // which use per-user quotas with shorter windows (#2922).
@@ -1000,6 +1000,16 @@ export function registerDbTools(pi: ExtensionAPI): void {
1000
1000
  updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
1001
1001
  invalidateStateCache();
1002
1002
 
1003
+ // Rebuild STATE.md so it reflects the skip immediately (#3477).
1004
+ // Without this, /gsd auto reads stale STATE.md and resumes the skipped slice.
1005
+ try {
1006
+ const basePath = process.cwd();
1007
+ const { rebuildState } = await import("../doctor.js");
1008
+ await rebuildState(basePath);
1009
+ } catch (err) {
1010
+ logError("tool", `skip_slice rebuildState failed: ${(err as Error).message}`, { tool: "gsd_skip_slice" });
1011
+ }
1012
+
1003
1013
  return {
1004
1014
  content: [{ type: "text" as const, text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}. Auto-mode will advance past this slice.` }],
1005
1015
  details: {
@@ -22,17 +22,18 @@ export function registerQueryTools(pi: ExtensionAPI): void {
22
22
  }),
23
23
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
24
24
  try {
25
- // Strictly read-only: only use an already-open DB connection.
26
- // Do NOT call ensureDbOpen() it can create/migrate the DB as a side effect.
25
+ // Open the DB if not already open safe for read-only use since
26
+ // ensureDbOpen() only creates/migrates when .gsd/ has content (#3644).
27
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
28
+ const dbAvailable = await ensureDbOpen();
27
29
  const {
28
- isDbAvailable,
29
30
  getMilestone,
30
31
  getSliceStatusSummary,
31
32
  getSliceTaskCounts,
32
33
  _getAdapter,
33
34
  } = await import("../gsd-db.js");
34
35
 
35
- if (!isDbAvailable()) {
36
+ if (!dbAvailable) {
36
37
  return {
37
38
  content: [{ type: "text" as const, text: "Error: GSD database is not available." }],
38
39
  details: { operation: "milestone_status", error: "db_unavailable" } as any,
@@ -36,7 +36,10 @@ function installEpipeGuard(): void {
36
36
  if (handleRecoverableExtensionProcessError(err)) {
37
37
  return;
38
38
  }
39
- throw err;
39
+ // Log unhandled errors instead of re-throwing — throwing inside an
40
+ // uncaughtException handler is a fatal double-fault in Node.js (#3163).
41
+ process.stderr.write(`[gsd] uncaught extension error (non-fatal): ${err.message}\n`);
42
+ if (err.stack) process.stderr.write(`${err.stack}\n`);
40
43
  };
41
44
  process.on("uncaughtException", _gsdEpipeGuard);
42
45
  }