@opengsd/gsd-pi 1.2.0-dev.e8563f58 → 1.2.0-dev.fbdca60b

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 (432) hide show
  1. package/dist/cli-model-override.d.ts +15 -0
  2. package/dist/cli-model-override.js +21 -0
  3. package/dist/cli.js +1 -18
  4. package/dist/loader.js +6 -4
  5. package/dist/register-agent-bundles.d.ts +11 -2
  6. package/dist/register-agent-bundles.js +18 -4
  7. package/dist/resource-loader.d.ts +10 -5
  8. package/dist/resource-loader.js +121 -6
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/ask-user-questions.js +3 -2
  11. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +447 -215
  12. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +33 -1
  13. package/dist/resources/extensions/gsd/auto/closeout.js +215 -0
  14. package/dist/resources/extensions/gsd/auto/dispatch-history.js +21 -6
  15. package/dist/resources/extensions/gsd/auto/dispatch.js +365 -0
  16. package/dist/resources/extensions/gsd/auto/finalize.js +347 -0
  17. package/dist/resources/extensions/gsd/auto/loop.js +4 -1
  18. package/dist/resources/extensions/gsd/auto/milestone-lease-reclaim.js +56 -0
  19. package/dist/resources/extensions/gsd/auto/orchestrator.js +85 -15
  20. package/dist/resources/extensions/gsd/auto/phase-helpers.js +146 -0
  21. package/dist/resources/extensions/gsd/auto/phases.js +17 -2329
  22. package/dist/resources/extensions/gsd/auto/pre-dispatch.js +534 -0
  23. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  24. package/dist/resources/extensions/gsd/auto/unit-phase.js +694 -0
  25. package/dist/resources/extensions/gsd/auto/workflow-unit-dispatch.js +1 -1
  26. package/dist/resources/extensions/gsd/auto/worktree-safety-phase.js +125 -0
  27. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +3 -2
  28. package/dist/resources/extensions/gsd/auto-dispatch.js +11 -2
  29. package/dist/resources/extensions/gsd/auto-post-unit.js +18 -6
  30. package/dist/resources/extensions/gsd/auto-start.js +23 -3
  31. package/dist/resources/extensions/gsd/auto-unit-closeout.js +45 -21
  32. package/dist/resources/extensions/gsd/auto-verification.js +14 -2
  33. package/dist/resources/extensions/gsd/auto-worktree.js +15 -2
  34. package/dist/resources/extensions/gsd/auto.js +45 -2
  35. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +37 -7
  36. package/dist/resources/extensions/gsd/commands/context.js +16 -2
  37. package/dist/resources/extensions/gsd/commands-mcp-status.js +2 -2
  38. package/dist/resources/extensions/gsd/commands-workflow-templates.js +9 -2
  39. package/dist/resources/extensions/gsd/crash-recovery.js +8 -3
  40. package/dist/resources/extensions/gsd/db/engine.js +24 -6
  41. package/dist/resources/extensions/gsd/db/queries.js +30 -0
  42. package/dist/resources/extensions/gsd/db-migration-backup.js +51 -8
  43. package/dist/resources/extensions/gsd/db-transaction.js +27 -23
  44. package/dist/resources/extensions/gsd/db-writer.js +8 -17
  45. package/dist/resources/extensions/gsd/doctor-engine-checks.js +5 -5
  46. package/dist/resources/extensions/gsd/doctor-environment.js +256 -125
  47. package/dist/resources/extensions/gsd/gsd-db.js +15 -20
  48. package/dist/resources/extensions/gsd/guided-flow.js +93 -4
  49. package/dist/resources/extensions/gsd/health-widget.js +87 -28
  50. package/dist/resources/extensions/gsd/mcp-bridge.js +10 -0
  51. package/dist/resources/extensions/gsd/memory-relations.js +1 -1
  52. package/dist/resources/extensions/gsd/milestone-planning-persistence.js +2 -2
  53. package/dist/resources/extensions/gsd/milestone-reopen-events.js +3 -5
  54. package/dist/resources/extensions/gsd/milestone-settlement.js +2 -2
  55. package/dist/resources/extensions/gsd/notifications.js +12 -7
  56. package/dist/resources/extensions/gsd/projection-flush.js +7 -0
  57. package/dist/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  58. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -2
  59. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  60. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  61. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -1
  62. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  63. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  64. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  65. package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  66. package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  68. package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -1
  69. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  70. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  71. package/dist/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  72. package/dist/resources/extensions/gsd/roadmap-slices.js +25 -3
  73. package/dist/resources/extensions/gsd/session-lock.js +1 -1
  74. package/dist/resources/extensions/gsd/skill-activation.js +3 -6
  75. package/dist/resources/extensions/gsd/state.js +6 -2
  76. package/dist/resources/extensions/gsd/tool-contract.js +14 -3
  77. package/dist/resources/extensions/gsd/tool-surface-readiness.js +83 -31
  78. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  79. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  80. package/dist/resources/extensions/gsd/tools/complete-task.js +65 -2
  81. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -2
  82. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -2
  83. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +2 -2
  84. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
  85. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -2
  86. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -2
  87. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -2
  88. package/dist/resources/extensions/gsd/unit-context-composer.js +1 -1
  89. package/dist/resources/extensions/gsd/unit-registry.js +34 -4
  90. package/dist/resources/extensions/gsd/verification-verdict.js +2 -1
  91. package/dist/resources/extensions/gsd/workflow-event-ledger.js +91 -0
  92. package/dist/resources/extensions/gsd/workflow-event-vocabulary.js +46 -0
  93. package/dist/resources/extensions/gsd/workflow-events.js +6 -18
  94. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +2 -0
  95. package/dist/resources/extensions/gsd/workflow-mcp-readiness-cache.js +105 -0
  96. package/dist/resources/extensions/gsd/workflow-reconcile.js +21 -56
  97. package/dist/resources/extensions/gsd/worktree-manager.js +7 -1
  98. package/dist/resources/extensions/gsd/worktree-safety.js +28 -26
  99. package/dist/resources/extensions/gsd/worktree.js +8 -1
  100. package/dist/resources/extensions/mcp-client/manager.js +6 -1
  101. package/dist/resources/skills/create-skill/SKILL.md +3 -0
  102. package/dist/resources/skills/create-skill/references/skill-structure.md +1 -0
  103. package/dist/runtime-checks.d.ts +10 -0
  104. package/dist/runtime-checks.js +27 -0
  105. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  106. package/dist/web/standalone/.next/BUILD_ID +1 -1
  107. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  108. package/dist/web/standalone/.next/build-manifest.json +2 -2
  109. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  110. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  127. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  128. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  130. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  131. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  132. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  133. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  134. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  135. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  136. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  137. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  138. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  139. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  140. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  141. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  142. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  143. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  144. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  151. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  152. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  153. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  154. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  155. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  158. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  159. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  160. package/dist/web/standalone/.next/server/app/index.html +1 -1
  161. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  163. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  164. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  166. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  167. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  168. package/dist/web/standalone/.next/server/chunks/{5942.js → 1128.js} +1 -1
  169. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  170. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  172. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  173. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  174. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  175. package/package.json +3 -3
  176. package/packages/cloud-mcp-gateway/package.json +2 -2
  177. package/packages/contracts/package.json +1 -1
  178. package/packages/daemon/package.json +4 -4
  179. package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
  180. package/packages/gsd-agent-core/dist/sdk.js +6 -4
  181. package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
  182. package/packages/gsd-agent-core/package.json +5 -5
  183. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
  187. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +8 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +50 -6
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts +2 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.d.ts.map +1 -1
  193. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js +34 -5
  194. package/packages/gsd-agent-modes/dist/modes/interactive/components/transcript-design.js.map +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  197. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +17 -0
  198. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  199. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +4 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
  202. package/packages/gsd-agent-modes/package.json +7 -7
  203. package/packages/mcp-server/README.md +12 -3
  204. package/packages/mcp-server/dist/cli-runner.d.ts +40 -0
  205. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -0
  206. package/packages/mcp-server/dist/cli-runner.js +137 -0
  207. package/packages/mcp-server/dist/cli-runner.js.map +1 -0
  208. package/packages/mcp-server/dist/cli.js +2 -53
  209. package/packages/mcp-server/dist/cli.js.map +1 -1
  210. package/packages/mcp-server/dist/pid-registry.d.ts +46 -0
  211. package/packages/mcp-server/dist/pid-registry.d.ts.map +1 -0
  212. package/packages/mcp-server/dist/pid-registry.js +459 -0
  213. package/packages/mcp-server/dist/pid-registry.js.map +1 -0
  214. package/packages/mcp-server/dist/probe-mode.d.ts +4 -0
  215. package/packages/mcp-server/dist/probe-mode.d.ts.map +1 -0
  216. package/packages/mcp-server/dist/probe-mode.js +10 -0
  217. package/packages/mcp-server/dist/probe-mode.js.map +1 -0
  218. package/packages/mcp-server/dist/stdio-watchdog.d.ts +8 -0
  219. package/packages/mcp-server/dist/stdio-watchdog.d.ts.map +1 -0
  220. package/packages/mcp-server/dist/stdio-watchdog.js +40 -0
  221. package/packages/mcp-server/dist/stdio-watchdog.js.map +1 -0
  222. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  223. package/packages/mcp-server/dist/workflow-tools.js +62 -43
  224. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  225. package/packages/mcp-server/package.json +5 -5
  226. package/packages/native/package.json +1 -1
  227. package/packages/pi-agent-core/dist/agent-loop.js +43 -2
  228. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  229. package/packages/pi-agent-core/package.json +1 -1
  230. package/packages/pi-ai/package.json +1 -1
  231. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  232. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
  234. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/theme/theme.js +45 -17
  237. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  238. package/packages/pi-coding-agent/package.json +7 -7
  239. package/packages/pi-tui/README.md +15 -0
  240. package/packages/pi-tui/dist/index.d.ts +2 -2
  241. package/packages/pi-tui/dist/index.d.ts.map +1 -1
  242. package/packages/pi-tui/dist/index.js +2 -2
  243. package/packages/pi-tui/dist/index.js.map +1 -1
  244. package/packages/pi-tui/dist/terminal-image.d.ts +33 -0
  245. package/packages/pi-tui/dist/terminal-image.d.ts.map +1 -1
  246. package/packages/pi-tui/dist/terminal-image.js +54 -2
  247. package/packages/pi-tui/dist/terminal-image.js.map +1 -1
  248. package/packages/pi-tui/dist/terminal.d.ts +12 -0
  249. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  250. package/packages/pi-tui/dist/terminal.js +70 -25
  251. package/packages/pi-tui/dist/terminal.js.map +1 -1
  252. package/packages/pi-tui/dist/tui.d.ts +15 -0
  253. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  254. package/packages/pi-tui/dist/tui.js +106 -21
  255. package/packages/pi-tui/dist/tui.js.map +1 -1
  256. package/packages/pi-tui/dist/utils.d.ts.map +1 -1
  257. package/packages/pi-tui/dist/utils.js +110 -36
  258. package/packages/pi-tui/dist/utils.js.map +1 -1
  259. package/packages/pi-tui/package.json +2 -2
  260. package/packages/rpc-client/package.json +2 -2
  261. package/pkg/dist/theme/theme.d.ts.map +1 -1
  262. package/pkg/dist/theme/theme.js +45 -17
  263. package/pkg/dist/theme/theme.js.map +1 -1
  264. package/pkg/package.json +1 -1
  265. package/src/resources/extensions/ask-user-questions.ts +7 -2
  266. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +531 -226
  267. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +672 -7
  268. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +38 -1
  269. package/src/resources/extensions/gsd/auto/closeout.ts +309 -0
  270. package/src/resources/extensions/gsd/auto/dispatch-history.ts +22 -6
  271. package/src/resources/extensions/gsd/auto/dispatch.ts +449 -0
  272. package/src/resources/extensions/gsd/auto/finalize.ts +445 -0
  273. package/src/resources/extensions/gsd/auto/loop.ts +4 -1
  274. package/src/resources/extensions/gsd/auto/milestone-lease-reclaim.ts +74 -0
  275. package/src/resources/extensions/gsd/auto/orchestrator.ts +95 -15
  276. package/src/resources/extensions/gsd/auto/phase-helpers.ts +199 -0
  277. package/src/resources/extensions/gsd/auto/phases.ts +58 -3022
  278. package/src/resources/extensions/gsd/auto/pre-dispatch.ts +704 -0
  279. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  280. package/src/resources/extensions/gsd/auto/unit-phase.ts +910 -0
  281. package/src/resources/extensions/gsd/auto/workflow-unit-dispatch.ts +1 -1
  282. package/src/resources/extensions/gsd/auto/worktree-safety-phase.ts +149 -0
  283. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +10 -16
  284. package/src/resources/extensions/gsd/auto-dispatch.ts +11 -10
  285. package/src/resources/extensions/gsd/auto-post-unit.ts +21 -6
  286. package/src/resources/extensions/gsd/auto-start.ts +24 -4
  287. package/src/resources/extensions/gsd/auto-unit-closeout.ts +83 -28
  288. package/src/resources/extensions/gsd/auto-verification.ts +18 -2
  289. package/src/resources/extensions/gsd/auto-worktree.ts +15 -2
  290. package/src/resources/extensions/gsd/auto.ts +56 -2
  291. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +56 -6
  292. package/src/resources/extensions/gsd/commands/context.ts +16 -2
  293. package/src/resources/extensions/gsd/commands-mcp-status.ts +2 -2
  294. package/src/resources/extensions/gsd/commands-workflow-templates.ts +11 -4
  295. package/src/resources/extensions/gsd/crash-recovery.ts +10 -2
  296. package/src/resources/extensions/gsd/db/engine.ts +26 -6
  297. package/src/resources/extensions/gsd/db/queries.ts +29 -0
  298. package/src/resources/extensions/gsd/db-migration-backup.ts +56 -7
  299. package/src/resources/extensions/gsd/db-transaction.ts +37 -20
  300. package/src/resources/extensions/gsd/db-writer.ts +11 -19
  301. package/src/resources/extensions/gsd/doctor-engine-checks.ts +5 -4
  302. package/src/resources/extensions/gsd/doctor-environment.ts +267 -142
  303. package/src/resources/extensions/gsd/gsd-db.ts +15 -19
  304. package/src/resources/extensions/gsd/guided-flow.ts +145 -24
  305. package/src/resources/extensions/gsd/health-widget.ts +91 -27
  306. package/src/resources/extensions/gsd/mcp-bridge.ts +39 -0
  307. package/src/resources/extensions/gsd/memory-relations.ts +1 -1
  308. package/src/resources/extensions/gsd/milestone-planning-persistence.ts +2 -2
  309. package/src/resources/extensions/gsd/milestone-reopen-events.ts +3 -6
  310. package/src/resources/extensions/gsd/milestone-settlement.ts +2 -2
  311. package/src/resources/extensions/gsd/notifications.ts +13 -6
  312. package/src/resources/extensions/gsd/projection-flush.ts +20 -0
  313. package/src/resources/extensions/gsd/prompts/complete-slice.md +2 -2
  314. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -2
  315. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  316. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  317. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -1
  318. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
  319. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  320. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  321. package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
  322. package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
  323. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  324. package/src/resources/extensions/gsd/prompts/run-uat.md +3 -1
  325. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  326. package/src/resources/extensions/gsd/prompts/validate-milestone.md +1 -1
  327. package/src/resources/extensions/gsd/prompts/workflow-start.md +2 -1
  328. package/src/resources/extensions/gsd/roadmap-slices.ts +28 -3
  329. package/src/resources/extensions/gsd/session-lock.ts +1 -1
  330. package/src/resources/extensions/gsd/skill-activation.ts +3 -6
  331. package/src/resources/extensions/gsd/state.ts +7 -1
  332. package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/auto-blocked-remediation-message.test.ts +1 -1
  334. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +206 -22
  335. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +6 -1
  336. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +76 -12
  337. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +1 -1
  338. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +77 -1
  339. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +2 -1
  340. package/src/resources/extensions/gsd/tests/auto-remote-session-lock-cleanup.test.ts +65 -3
  341. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +236 -0
  342. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +169 -1
  343. package/src/resources/extensions/gsd/tests/complete-task.test.ts +141 -5
  344. package/src/resources/extensions/gsd/tests/db-migration-backup.test.ts +68 -19
  345. package/src/resources/extensions/gsd/tests/db-transaction.test.ts +59 -0
  346. package/src/resources/extensions/gsd/tests/db-writer.test.ts +15 -4
  347. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +2 -1
  348. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +62 -0
  349. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +12 -2
  350. package/src/resources/extensions/gsd/tests/dispatch-history.test.ts +55 -0
  351. package/src/resources/extensions/gsd/tests/dist-redirect.mjs +8 -0
  352. package/src/resources/extensions/gsd/tests/engine-interfaces-contract.test.ts +117 -91
  353. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +113 -0
  354. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +19 -0
  355. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +18 -6
  356. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +15 -0
  357. package/src/resources/extensions/gsd/tests/integration/doctor-environment-async.test.ts +104 -0
  358. package/src/resources/extensions/gsd/tests/integration/run-uat.test.ts +18 -0
  359. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +47 -16
  360. package/src/resources/extensions/gsd/tests/mcp-readiness-preflight.test.ts +205 -0
  361. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +6 -5
  362. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +1 -1
  363. package/src/resources/extensions/gsd/tests/milestone-report-path.test.ts +1 -1
  364. package/src/resources/extensions/gsd/tests/milestone-settlement.test.ts +92 -0
  365. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +1 -1
  366. package/src/resources/extensions/gsd/tests/notifications.test.ts +64 -9
  367. package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +2 -2
  368. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +5 -0
  369. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +1 -1
  370. package/src/resources/extensions/gsd/tests/phases-terminal-complete-idempotent.test.ts +242 -0
  371. package/src/resources/extensions/gsd/tests/plan-gate-failed-doctor-heal-hint.test.ts +3 -3
  372. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +63 -2
  373. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -2
  374. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -4
  375. package/src/resources/extensions/gsd/tests/remote-notification-from-desktop.test.ts +31 -81
  376. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +68 -0
  377. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +26 -2
  378. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +170 -48
  379. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -17
  380. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +7 -3
  381. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +1 -1
  382. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +17 -0
  383. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -2
  384. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +184 -10
  385. package/src/resources/extensions/gsd/tests/tool-unavailable-retry.test.ts +33 -0
  386. package/src/resources/extensions/gsd/tests/transport-gate-double-complete.test.ts +139 -0
  387. package/src/resources/extensions/gsd/tests/uok-audit-unified.test.ts +8 -0
  388. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
  389. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +2 -0
  390. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +19 -0
  391. package/src/resources/extensions/gsd/tests/workflow-mcp-readiness-cache.test.ts +119 -0
  392. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +65 -2
  393. package/src/resources/extensions/gsd/tests/workflow-phase-contract-matrix.test.ts +332 -0
  394. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +20 -0
  395. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +92 -0
  396. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +1 -1
  397. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +1 -1
  398. package/src/resources/extensions/gsd/tests/worktree-safety-phase.test.ts +100 -0
  399. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +72 -0
  400. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +22 -0
  401. package/src/resources/extensions/gsd/tests/worktree.test.ts +18 -0
  402. package/src/resources/extensions/gsd/tool-contract.ts +38 -3
  403. package/src/resources/extensions/gsd/tool-surface-readiness.ts +126 -19
  404. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  405. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  406. package/src/resources/extensions/gsd/tools/complete-task.ts +90 -2
  407. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -2
  408. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -2
  409. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +2 -2
  410. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
  411. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -2
  412. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -2
  413. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -2
  414. package/src/resources/extensions/gsd/unit-context-composer.ts +1 -1
  415. package/src/resources/extensions/gsd/unit-registry.ts +34 -4
  416. package/src/resources/extensions/gsd/verification-verdict.ts +4 -2
  417. package/src/resources/extensions/gsd/workflow-event-ledger.ts +131 -0
  418. package/src/resources/extensions/gsd/workflow-event-vocabulary.ts +59 -0
  419. package/src/resources/extensions/gsd/workflow-events.ts +12 -20
  420. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -0
  421. package/src/resources/extensions/gsd/workflow-mcp-readiness-cache.ts +150 -0
  422. package/src/resources/extensions/gsd/workflow-reconcile.ts +29 -62
  423. package/src/resources/extensions/gsd/worktree-manager.ts +6 -1
  424. package/src/resources/extensions/gsd/worktree-safety.ts +41 -39
  425. package/src/resources/extensions/gsd/worktree.ts +7 -1
  426. package/src/resources/extensions/mcp-client/manager.ts +7 -1
  427. package/src/resources/skills/create-skill/SKILL.md +3 -0
  428. package/src/resources/skills/create-skill/references/skill-structure.md +1 -0
  429. package/dist/resources/skills/gsd-browser/SKILL.md +0 -41
  430. package/src/resources/skills/gsd-browser/SKILL.md +0 -41
  431. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_buildManifest.js +0 -0
  432. /package/dist/web/standalone/.next/static/{LDHRKiRBIVZmiuMjrL1Vy → 2T9IOdiiM3o3gZ4UbPi8E}/_ssgManifest.js +0 -0
