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
@@ -6,7 +6,7 @@
6
6
  * No execution state, no hooks, no tools — the LLM does the rest.
7
7
  */
8
8
  import { showNextAction } from "../shared/tui.js";
9
- import { loadFile } from "./files.js";
9
+ import { loadFile, saveFile } from "./files.js";
10
10
  import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js";
11
11
  import { parseRoadmapSlices } from "./roadmap-slices.js";
12
12
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
@@ -34,6 +34,19 @@ import { findMilestoneIds, nextMilestoneId, reserveMilestoneId, getReservedMiles
34
34
  import { parkMilestone, discardMilestone } from "./milestone-actions.js";
35
35
  import { selectAndApplyModel } from "./auto-model-selection.js";
36
36
  import { DISCUSS_TOOLS_ALLOWLIST } from "./constants.js";
37
+ import { runPreparation, formatCodebaseBrief, formatPriorContextBrief, formatEcosystemBrief, } from "./preparation.js";
38
+ // ─── Preparation result storage ─────────────────────────────────────────────
39
+ // Stores the most recent preparation result for injection into discuss prompts.
40
+ // S02 will consume this when building the prepared discussion prompt.
41
+ let lastPreparationResult = null;
42
+ /** Get the most recent preparation result (for S02 prompt building). */
43
+ export function getLastPreparationResult() {
44
+ return lastPreparationResult;
45
+ }
46
+ /** Clear the preparation result (called after discussion completes). */
47
+ export function clearPreparationResult() {
48
+ lastPreparationResult = null;
49
+ }
37
50
  // ─── Re-exports (preserve public API for existing importers) ────────────────
38
51
  export { MILESTONE_ID_RE, generateMilestoneSuffix, nextMilestoneId, extractMilestoneSeq, parseMilestoneId, milestoneIdSort, maxMilestoneNum, findMilestoneIds, reserveMilestoneId, claimReservedId, getReservedMilestoneIds, clearReservedMilestoneIds, } from "./milestone-ids.js";
39
52
  export { showQueue, handleQueueReorder, showQueueAdd, buildExistingMilestonesContext, } from "./guided-flow-queue.js";
@@ -74,7 +87,7 @@ function _getPendingAutoStart(basePath) {
74
87
  * Exported for testing (#2985).
75
88
  */
76
89
  export function setPendingAutoStart(basePath, entry) {
77
- pendingAutoStartMap.set(basePath, entry);
90
+ pendingAutoStartMap.set(basePath, { createdAt: Date.now(), ...entry });
78
91
  }
79
92
  /**
80
93
  * Clear pending auto-start state.
@@ -248,8 +261,10 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
248
261
  // "Grammar is too complex" when the combined tool schema is too large.
249
262
  // Discuss flows only need a small subset of GSD tools — strip the heavy
250
263
  // planning/execution/completion tools to keep the grammar within limits.
264
+ let savedTools = null;
251
265
  if (unitType?.startsWith("discuss-")) {
252
266
  const currentTools = pi.getActiveTools();
267
+ savedTools = currentTools;
253
268
  // Keep all non-GSD tools (builtins, other extensions) and only the
254
269
  // GSD tools on the discuss allowlist.
255
270
  const scopedTools = currentTools.filter((t) => !t.startsWith("gsd_") || DISCUSS_TOOLS_ALLOWLIST.includes(t));
@@ -268,6 +283,12 @@ async function dispatchWorkflow(pi, note, customType = "gsd-run", ctx, unitType)
268
283
  content: `Read the following GSD workflow protocol and execute exactly.\n\n${workflow}\n\n## Your Task\n\n${note}`,
269
284
  display: false,
270
285
  }, { triggerTurn: true });
286
+ // Restore full tool set after the message is queued. The LLM turn has
287
+ // already captured the scoped set — restoring prevents the narrowed
288
+ // tools from leaking into subsequent dispatches (#3628).
289
+ if (savedTools) {
290
+ pi.setActiveTools(savedTools);
291
+ }
271
292
  }
272
293
  /**
273
294
  * Resolve a model ID string to a model object from available models.
@@ -340,6 +361,86 @@ function buildHeadlessDiscussPrompt(nextId, seedContext, _basePath) {
340
361
  multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
341
362
  });
342
363
  }
364
+ /**
365
+ * Build the prepared discuss prompt with brief injection.
366
+ * Uses the discuss-prepared template which encodes the 4-layer discussion protocol.
367
+ *
368
+ * @param nextId - The milestone ID being discussed
369
+ * @param preamble - Preamble text for the discuss prompt
370
+ * @param _basePath - Root directory of the project (unused, kept for signature consistency)
371
+ * @param prepResult - Preparation result containing briefs to inject
372
+ * @returns The prepared discuss prompt string
373
+ */
374
+ function buildPreparedPrompt(nextId, preamble, _basePath, prepResult) {
375
+ const milestoneRel = `.gsd/milestones/${nextId}`;
376
+ // Use context-enhanced instead of context for prepared discussions
377
+ const inlinedTemplates = [
378
+ inlineTemplate("project", "Project"),
379
+ inlineTemplate("requirements", "Requirements"),
380
+ inlineTemplate("context-enhanced", "Context Enhanced"),
381
+ inlineTemplate("roadmap", "Roadmap"),
382
+ inlineTemplate("decisions", "Decisions"),
383
+ ].join("\n\n---\n\n");
384
+ // Format the briefs from the preparation result
385
+ const codebaseBrief = prepResult.codebaseBrief || formatCodebaseBrief(prepResult.codebase);
386
+ const priorContextBrief = prepResult.priorContextBrief || formatPriorContextBrief(prepResult.priorContext);
387
+ const ecosystemBrief = prepResult.ecosystemBrief || formatEcosystemBrief(prepResult.ecosystem);
388
+ return loadPrompt("discuss-prepared", {
389
+ milestoneId: nextId,
390
+ preamble,
391
+ codebaseBrief,
392
+ priorContextBrief,
393
+ ecosystemBrief,
394
+ contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
395
+ roadmapPath: `${milestoneRel}/${nextId}-ROADMAP.md`,
396
+ inlinedTemplates,
397
+ commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
398
+ multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
399
+ });
400
+ }
401
+ /**
402
+ * Run preparation phase if enabled, then build the discuss prompt.
403
+ * This is the main entry point for new milestone discussions with preparation.
404
+ * Stores the preparation result for S02 to inject into the discuss prompt.
405
+ *
406
+ * When preparation succeeds, uses the discuss-prepared template with brief injection.
407
+ * Falls back to the standard discuss template when preparation is disabled or fails.
408
+ *
409
+ * @param ctx - Extension command context with UI for progress notifications
410
+ * @param nextId - The milestone ID being discussed
411
+ * @param preamble - Preamble text for the discuss prompt
412
+ * @param basePath - Root directory of the project
413
+ * @returns The discuss prompt string
414
+ */
415
+ async function prepareAndBuildDiscussPrompt(ctx, nextId, preamble, basePath) {
416
+ // Clear stale preparation result immediately to prevent cross-session/project
417
+ // state leaks. This ensures data from a prior milestone/project never leaks
418
+ // into subsequent discussions (adversarial review fix #3602).
419
+ lastPreparationResult = null;
420
+ const prefs = loadEffectiveGSDPreferences()?.preferences ?? {};
421
+ // Run preparation if enabled (default: true)
422
+ if (prefs.discuss_preparation !== false) {
423
+ try {
424
+ const prepResult = await runPreparation(basePath, ctx.ui, {
425
+ discuss_preparation: prefs.discuss_preparation,
426
+ discuss_web_research: prefs.discuss_web_research,
427
+ discuss_depth: prefs.discuss_depth,
428
+ });
429
+ lastPreparationResult = prepResult;
430
+ // Use prepared prompt if preparation was enabled and produced results
431
+ if (prepResult.enabled) {
432
+ return buildPreparedPrompt(nextId, preamble, basePath, prepResult);
433
+ }
434
+ }
435
+ catch {
436
+ // If preparation throws, ensure stale data doesn't persist
437
+ lastPreparationResult = null;
438
+ }
439
+ }
440
+ // Fall back to standard discuss prompt for backward compatibility
441
+ // lastPreparationResult is already null (cleared at entry or on error)
442
+ return buildDiscussPrompt(nextId, preamble, basePath);
443
+ }
343
444
  /**
344
445
  * Bootstrap a .gsd/ project from scratch for headless use.
345
446
  * Ensures git repo, .gsd/ structure, gitignore, and preferences all exist.
@@ -376,7 +477,7 @@ export async function showHeadlessMilestoneCreation(ctx, pi, basePath, seedConte
376
477
  // Build and dispatch the headless discuss prompt
377
478
  const prompt = buildHeadlessDiscussPrompt(nextId, seedContext, basePath);
378
479
  // Set pending auto start (auto-mode triggers on "Milestone X ready." via checkAutoStartAfterDiscuss)
379
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId });
480
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, createdAt: Date.now() });
380
481
  // Dispatch — headless milestone creation is a planning activity
381
482
  await dispatchWorkflow(pi, prompt, "gsd-run", ctx, "plan-milestone");
382
483
  }
@@ -457,6 +558,7 @@ async function buildDiscussSlicePrompt(mid, sid, sTitle, base, options) {
457
558
  contextPath: sliceContextPath,
458
559
  projectRoot: base,
459
560
  inlinedTemplates,
561
+ structuredQuestionsAvailable: options?.structuredQuestionsAvailable ?? "false",
460
562
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}/${sid}): slice context from discuss`),
461
563
  });
462
564
  }
@@ -474,6 +576,16 @@ export async function showDiscuss(ctx, pi, basePath) {
474
576
  // Invalidate caches to pick up artifacts written by a just-completed discuss/plan
475
577
  invalidateAllCaches();
476
578
  const state = await deriveState(basePath);
579
+ // Rebuild STATE.md from derived state before any dispatch (#3475).
580
+ // Without this, guided prompts read a stale STATE.md cache and the
581
+ // agent bootstraps from the wrong milestone.
582
+ try {
583
+ const { buildStateMarkdown } = await import("./doctor.js");
584
+ await saveFile(resolveGsdRootFile(basePath, "STATE"), buildStateMarkdown(state));
585
+ }
586
+ catch (err) {
587
+ logWarning("guided", `STATE.md rebuild failed: ${err.message}`);
588
+ }
477
589
  // No active milestone (or corrupted milestone with undefined id) —
478
590
  // check for pending milestones to discuss instead
479
591
  if (!state.activeMilestone?.id) {
@@ -521,28 +633,30 @@ export async function showDiscuss(ctx, pi, basePath) {
521
633
  const basePrompt = loadPrompt("guided-discuss-milestone", {
522
634
  milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
523
635
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
636
+ fastPathInstruction: "",
524
637
  });
525
638
  const seed = draftContent
526
639
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
527
640
  : basePrompt;
528
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
641
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
529
642
  await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
530
643
  }
531
644
  else if (choice === "discuss_fresh") {
532
645
  const discussMilestoneTemplates = inlineTemplate("context", "Context");
533
646
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
534
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false });
647
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: mid, step: false, createdAt: Date.now() });
535
648
  await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
536
649
  milestoneId: mid, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
537
650
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
651
+ fastPathInstruction: "",
538
652
  }), "gsd-discuss", ctx, "discuss-milestone");
539
653
  }
540
654
  else if (choice === "skip_milestone") {
541
655
  const milestoneIds = findMilestoneIds(basePath);
542
656
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
543
657
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
544
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false });
545
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
658
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: false, createdAt: Date.now() });
659
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
546
660
  }
547
661
  return;
548
662
  }
@@ -672,7 +786,8 @@ export async function showDiscuss(ctx, pi, basePath) {
672
786
  if (confirm !== "rediscuss")
673
787
  continue;
674
788
  }
675
- const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss });
789
+ const sqAvail = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
790
+ const prompt = await buildDiscussSlicePrompt(mid, chosen.id, chosen.title, basePath, { rediscuss: isRediscuss, structuredQuestionsAvailable: sqAvail });
676
791
  await dispatchWorkflow(pi, prompt, "gsd-discuss", ctx, "discuss-slice");
677
792
  // Wait for the discuss session to finish, then loop back to the picker
678
793
  await ctx.waitForIdle();
@@ -712,16 +827,55 @@ async function showDiscussQueuedMilestone(ctx, pi, basePath, pendingMilestones)
712
827
  const chosen = pendingMilestones.find(m => m.id === choice);
713
828
  if (!chosen)
714
829
  return;
715
- await dispatchDiscussForMilestone(ctx, pi, basePath, chosen.id, chosen.title);
830
+ const hasDraft = !!resolveMilestoneFile(basePath, chosen.id, "CONTEXT-DRAFT");
831
+ let fastPath = hasDraft;
832
+ if (!hasDraft) {
833
+ const mode = await showNextAction(ctx, {
834
+ title: `Discuss ${chosen.id}`,
835
+ summary: [
836
+ "Choose how to start the discussion.",
837
+ "Fast path skips generic scouting — use it when you already know the scope.",
838
+ ],
839
+ actions: [
840
+ {
841
+ id: "full",
842
+ label: "Full discussion",
843
+ description: "Scout the codebase, ask open-ended questions, explore deeply",
844
+ recommended: true,
845
+ },
846
+ {
847
+ id: "fast",
848
+ label: "I have the scope — fast path",
849
+ description: "Treat your first message as authoritative seed context; skip scouting",
850
+ },
851
+ ],
852
+ notYetMessage: "Run /gsd discuss when ready.",
853
+ });
854
+ if (mode === "not_yet")
855
+ return;
856
+ fastPath = mode === "fast";
857
+ }
858
+ await dispatchDiscussForMilestone(ctx, pi, basePath, chosen.id, chosen.title, { fastPath });
716
859
  }
717
860
  /**
718
861
  * Dispatch the guided-discuss-milestone prompt for a milestone without
719
862
  * setting pendingAutoStart — so discussing a queued milestone does not
720
863
  * implicitly activate it when the session ends.
721
864
  */
722
- async function dispatchDiscussForMilestone(ctx, pi, basePath, mid, milestoneTitle) {
865
+ async function dispatchDiscussForMilestone(ctx, pi, basePath, mid, milestoneTitle, opts = {}) {
723
866
  const draftFile = resolveMilestoneFile(basePath, mid, "CONTEXT-DRAFT");
724
867
  const draftContent = draftFile ? await loadFile(draftFile) : null;
868
+ const hasSeed = !!(draftContent || opts.fastPath);
869
+ const fastPathInstruction = hasSeed
870
+ ? [
871
+ "> **Fast path active — scope provided.**",
872
+ "> Do NOT perform a generic codebase scouting pass.",
873
+ "> Do at most 2 targeted reads to check for obvious conflicts with existing work.",
874
+ "> Treat the seed context or the operator's first message as authoritative.",
875
+ "> Move directly to the depth summary and write step.",
876
+ "> Ask only questions where the answer would materially change scope.",
877
+ ].join("\n")
878
+ : "";
725
879
  const discussMilestoneTemplates = inlineTemplate("context", "Context");
726
880
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
727
881
  const basePrompt = loadPrompt("guided-discuss-milestone", {
@@ -730,6 +884,7 @@ async function dispatchDiscussForMilestone(ctx, pi, basePath, mid, milestoneTitl
730
884
  inlinedTemplates: discussMilestoneTemplates,
731
885
  structuredQuestionsAvailable,
732
886
  commitInstruction: buildDocsCommitInstruction(`docs(${mid}): milestone context from discuss`),
887
+ fastPathInstruction,
733
888
  });
734
889
  const prompt = draftContent
735
890
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
@@ -856,8 +1011,8 @@ async function handleMilestoneActions(ctx, pi, basePath, milestoneId, milestoneT
856
1011
  const milestoneIds = findMilestoneIds(basePath);
857
1012
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
858
1013
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
859
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
860
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1014
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1015
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
861
1016
  return true;
862
1017
  }
863
1018
  // "back" or null
@@ -955,14 +1110,35 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
955
1110
  }
956
1111
  }
957
1112
  const state = await deriveState(basePath);
1113
+ // Rebuild STATE.md from derived state before any dispatch (#3475).
1114
+ try {
1115
+ const { buildStateMarkdown } = await import("./doctor.js");
1116
+ await saveFile(resolveGsdRootFile(basePath, "STATE"), buildStateMarkdown(state));
1117
+ }
1118
+ catch (err) {
1119
+ logWarning("guided", `STATE.md rebuild failed: ${err.message}`);
1120
+ }
958
1121
  if (!state.activeMilestone?.id) {
959
1122
  // Guard: if a discuss session is already in flight, don't re-inject the prompt.
960
1123
  // Both /gsd and /gsd auto reach this branch when no milestone exists yet.
961
1124
  // Without this guard, every subsequent /gsd call overwrites the pending auto-start
962
1125
  // and fires another dispatchWorkflow, resetting the conversation mid-interview.
963
1126
  if (pendingAutoStartMap.has(basePath)) {
964
- ctx.ui.notify("Discussion already in progress answer the question above to continue.", "info");
965
- return;
1127
+ // #3274: If /clear interrupted the discussion, the pending entry is stale.
1128
+ // Detect staleness: no manifest, no CONTEXT.md, AND entry is older than
1129
+ // 30s (avoids race between .set() and LLM writing first artifact).
1130
+ const entry = pendingAutoStartMap.get(basePath);
1131
+ const ageMs = Date.now() - (entry.createdAt || 0);
1132
+ const manifestExists = existsSync(join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json"));
1133
+ const milestoneHasContext = existsSync(join(gsdRoot(basePath), "milestones", entry.milestoneId, `${entry.milestoneId}-CONTEXT.md`));
1134
+ if (!manifestExists && !milestoneHasContext && ageMs > 30_000) {
1135
+ // Stale entry from an interrupted discussion — clear and continue
1136
+ pendingAutoStartMap.delete(basePath);
1137
+ }
1138
+ else {
1139
+ ctx.ui.notify("Discussion already in progress — answer the question above to continue.", "info");
1140
+ return;
1141
+ }
966
1142
  }
967
1143
  const milestoneIds = findMilestoneIds(basePath);
968
1144
  // Sanity check (#456): if findMilestoneIds returns [] but the milestones
@@ -989,8 +1165,8 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
989
1165
  const isFirst = milestoneIds.length === 0;
990
1166
  if (isFirst) {
991
1167
  // First ever — skip wizard, just ask directly
992
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
993
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
1168
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1169
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New project, milestone ${nextId}. Do NOT read or explore .gsd/ — it's empty scaffolding.`, basePath), "gsd-run", ctx, "discuss-milestone");
994
1170
  }
995
1171
  else {
996
1172
  const choice = await showNextAction(ctx, {
@@ -1007,8 +1183,8 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1007
1183
  notYetMessage: "Run /gsd when ready.",
1008
1184
  });
1009
1185
  if (choice === "new_milestone") {
1010
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
1011
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1186
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1187
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1012
1188
  }
1013
1189
  }
1014
1190
  return;
@@ -1039,8 +1215,8 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1039
1215
  const milestoneIds = findMilestoneIds(basePath);
1040
1216
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1041
1217
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
1042
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
1043
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1218
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1219
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1044
1220
  }
1045
1221
  else if (choice === "status") {
1046
1222
  const { fireStatusViaCommand } = await import("./commands.js");
@@ -1081,28 +1257,30 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1081
1257
  const basePrompt = loadPrompt("guided-discuss-milestone", {
1082
1258
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1083
1259
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1260
+ fastPathInstruction: "",
1084
1261
  });
1085
1262
  const seed = draftContent
1086
1263
  ? `${basePrompt}\n\n## Prior Discussion (Draft Seed)\n\n${draftContent}`
1087
1264
  : basePrompt;
1088
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
1265
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
1089
1266
  await dispatchWorkflow(pi, seed, "gsd-discuss", ctx, "discuss-milestone");
1090
1267
  }
1091
1268
  else if (choice === "discuss_fresh") {
1092
1269
  const discussMilestoneTemplates = inlineTemplate("context", "Context");
1093
1270
  const structuredQuestionsAvailable = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
1094
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
1271
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
1095
1272
  await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1096
1273
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1097
1274
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1275
+ fastPathInstruction: "",
1098
1276
  }), "gsd-discuss", ctx, "discuss-milestone");
1099
1277
  }
1100
1278
  else if (choice === "skip_milestone") {
1101
1279
  const milestoneIds = findMilestoneIds(basePath);
1102
1280
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1103
1281
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
1104
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
1105
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1282
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1283
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1106
1284
  }
1107
1285
  return;
1108
1286
  }
@@ -1110,7 +1288,18 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1110
1288
  if (!state.activeSlice) {
1111
1289
  const roadmapFile = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
1112
1290
  const hasRoadmap = !!(roadmapFile && await loadFile(roadmapFile));
1113
- if (!hasRoadmap) {
1291
+ // A roadmap file with zero parseable slices (placeholder text) should be
1292
+ // treated the same as no roadmap — offer "Create roadmap" instead of "Go auto"
1293
+ // which would immediately get stuck in blocked state (#3441).
1294
+ let roadmapHasSlices = false;
1295
+ if (hasRoadmap) {
1296
+ const roadmapContent = await loadFile(roadmapFile);
1297
+ if (roadmapContent) {
1298
+ const parsed = parseRoadmapSlices(roadmapContent);
1299
+ roadmapHasSlices = parsed.length > 0;
1300
+ }
1301
+ }
1302
+ if (!hasRoadmap || !roadmapHasSlices) {
1114
1303
  // No roadmap → discuss or plan
1115
1304
  const contextFile = resolveMilestoneFile(basePath, milestoneId, "CONTEXT");
1116
1305
  const hasContext = !!(contextFile && await loadFile(contextFile));
@@ -1146,7 +1335,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1146
1335
  notYetMessage: "Run /gsd when ready.",
1147
1336
  });
1148
1337
  if (choice === "plan") {
1149
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode });
1338
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId, step: stepMode, createdAt: Date.now() });
1150
1339
  const planMilestoneTemplates = [
1151
1340
  inlineTemplate("roadmap", "Roadmap"),
1152
1341
  inlineTemplate("plan", "Slice Plan"),
@@ -1173,14 +1362,15 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1173
1362
  await dispatchWorkflow(pi, loadPrompt("guided-discuss-milestone", {
1174
1363
  milestoneId, milestoneTitle, inlinedTemplates: discussMilestoneTemplates, structuredQuestionsAvailable,
1175
1364
  commitInstruction: buildDocsCommitInstruction(`docs(${milestoneId}): milestone context from discuss`),
1365
+ fastPathInstruction: "",
1176
1366
  }), "gsd-run", ctx, "discuss-milestone");
1177
1367
  }
