gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.800ece0

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 (349) hide show
  1. package/dist/mcp-server.js +6 -2
  2. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  3. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  4. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +2 -0
  5. package/dist/resources/extensions/gsd/auto/loop.js +2 -2
  6. package/dist/resources/extensions/gsd/auto/phases.js +48 -5
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  8. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  9. package/dist/resources/extensions/gsd/auto/types.js +2 -0
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  15. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  16. package/dist/resources/extensions/gsd/auto-start.js +175 -12
  17. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  18. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  19. package/dist/resources/extensions/gsd/auto.js +21 -15
  20. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  22. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  23. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -1
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  27. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -2
  29. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  31. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  32. package/dist/resources/extensions/gsd/detection.js +1 -1
  33. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  35. package/dist/resources/extensions/gsd/doctor.js +2 -1
  36. package/dist/resources/extensions/gsd/files.js +17 -0
  37. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  38. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  39. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  40. package/dist/resources/extensions/gsd/index.js +1 -1
  41. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  42. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  43. package/dist/resources/extensions/gsd/notification-overlay.js +1 -1
  44. package/dist/resources/extensions/gsd/notification-widget.js +2 -1
  45. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
  46. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  47. package/dist/resources/extensions/gsd/pre-execution-checks.js +26 -5
  48. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  49. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  50. package/dist/resources/extensions/gsd/preferences.js +9 -2
  51. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  52. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  53. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  54. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  56. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  57. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  59. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  60. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  61. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  62. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  63. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  64. package/dist/resources/extensions/gsd/quick.js +19 -15
  65. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  66. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  67. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  68. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  69. package/dist/resources/extensions/gsd/state.js +115 -28
  70. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  71. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  72. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  73. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  74. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  75. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  76. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  77. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  78. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  79. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  80. package/dist/resources/extensions/gsd/undo.js +3 -2
  81. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  82. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  83. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  84. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  85. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  86. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  87. package/dist/resources/extensions/gsd/worktree.js +9 -0
  88. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  89. package/dist/resources/extensions/subagent/agents.js +19 -5
  90. package/dist/web/standalone/.next/BUILD_ID +1 -1
  91. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
  92. package/dist/web/standalone/.next/build-manifest.json +3 -3
  93. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  94. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  96. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +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 +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  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 +1 -1
  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/index.html +1 -1
  112. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
  119. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  122. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  123. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  124. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  125. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  126. package/package.json +1 -1
  127. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  129. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  131. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  133. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  148. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  149. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  150. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  151. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  152. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  153. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  154. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  155. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  156. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  157. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  158. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  159. package/packages/pi-tui/dist/components/image.js +4 -0
  160. package/packages/pi-tui/dist/components/image.js.map +1 -1
  161. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  162. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  163. package/packages/pi-tui/dist/components/image.test.js +32 -0
  164. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  165. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  166. package/packages/pi-tui/dist/tui.js +3 -1
  167. package/packages/pi-tui/dist/tui.js.map +1 -1
  168. package/packages/pi-tui/src/components/image.test.ts +36 -0
  169. package/packages/pi-tui/src/components/image.ts +5 -0
  170. package/packages/pi-tui/src/tui.ts +3 -1
  171. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  172. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  173. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
  174. package/src/resources/extensions/gsd/auto/loop.ts +2 -2
  175. package/src/resources/extensions/gsd/auto/phases.ts +68 -3
  176. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  177. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  178. package/src/resources/extensions/gsd/auto/types.ts +5 -0
  179. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  180. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  181. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  182. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  183. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  184. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  185. package/src/resources/extensions/gsd/auto-start.ts +188 -10
  186. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  187. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  188. package/src/resources/extensions/gsd/auto.ts +19 -8
  189. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  190. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  191. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  192. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  193. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  194. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
  195. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  196. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  197. package/src/resources/extensions/gsd/commands/handlers/core.ts +26 -2
  198. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  199. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  200. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  201. package/src/resources/extensions/gsd/detection.ts +1 -1
  202. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  203. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  204. package/src/resources/extensions/gsd/doctor.ts +2 -1
  205. package/src/resources/extensions/gsd/files.ts +19 -0
  206. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  207. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  208. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  209. package/src/resources/extensions/gsd/index.ts +1 -0
  210. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  211. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  212. package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
  213. package/src/resources/extensions/gsd/notification-widget.ts +2 -1
  214. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
  215. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  216. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -7
  217. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  218. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  219. package/src/resources/extensions/gsd/preferences.ts +9 -2
  220. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  221. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  222. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  223. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  224. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  225. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  226. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  227. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  228. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  229. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  230. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  231. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  232. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  233. package/src/resources/extensions/gsd/quick.ts +20 -15
  234. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  235. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  236. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  237. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  238. package/src/resources/extensions/gsd/state.ts +115 -26
  239. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  240. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  241. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  242. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  243. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  244. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  245. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  246. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  247. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  248. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  249. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  250. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  251. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  252. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  253. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
  254. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  255. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
  256. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  257. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  258. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  259. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  260. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  261. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  262. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  263. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  264. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  265. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  266. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
  267. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  268. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  269. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
  270. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  271. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  272. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  273. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  274. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +284 -20
  275. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  276. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  277. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  278. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  279. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  280. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  281. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  282. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  283. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  284. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  285. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  286. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  287. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  288. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  289. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  290. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  291. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  292. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  293. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  294. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  295. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  296. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  297. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  298. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  299. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  300. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  301. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  302. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  303. package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
  304. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  305. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  306. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  307. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  308. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  309. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  310. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  311. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  312. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  313. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  314. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  315. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  316. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  317. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  318. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  319. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  320. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  321. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  322. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  323. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  324. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  325. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  326. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  327. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  328. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  329. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  330. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  331. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  332. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  333. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  334. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  335. package/src/resources/extensions/gsd/types.ts +4 -0
  336. package/src/resources/extensions/gsd/undo.ts +3 -2
  337. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  338. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  339. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  340. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  341. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  342. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  343. package/src/resources/extensions/gsd/worktree.ts +10 -0
  344. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  345. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  346. package/src/resources/extensions/subagent/agents.ts +30 -6
  347. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  348. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → E0hBt4ifuG7QBbhUR5-6U}/_buildManifest.js +0 -0
  349. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → E0hBt4ifuG7QBbhUR5-6U}/_ssgManifest.js +0 -0
