gsd-pi 2.63.0 → 2.64.0-dev.9c14bd0

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 (448) hide show
  1. package/README.md +46 -134
  2. package/dist/cli.js +48 -6
  3. package/dist/headless-query.js +11 -1
  4. package/dist/help-text.js +4 -1
  5. package/dist/onboarding.js +15 -8
  6. package/dist/resource-loader.js +18 -3
  7. package/dist/resources/extensions/cmux/index.js +21 -12
  8. package/dist/resources/extensions/gsd/auto/detect-stuck.js +27 -0
  9. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +40 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +4 -0
  11. package/dist/resources/extensions/gsd/auto/phases.js +157 -22
  12. package/dist/resources/extensions/gsd/auto/session.js +12 -0
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +14 -8
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +222 -11
  16. package/dist/resources/extensions/gsd/auto-prompts.js +25 -0
  17. package/dist/resources/extensions/gsd/auto-recovery.js +15 -7
  18. package/dist/resources/extensions/gsd/auto-start.js +10 -21
  19. package/dist/resources/extensions/gsd/auto-timers.js +2 -1
  20. package/dist/resources/extensions/gsd/auto-tool-tracking.js +17 -0
  21. package/dist/resources/extensions/gsd/auto-verification.js +138 -1
  22. package/dist/resources/extensions/gsd/auto-worktree.js +13 -7
  23. package/dist/resources/extensions/gsd/auto.js +24 -2
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
  25. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
  26. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
  27. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
  28. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  29. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
  30. package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
  31. package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
  32. package/dist/resources/extensions/gsd/constants.js +42 -0
  33. package/dist/resources/extensions/gsd/db-writer.js +72 -4
  34. package/dist/resources/extensions/gsd/forensics.js +20 -4
  35. package/dist/resources/extensions/gsd/gsd-db.js +64 -17
  36. package/dist/resources/extensions/gsd/guided-flow.js +19 -0
  37. package/dist/resources/extensions/gsd/metrics.js +27 -1
  38. package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
  39. package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
  40. package/dist/resources/extensions/gsd/pre-execution-checks.js +464 -0
  41. package/dist/resources/extensions/gsd/preferences-types.js +6 -0
  42. package/dist/resources/extensions/gsd/preferences-validation.js +33 -0
  43. package/dist/resources/extensions/gsd/preferences.js +11 -2
  44. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  45. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  48. package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
  49. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  50. package/dist/resources/extensions/gsd/prompts/system.md +4 -7
  51. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  52. package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
  53. package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
  54. package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
  55. package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
  56. package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
  57. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
  58. package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
  59. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
  60. package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
  61. package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
  62. package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
  64. package/dist/resources/extensions/gsd/state.js +74 -14
  65. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  66. package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
  67. package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
  68. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
  69. package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
  70. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
  71. package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
  72. package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
  73. package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
  74. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
  75. package/dist/resources/extensions/mcp-client/auth.js +101 -0
  76. package/dist/resources/extensions/mcp-client/index.js +10 -1
  77. package/dist/resources/extensions/ollama/index.js +28 -22
  78. package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
  79. package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
  80. package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
  81. package/dist/resources/extensions/ollama/ollama-client.js +23 -32
  82. package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
  83. package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
  84. package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
  85. package/dist/update-cmd.js +4 -2
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  88. package/dist/web/standalone/.next/build-manifest.json +3 -3
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/required-server-files.json +4 -4
  91. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  92. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  94. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  102. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  116. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  119. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  121. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  122. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +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.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  137. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  177. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  183. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  189. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  197. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  200. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  203. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  205. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  207. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  209. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  210. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  212. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  213. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  215. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  217. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  218. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  221. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  223. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/index.html +1 -1
  225. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  226. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  227. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  228. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  229. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  230. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  231. package/dist/web/standalone/.next/server/app/page.js +2 -2
  232. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  233. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  234. package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
  235. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  236. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  237. package/dist/web/standalone/.next/server/middleware.js +2 -2
  238. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  239. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  240. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  241. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  242. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  243. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  244. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  245. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  248. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  249. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  250. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  251. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  252. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  253. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  254. package/dist/web/standalone/server.js +1 -1
  255. package/dist/welcome-screen.js +1 -1
  256. package/package.json +1 -1
  257. package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
  258. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  259. package/packages/pi-agent-core/dist/agent-loop.js +50 -0
  260. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  261. package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
  262. package/packages/pi-agent-core/src/agent-loop.ts +53 -0
  263. package/packages/pi-ai/dist/types.d.ts +16 -1
  264. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  265. package/packages/pi-ai/dist/types.js.map +1 -1
  266. package/packages/pi-ai/src/types.ts +18 -1
  267. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
  268. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  269. package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  271. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
  272. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  273. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
  274. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  275. package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
  276. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
  278. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
  280. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
  281. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
  282. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
  283. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  284. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  286. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
  287. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  288. package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
  289. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
  291. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
  293. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  294. package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
  295. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  296. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  298. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  299. package/packages/pi-coding-agent/package.json +1 -1
  300. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
  301. package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
  302. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
  303. package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
  304. package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
  305. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  306. package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
  307. package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
  308. package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
  309. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  310. package/pkg/package.json +1 -1
  311. package/src/resources/extensions/cmux/index.ts +18 -12
  312. package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
  313. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
  314. package/src/resources/extensions/gsd/auto/loop.ts +5 -0
  315. package/src/resources/extensions/gsd/auto/phases.ts +194 -33
  316. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  317. package/src/resources/extensions/gsd/auto-dashboard.ts +16 -7
  318. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
  319. package/src/resources/extensions/gsd/auto-post-unit.ts +263 -12
  320. package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
  321. package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
  322. package/src/resources/extensions/gsd/auto-start.ts +11 -20
  323. package/src/resources/extensions/gsd/auto-timers.ts +2 -1
  324. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  325. package/src/resources/extensions/gsd/auto-verification.ts +190 -2
  326. package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
  327. package/src/resources/extensions/gsd/auto.ts +26 -1
  328. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
  329. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
  330. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
  331. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
  332. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
  333. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
  334. package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
  335. package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
  336. package/src/resources/extensions/gsd/constants.ts +44 -0
  337. package/src/resources/extensions/gsd/db-writer.ts +78 -4
  338. package/src/resources/extensions/gsd/forensics.ts +21 -5
  339. package/src/resources/extensions/gsd/gsd-db.ts +64 -17
  340. package/src/resources/extensions/gsd/guided-flow.ts +22 -0
  341. package/src/resources/extensions/gsd/metrics.ts +28 -1
  342. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
  343. package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
  344. package/src/resources/extensions/gsd/pre-execution-checks.ts +573 -0
  345. package/src/resources/extensions/gsd/preferences-types.ts +44 -0
  346. package/src/resources/extensions/gsd/preferences-validation.ts +33 -0
  347. package/src/resources/extensions/gsd/preferences.ts +13 -2
  348. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  349. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  350. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  351. package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  352. package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
  353. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  354. package/src/resources/extensions/gsd/prompts/system.md +4 -7
  355. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  356. package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
  357. package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
  358. package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
  359. package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
  360. package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
  361. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
  362. package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
  363. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
  364. package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
  365. package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
  366. package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
  367. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
  368. package/src/resources/extensions/gsd/state.ts +67 -12
  369. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  370. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
  371. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
  372. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +50 -0
  373. package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
  374. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
  375. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  376. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
  377. package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
  378. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
  379. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
  380. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
  381. package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
  382. package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
  383. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
  384. package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
  385. package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
  386. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
  387. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
  388. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
  389. package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
  390. package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
  391. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
  392. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
  393. package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
  394. package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
  395. package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
  396. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
  397. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
  398. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
  399. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +999 -0
  400. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
  401. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
  402. package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
  403. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
  404. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
  405. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
  406. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
  407. package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
  408. package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
  409. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
  410. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
  411. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
  412. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
  413. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
  414. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
  415. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
  416. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
  417. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
  418. package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
  419. package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
  420. package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
  421. package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
  422. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
  423. package/src/resources/extensions/gsd/types.ts +44 -22
  424. package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
  425. package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
  426. package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
  427. package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
  428. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
  429. package/src/resources/extensions/mcp-client/auth.ts +149 -0
  430. package/src/resources/extensions/mcp-client/index.ts +16 -1
  431. package/src/resources/extensions/ollama/index.ts +26 -25
  432. package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
  433. package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
  434. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
  435. package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
  436. package/src/resources/extensions/ollama/ollama-client.ts +30 -30
  437. package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
  438. package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
  439. package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
  440. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
  441. package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
  442. package/src/resources/extensions/ollama/types.ts +23 -0
  443. package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
  444. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  445. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  446. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  447. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_buildManifest.js +0 -0
  448. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → SoxM61WC_ia7R2gk4VMpJ}/_ssgManifest.js +0 -0