@@ -0,0 +1,92 @@
1
+ // Project/App: gsd-pi
2
+ // File Purpose: Regression tests for milestone closeout settlement guidance.
3
+
4
+ import test, { afterEach } from "node:test";
5
+ import assert from "node:assert/strict";
6
+ import { execFileSync } from "node:child_process";
7
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { dirname, join } from "node:path";
10
+
11
+ import { evaluateAllCompleteSettlement } from "../milestone-settlement.ts";
12
+ import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
13
+ import {
14
+ closeDatabase,
15
+ insertAssessment,
16
+ insertMilestone,
17
+ insertSlice,
18
+ insertTask,
19
+ openDatabase,
20
+ } from "../gsd-db.ts";
21
+
22
+ let base = "";
23
+
24
+ function runGit(cwd: string, args: string[]): void {
25
+ execFileSync("git", args, { cwd, stdio: "ignore" });
26
+ }
27
+
28
+ function initRepo(root: string): void {
29
+ runGit(root, ["init", "-b", "main"]);
30
+ runGit(root, ["config", "user.email", "test@example.com"]);
31
+ runGit(root, ["config", "user.name", "Test User"]);
32
+ writeFileSync(join(root, "README.md"), "# fixture\n");
33
+ runGit(root, ["add", "."]);
34
+ runGit(root, ["commit", "-m", "chore: init"]);
35
+ }
36
+
37
+ function seedClosedMilestone(root: string, worktree: string): void {
38
+ mkdirSync(join(root, ".gsd"), { recursive: true });
39
+ openDatabase(join(root, ".gsd", "gsd.db"));
40
+ insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
41
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice One", status: "complete" });
42
+ insertTask({
43
+ id: "T01",
44
+ sliceId: "S01",
45
+ milestoneId: "M001",
46
+ title: "Task One",
47
+ status: "complete",
48
+ verificationResult: "passed",
49
+ });
50
+ insertAssessment({
51
+ path: ".gsd/milestones/M001/M001-VALIDATION.md",
52
+ milestoneId: "M001",
53
+ status: "pass",
54
+ scope: "milestone-validation",
55
+ fullContent: "verdict: pass\n",
56
+ });
57
+
58
+ mkdirSync(join(worktree, ".gsd", "milestones", "M001"), { recursive: true });
59
+ const summaryPath = resolveExpectedArtifactPath("complete-milestone", "M001", worktree);
60
+ assert.ok(summaryPath, "complete-milestone summary path should resolve");
61
+ mkdirSync(dirname(summaryPath), { recursive: true });
62
+ writeFileSync(summaryPath, "# Milestone One\n\nComplete.\n");
63
+ }
64
+
65
+ afterEach(() => {
66
+ try { closeDatabase(); } catch { /* ignore */ }
67
+ if (base) rmSync(base, { recursive: true, force: true });
68
+ base = "";
69
+ });
70
+
71
+ test("merge-pending settlement routes back to complete-milestone dispatch without manual merge guidance", () => {
72
+ base = mkdtempSync(join(tmpdir(), "gsd-milestone-settlement-"));
73
+ initRepo(base);
74
+ const worktree = join(base, ".gsd", "worktrees", "M001");
75
+ mkdirSync(dirname(worktree), { recursive: true });
76
+ runGit(base, ["worktree", "add", "-b", "milestone/M001", worktree, "HEAD"]);
77
+ seedClosedMilestone(base, worktree);
78
+
79
+ const result = evaluateAllCompleteSettlement({
80
+ milestoneId: "M001",
81
+ statePhase: "complete",
82
+ basePath: worktree,
83
+ originalBasePath: base,
84
+ milestoneMerged: false,
85
+ });
86
+
87
+ assert.equal(result.ok, false);
88
+ assert.equal(result.reason, "merge-pending");
89
+ assert.equal(result.nextAction, "Retry `/gsd dispatch complete-milestone M001`.");
90
+ assert.doesNotMatch(result.message, /merge manually/i);
91
+ assert.doesNotMatch(result.nextAction, /merge manually/i);
92
+ });
@@ -14,7 +14,7 @@ import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync
14
14
  import { join } from "node:path";
