gsd-pi 2.64.0-dev.f8aad9b → 2.65.0-dev.16e10d7

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 (473) hide show
  1. package/dist/headless.js +3 -1
  2. package/dist/mcp-server.js +6 -2
  3. package/dist/resources/extensions/bg-shell/bg-shell-lifecycle.js +22 -7
  4. package/dist/resources/extensions/bg-shell/process-manager.js +6 -1
  5. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  6. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  7. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +94 -8
  9. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +115 -7
  11. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  12. package/dist/resources/extensions/gsd/auto-recovery.js +12 -8
  13. package/dist/resources/extensions/gsd/auto-start.js +35 -1
  14. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  15. package/dist/resources/extensions/gsd/auto-verification.js +138 -1
  16. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  17. package/dist/resources/extensions/gsd/auto.js +2 -2
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +34 -13
  20. package/dist/resources/extensions/gsd/bootstrap/notify-interceptor.js +28 -0
  21. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  22. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  23. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +12 -1
  24. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +16 -0
  25. package/dist/resources/extensions/gsd/bootstrap/system-context.js +20 -0
  26. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  27. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +21 -0
  29. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +104 -0
  30. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  31. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  32. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  33. package/dist/resources/extensions/gsd/detection.js +1 -1
  34. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  35. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  36. package/dist/resources/extensions/gsd/doctor.js +2 -1
  37. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  38. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  39. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  40. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  41. package/dist/resources/extensions/gsd/notification-overlay.js +256 -0
  42. package/dist/resources/extensions/gsd/notification-store.js +273 -0
  43. package/dist/resources/extensions/gsd/notification-widget.js +56 -0
  44. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  45. package/dist/resources/extensions/gsd/post-execution-checks.js +407 -0
  46. package/dist/resources/extensions/gsd/pre-execution-checks.js +471 -0
  47. package/dist/resources/extensions/gsd/preferences-types.js +7 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +78 -1
  49. package/dist/resources/extensions/gsd/preferences.js +13 -2
  50. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  51. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  52. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  53. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  55. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +4 -1
  56. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  57. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  58. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  59. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  60. package/dist/resources/extensions/gsd/quick.js +19 -15
  61. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  62. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  63. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  64. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  65. package/dist/resources/extensions/gsd/state.js +100 -12
  66. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  67. package/dist/resources/extensions/gsd/tools/complete-slice.js +12 -3
  68. package/dist/resources/extensions/gsd/tools/complete-task.js +16 -4
  69. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  70. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  71. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  72. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  73. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  74. package/dist/resources/extensions/gsd/triage-resolution.js +25 -9
  75. package/dist/resources/extensions/gsd/verification-evidence.js +18 -0
  76. package/dist/resources/extensions/gsd/workflow-logger.js +8 -0
  77. package/dist/resources/extensions/gsd/workflow-projections.js +4 -7
  78. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -4
  79. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  80. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  81. package/dist/resources/extensions/gsd/worktree.js +9 -0
  82. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  83. package/dist/web/standalone/.next/BUILD_ID +1 -1
  84. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -17
  85. package/dist/web/standalone/.next/build-manifest.json +3 -3
  86. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  87. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  88. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  89. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  90. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/api/notifications/route.js +3 -0
  106. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -0
  107. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -0
  108. package/dist/web/standalone/.next/server/app/index.html +1 -1
  109. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -17
  116. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  117. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  120. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  121. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  122. package/dist/web/standalone/.next/static/Z3TgDP0c7kG9j8CVQVGcl/_buildManifest.js +1 -0
  123. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  124. package/dist/web/standalone/.next/static/chunks/app/_global-error/page-8805a20e15762c3c.js +1 -0
  125. package/dist/web/standalone/.next/static/chunks/app/api/boot/route-8805a20e15762c3c.js +1 -0
  126. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-8805a20e15762c3c.js +1 -0
  127. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-8805a20e15762c3c.js +1 -0
  128. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-8805a20e15762c3c.js +1 -0
  129. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-8805a20e15762c3c.js +1 -0
  130. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-8805a20e15762c3c.js +1 -0
  131. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-8805a20e15762c3c.js +1 -0
  132. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-8805a20e15762c3c.js +1 -0
  133. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-8805a20e15762c3c.js +1 -0
  134. package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-8805a20e15762c3c.js +1 -0
  135. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-8805a20e15762c3c.js +1 -0
  136. package/dist/web/standalone/.next/static/chunks/app/api/files/route-8805a20e15762c3c.js +1 -0
  137. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-8805a20e15762c3c.js +1 -0
  138. package/dist/web/standalone/.next/static/chunks/app/api/git/route-8805a20e15762c3c.js +1 -0
  139. package/dist/web/standalone/.next/static/chunks/app/api/history/route-8805a20e15762c3c.js +1 -0
  140. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-8805a20e15762c3c.js +1 -0
  141. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-8805a20e15762c3c.js +1 -0
  142. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-8805a20e15762c3c.js +1 -0
  143. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-8805a20e15762c3c.js +1 -0
  144. package/dist/web/standalone/.next/static/chunks/app/api/notifications/route-8805a20e15762c3c.js +1 -0
  145. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-8805a20e15762c3c.js +1 -0
  146. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-8805a20e15762c3c.js +1 -0
  147. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-8805a20e15762c3c.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-8805a20e15762c3c.js +1 -0
  149. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-8805a20e15762c3c.js +1 -0
  150. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-8805a20e15762c3c.js +1 -0
  151. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-8805a20e15762c3c.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-8805a20e15762c3c.js +1 -0
  153. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-8805a20e15762c3c.js +1 -0
  154. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-8805a20e15762c3c.js +1 -0
  155. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-8805a20e15762c3c.js +1 -0
  156. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-8805a20e15762c3c.js +1 -0
  157. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-8805a20e15762c3c.js +1 -0
  158. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-8805a20e15762c3c.js +1 -0
  159. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-8805a20e15762c3c.js +1 -0
  160. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-8805a20e15762c3c.js +1 -0
  161. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-8805a20e15762c3c.js +1 -0
  162. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-8805a20e15762c3c.js +1 -0
  163. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-8805a20e15762c3c.js +1 -0
  164. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-8805a20e15762c3c.js +1 -0
  165. package/dist/web/standalone/.next/static/chunks/app/api/update/route-8805a20e15762c3c.js +1 -0
  166. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-8805a20e15762c3c.js +1 -0
  167. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-8805a20e15762c3c.js +1 -0
  168. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-8805a20e15762c3c.js +1 -0
  169. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-8805a20e15762c3c.js +1 -0
  170. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-8805a20e15762c3c.js +1 -0
  171. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  172. package/package.json +1 -1
  173. package/packages/pi-agent-core/dist/agent-loop.js +26 -9
  174. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  175. package/packages/pi-agent-core/src/agent-loop.test.ts +100 -4
  176. package/packages/pi-agent-core/src/agent-loop.ts +43 -12
  177. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts +2 -0
  178. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.d.ts.map +1 -0
  179. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +38 -0
  180. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -0
  181. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  182. package/packages/pi-coding-agent/dist/core/agent-session.js +11 -0
  183. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts +2 -0
  185. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.d.ts.map +1 -0
  186. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +24 -0
  187. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -0
  188. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/resource-loader.js +4 -1
  190. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  193. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  195. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  197. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  199. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  201. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +28 -5
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  206. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  209. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +6 -0
  211. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +40 -0
  213. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  214. package/packages/pi-coding-agent/package.json +1 -1
  215. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +64 -0
  216. package/packages/pi-coding-agent/src/core/agent-session.ts +10 -0
  217. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +42 -0
  218. package/packages/pi-coding-agent/src/core/resource-loader.ts +5 -1
  219. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  220. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  221. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  222. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  223. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +29 -4
  224. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  225. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  226. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +38 -0
  227. package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts +2 -0
  228. package/packages/pi-tui/dist/__tests__/overlay-layout.test.d.ts.map +1 -0
  229. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +66 -0
  230. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -0
  231. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  232. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  233. package/packages/pi-tui/dist/components/image.js +4 -0
  234. package/packages/pi-tui/dist/components/image.js.map +1 -1
  235. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  236. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  237. package/packages/pi-tui/dist/components/image.test.js +32 -0
  238. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  239. package/packages/pi-tui/dist/components/loader.d.ts +4 -2
  240. package/packages/pi-tui/dist/components/loader.d.ts.map +1 -1
  241. package/packages/pi-tui/dist/components/loader.js +27 -9
  242. package/packages/pi-tui/dist/components/loader.js.map +1 -1
  243. package/packages/pi-tui/dist/components/text.d.ts.map +1 -1
  244. package/packages/pi-tui/dist/components/text.js +2 -0
  245. package/packages/pi-tui/dist/components/text.js.map +1 -1
  246. package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
  247. package/packages/pi-tui/dist/overlay-layout.js +12 -1
  248. package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
  249. package/packages/pi-tui/dist/tui.d.ts +4 -0
  250. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  251. package/packages/pi-tui/dist/tui.js +35 -0
  252. package/packages/pi-tui/dist/tui.js.map +1 -1
  253. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +82 -0
  254. package/packages/pi-tui/src/components/image.test.ts +36 -0
  255. package/packages/pi-tui/src/components/image.ts +5 -0
  256. package/packages/pi-tui/src/components/loader.ts +27 -10
  257. package/packages/pi-tui/src/components/text.ts +1 -0
  258. package/packages/pi-tui/src/overlay-layout.ts +13 -1
  259. package/packages/pi-tui/src/tui.ts +34 -0
  260. package/pkg/package.json +1 -1
  261. package/src/resources/extensions/bg-shell/bg-shell-lifecycle.ts +19 -7
  262. package/src/resources/extensions/bg-shell/process-manager.ts +8 -2
  263. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  264. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  265. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  266. package/src/resources/extensions/gsd/auto-dispatch.ts +105 -8
  267. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  268. package/src/resources/extensions/gsd/auto-post-unit.ts +138 -6
  269. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  270. package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
  271. package/src/resources/extensions/gsd/auto-start.ts +38 -0
  272. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  273. package/src/resources/extensions/gsd/auto-verification.ts +190 -2
  274. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  275. package/src/resources/extensions/gsd/auto.ts +2 -1
  276. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  277. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +35 -13
  278. package/src/resources/extensions/gsd/bootstrap/notify-interceptor.ts +34 -0
  279. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  280. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  281. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +12 -1
  282. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +20 -0
  283. package/src/resources/extensions/gsd/bootstrap/system-context.ts +28 -0
  284. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  285. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  286. package/src/resources/extensions/gsd/commands/handlers/core.ts +24 -0
  287. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +140 -0
  288. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  289. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  290. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  291. package/src/resources/extensions/gsd/detection.ts +1 -1
  292. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  293. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  294. package/src/resources/extensions/gsd/doctor.ts +2 -1
  295. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  296. package/src/resources/extensions/gsd/gsd-db.ts +13 -2
  297. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  298. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  299. package/src/resources/extensions/gsd/notification-overlay.ts +295 -0
  300. package/src/resources/extensions/gsd/notification-store.ts +293 -0
  301. package/src/resources/extensions/gsd/notification-widget.ts +68 -0
  302. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  303. package/src/resources/extensions/gsd/post-execution-checks.ts +539 -0
  304. package/src/resources/extensions/gsd/pre-execution-checks.ts +581 -0
  305. package/src/resources/extensions/gsd/preferences-types.ts +53 -0
  306. package/src/resources/extensions/gsd/preferences-validation.ts +78 -1
  307. package/src/resources/extensions/gsd/preferences.ts +13 -2
  308. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  309. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  310. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  311. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  312. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  313. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +4 -1
  314. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  315. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  316. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  317. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  318. package/src/resources/extensions/gsd/quick.ts +20 -15
  319. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  320. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  321. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  322. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  323. package/src/resources/extensions/gsd/state.ts +101 -11
  324. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  325. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  326. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  327. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  328. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  329. package/src/resources/extensions/gsd/tests/complete-slice-string-coercion.test.ts +36 -0
  330. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  331. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  332. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  333. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  334. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  335. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  336. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +76 -0
  337. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  338. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  339. package/src/resources/extensions/gsd/tests/enhanced-verification-integration.test.ts +526 -0
  340. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  341. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  342. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  343. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  344. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  345. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  346. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +5 -3
  347. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  348. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  349. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  350. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  351. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  352. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  353. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  354. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +73 -0
  355. package/src/resources/extensions/gsd/tests/notification-store.test.ts +282 -0
  356. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  357. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  358. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  359. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +312 -0
  360. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +813 -0
  361. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  362. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +1197 -0
  363. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +266 -0
  364. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +457 -0
  365. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  366. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  367. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  368. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  369. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  370. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  371. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  372. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  373. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  374. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  375. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  376. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  377. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  378. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  379. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  380. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  381. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  382. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  383. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  384. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  385. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  386. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  387. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  388. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  389. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  390. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  391. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  392. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  393. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  394. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  395. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +163 -0
  396. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  397. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  398. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  399. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  400. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  401. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  402. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  403. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  404. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  405. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  406. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  407. package/src/resources/extensions/gsd/tools/complete-slice.ts +13 -3
  408. package/src/resources/extensions/gsd/tools/complete-task.ts +16 -4
  409. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  410. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  411. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  412. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  413. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  414. package/src/resources/extensions/gsd/triage-resolution.ts +29 -10
  415. package/src/resources/extensions/gsd/types.ts +4 -0
  416. package/src/resources/extensions/gsd/verification-evidence.ts +68 -0
  417. package/src/resources/extensions/gsd/workflow-logger.ts +13 -0
  418. package/src/resources/extensions/gsd/workflow-projections.ts +4 -6
  419. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -3
  420. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  421. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  422. package/src/resources/extensions/gsd/worktree.ts +10 -0
  423. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  424. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  425. package/dist/web/standalone/.next/static/F1mOwzgCW9R8N3Pt1Et87/_buildManifest.js +0 -1
  426. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  427. package/dist/web/standalone/.next/static/chunks/app/_global-error/page-c4cc189e7b117ea2.js +0 -1
  428. package/dist/web/standalone/.next/static/chunks/app/api/boot/route-c4cc189e7b117ea2.js +0 -1
  429. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/route-c4cc189e7b117ea2.js +0 -1
  430. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/route-c4cc189e7b117ea2.js +0 -1
  431. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-c4cc189e7b117ea2.js +0 -1
  432. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-c4cc189e7b117ea2.js +0 -1
  433. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-c4cc189e7b117ea2.js +0 -1
  434. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-c4cc189e7b117ea2.js +0 -1
  435. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-c4cc189e7b117ea2.js +0 -1
  436. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-c4cc189e7b117ea2.js +0 -1
  437. package/dist/web/standalone/.next/static/chunks/app/api/experimental/route-c4cc189e7b117ea2.js +0 -1
  438. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-c4cc189e7b117ea2.js +0 -1
  439. package/dist/web/standalone/.next/static/chunks/app/api/files/route-c4cc189e7b117ea2.js +0 -1
  440. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-c4cc189e7b117ea2.js +0 -1
  441. package/dist/web/standalone/.next/static/chunks/app/api/git/route-c4cc189e7b117ea2.js +0 -1
  442. package/dist/web/standalone/.next/static/chunks/app/api/history/route-c4cc189e7b117ea2.js +0 -1
  443. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-c4cc189e7b117ea2.js +0 -1
  444. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-c4cc189e7b117ea2.js +0 -1
  445. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-c4cc189e7b117ea2.js +0 -1
  446. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-c4cc189e7b117ea2.js +0 -1
  447. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-c4cc189e7b117ea2.js +0 -1
  448. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-c4cc189e7b117ea2.js +0 -1
  449. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-c4cc189e7b117ea2.js +0 -1
  450. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-c4cc189e7b117ea2.js +0 -1
  451. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-c4cc189e7b117ea2.js +0 -1
  452. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-c4cc189e7b117ea2.js +0 -1
  453. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-c4cc189e7b117ea2.js +0 -1
  454. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-c4cc189e7b117ea2.js +0 -1
  455. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-c4cc189e7b117ea2.js +0 -1
  456. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-c4cc189e7b117ea2.js +0 -1
  457. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-c4cc189e7b117ea2.js +0 -1
  458. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-c4cc189e7b117ea2.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-c4cc189e7b117ea2.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-c4cc189e7b117ea2.js +0 -1
  461. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-c4cc189e7b117ea2.js +0 -1
  462. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-c4cc189e7b117ea2.js +0 -1
  463. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-c4cc189e7b117ea2.js +0 -1
  464. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-c4cc189e7b117ea2.js +0 -1
  465. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-c4cc189e7b117ea2.js +0 -1
  466. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-c4cc189e7b117ea2.js +0 -1
  467. package/dist/web/standalone/.next/static/chunks/app/api/update/route-c4cc189e7b117ea2.js +0 -1
  468. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-c4cc189e7b117ea2.js +0 -1
  469. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-c4cc189e7b117ea2.js +0 -1
  470. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-c4cc189e7b117ea2.js +0 -1
  471. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-c4cc189e7b117ea2.js +0 -1
  472. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-c4cc189e7b117ea2.js +0 -1
  473. /package/dist/web/standalone/.next/static/{F1mOwzgCW9R8N3Pt1Et87 → Z3TgDP0c7kG9j8CVQVGcl}/_ssgManifest.js +0 -0
