@opengsd/gsd-pi 1.2.0-dev.844675c9 → 1.2.0-dev.b1abb545

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 (375) hide show
  1. package/dist/cli-web-branch.d.ts +2 -0
  2. package/dist/cli-web-branch.js +9 -2
  3. package/dist/help-text.js +5 -0
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/ask-user-questions.js +78 -23
  6. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +84 -228
  7. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +224 -0
  8. package/dist/resources/extensions/github-sync/templates.js +3 -3
  9. package/dist/resources/extensions/gsd/artifact-projection.js +14 -0
  10. package/dist/resources/extensions/gsd/auto/loop.js +74 -56
  11. package/dist/resources/extensions/gsd/auto/orchestrator.js +109 -11
  12. package/dist/resources/extensions/gsd/auto/phases.js +28 -3
  13. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -1
  14. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  15. package/dist/resources/extensions/gsd/auto-dashboard.js +16 -4
  16. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -5
  17. package/dist/resources/extensions/gsd/auto-model-selection.js +8 -0
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  19. package/dist/resources/extensions/gsd/auto-prompts.js +81 -8
  20. package/dist/resources/extensions/gsd/auto-recovery.js +48 -49
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +14 -0
  22. package/dist/resources/extensions/gsd/auto-start.js +12 -23
  23. package/dist/resources/extensions/gsd/auto-timers.js +16 -2
  24. package/dist/resources/extensions/gsd/auto-tool-tracking.js +32 -0
  25. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +4 -29
  26. package/dist/resources/extensions/gsd/auto-verification.js +7 -7
  27. package/dist/resources/extensions/gsd/auto-worktree.js +21 -19
  28. package/dist/resources/extensions/gsd/auto.js +11 -7
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +28 -37
  30. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +11 -37
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +100 -138
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +63 -4
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +21 -4
  35. package/dist/resources/extensions/gsd/codebase-generator.js +8 -4
  36. package/dist/resources/extensions/gsd/commands/handlers/auto.js +3 -0
  37. package/dist/resources/extensions/gsd/commands-handlers.js +20 -0
  38. package/dist/resources/extensions/gsd/commands-inspect.js +4 -8
  39. package/dist/resources/extensions/gsd/commands-maintenance.js +61 -41
  40. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  41. package/dist/resources/extensions/gsd/commands-verdict.js +12 -2
  42. package/dist/resources/extensions/gsd/db-workspace.js +103 -0
  43. package/dist/resources/extensions/gsd/delegation-policy.js +2 -10
  44. package/dist/resources/extensions/gsd/discussion-handoff.js +218 -0
  45. package/dist/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  46. package/dist/resources/extensions/gsd/doctor.js +16 -9
  47. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  48. package/dist/resources/extensions/gsd/git-conflict-state.js +16 -1
  49. package/dist/resources/extensions/gsd/gsd-db.js +12 -0
  50. package/dist/resources/extensions/gsd/guided-flow.js +34 -468
  51. package/dist/resources/extensions/gsd/guided-unit-completion.js +225 -0
  52. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -1
  53. package/dist/resources/extensions/gsd/mcp-filter.js +2 -1
  54. package/dist/resources/extensions/gsd/mcp-tool-name.js +26 -0
  55. package/dist/resources/extensions/gsd/md-importer.js +4 -3
  56. package/dist/resources/extensions/gsd/migrate/safety.js +2 -2
  57. package/dist/resources/extensions/gsd/migration-auto-check.js +3 -2
  58. package/dist/resources/extensions/gsd/milestone-closeout-proof.js +72 -0
  59. package/dist/resources/extensions/gsd/milestone-closeout.js +12 -4
  60. package/dist/resources/extensions/gsd/milestone-merge-transaction.js +10 -0
  61. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +156 -0
  62. package/dist/resources/extensions/gsd/milestone-readiness.js +77 -0
  63. package/dist/resources/extensions/gsd/milestone-settlement.js +50 -0
  64. package/dist/resources/extensions/gsd/milestone-validation-evidence.js +73 -0
  65. package/dist/resources/extensions/gsd/milestone-validation-verdict.js +57 -0
  66. package/dist/resources/extensions/gsd/parallel-eligibility.js +3 -6
  67. package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -2
  68. package/dist/resources/extensions/gsd/preferences-diagnostics.js +67 -0
  69. package/dist/resources/extensions/gsd/preferences.js +147 -29
  70. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  71. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  72. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  73. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  74. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  75. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  76. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  77. package/dist/resources/extensions/gsd/provider-payload-policy.js +83 -0
  78. package/dist/resources/extensions/gsd/pull-request-process.js +13 -0
  79. package/dist/resources/extensions/gsd/quality-gate-closure.js +109 -0
  80. package/dist/resources/extensions/gsd/question-transport.js +86 -0
  81. package/dist/resources/extensions/gsd/roadmap-slices.js +8 -2
  82. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +3 -2
  83. package/dist/resources/extensions/gsd/state.js +13 -5
  84. package/dist/resources/extensions/gsd/templates/plan.md +7 -0
  85. package/dist/resources/extensions/gsd/templates/project.md +1 -0
  86. package/dist/resources/extensions/gsd/templates/roadmap.md +1 -1
  87. package/dist/resources/extensions/gsd/templates/uat.md +5 -1
  88. package/dist/resources/extensions/gsd/tool-contract.js +52 -8
  89. package/dist/resources/extensions/gsd/tool-presentation-plan.js +15 -34
  90. package/dist/resources/extensions/gsd/tool-surface-snapshot.js +17 -0
  91. package/dist/resources/extensions/gsd/tools/plan-milestone.js +15 -143
  92. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +39 -0
  93. package/dist/resources/extensions/gsd/tools/validate-milestone.js +15 -78
  94. package/dist/resources/extensions/gsd/uat-policy.js +16 -10
  95. package/dist/resources/extensions/gsd/uat-run.js +9 -14
  96. package/dist/resources/extensions/gsd/unit-context-composer.js +40 -20
  97. package/dist/resources/extensions/gsd/unit-runtime.js +3 -2
  98. package/dist/resources/extensions/gsd/unit-tool-contracts.js +2 -1
  99. package/dist/resources/extensions/gsd/user-input-boundary.js +23 -0
  100. package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
  101. package/dist/resources/extensions/gsd/web-app-uat.js +80 -0
  102. package/dist/resources/extensions/gsd/workflow-mcp.js +15 -102
  103. package/dist/resources/extensions/gsd/workflow-reconcile.js +4 -3
  104. package/dist/resources/extensions/gsd/workflow-tool-surface.js +46 -0
  105. package/dist/resources/extensions/gsd/workspace-git-guard.js +2 -0
  106. package/dist/resources/extensions/gsd/worktree-state-projection.js +33 -4
  107. package/dist/resources/extensions/gsd/worktree-telemetry.js +12 -0
  108. package/dist/resources/extensions/shared/interview-ui.js +2 -2
  109. package/dist/resources/shared/claude-runtime-floor.js +182 -0
  110. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  111. package/dist/update-cmd.js +20 -0
  112. package/dist/web/standalone/.next/BUILD_ID +1 -1
  113. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  114. package/dist/web/standalone/.next/build-manifest.json +3 -3
  115. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  116. package/dist/web/standalone/.next/react-loadable-manifest.json +8 -8
  117. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  129. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  130. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  131. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  132. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  133. package/dist/web/standalone/.next/server/app/index.html +1 -1
  134. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  137. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  138. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  141. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  142. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  145. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  146. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  147. package/dist/web/standalone/.next/static/chunks/2659.b7b129ee6a769448.js +1 -0
  148. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +1 -0
  149. package/dist/web/standalone/.next/static/chunks/{3616.4113d484a994e411.js → 3616.3c60753b8ffcbd2e.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/4283.e4873b058df143a1.js +2 -0
  151. package/dist/web/standalone/.next/static/chunks/5826.a46ecdd1cfe8dabc.js +1 -0
  152. package/dist/web/standalone/.next/static/chunks/796.cf859a427a2cb2ac.js +10 -0
  153. package/dist/web/standalone/.next/static/chunks/8785.2e5a118797fb2dd2.js +1 -0
  154. package/dist/web/standalone/.next/static/chunks/{webpack-dda80a1ef5587410.js → webpack-fbea77b5f9953368.js} +1 -1
  155. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  156. package/dist/web-mode.d.ts +2 -0
  157. package/dist/web-mode.js +20 -8
  158. package/package.json +2 -1
  159. package/packages/cloud-mcp-gateway/package.json +2 -2
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts +2 -0
  163. package/packages/gsd-agent-core/dist/session/agent-session-extensions.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js +14 -0
  165. package/packages/gsd-agent-core/dist/session/agent-session-extensions.js.map +1 -1
  166. package/packages/gsd-agent-core/package.json +5 -5
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +106 -40
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  170. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.d.ts.map +1 -1
  171. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js +6 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-widgets.js.map +1 -1
  173. package/packages/gsd-agent-modes/package.json +7 -7
  174. package/packages/mcp-server/dist/server.d.ts +10 -0
  175. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  176. package/packages/mcp-server/dist/server.js +8 -0
  177. package/packages/mcp-server/dist/server.js.map +1 -1
  178. package/packages/mcp-server/dist/workflow-tools.d.ts +41 -0
  179. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  180. package/packages/mcp-server/dist/workflow-tools.js +2 -1
  181. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  182. package/packages/mcp-server/package.json +3 -3
  183. package/packages/native/package.json +1 -1
  184. package/packages/pi-agent-core/package.json +1 -1
  185. package/packages/pi-ai/dist/models.generated.d.ts +8 -93
  186. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  187. package/packages/pi-ai/dist/models.generated.js +35 -120
  188. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  189. package/packages/pi-ai/package.json +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/dist/components/input.js +1 -1
  192. package/packages/pi-tui/dist/components/input.js.map +1 -1
  193. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  194. package/packages/pi-tui/dist/keys.js +39 -30
  195. package/packages/pi-tui/dist/keys.js.map +1 -1
  196. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  197. package/packages/pi-tui/dist/stdin-buffer.js +22 -0
  198. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  199. package/packages/pi-tui/package.json +2 -2
  200. package/packages/rpc-client/package.json +2 -2
  201. package/pkg/package.json +1 -1
  202. package/src/resources/extensions/ask-user-questions.ts +87 -24
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +108 -281
  204. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +240 -0
  205. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +287 -0
  206. package/src/resources/extensions/github-sync/templates.ts +3 -3
  207. package/src/resources/extensions/github-sync/tests/templates.test.ts +2 -2
  208. package/src/resources/extensions/gsd/artifact-projection.ts +31 -0
  209. package/src/resources/extensions/gsd/auto/contracts.ts +32 -2
  210. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  211. package/src/resources/extensions/gsd/auto/loop.ts +83 -61
  212. package/src/resources/extensions/gsd/auto/orchestrator.ts +125 -12
  213. package/src/resources/extensions/gsd/auto/phases.ts +35 -3
  214. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -1
  215. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  216. package/src/resources/extensions/gsd/auto-dashboard.ts +18 -4
  217. package/src/resources/extensions/gsd/auto-dispatch.ts +20 -7
  218. package/src/resources/extensions/gsd/auto-model-selection.ts +8 -0
  219. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -3
  220. package/src/resources/extensions/gsd/auto-prompts.ts +107 -9
  221. package/src/resources/extensions/gsd/auto-recovery.ts +50 -50
  222. package/src/resources/extensions/gsd/auto-runtime-state.ts +26 -0
  223. package/src/resources/extensions/gsd/auto-start.ts +17 -20
  224. package/src/resources/extensions/gsd/auto-timers.ts +16 -2
  225. package/src/resources/extensions/gsd/auto-tool-tracking.ts +35 -0
  226. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +9 -30
  227. package/src/resources/extensions/gsd/auto-verification.ts +7 -8
  228. package/src/resources/extensions/gsd/auto-worktree.ts +33 -26
  229. package/src/resources/extensions/gsd/auto.ts +15 -8
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +29 -37
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +10 -37
  232. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  233. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +116 -151
  234. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +107 -3
  235. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +27 -5
  236. package/src/resources/extensions/gsd/codebase-generator.ts +9 -5
  237. package/src/resources/extensions/gsd/commands/handlers/auto.ts +3 -0
  238. package/src/resources/extensions/gsd/commands-handlers.ts +18 -0
  239. package/src/resources/extensions/gsd/commands-inspect.ts +7 -8
  240. package/src/resources/extensions/gsd/commands-maintenance.ts +74 -40
  241. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  242. package/src/resources/extensions/gsd/commands-verdict.ts +19 -2
  243. package/src/resources/extensions/gsd/db-workspace.ts +170 -0
  244. package/src/resources/extensions/gsd/delegation-policy.ts +3 -11
  245. package/src/resources/extensions/gsd/discussion-handoff.ts +276 -0
  246. package/src/resources/extensions/gsd/docs/preferences-reference.md +9 -0
  247. package/src/resources/extensions/gsd/doctor.ts +15 -5
  248. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  249. package/src/resources/extensions/gsd/git-conflict-state.ts +17 -1
  250. package/src/resources/extensions/gsd/gsd-db.ts +12 -0
  251. package/src/resources/extensions/gsd/guided-flow.ts +47 -558
  252. package/src/resources/extensions/gsd/guided-unit-completion.ts +275 -0
  253. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -1
  254. package/src/resources/extensions/gsd/mcp-filter.ts +2 -1
  255. package/src/resources/extensions/gsd/mcp-tool-name.ts +35 -0
  256. package/src/resources/extensions/gsd/md-importer.ts +3 -3
  257. package/src/resources/extensions/gsd/migrate/safety.ts +2 -2
  258. package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
  259. package/src/resources/extensions/gsd/milestone-closeout-proof.ts +131 -0
  260. package/src/resources/extensions/gsd/milestone-closeout.ts +12 -4
  261. package/src/resources/extensions/gsd/milestone-merge-transaction.ts +47 -0
  262. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +224 -0
  263. package/src/resources/extensions/gsd/milestone-readiness.ts +125 -0
  264. package/src/resources/extensions/gsd/milestone-settlement.ts +81 -0
  265. package/src/resources/extensions/gsd/milestone-validation-evidence.ts +95 -0
  266. package/src/resources/extensions/gsd/milestone-validation-verdict.ts +80 -0
  267. package/src/resources/extensions/gsd/parallel-eligibility.ts +4 -5
  268. package/src/resources/extensions/gsd/parallel-orchestrator.ts +6 -2
  269. package/src/resources/extensions/gsd/preferences-diagnostics.ts +98 -0
  270. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  271. package/src/resources/extensions/gsd/preferences.ts +173 -28
  272. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -0
  273. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  274. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +3 -1
  275. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -0
  276. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -0
  277. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -0
  278. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  279. package/src/resources/extensions/gsd/provider-payload-policy.ts +140 -0
  280. package/src/resources/extensions/gsd/pull-request-process.ts +41 -0
  281. package/src/resources/extensions/gsd/quality-gate-closure.ts +140 -0
  282. package/src/resources/extensions/gsd/question-transport.ts +138 -0
  283. package/src/resources/extensions/gsd/roadmap-slices.ts +8 -2
  284. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +6 -2
  285. package/src/resources/extensions/gsd/state.ts +15 -5
  286. package/src/resources/extensions/gsd/templates/plan.md +7 -0
  287. package/src/resources/extensions/gsd/templates/project.md +1 -0
  288. package/src/resources/extensions/gsd/templates/roadmap.md +1 -1
  289. package/src/resources/extensions/gsd/templates/uat.md +5 -1
  290. package/src/resources/extensions/gsd/tests/ask-user-questions-render.test.ts +92 -0
  291. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +29 -1
  292. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +321 -5
  293. package/src/resources/extensions/gsd/tests/auto-milestone-target.test.ts +23 -0
  294. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +18 -0
  295. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +133 -4
  296. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +34 -0
  297. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +20 -0
  298. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  299. package/src/resources/extensions/gsd/tests/commands-dispatcher-workspace-git.test.ts +11 -0
  300. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +38 -1
  301. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +34 -3
  302. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +88 -0
  303. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +18 -0
  304. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +1 -0
  305. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +1 -5
  306. package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +1 -5
  307. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +48 -1
  308. package/src/resources/extensions/gsd/tests/mcp-tool-name.test.ts +34 -0
  309. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +58 -0
  310. package/src/resources/extensions/gsd/tests/milestone-closeout-proof.test.ts +99 -0
  311. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +25 -0
  312. package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +46 -0
  313. package/src/resources/extensions/gsd/tests/milestone-readiness.test.ts +65 -0
  314. package/src/resources/extensions/gsd/tests/milestone-validation-evidence.test.ts +41 -0
  315. package/src/resources/extensions/gsd/tests/milestone-validation-verdict.test.ts +55 -0
  316. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +45 -0
  317. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  318. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +45 -0
  319. package/src/resources/extensions/gsd/tests/preferences-diagnostics.test.ts +67 -0
  320. package/src/resources/extensions/gsd/tests/preferences.test.ts +183 -0
  321. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +46 -0
  322. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  323. package/src/resources/extensions/gsd/tests/provider-payload-policy.test.ts +165 -0
  324. package/src/resources/extensions/gsd/tests/pull-request-process.test.ts +47 -0
  325. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +94 -0
  326. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +40 -0
  327. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +25 -1
  328. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +80 -0
  329. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +101 -1
  330. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +27 -0
  331. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +2 -1
  332. package/src/resources/extensions/gsd/tests/tool-availability-audit.test.ts +35 -0
  333. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +35 -42
  334. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +23 -0
  335. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +47 -0
  336. package/src/resources/extensions/gsd/tests/user-input-boundary.test.ts +86 -1
  337. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +39 -0
  338. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +150 -0
  339. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +126 -9
  340. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +15 -0
  341. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +21 -0
  342. package/src/resources/extensions/gsd/tests/worktree-projection-writers.test.ts +1 -1
  343. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +22 -0
  344. package/src/resources/extensions/gsd/tests/write-gate.test.ts +79 -0
  345. package/src/resources/extensions/gsd/tool-contract.ts +86 -8
  346. package/src/resources/extensions/gsd/tool-presentation-plan.ts +16 -33
  347. package/src/resources/extensions/gsd/tool-surface-snapshot.ts +47 -0
  348. package/src/resources/extensions/gsd/tools/plan-milestone.ts +19 -160
  349. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +43 -0
  350. package/src/resources/extensions/gsd/tools/validate-milestone.ts +25 -84
  351. package/src/resources/extensions/gsd/uat-policy.ts +19 -10
  352. package/src/resources/extensions/gsd/uat-run.ts +10 -14
  353. package/src/resources/extensions/gsd/unit-context-composer.ts +85 -20
  354. package/src/resources/extensions/gsd/unit-runtime.ts +3 -2
  355. package/src/resources/extensions/gsd/unit-tool-contracts.ts +2 -1
  356. package/src/resources/extensions/gsd/user-input-boundary.ts +18 -0
  357. package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
  358. package/src/resources/extensions/gsd/web-app-uat.ts +101 -0
  359. package/src/resources/extensions/gsd/workflow-mcp.ts +22 -110
  360. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  361. package/src/resources/extensions/gsd/workflow-tool-surface.ts +73 -0
  362. package/src/resources/extensions/gsd/workspace-git-guard.ts +1 -0
  363. package/src/resources/extensions/gsd/worktree-lifecycle.ts +7 -16
  364. package/src/resources/extensions/gsd/worktree-state-projection.ts +55 -7
  365. package/src/resources/extensions/gsd/worktree-telemetry.ts +16 -0
  366. package/src/resources/extensions/shared/interview-ui.ts +15 -2
  367. package/src/resources/shared/claude-runtime-floor.ts +248 -0
  368. package/dist/web/standalone/.next/static/chunks/2659.feb6499ca863ebfc.js +0 -1
  369. package/dist/web/standalone/.next/static/chunks/2772.151789db0edea835.js +0 -1
  370. package/dist/web/standalone/.next/static/chunks/4283.10a065467b5340d8.js +0 -2
  371. package/dist/web/standalone/.next/static/chunks/5826.960dc4634cc9b0d3.js +0 -1
  372. package/dist/web/standalone/.next/static/chunks/796.46f811c0fac23aab.js +0 -10
  373. package/dist/web/standalone/.next/static/chunks/8785.d32f7a61f55c1600.js +0 -1
  374. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_buildManifest.js +0 -0
  375. /package/dist/web/standalone/.next/static/{Qbr81pQ-pbQXP4bq2VXLv → 3PtrU9qGPEXwNLWkIyiqk}/_ssgManifest.js +0 -0
@@ -0,0 +1,150 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+
7
+ import {
8
+ buildWebAppUatGuidanceBlock,
9
+ detectWebApp,
10
+ findPlaywrightTestScript,
11
+ hasPlaywrightTestDependency,
12
+ } from "../web-app-uat.ts";
13
+
14
+ function scaffoldProject(root: string, pkg: Record<string, unknown>): void {
15
+ writeFileSync(join(root, "package.json"), JSON.stringify(pkg, null, 2));
16
+ }
17
+
18
+ describe("web-app-uat guidance", () => {
19
+ test("returns null for non-web projects", () => {
20
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
21
+ try {
22
+ writeFileSync(join(root, "README.md"), "# CLI tool\n");
23
+ assert.equal(detectWebApp(root), false);
24
+ assert.equal(buildWebAppUatGuidanceBlock(root), null);
25
+ } finally {
26
+ rmSync(root, { recursive: true, force: true });
27
+ }
28
+ });
29
+
30
+ test("returns guidance for react/vite web apps", () => {
31
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
32
+ try {
33
+ scaffoldProject(root, {
34
+ dependencies: { react: "19.0.0", "react-dom": "19.0.0" },
35
+ devDependencies: { vite: "6.0.0" },
36
+ scripts: { dev: "vite" },
37
+ });
38
+ assert.equal(detectWebApp(root), true);
39
+ const block = buildWebAppUatGuidanceBlock(root);
40
+ assert.ok(block);
41
+ assert.match(block!, /browser-executable/);
42
+ assert.match(block!, /Playwright scaffolding/);
43
+ } finally {
44
+ rmSync(root, { recursive: true, force: true });
45
+ }
46
+ });
47
+
48
+ test("detects existing Playwright and npm script", () => {
49
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
50
+ try {
51
+ scaffoldProject(root, {
52
+ dependencies: { next: "15.0.0" },
53
+ devDependencies: { "@playwright/test": "1.60.0", playwright: "1.60.0" },
54
+ scripts: { "test:e2e": "playwright test" },
55
+ });
56
+ assert.equal(hasPlaywrightTestDependency(root), true);
57
+ assert.equal(findPlaywrightTestScript(root), "npm run test:e2e");
58
+ const block = buildWebAppUatGuidanceBlock(root);
59
+ assert.ok(block);
60
+ assert.match(block!, /dependency detected/);
61
+ assert.match(block!, /npm run test:e2e/);
62
+ assert.doesNotMatch(block!, /Playwright scaffolding/);
63
+ } finally {
64
+ rmSync(root, { recursive: true, force: true });
65
+ }
66
+ });
67
+
68
+ test("does not mistake a non-playwright e2e script as playwright", () => {
69
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
70
+ try {
71
+ scaffoldProject(root, {
72
+ dependencies: { react: "19.0.0" },
73
+ devDependencies: { "@playwright/test": "1.60.0" },
74
+ scripts: { e2e: "cypress run", dev: "vite" },
75
+ });
76
+ // The e2e script runs Cypress, not Playwright — must not be returned
77
+ assert.equal(findPlaywrightTestScript(root), null);
78
+ const block = buildWebAppUatGuidanceBlock(root);
79
+ assert.ok(block);
80
+ // Playwright dep is present so guidance shows "dependency detected", not "scaffolding"
81
+ assert.match(block!, /dependency detected/);
82
+ assert.doesNotMatch(block!, /Playwright scaffolding/);
83
+ // Falls back to generic npx command because no playwright-named script exists
84
+ assert.doesNotMatch(block!, /npm run e2e/);
85
+ } finally {
86
+ rmSync(root, { recursive: true, force: true });
87
+ }
88
+ });
89
+
90
+ test("recognises playwright via script value even without a local dependency", () => {
91
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
92
+ try {
93
+ scaffoldProject(root, {
94
+ dependencies: { next: "15.0.0" },
95
+ scripts: { "test:e2e": "npx playwright test" },
96
+ });
97
+ assert.equal(hasPlaywrightTestDependency(root), false);
98
+ assert.equal(findPlaywrightTestScript(root), "npm run test:e2e");
99
+ const block = buildWebAppUatGuidanceBlock(root);
100
+ assert.ok(block);
101
+ // Script-based detection should trigger "dependency detected" path, not scaffolding
102
+ assert.match(block!, /dependency detected/);
103
+ assert.match(block!, /npm run test:e2e/);
104
+ assert.doesNotMatch(block!, /Playwright scaffolding/);
105
+ } finally {
106
+ rmSync(root, { recursive: true, force: true });
107
+ }
108
+ });
109
+
110
+ test("does not treat playwright install script as a test command", () => {
111
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
112
+ try {
113
+ scaffoldProject(root, {
114
+ dependencies: { react: "19.0.0" },
115
+ devDependencies: { "@playwright/test": "1.60.0" },
116
+ // postinstall runs 'playwright install' (browser setup), not a test
117
+ scripts: { postinstall: "playwright install", "test:e2e": "npx playwright test" },
118
+ });
119
+ // install script must not be returned; the real test script should be
120
+ assert.equal(findPlaywrightTestScript(root), "npm run test:e2e");
121
+ } finally {
122
+ rmSync(root, { recursive: true, force: true });
123
+ }
124
+ });
125
+
126
+ test("returns null when only an install script mentions playwright", () => {
127
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
128
+ try {
129
+ scaffoldProject(root, {
130
+ dependencies: { react: "19.0.0" },
131
+ devDependencies: { "@playwright/test": "1.60.0" },
132
+ scripts: { postinstall: "playwright install --with-deps" },
133
+ });
134
+ assert.equal(findPlaywrightTestScript(root), null);
135
+ } finally {
136
+ rmSync(root, { recursive: true, force: true });
137
+ }
138
+ });
139
+
140
+ test("detects static sites via index.html", () => {
141
+ const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
142
+ try {
143
+ mkdirSync(join(root, "public"), { recursive: true });
144
+ writeFileSync(join(root, "public", "index.html"), "<html></html>");
145
+ assert.equal(detectWebApp(root), true);
146
+ } finally {
147
+ rmSync(root, { recursive: true, force: true });
148
+ }
149
+ });
150
+ });
@@ -17,9 +17,11 @@ import {
17
17
  getRequiredWorkflowToolsForAutoUnit,
18
18
  getRequiredWorkflowToolsForGuidedUnit,
19
19
  resolveWorkflowMcpProjectRoot,
20
+ isWorkflowMcpSurfaceTool,
20
21
  supportsStructuredQuestions,
21
22
  usesWorkflowMcpTransport,
22
23
  } from "../workflow-mcp.ts";
24
+ import { DB_WORKFLOW_TOOL_NAMES } from "../workflow-tool-surface.ts";
23
25
 
24
26
  const MCP_STDIO_TIMEOUT_MS = 90_000;
25
27
 
@@ -109,6 +111,18 @@ test("workflow MCP capability surface includes native legacy gsd aliases", () =>
109
111
  assert.equal(err, null);
110
112
  });