1178
1368
  else if (choice === "skip_milestone") {
1179
1369
  const milestoneIds = findMilestoneIds(basePath);
1180
1370
  const uniqueMilestoneIds = !!loadEffectiveGSDPreferences()?.preferences?.unique_milestone_ids;
1181
1371
  const nextId = nextMilestoneIdReserved(milestoneIds, uniqueMilestoneIds);
1182
- pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode });
1183
- await dispatchWorkflow(pi, buildDiscussPrompt(nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1372
+ pendingAutoStartMap.set(basePath, { ctx, pi, basePath, milestoneId: nextId, step: stepMode, createdAt: Date.now() });
1373
+ await dispatchWorkflow(pi, await prepareAndBuildDiscussPrompt(ctx, nextId, `New milestone ${nextId}.`, basePath), "gsd-run", ctx, "discuss-milestone");
1184
1374
  }
1185
1375
  else if (choice === "discard_milestone") {
1186
1376
  const confirmed = await showConfirm(ctx, {
@@ -1306,7 +1496,8 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
1306
1496
  }), "gsd-run", ctx, "plan-slice");
1307
1497
  }
1308
1498
  else if (choice === "discuss") {
1309
- await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext }), "gsd-run", ctx, "discuss-slice");
1499
+ const sqAvail = pi.getActiveTools().includes("ask_user_questions") ? "true" : "false";
1500
+ await dispatchWorkflow(pi, await buildDiscussSlicePrompt(milestoneId, sliceId, sliceTitle, basePath, { rediscuss: hasContext, structuredQuestionsAvailable: sqAvail }), "gsd-run", ctx, "discuss-slice");
1310
1501
  }