@@ -46,11 +46,12 @@ reason: "<reason>"
46
46
  Remove the `{ID}-PARKED.md` file from the milestone directory to reactivate it.
47
47
 
48
48
  ### Skip a slice
49
- Mark a slice as skipped so auto-mode advances past it without executing. Use the `gsd_skip_slice` tool:
49
+ Mark a slice as skipped so auto-mode advances past it without executing. **You MUST call the `gsd_skip_slice` tool** — editing the roadmap markdown alone is NOT sufficient because auto-mode reads slice status from the database, not the roadmap file:
50
50
  ```
51
51
  gsd_skip_slice({ milestoneId: "M003", sliceId: "S02", reason: "Descoped — feature moved to M005" })
52
52
  ```
53
53
  Skipped slices are treated as closed by the state machine (like "complete" but distinct). Use when a slice is no longer needed or has been superseded. The slice data is preserved for reference.
54
+ **Do NOT** just check the slice checkbox in the roadmap — this does not update the DB and auto-mode will resume the slice.
54
55
 
55
56
  ### Discard a milestone
56
57
  **Permanently** delete a milestone directory and prune it from QUEUE-ORDER.json. **Always confirm with the user before discarding.** Warn explicitly if the milestone has completed work.
@@ -131,8 +131,8 @@ Templates showing the expected format for each artifact type are in:
131
131
  - `/gsd status` - progress dashboard overlay
132
132
  - `/gsd queue` - queue future milestones (safe while auto-mode is running)
133
133
  - `/gsd quick <task>` - quick task with GSD guarantees (atomic commits, state tracking) but no milestone ceremony
134
- - `Ctrl+Alt+G` - toggle dashboard overlay
135
- - `Ctrl+Alt+B` - show shell processes
134
+ - `{{shortcutDashboard}}` - toggle dashboard overlay
135
+ - `{{shortcutShell}}` - show shell processes
136
136
 
137
137
  ## Execution Heuristics
138
138
 
@@ -1,48 +1,81 @@
1
- You are executing GSD auto-mode.
1
+ # Milestone Validation Parallel Review
2
2
 
3
- ## UNIT: Validate Milestone {{milestoneId}} ("{{milestoneTitle}}")
3
+ You are the validation orchestrator for **{{milestoneId}} {{milestoneTitle}}**.
4
4
 
5
5
  ## Working Directory
6
6
 
7
7
  Your working directory is `{{workingDirectory}}`. All file reads, writes, and shell commands MUST operate relative to this directory. Do NOT `cd` to any other directory.
8
8
 
9
- ## Your Role in the Pipeline
9
+ ## Mission
10
10
 
11
- All slices are done. Before the milestone can be completed, you must validate that the planned work was delivered as specified. Compare the roadmap's success criteria and slice definitions against the actual slice summaries and UAT results. This is a reconciliation gate — catch gaps, regressions, or missing deliverables before the milestone is sealed.
11
+ Dispatch 3 independent parallel reviewers, then synthesize their findings into the final VALIDATION verdict.
12
12
 
13
13
  This is remediation round {{remediationRound}}. If this is round 0, this is the first validation pass. If > 0, prior validation found issues and remediation slices were added and executed — verify those remediation slices resolved the issues.
