gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.d0517ff

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 (316) 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/run-unit.js +13 -2
  5. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  7. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  9. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  10. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  11. package/dist/resources/extensions/gsd/auto-start.js +42 -11
  12. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  13. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  14. package/dist/resources/extensions/gsd/auto.js +21 -15
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  17. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  21. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  22. package/dist/resources/extensions/gsd/commands/handlers/core.js +20 -0
  23. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  24. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  25. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  26. package/dist/resources/extensions/gsd/detection.js +1 -1
  27. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  28. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  29. package/dist/resources/extensions/gsd/doctor.js +2 -1
  30. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  31. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  32. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  33. package/dist/resources/extensions/gsd/index.js +1 -1
  34. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  35. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  37. package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -5
  38. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  39. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  40. package/dist/resources/extensions/gsd/preferences.js +9 -2
  41. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  42. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  46. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  48. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  49. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  50. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  51. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  52. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  53. package/dist/resources/extensions/gsd/quick.js +19 -15
  54. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  55. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  56. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  57. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  58. package/dist/resources/extensions/gsd/state.js +115 -28
  59. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  60. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  61. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  63. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  64. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  65. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  66. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  67. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  68. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  69. package/dist/resources/extensions/gsd/undo.js +3 -2
  70. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  71. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  72. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  73. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  74. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  75. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  76. package/dist/resources/extensions/gsd/worktree.js +9 -0
  77. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  78. package/dist/web/standalone/.next/BUILD_ID +1 -1
  79. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  80. package/dist/web/standalone/.next/build-manifest.json +3 -3
  81. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  82. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  84. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.html +1 -1
  100. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  107. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  110. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  111. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  112. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  113. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  114. package/package.json +1 -1
  115. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  117. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  119. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  121. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  137. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  138. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  139. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  140. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  142. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  144. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  145. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  146. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  147. package/packages/pi-tui/dist/components/image.js +4 -0
  148. package/packages/pi-tui/dist/components/image.js.map +1 -1
  149. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  150. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  151. package/packages/pi-tui/dist/components/image.test.js +32 -0
  152. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  153. package/packages/pi-tui/src/components/image.test.ts +36 -0
  154. package/packages/pi-tui/src/components/image.ts +5 -0
  155. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  156. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  157. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  158. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  159. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  160. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  161. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  162. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  163. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  164. package/src/resources/extensions/gsd/auto-start.ts +45 -10
  165. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  166. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  167. package/src/resources/extensions/gsd/auto.ts +19 -8
  168. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  169. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  170. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  171. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  173. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  174. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  175. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -0
  176. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  177. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  178. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  179. package/src/resources/extensions/gsd/detection.ts +1 -1
  180. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  181. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  182. package/src/resources/extensions/gsd/doctor.ts +2 -1
  183. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  184. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  185. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  186. package/src/resources/extensions/gsd/index.ts +1 -0
  187. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  188. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  189. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  190. package/src/resources/extensions/gsd/pre-execution-checks.ts +15 -7
  191. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  192. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  193. package/src/resources/extensions/gsd/preferences.ts +9 -2
  194. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  195. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  196. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  197. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  198. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  199. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  200. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  201. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  202. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  203. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  204. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  205. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  206. package/src/resources/extensions/gsd/quick.ts +20 -15
  207. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  208. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  209. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  210. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  211. package/src/resources/extensions/gsd/state.ts +115 -26
  212. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  213. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  214. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  215. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  216. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  217. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  218. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  219. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  220. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  221. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  222. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  223. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  224. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  225. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  226. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  227. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  228. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  229. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  230. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  231. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  232. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  233. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  234. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  235. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  236. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  237. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  238. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  239. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  240. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  241. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  242. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  243. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +218 -20
  244. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  245. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  246. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  247. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  248. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  249. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  250. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  251. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  252. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  253. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  254. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  255. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  256. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  257. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  258. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  259. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  260. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  261. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  262. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  263. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  265. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  266. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  267. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  268. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  269. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  270. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  271. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  272. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  273. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  274. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  275. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  276. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  277. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  278. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  279. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  280. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  281. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  282. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  283. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  284. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  285. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  286. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  287. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  288. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  289. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  290. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  291. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  292. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  293. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  294. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  295. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  296. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  297. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  298. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  299. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  300. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  301. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  302. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  303. package/src/resources/extensions/gsd/types.ts +4 -0
  304. package/src/resources/extensions/gsd/undo.ts +3 -2
  305. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  306. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  307. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  308. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  309. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  310. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  311. package/src/resources/extensions/gsd/worktree.ts +10 -0
  312. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  313. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  314. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  315. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_buildManifest.js +0 -0
  316. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_ssgManifest.js +0 -0
