gsd-pi 2.63.0 → 2.64.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (353) 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 +9 -3
  14. package/dist/resources/extensions/gsd/auto-model-selection.js +32 -0
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +124 -10
  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-worktree.js +13 -7
  22. package/dist/resources/extensions/gsd/auto.js +19 -2
  23. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +147 -75
  24. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +13 -0
  25. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +85 -0
  26. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +3 -0
  27. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +32 -1
  28. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +54 -0
  29. package/dist/resources/extensions/gsd/bootstrap/system-context.js +30 -2
  30. package/dist/resources/extensions/gsd/commands-handlers.js +9 -4
  31. package/dist/resources/extensions/gsd/constants.js +42 -0
  32. package/dist/resources/extensions/gsd/db-writer.js +72 -4
  33. package/dist/resources/extensions/gsd/forensics.js +20 -4
  34. package/dist/resources/extensions/gsd/gsd-db.js +64 -17
  35. package/dist/resources/extensions/gsd/guided-flow.js +19 -0
  36. package/dist/resources/extensions/gsd/metrics.js +27 -1
  37. package/dist/resources/extensions/gsd/native-git-bridge.js +5 -3
  38. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  39. package/dist/resources/extensions/gsd/preferences.js +7 -2
  40. package/dist/resources/extensions/gsd/prompt-loader.js +7 -0
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  42. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  43. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  44. package/dist/resources/extensions/gsd/prompts/forensics.md +2 -0
  45. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/system.md +4 -7
  47. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  48. package/dist/resources/extensions/gsd/roadmap-mutations.js +1 -1
  49. package/dist/resources/extensions/gsd/roadmap-slices.js +9 -5
  50. package/dist/resources/extensions/gsd/safety/content-validator.js +73 -0
  51. package/dist/resources/extensions/gsd/safety/destructive-guard.js +34 -0
  52. package/dist/resources/extensions/gsd/safety/evidence-collector.js +109 -0
  53. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +83 -0
  54. package/dist/resources/extensions/gsd/safety/file-change-validator.js +71 -0
  55. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +91 -0
  56. package/dist/resources/extensions/gsd/safety/safety-harness.js +64 -0
  57. package/dist/resources/extensions/gsd/slice-parallel-conflict.js +67 -0
  58. package/dist/resources/extensions/gsd/slice-parallel-eligibility.js +51 -0
  59. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +378 -0
  60. package/dist/resources/extensions/gsd/state.js +74 -14
  61. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  62. package/dist/resources/extensions/gsd/tools/complete-milestone.js +17 -12
  63. package/dist/resources/extensions/gsd/tools/complete-slice.js +40 -26
  64. package/dist/resources/extensions/gsd/tools/complete-task.js +12 -12
  65. package/dist/resources/extensions/gsd/tools/plan-milestone.js +33 -25
  66. package/dist/resources/extensions/gsd/tools/plan-slice.js +5 -8
  67. package/dist/resources/extensions/gsd/workflow-projections.js +21 -5
  68. package/dist/resources/extensions/gsd/worktree-manager.js +82 -29
  69. package/dist/resources/extensions/gsd/worktree-resolver.js +4 -3
  70. package/dist/resources/extensions/mcp-client/auth.js +101 -0
  71. package/dist/resources/extensions/mcp-client/index.js +10 -1
  72. package/dist/resources/extensions/ollama/index.js +28 -22
  73. package/dist/resources/extensions/ollama/model-capabilities.js +37 -34
  74. package/dist/resources/extensions/ollama/ndjson-stream.js +54 -0
  75. package/dist/resources/extensions/ollama/ollama-chat-provider.js +380 -0
  76. package/dist/resources/extensions/ollama/ollama-client.js +23 -32
  77. package/dist/resources/extensions/ollama/ollama-discovery.js +2 -7
  78. package/dist/resources/extensions/ollama/ollama-tool.js +62 -0
  79. package/dist/resources/extensions/ollama/thinking-parser.js +104 -0
  80. package/dist/update-cmd.js +4 -2
  81. package/dist/web/standalone/.next/BUILD_ID +1 -1
  82. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  83. package/dist/web/standalone/.next/build-manifest.json +2 -2
  84. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  85. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  86. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  107. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  108. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  115. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  117. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  119. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  121. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  123. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  125. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  144. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  158. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/index.html +1 -1
  164. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  168. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  169. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  170. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  171. package/dist/web/standalone/.next/server/chunks/6897.js +12 -0
  172. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  173. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  174. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  175. package/dist/welcome-screen.js +1 -1
  176. package/package.json +1 -1
  177. package/packages/pi-agent-core/dist/agent-loop.d.ts +8 -0
  178. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  179. package/packages/pi-agent-core/dist/agent-loop.js +50 -0
  180. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  181. package/packages/pi-agent-core/src/agent-loop.test.ts +221 -5
  182. package/packages/pi-agent-core/src/agent-loop.ts +53 -0
  183. package/packages/pi-ai/dist/types.d.ts +16 -1
  184. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  185. package/packages/pi-ai/dist/types.js.map +1 -1
  186. package/packages/pi-ai/src/types.ts +18 -1
  187. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +9 -0
  188. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/auth-storage.js +50 -1
  190. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +41 -0
  192. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +7 -0
  194. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/extensions/loader.js +31 -4
  196. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +28 -1
  198. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  199. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts +2 -0
  200. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.d.ts.map +1 -0
  201. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js +46 -0
  202. package/packages/pi-coding-agent/dist/core/extensions/provider-registration.test.js.map +1 -0
  203. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -0
  204. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  206. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +1 -0
  207. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  208. package/packages/pi-coding-agent/dist/core/model-registry.js +12 -0
  209. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/core/model-resolver.js +3 -3
  211. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  212. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +23 -1
  213. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  214. package/packages/pi-coding-agent/dist/core/resource-loader.js +80 -56
  215. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  217. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  218. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  219. package/packages/pi-coding-agent/package.json +1 -1
  220. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +53 -0
  221. package/packages/pi-coding-agent/src/core/auth-storage.ts +66 -1
  222. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +39 -1
  223. package/packages/pi-coding-agent/src/core/extensions/loader.ts +34 -4
  224. package/packages/pi-coding-agent/src/core/extensions/provider-registration.test.ts +81 -0
  225. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -0
  226. package/packages/pi-coding-agent/src/core/model-registry.ts +14 -0
  227. package/packages/pi-coding-agent/src/core/model-resolver.ts +3 -3
  228. package/packages/pi-coding-agent/src/core/resource-loader.ts +89 -56
  229. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  230. package/pkg/package.json +1 -1
  231. package/src/resources/extensions/cmux/index.ts +18 -12
  232. package/src/resources/extensions/gsd/auto/detect-stuck.ts +27 -0
  233. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +46 -0
  234. package/src/resources/extensions/gsd/auto/loop.ts +5 -0
  235. package/src/resources/extensions/gsd/auto/phases.ts +194 -33
  236. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  237. package/src/resources/extensions/gsd/auto-dashboard.ts +11 -3
  238. package/src/resources/extensions/gsd/auto-model-selection.ts +36 -0
  239. package/src/resources/extensions/gsd/auto-post-unit.ts +141 -12
  240. package/src/resources/extensions/gsd/auto-prompts.ts +21 -0
  241. package/src/resources/extensions/gsd/auto-recovery.ts +9 -8
  242. package/src/resources/extensions/gsd/auto-start.ts +11 -20
  243. package/src/resources/extensions/gsd/auto-timers.ts +2 -1
  244. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  245. package/src/resources/extensions/gsd/auto-worktree.ts +14 -6
  246. package/src/resources/extensions/gsd/auto.ts +22 -1
  247. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +160 -88
  248. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +15 -0
  249. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +98 -0
  250. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -0
  251. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +36 -1
  252. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +57 -0
  253. package/src/resources/extensions/gsd/bootstrap/system-context.ts +31 -2
  254. package/src/resources/extensions/gsd/commands-handlers.ts +10 -4
  255. package/src/resources/extensions/gsd/constants.ts +44 -0
  256. package/src/resources/extensions/gsd/db-writer.ts +78 -4
  257. package/src/resources/extensions/gsd/forensics.ts +21 -5
  258. package/src/resources/extensions/gsd/gsd-db.ts +64 -17
  259. package/src/resources/extensions/gsd/guided-flow.ts +22 -0
  260. package/src/resources/extensions/gsd/metrics.ts +28 -1
  261. package/src/resources/extensions/gsd/native-git-bridge.ts +5 -3
  262. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  263. package/src/resources/extensions/gsd/preferences.ts +9 -2
  264. package/src/resources/extensions/gsd/prompt-loader.ts +8 -0
  265. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -0
  266. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -0
  267. package/src/resources/extensions/gsd/prompts/doctor-heal.md +1 -0
  268. package/src/resources/extensions/gsd/prompts/forensics.md +2 -0
  269. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +2 -0
  270. package/src/resources/extensions/gsd/prompts/system.md +4 -7
  271. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  272. package/src/resources/extensions/gsd/roadmap-mutations.ts +1 -1
  273. package/src/resources/extensions/gsd/roadmap-slices.ts +10 -5
  274. package/src/resources/extensions/gsd/safety/content-validator.ts +98 -0
  275. package/src/resources/extensions/gsd/safety/destructive-guard.ts +49 -0
  276. package/src/resources/extensions/gsd/safety/evidence-collector.ts +151 -0
  277. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +120 -0
  278. package/src/resources/extensions/gsd/safety/file-change-validator.ts +108 -0
  279. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +106 -0
  280. package/src/resources/extensions/gsd/safety/safety-harness.ts +105 -0
  281. package/src/resources/extensions/gsd/slice-parallel-conflict.ts +86 -0
  282. package/src/resources/extensions/gsd/slice-parallel-eligibility.ts +73 -0
  283. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +477 -0
  284. package/src/resources/extensions/gsd/state.ts +67 -12
  285. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  286. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +288 -0
  287. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +34 -13
  288. package/src/resources/extensions/gsd/tests/cmux.test.ts +58 -0
  289. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +51 -0
  290. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  291. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +211 -0
  292. package/src/resources/extensions/gsd/tests/complete-task.test.ts +39 -0
  293. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +107 -0
  294. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +109 -0
  295. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +13 -9
  296. package/src/resources/extensions/gsd/tests/db-writer.test.ts +134 -0
  297. package/src/resources/extensions/gsd/tests/deferred-slice-dispatch.test.ts +203 -0
  298. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +130 -0
  299. package/src/resources/extensions/gsd/tests/doctor-fix-flag.test.ts +92 -0
  300. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +116 -0
  301. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +50 -0
  302. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +103 -0
  303. package/src/resources/extensions/gsd/tests/git-checkpoint.test.ts +94 -0
  304. package/src/resources/extensions/gsd/tests/insert-slice-no-wipe.test.ts +88 -0
  305. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +27 -7
  306. package/src/resources/extensions/gsd/tests/integration/idle-recovery.test.ts +34 -0
  307. package/src/resources/extensions/gsd/tests/metrics.test.ts +116 -1
  308. package/src/resources/extensions/gsd/tests/milestone-status-tool.test.ts +201 -0
  309. package/src/resources/extensions/gsd/tests/plan-milestone-title.test.ts +2 -1
  310. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +82 -18
  311. package/src/resources/extensions/gsd/tests/preferences.test.ts +10 -0
  312. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +25 -0
  313. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +69 -0
  314. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +30 -0
  315. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +50 -0
  316. package/src/resources/extensions/gsd/tests/slice-parallel-conflict.test.ts +92 -0
  317. package/src/resources/extensions/gsd/tests/slice-parallel-eligibility.test.ts +95 -0
  318. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +83 -0
  319. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +42 -0
  320. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +103 -0
  321. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +349 -0
  322. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +35 -2
  323. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +73 -0
  324. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +34 -0
  325. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +1 -1
  326. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +148 -0
  327. package/src/resources/extensions/gsd/tools/complete-milestone.ts +34 -20
  328. package/src/resources/extensions/gsd/tools/complete-slice.ts +41 -26
  329. package/src/resources/extensions/gsd/tools/complete-task.ts +12 -12
  330. package/src/resources/extensions/gsd/tools/plan-milestone.ts +55 -30
  331. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -8
  332. package/src/resources/extensions/gsd/types.ts +44 -22
  333. package/src/resources/extensions/gsd/workflow-logger.ts +2 -1
  334. package/src/resources/extensions/gsd/workflow-projections.ts +23 -5
  335. package/src/resources/extensions/gsd/worktree-manager.ts +76 -28
  336. package/src/resources/extensions/gsd/worktree-resolver.ts +4 -3
  337. package/src/resources/extensions/mcp-client/auth.ts +149 -0
  338. package/src/resources/extensions/mcp-client/index.ts +16 -1
  339. package/src/resources/extensions/ollama/index.ts +26 -25
  340. package/src/resources/extensions/ollama/model-capabilities.ts +41 -34
  341. package/src/resources/extensions/ollama/ndjson-stream.ts +63 -0
  342. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +20 -0
  343. package/src/resources/extensions/ollama/ollama-chat-provider.ts +459 -0
  344. package/src/resources/extensions/ollama/ollama-client.ts +30 -30
  345. package/src/resources/extensions/ollama/ollama-discovery.ts +5 -8
  346. package/src/resources/extensions/ollama/ollama-tool.ts +69 -0
  347. package/src/resources/extensions/ollama/tests/ollama-chat-provider-stream.test.ts +82 -0
  348. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +0 -27
  349. package/src/resources/extensions/ollama/thinking-parser.ts +116 -0
  350. package/src/resources/extensions/ollama/types.ts +23 -0
  351. package/dist/web/standalone/.next/server/chunks/2229.js +0 -12
  352. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → eebXKteM9EaWyseHKTjqp}/_buildManifest.js +0 -0
  353. /package/dist/web/standalone/.next/static/{5FLUBNdqolRyyehCyChPd → eebXKteM9EaWyseHKTjqp}/_ssgManifest.js +0 -0
