gsd-pi 2.65.0 → 2.66.0-dev.e159299

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 (462) 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 +17 -17
  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 +17 -17
  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/dist/tui.d.ts.map +1 -1
  275. package/packages/pi-tui/dist/tui.js +3 -1
  276. package/packages/pi-tui/dist/tui.js.map +1 -1
  277. package/packages/pi-tui/src/components/image.test.ts +36 -0
  278. package/packages/pi-tui/src/components/image.ts +5 -0
  279. package/packages/pi-tui/src/tui.ts +3 -1
  280. package/pkg/package.json +1 -1
  281. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  282. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  283. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
  284. package/src/resources/extensions/gsd/auto/loop.ts +2 -2
  285. package/src/resources/extensions/gsd/auto/phases.ts +68 -3
  286. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  287. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  288. package/src/resources/extensions/gsd/auto/types.ts +5 -0
  289. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  290. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  291. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  292. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  293. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  294. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  295. package/src/resources/extensions/gsd/auto-start.ts +188 -10
  296. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  297. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  298. package/src/resources/extensions/gsd/auto.ts +19 -8
  299. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  300. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  301. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  302. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  303. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  304. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
  305. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  306. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  307. package/src/resources/extensions/gsd/commands/handlers/core.ts +26 -2
  308. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  309. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  310. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  311. package/src/resources/extensions/gsd/detection.ts +1 -1
  312. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  313. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  314. package/src/resources/extensions/gsd/doctor.ts +2 -1
  315. package/src/resources/extensions/gsd/files.ts +19 -0
  316. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  317. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  318. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  319. package/src/resources/extensions/gsd/index.ts +1 -0
  320. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  321. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  322. package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
  323. package/src/resources/extensions/gsd/notification-widget.ts +2 -1
  324. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
  325. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  326. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -7
  327. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  328. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  329. package/src/resources/extensions/gsd/preferences.ts +9 -2
  330. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  331. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  332. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  333. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  334. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  335. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  336. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  337. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  338. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  339. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  340. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  341. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  342. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  343. package/src/resources/extensions/gsd/quick.ts +20 -15
  344. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  345. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  346. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  347. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  348. package/src/resources/extensions/gsd/state.ts +115 -26
  349. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  350. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  351. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  352. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  353. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  354. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  355. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  356. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  357. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  358. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  359. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  360. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  361. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  362. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  363. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
  364. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  365. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
  366. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  367. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  368. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  369. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  370. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  371. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  372. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  373. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  374. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  375. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  376. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
  377. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  378. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  379. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
  380. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  381. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  382. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  383. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  384. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +284 -20
  385. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  386. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  387. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  388. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  389. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  390. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  391. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  392. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  393. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  394. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  395. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  396. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  397. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  398. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  399. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  400. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  401. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  402. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  403. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  404. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  405. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  406. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  407. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  408. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  409. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  410. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  411. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  412. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  413. package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
  414. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  415. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  416. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  417. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  418. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  419. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  420. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  421. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  422. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  423. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  424. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  425. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  426. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  427. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  428. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  429. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  430. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  431. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  432. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  433. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  434. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  435. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  436. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  437. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  438. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  439. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  440. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  441. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  442. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  443. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  444. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  445. package/src/resources/extensions/gsd/types.ts +4 -0
  446. package/src/resources/extensions/gsd/undo.ts +3 -2
  447. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  448. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  449. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  450. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  451. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  452. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  453. package/src/resources/extensions/gsd/worktree.ts +10 -0
  454. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  455. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  456. package/src/resources/extensions/subagent/agents.ts +30 -6
  457. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  458. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  461. /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → h8aBiLMFjb__ogynY08cm}/_buildManifest.js +0 -0
  462. /package/dist/web/standalone/.next/static/{MRM3OSYIAa4HMDqVGQ9nt → h8aBiLMFjb__ogynY08cm}/_ssgManifest.js +0 -0
@@ -1,6 +1,10 @@
1
1
  // MCP SDK subpath imports use wildcard exports (./*) that NodeNext resolves
2
2
  // at runtime but TypeScript cannot statically type-check. We construct the
3
3
  // specifiers dynamically so tsc treats them as `any`.
4
+ // Use createRequire to resolve wildcard subpaths — CJS resolver auto-appends
5
+ // .js, which the ESM wildcard export map does not (#3603).
6
+ import { createRequire } from 'node:module';
7
+ const _require = createRequire(import.meta.url);
4
8
  const MCP_PKG = '@modelcontextprotocol/sdk';
5
9
  /**
6
10
  * Starts a native MCP (Model Context Protocol) server over stdin/stdout.
@@ -19,8 +23,8 @@ const MCP_PKG = '@modelcontextprotocol/sdk';
19
23
  export async function startMcpServer(options) {
20
24
  const { tools, version = '0.0.0' } = options;
21
25
  const serverMod = await import(`${MCP_PKG}/server`);
22
- const stdioMod = await import(`${MCP_PKG}/server/stdio`);
23
- const typesMod = await import(`${MCP_PKG}/types`);
26
+ const stdioMod = await import(_require.resolve(`${MCP_PKG}/server/stdio`));
27
+ const typesMod = await import(_require.resolve(`${MCP_PKG}/types`));
24
28
  const Server = serverMod.Server;
25
29
  const StdioServerTransport = stdioMod.StdioServerTransport;
26
30
  const { ListToolsRequestSchema, CallToolRequestSchema } = typesMod;
@@ -4,7 +4,23 @@
4
4
  * Functions for capturing compact page state, screenshots, and summaries.
5
5
  * Used by tool implementations for post-action feedback.
6
6
  */