14
14
 
15
+ ## Context
16
+
15
17
  All relevant context has been preloaded below — the roadmap, all slice summaries, UAT results, requirements, decisions, and project context are inlined. Start working immediately without re-reading these files.
16
18
 
17
19
  {{inlinedContext}}
18
20
 
19
- {{skillActivation}}
21
+ ## Execution Protocol
22
+
23
+ ### Step 1 — Dispatch Parallel Reviewers
24
+
25
+ Call `subagent` with `tasks: [...]` containing ALL THREE reviewers simultaneously:
26
+
27
+ **Reviewer A — Requirements Coverage**
28
+ Prompt: "Review milestone {{milestoneId}} requirements coverage. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/REQUIREMENTS.md` (or equivalent requirements file). For each requirement, check the slice SUMMARY files in `.gsd/{{milestoneId}}/` to determine if it is: COVERED (clearly demonstrated), PARTIAL (mentioned but not fully demonstrated), or MISSING (no evidence). Output a markdown table with columns: Requirement | Status | Evidence. End with a one-line verdict: PASS if all covered, NEEDS-ATTENTION if partials exist, FAIL if any missing."
29
+
30
+ **Reviewer B — Cross-Slice Integration**
31
+ Prompt: "Review milestone {{milestoneId}} cross-slice integration. Working directory: {{workingDirectory}}. Read `{{roadmapPath}}` and find the boundary map (produces/consumes contracts). For each boundary, check that the producing slice's SUMMARY confirms it produced the artifact, and the consuming slice's SUMMARY confirms it consumed it. Output a markdown table: Boundary | Producer Summary | Consumer Summary | Status. End with a one-line verdict: PASS if all boundaries honored, NEEDS-ATTENTION if any gaps."
32
+
33
+ **Reviewer C — UAT & Acceptance Criteria**
34
+ Prompt: "Review milestone {{milestoneId}} UAT and acceptance criteria. Working directory: {{workingDirectory}}. Read `.gsd/{{milestoneId}}/CONTEXT.md` for acceptance criteria. Check for UAT-RESULT files in each slice directory. Verify each acceptance criterion maps to either a passing UAT result or clear SUMMARY evidence. Output a checklist: [ ] Criterion | Evidence. End with a one-line verdict: PASS if all criteria met, NEEDS-ATTENTION if gaps exist."
35
+
36
+ ### Step 2 — Synthesize Findings
20
37
 
21
- ## Validation Steps
38
+ After all reviewers complete, aggregate their verdicts:
39
+ - If ALL reviewers say PASS → overall verdict: `pass`
40
+ - If any reviewer says NEEDS-ATTENTION → overall verdict: `needs-attention`
41
+ - If any reviewer says FAIL → overall verdict: `needs-remediation`
22
42
 
23
- 1. For each **success criterion** in `{{roadmapPath}}`, check whether slice summaries and UAT results provide evidence that it was met. Record pass/fail per criterion.
24
- 2. For each **slice** in the roadmap, verify its demo/deliverable claim against its summary. Flag any slice whose summary does not substantiate its claimed output.
25
- 3. Check **cross-slice integration points** — do boundary map entries (produces/consumes) align with what was actually built?
26
- 4. Check **requirement coverage** — are all active requirements addressed by at least one slice?
27
- 5. If **Verification Classes** are provided in the inlined context above, check each non-empty class:
28
- - For each verification class (Contract, Integration, Operational, UAT), determine whether slice summaries, UAT results, or observable behavior provide evidence that this verification tier was addressed.
29
- - Document the compliance status of each class in a dedicated verification classes section.
30
- - If `Operational` verification is non-empty and no evidence of operational verification exists, flag this explicitly — it means planned operational checks (migrations, deployments, runtime verification) were not proven.
31
- - A milestone with unaddressed verification classes may still pass if the gaps are minor, but the gaps MUST be documented in the Deferred Work Inventory.
32
- 6. Determine a verdict:
33
- - `pass` — all criteria met, all slices delivered, no gaps
34
- - `needs-attention` — minor gaps that do not block completion (document them)
35
- - `needs-remediation` — material gaps found; remediation slices must be added to the roadmap
43
+ ### Step 3 Write VALIDATION File
36
44
 
37
- ## Persist Validation
45
+ Write to `{{validationPath}}`:
38
46
 