@@ -9,7 +9,7 @@
9
9
  import { parseUnitId } from "./unit-id.js";
10
10
  import { clearParseCache } from "./files.js";
11
11
  import { parseRoadmap as parseLegacyRoadmap, parsePlan as parseLegacyPlan } from "./parsers-legacy.js";
12
- import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus } from "./gsd-db.js";
12
+ import { isDbAvailable, getTask, getSlice, getSliceTasks, updateTaskStatus, updateSliceStatus } from "./gsd-db.js";
13
13
  import { isValidationTerminal } from "./state.js";
14
14
  import { getErrorMessage } from "./error-utils.js";
15
15
  import { logWarning, logError } from "./workflow-logger.js";
@@ -377,17 +377,25 @@ export function writeBlockerPlaceholder(unitType, unitId, base, reason) {
377
377
  `Review and replace this file before relying on downstream artifacts.`,
378
378
  ].join("\n");
379
379
  writeFileSync(absPath, content, "utf-8");
380
- // Mark the task as complete in the DB so verifyExpectedArtifact passes.
380
+ // Mark the task/slice as complete in the DB so verifyExpectedArtifact passes.
381
381
  // Without this, the DB status stays "pending" and the dispatch loop
382
- // re-derives the same task indefinitely (#2531).
383
- if (unitType === "execute-task" && isDbAvailable()) {
382
+ // re-derives the same unit indefinitely (#2531, #2653).
383
+ if (isDbAvailable()) {
384
384
  const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
385
- if (mid && sid && tid) {
385
+ if (unitType === "execute-task" && mid && sid && tid) {
386
386
  try {
387
387
  updateTaskStatus(mid, sid, tid, "complete", new Date().toISOString());
388
388
  }
389
- catch (err) { /* non-fatal */
390
- logError("recovery", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
389
+ catch (e) {
390
+ logWarning("recovery", `updateTaskStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`);
391
+ }
392
+ }
393
+ if (unitType === "complete-slice" && mid && sid) {
394
+ try {
395
+ updateSliceStatus(mid, sid, "complete", new Date().toISOString());
396
+ }
397
+ catch (e) {
398
+ logWarning("recovery", `updateSliceStatus failed during context exhaustion: ${e instanceof Error ? e.message : String(e)}`);
391
399
  }
392
400
  }
393
401
  }
@@ -30,7 +30,7 @@ import { initRoutingHistory } from "./routing-history.js";
30
30
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
31
31
  import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
32
32
  import { snapshotSkills } from "./skill-discovery.js";
33
- import { isDbAvailable, getMilestone } from "./gsd-db.js";
33
+ import { isDbAvailable, getMilestone, openDatabase } from "./gsd-db.js";
34
34
  import { hideFooter } from "./auto-dashboard.js";
35
35
  import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath, } from "./debug-logger.js";
36
36
  import { logWarning, logError } from "./workflow-logger.js";
@@ -48,34 +48,23 @@ import { resolveDefaultSessionModel } from "./preferences-models.js";
48
48
  * Returns false if the bootstrap aborted (e.g., guided flow returned,
49
49
  * concurrent session detected). Returns true when ready to dispatch.
50
50
  */
51
- /**
52
- * Open the project-root DB before the first deriveState call (#2841).
53
- * When auto-mode starts cold (no prior DB handle), state derivation that
54
- * touches DB-backed helpers (queue-order, task status) silently falls back
55
- * to markdown-only data, producing stale or incomplete state. Opening the
56
- * DB first ensures deriveState sees the full picture on its very first run.
57
- */
58
- async function openProjectDbIfPresent(basePath) {
51
+ /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
52
+ * Prevents the recursive dialog loop described in #1348 where
53
+ * bootstrapAutoSession showSmartEntry checkAutoStartAfterDiscuss startAuto
54
+ * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
55
+ let _consecutiveCompleteBootstraps = 0;
56
+ const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
57
+ export async function openProjectDbIfPresent(basePath) {
59
58
  const gsdDbPath = resolveProjectRootDbPath(basePath);
60
- if (!existsSync(gsdDbPath))
61
- return;
62
- if (isDbAvailable())
59
+ if (!existsSync(gsdDbPath) || isDbAvailable())
63
60
  return;
64
61
  try {
65
- const { openDatabase } = await import("./gsd-db.js");
66
62
  openDatabase(gsdDbPath);
67
63
  }
68
64
  catch (err) {
69
- /* non-fatal DB lifecycle block below will retry */
70
- logWarning("engine", `DB open failed: ${err instanceof Error ? err.message : String(err)}`);
65
+ logWarning("engine", `gsd-db: failed to open existing database: ${err instanceof Error ? err.message : String(err)}`);
71
66
  }
72
67
  }
73
- /** Guard: tracks consecutive bootstrap attempts that found phase === "complete".
74
- * Prevents the recursive dialog loop described in #1348 where
75
- * bootstrapAutoSession → showSmartEntry → checkAutoStartAfterDiscuss → startAuto
76
- * cycles indefinitely when the discuss workflow doesn't produce a milestone. */
77
- let _consecutiveCompleteBootstraps = 0;
78
- const MAX_CONSECUTIVE_COMPLETE_BOOTSTRAPS = 2;
79
68
  export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, requestedStepMode, deps) {
80
69
  const { shouldUseWorktreeIsolation, registerSigtermHandler, lockBase, buildResolver, } = deps;
81
70
  const lockResult = acquireSessionLock(base);
@@ -77,8 +77,9 @@ export function startUnitSupervision(sctx) {
77
77
  }
78
78
  }
79
79
  const estimateMinutes = taskEstimate ? parseEstimateMinutes(taskEstimate) : null;
80
+ const MAX_TIMEOUT_SCALE = 6; // Cap at 6x (60min task). Prevents 2h+ tasks from creating 120min+ timeout windows.
80
81
  const timeoutScale = estimateMinutes && estimateMinutes > 0
81
- ? Math.max(1, estimateMinutes / 10) // 10min task = 1x, 30min = 3x, 2h = 12x
82
+ ? Math.min(MAX_TIMEOUT_SCALE, Math.max(1, estimateMinutes / 10))
82
83
  : 1;
83
84
  const softTimeoutMs = (supervisor.soft_timeout_minutes ?? 0) * 60 * 1000 * timeoutScale;
84
85
  const idleTimeoutMs = (supervisor.idle_timeout_minutes ?? 0) * 60 * 1000; // idle not scaled — idle is idle
@@ -75,3 +75,20 @@ export function hasInteractiveToolInFlight() {
75
75
  export function clearInFlightTools() {
76
76
  inFlightTools.clear();
77
77
  }
78
+ // ─── Tool invocation error classification (#2883) ────────────────────────
79
+ /**
80
+ * Patterns that indicate a tool invocation failed due to malformed or truncated
81
+ * JSON arguments — as opposed to a normal business-logic error from the tool
82
+ * handler. When these errors occur, retrying the same unit will produce the same
83
+ * failure, so the retry loop must be broken.
84
+ */
85
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
86
+ /**
87
+ * Returns true if the error message indicates a tool invocation failure due to
88
+ * malformed/truncated arguments (as opposed to a normal tool execution error).
89
+ */
90
+ export function isToolInvocationError(errorMsg) {
91
+ if (!errorMsg)
92
+ return false;
93
+ return TOOL_INVOCATION_ERROR_RE.test(errorMsg);
94
+ }
@@ -14,7 +14,7 @@ import { atomicWriteSync } from "./atomic-write.js";
14
14
  import { execFileSync } from "node:child_process";
15
15
  import { safeCopy, safeCopyRecursive } from "./safe-fs.js";
16
16
  import { gsdRoot } from "./paths.js";
17
- import { createWorktree, removeWorktree, resolveGitDir, worktreePath, } from "./worktree-manager.js";
17
+ import { createWorktree, removeWorktree, resolveGitDir, worktreePath, isInsideWorktreesDir, } from "./worktree-manager.js";
18
18
  import { detectWorktreeName, nudgeGitBranchCache, } from "./worktree.js";
19
19
  import { MergeConflictError, readIntegrationBranch, RUNTIME_EXCLUSION_PATHS } from "./git-service.js";
20
20
  import { debugLog } from "./debug-logger.js";
@@ -1025,13 +1025,19 @@ export function teardownAutoWorktree(originalBasePath, milestoneId, opts = {}) {
1025
1025
  logWarning("reconcile", `Worktree directory still exists after teardown: ${wtDir}. ` +
1026
1026
  `This is likely an orphaned directory consuming disk space. ` +
1027
1027
  `Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`, { worktree: milestoneId });
1028
- // Attempt a direct filesystem removal as a fallback
1029
- try {
1030
- rmSync(wtDir, { recursive: true, force: true });
1028
+ // Attempt a direct filesystem removal as a fallback — but ONLY if the
1029
+ // path is safely inside .gsd/worktrees/ to prevent #2365 data loss.
1030
+ if (isInsideWorktreesDir(originalBasePath, wtDir)) {
1031
+ try {
1032
+ rmSync(wtDir, { recursive: true, force: true });
1033
+ }
1034
+ catch (err) {
1035
+ // Non-fatal — the warning above tells the user how to clean up
1036
+ logWarning("worktree", `worktree directory removal failed: ${err instanceof Error ? err.message : String(err)}`);
1037
+ }
1031
1038
  }
1032
- catch (err) {
1033
- // Non-fatal the warning above tells the user how to clean up
1034
- logWarning("worktree", `worktree directory removal failed: ${err instanceof Error ? err.message : String(err)}`);
1039
+ else {
1040
+ console.error(`[GSD] REFUSING fallback rmSync path is outside .gsd/worktrees/: ${wtDir}`);
1035
1041
  }
1036
1042
  }
1037
1043
  }
@@ -23,7 +23,7 @@ import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSes
23
23
  import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
24
24
  import { sendDesktopNotification } from "./notifications.js";
25
25
  import { getBudgetAlertLevel, getNewBudgetAlertLevel, getBudgetEnforcementAction, } from "./auto-budget.js";
26
- import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, } from "./auto-tool-tracking.js";
26
+ import { markToolStart as _markToolStart, markToolEnd as _markToolEnd, getOldestInFlightToolAgeMs as _getOldestInFlightToolAgeMs, clearInFlightTools, isToolInvocationError, } from "./auto-tool-tracking.js";
27
27
  import { closeoutUnit } from "./auto-unit-closeout.js";
28
28
  import { selectAndApplyModel, resolveModelId } from "./auto-model-selection.js";
29
29
  import { resetRoutingHistory, recordOutcome } from "./routing-history.js";
@@ -57,7 +57,7 @@ import { clearCmuxSidebar, logCmuxEvent, syncCmuxSidebar } from "../cmux/index.j
57
57
  import { startUnitSupervision } from "./auto-timers.js";
58
58
  import { runPostUnitVerification } from "./auto-verification.js";
59
59
  import { postUnitPreVerification, postUnitPostVerification, } from "./auto-post-unit.js";
60
- import { bootstrapAutoSession } from "./auto-start.js";
60
+ import { bootstrapAutoSession, openProjectDbIfPresent } from "./auto-start.js";
61
61
  import { autoLoop, resolveAgentEnd, resolveAgentEndCancelled, _resetPendingResolve, isSessionSwitchInFlight } from "./auto-loop.js";
62
62
  import { WorktreeResolver, } from "./worktree-resolver.js";
63
63
  import { reorderForCaching } from "./prompt-ordering.js";
@@ -192,6 +192,19 @@ export function markToolStart(toolCallId, toolName) {
192
192
  export function markToolEnd(toolCallId) {
193
193
  _markToolEnd(toolCallId);
194
194
  }
195
+ /**
196
+ * Record a tool invocation error on the current session (#2883).
197
+ * Called from tool_execution_end when a GSD tool fails with isError.
198
+ * Only stores the error if it matches the tool-invocation-error pattern
199
+ * (malformed/truncated JSON), not normal business-logic errors.
200
+ */
201
+ export function recordToolInvocationError(toolName, errorMsg) {
202
+ if (!s.active)
203
+ return;
204
+ if (isToolInvocationError(errorMsg)) {
205
+ s.lastToolInvocationError = `${toolName}: ${errorMsg}`;
206
+ }
207
+ }
195
208
  export function getOldestInFlightToolAgeMs() {
196
209
  return _getOldestInFlightToolAgeMs();
197
210
  }
@@ -909,6 +922,9 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
909
922
  ctx.ui.setFooter(hideFooter);
910
923
  ctx.ui.notify(s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", "info");
911
924
  restoreHookState(s.basePath);
925
+ // Open the project DB before rebuild/derive so resume uses DB-backed
926
+ // state instead of falling back to stale markdown parsing (#2940).
927
+ await openProjectDbIfPresent(s.basePath);
912
928
  try {
913
929
  await rebuildState(s.basePath);
914
930
  syncCmuxSidebar(loadEffectiveGSDPreferences()?.preferences, await deriveState(s.basePath));
@@ -1005,6 +1021,7 @@ const widgetStateAccessors = {
1005
1021
  getBasePath: () => s.basePath,
1006
1022
  isVerbose: () => s.verbose,
1007
1023
  isSessionSwitching: isSessionSwitchInFlight,
1024
+ getCurrentDispatchedModelId: () => s.currentDispatchedModelId,
1008
1025
  };
1009
1026
  // ─── Preconditions ────────────────────────────────────────────────────────────
1010
1027
  /**
@@ -486,28 +486,10 @@ export function registerDbTools(pi) {
486
486
  "Use the canonical name gsd_plan_milestone; gsd_milestone_plan is only an alias.",
487
487
  ],
488
488
  parameters: Type.Object({
489
+ // ── Core identification + content (required) ──────────────────────
489
490
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
490
491
  title: Type.String({ description: "Milestone title" }),
491
- status: Type.Optional(Type.String({ description: "Milestone status (defaults to active)" })),
492
- dependsOn: Type.Optional(Type.Array(Type.String(), { description: "Milestone dependencies" })),
493
492
  vision: Type.String({ description: "Milestone vision" }),
494
- successCriteria: Type.Array(Type.String(), { description: "Top-level success criteria bullets" }),
495
- keyRisks: Type.Array(Type.Object({
496
- risk: Type.String({ description: "Risk statement" }),
497
- whyItMatters: Type.String({ description: "Why the risk matters" }),
498
- }), { description: "Structured risk entries" }),
499
- proofStrategy: Type.Array(Type.Object({
500
- riskOrUnknown: Type.String({ description: "Risk or unknown to retire" }),
501
- retireIn: Type.String({ description: "Where it will be retired" }),
502
- whatWillBeProven: Type.String({ description: "What proof will be produced" }),
503
- }), { description: "Structured proof strategy entries" }),
504
- verificationContract: Type.String({ description: "Verification contract text" }),
505
- verificationIntegration: Type.String({ description: "Integration verification text" }),
506
- verificationOperational: Type.String({ description: "Operational verification text" }),
507
- verificationUat: Type.String({ description: "UAT verification text" }),
508
- definitionOfDone: Type.Array(Type.String(), { description: "Definition of done bullets" }),
509
- requirementCoverage: Type.String({ description: "Requirement coverage text" }),
510
- boundaryMapMarkdown: Type.String({ description: "Boundary map markdown block" }),
511
493
  slices: Type.Array(Type.Object({
512
494
  sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
513
495
  title: Type.String({ description: "Slice title" }),
@@ -520,6 +502,26 @@ export function registerDbTools(pi) {
520
502
  integrationClosure: Type.String({ description: "Slice integration closure" }),
521
503
  observabilityImpact: Type.String({ description: "Slice observability impact" }),
522
504
  }), { description: "Planned slices for the milestone" }),
505
+ // ── Enrichment metadata (optional — defaults to empty) ────────────
506
+ status: Type.Optional(Type.String({ description: "Milestone status (defaults to active)" })),
507
+ dependsOn: Type.Optional(Type.Array(Type.String(), { description: "Milestone dependencies" })),
508
+ successCriteria: Type.Optional(Type.Array(Type.String(), { description: "Top-level success criteria bullets" })),
509
+ keyRisks: Type.Optional(Type.Array(Type.Object({
510
+ risk: Type.String({ description: "Risk statement" }),
511
+ whyItMatters: Type.String({ description: "Why the risk matters" }),
512
+ }), { description: "Structured risk entries" })),
513
+ proofStrategy: Type.Optional(Type.Array(Type.Object({
514
+ riskOrUnknown: Type.String({ description: "Risk or unknown to retire" }),
515
+ retireIn: Type.String({ description: "Where it will be retired" }),
516
+ whatWillBeProven: Type.String({ description: "What proof will be produced" }),
517
+ }), { description: "Structured proof strategy entries" })),
518
+ verificationContract: Type.Optional(Type.String({ description: "Verification contract text" })),
519
+ verificationIntegration: Type.Optional(Type.String({ description: "Integration verification text" })),
520
+ verificationOperational: Type.Optional(Type.String({ description: "Operational verification text" })),
521
+ verificationUat: Type.Optional(Type.String({ description: "UAT verification text" })),
522
+ definitionOfDone: Type.Optional(Type.Array(Type.String(), { description: "Definition of done bullets" })),
523
+ requirementCoverage: Type.Optional(Type.String({ description: "Requirement coverage text" })),
524
+ boundaryMapMarkdown: Type.Optional(Type.String({ description: "Boundary map markdown block" })),
523
525
  }),
524
526
  execute: planMilestoneExecute,
525
527
  };
@@ -575,13 +577,10 @@ export function registerDbTools(pi) {
575
577
  "Use the canonical name gsd_plan_slice; gsd_slice_plan is only an alias.",
576
578
  ],
577
579
  parameters: Type.Object({
580
+ // ── Core identification + content (required) ──────────────────────
578
581
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
579
582
  sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
580
583
  goal: Type.String({ description: "Slice goal" }),
581
- successCriteria: Type.String({ description: "Slice success criteria block" }),
582
- proofLevel: Type.String({ description: "Slice proof level" }),
583
- integrationClosure: Type.String({ description: "Slice integration closure" }),
584
- observabilityImpact: Type.String({ description: "Slice observability impact" }),
585
584
  tasks: Type.Array(Type.Object({
586
585
  taskId: Type.String({ description: "Task ID (e.g. T01)" }),
587
586
  title: Type.String({ description: "Task title" }),
@@ -593,6 +592,11 @@ export function registerDbTools(pi) {
593
592
  expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
594
593
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
595
594
  }), { description: "Planned tasks for the slice" }),
595
+ // ── Enrichment metadata (optional — defaults to empty) ────────────
596
+ successCriteria: Type.Optional(Type.String({ description: "Slice success criteria block" })),
597
+ proofLevel: Type.Optional(Type.String({ description: "Slice proof level" })),
598
+ integrationClosure: Type.Optional(Type.String({ description: "Slice integration closure" })),
599
+ observabilityImpact: Type.Optional(Type.String({ description: "Slice observability impact" })),
596
600
  }),
597
601
  execute: planSliceExecute,
598
602
  };
@@ -674,8 +678,11 @@ export function registerDbTools(pi) {
674
678
  };
675
679
  }
676
680
  try {
681
+ // Coerce string items to objects for verificationEvidence (#3541).
682
+ const coerced = { ...params };
683
+ coerced.verificationEvidence = (params.verificationEvidence ?? []).map((v) => typeof v === "string" ? { command: v, exitCode: -1, verdict: "unknown (coerced from string)", durationMs: 0 } : v);
677
684
  const { handleCompleteTask } = await import("../tools/complete-task.js");
678
- const result = await handleCompleteTask(params, process.cwd());
685
+ const result = await handleCompleteTask(coerced, process.cwd());
679
686
  if ("error" in result) {
680
687
  return {
681
688
  content: [{ type: "text", text: `Error completing task: ${result.error}` }],
@@ -716,23 +723,28 @@ export function registerDbTools(pi) {
716
723
  "Idempotent — calling with the same params twice will upsert (INSERT OR REPLACE) without error.",
717
724
  ],
718
725
  parameters: Type.Object({
726
+ // ── Core identification + content (required) ──────────────────────
719
727
  taskId: Type.String({ description: "Task ID (e.g. T01)" }),
720
728
  sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
721
729
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
722
730
  oneLiner: Type.String({ description: "One-line summary of what was accomplished" }),
723
731
  narrative: Type.String({ description: "Detailed narrative of what happened during the task" }),
724
732
  verification: Type.String({ description: "What was verified and how — commands run, tests passed, behavior confirmed" }),
725
- deviations: Type.String({ description: "Deviations from the task plan, or 'None.'" }),
726
- knownIssues: Type.String({ description: "Known issues discovered but not fixed, or 'None.'" }),
727
- keyFiles: Type.Array(Type.String(), { description: "List of key files created or modified" }),
728
- keyDecisions: Type.Array(Type.String(), { description: "List of key decisions made during this task" }),
729
- blockerDiscovered: Type.Boolean({ description: "Whether a plan-invalidating blocker was discovered" }),
730
- verificationEvidence: Type.Array(Type.Object({
731
- command: Type.String({ description: "Verification command that was run" }),
732
- exitCode: Type.Number({ description: "Exit code of the command" }),
733
- verdict: Type.String({ description: "Pass/fail verdict (e.g. '✅ pass', '❌ fail')" }),
734
- durationMs: Type.Number({ description: "Duration of the command in milliseconds" }),
735
- }), { description: "Array of verification evidence entries" }),
733
+ // ── Enrichment metadata (optional defaults to empty) ────────────
734
+ deviations: Type.Optional(Type.String({ description: "Deviations from the task plan, or 'None.'" })),
735
+ knownIssues: Type.Optional(Type.String({ description: "Known issues discovered but not fixed, or 'None.'" })),
736
+ keyFiles: Type.Optional(Type.Array(Type.String(), { description: "List of key files created or modified" })),
737
+ keyDecisions: Type.Optional(Type.Array(Type.String(), { description: "List of key decisions made during this task" })),
738
+ blockerDiscovered: Type.Optional(Type.Boolean({ description: "Whether a plan-invalidating blocker was discovered" })),
739
+ verificationEvidence: Type.Optional(Type.Array(Type.Union([
740
+ Type.Object({
741
+ command: Type.String({ description: "Verification command that was run" }),
742
+ exitCode: Type.Number({ description: "Exit code of the command" }),
743
+ verdict: Type.String({ description: "Pass/fail verdict (e.g. '✅ pass', '❌ fail')" }),
744
+ durationMs: Type.Number({ description: "Duration of the command in milliseconds" }),
745
+ }),
746
+ Type.String({ description: "Fallback: verification summary string" }),
747
+ ]), { description: "Array of verification evidence entries" })),
736
748
  }),
737
749
  execute: taskCompleteExecute,
738
750
  };
@@ -748,8 +760,46 @@ export function registerDbTools(pi) {
748
760
  };
749
761
  }
750
762
  try {
763
+ // Coerce string items to objects for fields where LLMs sometimes pass
764
+ // plain strings instead of the expected { key, value } shape (#3541).
765
+ // Parses "key — value" or "key - value" format when possible.
766
+ const splitPair = (s) => {
767
+ const m = s.match(/^(.+?)\s*(?:—|-)\s+(.+)$/);
768
+ return m ? [m[1].trim(), m[2].trim()] : [s.trim(), ""];
769
+ };
770
+ const coerced = { ...params };
771
+ coerced.filesModified = (params.filesModified ?? []).map((f) => {
772
+ if (typeof f !== "string")
773
+ return f;
774
+ const [path, description] = splitPair(f);
775
+ return { path, description };
776
+ });
777
+ coerced.requires = (params.requires ?? []).map((r) => {
778
+ if (typeof r !== "string")
779
+ return r;
780
+ const [slice, provides] = splitPair(r);
781
+ return { slice, provides };
782
+ });
783
+ coerced.requirementsAdvanced = (params.requirementsAdvanced ?? []).map((r) => {
784
+ if (typeof r !== "string")
785
+ return r;
786
+ const [id, how] = splitPair(r);
787
+ return { id, how };
788
+ });
789
+ coerced.requirementsValidated = (params.requirementsValidated ?? []).map((r) => {
790
+ if (typeof r !== "string")
791
+ return r;
792
+ const [id, proof] = splitPair(r);
793
+ return { id, proof };
794
+ });
795
+ coerced.requirementsInvalidated = (params.requirementsInvalidated ?? []).map((r) => {
796
+ if (typeof r !== "string")
797
+ return r;
798
+ const [id, what] = splitPair(r);
799
+ return { id, what };
800
+ });
751
801
  const { handleCompleteSlice } = await import("../tools/complete-slice.js");
752
- const result = await handleCompleteSlice(params, process.cwd());
802
+ const result = await handleCompleteSlice(coerced, process.cwd());
753
803
  if ("error" in result) {
754
804
  return {
755
805
  content: [{ type: "text", text: `Error completing slice: ${result.error}` }],
@@ -789,44 +839,61 @@ export function registerDbTools(pi) {
789
839
  "Idempotent — calling with the same params twice will not crash.",
790
840
  ],
791
841
  parameters: Type.Object({
842
+ // ── Core identification + content (required) ──────────────────────
792
843
  sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
793
844
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
794
845
  sliceTitle: Type.String({ description: "Title of the slice" }),
795
846
  oneLiner: Type.String({ description: "One-line summary of what the slice accomplished" }),
796
847
  narrative: Type.String({ description: "Detailed narrative of what happened across all tasks" }),
797
848
  verification: Type.String({ description: "What was verified across all tasks" }),
798
- deviations: Type.String({ description: "Deviations from the slice plan, or 'None.'" }),
799
- knownLimitations: Type.String({ description: "Known limitations or gaps, or 'None.'" }),
800
- followUps: Type.String({ description: "Follow-up work discovered during execution, or 'None.'" }),
801
- keyFiles: Type.Array(Type.String(), { description: "Key files created or modified" }),
802
- keyDecisions: Type.Array(Type.String(), { description: "Key decisions made during this slice" }),
803
- patternsEstablished: Type.Array(Type.String(), { description: "Patterns established by this slice" }),
804
- observabilitySurfaces: Type.Array(Type.String(), { description: "Observability surfaces added" }),
805
- provides: Type.Array(Type.String(), { description: "What this slice provides to downstream slices" }),
806
- requirementsSurfaced: Type.Array(Type.String(), { description: "New requirements surfaced" }),
807
- drillDownPaths: Type.Array(Type.String(), { description: "Paths to task summaries for drill-down" }),
808
- affects: Type.Array(Type.String(), { description: "Downstream slices affected" }),
809
- requirementsAdvanced: Type.Array(Type.Object({
810
- id: Type.String({ description: "Requirement ID" }),
811
- how: Type.String({ description: "How it was advanced" }),
812
- }), { description: "Requirements advanced by this slice" }),
813
- requirementsValidated: Type.Array(Type.Object({
814
- id: Type.String({ description: "Requirement ID" }),
815
- proof: Type.String({ description: "What proof validates it" }),
816
- }), { description: "Requirements validated by this slice" }),
817
- requirementsInvalidated: Type.Array(Type.Object({
818
- id: Type.String({ description: "Requirement ID" }),
819
- what: Type.String({ description: "What changed" }),
820
- }), { description: "Requirements invalidated or re-scoped" }),
821
- filesModified: Type.Array(Type.Object({
822
- path: Type.String({ description: "File path" }),
823
- description: Type.String({ description: "What changed" }),
824
- }), { description: "Files modified with descriptions" }),
825
- requires: Type.Array(Type.Object({
826
- slice: Type.String({ description: "Dependency slice ID" }),
827
- provides: Type.String({ description: "What was consumed from it" }),
828
- }), { description: "Upstream slice dependencies consumed" }),
829
849
  uatContent: Type.String({ description: "UAT test content (markdown body)" }),
850
+ // ── Enrichment metadata (optional — defaults to empty) ────────────
851
+ deviations: Type.Optional(Type.String({ description: "Deviations from the slice plan, or 'None.'" })),
852
+ knownLimitations: Type.Optional(Type.String({ description: "Known limitations or gaps, or 'None.'" })),
853
+ followUps: Type.Optional(Type.String({ description: "Follow-up work discovered during execution, or 'None.'" })),
854
+ keyFiles: Type.Optional(Type.Array(Type.String(), { description: "Key files created or modified" })),
855
+ keyDecisions: Type.Optional(Type.Array(Type.String(), { description: "Key decisions made during this slice" })),
856
+ patternsEstablished: Type.Optional(Type.Array(Type.String(), { description: "Patterns established by this slice" })),
857
+ observabilitySurfaces: Type.Optional(Type.Array(Type.String(), { description: "Observability surfaces added" })),
858
+ provides: Type.Optional(Type.Array(Type.String(), { description: "What this slice provides to downstream slices" })),
859
+ requirementsSurfaced: Type.Optional(Type.Array(Type.String(), { description: "New requirements surfaced" })),
860
+ drillDownPaths: Type.Optional(Type.Array(Type.String(), { description: "Paths to task summaries for drill-down" })),
861
+ affects: Type.Optional(Type.Array(Type.String(), { description: "Downstream slices affected" })),
862
+ requirementsAdvanced: Type.Optional(Type.Array(Type.Union([
863
+ Type.Object({
864
+ id: Type.String({ description: "Requirement ID" }),
865
+ how: Type.String({ description: "How it was advanced" }),
866
+ }),
867
+ Type.String({ description: "Fallback: 'ID — how' string" }),
868
+ ]), { description: "Requirements advanced by this slice" })),
869
+ requirementsValidated: Type.Optional(Type.Array(Type.Union([
870
+ Type.Object({
871
+ id: Type.String({ description: "Requirement ID" }),
872
+ proof: Type.String({ description: "What proof validates it" }),
873
+ }),
874
+ Type.String({ description: "Fallback: 'ID — proof' string" }),
875
+ ]), { description: "Requirements validated by this slice" })),
876
+ requirementsInvalidated: Type.Optional(Type.Array(Type.Union([
877
+ Type.Object({
878
+ id: Type.String({ description: "Requirement ID" }),
879
+ what: Type.String({ description: "What changed" }),
880
+ }),
881
+ Type.String({ description: "Fallback: 'ID — what' string" }),
882
+ ]), { description: "Requirements invalidated or re-scoped" })),
883
+ filesModified: Type.Optional(Type.Array(Type.Union([
884
+ Type.Object({
885
+ path: Type.String({ description: "File path" }),
886
+ description: Type.String({ description: "What changed" }),
887
+ }),
888
+ Type.String({ description: "Fallback: file path string" }),
889
+ ]), { description: "Files modified with descriptions" })),
890
+ requires: Type.Optional(Type.Array(Type.Union([
891
+ Type.Object({
892
+ slice: Type.String({ description: "Dependency slice ID" }),
893
+ provides: Type.String({ description: "What was consumed from it" }),
894
+ }),
895
+ Type.String({ description: "Fallback: slice ID string" }),
896
+ ]), { description: "Upstream slice dependencies consumed" })),
830
897
  }),
831
898
  execute: sliceCompleteExecute,
832
899
  };
@@ -912,8 +979,11 @@ export function registerDbTools(pi) {
912
979
  };
913
980
  }
914
981
  try {
982
+ // ── Input sanitization: normalize markdown parameters (#3013) ──────
983
+ const { sanitizeCompleteMilestoneParams } = await import("./sanitize-complete-milestone.js");
984
+ const sanitized = sanitizeCompleteMilestoneParams(params);
915
985
  const { handleCompleteMilestone } = await import("../tools/complete-milestone.js");
916
- const result = await handleCompleteMilestone(params, process.cwd());
986
+ const result = await handleCompleteMilestone(sanitized, process.cwd());
917
987
  if ("error" in result) {
918
988
  return {
919
989
  content: [{ type: "text", text: `Error completing milestone: ${result.error}` }],
@@ -951,19 +1021,21 @@ export function registerDbTools(pi) {
951
1021
  "On success, returns summaryPath where the MILESTONE-SUMMARY.md was written.",
952
1022
  ],
953
1023
  parameters: Type.Object({
1024
+ // ── Core identification + content (required) ──────────────────────
954
1025
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
955
1026
  title: Type.String({ description: "Milestone title" }),
956
1027
  oneLiner: Type.String({ description: "One-sentence summary of what the milestone achieved" }),
957
1028
  narrative: Type.String({ description: "Detailed narrative of what happened during the milestone" }),
958
- successCriteriaResults: Type.String({ description: "Markdown detailing how each success criterion was met or not met" }),
959
- definitionOfDoneResults: Type.String({ description: "Markdown detailing how each definition-of-done item was met" }),
960
- requirementOutcomes: Type.String({ description: "Markdown detailing requirement status transitions with evidence" }),
961
- keyDecisions: Type.Array(Type.String(), { description: "Key architectural/pattern decisions made during the milestone" }),
962
- keyFiles: Type.Array(Type.String(), { description: "Key files created or modified during the milestone" }),
963
- lessonsLearned: Type.Array(Type.String(), { description: "Lessons learned during the milestone" }),
1029
+ verificationPassed: Type.Boolean({ description: "Must be true confirms that code change verification, success criteria, and definition of done checks all passed before completion" }),
1030
+ // ── Enrichment metadata (optional defaults to empty) ────────────
1031
+ successCriteriaResults: Type.Optional(Type.String({ description: "Markdown detailing how each success criterion was met or not met" })),
1032
+ definitionOfDoneResults: Type.Optional(Type.String({ description: "Markdown detailing how each definition-of-done item was met" })),
1033
+ requirementOutcomes: Type.Optional(Type.String({ description: "Markdown detailing requirement status transitions with evidence" })),
1034
+ keyDecisions: Type.Optional(Type.Array(Type.String(), { description: "Key architectural/pattern decisions made during the milestone" })),
1035
+ keyFiles: Type.Optional(Type.Array(Type.String(), { description: "Key files created or modified during the milestone" })),
1036
+ lessonsLearned: Type.Optional(Type.Array(Type.String(), { description: "Lessons learned during the milestone" })),
964
1037
  followUps: Type.Optional(Type.String({ description: "Follow-up items for future milestones" })),
965
1038
  deviations: Type.Optional(Type.String({ description: "Deviations from the original plan" })),
966
- verificationPassed: Type.Boolean({ description: "Must be true — confirms that code change verification, success criteria, and definition of done checks all passed before completion" }),
967
1039
  }),
968
1040
  execute: milestoneCompleteExecute,
969
1041
  };
@@ -26,6 +26,19 @@ export function resolveProjectRootDbPath(basePath) {
26
26
  const projectRoot = basePath.slice(0, fwdIdx);
27
27
  return join(projectRoot, ".gsd", "gsd.db");
28
28
  }
29
+ // External-state layout: ~/.gsd/projects/<hash>/worktrees/<MID>/...
30
+ // Resolve to ~/.gsd/projects/<hash>/gsd.db (the canonical project DB) (#2952).
31
+ // Must be checked before the generic symlink-resolved handler: both match
32
+ // /.gsd/projects/<hash>/worktrees/ but require different resolution targets.
33
+ const extRe = /[/\\]\.gsd[/\\]projects[/\\][a-f0-9]+[/\\]worktrees(?:[/\\]|$)/;
34
+ const extMatch = extRe.exec(basePath);
35
+ if (extMatch) {
36
+ const matchStr = extMatch[0];
37
+ // Find the "/worktrees" portion within the match and slice up to it
38
+ const wtIdx = matchStr.search(/[/\\]worktrees(?:[/\\]|$)/);
39
+ const projectStateRoot = basePath.slice(0, extMatch.index + wtIdx);
40
+ return join(projectStateRoot, "gsd.db");
41
+ }
29
42
  // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/M001/...
30
43
  // The project root is everything before /.gsd/projects/ (#2517)
31
44
  const symlinkMarker = `${sep}.gsd${sep}projects${sep}`;