@@ -0,0 +1,163 @@
1
+ // GSD-2 — Regression test for #3615: unstructured "continue" must inject task context
2
+ // Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
3
+
4
+ /**
5
+ * Bug #3615: When a user types "continue" (or any bare text) to resume
6
+ * an in-progress session, buildGuidedExecuteContextInjection() only
7
+ * matched two hardcoded regex patterns (auto-dispatch and guided-resume).
8
+ * The function returned null for any other input, so no task context was
9
+ * injected — causing the agent to rebuild everything from scratch and
10
+ * burn ~86k tokens.
11
+ *
12
+ * This test verifies:
13
+ * 1. Structural: the fallback exists with phase + intent guards
14
+ * 2. Behavioral: RESUME_INTENT_PATTERNS matches expected prompts and
15
+ * rejects non-resume prompts (control, help, diagnostic, etc.)
16
+ */
17
+
18
+ import { describe, test } from "node:test";
19
+ import assert from "node:assert/strict";
20
+ import { readFileSync } from "node:fs";
21
+ import { join, dirname } from "node:path";
22
+ import { fileURLToPath } from "node:url";
23
+
24
+ const __dirname = dirname(fileURLToPath(import.meta.url));
25
+ const systemContextSource = readFileSync(
26
+ join(__dirname, "..", "bootstrap", "system-context.ts"),
27
+ "utf-8",
28
+ );
29
+
30
+ // ── Structural tests ────────────────────────────────────────────────
31
+
32
+ describe("#3615 — structural: fallback exists with correct guards", () => {
33
+ const fnStart = systemContextSource.indexOf("async function buildGuidedExecuteContextInjection(");
34
+ assert.ok(fnStart >= 0, "should find buildGuidedExecuteContextInjection");
35
+ const fnEnd = systemContextSource.indexOf("\nasync function ", fnStart + 1);
36
+ const fnBody = fnEnd >= 0
37
+ ? systemContextSource.slice(fnStart, fnEnd)
38
+ : systemContextSource.slice(fnStart);
39
+
40
+ test("has a deriveState fallback after the two regex branches", () => {
41
+ const deriveStateCalls = fnBody.match(/deriveState\(basePath\)/g);
42
+ assert.ok(
43
+ deriveStateCalls && deriveStateCalls.length >= 2,
44
+ `expected >=2 deriveState(basePath) calls, got ${deriveStateCalls?.length ?? 0}`,
45
+ );
46
+ });
47
+
48
+ test("fallback is phase-gated to executing only", () => {
49
+ const afterFallback = fnBody.indexOf("// Fallback:");
50
+ assert.ok(afterFallback >= 0, "should have a fallback comment");
51
+ const fallbackSection = fnBody.slice(afterFallback);
52
+ assert.ok(
53
+ fallbackSection.includes('state.phase === "executing"'),
54
+ 'fallback must be gated on state.phase === "executing"',
55
+ );
56
+ });
57
+
58
+ test("fallback is intent-gated via RESUME_INTENT_PATTERNS", () => {
59
+ const afterFallback = fnBody.indexOf("// Fallback:");
60
+ const fallbackSection = fnBody.slice(afterFallback);
61
+ assert.ok(
62
+ fallbackSection.includes("RESUME_INTENT_PATTERNS"),
63
+ "fallback must check RESUME_INTENT_PATTERNS before deriveState",
64
+ );
65
+ });
66
+
67
+ test("fallback calls buildTaskExecutionContextInjection with derived state", () => {
68
+ const afterFallback = fnBody.indexOf("// Fallback:");
69
+ const fallbackSection = fnBody.slice(afterFallback);
70
+ assert.ok(
71
+ fallbackSection.includes("buildTaskExecutionContextInjection") &&
72
+ fallbackSection.includes("state.activeMilestone.id") &&
73
+ fallbackSection.includes("state.activeSlice.id") &&
74
+ fallbackSection.includes("state.activeTask.id"),
75
+ "fallback must call buildTaskExecutionContextInjection with state-derived IDs",
76
+ );
77
+ });
78
+
79
+ test("only one return null at the end", () => {
80
+ const returnNulls = fnBody.match(/return null;/g);
81
+ assert.ok(
82
+ returnNulls && returnNulls.length === 1,
83
+ `expected exactly 1 'return null' (at end after fallback), got ${returnNulls?.length ?? 0}`,
84
+ );
85
+ });
86
+ });
87
+
88
+ // ── Behavioral tests: RESUME_INTENT_PATTERNS ────────────────────────
89
+
90
+ describe("#3615 — behavioral: RESUME_INTENT_PATTERNS matches resume prompts", () => {
91
+ // Extract the regex from source so the test stays in sync
92
+ const patternMatch = systemContextSource.match(/const RESUME_INTENT_PATTERNS\s*=\s*\/(.+)\/;/);
93
+ assert.ok(patternMatch, "should find RESUME_INTENT_PATTERNS definition");
94
+ const pattern = new RegExp(patternMatch[1]);
95
+
96
+ // Helper: normalize prompt the same way the production code does
97
+ const normalize = (s: string) => s.trim().toLowerCase().replace(/[.!?,]+$/g, "");
98
+
99
+ const shouldMatch = [
100
+ "continue",
101
+ "Continue",
102
+ "CONTINUE",
103
+ "continue.",
104
+ "continue!",
105
+ "resume",
106
+ "ok",
107
+ "OK",
108
+ "Ok!",
109
+ "go",
110
+ "go ahead",
111
+ "Go ahead.",
112
+ "proceed",
113
+ "keep going",
114
+ "carry on",
115
+ "next",
116
+ "yes",
117
+ "yeah",
118
+ "yep",
119
+ "sure",
120
+ "do it",
121
+ "let's go",
122
+ "pick up where you left off",
123
+ " continue ", // whitespace padded
124
+ ];
125
+
126
+ const shouldNotMatch = [
127
+ "help",
128
+ "status",
129
+ "/gsd auto",
130
+ "/gsd stats",
131
+ "what's the plan?",
132
+ "show me the logs",
133
+ "abort",
134
+ "stop",
135
+ "cancel",
136
+ "replan this slice",
137
+ "I think we should change the approach",
138
+ "can you explain what you just did?",
139
+ "run the tests",
140
+ "check the build",
141
+ "Execute the next task: T01",
142
+ "what files were changed",
143
+ "",
144
+ ];
145
+
146
+ for (const prompt of shouldMatch) {
147
+ test(`matches resume prompt: "${prompt}"`, () => {
148
+ assert.ok(
149
+ pattern.test(normalize(prompt)),
150
+ `expected RESUME_INTENT_PATTERNS to match "${prompt}" (normalized: "${normalize(prompt)}")`,
151
+ );
152
+ });
153
+ }
154
+
155
+ for (const prompt of shouldNotMatch) {
156
+ test(`rejects non-resume prompt: "${prompt}"`, () => {
157
+ assert.ok(
158
+ !pattern.test(normalize(prompt)),
159
+ `expected RESUME_INTENT_PATTERNS to NOT match "${prompt}" (normalized: "${normalize(prompt)}")`,
160
+ );
161
+ });
162
+ }
163
+ });
@@ -163,16 +163,15 @@ test("deriveState returns completing-milestone when VALIDATION exists with termi
163
163
  }
