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
@@ -7,6 +7,7 @@
7
7
  * globals or AutoContext dependency.
8
8
  */
9
9
  import { parseUnitId } from "./unit-id.js";
10
+ import { appendEvent } from "./workflow-events.js";
10
11
  import { clearParseCache } from "./files.js";
11
12
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
12
13
  import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus, updateSliceStatus } from "./gsd-db.js";
@@ -27,13 +28,12 @@ export { resolveExpectedArtifactPath, diagnoseExpectedArtifact };
27
28
  * in the git history. Uses `git log --name-only` to inspect all commits on the
28
29
  * current branch that touch files outside `.gsd/`.
29
30
  *
30
- * Returns true if at least one non-`.gsd/` file was committed, false otherwise.
31
- * Non-fatal: returns true on git errors to avoid blocking the pipeline when
32
- * running outside a git repo (e.g., tests).
31
+ * Returns "present" if implementation files found, "absent" if only .gsd/ files,
32
+ * "unknown" if git is unavailable or check failed (callers decide how to handle).
33
33
  */
34
34
  export function hasImplementationArtifacts(basePath) {
35
35
  try {
36
- // Verify we're in a git repo — fail open if not
36
+ // Verify we're in a git repo
37
37
  try {
38
38
  execFileSync("git", ["rev-parse", "--is-inside-work-tree"], {
39
39
  cwd: basePath,
@@ -43,7 +43,7 @@ export function hasImplementationArtifacts(basePath) {
43
43
  }
44
44
  catch (e) {
45
45
  logWarning("recovery", `git rev-parse check failed: ${e.message}`);
46
- return true;
46
+ return "unknown";
47
47
  }
48
48
  // Strategy: check `git diff --name-only` against the merge-base with the
49
49
  // main branch. This captures ALL files changed during the milestone's
@@ -51,20 +51,20 @@ export function hasImplementationArtifacts(basePath) {
51
51
  // back to checking the last N commits.
52
52
  const mainBranch = detectMainBranch(basePath);
53
53
  const changedFiles = getChangedFilesSinceBranch(basePath, mainBranch);
54
- // No files changed at all — fail open (could be detached HEAD, single-
54
+ // No files changed at all — unknown (could be detached HEAD, single-
55
55
  // commit repo, or other edge case where git diff returns nothing).
56
56
  if (changedFiles.length === 0)
57
- return true;
57
+ return "unknown";
58
58
  // Filter out .gsd/ files — only implementation files count.
59
59
  // If every changed file is under .gsd/, the milestone produced no
60
60
  // implementation code (#1703).
61
61
  const implFiles = changedFiles.filter(f => !f.startsWith(".gsd/") && !f.startsWith(".gsd\\"));
62
- return implFiles.length > 0;
62
+ return implFiles.length > 0 ? "present" : "absent";
63
63
  }
64
64
  catch (e) {
65
- // Non-fatal — if git operations fail, don't block the pipeline
65
+ // Non-fatal — if git operations fail, return unknown so callers can decide
66
66
  logWarning("recovery", `implementation artifact check failed: ${e.message}`);
67
- return true;
67
+ return "unknown";
68
68
  }
69
69
  }
70
70
  /**
@@ -238,7 +238,7 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
238
238
  if (!hasCheckboxTask && !hasHeadingTask)
239
239
  return false;
240
240
  }
241
- // execute-task: DB status is authoritative. Fall back to heading-style plan
241
+ // execute-task: DB status is authoritative. Fall back to checked-checkbox
242
242
  // detection when the DB is unavailable (unmigrated projects).
243
243
  if (unitType === "execute-task") {
244
244
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
@@ -251,21 +251,25 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
251
251
  }
252
252
  else if (!isDbAvailable()) {
253
253
  // LEGACY: Pre-migration fallback for projects without DB.
254
- // Fall back to plan heading check (format detection, not reconciliation).
255
- // Heading-style entries (### T01 --) count as verified because the
256
- // summary file existence (checked above) is the real signal.
254
+ // Require a CHECKED checkbox a bare heading or unchecked checkbox
255
+ // does not prove gsd_complete_task ran. Summary file on disk alone
256
+ // is not sufficient evidence (could be a rogue write) (#3607).
257
257
  const planAbs = resolveSliceFile(base, mid, sid, "PLAN");
258
258
  if (planAbs && existsSync(planAbs)) {
259
259
  const planContent = readFileSync(planAbs, "utf-8");
260
260
  const escapedTid = tid.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
261
- const hdRe = new RegExp(`^#{2,4}\\s+${escapedTid}\\s*(?:--|—|:)`, "m");
262
261
  const cbRe = new RegExp(`^- \\[[xX]\\] \\*\\*${escapedTid}:`, "m");
263
- if (!hdRe.test(planContent) && !cbRe.test(planContent))
262
+ if (!cbRe.test(planContent))
264
263
  return false;
265
264
  }
265
+ else {
266
+ return false; // no plan file → cannot verify
267
+ }
268
+ }
269
+ else {
270
+ // DB available but task row not found — completion tool never ran (#3607)
271
+ return false;
266
272
  }
267
- // else: DB available but task not found — summary file exists (checked above),
268
- // so treat as verified (task may not be imported yet)
269
273
  }
270
274
  }
271
275
  // plan-slice must also produce individual task plan files for every task listed
@@ -350,7 +354,7 @@ export function verifyExpectedArtifact(unitType, unitId, base) {
350
354
  // A milestone with only .gsd/ plan files and zero implementation code is
351
355
  // not genuinely complete — the LLM wrote plan files but skipped actual work.
352
356
  if (unitType === "complete-milestone") {
353
- if (!hasImplementationArtifacts(base))
357
+ if (hasImplementationArtifacts(base) === "absent")
354
358
  return false;
355
359
  }
356
360
  return true;
@@ -382,21 +386,35 @@ export function writeBlockerPlaceholder(unitType, unitId, base, reason) {
382
386
  // re-derives the same unit indefinitely (#2531, #2653).
383
387
  if (isDbAvailable()) {
384
388
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
389
+ const ts = new Date().toISOString();
385
390
  if (unitType === "execute-task" && mid && sid && tid) {
386
391
  try {
387
- updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString());
392
+ updateTaskStatus(mid, sid, tid, "complete", ts);
388
393
  }
389
394
  catch (e) {
390
395
  logWarning("recovery", `updateTaskStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`);
391
396
  }
397
+ // Append event so worktree reconciliation can replay this recovery completion
398
+ try {
399
+ appendEvent(base, { cmd: "complete-task", params: { milestoneId: mid, sliceId: sid, taskId: tid }, ts, actor: "system", trigger_reason: "blocker-placeholder-recovery" });
400
+ }
401
+ catch (e) {
402
+ logWarning("recovery", `appendEvent failed for task recovery: ${e instanceof Error ? e.message : String(e)}`);
403
+ }
392
404
  }
393
405
  if (unitType === "complete-slice" && mid && sid) {
394
406
  try {
395
- updateSliceStatus(mid, sid, "complete", new Date().toISOString());
407
+ updateSliceStatus(mid, sid, "complete", ts);
396
408
  }
397
409
  catch (e) {
398
410
  logWarning("recovery", `updateSliceStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`);
399
411
  }
412
+ try {
413
+ appendEvent(base, { cmd: "complete-slice", params: { milestoneId: mid, sliceId: sid }, ts, actor: "system", trigger_reason: "blocker-placeholder-recovery" });
414
+ }
415
+ catch (e) {
416
+ logWarning("recovery", `appendEvent failed for slice recovery: ${e instanceof Error ? e.message : String(e)}`);
417
+ }
400
418
  }
401
419
  }
402
420
  return diagnoseExpectedArtifact(unitType, unitId, base);
@@ -451,7 +469,7 @@ export function reconcileMergeState(basePath, ctx) {
451
469
  if (conflictedFiles.length === 0) {
452
470
  // All conflicts resolved — finalize the merge/squash commit
453
471
  try {
454
- const commitSha = nativeCommit(basePath, ""); // --no-edit equivalent: use empty message placeholder
472
+ const commitSha = nativeCommit(basePath, "chore(gsd): reconcile merge state");
455
473
  if (commitSha) {
456
474
  const mode = hasMergeHead ? "merge" : "squash commit";
457
475
  ctx.ui.notify(`Finalized leftover ${mode} from prior session.`, "info");
@@ -20,11 +20,12 @@ import { synthesizeCrashRecovery } from "./session-forensics.js";
20
20
  import { writeLock, clearLock, readCrashLock, formatCrashInfo, isLockProcessAlive, } from "./crash-recovery.js";
21
21
  import { acquireSessionLock, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
22
22
  import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
23
- import { nativeInit, nativeAddAll, nativeCommit, } from "./native-git-bridge.js";
23
+ import { nativeIsRepo, nativeInit, nativeAddAll, nativeCommit, nativeGetCurrentBranch, nativeDetectMainBranch, nativeCheckoutBranch, nativeBranchList, nativeBranchListMerged, nativeBranchDelete, nativeWorktreeRemove, } from "./native-git-bridge.js";
24
24
  import { GitServiceImpl } from "./git-service.js";
25
25
  import { captureIntegrationBranch, detectWorktreeName, setActiveMilestoneId, } from "./worktree.js";
26
26
  import { getAutoWorktreePath } from "./auto-worktree.js";
27
27
  import { readResourceVersion, cleanStaleRuntimeUnits } from "./auto-worktree.js";
28
+ import { worktreePath as getWorktreeDir, isInsideWorktreesDir } from "./worktree-manager.js";
28
29
  import { initMetrics } from "./metrics.js";
29
30
  import { initRoutingHistory } from "./routing-history.js";
30
31
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
@@ -35,7 +36,7 @@ import { hideFooter } from "./auto-dashboard.js";
35
36
  import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath, } from "./debug-logger.js";
36
37
  import { logWarning, logError } from "./workflow-logger.js";
37
38
  import { parseUnitId } from "./unit-id.js";
38
- import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync, } from "node:fs";
39
+ import { existsSync, mkdirSync, readdirSync, rmSync, statSync, unlinkSync, } from "node:fs";
39
40
  import { join } from "node:path";
40
41
  import { sep as pathSep } from "node:path";
41
42
  import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
@@ -48,11 +49,8 @@ import { resolveDefaultSessionModel } from "./preferences-models.js";
48
49
  * Returns false if the bootstrap aborted (e.g., guided flow returned,
49
50
  * concurrent session detected). Returns true when ready to dispatch.
50
51
  */
51
- /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
52
- * Prevents the recursive dialog loop described in #1348 where
53
- * bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
54
- * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
55
- let _consecutiveCompleteBootstraps = 0;
52
+ // Guard constant for consecutive bootstrap attempts that found phase === "complete".
53
+ // Counter moved to AutoSession.consecutiveCompleteBootstraps so s.reset() clears it.
56
54
  const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
57
55
  export async function openProjectDbIfPresent(basePath) {
58
56
  const gsdDbPath = resolveProjectRootDbPath(basePath);
@@ -65,6 +63,117 @@ export async function openProjectDbIfPresent(basePath) {
65
63
  logWarning("engine", `gsd-db: failed to open existing database: ${err instanceof Error ? err.message : String(err)}`);
66
64
  }
67
65
  }
66
+ /**
67
+ * Audit for orphaned milestone branches at bootstrap.
68
+ *
69
+ * After a milestone completes, the teardown step (merge branch → main,
70
+ * delete branch, remove worktree) runs as a post-completion engine step.
71
+ * If the session ends between completion and teardown, the branch and
72
+ * worktree are orphaned — the DB says "complete" so auto-mode won't
73
+ * re-enter the milestone, and the teardown is never retried.
74
+ *
75
+ * This audit runs on every fresh bootstrap to catch that gap:
76
+ * 1. Lists all local `milestone/*` branches.
77
+ * 2. For each, checks if the milestone's DB status is "complete".
78
+ * 3. If the branch is already merged into main → deletes the branch
79
+ * and cleans up any orphaned worktree directory (safe, no data loss).
80
+ * 4. If the branch is NOT merged → preserves it and warns the user
81
+ * so they can merge manually (data safety first).
82
+ *
83
+ * Returns a summary of actions taken for the caller to surface via notify.
84
+ */
85
+ export function auditOrphanedMilestoneBranches(basePath, isolationMode) {
86
+ const recovered = [];
87
+ const warnings = [];
88
+ // Skip in none mode — no milestone branches are created
89
+ if (isolationMode === "none")
90
+ return { recovered, warnings };
91
+ // Skip if DB not available — can't determine completion status
92
+ if (!isDbAvailable())
93
+ return { recovered, warnings };
94
+ let milestoneBranches;
95
+ try {
96
+ milestoneBranches = nativeBranchList(basePath, "milestone/*");
97
+ }
98
+ catch {
99
+ // git branch list failed — skip audit
100
+ return { recovered, warnings };
101
+ }
102
+ if (milestoneBranches.length === 0)
103
+ return { recovered, warnings };
104
+ // Detect main branch for merge-check
105
+ let mainBranch;
106
+ try {
107
+ mainBranch = nativeDetectMainBranch(basePath);
108
+ }
109
+ catch {
110
+ mainBranch = "main";
111
+ }
112
+ // Get branches already merged into main
113
+ let mergedBranches;
114
+ try {
115
+ mergedBranches = new Set(nativeBranchListMerged(basePath, mainBranch, "milestone/*"));
116
+ }
117
+ catch {
118
+ mergedBranches = new Set();
119
+ }
120
+ for (const branch of milestoneBranches) {
121
+ const milestoneId = branch.replace(/^milestone\//, "");
122
+ const milestone = getMilestone(milestoneId);
123
+ // Only audit completed milestones
124
+ if (!milestone || milestone.status !== "complete")
125
+ continue;
126
+ const isMerged = mergedBranches.has(branch);
127
+ if (isMerged) {
128
+ // Branch is merged — safe to delete branch and clean up worktree dir
129
+ try {
130
+ nativeBranchDelete(basePath, branch, true);
131
+ recovered.push(`Deleted merged branch ${branch} for completed milestone ${milestoneId}.`);
132
+ }
133
+ catch (err) {
134
+ warnings.push(`Failed to delete merged branch ${branch}: ${err instanceof Error ? err.message : String(err)}`);
135
+ }
136
+ // Clean up orphaned worktree directory if it exists
137
+ const wtDir = getWorktreeDir(basePath, milestoneId);
138
+ if (existsSync(wtDir)) {
139
+ // Try git worktree remove first (handles registered worktrees)
140
+ try {
141
+ nativeWorktreeRemove(basePath, wtDir, true);
142
+ }
143
+ catch (e) {
144
+ // Not a registered worktree — expected for orphaned dirs
145
+ logWarning("engine", `worktree remove failed (expected for orphaned dirs): ${e instanceof Error ? e.message : String(e)}`);
146
+ }
147
+ // If the directory still exists after git worktree remove (either it
148
+ // wasn't registered or the remove was a noop), fall back to direct
149
+ // filesystem removal — but only inside .gsd/worktrees/ for safety (#2365).
150
+ if (existsSync(wtDir)) {
151
+ if (isInsideWorktreesDir(basePath, wtDir)) {
152
+ try {
153
+ rmSync(wtDir, { recursive: true, force: true });
154
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
155
+ }
156
+ catch (err2) {
157
+ warnings.push(`Failed to remove worktree directory for ${milestoneId}: ${err2 instanceof Error ? err2.message : String(err2)}`);
158
+ }
159
+ }
160
+ else {
161
+ warnings.push(`Orphaned worktree directory for ${milestoneId} is outside .gsd/worktrees/ — skipping removal for safety.`);
162
+ }
163
+ }
164
+ else {
165
+ recovered.push(`Removed orphaned worktree directory for ${milestoneId}.`);
166
+ }
167
+ }
168
+ }
169
+ else {
170
+ // Branch is NOT merged — preserve for safety, warn the user
171
+ warnings.push(`Branch ${branch} exists for completed milestone ${milestoneId} but is NOT merged into ${mainBranch}. ` +
172
+ `This may contain unmerged work. Merge manually or run \`/gsd health --fix\` to resolve.`);
173
+ }
174
+ }
175
+ return { recovered, warnings };
176
+ }
68
177
  export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, deps) {
69
178
  const { shouldUseWorktreeIsolation, registerSigtermHandler, lockBase, buildResolver, } = deps;
70
179
  const lockResult = acquireSessionLock(base);
@@ -194,6 +303,26 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
194
303
  // Open the project-root DB before deriveState so DB-backed state
195
304
  // derivation (queue-order, task status) works on a cold start (#2841).
196
305
  await openProjectDbIfPresent(base);
306
+ // ── Orphaned milestone branch audit ──
307
+ // Catches completed milestones whose teardown (merge + branch delete)
308
+ // was lost due to session ending between completion and teardown.
309
+ // Must run after DB open and before worktree entry.
310
+ try {
311
+ const auditResult = auditOrphanedMilestoneBranches(base, getIsolationMode());
312
+ for (const msg of auditResult.recovered) {
313
+ ctx.ui.notify(`Orphan audit: ${msg}`, "info");
314
+ }
315
+ for (const msg of auditResult.warnings) {
316
+ ctx.ui.notify(`Orphan audit: ${msg}`, "warning");
317
+ }
318
+ if (auditResult.recovered.length > 0) {
319
+ debugLog("orphan-audit", { recovered: auditResult.recovered, warnings: auditResult.warnings });
320
+ }
321
+ }
322
+ catch (err) {
323
+ // Non-fatal — the audit is defensive, never block bootstrap
324
+ logWarning("bootstrap", `orphaned milestone branch audit failed: ${err instanceof Error ? err.message : String(err)}`);
325
+ }
197
326
  let state = await deriveState(base);
198
327
  // Stale worktree state recovery (#654)
199
328
  if (state.activeMilestone &&
@@ -263,9 +392,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
263
392
  // Guard against recursive dialog loop (#1348):
264
393
  // If we've entered this branch multiple times in quick succession,
265
394
  // the discuss workflow isn't producing a milestone. Break the cycle.
266
- _consecutiveCompleteBootstraps++;
267
- if (_consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
268
- _consecutiveCompleteBootstraps = 0;
395
+ s.consecutiveCompleteBootstraps++;
396
+ if (s.consecutiveCompleteBootstraps > MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS) {
397
+ s.consecutiveCompleteBootstraps = 0;
269
398
  ctx.ui.notify("All milestones are complete and the discussion didn't produce a new one. " +
270
399
  "Run /gsd to start a new milestone manually.", "warning");
271
400
  return releaseLockAndReturn();
@@ -277,7 +406,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
277
406
  if (postState.activeMilestone &&
278
407
  postState.phase !== "complete" &&
279
408
  postState.phase !== "pre-planning") {
280
- _consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
409
+ s.consecutiveCompleteBootstraps = 0; // Successfully advanced past "complete"
281
410
  state = postState;
282
411
  }
283
412
  else if (postState.activeMilestone &&
@@ -338,7 +467,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
338
467
  return releaseLockAndReturn();
339
468
  }
340
469
  // Successfully resolved an active milestone — reset the re-entry guard
341
- _consecutiveCompleteBootstraps = 0;
470
+ s.consecutiveCompleteBootstraps = 0;
342
471
  // ── Initialize session state ──
343
472
  s.active = true;
344
473
  s.stepMode = requestedStepMode;
@@ -373,6 +502,22 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
373
502
  }
374
503
  setActiveMilestoneId(base, s.currentMilestoneId);
375
504
  }
505
+ // Guard against stale milestone branch when isolation:none (#3613).
506
+ // A prior session with isolation:branch/worktree may have left HEAD on
507
+ // milestone/<MID>. Auto-checkout back to the integration branch.
508
+ if (getIsolationMode() === "none" && nativeIsRepo(base)) {
509
+ try {
510
+ const currentBranch = nativeGetCurrentBranch(base);
511
+ if (currentBranch.startsWith("milestone/")) {
512
+ const integrationBranch = nativeDetectMainBranch(base);
513
+ nativeCheckoutBranch(base, integrationBranch);
514
+ logWarning("bootstrap", `Returned to "${integrationBranch}" — HEAD was on stale milestone branch "${currentBranch}" (isolation: none does not use milestone branches).`);
515
+ }
516
+ }
517
+ catch (err) {
518
+ logWarning("bootstrap", `Could not auto-checkout from stale milestone branch: ${err instanceof Error ? err.message : String(err)}`);
519
+ }
520
+ }
376
521
  // ── Auto-worktree setup ──
377
522
  s.originalBasePath = base;
378
523
  const isUnderGsdWorktrees = (p) => {
@@ -448,6 +593,24 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
448
593
  id: startModelSnapshot.id,
449
594
  };
450
595
  }
596
+ // Apply worker model override from parallel orchestrator (#worker-model).
597
+ // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
598
+ // is configured, so parallel milestone workers use a cheaper model than the
599
+ // coordinator session (e.g. Haiku for execution, Sonnet for planning).
600
+ const workerModelOverride = process.env.GSD_WORKER_MODEL;
601
+ if (workerModelOverride && process.env.GSD_PARALLEL_WORKER === "1") {
602
+ const availableModels = ctx.modelRegistry.getAvailable();
603
+ const { resolveModelId } = await import("./auto-model-selection.js");
604
+ const overrideModel = resolveModelId(workerModelOverride, availableModels, ctx.model?.provider);
605
+ if (overrideModel) {
606
+ const ok = await pi.setModel(overrideModel, { persist: false });
607
+ if (ok) {
608
+ // Update start model so all subsequent units use this as the baseline
609
+ s.autoModeStartModel = { provider: overrideModel.provider, id: overrideModel.id };
610
+ ctx.ui.notify(`Worker model override: ${overrideModel.provider}/${overrideModel.id}`, "info");
611
+ }
612
+ }
613
+ }
451
614
  // Snapshot installed skills
452
615
  if (resolveSkillDiscoveryMode() !== "off") {
453
616
  snapshotSkills();
@@ -92,3 +92,13 @@ export function isToolInvocationError(errorMsg) {
92
92
  return false;
93
93
  return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
94
94
  }
95
+ /**
96
+ * Returns true if the error message indicates the tool was skipped because
97
+ * a queued user message interrupted the turn (#3595). Retrying will produce
98
+ * the same skip, so the unit should be paused rather than retried.
99
+ */
100
+ export function isQueuedUserMessageSkip(errorMsg) {
101
+ if (!errorMsg)
102
+ return false;
103
+ return /^Skipped due to queued user message\.?$/i.test(errorMsg.trim());
104
+ }
@@ -135,8 +135,10 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
135
135
  unlinkSync(file);
136
136
  }
137
137
  catch (err) {
138
- /* non-fatal — file may not exist */
139
- logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
138
+ // ENOENT is expected — file may not exist (#3597)
139
+ if (err.code !== "ENOENT") {
140
+ logWarning("worktree", `file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
141
+ }
140
142
  }
141
143
  }
142
144
  // Clean up entire synced milestone directory and runtime/units.
@@ -160,8 +162,11 @@ function clearProjectRootStateFiles(basePath, milestoneId) {
160
162
  unlinkSync(join(basePath, f));
161
163
  }
162
164
  catch (err) {
163
- /* non-fatal */
164
- logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
165
+ // ENOENT/EISDIR are expected for already-removed or directory entries (#3597)
166
+ const code = err.code;
167
+ if (code !== "ENOENT" && code !== "EISDIR") {
168
+ logWarning("worktree", `untracked file unlink failed: ${err instanceof Error ? err.message : String(err)}`);
169
+ }
165
170
  }
166
171
  }
167
172
  }
@@ -656,6 +661,10 @@ export function syncWorktreeStateBack(mainBasePath, worktreePath, milestoneId) {
656
661
  .filter((d) => d.isDirectory())
657
662
  .map((d) => d.name);
658
663
  for (const mid of wtMilestones) {
664
+ // Skip the current milestone being merged — its files are already in the
665
+ // milestone branch and would conflict with the squash merge (#3641).
666
+ if (mid === milestoneId)
667
+ continue;
659
668
  syncMilestoneDir(wtGsd, mainGsd, mid, synced);
660
669
  }
661
670
  }
@@ -901,11 +910,19 @@ export function createAutoWorktree(basePath, milestoneId) {
901
910
  });
902
911
  }
903
912
  else {
904
- // Fresh start — create branch from integration branch
913
+ // Fresh start — create branch from integration branch.
914
+ // Use the same 3-tier fallback as mergeMilestoneToMain (#3461):
915
+ // 1. META.json integration branch (explicit per-milestone override)
916
+ // 2. git.main_branch preference (user's configured working branch)
917
+ // 3. nativeDetectMainBranch (origin/HEAD auto-detection)
918
+ // Without tier 2, projects with main_branch=dev but origin/HEAD→master
919
+ // would fork worktrees from the wrong (stale) branch.
905
920
  const integrationBranch = readIntegrationBranch(basePath, milestoneId) ?? undefined;
921
+ const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git;
922
+ const startPoint = integrationBranch ?? gitPrefs?.main_branch ?? undefined;
906
923
  info = createWorktree(basePath, milestoneId, {
907
924
  branch,
908
- startPoint: integrationBranch,
925
+ startPoint,
909
926
  });
910
927
  }
911
928
  // Copy .gsd/ planning artifacts from the source repo into the new worktree.
@@ -1235,7 +1252,12 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1235
1252
  // checkout and leave the user with a broken merge state (#1668).
1236
1253
  const prefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
1237
1254
  const integrationBranch = readIntegrationBranch(originalBasePath_, milestoneId);
1238
- const mainBranch = integrationBranch ?? prefs.main_branch ?? nativeDetectMainBranch(originalBasePath_);
1255
+ // Validate prefs.main_branch exists before using it — a stale preference
1256
+ // (e.g. "master" when repo uses "main") causes merge failure (#3589).
1257
+ const validatedPrefBranch = prefs.main_branch && nativeBranchExists(originalBasePath_, prefs.main_branch)
1258
+ ? prefs.main_branch
1259
+ : undefined;
1260
+ const mainBranch = integrationBranch ?? validatedPrefBranch ?? nativeDetectMainBranch(originalBasePath_);
1239
1261
  // Remove transient project-root state files before any branch or merge
1240
1262
  // operation. Untracked milestone metadata can otherwise block squash merges.
1241
1263
  clearProjectRootStateFiles(originalBasePath_, milestoneId);
@@ -23,7 +23,7 @@ import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSes
23
23
  import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
24
24
  import { sendDesktopNotification } from "./notifications.js";
25
25
  import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, } from "./auto-budget.js";
26
- import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, } from "./auto-tool-tracking.js";
26
+ import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, isQueuedUserMessageSkip, } from "./auto-tool-tracking.js";
27
27
  import { closeoutUnit } from "./auto-unit-closeout.js";
28
28
  import { selectAndApplyModel, resolveModelId } from "./auto-model-selection.js";
29
29
  import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
@@ -201,7 +201,7 @@ export function markToolEnd(toolCallId) {
201
201
  export function recordToolInvocationError(toolName, errorMsg) {
202
202
  if (!s.active)
203
203
  return;
204
- if (isToolInvocationError(errorMsg)) {
204
+ if (isToolInvocationError(errorMsg) || isQueuedUserMessageSkip(errorMsg)) {
205
205
  s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
206
206
  }
207
207
  }
@@ -845,12 +845,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
845
845
  s.stepMode = meta.stepMode ?? requestedStepMode;
846
846
  s.autoStartTime = meta.autoStartTime || Date.now();
847
847
  s.paused = true;
848
- try {
849
- unlinkSync(pausedPath);
850
- }
851
- catch (err) { /* non-fatal */
852
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
853
- }
848
+ // Don't delete pause file yet — defer until lock is acquired.
849
+ // If lock fails, the file must survive for retry.
850
+ s.pausedSessionFile = pausedPath;
854
851
  ctx.ui.notify(`Resuming paused custom workflow${meta.activeRunDir ? ` (${meta.activeRunDir})` : ""}.`, "info");
855
852
  }
856
853
  else if (meta.milestoneId) {
@@ -873,13 +870,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
873
870
  s.stepMode = meta.stepMode ?? requestedStepMode;
874
871
  s.autoStartTime = meta.autoStartTime || Date.now();
875
872
  s.paused = true;
876
- // Clean up the persisted file — we're consuming it
877
- try {
878
- unlinkSync(pausedPath);
879
- }
880
- catch (err) { /* non-fatal */
881
- logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
882
- }
873
+ // Don't delete pause file yet defer until lock is acquired.
874
+ // If lock fails, the file must survive for retry.
875
+ s.pausedSessionFile = pausedPath;
883
876
  ctx.ui.notify(`Resuming paused session for ${meta.milestoneId}${meta.worktreePath ? ` (worktree)` : ""}.`, "info");
884
877
  }
885
878
  }
@@ -893,9 +886,22 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
893
886
  if (s.paused) {
894
887
  const resumeLock = acquireSessionLock(base);
895
888
  if (!resumeLock.acquired) {
889
+ // Reset paused state so isAutoPaused() doesn't stick true after lock failure.
890
+ // Pause file is preserved on disk for retry — not deleted.
891
+ s.paused = false;
896
892
  ctx.ui.notify(`Cannot resume: ${resumeLock.reason}`, "error");
897
893
  return;
898
894
  }
895
+ // Lock acquired — now safe to delete the pause file
896
+ if (s.pausedSessionFile) {
897
+ try {
898
+ unlinkSync(s.pausedSessionFile);
899
+ }
900
+ catch (err) {
901
+ logWarning("session", `pause file cleanup failed: ${err instanceof Error ? err.message : String(err)}`, { file: "auto.ts" });
902
+ }
903
+ s.pausedSessionFile = null;
904
+ }
899
905
  s.paused = false;
900
906
  s.active = true;
901
907
  s.verbose = verboseMode;
@@ -79,11 +79,24 @@ export async function handleAgentEnd(pi, event, ctx) {
79
79
  return;
80
80
  }
81
81
  if (lastMsg && "stopReason" in lastMsg && lastMsg.stopReason === "error") {
82
- const errorDetail = "errorMessage" in lastMsg && lastMsg.errorMessage ? `: ${lastMsg.errorMessage}` : "";
83
- const errorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
82
+ // #3588: errorMessage can be useless (e.g. "success") while the real error
83
+ // is in the assistant message text content. Fall back to content when
84
+ // errorMessage looks uninformative.
85
+ const rawErrorMsg = ("errorMessage" in lastMsg && lastMsg.errorMessage) ? String(lastMsg.errorMessage) : "";
86
+ const isUseless = !rawErrorMsg || /^(success|ok|true|error|unknown)$/i.test(rawErrorMsg.trim());
87
+ // #3588: When errorMessage is uninformative, extract the real error from
88
+ // the assistant message text content for display purposes only.
89
+ // Classification still uses rawErrorMsg to avoid false positives from prose.
90
+ let displayMsg = rawErrorMsg;
91
+ if (isUseless && "content" in lastMsg && Array.isArray(lastMsg.content)) {
92
+ const textBlock = lastMsg.content.find((b) => b.type === "text" && b.text);
93
+ if (textBlock)
94
+ displayMsg = textBlock.text.slice(0, 300);
95
+ }
96
+ const errorDetail = displayMsg ? `: ${displayMsg}` : "";
84
97
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
85
- // ── 1. Classify ──────────────────────────────────────────────────────
86
- const cls = classifyError(errorMsg, explicitRetryAfterMs);
98
+ // ── 1. Classify using rawErrorMsg to avoid prose false-positives ────
99
+ const cls = classifyError(rawErrorMsg, explicitRetryAfterMs);
87
100
  // Cap rate-limit backoff for CLI-style providers (openai-codex, google-gemini-cli)
88
101
  // which use per-user quotas with shorter windows (#2922).
89
102
  if (cls.kind === "rate-limit") {
@@ -943,6 +943,16 @@ export function registerDbTools(pi) {
943
943
  }
944
944
  updateSliceStatus(params.milestoneId, params.sliceId, "skipped");
945
945
  invalidateStateCache();
946
+ // Rebuild STATE.md so it reflects the skip immediately (#3477).
947
+ // Without this, /gsd auto reads stale STATE.md and resumes the skipped slice.
948
+ try {
949
+ const basePath = process.cwd();
950
+ const { rebuildState } = await import("../doctor.js");
951
+ await rebuildState(basePath);
952
+ }
953
+ catch (err) {
954
+ logError("tool", `skip_slice rebuildState failed: ${err.message}`, { tool: "gsd_skip_slice" });
955
+ }
946
956
  return {
947
957
  content: [{ type: "text", text: `Skipped slice ${params.sliceId} (${params.milestoneId}). Reason: ${params.reason ?? "User-directed skip"}. Auto-mode will advance past this slice.` }],
948
958
  details: {
@@ -17,10 +17,12 @@ export function registerQueryTools(pi) {
17
17
  }),
18
18
  async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
19
19
  try {
20
- // Strictly read-only: only use an already-open DB connection.
21
- // Do NOT call ensureDbOpen() it can create/migrate the DB as a side effect.
22
- const { isDbAvailable, getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, } = await import("../gsd-db.js");
23
- if (!isDbAvailable()) {
20
+ // Open the DB if not already open safe for read-only use since
21
+ // ensureDbOpen() only creates/migrates when .gsd/ has content (#3644).
22
+ const { ensureDbOpen } = await import("./dynamic-tools.js");
23
+ const dbAvailable = await ensureDbOpen();
24
+ const { getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, } = await import("../gsd-db.js");
25
+ if (!dbAvailable) {
24
26
  return {
25
27
  content: [{ type: "text", text: "Error: GSD database is not available." }],
26
28
  details: { operation: "milestone_status", error: "db_unavailable" },