111
113
 
114
+ test("workflow MCP capability surface includes every shared workflow contract tool", () => {
115
+ for (const name of DB_WORKFLOW_TOOL_NAMES) {
116
+ assert.equal(isWorkflowMcpSurfaceTool(name), true, `${name} should be in workflow MCP surface`);
117
+ }
118
+ });
119
+
120
+ test("workflow MCP capability surface preserves session and read tools outside DB contracts", () => {
121
+ for (const name of ["gsd_execute", "gsd_status", "gsd_progress", "gsd_doctor", "gsd_graph"]) {
122
+ assert.equal(isWorkflowMcpSurfaceTool(name), true, `${name} should stay in workflow MCP surface`);
123
+ }
124
+ });
125
+
112
126
  test("deep project setup units declare required workflow MCP tools", () => {
113
127
  assert.deepEqual(getRequiredWorkflowToolsForGuidedUnit("discuss-project"), [
114
128
  "ask_user_questions",
@@ -615,23 +629,47 @@ test("usesWorkflowMcpTransport matches local externalCli providers", () => {
615
629
  assert.equal(usesWorkflowMcpTransport("oauth", "local://custom"), false);
616
630
  });
617
631
 
618
- test("supportsStructuredQuestions disables local workflow MCP questions unless explicitly enabled", () => {
632
+ test("supportsStructuredQuestions recognizes workflow MCP question tools", () => {
619
633
  assert.equal(
620
634
  supportsStructuredQuestions(["ask_user_questions"], {
621
635
  authMode: "externalCli",
622
636
  baseUrl: "local://claude-code",
623
637
  env: {},
624
638
  }),
625
- false,
639
+ true,
626
640
  );
627
641
  assert.equal(
628
642
  supportsStructuredQuestions(["mcp__gsd-workflow__ask_user_questions"], {
629
643
  authMode: "externalCli",
630
644
  baseUrl: "local://claude-code",
631
- env: { GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS: "1" } as NodeJS.ProcessEnv,
645
+ env: {},
646
+ }),
647
+ true,
648
+ );
649
+ assert.equal(
650
+ supportsStructuredQuestions(["mcp__gsd-workflow__*"], {
651
+ authMode: "externalCli",
652
+ baseUrl: "local://claude-code",
653
+ env: {},
632
654
  }),
633
655
  true,
634
656
  );
657
+ assert.equal(
658
+ supportsStructuredQuestions(["mcp__gsd-browser__*"], {
659
+ authMode: "externalCli",
660
+ baseUrl: "local://claude-code",
661
+ env: {},
662
+ }),
663
+ false,
664
+ );
665
+ assert.equal(
666
+ supportsStructuredQuestions(["mcp__gsd-workflow__ask_user_questions"], {
667
+ authMode: "externalCli",
668
+ baseUrl: "local://claude-code",
669
+ env: { GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS: "0" } as NodeJS.ProcessEnv,
670
+ }),
671
+ false,
672
+ );
635
673
  assert.equal(
636
674
  supportsStructuredQuestions(["ask_user_questions"], {
637
675
  authMode: "oauth",
@@ -648,6 +686,39 @@ test("supportsStructuredQuestions disables local workflow MCP questions unless e
648
686
  );
649
687
  });
650
688
 
689
+ test("supportsStructuredQuestions gates non-local externalCli providers", () => {
690
+ assert.equal(
691
+ supportsStructuredQuestions(["ask_user_questions"], {
692
+ authMode: "externalCli",
693
+ env: {},
694
+ }),
695
+ true,
696
+ );
697
+ assert.equal(
698
+ supportsStructuredQuestions(["ask_user_questions"], {
699
+ authMode: "externalCli",
700
+ baseUrl: "https://api.example.com",
701
+ env: {},
702
+ }),
703
+ true,
704
+ );
705
+ assert.equal(
706
+ supportsStructuredQuestions(["ask_user_questions"], {
707
+ authMode: "externalCli",
708
+ env: { GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS: "0" } as NodeJS.ProcessEnv,
709
+ }),
710
+ false,
711
+ );
712
+ assert.equal(
713
+ supportsStructuredQuestions(["ask_user_questions"], {
714
+ authMode: "externalCli",
715
+ baseUrl: "https://api.example.com",
716
+ env: { GSD_WORKFLOW_MCP_STRUCTURED_QUESTIONS: "0" } as NodeJS.ProcessEnv,
717
+ }),
718
+ false,
719
+ );
720
+ });
721
+
651
722
  test("transport compatibility passes when required tools fit current MCP surface", () => {
652
723
  const error = getWorkflowTransportSupportError(
653
724
  "claude-code",
@@ -835,7 +906,7 @@ test("transport compatibility now allows replan-slice over workflow MCP surface"
835
906
  assert.equal(error, null);
836
907
  });
837
908
 
838
- test("transport compatibility rejects MCP tools not connected in active tool surface", () => {
909
+ test("transport compatibility accepts workflow MCP tools absent from parent active tool surface", () => {
839
910
  const error = getWorkflowTransportSupportError(
840
911
  "claude-code",
841
912
  ["gsd_summary_save"],
@@ -850,10 +921,10 @@ test("transport compatibility rejects MCP tools not connected in active tool sur
850
921
  },
851
922
  );
852
923
 
853
- assert.match(error ?? "", /requires gsd_summary_save/);
924
+ assert.equal(error, null);
854
925
  });
855
926
 
856
- test("transport compatibility checks all required tools against active tool surface", () => {
927
+ test("transport compatibility still checks non-MCP tools against parent active tool surface", () => {
857
928
  const error = getWorkflowTransportSupportError(
858
929
  "claude-code",
859
930
  ["gsd_summary_save", "secure_env_collect"],
@@ -868,9 +939,54 @@ test("transport compatibility checks all required tools against active tool surf
868
939
  },
869
940
  );
870
941
 
871
- assert.match(error ?? "", /requires.*(?:gsd_summary_save|secure_env_collect)/);
872
- assert.match(error ?? "", /gsd_summary_save/);
873
- assert.match(error ?? "", /secure_env_collect/);
942
+ assert.match(error ?? "", /requires secure_env_collect/);
943
+ assert.doesNotMatch(error ?? "", /gsd_summary_save/);
944
+ });
945
+
946
+ test("transport compatibility allows plan-slice MCP tools when parent surface is scoped (regression #457)", () => {
947
+ const error = getWorkflowTransportSupportError(
948
+ "claude-code",
949
+ getRequiredWorkflowToolsForAutoUnit("plan-slice"),
950
+ {
951
+ projectRoot: "/tmp/project",
952
+ env: { GSD_WORKFLOW_MCP_COMMAND: "node" },
953
+ surface: "auto-mode",
954
+ unitType: "plan-slice",
955
+ authMode: "externalCli",
956
+ baseUrl: "local://claude-code",
957
+ activeTools: [
958
+ "ScheduleWakeup",
959
+ "ToolSearch",
960
+ "ask_user_questions",
961
+ "async_bash",
962
+ "await_job",
963
+ "bash",
964
+ "bg_shell",
965
+ "cancel_job",
966
+ "capture_thought",
967
+ "discover_configs",
968
+ "edit",
969
+ "fetch_page",
970
+ "get_library_docs",
971
+ "gsd_checkpoint_db",
972
+ "gsd_exec",
973
+ "gsd_exec_search",
974
+ "gsd_milestone_status",
975
+ "gsd_resume",
976
+ "mcp_call",
977
+ "mcp_discover",
978
+ "mcp_servers",
979
+ "memory_query",
980
+ "read",
981
+ "resolve_library",
982
+ "secure_env_collect",
983
+ "subagent",
984
+ "write",
985
+ ],
986
+ },
987
+ );
988
+
989
+ assert.equal(error, null);
874
990
  });
875
991
 
876
992
  test("transport compatibility still blocks units whose MCP tools are not exposed", () => {
@@ -895,6 +1011,7 @@ test("discuss-milestone guided flow does not abort when all required tools are o
895
1011
  // Guided flow starts the workflow MCP server as part of dispatch, so the
896
1012
  // parent session active-tool list is not authoritative for MCP tools.
897
1013
  const discussMilestoneTools = [
1014
+ "ask_user_questions",
898
1015
  "gsd_summary_save",
899
1016
  "gsd_requirement_save",
900
1017
  "gsd_requirement_update",
@@ -109,6 +109,21 @@ test("ensureWorkspaceGitReadyForPath blocks product file conflicts", async () =>
109
109
  }
110
110
  });
111
111
 
112
+ test("ensureWorkspaceGitReadyForPath does not block on staged files with trailing whitespace", async () => {
113
+ // Regression test for #599: git diff --check exits non-zero for whitespace
114
+ // errors as well as conflict markers. Staged files from auto-generated code
115
+ // often have trailing whitespace; these must not trigger the conflict gate.
116
+ const base = makeTempRepo("gsd-ws-git-whitespace-");
117
+ try {
118
+ writeFileSync(join(base, "app.ts"), "const x = 1; \n"); // trailing whitespace
119
+ git(base, "add", "app.ts");
120
+ const ready = await ensureWorkspaceGitReadyForPath(base);
121
+ assert.equal(ready.ok, true, "trailing whitespace in staged files must not block");
122
+ } finally {
123
+ cleanup(base);
124
+ }
125
+ });
126
+
112
127
  test("isWorkspaceGitAllowedCommand allowlists doctor, closeout, and dispatch complete-milestone", () => {
113
128
  assert.equal(isWorkspaceGitAllowedCommand("doctor"), true);
114
129
  assert.equal(isWorkspaceGitAllowedCommand("doctor fix"), true);
@@ -121,6 +121,27 @@ describe("createWorktree", () => {
121
121
  run("git rev-parse --git-dir", info.path);
122
122
  assert.ok(!existsSync(join(info.path, "orphan.txt")), "stale file removed by recovery");
123
123
  });
124
+
125
+ test("removes stale worktree directory with .git file not registered with git and recreates", () => {
126
+ // Simulate the scenario from issue #590: removeWorktree() failed to delete
127
+ // the directory (e.g. EPERM on Windows) but git worktree prune cleaned up
128
+ // the registry — leaving an orphaned directory with a .git *file* (not
129
+ // directory) that git no longer knows about.
130
+ const staleDir = worktreePath(base, "M010");
131
+ mkdirSync(staleDir, { recursive: true });
132
+ // Write a .git file (worktree gitdir pointer) — not a directory
133
+ writeFileSync(join(staleDir, ".git"), "gitdir: ../../../../.git/worktrees/M010\n", "utf-8");
134
+ writeFileSync(join(staleDir, "orphan.txt"), "stale leftover\n", "utf-8");
135
+
136
+ // createWorktree must detect the orphan (not in git worktree list), clean it
137
+ // up, and succeed — not throw GSD_STALE_STATE as if it were a live conflict.
138
+ const info = createWorktree(base, "M010");
139
+ assert.strictEqual(info.name, "M010");
140
+ assert.ok(existsSync(info.path));
141
+ assert.ok(existsSync(join(info.path, ".git")), "recovered worktree has .git marker");
142
+ run("git rev-parse --git-dir", info.path);
143
+ assert.ok(!existsSync(join(info.path, "orphan.txt")), "stale file removed by recovery");
144
+ });
124
145
  });
125
146
 
126
147
  describe("createWorktree — duplicate rejection", () => {
@@ -171,7 +171,7 @@ test("validate-milestone invoked from the project root writes VALIDATION under t
171
171
  const projectProjection = join(projectRoot, ".gsd", "milestones", MID, "M001-VALIDATION.md");
172
172
  assert.equal(result.validationPath, expected);
173
173
  assert.equal(existsSync(expected), true);
174
- assert.equal(existsSync(projectProjection), false);
174
+ assert.equal(existsSync(projectProjection), true, "VALIDATION should mirror to project root for consistent reads");
175
175
  });
176
176
 
177
177
  test("complete-milestone writes SUMMARY under the active worktree projection", async (t) => {
@@ -18,6 +18,7 @@ import {
18
18
  emitWorktreeOrphaned,
19
19
  emitAutoExit,
20
20
  emitCanonicalRootRedirect,
21
+ autoExitReasonForTerminalOutcome,
21
22
  normalizeAutoExitReason,
22
23
  summarizeWorktreeTelemetry,
23
24
  percentile,
@@ -117,6 +118,27 @@ test("normalizeAutoExitReason maps new buckets and ignores casing", () => {
117
118
  }
118
119
  });
119
120
 
121
+ test("autoExitReasonForTerminalOutcome maps typed terminal outcomes", () => {
122
+ assert.equal(
123
+ autoExitReasonForTerminalOutcome({
124
+ code: "all-complete",
125
+ displayReason: "All milestones complete",
126
+ allMilestonesComplete: true,
127
+ }),
128
+ "all-complete",
129
+ );
130
+ assert.equal(
131
+ autoExitReasonForTerminalOutcome({
132
+ code: "settlement-blocked",
133
+ displayReason: "Milestone complete, merge blocked",
134
+ nextAction: "Retry closeout",
135
+ milestoneId: "M001",
136
+ allMilestonesComplete: false,
137
+ }),
138
+ "blocked",
139
+ );
140
+ });
141
+
120
142
  test("summarizeWorktreeTelemetry only counts unmerged exits from active worktrees", (t) => {
121
143
  const base = makeTmpBase();
122
144
  t.after(() => cleanup(base));
@@ -340,6 +340,8 @@ test('write-gate: reopening a gate revokes its previous verified approval', () =
340
340
  // ═══════════════════════════════════════════════════════════════════════
341
341
 
342
342
  import {
343
+ applyAskUserQuestionsGateResult,
344
+ formatPendingAskUserQuestionsGateMessage,
343
345
  isGateQuestionId,
344
346
  shouldBlockPendingGate,
345
347
  shouldBlockPendingGateBash,
@@ -379,6 +381,83 @@ test('write-gate: pending gate lifecycle (set, get, clear)', () => {
379
381
  assert.strictEqual(getPendingGate(), null, 'clearDiscussionFlowState clears pending gate');
380
382
  });
381
383
 
384
+ test('write-gate: applyAskUserQuestionsGateResult keeps cancelled pending gate waiting', () => {
385
+ const base = join(tmpdir(), `gsd-write-gate-ask-waiting-${randomUUID()}`);
386
+ const gateId = 'depth_verification_M001_confirm';
387
+
388
+ try {
389
+ mkdirSync(base, { recursive: true });
390
+ clearDiscussionFlowState(base);
391
+ setPendingGate(gateId, base);
392
+
393
+ const result = applyAskUserQuestionsGateResult({
394
+ basePath: base,
395
+ questions: [{
396
+ id: gateId,
397
+ options: [{ label: 'Confirm depth (Recommended)' }, { label: 'Needs adjustment' }],
398
+ }],
399
+ details: { cancelled: true, interrupted: true },
400
+ });
401
+
402
+ assert.deepEqual(result, {
403
+ status: 'waiting',
404
+ pendingGateId: gateId,
405
+ interrupted: true,
406
+ });
407
+ assert.strictEqual(getPendingGate(base), gateId, 'cancelled question must leave gate pending');
408
+ assert.strictEqual(isMilestoneDepthVerified('M001', base), false, 'cancelled question must not verify depth');
409
+ assert.match(
410
+ formatPendingAskUserQuestionsGateMessage(gateId, true),
411
+ /Re-call ask_user_questions with the same gate question id/,
412
+ );
413
+ } finally {
414
+ clearDiscussionFlowState(base);
415
+ rmSync(base, { recursive: true, force: true });
416
+ }
417
+ });
418
+
419
+ test('write-gate: applyAskUserQuestionsGateResult verifies confirmed pending gate', () => {
420
+ const base = join(tmpdir(), `gsd-write-gate-ask-verified-${randomUUID()}`);
421
+ const gateId = 'depth_verification_M001_confirm';
422
+ const confirmLabel = 'Confirm depth (Recommended)';
423
+
424
+ try {
425
+ mkdirSync(base, { recursive: true });
426
+ clearDiscussionFlowState(base);
427
+ setPendingGate(gateId, base);
428
+
429
+ const result = applyAskUserQuestionsGateResult({
430
+ basePath: base,
431
+ questions: [{
432
+ id: gateId,
433
+ options: [{ label: confirmLabel }, { label: 'Needs adjustment' }],
434
+ }],
435
+ details: {
436
+ response: {
437
+ answers: {
438
+ [gateId]: { selected: confirmLabel },
439
+ },
440
+ },
441
+ },
442
+ });
443
+
444
+ assert.deepEqual(result, {
445
+ status: 'verified',
446
+ gateId,
447
+ milestoneId: 'M001',
448
+ });
449
+ assert.strictEqual(getPendingGate(base), null, 'confirmed gate must clear pending state');
450
+ assert.strictEqual(isMilestoneDepthVerified('M001', base), true, 'confirmed gate must verify milestone depth');
451
+ assert.ok(
452
+ loadWriteGateSnapshot(base).verifiedApprovalGates?.includes(gateId) ?? false,
453
+ 'confirmed gate must record verified approval',
454
+ );
455
+ } finally {
456
+ clearDiscussionFlowState(base);
457
+ rmSync(base, { recursive: true, force: true });
458
+ }
459
+ });
460
+
382
461
  // ─── Scenario 21: shouldBlockPendingGate blocks non-safe tools when gate is pending ──
383
462
 
384
463
  test('write-gate: shouldBlockPendingGate blocks write/edit during pending gate', () => {