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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/dist/mcp-server.js +6 -2
  2. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  3. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  4. package/dist/resources/extensions/gsd/auto/finalize-timeout.js +2 -0
  5. package/dist/resources/extensions/gsd/auto/loop.js +2 -2
  6. package/dist/resources/extensions/gsd/auto/phases.js +48 -5
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  8. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  9. package/dist/resources/extensions/gsd/auto/types.js +2 -0
  10. package/dist/resources/extensions/gsd/auto-dashboard.js +2 -1
  11. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  12. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  13. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  14. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  15. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  16. package/dist/resources/extensions/gsd/auto-start.js +175 -12
  17. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  18. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  19. package/dist/resources/extensions/gsd/auto.js +21 -15
  20. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  22. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  23. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  24. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +3 -1
  26. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  27. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +23 -2
  29. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  30. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  31. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  32. package/dist/resources/extensions/gsd/detection.js +1 -1
  33. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  35. package/dist/resources/extensions/gsd/doctor.js +2 -1
  36. package/dist/resources/extensions/gsd/files.js +17 -0
  37. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  38. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  39. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  40. package/dist/resources/extensions/gsd/index.js +1 -1
  41. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  42. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  43. package/dist/resources/extensions/gsd/notification-overlay.js +1 -1
  44. package/dist/resources/extensions/gsd/notification-widget.js +2 -1
  45. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +1 -1
  46. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  47. package/dist/resources/extensions/gsd/pre-execution-checks.js +26 -5
  48. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  49. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  50. package/dist/resources/extensions/gsd/preferences.js +9 -2
  51. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  52. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  53. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  54. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  56. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  57. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  59. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  60. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  61. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  62. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  63. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  64. package/dist/resources/extensions/gsd/quick.js +19 -15
  65. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  66. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  67. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  68. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  69. package/dist/resources/extensions/gsd/state.js +115 -28
  70. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  71. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  72. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  73. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  74. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  75. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  76. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  77. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  78. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  79. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  80. package/dist/resources/extensions/gsd/undo.js +3 -2
  81. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  82. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  83. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  84. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  85. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  86. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  87. package/dist/resources/extensions/gsd/worktree.js +9 -0
  88. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  89. package/dist/resources/extensions/subagent/agents.js +19 -5
  90. package/dist/web/standalone/.next/BUILD_ID +1 -1
  91. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
  92. package/dist/web/standalone/.next/build-manifest.json +3 -3
  93. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  94. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  96. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.html +1 -1
  112. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
  119. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  122. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  123. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  124. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  125. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  126. package/package.json +1 -1
  127. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  129. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  131. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  133. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  145. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  148. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  149. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  150. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  151. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  152. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  153. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  154. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  155. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  156. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  157. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  158. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  159. package/packages/pi-tui/dist/components/image.js +4 -0
  160. package/packages/pi-tui/dist/components/image.js.map +1 -1
  161. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  162. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  163. package/packages/pi-tui/dist/components/image.test.js +32 -0
  164. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  165. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  166. package/packages/pi-tui/dist/tui.js +3 -1
  167. package/packages/pi-tui/dist/tui.js.map +1 -1
  168. package/packages/pi-tui/src/components/image.test.ts +36 -0
  169. package/packages/pi-tui/src/components/image.ts +5 -0
  170. package/packages/pi-tui/src/tui.ts +3 -1
  171. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  172. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  173. package/src/resources/extensions/gsd/auto/finalize-timeout.ts +3 -0
  174. package/src/resources/extensions/gsd/auto/loop.ts +2 -2
  175. package/src/resources/extensions/gsd/auto/phases.ts +68 -3
  176. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  177. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  178. package/src/resources/extensions/gsd/auto/types.ts +5 -0
  179. package/src/resources/extensions/gsd/auto-dashboard.ts +2 -1
  180. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  181. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  182. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  183. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  184. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  185. package/src/resources/extensions/gsd/auto-start.ts +188 -10
  186. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  187. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  188. package/src/resources/extensions/gsd/auto.ts +19 -8
  189. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  190. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  191. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  192. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  193. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  194. package/src/resources/extensions/gsd/bootstrap/system-context.ts +3 -1
  195. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  196. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  197. package/src/resources/extensions/gsd/commands/handlers/core.ts +26 -2
  198. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  199. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  200. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  201. package/src/resources/extensions/gsd/detection.ts +1 -1
  202. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  203. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  204. package/src/resources/extensions/gsd/doctor.ts +2 -1
  205. package/src/resources/extensions/gsd/files.ts +19 -0
  206. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  207. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  208. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  209. package/src/resources/extensions/gsd/index.ts +1 -0
  210. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  211. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  212. package/src/resources/extensions/gsd/notification-overlay.ts +1 -1
  213. package/src/resources/extensions/gsd/notification-widget.ts +2 -1
  214. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +1 -1
  215. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  216. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -7
  217. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  218. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  219. package/src/resources/extensions/gsd/preferences.ts +9 -2
  220. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  221. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  222. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  223. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  224. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  225. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  226. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  227. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  228. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  229. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  230. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  231. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  232. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  233. package/src/resources/extensions/gsd/quick.ts +20 -15
  234. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  235. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  236. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  237. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  238. package/src/resources/extensions/gsd/state.ts +115 -26
  239. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  240. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  241. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  242. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  243. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  244. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  245. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  246. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  247. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  248. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  249. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  250. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  251. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  252. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  253. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +125 -0
  254. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  255. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +69 -0
  256. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  257. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  258. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  259. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  260. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  261. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  262. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  263. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  264. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  265. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  266. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +11 -10
  267. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  268. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  269. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +189 -0
  270. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  271. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  272. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  273. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  274. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +284 -20
  275. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  276. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  277. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  278. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  279. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  280. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  281. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  282. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  283. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  284. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  285. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  286. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  287. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  288. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  289. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  290. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  291. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  292. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  293. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  294. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  295. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  296. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  297. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  298. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  299. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  300. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  301. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  302. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  303. package/src/resources/extensions/gsd/tests/subagent-agent-discovery.test.ts +47 -0
  304. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  305. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  306. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  307. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  308. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  309. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  310. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  311. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  312. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  313. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  314. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  315. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  316. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  317. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  318. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  319. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  320. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  321. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  322. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  323. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  324. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  325. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  326. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  327. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  328. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  329. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  330. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  331. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  332. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  333. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  334. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  335. package/src/resources/extensions/gsd/types.ts +4 -0
  336. package/src/resources/extensions/gsd/undo.ts +3 -2
  337. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  338. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  339. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  340. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  341. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  342. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  343. package/src/resources/extensions/gsd/worktree.ts +10 -0
  344. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  345. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  346. package/src/resources/extensions/subagent/agents.ts +30 -6
  347. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  348. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → E0hBt4ifuG7QBbhUR5-6U}/_buildManifest.js +0 -0
  349. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → E0hBt4ifuG7QBbhUR5-6U}/_ssgManifest.js +0 -0
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Regression test for #3461: createAutoWorktree must use git.main_branch
3
+ * preference when META.json integration branch is absent.
4
+ */
5
+ import { test } from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ test("auto-worktree.ts includes main_branch preference in startPoint fallback (#3461)", () => {
11
+ const src = readFileSync(
12
+ join(import.meta.dirname, "..", "auto-worktree.ts"),
13
+ "utf-8",
14
+ );
15
+ // The fix adds gitPrefs?.main_branch to the startPoint fallback chain
16
+ assert.ok(
17
+ src.includes("gitPrefs?.main_branch") || src.includes("prefs.main_branch"),
18
+ "createAutoWorktree must check git.main_branch preference before falling back to nativeDetectMainBranch",
19
+ );
20
+ });
@@ -221,7 +221,8 @@ describe('worktree-sync-milestones', async () => {
221
221
 
222
222
  try {
223
223
  // Build worktree milestone structure with slice-level and task-level files
224
- const wtSliceDir = join(wtBase, '.gsd', 'milestones', 'M001', 'slices', 'S01');
224
+ // Use M002 as the milestone to sync, M001 as the "current" being merged (skipped)
225
+ const wtSliceDir = join(wtBase, '.gsd', 'milestones', 'M002', 'slices', 'S01');
225
226
  const wtTasksDir = join(wtSliceDir, 'tasks');
226
227
  mkdirSync(wtTasksDir, { recursive: true });
227
228
  writeFileSync(join(wtSliceDir, 'S01-SUMMARY.md'), '# S01 Summary');
@@ -229,11 +230,12 @@ describe('worktree-sync-milestones', async () => {
229
230
  writeFileSync(join(wtTasksDir, 'T02-SUMMARY.md'), '# T02 Summary');
230
231
 
231
232
  // Main project root starts with only the milestone directory (no slices yet)
232
- mkdirSync(join(mainBase, '.gsd', 'milestones', 'M001'), { recursive: true });
233
+ mkdirSync(join(mainBase, '.gsd', 'milestones', 'M002'), { recursive: true });
233
234
 
235
+ // Pass M001 as milestoneId (the one being merged/skipped), M002 should still sync
234
236
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
235
237
 
236
- const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M001', 'slices', 'S01');
238
+ const mainSliceDir = join(mainBase, '.gsd', 'milestones', 'M002', 'slices', 'S01');
237
239
  const mainTasksDir = join(mainSliceDir, 'tasks');
238
240
 
239
241
  assert.ok(
@@ -341,16 +343,16 @@ describe('worktree-sync-milestones', async () => {
341
343
  'M002 missing in main before sync',
342
344
  );
343
345
 
344
- // Sync with milestoneId = M001 (the current milestone)
346
+ // Sync with milestoneId = M001 (the current milestone being merged — skipped)
345
347
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M001');
346
348
 
347
- // M001 should be synced (current milestone — always synced)
349
+ // M001 should be SKIPPED (current milestone being merged #3641)
348
350
  assert.ok(
349
- existsSync(join(mainBase, '.gsd', 'milestones', 'M001', 'M001-SUMMARY.md')),
350
- 'M001 SUMMARY synced to main',
351
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'M001', 'M001-SUMMARY.md')),
352
+ 'M001 SUMMARY NOT synced (current milestone skipped to prevent merge conflicts)',
351
353
  );
352
354
 
353
- // M002 should ALSO be synced (next milestone — the fix)
355
+ // M002 should be synced (other milestone — not skipped)
354
356
  assert.ok(
355
357
  existsSync(join(mainBase, '.gsd', 'milestones', 'M002-abc123', 'M002-abc123-CONTEXT.md')),
356
358
  'M002 CONTEXT synced to main (next-milestone fix)',
@@ -407,20 +409,17 @@ describe('worktree-sync-milestones', async () => {
407
409
  writeFileSync(join(wtBase, '.gsd', 'REQUIREMENTS.md'), '# Requirements\n## R001-R089\n## R090 — SCIM\n## R091 — WebAuthn');
408
410
  writeFileSync(join(wtBase, '.gsd', 'PROJECT.md'), '# Project\nMilestones: M001-M007');
409
411
 
410
- // Sync with milestoneId = M006 (the completing milestone)
412
+ // Sync with milestoneId = M006 (the completing milestone — skipped by sync)
411
413
  const { synced } = syncWorktreeStateBack(mainBase, wtBase, 'M006-589wvh');
412
414
 
413
- // Verify M006 artifacts synced
414
- assert.ok(
415
- existsSync(join(mainBase, '.gsd', 'milestones', 'M006-589wvh', 'M006-589wvh-SUMMARY.md')),
416
- 'M006 SUMMARY synced',
417
- );
415
+ // M006 is the current milestone being merged — it should be SKIPPED (#3641)
416
+ // Its files are already in the milestone branch and would conflict with squash merge.
418
417
  assert.ok(
419
- existsSync(join(mainBase, '.gsd', 'milestones', 'M006-589wvh', 'slices', 'S01', 'S01-SUMMARY.md')),
420
- 'M006 S01 SUMMARY synced',
418
+ !existsSync(join(mainBase, '.gsd', 'milestones', 'M006-589wvh', 'M006-589wvh-SUMMARY.md')),
419
+ 'M006 SUMMARY NOT synced (current milestone skipped)',
421
420
  );
422
421
 
423
- // Verify M007 artifacts synced (the critical fix)
422
+ // Verify M007 artifacts synced (the critical fix — other milestones still sync)
424
423
  assert.ok(
425
424
  existsSync(join(mainBase, '.gsd', 'milestones', 'M007-wortc8', 'M007-wortc8-CONTEXT.md')),
426
425
  'M007 CONTEXT synced to main (next-milestone)',
@@ -47,7 +47,8 @@ function writeFile(dir: string, relativePath: string, content: string): void {
47
47
  test("syncWorktreeStateBack copies task summaries from tasks/ subdirectory (#1678)", () => {
48
48
  const mainBase = makeTempDir("main");
49
49
  const wtBase = makeTempDir("wt");
50
- const mid = "M001";
50
+ const currentMid = "M000"; // milestone being merged (skipped by sync)
51
+ const mid = "M001"; // other milestone that should be synced
51
52
 
52
53
  try {
53
54
  // Set up worktree with milestone, slice, and task files
@@ -64,8 +65,8 @@ test("syncWorktreeStateBack copies task summaries from tasks/ subdirectory (#167
64
65
  // Set up main with empty .gsd
65
66
  mkdirSync(join(mainBase, ".gsd"), { recursive: true });
66
67
 
67
- // Run sync
68
- const result = syncWorktreeStateBack(mainBase, wtBase, mid);
68
+ // Run sync — currentMid is skipped, mid (M001) should be synced
69
+ const result = syncWorktreeStateBack(mainBase, wtBase, currentMid);
69
70
 
70
71
  // Verify milestone-level files synced
71
72
  assert.ok(
@@ -126,7 +127,8 @@ test("syncWorktreeStateBack copies task summaries from tasks/ subdirectory (#167
126
127
  test("syncWorktreeStateBack handles multiple slices with tasks (#1678)", () => {
127
128
  const mainBase = makeTempDir("main");
128
129
  const wtBase = makeTempDir("wt");
129
- const mid = "M002";
130
+ const currentMid = "M000"; // milestone being merged (skipped)
131
+ const mid = "M002"; // other milestone that should be synced
130
132
 
131
133
  try {
132
134
  // Set up two slices with tasks
@@ -139,7 +141,7 @@ test("syncWorktreeStateBack handles multiple slices with tasks (#1678)", () => {
139
141
 
140
142
  mkdirSync(join(mainBase, ".gsd"), { recursive: true });
141
143
 
142
- const result = syncWorktreeStateBack(mainBase, wtBase, mid);
144
+ const result = syncWorktreeStateBack(mainBase, wtBase, currentMid);
143
145
 
144
146
  // All task summaries from both slices should be synced
145
147
  assert.ok(existsSync(join(mainBase, `.gsd/milestones/${mid}/slices/S01/tasks/T01-SUMMARY.md`)));
@@ -160,7 +162,8 @@ test("syncWorktreeStateBack handles multiple slices with tasks (#1678)", () => {
160
162
  test("syncWorktreeStateBack handles slices without tasks/ directory", () => {
161
163
  const mainBase = makeTempDir("main");
162
164
  const wtBase = makeTempDir("wt");
163
- const mid = "M003";
165
+ const currentMid = "M000"; // milestone being merged (skipped)
166
+ const mid = "M003"; // other milestone that should be synced
164
167
 
165
168
  try {
166
169
  // Slice with no tasks/ subdirectory (legitimate case: pre-planning)
@@ -168,7 +171,7 @@ test("syncWorktreeStateBack handles slices without tasks/ directory", () => {
168
171
 
169
172
  mkdirSync(join(mainBase, ".gsd"), { recursive: true });
170
173
 
171
- const result = syncWorktreeStateBack(mainBase, wtBase, mid);
174
+ const result = syncWorktreeStateBack(mainBase, wtBase, currentMid);
172
175
 
173
176
  // Should sync the slice file without errors
174
177
  assert.ok(existsSync(join(mainBase, `.gsd/milestones/${mid}/slices/S01/S01-RESEARCH.md`)));
@@ -183,7 +186,8 @@ test("syncWorktreeStateBack handles slices without tasks/ directory", () => {
183
186
  test("syncWorktreeStateBack ignores non-md files in tasks/", () => {
184
187
  const mainBase = makeTempDir("main");
185
188
  const wtBase = makeTempDir("wt");
186
- const mid = "M004";
189
+ const currentMid = "M000"; // milestone being merged (skipped)
190
+ const mid = "M004"; // other milestone that should be synced
187
191
 
188
192
  try {
189
193
  writeFile(wtBase, `.gsd/milestones/${mid}/slices/S01/S01-PLAN.md`, "# Plan\n");
@@ -194,7 +198,7 @@ test("syncWorktreeStateBack ignores non-md files in tasks/", () => {
194
198
 
195
199
  mkdirSync(join(mainBase, ".gsd"), { recursive: true });
196
200
 
197
- const result = syncWorktreeStateBack(mainBase, wtBase, mid);
201
+ const result = syncWorktreeStateBack(mainBase, wtBase, currentMid);
198
202
 
199
203
  // Only .md files should be synced
200
204
  assert.ok(existsSync(join(mainBase, `.gsd/milestones/${mid}/slices/S01/tasks/T01-SUMMARY.md`)));
@@ -14,9 +14,11 @@ import {
14
14
  resolveProjectRoot,
15
15
  setActiveMilestoneId,
16
16
  SLICE_BRANCH_RE,
17
+ _resetServiceCache,
17
18
  } from "../worktree.ts";
18
19
  import { readIntegrationBranch } from "../git-service.ts";
19
20
  import { _resetHasChangesCache } from "../native-git-bridge.ts";
21
+ import { _clearGsdRootCache } from "../paths.ts";
20
22
  import { describe, test } from 'node:test';
21
23
  import assert from 'node:assert/strict';
22
24
 
@@ -165,15 +167,30 @@ describe('worktree', async () => {
165
167
  run("git checkout -b my-feature", repo);
166
168
  captureIntegrationBranch(repo, "M001");
167
169
 
168
- // Without milestone set, getMainBranch returns "main"
169
- setActiveMilestoneId(repo, null);
170
- assert.deepStrictEqual(getMainBranch(repo), "main",
171
- "getMainBranch returns main without milestone set");
172
-
173
- // With milestone set, getMainBranch returns feature branch
174
- setActiveMilestoneId(repo, "M001");
175
- assert.deepStrictEqual(getMainBranch(repo), "my-feature",
176
- "getMainBranch returns integration branch with milestone set");
170
+ // Isolate from user's global preferences (which may have git.main_branch set).
171
+ // Reset caches so getService() creates a fresh instance with empty preferences.
172
+ const originalHome = process.env.HOME;
173
+ const fakeHome = mkdtempSync(join(tmpdir(), "gsd-fake-home-"));
174
+ process.env.HOME = fakeHome;
175
+ _clearGsdRootCache();
176
+ _resetServiceCache();
177
+
178
+ try {
179
+ // Without milestone set, getMainBranch returns "main"
180
+ setActiveMilestoneId(repo, null);
181
+ assert.deepStrictEqual(getMainBranch(repo), "main",
182
+ "getMainBranch returns main without milestone set");
183
+
184
+ // With milestone set, getMainBranch returns feature branch
185
+ setActiveMilestoneId(repo, "M001");
186
+ assert.deepStrictEqual(getMainBranch(repo), "my-feature",
187
+ "getMainBranch returns integration branch with milestone set");
188
+ } finally {
189
+ process.env.HOME = originalHome;
190
+ _clearGsdRootCache();
191
+ _resetServiceCache();
192
+ rmSync(fakeHome, { recursive: true, force: true });
193
+ }
177
194
 
178
195
  rmSync(repo, { recursive: true, force: true });
179
196
  }
@@ -12,6 +12,7 @@
12
12
  import test from 'node:test';
13
13
  import assert from 'node:assert/strict';
14
14
  import {
15
+ isDepthConfirmationAnswer,
15
16
  shouldBlockContextWrite,
16
17
  isDepthVerified,
17
18
  isQueuePhaseActive,
@@ -117,9 +118,9 @@ test('write-gate: regex does not match slice context files (S01-CONTEXT.md)', ()
117
118
  assert.strictEqual(result.block, false, 'S01-CONTEXT.md should not be blocked');
118
119
  });
119
120
 
120
- // ─── Scenario 7: Error message contains actionable instruction ──
121
+ // ─── Scenario 7: Error message contains actionable instruction and anti-bypass language ──
121
122
 
122
- test('write-gate: blocked reason contains depth_verification keyword', () => {
123
+ test('write-gate: blocked reason contains depth_verification keyword and anti-bypass language', () => {
123
124
  const result = shouldBlockContextWrite(
124
125
  'write',
125
126
  '.gsd/milestones/M999/M999-CONTEXT.md',
@@ -129,6 +130,8 @@ test('write-gate: blocked reason contains depth_verification keyword', () => {
129
130
  assert.strictEqual(result.block, true);
130
131
  assert.ok(result.reason!.includes('depth_verification'), 'reason should mention depth_verification question id');
131
132
  assert.ok(result.reason!.includes('ask_user_questions'), 'reason should mention ask_user_questions tool');
133
+ assert.ok(result.reason!.includes('MUST NOT'), 'reason should include anti-bypass language');
134
+ assert.ok(result.reason!.includes('(Recommended)'), 'reason should specify the required confirmation option');
132
135
  });
133
136
 
134
137
  // ─── Scenario 8: Queue mode blocks CONTEXT.md write without depth verification ──
@@ -191,3 +194,125 @@ test('write-gate: markDepthVerified unblocks queue-mode writes when milestoneId
191
194
 
192
195
  clearDiscussionFlowState();
193
196
  });
197
+
198
+ // ─── Standard options fixture used across depth confirmation tests ──
199
+
200
+ const STANDARD_OPTIONS = [
201
+ { label: 'Yes, you got it (Recommended)' },
202
+ { label: 'Not quite — let me clarify' },
203
+ ];
204
+
205
+ // ─── Scenario 11: accepts first option (confirmation) with structural validation ──
206
+
207
+ test('write-gate: isDepthConfirmationAnswer accepts first option with options present', () => {
208
+ assert.strictEqual(
209
+ isDepthConfirmationAnswer('Yes, you got it (Recommended)', STANDARD_OPTIONS),
210
+ true,
211
+ 'should accept exact match of first option label',
212
+ );
213
+ });
214
+
215
+ // ─── Scenario 12: rejects second option (decline) ──
216
+
217
+ test('write-gate: isDepthConfirmationAnswer rejects decline option', () => {
218
+ assert.strictEqual(
219
+ isDepthConfirmationAnswer('Not quite — let me clarify', STANDARD_OPTIONS),
220
+ false,
221
+ 'should reject the clarification option',
222
+ );
223
+ });
224
+
225
+ // ─── Scenario 13: rejects "None of the above" ──
226
+
227
+ test('write-gate: isDepthConfirmationAnswer rejects None of the above', () => {
228
+ assert.strictEqual(
229
+ isDepthConfirmationAnswer('None of the above', STANDARD_OPTIONS),
230
+ false,
231
+ 'should reject None of the above',
232
+ );
233
+ });
234
+
235
+ // ─── Scenario 14: rejects garbage/empty input ──
236
+
237
+ test('write-gate: isDepthConfirmationAnswer rejects garbage and edge cases', () => {
238
+ assert.strictEqual(isDepthConfirmationAnswer('discord', STANDARD_OPTIONS), false, 'garbage string');
239
+ assert.strictEqual(isDepthConfirmationAnswer('', STANDARD_OPTIONS), false, 'empty string');
240
+ assert.strictEqual(isDepthConfirmationAnswer(undefined, STANDARD_OPTIONS), false, 'undefined');
241
+ assert.strictEqual(isDepthConfirmationAnswer(null, STANDARD_OPTIONS), false, 'null');
242
+ assert.strictEqual(isDepthConfirmationAnswer(42, STANDARD_OPTIONS), false, 'number');
243
+ });
244
+
245
+ // ─── Scenario 15: handles array-wrapped selection ──
246
+
247
+ test('write-gate: isDepthConfirmationAnswer handles array-wrapped selected value', () => {
248
+ assert.strictEqual(
249
+ isDepthConfirmationAnswer(['Yes, you got it (Recommended)'], STANDARD_OPTIONS),
250
+ true,
251
+ 'should accept array-wrapped confirmation',
252
+ );
253
+ assert.strictEqual(
254
+ isDepthConfirmationAnswer(['Not quite — let me clarify'], STANDARD_OPTIONS),
255
+ false,
256
+ 'should reject array-wrapped decline',
257
+ );
258
+ assert.strictEqual(
259
+ isDepthConfirmationAnswer([], STANDARD_OPTIONS),
260
+ false,
261
+ 'should reject empty array',
262
+ );
263
+ });
264
+
265
+ // ─── Scenario 16: rejects free-form "Other" text that contains "(Recommended)" ──
266
+
267
+ test('write-gate: isDepthConfirmationAnswer rejects free-form text containing Recommended', () => {
268
+ assert.strictEqual(
269
+ isDepthConfirmationAnswer('I think this is fine (Recommended)', STANDARD_OPTIONS),
270
+ false,
271
+ 'free-form text with (Recommended) substring must not unlock gate',
272
+ );
273
+ assert.strictEqual(
274
+ isDepthConfirmationAnswer('(Recommended)', STANDARD_OPTIONS),
275
+ false,
276
+ 'bare (Recommended) string must not unlock gate',
277
+ );
278
+ });
279
+
280
+ // ─── Scenario 17: works with changed label text (decoupled from specific copy) ──
281
+
282
+ test('write-gate: isDepthConfirmationAnswer works with different label text', () => {
283
+ const customOptions = [
284
+ { label: 'Looks good, proceed' },
285
+ { label: 'Needs more discussion' },
286
+ ];
287
+ assert.strictEqual(
288
+ isDepthConfirmationAnswer('Looks good, proceed', customOptions),
289
+ true,
290
+ 'should accept first option regardless of label text',
291
+ );
292
+ assert.strictEqual(
293
+ isDepthConfirmationAnswer('Needs more discussion', customOptions),
294
+ false,
295
+ 'should reject second option',
296
+ );
297
+ // Old label should NOT work with new options
298
+ assert.strictEqual(
299
+ isDepthConfirmationAnswer('Yes, you got it (Recommended)', customOptions),
300
+ false,
301
+ 'old label text should not match new options',
302
+ );
303
+ });
304
+
305
+ // ─── Scenario 18: fallback when options not available ──
306
+
307
+ test('write-gate: isDepthConfirmationAnswer falls back to (Recommended) match without options', () => {
308
+ assert.strictEqual(
309
+ isDepthConfirmationAnswer('Yes, you got it (Recommended)'),
310
+ true,
311
+ 'should accept via fallback when no options provided',
312
+ );
313
+ assert.strictEqual(
314
+ isDepthConfirmationAnswer('Not quite — let me clarify'),
315
+ false,
316
+ 'should reject non-Recommended via fallback',
317
+ );
318
+ });
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Regression test for #3441: guided flow must treat a roadmap with zero
3
+ * parseable slices the same as no roadmap — offer "Create roadmap" not "Go auto".
4
+ */
5
+ import { test } from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ test("guided-flow checks roadmap slice count before offering auto (#3441)", () => {
11
+ const src = readFileSync(
12
+ join(import.meta.dirname, "..", "guided-flow.ts"),
13
+ "utf-8",
14
+ );
15
+ assert.ok(
16
+ src.includes("roadmapHasSlices") || src.includes("parseRoadmapSlices"),
17
+ "Guided flow must parse roadmap for slices before deciding which options to show",
18
+ );
19
+ });
@@ -23,7 +23,7 @@ import { invalidateStateCache } from "../state.js";
23
23
  import { renderAllProjections, stripIdPrefix } from "../workflow-projections.js";
24
24
  import { writeManifest } from "../workflow-manifest.js";
25
25
  import { appendEvent } from "../workflow-events.js";
26
- import { logWarning } from "../workflow-logger.js";
26
+ import { logWarning, logError } from "../workflow-logger.js";
27
27
 
28
28
  export interface CompleteMilestoneParams {
29
29
  milestoneId: string;
@@ -218,9 +218,19 @@ export async function handleCompleteMilestone(
218
218
  clearParseCache();
219
219
 
220
220
  // ── Post-mutation hook: projections, manifest, event log ───────────────
221
+ // Separate try/catch per step so a projection failure doesn't prevent
222
+ // the event log entry (critical for worktree reconciliation).
221
223
  try {
222
224
  await renderAllProjections(basePath, params.milestoneId);
225
+ } catch (projErr) {
226
+ logWarning("tool", `complete-milestone projection warning: ${(projErr as Error).message}`);
227
+ }
228
+ try {
223
229
  writeManifest(basePath);
230
+ } catch (mfErr) {
231
+ logWarning("tool", `complete-milestone manifest warning: ${(mfErr as Error).message}`);
232
+ }
233
+ try {
224
234
  appendEvent(basePath, {
225
235
  cmd: "complete-milestone",
226
236
  params: { milestoneId: params.milestoneId },
@@ -229,8 +239,8 @@ export async function handleCompleteMilestone(
229
239
  actor_name: params.actorName,
230
240
  trigger_reason: params.triggerReason,
231
241
  });
232
- } catch (hookErr) {
233
- logWarning("tool", `complete-milestone post-mutation hook warning: ${(hookErr as Error).message}`);
242
+ } catch (eventErr) {
243
+ logError("tool", `complete-milestone event log FAILED — completion invisible to reconciliation`, { error: (eventErr as Error).message });
234
244
  }
235
245
 
236
246
  return {
@@ -30,7 +30,7 @@ import { renderRoadmapCheckboxes } from "../markdown-renderer.js";
30
30
  import { renderAllProjections } from "../workflow-projections.js";
31
31
  import { writeManifest } from "../workflow-manifest.js";
32
32
  import { appendEvent } from "../workflow-events.js";
33
- import { logWarning } from "../workflow-logger.js";
33
+ import { logWarning, logError } from "../workflow-logger.js";
34
34
 
35
35
  export interface CompleteSliceResult {
36
36
  sliceId: string;
@@ -233,8 +233,18 @@ export async function handleCompleteSlice(
233
233
  return { error: ownershipErr };
234
234
  }
235
235
 
236
+ // ── Verification content gate (#3580) ──────────────────────────────────
237
+ // Reject completion when the provided verification/UAT clearly indicates
238
+ // the slice is blocked or failed. Prevents prompt regressions from
239
+ // silently advancing blocked slices.
240
+ const BLOCKED_SIGNALS = /\b(status:\s*blocked|verification_result:\s*failed|slice is blocked|cannot complete|verification failed)\b/i;
241
+ if (BLOCKED_SIGNALS.test(params.verification || "") || BLOCKED_SIGNALS.test(params.uatContent || "")) {
242
+ return { error: `slice verification indicates blocked/failed state — do not complete a slice that has not passed verification. Address the blockers and re-verify first.` };
243
+ }
244
+
236
245
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
237
246
  const completedAt = new Date().toISOString();
247
+ const originalSliceStatus = getSlice(params.milestoneId, params.sliceId)?.status ?? "pending";
238
248
  let guardError: string | null = null;
239
249
 
240
250
  transaction(() => {
@@ -268,8 +278,8 @@ export async function handleCompleteSlice(
268
278
  }
269
279
 
270
280
  // All guards passed — perform writes
271
- insertMilestone({ id: params.milestoneId });
272
- insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
281
+ insertMilestone({ id: params.milestoneId, title: params.milestoneId });
282
+ insertSlice({ id: params.sliceId, milestoneId: params.milestoneId, title: params.sliceId });
273
283
  updateSliceStatus(params.milestoneId, params.sliceId, "complete", completedAt);
274
284
  });
275
285
 
@@ -312,7 +322,7 @@ export async function handleCompleteSlice(
312
322
  } catch (renderErr) {
313
323
  // Disk render failed — roll back DB status so state stays consistent
314
324
  logWarning("tool", `complete_slice — disk render failed for ${params.milestoneId}/${params.sliceId}, rolling back DB status`, { error: (renderErr as Error).message });
315
- updateSliceStatus(params.milestoneId, params.sliceId, 'pending');
325
+ updateSliceStatus(params.milestoneId, params.sliceId, originalSliceStatus);
316
326
  invalidateStateCache();
317
327
  return { error: `disk render failed: ${(renderErr as Error).message}` };
318
328
  }
@@ -326,9 +336,19 @@ export async function handleCompleteSlice(
326
336
  clearParseCache();
327
337
 
328
338
  // ── Post-mutation hook: projections, manifest, event log ───────────────
339
+ // Separate try/catch per step so a projection failure doesn't prevent
340
+ // the event log entry (critical for worktree reconciliation).
329
341
  try {
330
342
  await renderAllProjections(basePath, params.milestoneId);
343
+ } catch (projErr) {
344
+ logWarning("tool", `complete-slice projection warning for ${params.milestoneId}/${params.sliceId}: ${(projErr as Error).message}`);
345
+ }
346
+ try {
331
347
  writeManifest(basePath);
348
+ } catch (mfErr) {
349
+ logWarning("tool", `complete-slice manifest warning: ${(mfErr as Error).message}`);
350
+ }
351
+ try {
332
352
  appendEvent(basePath, {
333
353
  cmd: "complete-slice",
334
354
  params: { milestoneId: params.milestoneId, sliceId: params.sliceId },
@@ -337,8 +357,8 @@ export async function handleCompleteSlice(
337
357
  actor_name: params.actorName,
338
358
  trigger_reason: params.triggerReason,
339
359
  });
340
- } catch (hookErr) {
341
- logWarning("tool", `complete-slice post-mutation hook failed for ${params.milestoneId}/${params.sliceId}`, { error: (hookErr as Error).message });
360
+ } catch (eventErr) {
361
+ logError("tool", `complete-slice event log FAILED completion invisible to reconciliation`, { error: (eventErr as Error).message });
342
362
  }
343
363
 
344
364
  return {
@@ -33,7 +33,7 @@ import { renderPlanCheckboxes } from "../markdown-renderer.js";
33
33
  import { renderAllProjections, renderSummaryContent } from "../workflow-projections.js";
34
34
  import { writeManifest } from "../workflow-manifest.js";
35
35
  import { appendEvent } from "../workflow-events.js";
36
- import { logWarning } from "../workflow-logger.js";
36
+ import { logWarning, logError } from "../workflow-logger.js";
37
37
 
38
38
  export interface CompleteTaskResult {
39
39
  taskId: string;
@@ -44,6 +44,18 @@ export interface CompleteTaskResult {
44
44
 
45
45
  import type { TaskRow } from "../gsd-db.js";
46
46
 
47
+ /**
48
+ * Normalize a list parameter that may arrive as a string (newline-delimited
49
+ * bullet list from the LLM) into a string array (#3361).
50
+ */
51
+ function normalizeListParam(value: unknown): string[] {
52
+ if (Array.isArray(value)) return value.map(String);
53
+ if (typeof value === "string" && value.trim()) {
54
+ return value.split(/\n/).map(s => s.replace(/^[\s\-*•]+/, "").trim()).filter(Boolean);
55
+ }
56
+ return [];
57
+ }
58
+
47
59
  /**
48
60
  * Build a TaskRow-shaped object from CompleteTaskParams so the unified
49
61
  * renderSummaryContent() can be used at completion time (#2720).
@@ -63,8 +75,8 @@ function paramsToTaskRow(params: CompleteTaskParams, completedAt: string): TaskR
63
75
  blocker_discovered: params.blockerDiscovered ?? false,
64
76
  deviations: params.deviations ?? "",
65
77
  known_issues: params.knownIssues ?? "",
66
- key_files: params.keyFiles ?? [],
67
- key_decisions: params.keyDecisions ?? [],
78
+ key_files: normalizeListParam(params.keyFiles),
79
+ key_decisions: normalizeListParam(params.keyDecisions),
68
80
  full_summary_md: "",
69
81
  description: "",
70
82
  estimate: "",
@@ -140,8 +152,8 @@ export async function handleCompleteTask(
140
152
  }
141
153
 
142
154
  // All guards passed — perform writes
143
- insertMilestone({ id: params.milestoneId });
144
- insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
155
+ insertMilestone({ id: params.milestoneId, title: params.milestoneId });
156
+ insertSlice({ id: params.sliceId, milestoneId: params.milestoneId, title: params.sliceId });
145
157
  insertTask({
146
158
  id: params.taskId,
147
159
  sliceId: params.sliceId,
@@ -230,9 +242,19 @@ export async function handleCompleteTask(
230
242
  clearParseCache();
231
243
 
232
244
  // ── Post-mutation hook: projections, manifest, event log ───────────────
245
+ // Separate try/catch per step so a projection failure doesn't prevent
246
+ // the event log entry (critical for worktree reconciliation).
233
247
  try {
234
248
  await renderAllProjections(basePath, params.milestoneId);
249
+ } catch (projErr) {
250
+ logWarning("tool", `complete-task projection warning: ${(projErr as Error).message}`);
251
+ }
252
+ try {
235
253
  writeManifest(basePath);
254
+ } catch (mfErr) {
255
+ logWarning("tool", `complete-task manifest warning: ${(mfErr as Error).message}`);
256
+ }
257
+ try {
236
258
  appendEvent(basePath, {
237
259
  cmd: "complete-task",
238
260
  params: { milestoneId: params.milestoneId, sliceId: params.sliceId, taskId: params.taskId },
@@ -241,8 +263,8 @@ export async function handleCompleteTask(
241
263
  actor_name: params.actorName,
242
264
  trigger_reason: params.triggerReason,
243
265
  });
244
- } catch (hookErr) {
245
- logWarning("tool", `complete-task post-mutation hook warning: ${(hookErr as Error).message}`);
266
+ } catch (eventErr) {
267
+ logError("tool", `complete-task event log FAILED — completion invisible to reconciliation`, { error: (eventErr as Error).message });
246
268
  }
247
269
 
248
270
  return {
@@ -48,13 +48,13 @@ export interface PlanMilestoneParams {
48
48
  keyRisks?: Array<{ risk: string; whyItMatters: string }>;
49
49
  /** @optional — defaults to [] when omitted */
50
50
  proofStrategy?: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
51
- /** @optional — defaults to "Not provided." when omitted */
51
+ /** @optional — defaults to "" when omitted */
52
52
  verificationContract?: string;
53
- /** @optional — defaults to "Not provided." when omitted */
53
+ /** @optional — defaults to "" when omitted */
54
54
  verificationIntegration?: string;
55
- /** @optional — defaults to "Not provided." when omitted */
55
+ /** @optional — defaults to "" when omitted */
56
56
  verificationOperational?: string;
57
- /** @optional — defaults to "Not provided." when omitted */
57
+ /** @optional — defaults to "" when omitted */
58
58
  verificationUat?: string;
59
59
  /** @optional — defaults to [] when omitted */
60
60
  definitionOfDone?: string[];
@@ -168,10 +168,10 @@ function validateParams(params: PlanMilestoneParams): PlanMilestoneParams {
168
168
  successCriteria: params.successCriteria ? validateStringArray(params.successCriteria, "successCriteria") : [],
169
169
  keyRisks: params.keyRisks ? validateRiskEntries(params.keyRisks) : [],
170
170
  proofStrategy: params.proofStrategy ? validateProofStrategy(params.proofStrategy) : [],
171
- verificationContract: params.verificationContract ?? "Not provided.",
172
- verificationIntegration: params.verificationIntegration ?? "Not provided.",
173
- verificationOperational: params.verificationOperational ?? "Not provided.",
174
- verificationUat: params.verificationUat ?? "Not provided.",
171
+ verificationContract: params.verificationContract ?? "",
172
+ verificationIntegration: params.verificationIntegration ?? "",
173
+ verificationOperational: params.verificationOperational ?? "",
174
+ verificationUat: params.verificationUat ?? "",
175
175
  definitionOfDone: params.definitionOfDone ? validateStringArray(params.definitionOfDone, "definitionOfDone") : [],
176
176
  requirementCoverage: params.requirementCoverage ?? "Not provided.",
177
177
  boundaryMapMarkdown: params.boundaryMapMarkdown ?? "Not provided.",
@@ -256,7 +256,8 @@ export async function handlePlanMilestone(
256
256
  boundaryMapMarkdown: params.boundaryMapMarkdown,
257
257
  });
258
258
 
259
- for (const slice of params.slices) {
259
+ for (let i = 0; i < params.slices.length; i++) {
260
+ const slice = params.slices[i]!;
260
261
  // Preserve completed/done status on re-plan (#2558).
261
262
  // Without this, a re-plan after milestone transition would reset
262
263
  // already-completed slices back to "pending".
@@ -272,6 +273,7 @@ export async function handlePlanMilestone(
272
273
  risk: slice.risk,
273
274
  depends: slice.depends,
274
275
  demo: slice.demo,
276
+ sequence: i + 1, // Preserve agent-ordered sequence (#3356)
275
277
  });
276
278
  upsertSlicePlanning(params.milestoneId, slice.sliceId, {
277
279
  goal: slice.goal,