39
- **Persist validation results through `gsd_validate_milestone`.** Call it with: `milestoneId`, `verdict`, `remediationRound`, `successCriteriaChecklist`, `sliceDeliveryAudit`, `crossSliceIntegration`, `requirementCoverage`, `verificationClasses` (when non-empty), `verdictRationale`, and `remediationPlan` (if verdict is `needs-remediation`). The tool writes the validation to the DB and renders VALIDATION.md to disk.
47
+ ```markdown
48
+ ---
49
+ verdict: <pass|needs-attention|needs-remediation>
50
+ remediation_round: {{remediationRound}}
51
+ reviewers: 3
52
+ ---
53
+
54
+ # Milestone Validation: {{milestoneId}}
55
+
56
+ ## Reviewer A — Requirements Coverage
57
+ <paste Reviewer A output>
58
+
59
+ ## Reviewer B — Cross-Slice Integration
60
+ <paste Reviewer B output>
61
+
62
+ ## Reviewer C — UAT & Acceptance Criteria
63
+ <paste Reviewer C output>
64
+
65
+ ## Synthesis
66
+ <2-3 sentences summarizing overall findings and verdict rationale>
67
+
68
+ ## Remediation Plan
69
+ <if verdict is not pass: specific actions required>
70
+ ```
40
71
 
41
72
  **DB access safety:** Do NOT query `.gsd/gsd.db` directly via `sqlite3` or `node -e require('better-sqlite3')` — the engine owns the WAL connection. Use `gsd_milestone_status` to read milestone and slice state. All data you need is already inlined in the context above or accessible via the `gsd_*` tools. Direct DB access corrupts the WAL and bypasses tool-level validation.
42
73
 
43
74
  If verdict is `needs-remediation`:
44
- - After calling `gsd_validate_milestone`, use `gsd_reassess_roadmap` to add remediation slices. Pass `milestoneId`, a synthetic `completedSliceId` (e.g. "VALIDATION"), `verdict: "roadmap-adjusted"`, `assessment` text, and `sliceChanges` with the new slices in the `added` array. The tool persists the changes to the DB and re-renders ROADMAP.md.
45
- - These remediation slices will be planned and executed before validation re-runs.
75
+ - Add new slices to `{{roadmapPath}}` with unchecked `[ ]` status
76
+ - These slices will be planned and executed before validation re-runs
77
+
78
+ **You MUST write `{{validationPath}}` before finishing.**
46
79
 
47
80
  **File system safety:** When scanning milestone directories for evidence, use `ls` or `find` to list directory contents first — never pass a directory path (e.g. `tasks/`, `slices/`) directly to the `read` tool. The `read` tool only accepts file paths, not directories.
48
81
 