1311
1502
  else if (choice === "research") {
1312
1503
  const researchTemplates = inlineTemplate("research", "Research");
@@ -1,4 +1,4 @@
1
- export { isDepthVerified, isQueuePhaseActive, setQueuePhaseActive, shouldBlockContextWrite, shouldBlockQueueExecution, } from "./bootstrap/write-gate.js";
1
+ export { isDepthConfirmationAnswer, isDepthVerified, isQueuePhaseActive, setQueuePhaseActive, shouldBlockContextWrite, shouldBlockQueueExecution, } from "./bootstrap/write-gate.js";
2
2
  export default async function registerExtension(pi) {
3
3
  const { registerGsdExtension } = await import("./bootstrap/register-extension.js");
4
4
  registerGsdExtension(pi);
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from "node:fs";
2
2
  import { dirname } from "node:path";
3
+ import { randomBytes } from "node:crypto";
3
4
  /**
4
5
  * Load a JSON file with validation, returning a default on failure.
5
6
  * Handles missing files, corrupt JSON, and schema mismatches uniformly.
@@ -45,9 +46,11 @@ export function loadJsonFileOrNull(filePath, validate) {
45
46
  export function saveJsonFile(filePath, data) {
46
47
  try {
47
48
  mkdirSync(dirname(filePath), { recursive: true });
48
- const tmp = filePath + ".tmp";
49
+ // Use randomized tmp suffix to prevent concurrent-write data loss
50
+ const tmp = `${filePath}.tmp.${randomBytes(4).toString("hex")}`;
49
51
  writeFileSync(tmp, JSON.stringify(data, null, 2) + "\n", "utf-8");
50
52
  renameSync(tmp, filePath);
53
+ // No cleanup needed — renameSync atomically removes tmp on success
51
54
  }
52
55
  catch {
53
56
  // Non-fatal — don't let persistence failures break operation
@@ -60,7 +63,7 @@ export function saveJsonFile(filePath, data) {
60
63
  export function writeJsonFileAtomic(filePath, data) {
61
64
  try {
62
65
  mkdirSync(dirname(filePath), { recursive: true });
63
- const tmp = filePath + ".tmp";
66
+ const tmp = `${filePath}.tmp.${randomBytes(4).toString("hex")}`;
64
67
  writeFileSync(tmp, JSON.stringify(data, null, 2), "utf-8");
65
68
  renameSync(tmp, filePath);
66
69
  }
@@ -438,12 +438,6 @@ export function migrateHierarchyToDb(basePath) {
438
438
  // Ghost milestone: no CONTEXT, ROADMAP, or SUMMARY → skip
439
439
  if (!hasRoadmap && !hasContext && !hasSummary)
440
440
  continue;
441
- // Determine milestone status
442
- let milestoneStatus = 'active';
443
- if (hasSummary)
444
- milestoneStatus = 'complete';
445
- else if (hasParked)
446
- milestoneStatus = 'parked';
447
441
  // Determine milestone title from roadmap H1 or CONTEXT heading
448
442
  let milestoneTitle = '';
449
443
  let roadmapContent = null;
@@ -453,6 +447,17 @@ export function migrateHierarchyToDb(basePath) {
453
447
  roadmap = parseRoadmap(roadmapContent);
454
448
  milestoneTitle = roadmap.title;
455
449
  }
450
+ // Determine milestone status
451
+ let milestoneStatus = 'active';
452
+ if (hasSummary)
453
+ milestoneStatus = 'complete';
454
+ else if (hasParked)
455
+ milestoneStatus = 'parked';
456
+ // Import milestones with all-done roadmap slices as complete (#3390, #3379)
457
+ // even when SUMMARY.md is missing — the roadmap checkboxes are authoritative.
458
+ else if (roadmap && roadmap.slices.length > 0 && roadmap.slices.every(s => s.done)) {
459
+ milestoneStatus = 'complete';
460
+ }
456
461
  if (!milestoneTitle && hasContext) {
457
462
  const contextContent = readFileSync(contextPath, 'utf-8');
458
463
  const h1Match = contextContent.match(/^#\s+(.+)/m);
@@ -492,7 +497,8 @@ export function migrateHierarchyToDb(basePath) {
492
497
  // Parse roadmap for slices
493
498
  if (!roadmap)
494
499
  continue;
495
- for (const sliceEntry of roadmap.slices) {
500
+ for (let si = 0; si < roadmap.slices.length; si++) {
501
+ const sliceEntry = roadmap.slices[si];
496
502
  // Per K002: use 'complete' not 'done'
497
503
  const sliceStatus = sliceEntry.done ? 'complete' : 'pending';
498
504
  // Parse slice plan early so goal is available for insertSlice planning column
@@ -510,6 +516,7 @@ export function migrateHierarchyToDb(basePath) {
510
516
  risk: sliceEntry.risk,
511
517
  depends: sliceEntry.depends,
512
518
  demo: sliceEntry.demo,
519
+ sequence: si + 1, // Preserve roadmap parse order (#3356)
513
520
  planning: {
514
521
  goal: plan?.goal ?? '',
515
522
  },
@@ -440,19 +440,25 @@ export function spawnWorker(basePath, milestoneId) {
440
440
  return false;
441
441
  let child;
442
442
  try {
443
+ const workerEnv = {
444
+ ...process.env,
445
+ GSD_MILESTONE_LOCK: milestoneId,
446
+ // Pass the real project root so workers don't need to re-derive it.
447
+ // Without this, process.cwd() resolves symlinks and the worktree
448
+ // path heuristic can match the user-level ~/.gsd instead of the
449
+ // project .gsd, causing writes to ~ and corrupting user config.
450
+ GSD_PROJECT_ROOT: basePath,
451
+ // Prevent workers from spawning their own parallel sessions
452
+ GSD_PARALLEL_WORKER: "1",
453
+ };
454
+ // Apply worker model override if configured, so workers use a cheaper
455
+ // model (e.g. Haiku) rather than inheriting the coordinator's model.
456
+ if (state.config.worker_model) {
457
+ workerEnv.GSD_WORKER_MODEL = state.config.worker_model;
458
+ }
443
459
  child = spawn(process.execPath, [binPath, "headless", "--json", "auto"], {
444
460
  cwd: worker.worktreePath,
445
- env: {
446
- ...process.env,
447
- GSD_MILESTONE_LOCK: milestoneId,
448
- // Pass the real project root so workers don't need to re-derive it.
449
- // Without this, process.cwd() resolves symlinks and the worktree
450
- // path heuristic can match the user-level ~/.gsd instead of the
451
- // project .gsd, causing writes to ~ and corrupting user config.
452
- GSD_PROJECT_ROOT: basePath,
453
- // Prevent workers from spawning their own parallel sessions
454
- GSD_PARALLEL_WORKER: "1",
455
- },
461
+ env: workerEnv,
456
462
  stdio: ["ignore", "pipe", "pipe"],
457
463
  detached: false,
458
464
  });
@@ -197,8 +197,10 @@ export async function checkPackageExistence(tasks, _basePath) {
197
197
  export function normalizeFilePath(filePath) {
198
198
  if (!filePath)
199
199
  return filePath;
200
+ // Strip backtick wrapping from LLM-generated paths (#3649)
201
+ let normalized = filePath.replace(/`/g, "");
200
202
  // Normalize path separators to forward slashes
201
- let normalized = filePath.replace(/\\/g, "/");
203
+ normalized = normalized.replace(/\\/g, "/");
202
204
  // Remove leading ./
203
205
  while (normalized.startsWith("./")) {
204
206
  normalized = normalized.slice(2);
@@ -225,10 +227,13 @@ function getExpectedOutputsUpTo(tasks, taskIndex) {
225
227
  return outputs;
226
228
  }
227
229
  /**
228
- * Check that all files referenced in task.files and task.inputs either:
230
+ * Check that all files referenced in task.inputs either:
229
231
  * 1. Exist on disk, OR
230
232
  * 2. Are in a prior task's expected_output
231
233
  *
234
+ * task.files ("files likely touched") is excluded — it intentionally includes
235
+ * files the task will create, so they don't need to pre-exist (#3626).
236
+ *
232
237
  * All paths are normalized before comparison to ensure ./src/a.ts matches src/a.ts.
233
238
  */
234
239
  export function checkFilePathConsistency(tasks, basePath) {
@@ -236,7 +241,7 @@ export function checkFilePathConsistency(tasks, basePath) {
236
241
  for (let i = 0; i < tasks.length; i++) {
237
242
  const task = tasks[i];
238
243
  const priorOutputs = getExpectedOutputsUpTo(tasks, i);
239
- const filesToCheck = [...task.files, ...task.inputs];
244
+ const filesToCheck = [...task.inputs];
240
245
  for (const file of filesToCheck) {
241
246
  // Skip empty strings
242
247
  if (!file.trim())
@@ -281,10 +286,12 @@ export function checkTaskOrdering(tasks, _basePath) {
281
286
  }
282
287
  }
283
288
  }
284
- // Check each task's inputs against file creators
289
+ // Check each task's inputs against file creators.
290
+ // Only check task.inputs — task.files ("files likely touched") intentionally
291
+ // includes files the task will create, so they don't indicate read-before-create (#3677).
285
292
  for (let i = 0; i < tasks.length; i++) {
286
293
  const task = tasks[i];
287
- const filesToCheck = [...task.files, ...task.inputs];
294
+ const filesToCheck = [...task.inputs];
288
295
  for (const file of filesToCheck) {
289
296
  const normalizedFile = normalizeFilePath(file);
290
297
  const creator = fileCreators.get(normalizedFile);
@@ -80,6 +80,9 @@ export const KNOWN_PREFERENCE_KEYS = new Set([
80
80
  "enhanced_verification_pre",
81
81
  "enhanced_verification_post",
82
82
  "enhanced_verification_strict",
83
+ "discuss_preparation",
84
+ "discuss_web_research",
85
+ "discuss_depth",
83
86
  ]);
84
87
  /** Canonical list of all dispatch unit types. */
85
88
  export const KNOWN_UNIT_TYPES = [