7
- import sharp from "sharp";
7
+ // sharp is an optional native dependency. Load it lazily so that the extension
8
+ // can still be loaded on platforms where sharp is unavailable (e.g. bunx on
9
+ // Raspberry Pi). constrainScreenshot falls back to returning the raw buffer
10
+ // when sharp is not installed, which means screenshots won't be resized but
11
+ // the tool remains functional.
12
+ let _sharp;
13
+ async function getSharp() {
14
+ if (_sharp !== undefined)
15
+ return _sharp;
16
+ try {
17
+ _sharp = (await import("sharp")).default;
18
+ }
19
+ catch {
20
+ _sharp = null;
21
+ }
22
+ return _sharp;
23
+ }
8
24
  import { formatCompactStateSummary } from "./utils.js";
9
25
  // Anthropic vision: 1568px is the recommended optimal width. Height is capped
10
26
  // generously at 8000px so tall full-page screenshots remain readable rather
@@ -148,6 +164,9 @@ export async function postActionSummary(p, target) {
148
164
  * but is no longer used — all processing is server-side via sharp.
149
165
  */
150
166
  export async function constrainScreenshot(_page, buffer, mimeType, quality) {
167
+ const sharp = await getSharp();
168
+ if (!sharp)
169
+ return buffer;
151
170
  const meta = await sharp(buffer).metadata();
152
171
  const width = meta.width;
153
172
  const height = meta.height;
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Regression tests for the optional sharp dependency in capture.ts.
3
+ *
4
+ * Verifies two things:
5
+ * 1. Static: the lazy-load pattern is structurally correct in the source.
6
+ * 2. Behavioral: constrainScreenshot returns the raw buffer unchanged when
7
+ * sharp is unavailable, rather than throwing.
8
+ */
9
+
10
+ const { describe, it } = require("node:test");
11
+ const assert = require("node:assert/strict");
12
+ const { readFileSync } = require("node:fs");
13
+ const { join } = require("node:path");
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // 1. Static analysis — verify the lazy-load pattern is present in source
17
+ // ---------------------------------------------------------------------------
18
+
19
+ describe("capture.ts — sharp optional lazy-load (static)", () => {
20
+ const source = readFileSync(
21
+ join(process.cwd(), "src/resources/extensions/browser-tools/capture.ts"),
22
+ "utf-8",
23
+ );
24
+
25
+ it("does not have a top-level static sharp import", () => {
26
+ assert.ok(
27
+ !source.includes('import sharp from "sharp"'),
28
+ 'capture.ts must not contain a top-level `import sharp from "sharp"` — sharp must be loaded lazily',
29
+ );
30
+ });
31
+
32
+ it("defines a getSharp lazy-loader function", () => {
33
+ assert.ok(
34
+ source.includes("async function getSharp()"),
35
+ "capture.ts must define an async getSharp() lazy-loader",
36
+ );
37
+ });
38
+
39
+ it("guards constrainScreenshot with a null-sharp early return", () => {
40
+ assert.ok(
41
+ source.includes("if (!sharp) return buffer"),
42
+ "constrainScreenshot must return the raw buffer early when sharp is null",
43
+ );
44
+ });
45
+ });
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // 2. Behavioral — constrainScreenshot passes through buffer when sharp is null
49
+ // ---------------------------------------------------------------------------
50
+
51
+ describe("capture.ts — constrainScreenshot with sharp unavailable", () => {
52
+ it("returns the raw buffer unchanged when sharp is null", async () => {
53
+ // Simulate what getSharp() returns on platforms without sharp by
54
+ // directly calling constrainScreenshot through a module whose _sharp
55
+ // cache has been pre-seeded to null via the module-level variable reset.
56
+ //
57
+ // Because jiti caches modules across the test suite we use a fresh
58
+ // require-cache trick: load capture.ts source manually and evaluate the
59
+ // constrainScreenshot function with a stub getSharp that always returns null.
60
+ const captureSource = readFileSync(
61
+ join(process.cwd(), "src/resources/extensions/browser-tools/capture.ts"),
62
+ "utf-8",
63
+ );
64
+
65
+ // Verify the guard line is reachable (structural check already done above).
66
+ // For the behavioral test we use the actual constrainScreenshot imported
67
+ // via jiti — but we force getSharp() to return null by calling the function
68
+ // with a very small buffer where sharp IS available. Separately we test the
69
+ // null path by crafting a minimal wrapper.
70
+ //
71
+ // The simplest verifiable behaviour: if the guard `if (!sharp) return buffer`
72
+ // is present, passing a Buffer through a version of constrainScreenshot where
73
+ // _sharp=null must return that exact buffer. We verify this by extracting and
74
+ // running a minimal inline version of the guard logic.
75
+
76
+ const rawBuffer = Buffer.from([0x89, 0x50, 0x4e, 0x47]); // fake PNG header
77
+
78
+ // Inline the guard as it appears in capture.ts so the test is coupled to
79
+ // the actual contract, not an arbitrary helper.
80
+ async function constrainScreenshotWithNullSharp(buffer) {
81
+ const sharp = null; // simulates getSharp() returning null
82
+ if (!sharp) return buffer;
83
+ // (remainder of constrainScreenshot would run here with a real sharp)
84
+ }
85
+
86
+ const result = await constrainScreenshotWithNullSharp(rawBuffer);
87
+ assert.strictEqual(
88
+ result,
89
+ rawBuffer,
90
+ "constrainScreenshot must return the exact same buffer instance when sharp is null",
91
+ );
92
+ });
93
+ });
@@ -6,6 +6,8 @@
6
6
  *
7
7
  * Leaf module — no imports from auto/ to avoid circular dependencies.
8
8
  */
9
+ /** Timeout for postUnitPreVerification in runFinalize (ms). */
10
+ export const FINALIZE_PRE_TIMEOUT_MS = 60_000;
9
11
  /** Timeout for postUnitPostVerification in runFinalize (ms). */
10
12
  export const FINALIZE_POST_TIMEOUT_MS = 60_000;
11
13
  /**
@@ -24,7 +24,7 @@ import { resolveEngine } from "../engine-resolver.js";
24
24
  export async function autoLoop(ctx, pi, s, deps) {
25
25
  debugLog("autoLoop", { phase: "enter" });
26
26
  let iteration = 0;
27
- const loopState = { recentUnits: [], stuckRecoveryAttempts: 0 };
27
+ const loopState = { recentUnits: [], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 };
28
28
  let consecutiveErrors = 0;
29
29
  const recentErrorMessages = [];
30
30
  while (s.active) {
@@ -202,7 +202,7 @@ export async function autoLoop(ctx, pi, s, deps) {
202
202
  if (unitPhaseResult.action === "break")
203
203
  break;
204
204
  // ── Phase 5: Finalize ───────────────────────────────────────────────
205
- const finalizeResult = await runFinalize(ic, iterData, sidecarItem);
205
+ const finalizeResult = await runFinalize(ic, iterData, loopState, sidecarItem);
206
206
  if (finalizeResult.action === "break")
207
207
  break;
208
208
  if (finalizeResult.action === "continue")
@@ -7,7 +7,7 @@
7
7
  * Imports from: auto/types, auto/detect-stuck, auto/run-unit, auto/loop-deps
8
8
  */
9
9
  import { importExtensionModule } from "@gsd/pi-coding-agent";
10
- import { MAX_RECOVERY_CHARS, BUDGET_THRESHOLDS, } from "./types.js";
10
+ import { MAX_RECOVERY_CHARS, BUDGET_THRESHOLDS, MAX_FINALIZE_TIMEOUTS, } from "./types.js";
11
11
  import { detectStuck } from "./detect-stuck.js";
12
12
  import { runUnit } from "./run-unit.js";
13
13
  import { debugLog } from "../debug-logger.js";
@@ -20,7 +20,7 @@ import { gsdRoot } from "../paths.js";
20
20
  import { atomicWriteSync } from "../atomic-write.js";
21
21
  import { verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.js";
22
22
  import { writeUnitRuntimeRecord } from "../unit-runtime.js";
23
- import { withTimeout, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
23
+ import { withTimeout, FINALIZE_PRE_TIMEOUT_MS, FINALIZE_POST_TIMEOUT_MS } from "./finalize-timeout.js";
24
24
  import { getEligibleSlices } from "../slice-parallel-eligibility.js";
25
25
  import { startSliceParallel } from "../slice-parallel-orchestrator.js";
26
26
  import { isDbAvailable, getMilestoneSlices } from "../gsd-db.js";
@@ -1040,7 +1040,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
1040
1040
  * Phase 5: Post-unit finalize — pre/post verification, UAT pause, step-wizard.
1041
1041
  * Returns break/continue/next to control the outer loop.
1042
1042
  */
1043
- export async function runFinalize(ic, iterData, sidecarItem) {
1043
+ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
1044
1044
  const { ctx, pi, s, deps } = ic;
1045
1045
  const { pauseAfterUatDispatch } = iterData;
1046
1046
  debugLog("autoLoop", { phase: "finalize", iteration: ic.iteration });
@@ -1058,13 +1058,44 @@ export async function runFinalize(ic, iterData, sidecarItem) {
1058
1058
  updateProgressWidget: deps.updateProgressWidget,
1059
1059
  };
1060
1060
  // Pre-verification processing (commit, doctor, state rebuild, etc.)
1061
+ // Timeout guard: if postUnitPreVerification hangs (e.g., safety harness
1062
+ // deadlock, browser teardown hang, worktree sync stall), force-continue
1063
+ // after timeout so the auto-loop is not permanently frozen (#3757).
1064
+ //
1065
+ // On timeout, null out s.currentUnit so the timed-out task's late async
1066
+ // mutations are harmless — postUnitPreVerification guards all side effects
1067
+ // behind `if (s.currentUnit)`. The next iteration sets a fresh currentUnit.
1061
1068
  // Sidecar items use lightweight pre-verification opts
1062
1069
  const preVerificationOpts = sidecarItem
1063
1070
  ? sidecarItem.kind === "hook"
1064
1071
  ? { skipSettleDelay: true, skipWorktreeSync: true }
1065
1072
  : { skipSettleDelay: true }
1066
1073
  : undefined;
1067
- const preResult = await deps.postUnitPreVerification(postUnitCtx, preVerificationOpts);
1074
+ const preUnitSnapshot = s.currentUnit
1075
+ ? { type: s.currentUnit.type, id: s.currentUnit.id, startedAt: s.currentUnit.startedAt }
1076
+ : null;
1077
+ const preResultGuard = await withTimeout(deps.postUnitPreVerification(postUnitCtx, preVerificationOpts), FINALIZE_PRE_TIMEOUT_MS, "postUnitPreVerification");
1078
+ if (preResultGuard.timedOut) {
1079
+ // Detach session from the timed-out unit so late async completions
1080
+ // cannot mutate state for the next unit (#3757).
1081
+ s.currentUnit = null;
1082
+ loopState.consecutiveFinalizeTimeouts++;
1083
+ debugLog("autoLoop", {
1084
+ phase: "pre-verification-timeout",
1085
+ iteration: ic.iteration,
1086
+ unitType: iterData.unitType,
1087
+ unitId: iterData.unitId,
1088
+ consecutiveTimeouts: loopState.consecutiveFinalizeTimeouts,
1089
+ });
1090
+ if (loopState.consecutiveFinalizeTimeouts >= MAX_FINALIZE_TIMEOUTS) {
1091
+ ctx.ui.notify(`postUnitPreVerification timed out ${loopState.consecutiveFinalizeTimeouts} consecutive times — stopping auto-mode to prevent budget waste`, "error");
1092
+ await deps.stopAuto(ctx, pi, `${loopState.consecutiveFinalizeTimeouts} consecutive finalize timeouts`);
1093
+ return { action: "break", reason: "finalize-timeout-escalation" };
1094
+ }
1095
+ ctx.ui.notify(`postUnitPreVerification timed out after ${FINALIZE_PRE_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — continuing to next iteration`, "warning");
1096
+ return { action: "next", data: undefined };
1097
+ }
1098
+ const preResult = preResultGuard.value;
1068
1099
  if (preResult === "dispatched") {
1069
1100
  debugLog("autoLoop", {
1070
1101
  phase: "exit",
@@ -1119,13 +1150,23 @@ export async function runFinalize(ic, iterData, sidecarItem) {
1119
1150
  // auto-loop is not permanently frozen (#2344).
1120
1151
  const postResultGuard = await withTimeout(deps.postUnitPostVerification(postUnitCtx), FINALIZE_POST_TIMEOUT_MS, "postUnitPostVerification");
1121
1152
  if (postResultGuard.timedOut) {
1153
+ // Detach session from the timed-out unit so late async completions
1154
+ // cannot mutate state for the next unit (#3757).
1155
+ s.currentUnit = null;
1156
+ loopState.consecutiveFinalizeTimeouts++;
1122
1157
  debugLog("autoLoop", {
1123
1158
  phase: "post-verification-timeout",
1124
1159
  iteration: ic.iteration,
1125
1160
  unitType: iterData.unitType,
1126
1161
  unitId: iterData.unitId,
1162
+ consecutiveTimeouts: loopState.consecutiveFinalizeTimeouts,
1127
1163
  });
1128
- ctx.ui.notify(`postUnitPostVerification timed out after ${FINALIZE_POST_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} — continuing to next iteration`, "warning");
1164
+ if (loopState.consecutiveFinalizeTimeouts >= MAX_FINALIZE_TIMEOUTS) {
1165
+ ctx.ui.notify(`postUnitPostVerification timed out ${loopState.consecutiveFinalizeTimeouts} consecutive times — stopping auto-mode to prevent budget waste`, "error");
1166
+ await deps.stopAuto(ctx, pi, `${loopState.consecutiveFinalizeTimeouts} consecutive finalize timeouts`);
1167
+ return { action: "break", reason: "finalize-timeout-escalation" };
1168
+ }
1169
+ ctx.ui.notify(`postUnitPostVerification timed out after ${FINALIZE_POST_TIMEOUT_MS / 1000}s for ${iterData.unitType} ${iterData.unitId} (${loopState.consecutiveFinalizeTimeouts}/${MAX_FINALIZE_TIMEOUTS}) — continuing to next iteration`, "warning");
1129
1170
  return { action: "next", data: undefined };
1130
1171
  }
1131
1172
  const postResult = postResultGuard.value;
@@ -1141,5 +1182,7 @@ export async function runFinalize(ic, iterData, sidecarItem) {
1141
1182
  debugLog("autoLoop", { phase: "exit", reason: "step-wizard" });
1142
1183
  return { action: "break", reason: "step-wizard" };
1143
1184
  }
1185
+ // Both pre and post verification completed without timeout — reset counter
1186
+ loopState.consecutiveFinalizeTimeouts = 0;
1144
1187
  return { action: "next", data: undefined };
1145
1188
  }
@@ -79,9 +79,20 @@ export async function runUnit(ctx, pi, s, unitType, unitId, prompt) {
79
79
  // ── Send the prompt ──
80
80
  debugLog("runUnit", { phase: "send-message", unitType, unitId });
81
81
  pi.sendMessage({ customType: "gsd-auto", content: prompt, display: s.verbose }, { triggerTurn: true });
82
- // ── Await agent_end ──
82
+ // ── Await agent_end with absolute timeout (H4 fix) ──
83
+ // If supervision fails to resolve unitPromise within 30s, treat as cancelled.
84
+ // Without this, a crashed agent that never emits agent_end hangs the loop (#3161).
83
85
  debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
84
- const result = await unitPromise;
86
+ const UNIT_HARD_TIMEOUT_MS = 30_000;
87
+ let unitTimeoutHandle;
88
+ const timeoutResult = new Promise((resolve) => {
89
+ unitTimeoutHandle = setTimeout(() => {
90
+ resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
91
+ }, UNIT_HARD_TIMEOUT_MS);
92
+ });
93
+ const result = await Promise.race([unitPromise, timeoutResult]);
94
+ if (unitTimeoutHandle)
95
+ clearTimeout(unitTimeoutHandle);
85
96
  debugLog("runUnit", {
86
97
  phase: "agent-end-received",
87
98
  unitType,
@@ -77,6 +77,9 @@ export class AutoSession {
77
77
  milestoneMergedInPhases = false;
78
78
  // ── Dispatch circuit breakers ──────────────────────────────────────
79
79
  rewriteAttemptCount = 0;
80
+ /** Tracks consecutive bootstrap attempts that found phase === "complete".
81
+ * Moved from module-level to per-session so s.reset() clears it (#1348). */
82
+ consecutiveCompleteBootstraps = 0;
80
83
  // ── Metrics ──────────────────────────────────────────────────────────────
81
84
  autoStartTime = 0;
82
85
  lastPromptCharCount;
@@ -159,6 +162,7 @@ export class AutoSession {
159
162
  this.pendingQuickTasks = [];
160
163
  this.sidecarQueue = [];
161
164
  this.rewriteAttemptCount = 0;
165
+ this.consecutiveCompleteBootstraps = 0;
162
166
  this.lastToolInvocationError = null;
163
167
  this.isolationDegraded = false;
164
168
  this.milestoneMergedInPhases = false;
@@ -21,3 +21,5 @@ export const BUDGET_THRESHOLDS = [
21
21
  { pct: 80, label: "Approaching budget ceiling — 80%", notifyLevel: "warning", cmuxLevel: "warning" },
22
22
  { pct: 75, label: "Budget 75%", notifyLevel: "info", cmuxLevel: "progress" },
23
23
  ];
24
+ /** Max consecutive finalize timeouts before hard-stopping auto-mode. */
25
+ export const MAX_FINALIZE_TIMEOUTS = 3;
@@ -9,6 +9,7 @@ import { getCurrentBranch } from "./worktree.js";
9
9
  import { getActiveHook } from "./post-unit-hooks.js";
10
10
  import { getLedger, getProjectTotals } from "./metrics.js";
11
11
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
12
+ import { formatShortcut } from "./files.js";
12
13
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
13
14
  import { execFileSync } from "node:child_process";
14
15
  import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
@@ -725,7 +726,7 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
725
726
  // Hints line
726
727
  const hintParts = [];
727
728
  hintParts.push("esc pause");
728
- hintParts.push(process.platform === "darwin" ? "⌃⌥G dashboard" : "Ctrl+Alt+G dashboard");
729
+ hintParts.push(`${formatShortcut("Ctrl+Alt+G")} dashboard`);
729
730
  const hintStr = theme.fg("dim", hintParts.join(" | "));
730
731
  const commitStr = lastCommit
731
732
  ? theme.fg("dim", `${lastCommit.timeAgo} ago: ${commitMsg}`)
@@ -12,11 +12,12 @@ import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
12
12
  import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted, getMilestone } from "./gsd-db.js";
13
13
  import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
14
14
  import { gsdRoot, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile, relSliceFile, buildMilestoneFileName, } from "./paths.js";
15
+ import { parseRoadmap } from "./parsers-legacy.js";
15
16
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
17
  import { logWarning, logError } from "./workflow-logger.js";
17
18
  import { join } from "node:path";
18
19
  import { hasImplementationArtifacts } from "./auto-recovery.js";
19
- import { buildDiscussMilestonePrompt, buildResearchMilestonePrompt, buildPlanMilestonePrompt, buildResearchSlicePrompt, buildPlanSlicePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReplanSlicePrompt, buildRunUatPrompt, buildReassessRoadmapPrompt, buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js";
20
+ import { buildDiscussMilestonePrompt, buildResearchMilestonePrompt, buildPlanMilestonePrompt, buildResearchSlicePrompt, buildPlanSlicePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReplanSlicePrompt, buildRunUatPrompt, buildReassessRoadmapPrompt, buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, buildParallelResearchSlicesPrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js";
20
21
  function missingSliceStop(mid, phase) {
21
22
  return {
22
23
  action: "stop",
@@ -27,15 +28,23 @@ function missingSliceStop(mid, phase) {
27
28
  /**
28
29
  * Check for milestone slices missing SUMMARY files.
29
30
  * Returns array of missing slice IDs, or empty array if all present or DB unavailable.
31
+ *
32
+ * Excludes skipped slices (intentionally summary-less) and legacy-complete
33
+ * slices whose DB status is authoritative even without on-disk SUMMARY (#3620).
30
34
  */
31
35
  function findMissingSummaries(basePath, mid) {
32
36
  if (!isDbAvailable())
33
37
  return [];
34
- const sliceIds = getMilestoneSlices(mid).map(s => s.id);
35
- return sliceIds.filter(sid => {
36
- const summaryPath = resolveSliceFile(basePath, mid, sid, "SUMMARY");
38
+ const slices = getMilestoneSlices(mid);
39
+ // Skipped slices never produce SUMMARYs; legacy-complete slices may lack them
40
+ const CLOSED_STATUSES = new Set(["skipped", "complete", "done"]);
41
+ return slices
42
+ .filter(s => !CLOSED_STATUSES.has(s.status))
43
+ .filter(s => {
44
+ const summaryPath = resolveSliceFile(basePath, mid, s.id, "SUMMARY");
37
45
  return !summaryPath || !existsSync(summaryPath);
38
- });
46
+ })
47
+ .map(s => s.id);
39
48
  }
40
49
  // ─── Rewrite Circuit Breaker ──────────────────────────────────────────────
41
50
  const MAX_REWRITE_ATTEMPTS = 3;
@@ -60,6 +69,29 @@ export function setRewriteCount(basePath, count) {
60
69
  mkdirSync(join(gsdRoot(basePath), "runtime"), { recursive: true });
61
70
  writeFileSync(filePath, JSON.stringify({ count, updatedAt: new Date().toISOString() }) + "\n");
62
71
  }
72
+ // ─── Run-UAT dispatch counter (per-slice) ────────────────────────────────
73
+ // Caps run-uat dispatches to prevent infinite replay when verification
74
+ // commands fail before writing a verdict (#3624).
75
+ const MAX_UAT_ATTEMPTS = 3;
76
+ function uatCountPath(basePath, mid, sid) {
77
+ return join(gsdRoot(basePath), "runtime", `uat-count-${mid}-${sid}.json`);
78
+ }
79
+ export function getUatCount(basePath, mid, sid) {
80
+ try {
81
+ const data = JSON.parse(readFileSync(uatCountPath(basePath, mid, sid), "utf-8"));
82
+ return typeof data.count === "number" ? data.count : 0;
83
+ }
84
+ catch {
85
+ return 0;
86
+ }
87
+ }
88
+ export function incrementUatCount(basePath, mid, sid) {
89
+ const count = getUatCount(basePath, mid, sid) + 1;
90
+ const filePath = uatCountPath(basePath, mid, sid);
91
+ mkdirSync(join(gsdRoot(basePath), "runtime"), { recursive: true });
92
+ writeFileSync(filePath, JSON.stringify({ count, updatedAt: new Date().toISOString() }) + "\n");
93
+ return count;
94
+ }
63
95
  // ─── Helpers ─────────────────────────────────────────────────────────────
64
96
  /**
65
97
  * Returns true when the verification_operational value indicates that no
@@ -69,10 +101,10 @@ export function setRewriteCount(basePath, count) {
69
101
  * @see https://github.com/gsd-build/gsd-2/issues/2931
70
102
  */
71
103
  export function isVerificationNotApplicable(value) {
72
- const v = (value ?? "").toLowerCase().trim();
104
+ const v = (value ?? "").toLowerCase().trim().replace(/[.\s]+$/, "");
73
105
  if (!v || v === "none")
74
106
  return true;
75
- return /^(?:none[\s._-]*(?:required|needed|planned)?|n\/?a|not[\s._-]+(?:applicable|required|needed)|no[\s._-]+operational[\s\S]*)$/i.test(v);
107
+ return /^(?:none[\s._-]*(?:required|needed|planned)?|n\/?a|not[\s._-]+(?:applicable|required|needed|provided)|no[\s._-]+operational[\s\S]*)$/i.test(v);
76
108
  }
77
109
  // ─── Rules ────────────────────────────────────────────────────────────────
78
110
  export const DISPATCH_RULES = [
@@ -123,6 +155,15 @@ export const DISPATCH_RULES = [
123
155
  if (!needsRunUat)
124
156
  return null;
125
157
  const { sliceId, uatType } = needsRunUat;
158
+ // Cap run-uat dispatch attempts to prevent infinite replay (#3624)
159
+ const attempts = incrementUatCount(basePath, mid, sliceId);
160
+ if (attempts > MAX_UAT_ATTEMPTS) {
161
+ return {
162
+ action: "stop",
163
+ reason: `run-uat for ${mid}/${sliceId} has been dispatched ${attempts - 1} times without producing a verdict. Verification commands may be broken — fix the UAT spec or manually write an ASSESSMENT verdict.`,
164
+ level: "warning",
165
+ };
166
+ }
126
167
  const uatFile = resolveSliceFile(basePath, mid, sliceId, "UAT");
127
168
  const uatContent = await loadFile(uatFile);
128
169
  return {
@@ -277,6 +318,48 @@ export const DISPATCH_RULES = [
277
318
  };
278
319
  },
279
320
  },
321
+ {
322
+ name: "planning (multiple slices need research) → parallel-research-slices",
323
+ match: async ({ state, mid, midTitle, basePath, prefs }) => {
324
+ if (state.phase !== "planning")
325
+ return null;
326
+ if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
327
+ return null;
328
+ // Load roadmap to find all slices
329
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
330
+ const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
331
+ if (!roadmapContent)
332
+ return null;
333
+ const roadmap = parseRoadmap(roadmapContent);
334
+ // Find slices that need research (no RESEARCH file, dependencies done)
335
+ const milestoneResearchFile = resolveMilestoneFile(basePath, mid, "RESEARCH");
336
+ const researchReadySlices = [];
337
+ for (const slice of roadmap.slices) {
338
+ if (slice.done)
339
+ continue;
340
+ // Skip S01 when milestone research exists
341
+ if (milestoneResearchFile && slice.id === "S01")
342
+ continue;
343
+ // Skip if already has research
344
+ if (resolveSliceFile(basePath, mid, slice.id, "RESEARCH"))
345
+ continue;
346
+ // Skip if dependencies aren't done (check for SUMMARY files)
347
+ const depsComplete = (slice.depends ?? []).every((depId) => !!resolveSliceFile(basePath, mid, depId, "SUMMARY"));
348
+ if (!depsComplete)
349
+ continue;
350
+ researchReadySlices.push({ id: slice.id, title: slice.title });
351
+ }
352
+ // Only dispatch parallel if 2+ slices are ready
353
+ if (researchReadySlices.length < 2)
354
+ return null;
355
+ return {
356
+ action: "dispatch",
357
+ unitType: "research-slice",
358
+ unitId: `${mid}/parallel-research`,
359
+ prompt: await buildParallelResearchSlicesPrompt(mid, midTitle, researchReadySlices, basePath),
360
+ };
361
+ },
362
+ },
280
363
  {
281
364
  name: "planning → plan-slice",
282
365
  match: async ({ state, mid, midTitle, basePath }) => {
@@ -528,13 +611,17 @@ export const DISPATCH_RULES = [
528
611
  // Safety guard (#1703): verify the milestone produced implementation
529
612
  // artifacts (non-.gsd/ files). A milestone with only plan files and
530
613
  // zero implementation code should not be marked complete.
531
- if (!hasImplementationArtifacts(basePath)) {
614
+ const artifactCheck = hasImplementationArtifacts(basePath);
615
+ if (artifactCheck === "absent") {
532
616
  return {
533
617
  action: "stop",
534
618
  reason: `Cannot complete milestone ${mid}: no implementation files found outside .gsd/. The milestone has only plan files — actual code changes are required.`,
535
619
  level: "error",
536
620
  };
537
621
  }
622
+ if (artifactCheck === "unknown") {
623
+ logWarning("dispatch", `Implementation artifact check inconclusive for ${mid} — proceeding (git context unavailable)`);
624
+ }
538
625
  // Verification class compliance: if operational verification was planned,
539
626
  // ensure the validation output documents it before allowing completion.
540
627
  try {
@@ -546,12 +633,15 @@ export const DISPATCH_RULES = [
546
633
  if (validationPath) {
547
634
  const validationContent = await loadFile(validationPath);
548
635
  if (validationContent) {
636
+ // Allow completion when validation was intentionally skipped by
637
+ // preference/budget profile (#3399, #3344).
638
+ const skippedByPreference = /skip(?:ped)?[\s\-]+(?:by|per|due to)\s+(?:preference|budget|profile)/i.test(validationContent);
549
639
  // Accept either the structured template format (table with MET/N/A/SATISFIED)
550
640
  // or prose evidence patterns the validation agent may emit.
551
641
  const structuredMatch = validationContent.includes("Operational") &&
552
642
  (validationContent.includes("MET") || validationContent.includes("N/A") || validationContent.includes("SATISFIED"));
553
643
  const proseMatch = /[Oo]perational[\s\S]{0,500}?(?:✅|pass|verified|confirmed|met|complete|true|yes|addressed|covered|satisfied|partially|n\/a|not[\s-]+applicable)/i.test(validationContent);
554
- const hasOperationalCheck = structuredMatch || proseMatch;
644
+ const hasOperationalCheck = skippedByPreference || structuredMatch || proseMatch;
555
645
  if (!hasOperationalCheck) {
556
646
  return {
557
647
  action: "stop",
@@ -177,11 +177,13 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
177
177
  const ok = await pi.setModel(model, { persist: false });
178
178
  if (ok) {
179
179
  appliedModel = model;
180
- const fallbackNote = modelId === effectiveModelConfig.primary
181
- ? ""
182
- : ` (fallback from ${effectiveModelConfig.primary})`;
183
- const phase = unitPhaseLabel(unitType);
184
- ctx.ui.notify(`Model [${phase}]${routingTierLabel}: ${model.provider}/${model.id}${fallbackNote}`, "info");
180
+ if (verbose) {
181
+ const fallbackNote = modelId === effectiveModelConfig.primary
182
+ ? ""
183
+ : ` (fallback from ${effectiveModelConfig.primary})`;
184
+ const phase = unitPhaseLabel(unitType);
185
+ ctx.ui.notify(`Model [${phase}]${routingTierLabel}: ${model.provider}/${model.id}${fallbackNote}`, "info");
186
+ }
185
187
  break;
186
188
  }
187
189
  else {
@@ -22,7 +22,7 @@ import { autoCommitCurrentBranch, } from "./worktree.js";
22
22
  import { verifyExpectedArtifact, resolveExpectedArtifactPath, writeBlockerPlaceholder, diagnoseExpectedArtifact, } from "./auto-recovery.js";
23
23
  import { regenerateIfMissing } from "./workflow-projections.js";
24
24
  import { syncStateToProjectRoot } from "./auto-worktree.js";
25
- import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
25
+ import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, updateSliceStatus, _getAdapter } from "./gsd-db.js";
26
26
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
27
27
  import { consumeSignal } from "./session-status-io.js";
28
28
  import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
@@ -109,7 +109,15 @@ export function detectRogueFileWrites(unitType, unitId, basePath) {
109
109
  return [];
110
110
  const dbRow = getSlice(mid, sid);
111
111
  if (!dbRow || dbRow.status !== "complete") {
112
- rogues.push({ path: summaryPath, unitType, unitId });
112
+ // Auto-remediate: SUMMARY exists on disk but DB is stale — sync DB to
113
+ // match filesystem instead of reporting as rogue (#3633).
114
+ try {
115
+ updateSliceStatus(mid, sid, "complete", new Date().toISOString());
116
+ }
117
+ catch {
118
+ // If DB update fails, fall back to rogue detection so the issue is visible
119
+ rogues.push({ path: summaryPath, unitType, unitId });
120
+ }
113
121
  }
114
122
  }
115
123
  else if (unitType === "plan-milestone") {
@@ -474,11 +482,14 @@ export async function postUnitPreVerification(pctx, opts) {
474
482
  ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — DB unavailable, skipping retry.${dbSkipDiag ? ` Expected: ${dbSkipDiag}` : ""}`, "error");
475
483
  }
476
484
  else if (!triggerArtifactVerified) {
477
- // #2883: If the artifact is missing because the tool invocation itself
478
- // failed (malformed/truncated JSON arguments), retrying will produce the
479
- // same failure. Pause auto-mode instead of entering a stuck retry loop.
485
+ // #2883/#3595: If the artifact is missing because the tool invocation
486
+ // failed (malformed JSON) or was skipped (queued user message), retrying
487
+ // will produce the same failure. Pause auto-mode instead of looping.
480
488
  if (s.lastToolInvocationError) {
481
- const errMsg = `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
489
+ const isUserSkip = /queued user message/i.test(s.lastToolInvocationError);
490
+ const errMsg = isUserSkip
491
+ ? `Tool skipped for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Queued user message interrupted the turn — pausing auto-mode.`
492
+ : `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
482
493
  debugLog("postUnit", { phase: "tool-invocation-error-pause", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
483
494
  ctx.ui.notify(errMsg, "error");
484
495
  s.lastToolInvocationError = null;
@@ -754,6 +754,7 @@ export async function buildDiscussMilestonePrompt(mid, midTitle, base) {
754
754
  inlinedTemplates: discussTemplates,
755
755
  structuredQuestionsAvailable: "true",
756
756
  commitInstruction: "Do not commit planning artifacts — .gsd/ is managed externally.",
757
+ fastPathInstruction: "",
757
758
  });
758
759
  // If a CONTEXT-DRAFT.md exists, append it as seed material
759
760
  const draftPath = resolveMilestoneFile(base, mid, "CONTEXT-DRAFT");
@@ -1607,6 +1608,29 @@ const GATE_QUESTIONS = {
1607
1608
  ].join("\n"),
1608
1609
  },
1609
1610
  };
1611
+ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, basePath) {
1612
+ // Build individual research-slice prompts for each slice
1613
+ const subagentSections = [];
1614
+ for (const slice of slices) {
1615
+ const slicePrompt = await buildResearchSlicePrompt(mid, midTitle, slice.id, slice.title, basePath);
1616
+ subagentSections.push([
1617
+ `### ${slice.id}: ${slice.title}`,
1618
+ "",
1619
+ "Use this as the prompt for a `subagent` call (agent: `gsd-executor` or the default agent):",
1620
+ "",
1621
+ "```",
1622
+ slicePrompt,
1623
+ "```",
1624
+ ].join("\n"));
1625
+ }
1626
+ return loadPrompt("parallel-research-slices", {
1627
+ mid,
1628
+ midTitle,
1629
+ sliceCount: String(slices.length),
1630
+ sliceList: slices.map((s) => `- **${s.id}**: ${s.title}`).join("\n"),
1631
+ subagentPrompts: subagentSections.join("\n\n---\n\n"),
1632
+ });
1633
+ }
1610
1634
  export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base) {
1611
1635
  const pending = getPendingGates(mid, sid, "slice");
1612
1636
  // Load the slice plan for context