@@ -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
 
@@ -151,28 +151,32 @@ export async function handleQuick(args, ctx, pi) {
151
151
  const taskDir = ensureQuickDir(basePath, taskNum, slug);
152
152
  const taskDirRel = `.gsd/quick/${taskNum}-${slug}`;
153
153
  const date = new Date().toISOString().split("T")[0];
154
- // Create git branch for the quick task
154
+ // Create git branch for the quick task (unless isolation:none — #3337)
155
155
  const gitPrefs = loadEffectiveGSDPreferences()?.preferences?.git ?? {};
156
156
  const git = new GitServiceImpl(basePath, gitPrefs);
157
157
  const branchName = `gsd/quick/${taskNum}-${slug}`;
158
158
  let originalBranch = git.getCurrentBranch();
159
+ const { getIsolationMode } = await import("./preferences.js");
160
+ const usesBranch = getIsolationMode() !== "none";
159
161
  let branchCreated = false;
160
- try {
161
- const current = originalBranch;
162
- if (current !== branchName) {
163
- // Auto-commit any dirty state before switching
164
- try {
165
- git.autoCommit("quick-task", `Q${taskNum}`, []);
162
+ if (usesBranch) {
163
+ try {
164
+ const current = originalBranch;
165
+ if (current !== branchName) {
166
+ // Auto-commit any dirty state before switching
167
+ try {
168
+ git.autoCommit("quick-task", `Q${taskNum}`, []);
169
+ }
170
+ catch { /* nothing to commit — fine */ }
171
+ runGit(basePath, ["checkout", "-b", branchName]);
172
+ branchCreated = true;
166
173
  }
167
- catch { /* nothing to commit — fine */ }
168
- runGit(basePath, ["checkout", "-b", branchName]);
169
- branchCreated = true;
170
174
  }
171
- }
172
- catch (err) {
173
- // Branch creation failed continue on current branch
174
- const message = err instanceof Error ? err.message : String(err);
175
- ctx.ui.notify(`Could not create branch ${branchName}: ${message}. Working on current branch.`, "warning");
175
+ catch (err) {
176
+ // Branch creation failed — continue on current branch
177
+ const message = err instanceof Error ? err.message : String(err);
178
+ ctx.ui.notify(`Could not create branch ${branchName}: ${message}. Working on current branch.`, "warning");
179
+ }
176
180
  }
177
181
  const actualBranch = branchCreated ? branchName : git.getCurrentBranch();
178
182
  if (actualBranch === branchName && originalBranch !== branchName) {
@@ -110,6 +110,18 @@ export function isGraphAmbiguous(graph) {
110
110
  node.inputFiles.length === 0 &&
111
111
  node.outputFiles.length === 0);
112
112
  }
113
+ /**
114
+ * Returns tasks that are missing IO annotations (no inputFiles and no outputFiles).
115
+ * These tasks prevent parallel dispatch by making the graph ambiguous.
116
+ * Used to surface actionable diagnostics when parallel execution falls back to sequential.
117
+ */
118
+ export function getMissingAnnotationTasks(graph) {
119
+ return graph
120
+ .filter((node) => !node.done &&
121
+ node.inputFiles.length === 0 &&
122
+ node.outputFiles.length === 0)
123
+ .map((node) => ({ id: node.id, title: node.title }));
124
+ }
113
125
  /**
114
126
  * Detect deadlock: no tasks are ready and none are in-flight, yet incomplete
115
127
  * tasks remain. This indicates a circular dependency or impossible state.
@@ -59,6 +59,19 @@ function extractSlicesSection(content) {
59
59
  function parseTableSlices(section) {
60
60
  const lines = section.split("\n");
61
61
  const slices = [];
62
+ // Detect dependency column index from the header row (#3383, #3336).
63
+ // Only parse deps from this column (or cells with explicit "depends"/"deps" keywords).
64
+ let depColumnIndex = -1;
65
+ for (const line of lines) {
66
+ if (!line.includes("|"))
67
+ continue;
68
+ if (/S\d+/.test(line))
69
+ break; // reached data rows
70
+ const headerCells = line.split("|").map(c => c.trim()).filter(Boolean);
71
+ depColumnIndex = headerCells.findIndex(c => /^(depends|deps|depend)/i.test(c));
72
+ if (depColumnIndex >= 0)
73
+ break;
74
+ }
62
75
  for (const line of lines) {
63
76
  // Skip non-table lines, separator lines (|---|---|), and header rows
64
77
  if (!line.includes("|"))
@@ -94,12 +107,18 @@ function parseTableSlices(section) {
94
107
  break;
95
108
  }
96
109
  }
97
- // Extract dependencies from cells containing S-prefixed IDs (excluding the slice's own ID)
110
+ // Extract dependencies only from the dependency column or cells with
111
+ // explicit "depends"/"deps" keywords — never from title cells (#3383).
98
112
  let depends = [];
99
- for (const cell of cells) {
100
- if (/depends|deps/i.test(cell) || (cell.match(/S\d+/g)?.length ?? 0) > 0) {
101
- const depIds = (cell.match(/S\d+/g) ?? []).filter(d => d !== id);
102
- if (depIds.length > 0 || /none|—|-/i.test(cell)) {
113
+ if (depColumnIndex >= 0 && cells[depColumnIndex]) {
114
+ const depCell = cells[depColumnIndex];
115
+ const depIds = (depCell.match(/S\d+/g) ?? []).filter(d => d !== id);
116
+ depends = expandDependencies(depIds);
117
+ }
118
+ else {
119
+ for (const cell of cells) {
120
+ if (/depends|deps/i.test(cell)) {
121
+ const depIds = (cell.match(/S\d+/g) ?? []).filter(d => d !== id);
103
122
  depends = expandDependencies(depIds);
104
123
  break;
105
124
  }
@@ -35,12 +35,12 @@ const VALIDATORS = {
35
35
  };
36
36
  function validatePlanSlice(content) {
37
37
  const violations = [];
38
- // Must have at least 2 task entries (checkbox pattern)
38
+ // Must have at least 1 task entry — single-task slices are valid (#3649)
39
39
  const taskCount = (content.match(/- \[[ x]\] \*\*T\d+/g) || []).length;
40
- if (taskCount < 2) {
40
+ if (taskCount < 1) {
41
41
  violations.push({
42
42
  severity: "warning",
43
- reason: `Slice plan has only ${taskCount} task(s) — expected at least 2`,
43
+ reason: `Slice plan has ${taskCount} task(s) — expected at least 1`,
44
44
  });
45
45
  }
46
46
  // Should have a Files Likely Touched section
@@ -242,6 +242,26 @@ export function acquireSessionLock(basePath) {
242
242
  }
243
243
  const gsdDir = gsdRoot(basePath);
244
244
  const lockTarget = effectiveLockTarget(gsdDir);
245
+ // #3218: Pre-flight stale lock cleanup — if the .lock/ directory exists but
246
+ // no auto.lock metadata is present (or the PID is dead), remove the lock
247
+ // directory before attempting acquisition. This prevents the 30-min stale
248
+ // window from blocking /gsd after crashes, SIGKILL, or laptop sleep.
249
+ const lockDir = lockTarget + ".lock";
250
+ if (existsSync(lockDir)) {
251
+ const existingData = readExistingLockData(lp);
252
+ const isOrphan = !existingData || (existingData.pid && !isPidAlive(existingData.pid));
253
+ if (isOrphan) {
254
+ try {
255
+ rmSync(lockDir, { recursive: true, force: true });
256
+ }
257
+ catch { /* best-effort */ }
258
+ try {
259
+ if (existsSync(lp))
260
+ unlinkSync(lp);
261
+ }
262
+ catch { /* best-effort */ }
263
+ }
264
+ }
245
265
  try {
246
266
  // Try to acquire an exclusive OS-level lock on the lock target.
247
267
  // We lock a directory since proper-lockfile works best on directories,
@@ -292,9 +312,11 @@ export function acquireSessionLock(basePath) {
292
312
  // Retry also failed — fall through to the error path
293
313
  }
294
314
  }
315
+ // #3218: Provide actionable workaround when lock recovery fails
316
+ const lockDirPath = lockTarget + ".lock";
295
317
  const reason = existingPid
296
318
  ? `Another auto-mode session (PID ${existingPid}) appears to be running.\nStop it with \`kill ${existingPid}\` before starting a new session.`
297
- : `Another auto-mode session is already running on this project.`;
319
+ : `Another auto-mode session lock is stuck on this project.\nRun: rm -rf "${lockDirPath}" && rm -f "${lp}"`;
298
320
  return { acquired: false, reason, existingPid };
299
321
  }