15
15
  import { tmpdir } from "node:os";
16
16
  import { AutoSession } from "../auto/session.ts";
17
- import { runPreDispatch } from "../auto/phases.ts";
17
+ import { runPreDispatch } from "../auto/pre-dispatch.ts";
18
18
 
19
19
  test("milestone transition archives completed units and rebuilds state", async () => {
20
20
  const tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-cu-reset-")));
@@ -1,7 +1,12 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { readFileSync } from "node:fs";
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
4
6
 
7
+ import { playQuestionBell } from "../../ask-user-questions.js";
8
+ import { stopAuto } from "../auto.js";
9
+ import { autoSession } from "../auto-runtime-state.js";
5
10
  import {
6
11
  buildDesktopNotificationCommand,
7
12
  shouldSendDesktopNotification,
@@ -58,18 +63,68 @@ test("playNotificationBell is silent when disabled", () => {
58
63
  assert.equal(output, "");
59
64
  });
60
65
 
61
- test("ask_user_questions plays local bell before waiting for an answer", () => {
62
- const source = readFileSync(new URL("../../ask-user-questions.ts", import.meta.url), "utf-8");
66
+ test("playQuestionBell writes a terminal bell when local bell is enabled", async () => {
67
+ let output = "";
68
+ const stream = { write: (chunk: string) => { output += chunk; } };
63
69
 
64
- assert.match(source, /playNotificationBell\("question"\)/);
65
- assert.match(source, /await playQuestionBell\(\)/);
70
+ await playQuestionBell({ enabled: true, local_bell: true }, stream);
71
+
72
+ assert.equal(output, "\u0007");
66
73
  });
67
74
 
68
- test("stopAuto plays local bell for auto-mode stop notifications", () => {
69
- const source = readFileSync(new URL("../auto.ts", import.meta.url), "utf-8");
75
+ test("playQuestionBell is silent when local bell is disabled", async () => {
76
+ let output = "";
77
+ const stream = { write: (chunk: string) => { output += chunk; } };
70
78
 
71
- assert.match(source, /import \{[^}]*playNotificationBell[^}]*\} from "\.\/notifications\.js"/);
72
- assert.match(source, /playNotificationBell\("stop", loadedPreferences\?\.notifications\)/);
79
+ await playQuestionBell({ enabled: true, local_bell: false }, stream);
80
+
81
+ assert.equal(output, "");
82
+ });
83
+
84
+ test("stopAuto plays local bell for auto-mode stop notifications", async () => {
85
+ const base = mkdtempSync(join(tmpdir(), "gsd-stop-bell-"));
86
+ const previousCwd = process.cwd();
87
+ const previousStderrWrite = process.stderr.write;
88
+ const previousStderrIsTTY = process.stderr.isTTY;
89
+ let bellOutput = "";
90
+
91
+ autoSession.reset();
92
+ autoSession.active = true;
93
+ autoSession.basePath = base;
94
+
95
+ mkdirSync(join(base, ".gsd"), { recursive: true });
96
+ writeFileSync(
97
+ join(base, ".gsd", "PREFERENCES.md"),
98
+ "---\nnotifications:\n enabled: true\n local_bell: true\n---\n",
99
+ "utf-8",
100
+ );
101
+
102
+ process.stderr.isTTY = true;
103
+ process.stderr.write = ((chunk: string | Uint8Array) => {
104
+ if (typeof chunk === "string") {
105
+ bellOutput += chunk;
106
+ }
107
+ return true;
108
+ }) as typeof process.stderr.write;
109
+
110
+ try {
111
+ await stopAuto(
112
+ { hasUI: false, ui: { notify: () => {}, setStatus: () => {}, setWidget: () => {}, setHeader: () => {} } } as any,
113
+ undefined,
114
+ "test stop",
115
+ );
116
+
117
+ assert.ok(
118
+ bellOutput.includes("\u0007"),
119
+ "stopAuto must write a terminal bell to stderr when the local bell preference is enabled",
120
+ );
121
+ } finally {
122
+ process.stderr.write = previousStderrWrite;
123
+ process.stderr.isTTY = previousStderrIsTTY;
124
+ autoSession.reset();
125
+ process.chdir(previousCwd);
126
+ rmSync(base, { recursive: true, force: true });
127
+ }
73
128
  });
74
129
 
75
130
  test("buildDesktopNotificationCommand falls back to osascript on macOS when terminal-notifier is absent", () => {
@@ -29,8 +29,8 @@ import {
29
29
 
30
30
  const SKILL_NAME = "testskill";
31
31
  const COMPLETE_SLICE_SKILL_NAME = "complete-slice-policies";
32
- const SKILL_ACTIVATION_SUBSTRING = `Call Skill({ skill: '${SKILL_NAME}' })`;
33
- const COMPLETE_SLICE_SKILL_ACTIVATION_SUBSTRING = `Call Skill({ skill: '${COMPLETE_SLICE_SKILL_NAME}' })`;
32
+ const SKILL_ACTIVATION_SUBSTRING = `Read the installed '${SKILL_NAME}' skill file from <available_skills>`;
33
+ const COMPLETE_SLICE_SKILL_ACTIVATION_SUBSTRING = `Read the installed '${COMPLETE_SLICE_SKILL_NAME}' skill file from <available_skills>`;
34
34
 
35
35
  const tmpDirs: string[] = [];
36
36
  let savedCwd: string | undefined;
@@ -28,6 +28,11 @@ const BANNED_DECISION_PATHS = new Set([
28
28
  "gsd/auto-post-unit.ts",
29
29
  "gsd/milestone-closeout.ts",
30
30
  "gsd/auto/phases.ts",
31
+ "gsd/auto/pre-dispatch.ts",
32
+ "gsd/auto/dispatch.ts",
33
+ "gsd/auto/unit-phase.ts",
34
+ "gsd/auto/finalize.ts",
35
+ "gsd/auto/closeout.ts",
31
36
  "gsd/auto/orchestrator.ts",
32
37
  "gsd/auto/loop.ts",
33
38
  "gsd/tools/complete-slice.ts",
@@ -10,7 +10,7 @@
10
10
  */
11
11
 
12
12
  import { createTestContext } from "./test-helpers.ts";
13
- import { runPreDispatch } from "../auto/phases.ts";
13
+ import { runPreDispatch } from "../auto/pre-dispatch.ts";
14
14
 
15
15
  const { assertTrue, report } = createTestContext();
16
16
 
@@ -0,0 +1,242 @@
1
+ /**
2
+ * phases-terminal-complete-idempotent.test.ts — Regression test for the
3
+ * milestone-completion double-closeout guard in `runPreDispatch`.
4
+ *
5
+ * When `runPreDispatch` observes that the active milestone (or the last one
6
+ * the session was working on) has already been closed by another session,
7
+ * the loop must exit cleanly with `{ action: "break", reason:
8
+ * "milestone-complete" }` and must NOT replay merge, desktop / cmux
9
+ * notifications, unit closeout, or `stopAuto`.
10
+ *
11
+ * There are two `deriveState` shapes the guard must cover, and both are
12
+ * exercised below because the canonical one is easy to miss:
13
+ *
14
+ * 1. `state.phase === "complete"` with `activeMilestone` set, so the
15
+ * loop computes a non-null `mid` from `state.activeMilestone.id`.
16
+ *
17
+ * 2. `state.phase === "complete"` with `activeMilestone: null` — the
18
+ * canonical "all milestones complete" return from `deriveState`
19
+ * (state.ts:613, state.ts:1293). `mid` is undefined here, so the
20
+ * guard must consult `s.currentMilestoneId` (the milestone this
21
+ * session was working on) instead. Without coverage for this case,
22
+ * a guard that only inspects `mid` is unreachable in production and
23
+ * the loop replays `_runMilestoneMergeOnceWithStashRestore`,
24
+ * `sendDesktopNotification("All milestones complete!")`,
25
+ * `logCmuxEvent`, and `stopAuto` — exactly the duplicate side
26
+ * effects this fix exists to prevent.
27
+ *
28
+ * Both fire-paths of the guard (`completionStopInProgress` and a
29
+ * DB-already-closed milestone) are exercised against each shape.
30
+ */
31
+
32
+ import { createTestContext } from "./test-helpers.ts";
33
+ import { runPreDispatch } from "../auto/pre-dispatch.ts";
34
+ import {
35
+ openDatabase,
36
+ closeDatabase,
37
+ insertMilestone,
38
+ isDbAvailable,
39
+ } from "../gsd-db.ts";
40
+
41
+ const { assertTrue, report } = createTestContext();
42
+
43
+ type SideEffect = string;
44
+
45
+ interface ScenarioOverrides {
46
+ completionStopInProgress: boolean;
47
+ sideEffects: SideEffect[];
48
+ notifications: Array<{ message: string; level?: string }>;
49
+ // When `null`, deriveState returns activeMilestone: null (canonical
50
+ // all-complete path). Otherwise, returns an activeMilestone with this id.
51
+ activeMilestone: { id: string; title: string } | null;
52
+ }
53
+
54
+ function makeIterationContext(overrides: ScenarioOverrides): any {
55
+ const basePath = "/tmp/gsd-test-terminal-complete";
56
+ const recordSideEffect = (label: string) => {
57
+ overrides.sideEffects.push(label);
58
+ };
59
+ return {
60
+ ctx: {
61
+ ui: {
62
+ notify(message: string, level?: string) {
63
+ overrides.notifications.push({ message, level });
64
+ },
65
+ },
66
+ },
67
+ pi: {},
68
+ s: {
69
+ basePath,
70
+ originalBasePath: basePath,
71
+ canonicalProjectRoot: basePath,
72
+ resourceVersionOnStart: "test",
73
+ // Critical for the `!mid` canonical path: even when deriveState returns
74
+ // activeMilestone: null, the session still remembers the milestone it
75
+ // was working on, and we use that to look up DB status.
76
+ currentMilestoneId: "M001",
77
+ currentUnit: null,
78
+ milestoneMergedInPhases: false,
79
+ completionStopInProgress: overrides.completionStopInProgress,
80
+ },
81
+ prefs: undefined,
82
+ iteration: 1,
83
+ flowId: "test-flow",
84
+ nextSeq: () => 1,
85
+ deps: {
86
+ checkResourcesStale() {
87
+ return null;
88
+ },
89
+ invalidateAllCaches() {},
90
+ async preDispatchHealthGate() {
91
+ return { proceed: true, fixesApplied: [] };
92
+ },
93
+ async deriveState() {
94
+ return {
95
+ phase: "complete",
96
+ activeMilestone: overrides.activeMilestone,
97
+ activeSlice: null,
98
+ activeTask: null,
99
+ // Registry says M001 is complete and no other milestones exist, so
100
+ // `incomplete.length === 0 && state.registry.length > 0` evaluates
101
+ // true in the `!mid` branch.
102
+ registry: [{ id: "M001", status: "complete" }],
103
+ nextAction: "complete",
104
+ };
105
+ },
106
+ syncCmuxSidebar() {},
107
+ setActiveMilestoneId() {},
108
+ reconcileMergeState() {
109
+ return "clean";
110
+ },
111
+ // Anything below this point MUST NOT be reached when the guard fires.
112
+ preflightCleanRoot() {
113
+ recordSideEffect("preflight");
114
+ return { ok: true, stashPushed: false, stashMarker: null };
115
+ },
116
+ postflightPopStash() {
117
+ recordSideEffect("postflight");
118
+ return { ok: true, needsManualRecovery: false };
119
+ },
120
+ lifecycle: {
121
+ exitMilestone() {
122
+ recordSideEffect("merge");
123
+ return { ok: true };
124
+ },
125
+ },
126
+ sendDesktopNotification() {
127
+ recordSideEffect("desktop-notify");
128
+ },
129
+ logCmuxEvent() {
130
+ recordSideEffect("cmux-event");
131
+ },
132
+ async closeoutUnit() {
133
+ recordSideEffect("closeout-unit");
134
+ },
135
+ buildSnapshotOpts() {
136
+ return {};
137
+ },
138
+ async stopAuto(_ctx: unknown, _pi: unknown, reason?: string) {
139
+ recordSideEffect(`stop:${reason ?? ""}`);
140
+ },
141
+ async pauseAuto() {
142
+ recordSideEffect("pause");
143
+ },
144
+ emitJournalEvent() {
145
+ recordSideEffect("journal-event");
146
+ },
147
+ },
148
+ };
149
+ }
150
+
151
+ async function runScenario(opts: {
152
+ label: string;
153
+ completionStopInProgress: boolean;
154
+ activeMilestone: { id: string; title: string } | null;
155
+ // When true, the test opens an in-memory DB and inserts M001 with status
156
+ // "complete" so the DB-closed branch of the guard can fire.
157
+ dbAlreadyClosed: boolean;
158
+ }): Promise<void> {
159
+ if (isDbAvailable()) {
160
+ closeDatabase();
161
+ }
162
+ if (opts.dbAlreadyClosed) {
163
+ openDatabase(":memory:");
164
+ insertMilestone({ id: "M001", title: "Milestone one", status: "complete" });
165
+ }
166
+
167
+ try {
168
+ const sideEffects: SideEffect[] = [];
169
+ const notifications: Array<{ message: string; level?: string }> = [];
170
+ const ic = makeIterationContext({
171
+ completionStopInProgress: opts.completionStopInProgress,
172
+ sideEffects,
173
+ notifications,
174
+ activeMilestone: opts.activeMilestone,
175
+ });
176
+
177
+ const result = await runPreDispatch(ic, {
178
+ recentUnits: [],
179
+ stuckRecoveryAttempts: 0,
180
+ consecutiveFinalizeTimeouts: 0,
181
+ });
182
+
183
+ assertTrue(
184
+ result.action === "break",
185
+ `${opts.label}: returns break instead of next`,
186
+ );
187
+ if (result.action === "break") {
188
+ assertTrue(
189
+ result.reason === "milestone-complete",
190
+ `${opts.label}: reason is milestone-complete (got "${result.reason}")`,
191
+ );
192
+ }
193
+ assertTrue(
194
+ sideEffects.length === 0,
195
+ `${opts.label}: no closeout side effects replayed (saw [${sideEffects.join(", ")}])`,
196
+ );
197
+ assertTrue(
198
+ notifications.length === 0,
199
+ `${opts.label}: no user notifications emitted (saw ${notifications.length})`,
200
+ );
201
+ } finally {
202
+ if (isDbAvailable()) {
203
+ closeDatabase();
204
+ }
205
+ }
206
+ }
207
+
208
+ console.log("\n=== Terminal complete is idempotent across both observer paths ===");
209
+
210
+ // ── state.phase === "complete" branch (activeMilestone non-null) ────────────
211
+ await runScenario({
212
+ label: "phase=complete + mid set + completionStopInProgress",
213
+ completionStopInProgress: true,
214
+ activeMilestone: { id: "M001", title: "Milestone one" },
215
+ dbAlreadyClosed: false,
216
+ });
217
+
218
+ await runScenario({
219
+ label: "phase=complete + mid set + DB closed",
220
+ completionStopInProgress: false,
221
+ activeMilestone: { id: "M001", title: "Milestone one" },
222
+ dbAlreadyClosed: true,
223
+ });
224
+
225
+ // ── Canonical !mid "all milestones complete" sub-branch ────────────────────
226
+ // deriveState returns phase: "complete" with activeMilestone: null. The
227
+ // session's s.currentMilestoneId (M001) is what the guard consults.
228
+ await runScenario({
229
+ label: "phase=complete + activeMilestone=null + completionStopInProgress",
230
+ completionStopInProgress: true,
231
+ activeMilestone: null,
232
+ dbAlreadyClosed: false,
233
+ });
234
+
235
+ await runScenario({
236
+ label: "phase=complete + activeMilestone=null + DB closed",
237
+ completionStopInProgress: false,
238
+ activeMilestone: null,
239
+ dbAlreadyClosed: true,
240
+ });
241
+
242
+ report();
@@ -18,12 +18,12 @@ function readSrc(file: string): string {
18
18
  return readFileSync(join(gsdDir, file), "utf-8");
19
19
  }
20
20
 
21
- test("#4620: auto/phases plan gate failed message includes doctor heal hint", () => {
22
- const src = readSrc("auto/phases.ts");
21
+ test("#4620: auto/pre-dispatch plan gate failed message includes doctor heal hint", () => {
22
+ const src = readSrc("auto/pre-dispatch.ts");
23
23
  assert.match(
24
24
  src,
25
25
  /Plan gate failed-closed:[\s\S]*\/gsd doctor heal/,
26
- "auto/phases.ts should include /gsd doctor heal in plan gate failed notification",
26
+ "auto/pre-dispatch.ts should include /gsd doctor heal in plan gate failed notification",
27
27
  );
28
28
  });
29
29
 
@@ -145,7 +145,7 @@ function createBasicTask(): void {
145
145
  });
146
146
  }
147
147
 
148
- function createTaskWithoutVerify(): void {
148
+ function createTaskWithoutVerify(status = "pending"): void {
149
149
  insertMilestone({ id: "M001" });
150
150
  insertSlice({
151
151
  id: "S01",
@@ -159,7 +159,7 @@ function createTaskWithoutVerify(): void {
159
159
  sliceId: "S01",
160
160
  milestoneId: "M001",
161
161
  title: "Task without host verification",
162
- status: "pending",
162
+ status,
163
163
  planning: {
164
164
  description: "Task intentionally missing runnable verification",
165
165
  estimate: "1h",
@@ -558,6 +558,32 @@ describe("Post-execution blocking failure retry bypass", () => {
558
558
  assert.equal(pauseAutoMock.mock.callCount(), 1);
559
559
  assert.equal(s.pendingVerificationRetry, null);
560
560
 
561
+ const notifyMessages = ctx.ui.notify.mock.calls.map((c: { arguments: unknown[] }) =>
562
+ String(c.arguments[0])
563
+ );
564
+ assert.ok(
565
+ notifyMessages.some(
566
+ (m: string) =>
567
+ m.includes(".gsd/PREFERENCES.md") &&
568
+ m.includes("task-plan Verify command") &&
569
+ m.includes("/gsd next")
570
+ ),
571
+ "no-host-checks notification should guide the user to add verification and resume",
572
+ );
573
+
574
+ const pauseCalls = pauseAutoMock.mock.calls as Array<{ arguments: unknown[] }>;
575
+ const pauseCallArgs = pauseCalls[0]?.arguments[2] as
576
+ | { message?: string }
577
+ | undefined;
578
+ assert.ok(
579
+ pauseCallArgs?.message?.includes(".gsd/PREFERENCES.md"),
580
+ "pause reason should include the project verification configuration path",
581
+ );
582
+ assert.ok(
583
+ pauseCallArgs?.message?.includes("/gsd next"),
584
+ "pause reason should tell the user how to resume",
585
+ );
586
+
561
587
  const evidencePath = join(tempDir, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-VERIFY.json");
562
588
  const evidence = JSON.parse(readFileSync(evidencePath, "utf-8"));
563
589
  assert.equal(evidence.passed, false);
@@ -566,6 +592,41 @@ describe("Post-execution blocking failure retry bypass", () => {
566
592
  assert.ok(!("maxRetries" in evidence), "no-host-checks evidence must not include maxRetries");
567
593
  });
568
594
 
595
+ test("completed browser-facing execute-task with no host-owned verification continues toward browser UAT", async () => {
596
+ createTaskWithoutVerify("complete");
597
+ writeFileSync(join(tempDir, "index.html"), "<!doctype html><button>Import</button>", "utf-8");
598
+
599
+ const ctx = makeMockCtx();
600
+ const pi = makeMockPi();
601
+ const pauseAutoMock = mock.fn(async () => {});
602
+ const s = makeMockSession(tempDir, { type: "execute-task", id: "M001/S01/T01" });
603
+
604
+ const result = await runPostUnitVerification({ s, ctx, pi }, pauseAutoMock);
605
+
606
+ assert.equal(result, "continue");
607
+ assert.equal(pauseAutoMock.mock.callCount(), 0);
608
+ assert.equal(s.pendingVerificationRetry, null);
609
+
610
+ const notifyMessages = ctx.ui.notify.mock.calls.map((c: { arguments: unknown[] }) =>
611
+ String(c.arguments[0])
612
+ );
613
+ assert.ok(
614
+ notifyMessages.some(
615
+ (m: string) =>
616
+ m.includes("browser-facing task") &&
617
+ m.includes("slice UAT") &&
618
+ m.includes("browser tools")
619
+ ),
620
+ "completed web tasks without task-level commands should explain browser UAT handoff",
621
+ );
622
+
623
+ const evidencePath = join(tempDir, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-VERIFY.json");
624
+ const evidence = JSON.parse(readFileSync(evidencePath, "utf-8"));
625
+ assert.equal(evidence.passed, false);
626
+ assert.equal(evidence.discoverySource, "none");
627
+ assert.ok(!("retryAttempt" in evidence), "browser-UAT handoff evidence must not request a task retry");
628
+ });
629
+
569
630
  test("auto-discovered package.json verification failure retries instead of continuing", async () => {
570
631
  createTaskWithoutVerify();
571
632
  writeFileSync(
@@ -55,14 +55,20 @@ const PHASE_PROMPT_TOOL_CALLS: Record<string, readonly string[]> = {
55
55
  "plan-slice": ["gsd_reassess_roadmap", "gsd_plan_slice", "gsd_decision_save"],
56
56
  "refine-slice": ["gsd_plan_slice", "gsd_decision_save"],
57
57
  "replan-slice": ["gsd_replan_slice"],
58
- "execute-task": ["gsd_task_complete"],
58
+ "execute-task": [
59
+ "gsd_task_complete",
60
+ "gsd_exec",
61
+ "gsd_exec_search",
62
+ "gsd_resume",
63
+ "gsd_capture_thought",
64
+ ],
59
65
  "reactive-execute": ["gsd_summary_save"],
60
66
  "complete-slice": [
61
67
  "gsd_exec",
62
68
  "gsd_task_reopen",
63
69
  "gsd_replan_slice",
64
70
  "gsd_requirement_update",
65
- "capture_thought",
71
+ "gsd_capture_thought",
66
72
  "gsd_slice_complete",
67
73
  "gsd_summary_save",
68
74
  ],
@@ -240,6 +246,8 @@ test("workflow-start prompt defaults to autonomy instead of per-phase confirmati
240
246
  const prompt = readPrompt("workflow-start");
241
247
  assert.match(prompt, /Keep moving by default/i);
242
248
  assert.match(prompt, /Decision gates, not ceremony/i);
249
+ assert.match(prompt, /Persist workflow state/i);
250
+ assert.match(prompt, /completedAt/i);
243
251
  assert.doesNotMatch(prompt, /confirm with the user before proceeding/i);
244
252
  assert.doesNotMatch(prompt, /Gate between phases/i);
245
253
  });
@@ -17,10 +17,8 @@ import {
17
17
  shouldDeferTransientErrorToCoreRetry,
18
18
  suppressTerminalDeletedWorktreeMessageEnd,
19
19
  } from "../bootstrap/agent-end-recovery.ts";
20
- import {
21
- _buildCancelledUnitStopReason,
22
- _classifyZeroToolProviderMessageForTest,
23
- } from "../auto/phases.ts";
20
+ import { _buildCancelledUnitStopReason } from "../auto/phase-helpers.ts";
21
+ import { _classifyZeroToolProviderMessageForTest } from "../auto/unit-phase.ts";
24
22
  import { autoSession } from "../auto-runtime-state.ts";
25
23
  import { getNextFallbackModel } from "../preferences.ts";
26
24
  // Zero-import module — imported by path rather than through the package