164
164
  });
165
165
 
166
- test("deriveState treats needs-remediation as terminal — does not re-enter validating-milestone (#832)", async () => {
166
+ test("deriveState treats needs-remediation as non-terminal — re-enters validating-milestone (#832)", async () => {
167
167
  const base = makeTmpBase();
168
168
  try {
169
169
  writeRoadmap(base, "M001", ALL_DONE_ROADMAP);
170
170
  writeValidation(base, "M001", "---\nverdict: needs-remediation\nremediation_round: 0\n---\n\n# Validation\nNeeds fixes.");
171
171
 
172
172
  const state = await deriveState(base);
173
- // needs-remediation is now terminal milestone needs a SUMMARY to be fully complete
174
- // Without SUMMARY, it enters completing-milestone (not validating-milestone)
175
- assert.notEqual(state.phase, "validating-milestone");
173
+ // needs-remediation routes back to validating-milestone for re-validation
174
+ assert.equal(state.phase, "validating-milestone");
176
175
  assert.equal(state.activeMilestone?.id, "M001");
177
176
  } finally {
178
177
  cleanup(base);
@@ -80,3 +80,18 @@ test("isVerificationNotApplicable: 'Verify API response times under load' requir
80
80
  test("isVerificationNotApplicable: 'Monitor error rates for 24h' requires verification", () => {
81
81
  assert.equal(isVerificationNotApplicable("Monitor error rates for 24h"), false);
82
82
  });
83
+
84
+ // Regression: #3634 — "Not provided." default from plan-milestone
85
+ test("isVerificationNotApplicable: 'Not provided.' is not applicable (#3634)", () => {
86
+ assert.equal(isVerificationNotApplicable("Not provided."), true);
87
+ });
88
+
89
+ test("isVerificationNotApplicable: 'Not provided' (no period) is not applicable (#3634)", () => {
90
+ assert.equal(isVerificationNotApplicable("Not provided"), true);
91
+ });
92
+
93
+ test("isVerificationNotApplicable: trailing period does not defeat match (#3634)", () => {
94
+ assert.equal(isVerificationNotApplicable("None required."), true);
95
+ assert.equal(isVerificationNotApplicable("N/A."), true);
96
+ assert.equal(isVerificationNotApplicable("Not applicable."), true);
97
+ });
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Regression test for #3607 — tighten verifyExpectedArtifact legacy branch
3
+ *
4
+ * The legacy (pre-migration) fallback in verifyExpectedArtifact previously
5
+ * accepted either a heading match (### T01 --) or a checked checkbox as proof
6
+ * that gsd_complete_task ran. A heading alone does not prove completion —
7
+ * it could result from a rogue write.
8
+ *
9
+ * The fix removes the hdRe heading regex and requires only a checked checkbox
10
+ * (cbRe) in the legacy branch, ensuring that only actual tool-completed tasks
11
+ * are treated as verified.
12
+ */
13
+
14
+ import { describe, it } from 'node:test'
15
+ import assert from 'node:assert/strict'
16
+ import { readFileSync } from 'node:fs'
17
+ import { resolve } from 'node:path'
18
+
19
+ const src = readFileSync(
20
+ resolve(process.cwd(), 'src', 'resources', 'extensions', 'gsd', 'auto-recovery.ts'),
21
+ 'utf-8',
22
+ )
23
+
24
+ describe('verifyExpectedArtifact legacy branch tightened (#3607)', () => {
25
+ it('legacy branch does NOT define hdRe heading regex', () => {
26
+ // Find the legacy fallback section
27
+ const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
28
+ assert.ok(legacyIdx !== -1, 'LEGACY comment must exist')
29
+
30
+ // Check the code within a reasonable window after the LEGACY comment
31
+ const legacyBlock = src.slice(legacyIdx, legacyIdx + 600)
32
+
33
+ assert.ok(
34
+ !legacyBlock.includes('hdRe'),
35
+ 'hdRe heading regex must NOT exist in legacy branch — heading alone is not proof of completion',
36
+ )
37
+ })
38
+
39
+ it('legacy branch requires checked checkbox via cbRe', () => {
40
+ const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
41
+ assert.ok(legacyIdx !== -1)
42
+
43
+ const legacyBlock = src.slice(legacyIdx, legacyIdx + 600)
44
+
45
+ assert.ok(
46
+ legacyBlock.includes('cbRe'),
47
+ 'cbRe checked-checkbox regex must exist in legacy branch',
48
+ )
49
+
50
+ // cbRe must match checked checkboxes [x] or [X]
51
+ assert.ok(
52
+ legacyBlock.includes('[xX]'),
53
+ 'cbRe must match both [x] and [X] checkbox variants',
54
+ )
55
+ })
56
+
57
+ it('legacy branch returns false when no plan file exists', () => {
58
+ const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
59
+ assert.ok(legacyIdx !== -1)
60
+
61
+ const legacyBlock = src.slice(legacyIdx, legacyIdx + 1000)
62
+
63
+ // The else branch: no plan file means cannot verify
64
+ assert.ok(
65
+ legacyBlock.includes('no plan file'),
66
+ 'missing plan file must be handled with return false',
67
+ )
68
+ })
69
+
70
+ it('DB available but task not found returns false', () => {
71
+ const legacyIdx = src.indexOf('LEGACY: Pre-migration fallback')
72
+ assert.ok(legacyIdx !== -1)
73
+
74
+ const legacyBlock = src.slice(legacyIdx, legacyIdx + 1000)
75
+
76
+ assert.ok(
77
+ legacyBlock.includes('DB available but task row not found'),
78
+ 'must handle case where DB is available but task row is missing',
79
+ )
80
+
81
+ // The comment should be followed by a return false
82
+ const commentIdx = legacyBlock.indexOf('DB available but task row not found')
83
+ const afterComment = legacyBlock.slice(commentIdx, commentIdx + 200)
84
+ assert.ok(
85
+ afterComment.includes('return false'),
86
+ 'missing task row when DB available must return false',
87
+ )
88
+ })
89
+ })
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Worker model override — tests for parallel.worker_model preference.
3
+ *
4
+ * Verifies validation, resolveParallelConfig pass-through, and type definitions.
5
+ */
6
+
7
+ import test from "node:test";
8
+ import assert from "node:assert/strict";
9
+ import { readFileSync } from "node:fs";
10
+ import { join, dirname } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+
13
+ const __dirname = dirname(fileURLToPath(import.meta.url));
14
+
15
+ const typesSrc = readFileSync(join(__dirname, "..", "types.ts"), "utf-8");
16
+ const validationSrc = readFileSync(join(__dirname, "..", "preferences-validation.ts"), "utf-8");
17
+ const preferencesSrc = readFileSync(join(__dirname, "..", "preferences.ts"), "utf-8");
18
+
19
+ // ─── Type definition ──────────────────────────────────────────────────────
20
+
21
+ test("ParallelConfig includes worker_model optional field", () => {
22
+ assert.ok(
23
+ typesSrc.includes("worker_model?: string"),
24
+ "ParallelConfig should have optional worker_model field",
25
+ );
26
+ });
27
+
28
+ // ─── Validation ───────────────────────────────────────────────────────────
29
+
30
+ test("validatePreferences accepts valid worker_model string", () => {
31
+ assert.ok(
32
+ validationSrc.includes("p.worker_model"),
33
+ "validation should check parallel.worker_model",
34
+ );
35
+ assert.ok(
36
+ validationSrc.includes('parallel.worker_model must be a non-empty string'),
37
+ "validation should reject invalid worker_model",
38
+ );
39
+ });
40
+
41
+ // ─── resolveParallelConfig ────────────────────────────────────────────────
42
+
43
+ test("resolveParallelConfig passes through worker_model", () => {
44
+ assert.ok(
45
+ preferencesSrc.includes("worker_model: prefs?.parallel?.worker_model"),
46
+ "resolveParallelConfig should pass through worker_model",
47
+ );
48
+ });
@@ -0,0 +1,38 @@
1
+ /**
2
+ * worktree-expected-warnings.test.ts — #3665
3
+ *
4
+ * Verify that auto-worktree.ts and worktree-manager.ts suppress expected
5
+ * ENOENT and EISDIR conditions instead of logging misleading warnings.
6
+ */
7
+
8
+ import { describe, test } from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { readFileSync } from "node:fs";
11
+ import { join, dirname } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const autoWorktreeFile = join(__dirname, "..", "auto-worktree.ts");
16
+ const worktreeManagerFile = join(__dirname, "..", "worktree-manager.ts");
17
+
18
+ describe("worktree expected-condition warning suppression (#3665)", () => {
19
+ const autoSource = readFileSync(autoWorktreeFile, "utf-8");
20
+
21
+ test("auto-worktree.ts checks for ENOENT before logging unlink warning", () => {
22
+ assert.match(autoSource, /code\s*!==\s*["']ENOENT["']/);
23
+ });
24
+
25
+ test("auto-worktree.ts checks for EISDIR before logging unlink warning", () => {
26
+ assert.match(autoSource, /code\s*!==\s*["']EISDIR["']/);
27
+ });
28
+
29
+ test("auto-worktree.ts references issue #3597", () => {
30
+ assert.match(autoSource, /#3597/);
31
+ });
32
+
33
+ const managerSource = readFileSync(worktreeManagerFile, "utf-8");
34
+
35
+ test("worktree-manager.ts checks isDirectory() before reading .git file", () => {
36
+ assert.match(managerSource, /lstatSync\(gitPath\)\.isDirectory\(\)/);
37
+ });
38
+ });
@@ -26,9 +26,11 @@ import {
26
26
  getSliceBranchName,
27
27
  autoCommitCurrentBranch,
28
28
  SLICE_BRANCH_RE,
29
+ _resetServiceCache,
29
30
  } from "../worktree.ts";
30
31
 
31
32
  import { deriveState } from "../state.ts";
33
+ import { _clearGsdRootCache } from "../paths.ts";
32
34
  import { describe, test } from 'node:test';
33
35
  import assert from 'node:assert/strict';
34
36
 
@@ -74,6 +76,14 @@ run("git add .", base);
74
76
  run('git commit -m "chore: init"', base);
75
77
 
76
78
  describe('worktree-integration', async () => {
79
+ // Isolate from user's global preferences (which may have git.main_branch set).
80
+ // Reset caches so getService() creates a fresh instance with empty preferences.
81
+ const originalHome = process.env.HOME;
82
+ const fakeHome = mkdtempSync(join(tmpdir(), "gsd-fake-home-"));
83
+ process.env.HOME = fakeHome;
84
+ _clearGsdRootCache();
85
+ _resetServiceCache();
86
+
77
87
  // ── Verify main tree baseline ──────────────────────────────────────────────
78
88
 
79
89
  console.log("\n=== Main tree baseline ===");
@@ -197,4 +207,10 @@ describe('worktree-integration', async () => {
197
207
  assert.deepStrictEqual(listWorktrees(base).length, 0, "all worktrees removed");
198
208
 
199
209
  rmSync(base, { recursive: true, force: true });
210
+
211
+ // Restore HOME and reset caches
212
+ process.env.HOME = originalHome;
213
+ _clearGsdRootCache();
214
+ _resetServiceCache();
215
+ rmSync(fakeHome, { recursive: true, force: true });
200
216
  });
@@ -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
  }
@@ -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
+ });