300
322
  }
@@ -6,14 +6,14 @@ import { parseSummary, loadFile, parseRequirementCounts, parseContextDependsOn,
6
6
  import { resolveMilestoneFile, resolveSlicePath, resolveSliceFile, resolveTaskFile, resolveTasksDir, resolveGsdRootFile, gsdRoot, } from './paths.js';
7
7
  import { findMilestoneIds } from './milestone-ids.js';
8
8
  import { loadQueueOrder, sortByQueueOrder } from './queue-order.js';
9
- import { isDeferredStatus } from './status-guards.js';
9
+ import { isClosedStatus, isDeferredStatus } from './status-guards.js';
10
10
  import { nativeBatchParseGsdFiles } from './native-parser-bridge.js';
11
11
  import { join, resolve } from 'path';
12
12
  import { existsSync, readdirSync, readFileSync } from 'node:fs';
13
13
  import { debugCount, debugTime } from './debug-logger.js';
14
14
  import { logWarning, logError } from './workflow-logger.js';
15
15
  import { extractVerdict } from './verdict-parser.js';
16
- import { isDbAvailable, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, updateTaskStatus, getPendingSliceGateCount, } from './gsd-db.js';
16
+ import { isDbAvailable, getAllMilestones, getMilestone, getMilestoneSlices, getSliceTasks, getReplanHistory, getSlice, insertMilestone, insertSlice, insertTask, updateTaskStatus, getPendingSliceGateCount, } from './gsd-db.js';
17
17
  /**
18
18
  * A "ghost" milestone directory contains only META.json (and no substantive
19
19
  * files like CONTEXT, CONTEXT-DRAFT, ROADMAP, or SUMMARY). These appear when
@@ -30,11 +30,20 @@ import { isDbAvailable, getAllMilestones, getMilestone, getMilestoneSlices, getS
30
30
  * as ghosts, causing auto-mode to skip them entirely.
31
31
  */
32
32
  export function isGhostMilestone(basePath, mid) {
33
- // If the milestone has a DB row, it's a known milestone — not a ghost.
33
+ // If the milestone has a DB row, it's usually a known milestone — not a ghost.
34
+ // Exception: a "queued" row with no disk artifacts is a phantom from
35
+ // gsd_milestone_generate_id that was never planned (#3645).
34
36
  if (isDbAvailable()) {
35
37
  const dbRow = getMilestone(mid);
36
- if (dbRow)
38
+ if (dbRow) {
39
+ if (dbRow.status === 'queued') {
40
+ const hasContent = resolveMilestoneFile(basePath, mid, "CONTEXT")
41
+ || resolveMilestoneFile(basePath, mid, "ROADMAP")
42
+ || resolveMilestoneFile(basePath, mid, "SUMMARY");
43
+ return !hasContent;
44
+ }
37
45
  return false;
46
+ }
38
47
  }
39
48
  // If a worktree exists for this milestone, it was legitimately created.
40
49
  const root = gsdRoot(basePath);
@@ -110,7 +119,7 @@ export async function getActiveMilestoneId(basePath) {
110
119
  const byId = new Map(allMilestones.map(m => [m.id, m]));
111
120
  for (const id of sortedIds) {
112
121
  const m = byId.get(id);
113
- if (m.status === "complete" || m.status === "done" || m.status === "parked")
122
+ if (isClosedStatus(m.status) || m.status === "parked")
114
123
  continue;
115
124
  return m.id;
116
125
  }
@@ -221,12 +230,9 @@ function extractContextTitle(content, fallback) {
221
230
  return stripMilestonePrefix(h1.slice(2).trim()) || fallback;
222
231
  }
223
232
  // ─── DB-backed State Derivation ────────────────────────────────────────────
224
- /**
225
- * Helper: check if a DB status counts as "done" (handles K002 ambiguity).
226
- */
227
- function isStatusDone(status) {
228
- return status === 'complete' || status === 'done' || status === 'skipped';
229
- }
233
+ // isStatusDone replaced by isClosedStatus from status-guards.ts (single source of truth).
234
+ // Alias kept for backward compatibility within this file.
235
+ const isStatusDone = isClosedStatus;
230
236
  /**
231
237
  * Derive GSD state from the milestones/slices/tasks DB tables.
232
238
  * Flag files (PARKED, VALIDATION, CONTINUE, REPLAN, REPLAN-TRIGGER, CONTEXT-DRAFT)
@@ -353,13 +359,10 @@ export async function deriveStateFromDb(basePath) {
353
359
  completeMilestoneIds.add(m.id);
354
360
  continue;
355
361
  }
356
- // Check roadmap: all slices done means milestone is complete
357
- const slices = getMilestoneSlices(m.id);
358
- if (slices.length > 0 && slices.every(s => isStatusDone(s.status))) {
359
- // All slices done but no summary — still counts as complete for dep resolution
360
- // if a summary file exists
361
- // Note: without summary file, the milestone is in validating/completing state, not complete
362
- }
362
+ // Milestones with all slices done but no SUMMARY file are in
363
+ // validating/completing state — intentionally NOT added to
364
+ // completeMilestoneIds. The SUMMARY file (checked above) is the
365
+ // terminal artifact that proves completion per #864.
363
366
  }
364
367
  // Phase 2: Build registry and find active milestone
365
368
  const registry = [];
@@ -367,14 +370,19 @@ export async function deriveStateFromDb(basePath) {
367
370
  let activeMilestoneSlices = [];
368
371
  let activeMilestoneFound = false;
369
372
  let activeMilestoneHasDraft = false;
373
+ // Queued shells (DB row, no slices, no content files) are deferred during
374
+ // the main loop so they don't eclipse real active milestones (#3470).
375
+ // If no real active milestone is found, the first deferred shell is promoted.
376
+ let firstDeferredQueuedShell = null;
370
377
  for (const m of milestones) {
371
378
  if (parkedMilestoneIds.has(m.id)) {
372
379
  registry.push({ id: m.id, title: stripMilestonePrefix(m.title) || m.id, status: 'parked' });
373
380
  continue;
374
381
  }
375
- // Ghost milestone check: no slices in DB AND no substantive files on disk
382
+ // Ghost milestone check: no slices in DB AND no substantive files on disk.
383
+ // Skip queued milestones — they are handled by the deferred-shell logic below (#3470).
376
384
  const slices = getMilestoneSlices(m.id);
377
- if (slices.length === 0 && !isStatusDone(m.status)) {
385
+ if (slices.length === 0 && !isStatusDone(m.status) && m.status !== 'queued') {
378
386
  // Check disk for ghost detection
379
387
  if (isGhostMilestone(basePath, m.id))
380
388
  continue;
@@ -413,6 +421,22 @@ export async function deriveStateFromDb(basePath) {
413
421
  registry.push({ id: m.id, title, status: 'pending', dependsOn: deps });
414
422
  continue;
415
423
  }
424
+ // Defer queued shell milestones with no substantive content (#3470).
425
+ // A queued milestone with no slices and no context/draft file is a
426
+ // placeholder that should not block later real active milestones.
427
+ // If no real active milestone is found after the loop, the first
428
+ // deferred shell is promoted to active (#2921).
429
+ if (m.status === 'queued' && slices.length === 0) {
430
+ const contextFile = resolveMilestoneFile(basePath, m.id, "CONTEXT");
431
+ const draftFile = resolveMilestoneFile(basePath, m.id, "CONTEXT-DRAFT");
432
+ if (!contextFile && !draftFile) {
433
+ if (!firstDeferredQueuedShell) {
434
+ firstDeferredQueuedShell = { id: m.id, title, deps };
435
+ }
436
+ registry.push({ id: m.id, title, status: 'pending', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
437
+ continue;
438
+ }
439
+ }
416
440
  // Handle all-slices-done case (validating/completing)
417
441
  if (allSlicesDone) {
418
442
  const validationFile = resolveMilestoneFile(basePath, m.id, "VALIDATION");
@@ -443,6 +467,16 @@ export async function deriveStateFromDb(basePath) {
443
467
  registry.push({ id: m.id, title, status: 'pending', ...(deps.length > 0 ? { dependsOn: deps } : {}) });
444
468
  }
445
469
  }
470
+ // Promote deferred queued shell if no real active milestone was found (#3470/#2921).
471
+ if (!activeMilestoneFound && firstDeferredQueuedShell) {
472
+ const shell = firstDeferredQueuedShell;
473
+ activeMilestone = { id: shell.id, title: shell.title };
474
+ activeMilestoneSlices = [];
475
+ activeMilestoneFound = true;
476
+ const entry = registry.find(e => e.id === shell.id);
477
+ if (entry)
478
+ entry.status = 'active';
479
+ }
446
480
  const milestoneProgress = {
447
481
  done: registry.filter(e => e.status === 'complete').length,
448
482
  total: registry.length,
@@ -538,11 +572,14 @@ export async function deriveStateFromDb(basePath) {
538
572
  const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
539
573
  const validationContent = validationFile ? await loadFile(validationFile) : null;
540
574
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
575
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
541
576
  const sliceProgress = {
542
577
  done: activeMilestoneSlices.length,
543
578
  total: activeMilestoneSlices.length,
544
579
  };
545
- if (!validationTerminal) {
580
+ // Force re-validation when verdict is needs-remediation — remediation slices
581
+ // may have completed since the stale validation was written (#3596).
582
+ if (!validationTerminal || verdict === 'needs-remediation') {
546
583
  return {
547
584
  activeMilestone, activeSlice: null, activeTask: null,
548
585
  phase: 'validating-milestone',
@@ -632,6 +669,45 @@ export async function deriveStateFromDb(basePath) {
632
669
  }
633
670
  // ── Get tasks from DB ────────────────────────────────────────────────
634
671
  let tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
672
+ // ── Reconcile missing tasks: plan file has tasks but DB is empty (#3600) ──
673
+ // When the planning agent writes S##-PLAN.md with task entries but never
674
+ // calls the gsd_plan_slice persistence tool, the DB has zero task rows
675
+ // even though the plan file contains valid tasks. Without this reconciliation,
676
+ // deriveState returns phase='planning' forever — the dispatcher re-dispatches
677
+ // plan-slice in an infinite loop.
678
+ if (tasks.length === 0 && planFile) {
679
+ try {
680
+ const planContent = await loadFile(planFile);
681
+ if (planContent) {
682
+ const diskPlan = parsePlan(planContent);
683
+ if (diskPlan.tasks.length > 0) {
684
+ for (let i = 0; i < diskPlan.tasks.length; i++) {
685
+ const t = diskPlan.tasks[i];
686
+ try {
687
+ insertTask({
688
+ id: t.id,
689
+ sliceId: activeSlice.id,
690
+ milestoneId: activeMilestone.id,
691
+ title: t.title,
692
+ status: t.done ? 'complete' : 'pending',
693
+ sequence: i + 1,
694
+ });
695
+ }
696
+ catch (insertErr) {
697
+ // Task may already exist from a partial previous import — skip
698
+ logWarning("reconcile", `failed to insert task ${t.id} from plan file: ${insertErr instanceof Error ? insertErr.message : String(insertErr)}`);
699
+ }
700
+ }
701
+ tasks = getSliceTasks(activeMilestone.id, activeSlice.id);
702
+ logWarning("reconcile", `imported ${tasks.length} tasks from plan file for ${activeMilestone.id}/${activeSlice.id} — DB was empty (#3600)`, { mid: activeMilestone.id, sid: activeSlice.id });
703
+ }
704
+ }
705
+ }
706
+ catch (err) {
707
+ // Non-fatal — fall through to the existing "empty plan" logic
708
+ logError("reconcile", `plan-file task import failed for ${activeMilestone.id}/${activeSlice.id}: ${err instanceof Error ? err.message : String(err)}`);
709
+ }
710
+ }
635
711
  // ── Reconcile stale task status (#2514) ──────────────────────────────
636
712
  // When a session disconnects after the agent writes SUMMARY + VERIFY
637
713
  // artifacts but before postUnitPostVerification updates the DB, tasks
@@ -758,7 +834,12 @@ export async function deriveStateFromDb(basePath) {
758
834
  // ── REPLAN-TRIGGER detection ─────────────────────────────────────────
759
835
  if (!blockerTaskId) {
760
836
  const sliceRow = getSlice(activeMilestone.id, activeSlice.id);
761
- if (sliceRow?.replan_triggered_at) {
837
+ // Check DB column first, fall back to disk trigger file when DB write
838
+ // was best-effort and failed (triage-resolution.ts dual-write gap).
839
+ const dbTriggered = !!sliceRow?.replan_triggered_at;
840
+ const diskTriggered = !dbTriggered &&
841
+ !!resolveSliceFile(basePath, activeMilestone.id, activeSlice.id, "REPLAN-TRIGGER");
842
+ if (dbTriggered || diskTriggered) {
762
843
  // Loop protection: if replan_history has entries, replan was already done
763
844
  const replanHistory = getReplanHistory(activeMilestone.id, activeSlice.id);
764
845
  if (replanHistory.length === 0) {
@@ -973,24 +1054,27 @@ export async function _deriveStateImpl(basePath) {
973
1054
  const validationFile = resolveMilestoneFile(basePath, mid, "VALIDATION");
974
1055
  const validationContent = validationFile ? await cachedLoadFile(validationFile) : null;
975
1056
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
1057
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
1058
+ // needs-remediation is terminal but requires re-validation (#3596)
1059
+ const needsRevalidation = !validationTerminal || verdict === 'needs-remediation';
976
1060
  if (summaryFile) {
977
1061
  // Summary exists → milestone is complete regardless of validation state.
978
1062
  // The summary is the terminal artifact (#864).
979
1063
  registry.push({ id: mid, title, status: 'complete' });
980
1064
  }
981
- else if (!validationTerminal && !activeMilestoneFound) {
982
- // No summary and no terminal validation → validating-milestone
1065
+ else if (needsRevalidation && !activeMilestoneFound) {
1066
+ // No summary and needs (re-)validation → validating-milestone
983
1067
  activeMilestone = { id: mid, title };
984
1068
  activeRoadmap = roadmap;
985
1069
  activeMilestoneFound = true;
986
1070
  registry.push({ id: mid, title, status: 'active' });
987
1071
  }
988
- else if (!validationTerminal && activeMilestoneFound) {
989
- // No summary and no terminal validation, but another milestone is already active
1072
+ else if (needsRevalidation && activeMilestoneFound) {
1073
+ // Needs (re-)validation, but another milestone is already active
990
1074
  registry.push({ id: mid, title, status: 'pending' });
991
1075
  }
992
1076
  else if (!activeMilestoneFound) {
993
- // Terminal validation but no summary → completing-milestone
1077
+ // Terminal validation (pass/needs-attention) but no summary → completing-milestone
994
1078
  activeMilestone = { id: mid, title };
995
1079
  activeRoadmap = roadmap;
996
1080
  activeMilestoneFound = true;
@@ -1175,11 +1259,14 @@ export async function _deriveStateImpl(basePath) {
1175
1259
  const validationFile = resolveMilestoneFile(basePath, activeMilestone.id, "VALIDATION");
1176
1260
  const validationContent = validationFile ? await cachedLoadFile(validationFile) : null;
1177
1261
  const validationTerminal = validationContent ? isValidationTerminal(validationContent) : false;
1262
+ const verdict = validationContent ? extractVerdict(validationContent) : undefined;
1178
1263
  const sliceProgress = {
1179
1264
  done: activeRoadmap.slices.length,
1180
1265
  total: activeRoadmap.slices.length,
1181
1266
  };
1182
- if (!validationTerminal) {
1267
+ // Force re-validation when verdict is needs-remediation — remediation slices
1268
+ // may have completed since the stale validation was written (#3596).
1269
+ if (!validationTerminal || verdict === 'needs-remediation') {
1183
1270
  return {
1184
1271
  activeMilestone,
1185
1272
  activeSlice: null,