@@ -192,28 +192,33 @@ export async function handleQuick(
192
192
  const taskDirRel = `.gsd/quick/${taskNum}-${slug}`;
193
193
  const date = new Date().toISOString().split("T")[0];
194
194
 
195
- // Create git branch for the quick task
195
+ // Create git branch for the quick task (unless isolation:none — #3337)
196
196
  const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
197
197
  const git = new GitServiceImpl(basePath, gitPrefs);
198
198
  const branchName = `gsd/quick/${taskNum}-${slug}`;
199
199
  let originalBranch = git.getCurrentBranch();
200
200
 
201
+ const { getIsolationMode } = await import("./preferences.js");
202
+ const usesBranch = getIsolationMode() !== "none";
203
+
201
204
  let branchCreated = false;
202
- try {
203
- const current = originalBranch;
204
- if (current !== branchName) {
205
- // Auto-commit any dirty state before switching
206
- try {
207
- git.autoCommit("quick-task", `Q${taskNum}`, []);
208
- } catch { /* nothing to commit — fine */ }
209
-
210
- runGit(basePath, ["checkout", "-b", branchName]);
211
- branchCreated = true;
205
+ if (usesBranch) {
206
+ try {
207
+ const current = originalBranch;
208
+ if (current !== branchName) {
209
+ // Auto-commit any dirty state before switching
210
+ try {
211
+ git.autoCommit("quick-task", `Q${taskNum}`, []);
212
+ } catch { /* nothing to commit — fine */ }
213
+
214
+ runGit(basePath, ["checkout", "-b", branchName]);
215
+ branchCreated = true;
216
+ }
217
+ } catch (err) {
218
+ // Branch creation failed — continue on current branch
219
+ const message = err instanceof Error ? err.message : String(err);
220
+ ctx.ui.notify(`Could not create branch ${branchName}: ${message}. Working on current branch.`, "warning");
212
221
  }
213
- } catch (err) {
214
- // Branch creation failed — continue on current branch
215
- const message = err instanceof Error ? err.message : String(err);
216
- ctx.ui.notify(`Could not create branch ${branchName}: ${message}. Working on current branch.`, "warning");
217
222
  }
218
223
 
219
224
  const actualBranch = branchCreated ? branchName : git.getCurrentBranch();
@@ -131,6 +131,24 @@ export function isGraphAmbiguous(graph: DerivedTaskNode[]): boolean {
131
131
  );
132
132
  }
133
133
 
134
+ /**
135
+ * Returns tasks that are missing IO annotations (no inputFiles and no outputFiles).
136
+ * These tasks prevent parallel dispatch by making the graph ambiguous.
137
+ * Used to surface actionable diagnostics when parallel execution falls back to sequential.
138
+ */
139
+ export function getMissingAnnotationTasks(
140
+ graph: DerivedTaskNode[],
141
+ ): Array<{ id: string; title: string }> {
142
+ return graph
143
+ .filter(
144
+ (node) =>
145
+ !node.done &&
146
+ node.inputFiles.length === 0 &&
147
+ node.outputFiles.length === 0,
148
+ )
149
+ .map((node) => ({ id: node.id, title: node.title }));
150
+ }
151
+
134
152
  /**
135
153
  * Detect deadlock: no tasks are ready and none are in-flight, yet incomplete
136
154
  * tasks remain. This indicates a circular dependency or impossible state.
@@ -66,6 +66,17 @@ function parseTableSlices(section: string): RoadmapSliceEntry[] {
66
66
  const lines = section.split("\n");
67
67
  const slices: RoadmapSliceEntry[] = [];
68
68
 
69
+ // Detect dependency column index from the header row (#3383, #3336).
70
+ // Only parse deps from this column (or cells with explicit "depends"/"deps" keywords).
71
+ let depColumnIndex = -1;
72
+ for (const line of lines) {
73
+ if (!line.includes("|")) continue;
74
+ if (/S\d+/.test(line)) break; // reached data rows
75
+ const headerCells = line.split("|").map(c => c.trim()).filter(Boolean);
76
+ depColumnIndex = headerCells.findIndex(c => /^(depends|deps|depend)/i.test(c));
77
+ if (depColumnIndex >= 0) break;
78
+ }
79
+
69
80
  for (const line of lines) {
70
81
  // Skip non-table lines, separator lines (|---|---|), and header rows
71
82
  if (!line.includes("|")) continue;
@@ -95,12 +106,17 @@ function parseTableSlices(section: string): RoadmapSliceEntry[] {
95
106
  if (/\bmedium\b/.test(cellLower) || /\bmed\b/.test(cellLower)) { risk = "medium"; break; }
96
107
  }
97
108
 
98
- // Extract dependencies from cells containing S-prefixed IDs (excluding the slice's own ID)
109
+ // Extract dependencies only from the dependency column or cells with
110
+ // explicit "depends"/"deps" keywords — never from title cells (#3383).
99
111
  let depends: string[] = [];
100
- for (const cell of cells) {
101
- if (/depends|deps/i.test(cell) || (cell.match(/S\d+/g)?.length ?? 0) > 0) {
102
- const depIds = (cell.match(/S\d+/g) ?? []).filter(d => d !== id);
103
- if (depIds.length > 0 || /none|—|-/i.test(cell)) {
112
+ if (depColumnIndex >= 0 && cells[depColumnIndex]) {
113
+ const depCell = cells[depColumnIndex]!;
114
+ const depIds = (depCell.match(/S\d+/g) ?? []).filter(d => d !== id);
115
+ depends = expandDependencies(depIds);
116
+ } else {
117
+ for (const cell of cells) {
118
+ if (/depends|deps/i.test(cell)) {
119
+ const depIds = (cell.match(/S\d+/g) ?? []).filter(d => d !== id);
104
120
  depends = expandDependencies(depIds);
105
121
  break;
106
122
  }
@@ -54,12 +54,12 @@ const VALIDATORS: Record<string, ContentValidatorFn> = {
54
54
  function validatePlanSlice(content: string): ContentViolation[] {
55
55
  const violations: ContentViolation[] = [];
56
56
 
57
- // Must have at least 2 task entries (checkbox pattern)
57
+ // Must have at least 1 task entry — single-task slices are valid (#3649)
58
58
  const taskCount = (content.match(/- \[[ x]\] \*\*T\d+/g) || []).length;
59
- if (taskCount < 2) {
59
+ if (taskCount < 1) {
60
60
  violations.push({
61
61
  severity: "warning",
62
- reason: `Slice plan has only ${taskCount} task(s) — expected at least 2`,
62
+ reason: `Slice plan has ${taskCount} task(s) — expected at least 1`,
63
63
  });
64
64
  }
65
65
 
@@ -288,6 +288,20 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
288
288
  const gsdDir = gsdRoot(basePath);
289
289
  const lockTarget = effectiveLockTarget(gsdDir);
290
290
 
291
+ // #3218: Pre-flight stale lock cleanup — if the .lock/ directory exists but
292
+ // no auto.lock metadata is present (or the PID is dead), remove the lock
293
+ // directory before attempting acquisition. This prevents the 30-min stale
294
+ // window from blocking /gsd after crashes, SIGKILL, or laptop sleep.
295
+ const lockDir = lockTarget + ".lock";
296
+ if (existsSync(lockDir)) {
297
+ const existingData = readExistingLockData(lp);
298
+ const isOrphan = !existingData || (existingData.pid && !isPidAlive(existingData.pid));
299
+ if (isOrphan) {
300
+ try { rmSync(lockDir, { recursive: true, force: true }); } catch { /* best-effort */ }
301
+ try { if (existsSync(lp)) unlinkSync(lp); } catch { /* best-effort */ }
302
+ }
303
+ }
304
+
291
305
  try {
292
306
  // Try to acquire an exclusive OS-level lock on the lock target.
293
307
  // We lock a directory since proper-lockfile works best on directories,
@@ -344,9 +358,11 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
344
358
  }