@@ -18,6 +18,7 @@ import { loadFile, parseSummary, resolveAllOverrides } from "./files.js";
18
18
  import { loadPrompt } from "./prompt-loader.js";
19
19
  import {
20
20
  resolveSliceFile,
21
+ resolveSlicePath,
21
22
  resolveTaskFile,
22
23
  resolveMilestoneFile,
23
24
  resolveTasksDir,
@@ -33,6 +34,7 @@ import {
33
34
  import {
34
35
  verifyExpectedArtifact,
35
36
  resolveExpectedArtifactPath,
37
+ writeBlockerPlaceholder,
36
38
  diagnoseExpectedArtifact,
37
39
  } from "./auto-recovery.js";
38
40
  import { regenerateIfMissing } from "./workflow-projections.js";
@@ -51,6 +53,20 @@ import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures
51
53
  import { debugLog } from "./debug-logger.js";
52
54
  import { runSafely } from "./auto-utils.js";
53
55
  import type { AutoSession, SidecarItem } from "./auto/session.js";
56
+ import { getEvidence } from "./safety/evidence-collector.js";
57
+ import { validateFileChanges } from "./safety/file-change-validator.js";
58
+ // crossReferenceEvidence available for future use when verification_evidence is stored in DB
59
+ // import { crossReferenceEvidence, type ClaimedEvidence } from "./safety/evidence-cross-ref.js";
60
+ import { validateContent } from "./safety/content-validator.js";
61
+ import { resolveSafetyHarnessConfig } from "./safety/safety-harness.js";
62
+ import { resolveExpectedArtifactPath as resolveArtifactForContent } from "./auto-artifact-paths.js";
63
+ import { loadEffectiveGSDPreferences } from "./preferences.js";
64
+ import { getSliceTasks } from "./gsd-db.js";
65
+ import { runPreExecutionChecks, type PreExecutionResult } from "./pre-execution-checks.js";
66
+ import { writePreExecutionEvidence } from "./verification-evidence.js";
67
+
68
+ /** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
69
+ const MAX_VERIFICATION_RETRIES = 3;
54
70
 
55
71
 
56
72
  /** Enqueue a sidecar item (hook, triage, or quick-task) for the main loop to
@@ -433,6 +449,87 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
433
449
  debugLog("postUnit", { phase: "rogue-detection", error: String(e) });
434
450
  }
435
451
 
452
+ // ── Safety harness: post-unit validation ──
453
+ try {
454
+ const { loadEffectiveGSDPreferences } = await import("./preferences.js");
455
+ const prefs = loadEffectiveGSDPreferences()?.preferences;
456
+ const safetyConfig = resolveSafetyHarnessConfig(
457
+ prefs?.safety_harness as Record<string, unknown> | undefined,
458
+ );
459
+
460
+ if (safetyConfig.enabled) {
461
+ const { milestone: sMid, slice: sSid, task: sTid } = parseUnitId(s.currentUnit.id);
462
+
463
+ // File change validation (execute-task only, after auto-commit)
464
+ if (safetyConfig.file_change_validation && s.currentUnit.type === "execute-task" && sMid && sSid && sTid && isDbAvailable()) {
465
+ try {
466
+ const taskRow = getTask(sMid, sSid, sTid);
467
+ if (taskRow) {
468
+ const expectedOutput = taskRow.expected_output ?? [];
469
+ const plannedFiles = taskRow.files ?? [];
470
+ const audit = validateFileChanges(s.basePath, expectedOutput, plannedFiles);
471
+ if (audit && audit.violations.length > 0) {
472
+ const warnings = audit.violations.filter(v => v.severity === "warning");
473
+ for (const v of warnings) {
474
+ logWarning("safety", `file-change: ${v.file} — ${v.reason}`);
475
+ }
476
+ if (warnings.length > 0) {
477
+ ctx.ui.notify(
478
+ `Safety: ${warnings.length} unexpected file change(s) outside task plan`,
479
+ "warning",
480
+ );
481
+ }
482
+ }
483
+ }
484
+ } catch (e) {
485
+ debugLog("postUnit", { phase: "safety-file-change", error: String(e) });
486
+ }
487
+ }
488
+
489
+ // Evidence cross-reference (execute-task only)
490
+ // Verification evidence is passed via the complete-task tool call and
491
+ // stored in the SUMMARY.md on disk — not available as structured data
492
+ // in the DB. The evidence collector tracks actual bash tool calls, so
493
+ // we can still detect units that claimed success but ran no commands.
494
+ if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
495
+ try {
496
+ const actual = getEvidence();
497
+ const bashCalls = actual.filter(e => e.kind === "bash");
498
+ // If the task is marked complete but zero bash commands were run,
499
+ // it's suspicious — the LLM may have fabricated results.
500
+ if (sMid && sSid && sTid && isDbAvailable()) {
501
+ const taskRow = getTask(sMid, sSid, sTid);
502
+ if (taskRow?.status === "complete" && taskRow.verify && bashCalls.length === 0) {
503
+ logWarning("safety", "task marked complete with verification commands but no bash calls were executed");
504
+ ctx.ui.notify(
505
+ `Safety: task ${sTid} has verification commands but no bash calls were recorded`,
506
+ "warning",
507
+ );
508
+ }
509
+ }
510
+ } catch (e) {
511
+ debugLog("postUnit", { phase: "safety-evidence-xref", error: String(e) });
512
+ }
513
+ }
514
+
515
+ // Content validation (plan-slice, plan-milestone)
516
+ if (safetyConfig.content_validation) {
517
+ try {
518
+ const artifactPath = resolveArtifactForContent(s.currentUnit.type, s.currentUnit.id, s.basePath);
519
+ const contentViolations = validateContent(s.currentUnit.type, artifactPath);
520
+ for (const v of contentViolations) {
521
+ logWarning("safety", `content: ${v.reason}`);
522
+ ctx.ui.notify(`Content validation: ${v.reason}`, "warning");
523
+ }
524
+ } catch (e) {
525
+ debugLog("postUnit", { phase: "safety-content-validation", error: String(e) });
526
+ }
527
+ }
528
+ }
529
+ } catch (e) {
530
+ debugLog("postUnit", { phase: "safety-harness", error: String(e) });
531
+ }
532
+
436
533
  // Artifact verification
437
534
  let triggerArtifactVerified = false;
438
535
  if (!s.currentUnit.type.startsWith("hook/")) {
@@ -468,6 +565,8 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
468
565
  // When artifact verification fails for a unit type that has a known expected
469
566
  // artifact, return "retry" so the caller re-dispatches with failure context
470
567
  // instead of blindly re-dispatching the same unit (#1571).
568
+ // After MAX_VERIFICATION_RETRIES, escalate to writeBlockerPlaceholder so the
569
+ // pipeline can advance instead of looping forever (#2653).
471
570
  //
472
571
  // HOWEVER, if the DB is unavailable (db_unavailable), the artifact was never
473
572
  // written because the completion tool failed at the infra level. Retrying
@@ -483,23 +582,58 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
483
582
  "error",
484
583
  );
485
584
  } else if (!triggerArtifactVerified) {
585
+ // #2883: If the artifact is missing because the tool invocation itself
586
+ // failed (malformed/truncated JSON arguments), retrying will produce the
587
+ // same failure. Pause auto-mode instead of entering a stuck retry loop.
588
+ if (s.lastToolInvocationError) {
589
+ const errMsg = `Tool invocation failed for ${s.currentUnit.type}: ${s.lastToolInvocationError}. Structured argument generation failed — pausing auto-mode.`;
590
+ debugLog("postUnit", { phase: "tool-invocation-error-pause", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
591
+ ctx.ui.notify(errMsg, "error");
592
+ s.lastToolInvocationError = null;
593
+ await pauseAuto(ctx, pi);
594
+ return "dispatched";
595
+ }
596
+
486
597
  const hasExpectedArtifact = resolveExpectedArtifactPath(s.currentUnit.type, s.currentUnit.id, s.basePath) !== null;
487
598
  if (hasExpectedArtifact) {
488
599
  const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
489
600
  const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
490
601
  s.verificationRetryCount.set(retryKey, attempt);
491
- const retryDiag = diagnoseExpectedArtifact(s.currentUnit.type, s.currentUnit.id, s.basePath);
492
- s.pendingVerificationRetry = {
493
- unitId: s.currentUnit.id,
494
- failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).${retryDiag ? ` Expected: ${retryDiag}` : ""}`,
495
- attempt,
496
- };
497
- debugLog("postUnit", { phase: "artifact-verify-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
498
- ctx.ui.notify(
499
- `Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — retrying (attempt ${attempt}).${retryDiag ? ` Expected: ${retryDiag}` : ""}`,
500
- "warning",
501
- );
502
- return "retry";
602
+
603
+ if (attempt > MAX_VERIFICATION_RETRIES) {
604
+ // Retries exhausted — write a blocker placeholder so the pipeline
605
+ // can advance past this stuck unit (#2653).
606
+ debugLog("postUnit", {
607
+ phase: "artifact-verify-escalate",
608
+ unitType: s.currentUnit.type,
609
+ unitId: s.currentUnit.id,
610
+ attempt,
611
+ maxRetries: MAX_VERIFICATION_RETRIES,
612
+ });
613
+ const reason = `Artifact verification failed after ${MAX_VERIFICATION_RETRIES} retries for ${s.currentUnit.type} "${s.currentUnit.id}".`;
614
+ writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
615
+ ctx.ui.notify(
616
+ `${s.currentUnit.type} ${s.currentUnit.id} — verification retries exhausted (${MAX_VERIFICATION_RETRIES}), wrote blocker placeholder to advance pipeline`,
617
+ "warning",
618
+ );
619
+ // Reset retry count and fall through to "continue" so the loop
620
+ // re-derives state with the placeholder in place.
621
+ s.verificationRetryCount.delete(retryKey);
622
+ s.pendingVerificationRetry = null;
623
+ // Do NOT return "retry" — fall through to "continue" below.
624
+ } else {
625
+ s.pendingVerificationRetry = {
626
+ unitId: s.currentUnit.id,
627
+ failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).`,
628
+ attempt,
629
+ };
630
+ debugLog("postUnit", { phase: "artifact-verify-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
631
+ ctx.ui.notify(
632
+ `Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — retrying (attempt ${attempt})`,
633
+ "warning",
634
+ );
635
+ return "retry";
636
+ }
503
637
  }
504
638
  }
505
639
  } else {
@@ -643,6 +777,123 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
643
777
  }
644
778
  }
645
779
 
780
+ // ── Pre-execution checks (after plan-slice completes) ──
781
+ if (
782
+ s.currentUnit &&
783
+ s.currentUnit.type === "plan-slice"
784
+ ) {
785
+ let preExecPauseNeeded = false;
786
+ await runSafely("postUnitPostVerification", "pre-execution-checks", async () => {
787
+ try {
788
+ // Check preferences — respect enhanced_verification and enhanced_verification_pre
789
+ const prefs = loadEffectiveGSDPreferences()?.preferences;
790
+ const enhancedEnabled = prefs?.enhanced_verification !== false; // default true
791
+ const preEnabled = prefs?.enhanced_verification_pre !== false; // default true
792
+
793
+ if (!enhancedEnabled || !preEnabled) {
794
+ debugLog("postUnitPostVerification", {
795
+ phase: "pre-execution-checks",
796
+ skipped: true,
797
+ reason: "disabled by preferences",
798
+ });
799
+ return;
800
+ }
801
+
802
+ // Parse the unit ID to get milestone/slice IDs
803
+ const { milestone: mid, slice: sid } = parseUnitId(s.currentUnit!.id);
804
+ if (!mid || !sid) {
805
+ debugLog("postUnitPostVerification", {
806
+ phase: "pre-execution-checks",
807
+ skipped: true,
808
+ reason: "could not parse milestone/slice from unit ID",
809
+ });
810
+ return;
811
+ }
812
+
813
+ // Get tasks for this slice from DB
814
+ const tasks = getSliceTasks(mid, sid);
815
+ if (tasks.length === 0) {
816
+ debugLog("postUnitPostVerification", {
817
+ phase: "pre-execution-checks",
818
+ skipped: true,
819
+ reason: "no tasks found for slice",
820
+ });
821
+ return;
822
+ }
823
+
824
+ // Run pre-execution checks
825
+ const result: PreExecutionResult = await runPreExecutionChecks(tasks, s.basePath);
826
+
827
+ // Log summary to stderr in existing verification output format
828
+ const emoji = result.status === "pass" ? "✅" : result.status === "warn" ? "⚠️" : "❌";
829
+ process.stderr.write(
830
+ `gsd-pre-exec: ${emoji} Pre-execution checks ${result.status} for ${mid}/${sid} (${result.durationMs}ms)\n`,
831
+ );
832
+
833
+ // Log individual check results
834
+ for (const check of result.checks) {
835
+ const checkEmoji = check.passed ? "✓" : check.blocking ? "✗" : "⚠";
836
+ process.stderr.write(
837
+ `gsd-pre-exec: ${checkEmoji} [${check.category}] ${check.target}: ${check.message}\n`,
838
+ );
839
+ }
840
+
841
+ // Write evidence JSON to slice artifacts directory
842
+ const slicePath = resolveSlicePath(s.basePath, mid, sid);
843
+ if (slicePath) {
844
+ writePreExecutionEvidence(result, slicePath, mid, sid);
845
+ }
846
+
847
+ // Notify UI
848
+ if (result.status === "fail") {
849
+ const blockingCount = result.checks.filter(c => !c.passed && c.blocking).length;
850
+ ctx.ui.notify(
851
+ `Pre-execution checks failed: ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} found`,
852
+ "error",
853
+ );
854
+ preExecPauseNeeded = true;
855
+ } else if (result.status === "warn") {
856
+ ctx.ui.notify(
857
+ `Pre-execution checks passed with warnings`,
858
+ "warning",
859
+ );
860
+ // Strict mode: treat warnings as blocking
861
+ if (prefs?.enhanced_verification_strict === true) {
862
+ preExecPauseNeeded = true;
863
+ }
864
+ }
865
+
866
+ debugLog("postUnitPostVerification", {
867
+ phase: "pre-execution-checks",
868
+ status: result.status,
869
+ checkCount: result.checks.length,
870
+ durationMs: result.durationMs,
871
+ });
872
+ } catch (preExecError) {
873
+ // Fail-closed: if runPreExecutionChecks throws, pause auto-mode instead of silently continuing
874
+ const errorMessage = preExecError instanceof Error ? preExecError.message : String(preExecError);
875
+ debugLog("postUnitPostVerification", {
876
+ phase: "pre-execution-checks",
877
+ error: errorMessage,
878
+ failClosed: true,
879
+ });
880
+ logError("engine", `gsd-pre-exec: Pre-execution checks threw an error: ${errorMessage}`);
881
+ ctx.ui.notify(
882
+ `Pre-execution checks error: ${errorMessage} — pausing for human review`,
883
+ "error",
884
+ );
885
+ preExecPauseNeeded = true;
886
+ }
887
+ });
888
+
889
+ // Check for blocking failures after runSafely completes
890
+ if (preExecPauseNeeded) {
891
+ debugLog("postUnitPostVerification", { phase: "pre-execution-checks", pausing: true, reason: "blocking failures detected" });
892
+ await pauseAuto(ctx, pi);
893
+ return "stopped";
894
+ }
895
+ }
896
+
646
897
  // ── Triage check ──
647
898
  if (
648
899
  !s.stepMode &&
@@ -994,10 +994,15 @@ export async function buildResearchSlicePrompt(
994
994
  const milestoneResearchPath = resolveMilestoneFile(base, mid, "RESEARCH");
995
995
  const milestoneResearchRel = relMilestoneFile(base, mid, "RESEARCH");
996
996
 
997
+ const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
998
+ const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
999
+
997
1000
  const inlined: string[] = [];
998
1001
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
999
1002
  const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
1000
1003
  if (contextInline) inlined.push(contextInline);
1004
+ const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
1005
+ if (sliceCtxInline) inlined.push(sliceCtxInline);
1001
1006
  const researchInline = await inlineFileOptional(milestoneResearchPath, milestoneResearchRel, "Milestone Research");
1002
1007
  if (researchInline) inlined.push(researchInline);
1003
1008
  const decisionsInline = await inlineDecisionsFromDb(base, mid);
@@ -1045,6 +1050,8 @@ export async function buildPlanSlicePrompt(
1045
1050
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
1046
1051
  const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
1047
1052
  const researchRel = relSliceFile(base, mid, sid, "RESEARCH");
1053
+ const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
1054
+ const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
1048
1055
 
1049
1056
  const inlined: string[] = [];
1050
1057
 
@@ -1053,6 +1060,8 @@ export async function buildPlanSlicePrompt(
1053
1060
  if (researchSliceAnchor) inlined.push(formatAnchorForPrompt(researchSliceAnchor));
1054
1061
 
1055
1062
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1063
+ const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
1064
+ if (sliceCtxInline) inlined.push(sliceCtxInline);
1056
1065
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
1057
1066
  if (researchInline) inlined.push(researchInline);
1058
1067
  if (inlineLevel !== "minimal") {
@@ -1253,9 +1262,13 @@ export async function buildCompleteSlicePrompt(
1253
1262
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
1254
1263
  const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
1255
1264
  const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
1265
+ const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
1266
+ const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
1256
1267
 
1257
1268
  const inlined: string[] = [];
1258
1269
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1270
+ const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
1271
+ if (sliceCtxInline) inlined.push(sliceCtxInline);
1259
1272
  inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Slice Plan"));
1260
1273
  if (inlineLevel !== "minimal") {
1261
1274
  const requirementsInline = await inlineRequirementsFromDb(base, sid, inlineLevel);
@@ -1510,9 +1523,13 @@ export async function buildReplanSlicePrompt(
1510
1523
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
1511
1524
  const slicePlanPath = resolveSliceFile(base, mid, sid, "PLAN");
1512
1525
  const slicePlanRel = relSliceFile(base, mid, sid, "PLAN");
1526
+ const sliceContextPath = resolveSliceFile(base, mid, sid, "CONTEXT");
1527
+ const sliceContextRel = relSliceFile(base, mid, sid, "CONTEXT");
1513
1528
 
1514
1529
  const inlined: string[] = [];
1515
1530
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
1531
+ const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
1532
+ if (sliceCtxInline) inlined.push(sliceCtxInline);
1516
1533
  inlined.push(await inlineFile(slicePlanPath, slicePlanRel, "Current Slice Plan"));
1517
1534
 
1518
1535
  // Find the blocker task summary — the completed task with blocker_discovered: true
@@ -1627,9 +1644,13 @@ export async function buildReassessRoadmapPrompt(
1627
1644
  const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
1628
1645
  const summaryPath = resolveSliceFile(base, mid, completedSliceId, "SUMMARY");
1629
1646
  const summaryRel = relSliceFile(base, mid, completedSliceId, "SUMMARY");
1647
+ const sliceContextPath = resolveSliceFile(base, mid, completedSliceId, "CONTEXT");
1648
+ const sliceContextRel = relSliceFile(base, mid, completedSliceId, "CONTEXT");
1630
1649
 
1631
1650
  const inlined: string[] = [];
1632
1651
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Current Roadmap"));
1652
+ const sliceCtxInline = await inlineFileOptional(sliceContextPath, sliceContextRel, "Slice Context (from discussion)");
1653
+ if (sliceCtxInline) inlined.push(sliceCtxInline);
1633
1654
  inlined.push(await inlineFile(summaryPath, summaryRel, `${completedSliceId} Summary`));
1634
1655
  if (inlineLevel !== "minimal") {
1635
1656
  const projectInline = await inlineProjectFromDb(base);
@@ -12,7 +12,7 @@ import { parseUnitId } from "./unit-id.js";
12
12
  import { atomicWriteSync } from "./atomic-write.js";
13
13
  import { clearParseCache } from "./files.js";
14
14
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
15
- import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus } from "./gsd-db.js";
15
+ import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus, updateSliceStatus } from "./gsd-db.js";
16
16
  import { isValidationTerminal } from "./state.js";
17
17
  import { getErrorMessage } from "./error-utils.js";
18
18
  import { logWarning, logError } from "./workflow-logger.js";
@@ -424,15 +424,16 @@ export function writeBlockerPlaceholder(
424
424
  ].join("\n");
425
425
  writeFileSync(absPath, content, "utf-8");
426
426
 
427
- // Mark the task as complete in the DB so verifyExpectedArtifact passes.
427
+ // Mark the task/slice as complete in the DB so verifyExpectedArtifact passes.
428
428
  // Without this, the DB status stays "pending" and the dispatch loop
429
- // re-derives the same task indefinitely (#2531).
430
- if (unitType === "execute-task" && isDbAvailable()) {
429
+ // re-derives the same unit indefinitely (#2531, #2653).
430
+ if (isDbAvailable()) {
431
431
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
432
- if (mid && sid && tid) {
433
- try { updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString()); } catch (err) { /* non-fatal */
434
- logError("recovery", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
435
- }
432
+ if (unitType === "execute-task" && mid && sid && tid) {
433
+ try { updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString()); } catch (e) { logWarning("recovery", `updateTaskStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
434
+ }
435
+ if (unitType === "complete-slice" && mid && sid) {
436
+ try { updateSliceStatus(mid, sid, "complete", new Date().toISOString()); } catch (e) { logWarning("recovery", `updateSliceStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`); }
436
437
  }
437
438
  }
438
439
 
@@ -58,7 +58,7 @@ import { initRoutingHistory } from "./routing-history.js";
58
58
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
59
59
  import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
60
60
  import { snapshotSkills } from "./skill-discovery.js";
61
- import { isDbAvailable, getMilestone } from "./gsd-db.js";
61
+ import { isDbAvailable, getMilestone, openDatabase } from "./gsd-db.js";
62
62
  import { hideFooter } from "./auto-dashboard.js";
63
63
  import {
64
64
  debugLog,
@@ -99,33 +99,24 @@ export interface BootstrapDeps {
99
99
  * concurrent session detected). Returns true when ready to dispatch.
100
100
  */
101
101
 
102
- /**
103
- * Open the project-root DB before the first deriveState call (#2841).
104
- * When auto-mode starts cold (no prior DB handle), state derivation that
105
- * touches DB-backed helpers (queue-order, task status) silently falls back
106
- * to markdown-only data, producing stale or incomplete state. Opening the
107
- * DB first ensures deriveState sees the full picture on its very first run.
108
- */
109
- async function openProjectDbIfPresent(basePath: string): Promise<void> {
102
+ /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
103
+ * Prevents the recursive dialog loop described in #1348 where
104
+ * bootstrapAutoSession showSmartEntry checkAutoStartAfterDiscuss startAuto
105
+ * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
106
+ let _consecutiveCompleteBootstraps = 0;
107
+ const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
108
+
109
+ export async function openProjectDbIfPresent(basePath: string): Promise<void> {
110
110
  const gsdDbPath = resolveProjectRootDbPath(basePath);
111
- if (!existsSync(gsdDbPath)) return;
112
- if (isDbAvailable()) return;
111
+ if (!existsSync(gsdDbPath) || isDbAvailable()) return;
113
112
 
114
113
  try {
115
- const { openDatabase } = await import("./gsd-db.js");
116
114
  openDatabase(gsdDbPath);
117
115
  } catch (err) {
118
- /* non-fatal DB lifecycle block below will retry */
119
- logWarning("engine", `DB open failed: ${err instanceof Error ? err.message : String(err)}`);
116
+ logWarning("engine", `gsd-db: failed to open existing database: ${err instanceof Error ? err.message : String(err)}`);
120
117
  }
121
118
  }
122
119
 
123
- /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
124
- * Prevents the recursive dialog loop described in #1348 where
125
- * bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
126
- * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
127
- let _consecutiveCompleteBootstraps = 0;
128
- const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
129
120
  export async function bootstrapAutoSession(
130
121
  s: AutoSession,
131
122
  ctx: ExtensionCommandContext,
@@ -106,8 +106,9 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
106
106
  }
107
107
  }
108
108
  const estimateMinutes = taskEstimate ? parseEstimateMinutes(taskEstimate) : null;
109
+ const MAX_TIMEOUT_SCALE = 6; // Cap at 6x (60min task). Prevents 2h+ tasks from creating 120min+ timeout windows.
109
110
  const timeoutScale = estimateMinutes && estimateMinutes > 0
110
- ? Math.max(1, estimateMinutes / 10) // 10min task = 1x, 30min = 3x, 2h = 12x
111
+ ? Math.min(MAX_TIMEOUT_SCALE, Math.max(1, estimateMinutes / 10))
111
112
  : 1;
112
113
 
113
114
  const softTimeoutMs = (supervisor.soft_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
@@ -83,3 +83,22 @@ export function hasInteractiveToolInFlight(): boolean {
83
83
  export function clearInFlightTools(): void {
84
84
  inFlightTools.clear();
85
85
  }
86
+
87
+ // ─── Tool invocation error classification (#2883) ────────────────────────
88
+
89
+ /**
90
+ * Patterns that indicate a tool invocation failed due to malformed or truncated
91
+ * JSON arguments — as opposed to a normal business-logic error from the tool
92
+ * handler. When these errors occur, retrying the same unit will produce the same
93
+ * failure, so the retry loop must be broken.
94
+ */
95
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
96
+
97
+ /**
98
+ * Returns true if the error message indicates a tool invocation failure due to
99
+ * malformed/truncated arguments (as opposed to a normal tool execution error).
100
+ */
101
+ export function isToolInvocationError(errorMsg: string): boolean {
102
+ if (!errorMsg) return false;
103
+ return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
104
+ }