345
359
  }
346
360
 
361
+ // #3218: Provide actionable workaround when lock recovery fails
362
+ const lockDirPath = lockTarget + ".lock";
347
363
  const reason = existingPid
348
364
  ? `Another auto-mode session (PID ${existingPid}) appears to be running.\nStop it with \`kill ${existingPid}\` before starting a new session.`
349
- : `Another auto-mode session is already running on this project.`;
365
+ : `Another auto-mode session lock is stuck on this project.\nRun: rm -rf "${lockDirPath}" && rm -f "${lp}"`;
350
366
 
351
367
  return { acquired: false, reason, existingPid };
352
368
  }
@@ -55,6 +55,7 @@ import {
55
55
  getSlice,
56
56
  insertMilestone,
57
57
  insertSlice,
58
+ insertTask,
58
59
  updateTaskStatus,
59
60
  getPendingSliceGateCount,
60
61
  type MilestoneRow,
@@ -78,10 +79,20 @@ import {
78
79
  * as ghosts, causing auto-mode to skip them entirely.
79
80
  */
80
81
  export function isGhostMilestone(basePath: string, mid: string): boolean {
81
- // If the milestone has a DB row, it's a known milestone — not a ghost.
82
+ // If the milestone has a DB row, it's usually a known milestone — not a ghost.
83
+ // Exception: a "queued" row with no disk artifacts is a phantom from
84
+ // gsd_milestone_generate_id that was never planned (#3645).
82
85
  if (isDbAvailable()) {
83
86
  const dbRow = getMilestone(mid);
84
- if (dbRow) return false;
87
+ if (dbRow) {
88
+ if (dbRow.status === 'queued') {
89
+ const hasContent = resolveMilestoneFile(basePath, mid, "CONTEXT")
90
+ || resolveMilestoneFile(basePath, mid, "ROADMAP")
91
+ || resolveMilestoneFile(basePath, mid, "SUMMARY");
92
+ return !hasContent;
93
+ }
94
+ return false;
95
+ }
85
96
  }
86
97
 
87
98
  // If a worktree exists for this milestone, it was legitimately created.
@@ -178,7 +189,7 @@ export async function getActiveMilestoneId(basePath: string): Promise<string | n
178
189
  const byId = new Map(allMilestones.map(m => [m.id, m]));
179
190
  for (const id of sortedIds) {
180
191
  const m = byId.get(id)!;
181
- if (m.status === "complete" || m.status === "done" || m.status === "parked") continue;
192
+ if (isClosedStatus(m.status) || m.status === "parked") continue;
182
193
  return m.id;
183
194
  }
184
195
  return null;
@@ -293,12 +304,9 @@ function extractContextTitle(content: string | null, fallback: string): string {
293
304
 
294
305
  // ─── DB-backed State Derivation ────────────────────────────────────────────
295
306
 
296
- /**
297
- * Helper: check if a DB status counts as "done" (handles K002 ambiguity).
298
- */
299
- function isStatusDone(status: string): boolean {
300
- return status === 'complete' || status === 'done' || status === 'skipped';
301
- }
307
+ // isStatusDone replaced by isClosedStatus from status-guards.ts (single source of truth).
308
+ // Alias kept for backward compatibility within this file.
309
+ const isStatusDone = isClosedStatus;
302
310
 
303
311
  /**
304
312
  * Derive GSD state from the milestones/slices/tasks DB tables.
@@ -431,13 +439,10 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
431
439
  continue;
432
440
  }
433
441
 
434
- // Check roadmap: all slices done means milestone is complete
435
- const slices = getMilestoneSlices(m.id);
436
- if (slices.length > 0 && slices.every(s => isStatusDone(s.status))) {
437
- // All slices done but no summary — still counts as complete for dep resolution
438
- // if a summary file exists
439
- // Note: without summary file, the milestone is in validating/completing state, not complete
440
- }
442
+ // Milestones with all slices done but no SUMMARY file are in
443
+ // validating/completing state — intentionally NOT added to
444
+ // completeMilestoneIds. The SUMMARY file (checked above) is the
445
+ // terminal artifact that proves completion per #864.
441
446
  }
442
447
 
443
448
  // Phase 2: Build registry and find active milestone
@@ -446,6 +451,10 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
446
451
  let activeMilestoneSlices: SliceRow[] = [];
447
452
  let activeMilestoneFound = false;
448
453
  let activeMilestoneHasDraft = false;
454
+ // Queued shells (DB row, no slices, no content files) are deferred during
455
+ // the main loop so they don't eclipse real active milestones (#3470).
456
+ // If no real active milestone is found, the first deferred shell is promoted.
457
+ let firstDeferredQueuedShell: { id: string; title: string; deps: string[] } | null = null;
449
458
 
450
459
  for (const m of milestones) {
451
460
  if (parkedMilestoneIds.has(m.id)) {
@@ -453,9 +462,10 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
453
462
  continue;
454
463
  }
455
464
 
456
- // Ghost milestone check: no slices in DB AND no substantive files on disk
465
+ // Ghost milestone check: no slices in DB AND no substantive files on disk.
466
+ // Skip queued milestones — they are handled by the deferred-shell logic below (#3470).
457
467
  const slices = getMilestoneSlices(m.id);
458
- if (slices.length === 0 && !isStatusDone(m.status)) {
468
+ if (slices.length === 0 && !isStatusDone(m.status) && m.status !== 'queued') {
459
469
  // Check disk for ghost detection
460
470
  if (isGhostMilestone(basePath, m.id)) continue;
461
471
  }
@@ -500,6 +510,23 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
500
510
  continue;
501
511
  }
502
512
 
513
+ // Defer queued shell milestones with no substantive content (#3470).
514
+ // A queued milestone with no slices and no context/draft file is a
515
+ // placeholder that should not block later real active milestones.
516
+ // If no real active milestone is found after the loop, the first
517
+ // deferred shell is promoted to active (#2921).
518
+ if (m.status === 'queued' && slices.length === 0) {
519
+ const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
520
+ const draftFile = resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
521
+ if (!contextFile && !draftFile) {
522
+ if (!firstDeferredQueuedShell) {
523
+ firstDeferredQueuedShell = { id: m.id, title, deps };
524
+ }
525
+ registry.push({ id: m.id, title, status: 'pending', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
526
+ continue;
527
+ }
528
+ }
529
+
503
530
  // Handle all-slices-done case (validating/completing)
504
531
  if (allSlicesDone) {
505
532
  const validationFile = resolveMilestoneFile(basePath, m.id, "VALIDATION");
@@ -532,6 +559,16 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
532
559
  }
533
560
  }
534
561
 
562
+ // Promote deferred queued shell if no real active milestone was found (#3470/#2921).
563
+ if (!activeMilestoneFound && firstDeferredQueuedShell) {
564
+ const shell = firstDeferredQueuedShell;
565
+ activeMilestone = { id: shell.id, title: shell.title };
566
+ activeMilestoneSlices = [];
567
+ activeMilestoneFound = true;
568
+ const entry = registry.find(e => e.id === shell.id);
569
+ if (entry) entry.status = 'active';
570
+ }
571
+
535
572
  const milestoneProgress = {
536
573
  done: registry.filter(e => e.status === 'complete').length,
537
574
  total: registry.length,
@@ -636,12 +673,15 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
636
673
  const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
637
674
  const validationContent = validationFile ? await loadFile(validationFile) : null;
638
675
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
676
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
639
677
  const sliceProgress = {
640
678
  done: activeMilestoneSlices.length,
641
679
  total: activeMilestoneSlices.length,
642
680
  };
643
681
 
644
- if (!validationTerminal) {
682
+ // Force re-validation when verdict is needs-remediation — remediation slices
683
+ // may have completed since the stale validation was written (#3596).
684
+ if (!validationTerminal || verdict === 'needs-remediation') {
645
685
  return {
646
686
  activeMilestone, activeSlice: null, activeTask: null,
647
687
  phase: 'validating-milestone',
@@ -738,6 +778,44 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
738
778
  // ── Get tasks from DB ────────────────────────────────────────────────
739
779
  let tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
740
780
 
781
+ // ── Reconcile missing tasks: plan file has tasks but DB is empty (#3600) ──
782
+ // When the planning agent writes S##-PLAN.md with task entries but never
783
+ // calls the gsd_plan_slice persistence tool, the DB has zero task rows
784
+ // even though the plan file contains valid tasks. Without this reconciliation,
785
+ // deriveState returns phase='planning' forever — the dispatcher re-dispatches
786
+ // plan-slice in an infinite loop.
787
+ if (tasks.length === 0 && planFile) {
788
+ try {
789
+ const planContent = await loadFile(planFile);
790
+ if (planContent) {
791
+ const diskPlan = parsePlan(planContent);
792
+ if (diskPlan.tasks.length > 0) {
793
+ for (let i = 0; i < diskPlan.tasks.length; i++) {
794
+ const t = diskPlan.tasks[i];
795
+ try {
796
+ insertTask({
797
+ id: t.id,
798
+ sliceId: activeSlice.id,
799
+ milestoneId: activeMilestone.id,
800
+ title: t.title,
801
+ status: t.done ? 'complete' : 'pending',
802
+ sequence: i + 1,
803
+ });
804
+ } catch (insertErr) {
805
+ // Task may already exist from a partial previous import — skip
806
+ logWarning("reconcile", `failed to insert task ${t.id} from plan file: ${insertErr instanceof Error ? insertErr.message : String(insertErr)}`);
807
+ }
808
+ }
809
+ tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
810
+ logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${activeMilestone.id}/${activeSlice.id} — DB was empty (#3600)`, { mid: activeMilestone.id, sid: activeSlice.id });
811
+ }
812
+ }
813
+ } catch (err) {
814
+ // Non-fatal — fall through to the existing "empty plan" logic
815
+ logError("reconcile", `plan-file task import failed for ${activeMilestone.id}/${activeSlice.id}: ${err instanceof Error ? err.message : String(err)}`);
816
+ }
817
+ }
818
+
741
819
  // ── Reconcile stale task status (#2514) ──────────────────────────────
742
820
  // When a session disconnects after the agent writes SUMMARY + VERIFY
743
821
  // artifacts but before postUnitPostVerification updates the DB, tasks
@@ -870,7 +948,12 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
870
948
  // ── REPLAN-TRIGGER detection ─────────────────────────────────────────
871
949
  if (!blockerTaskId) {
872
950
  const sliceRow = getSlice(activeMilestone.id, activeSlice.id);
873
- if (sliceRow?.replan_triggered_at) {
951
+ // Check DB column first, fall back to disk trigger file when DB write
952
+ // was best-effort and failed (triage-resolution.ts dual-write gap).
953
+ const dbTriggered = !!sliceRow?.replan_triggered_at;
954
+ const diskTriggered = !dbTriggered &&
955
+ !!resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "REPLAN-TRIGGER");
956
+ if (dbTriggered || diskTriggered) {
874
957
  // Loop protection: if replan_history has entries, replan was already done
875
958
  const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
876
959
  if (replanHistory.length === 0) {
@@ -1099,22 +1182,25 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
1099
1182
  const validationFile = resolveMilestoneFile(basePath, mid, "VALIDATION");
1100
1183
  const validationContent = validationFile ? await cachedLoadFile(validationFile) : null;
1101
1184
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
1185
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
1186
+ // needs-remediation is terminal but requires re-validation (#3596)
1187
+ const needsRevalidation = !validationTerminal || verdict === 'needs-remediation';
1102
1188
 
1103
1189
  if (summaryFile) {
1104
1190
  // Summary exists → milestone is complete regardless of validation state.
1105
1191
  // The summary is the terminal artifact (#864).
1106
1192
  registry.push({ id: mid, title, status: 'complete' });
1107
- } else if (!validationTerminal && !activeMilestoneFound) {
1108
- // No summary and no terminal validation → validating-milestone
1193
+ } else if (needsRevalidation && !activeMilestoneFound) {
1194
+ // No summary and needs (re-)validation → validating-milestone
1109
1195
  activeMilestone = { id: mid, title };
1110
1196
  activeRoadmap = roadmap;
1111
1197
  activeMilestoneFound = true;
1112
1198
  registry.push({ id: mid, title, status: 'active' });
1113
- } else if (!validationTerminal && activeMilestoneFound) {
1114
- // No summary and no terminal validation, but another milestone is already active
1199
+ } else if (needsRevalidation && activeMilestoneFound) {
1200
+ // Needs (re-)validation, but another milestone is already active
1115
1201
  registry.push({ id: mid, title, status: 'pending' });
1116
1202
  } else if (!activeMilestoneFound) {
1117
- // Terminal validation but no summary → completing-milestone
1203
+ // Terminal validation (pass/needs-attention) but no summary → completing-milestone
1118
1204
  activeMilestone = { id: mid, title };
1119
1205
  activeRoadmap = roadmap;
1120
1206
  activeMilestoneFound = true;
@@ -1299,12 +1385,15 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
1299
1385
  const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
1300
1386
  const validationContent = validationFile ? await cachedLoadFile(validationFile) : null;
1301
1387
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
1388
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
1302
1389
  const sliceProgress = {
1303
1390
  done: activeRoadmap.slices.length,
1304
1391
  total: activeRoadmap.slices.length,
1305
1392
  };
1306
1393
 
1307
- if (!validationTerminal) {
1394
+ // Force re-validation when verdict is needs-remediation — remediation slices
1395
+ // may have completed since the stale validation was written (#3596).
1396
+ if (!validationTerminal || verdict === 'needs-remediation') {
1308
1397
  return {
1309
1398
  activeMilestone,
1310
1399
  activeSlice: null,