gsd-pi 2.80.0-dev.3065f7bfa → 2.80.0-dev.488219eb6

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 (376) hide show
  1. package/dist/claude-cli-check.d.ts +30 -0
  2. package/dist/claude-cli-check.js +18 -7
  3. package/dist/cli.js +0 -19
  4. package/dist/headless-query.d.ts +10 -0
  5. package/dist/headless-query.js +6 -4
  6. package/dist/loader-entrypoint.d.ts +8 -0
  7. package/dist/loader-entrypoint.js +27 -0
  8. package/dist/loader.js +2 -11
  9. package/dist/mcp-server.d.ts +1 -0
  10. package/dist/mcp-server.js +6 -3
  11. package/dist/resources/.managed-resources-content-hash +1 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +18 -7
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +29 -0
  14. package/dist/resources/extensions/github-sync/sync.js +4 -1
  15. package/dist/resources/extensions/gsd/auto/loop.js +71 -8
  16. package/dist/resources/extensions/gsd/auto/phases.js +32 -8
  17. package/dist/resources/extensions/gsd/auto/resolve.js +12 -0
  18. package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
  19. package/dist/resources/extensions/gsd/auto/workflow-dispatch-claim.js +33 -1
  20. package/dist/resources/extensions/gsd/auto/workflow-worker-heartbeat.js +9 -1
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +81 -76
  22. package/dist/resources/extensions/gsd/auto-start.js +41 -6
  23. package/dist/resources/extensions/gsd/auto-worktree.js +98 -80
  24. package/dist/resources/extensions/gsd/auto.js +84 -29
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +62 -13
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +20 -26
  27. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +29 -12
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +6 -9
  29. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +4 -7
  30. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +6 -10
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +5 -9
  32. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +1 -1
  33. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +60 -26
  34. package/dist/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.js +4 -0
  35. package/dist/resources/extensions/gsd/commands/handlers/ops.js +2 -2
  36. package/dist/resources/extensions/gsd/commands-config.js +1 -1
  37. package/dist/resources/extensions/gsd/commands-eval-review.js +2 -2
  38. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  39. package/dist/resources/extensions/gsd/db/unit-dispatches.js +53 -0
  40. package/dist/resources/extensions/gsd/git-service.js +37 -1
  41. package/dist/resources/extensions/gsd/guided-flow.js +14 -4
  42. package/dist/resources/extensions/gsd/init-wizard.js +4 -1
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
  44. package/dist/resources/extensions/gsd/slice-cadence.js +45 -2
  45. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +15 -9
  46. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -7
  47. package/dist/resources/extensions/gsd/tools/complete-task.js +1 -1
  48. package/dist/resources/extensions/gsd/worktree-resolver.js +22 -1
  49. package/dist/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  50. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  51. package/dist/web/standalone/.next/BUILD_ID +1 -1
  52. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  53. package/dist/web/standalone/.next/build-manifest.json +3 -3
  54. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  55. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  56. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.html +1 -1
  75. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  76. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  77. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  78. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  83. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  84. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/middleware.js +3 -3
  86. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  87. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  88. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  89. package/dist/web/standalone/.next/static/chunks/8359.e059d86b255fce1c.js +10 -0
  90. package/dist/web/standalone/.next/static/chunks/app/{page-fab3ebb85b006001.js → page-752f1e2ebdaa3e45.js} +1 -1
  91. package/dist/web/standalone/.next/static/chunks/{webpack-0481f1221120a7c6.js → webpack-de742b64187e13fe.js} +1 -1
  92. package/dist/welcome-screen.d.ts +2 -0
  93. package/dist/welcome-screen.js +9 -7
  94. package/package.json +1 -1
  95. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +35 -13
  96. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -1
  97. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js +21 -11
  98. package/packages/pi-ai/dist/providers/anthropic-bearer-auth.test.js.map +1 -1
  99. package/packages/pi-ai/dist/providers/anthropic.d.ts +7 -0
  100. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  101. package/packages/pi-ai/dist/providers/anthropic.js +9 -7
  102. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  103. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js +23 -14
  104. package/packages/pi-ai/dist/providers/minimax-tool-name.test.js.map +1 -1
  105. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +48 -21
  106. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -1
  107. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js +22 -21
  108. package/packages/pi-ai/dist/utils/oauth/google-antigravity.test.js.map +1 -1
  109. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js +22 -21
  110. package/packages/pi-ai/dist/utils/oauth/google-gemini-cli.test.js.map +1 -1
  111. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +39 -25
  112. package/packages/pi-ai/src/providers/anthropic-bearer-auth.test.ts +26 -22
  113. package/packages/pi-ai/src/providers/anthropic.ts +22 -9
  114. package/packages/pi-ai/src/providers/minimax-tool-name.test.ts +34 -21
  115. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +56 -22
  116. package/packages/pi-ai/src/utils/oauth/google-antigravity.test.ts +24 -28
  117. package/packages/pi-ai/src/utils/oauth/google-gemini-cli.test.ts +24 -28
  118. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  119. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +3 -1
  120. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +2 -2
  122. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  124. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js +22 -56
  125. package/packages/pi-coding-agent/dist/core/tools/bash-spawn-windows.test.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/core/tools/bash.js +3 -1
  129. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts +2 -0
  131. package/packages/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  132. package/packages/pi-coding-agent/dist/core/tools/edit.js +12 -1
  133. package/packages/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/core/tools/find.d.ts +2 -0
  135. package/packages/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  136. package/packages/pi-coding-agent/dist/core/tools/find.js +14 -6
  137. package/packages/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  138. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts +2 -0
  139. package/packages/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  140. package/packages/pi-coding-agent/dist/core/tools/grep.js +12 -3
  141. package/packages/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  142. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts +2 -0
  143. package/packages/pi-coding-agent/dist/core/tools/hashline-read.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js +3 -1
  145. package/packages/pi-coding-agent/dist/core/tools/hashline-read.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/core/tools/index.d.ts +2 -1
  147. package/packages/pi-coding-agent/dist/core/tools/index.d.ts.map +1 -1
  148. package/packages/pi-coding-agent/dist/core/tools/index.js +1 -0
  149. package/packages/pi-coding-agent/dist/core/tools/index.js.map +1 -1
  150. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts +2 -0
  151. package/packages/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  152. package/packages/pi-coding-agent/dist/core/tools/ls.js +10 -3
  153. package/packages/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  154. package/packages/pi-coding-agent/dist/core/tools/read.d.ts +2 -0
  155. package/packages/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  156. package/packages/pi-coding-agent/dist/core/tools/read.js +3 -1
  157. package/packages/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  158. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js +7 -62
  159. package/packages/pi-coding-agent/dist/core/tools/spawn-shell-windows.test.js.map +1 -1
  160. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts +2 -0
  161. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.d.ts.map +1 -0
  162. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js +115 -0
  163. package/packages/pi-coding-agent/dist/core/tools/tool-target-metadata.test.js.map +1 -0
  164. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts +19 -0
  165. package/packages/pi-coding-agent/dist/core/tools/tool-target.d.ts.map +1 -0
  166. package/packages/pi-coding-agent/dist/core/tools/tool-target.js +20 -0
  167. package/packages/pi-coding-agent/dist/core/tools/tool-target.js.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/tools/write.d.ts +4 -0
  169. package/packages/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/tools/write.js +9 -1
  171. package/packages/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  172. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +89 -2
  173. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +25 -1
  175. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
  176. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  177. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +153 -8
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +18 -1
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +23 -5
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +19 -0
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +23 -5
  190. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -1
  191. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +3 -1
  192. package/packages/pi-coding-agent/src/core/extensions/types.ts +2 -1
  193. package/packages/pi-coding-agent/src/core/tools/bash-spawn-windows.test.ts +22 -66
  194. package/packages/pi-coding-agent/src/core/tools/bash.ts +4 -1
  195. package/packages/pi-coding-agent/src/core/tools/edit.ts +13 -1
  196. package/packages/pi-coding-agent/src/core/tools/find.ts +15 -6
  197. package/packages/pi-coding-agent/src/core/tools/grep.ts +13 -3
  198. package/packages/pi-coding-agent/src/core/tools/hashline-read.ts +4 -1
  199. package/packages/pi-coding-agent/src/core/tools/index.ts +8 -0
  200. package/packages/pi-coding-agent/src/core/tools/ls.ts +11 -3
  201. package/packages/pi-coding-agent/src/core/tools/read.ts +4 -1
  202. package/packages/pi-coding-agent/src/core/tools/spawn-shell-windows.test.ts +11 -72
  203. package/packages/pi-coding-agent/src/core/tools/tool-target-metadata.test.ts +127 -0
  204. package/packages/pi-coding-agent/src/core/tools/tool-target.ts +48 -0
  205. package/packages/pi-coding-agent/src/core/tools/write.ts +14 -2
  206. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +119 -2
  207. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +31 -1
  208. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +177 -8
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +24 -5
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +17 -1
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +21 -0
  212. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +25 -8
  213. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  214. package/src/resources/extensions/claude-code-cli/readiness.ts +25 -7
  215. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +30 -0
  216. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +26 -0
  217. package/src/resources/extensions/github-sync/sync.ts +8 -1
  218. package/src/resources/extensions/github-sync/tests/sync-source.test.ts +6 -18
  219. package/src/resources/extensions/gsd/auto/loop.ts +84 -8
  220. package/src/resources/extensions/gsd/auto/phases.ts +43 -8
  221. package/src/resources/extensions/gsd/auto/resolve.ts +19 -0
  222. package/src/resources/extensions/gsd/auto/run-unit.ts +3 -0
  223. package/src/resources/extensions/gsd/auto/workflow-dispatch-claim.ts +63 -1
  224. package/src/resources/extensions/gsd/auto/workflow-worker-heartbeat.ts +14 -1
  225. package/src/resources/extensions/gsd/auto-post-unit.ts +93 -78
  226. package/src/resources/extensions/gsd/auto-start.ts +56 -6
  227. package/src/resources/extensions/gsd/auto-worktree.ts +118 -72
  228. package/src/resources/extensions/gsd/auto.ts +121 -29
  229. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +76 -12
  230. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +20 -26
  231. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +27 -12
  232. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +7 -9
  233. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +4 -7
  234. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +6 -10
  235. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -9
  236. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +1 -1
  237. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +64 -26
  238. package/src/resources/extensions/gsd/bootstrap/sanitize-complete-milestone.ts +4 -0
  239. package/src/resources/extensions/gsd/commands/handlers/ops.ts +4 -2
  240. package/src/resources/extensions/gsd/commands-config.ts +1 -1
  241. package/src/resources/extensions/gsd/commands-eval-review.ts +2 -2
  242. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  243. package/src/resources/extensions/gsd/db/unit-dispatches.ts +66 -0
  244. package/src/resources/extensions/gsd/git-service.ts +41 -2
  245. package/src/resources/extensions/gsd/guided-flow.ts +19 -4
  246. package/src/resources/extensions/gsd/init-wizard.ts +5 -1
  247. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -4
  248. package/src/resources/extensions/gsd/slice-cadence.ts +49 -2
  249. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +23 -9
  250. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +59 -89
  251. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +47 -172
  252. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +0 -35
  253. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +35 -9
  254. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +15 -0
  255. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +80 -59
  256. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +3 -47
  257. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +76 -18
  258. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +106 -0
  259. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +54 -95
  260. package/src/resources/extensions/gsd/tests/auto-project-root-env.test.ts +67 -26
  261. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +32 -30
  262. package/src/resources/extensions/gsd/tests/auto-start-bootstrap-await-3420.test.ts +32 -128
  263. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +20 -54
  264. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +20 -30
  265. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +17 -29
  266. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +138 -0
  267. package/src/resources/extensions/gsd/tests/auto-start-time-persistence.test.ts +21 -39
  268. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +15 -24
  269. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +44 -29
  270. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +39 -51
  271. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +1 -217
  272. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +15 -32
  273. package/src/resources/extensions/gsd/tests/browser-teardown.test.ts +0 -41
  274. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +34 -27
  275. package/src/resources/extensions/gsd/tests/cmux.test.ts +51 -53
  276. package/src/resources/extensions/gsd/tests/cold-resume-db-reopen.test.ts +39 -61
  277. package/src/resources/extensions/gsd/tests/commands-config.test.ts +26 -19
  278. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +140 -0
  279. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +29 -33
  280. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +45 -108
  281. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +35 -31
  282. package/src/resources/extensions/gsd/tests/cwd-fallback-hardening.test.ts +138 -0
  283. package/src/resources/extensions/gsd/tests/dashboard-custom-engine.test.ts +4 -68
  284. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +4 -9
  285. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +14 -65
  286. package/src/resources/extensions/gsd/tests/discuss-tool-scoping.test.ts +44 -37
  287. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +58 -40
  288. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +25 -15
  289. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +35 -17
  290. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +16 -21
  291. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +15 -82
  292. package/src/resources/extensions/gsd/tests/flat-rate-routing-guard.test.ts +2 -20
  293. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +18 -26
  294. package/src/resources/extensions/gsd/tests/init-skip-git.test.ts +9 -12
  295. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +40 -0
  296. package/src/resources/extensions/gsd/tests/integration/commands-eval-review.integration.test.ts +4 -2
  297. package/src/resources/extensions/gsd/tests/integration/git-locale.test.ts +31 -20
  298. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +12 -0
  299. package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +0 -47
  300. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +60 -202
  301. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +13 -59
  302. package/src/resources/extensions/gsd/tests/lazy-pi-tui-import.test.ts +44 -6
  303. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +21 -35
  304. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +79 -98
  305. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +70 -278
  306. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +37 -30
  307. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +32 -28
  308. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +24 -37
  309. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +9 -24
  310. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +113 -0
  311. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +36 -22
  312. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +36 -30
  313. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +74 -4
  314. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +20 -22
  315. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +130 -32
  316. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +18 -36
  317. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +35 -73
  318. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +76 -138
  319. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +70 -125
  320. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +59 -161
  321. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +33 -29
  322. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +22 -196
  323. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +23 -93
  324. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +50 -79
  325. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +27 -13
  326. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +151 -251
  327. package/src/resources/extensions/gsd/tests/resource-loader-import-path.test.ts +41 -29
  328. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +58 -90
  329. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +32 -164
  330. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +57 -41
  331. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +14 -9
  332. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +77 -0
  333. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +61 -1
  334. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +44 -42
  335. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +56 -24
  336. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +51 -11
  337. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +23 -0
  338. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +66 -50
  339. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +68 -107
  340. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +115 -42
  341. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +21 -97
  342. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +25 -116
  343. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +21 -57
  344. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +29 -76
  345. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +33 -24
  346. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +39 -30
  347. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +33 -47
  348. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +48 -46
  349. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +14 -102
  350. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +78 -232
  351. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +32 -35
  352. package/src/resources/extensions/gsd/tests/token-profile.test.ts +84 -309
  353. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +134 -341
  354. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +8 -25
  355. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +50 -1
  356. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +6 -101
  357. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +43 -36
  358. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -444
  359. package/src/resources/extensions/gsd/tests/workflow-dispatch-claim.test.ts +142 -0
  360. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +44 -189
  361. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -57
  362. package/src/resources/extensions/gsd/tests/workflow-worker-heartbeat.test.ts +32 -1
  363. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +21 -44
  364. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +27 -26
  365. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +14 -13
  366. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -18
  367. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +36 -0
  368. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -13
  369. package/src/resources/extensions/gsd/tools/complete-milestone.ts +14 -15
  370. package/src/resources/extensions/gsd/tools/complete-task.ts +1 -1
  371. package/src/resources/extensions/gsd/worktree-resolver.ts +34 -1
  372. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +86 -40
  373. package/src/resources/skills/web-quality-audit/scripts/analyze.sh +0 -0
  374. package/dist/web/standalone/.next/static/chunks/8336.631939fb583761fa.js +0 -10
  375. /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_buildManifest.js +0 -0
  376. /package/dist/web/standalone/.next/static/{O2WvnXROue6l7nAp_4dhC → G68KoLv5claox_Dyd4pZn}/_ssgManifest.js +0 -0
@@ -26,6 +26,9 @@ import {
26
26
  formatRuntimeHealthSignal,
27
27
  shouldRenderRoadmapProgress,
28
28
  } from "../auto-dashboard.ts";
29
+ import { getAutoDashboardData } from "../auto.ts";
30
+ import { autoSession } from "../auto-runtime-state.ts";
31
+ import { formatRtkSavingsLabel } from "../../shared/rtk-session-stats.ts";
29
32
  import {
30
33
  openDatabase,
31
34
  closeDatabase,
@@ -34,9 +37,6 @@ import {
34
37
  insertTask,
35
38
  } from "../gsd-db.ts";
36
39
 
37
- const autoSource = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "auto.ts"), "utf-8");
38
- const dashboardSource = readFileSync(join(process.cwd(), "src", "resources", "extensions", "gsd", "auto-dashboard.ts"), "utf-8");
39
-
40
40
  function makeTempDir(prefix: string): string {
41
41
  return join(
42
42
  tmpdir(),
@@ -253,14 +253,40 @@ test("formatAutoElapsed returns empty string for negative autoStartTime", () =>
253
253
  });
254
254
 
255
255
  test("getAutoDashboardData returns RTK savings in the dashboard payload", () => {
256
- assert.match(autoSource, /const rtkSavings = sessionId && s\.basePath/);
257
- assert.match(autoSource, /rtkSavings,/);
256
+ autoSession.reset();
257
+ autoSession.active = true;
258
+ autoSession.basePath = makeTempDir("rtk-dashboard");
259
+ autoSession.cmdCtx = {
260
+ sessionManager: { getSessionId: () => "session-1" },
261
+ } as any;
262
+ try {
263
+ const data = getAutoDashboardData();
264
+ assert.equal(Object.hasOwn(data, "rtkSavings"), true);
265
+ assert.equal(
266
+ data.rtkSavings === null || typeof data.rtkSavings === "object",
267
+ true,
268
+ );
269
+ } finally {
270
+ cleanup(autoSession.basePath);
271
+ autoSession.reset();
272
+ }
258
273
  });
259
274
 
260
- test("auto progress widget renders RTK savings under the footer stats line", () => {
261
- assert.match(dashboardSource, /formatRtkSavingsLabel/);
262
- assert.match(dashboardSource, /getRtkSessionSavings\(accessors\.getBasePath\(\), sessionId\)/);
263
- assert.match(dashboardSource, /lines\.push\(rightAlign\("", theme\.fg\("dim", cachedRtkLabel\), width\)\);/);
275
+ test("RTK savings label formats the dashboard footer text", () => {
276
+ assert.equal(formatRtkSavingsLabel(null), null);
277
+ assert.equal(
278
+ formatRtkSavingsLabel({
279
+ commands: 2,
280
+ inputTokens: 10_000,
281
+ outputTokens: 1_000,
282
+ savedTokens: 2_500,
283
+ savingsPct: 25,
284
+ totalTimeMs: 100,
285
+ avgTimeMs: 50,
286
+ updatedAt: new Date(0).toISOString(),
287
+ }),
288
+ "rtk: 2.5k saved (25%)",
289
+ );
264
290
  });
265
291
 
266
292
  test("updateProgressWidget refreshes slice progress cache immediately", (t) => {
@@ -11,8 +11,11 @@ import {
11
11
  _hasPendingResolveForTest,
12
12
  _setActiveSession,
13
13
  _setSessionSwitchInFlight,
14
+ _markSessionSwitchAbortGraceWindow,
15
+ _clearSessionSwitchAbortGraceWindow,
14
16
  _consumePendingSwitchCancellation,
15
17
  isSessionSwitchInFlight,
18
+ isSessionSwitchAbortGraceActive,
16
19
  } from "../auto/resolve.js";
17
20
  import { runUnit } from "../auto/run-unit.js";
18
21
  import { autoLoop } from "../auto/loop.js";
@@ -2303,6 +2306,18 @@ test("resolveAgentEndCancelled queues cancellation that arrives during session s
2303
2306
  _resetPendingResolve();
2304
2307
  });
2305
2308
 
2309
+ test("session-switch abort grace window is short-lived and resettable", () => {
2310
+ _resetPendingResolve();
2311
+
2312
+ _markSessionSwitchAbortGraceWindow(1_000);
2313
+
2314
+ assert.equal(isSessionSwitchAbortGraceActive(Date.now()), true);
2315
+ assert.equal(isSessionSwitchAbortGraceActive(Date.now() + 10_000), false);
2316
+
2317
+ _clearSessionSwitchAbortGraceWindow();
2318
+ assert.equal(isSessionSwitchAbortGraceActive(), false);
2319
+ });
2320
+
2306
2321
  // ─── #1571: artifact verification retry ──────────────────────────────────────
2307
2322
 
2308
2323
  test("autoLoop re-iterates when postUnitPreVerification returns retry (#1571)", async () => {
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync, readFileSync } from "node:fs";
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
@@ -361,74 +361,95 @@ test("resolveModelId: bare ID with claude-code as only provider still resolves",
361
361
 
362
362
  // ─── selectAndApplyModel verbose-gating tests ──────────────────────────
363
363
 
364
- test("model change notify in selectAndApplyModel is gated behind verbose flag", () => {
365
- // The Model [phase] [tier] notification should only fire when verbose=true.
366
- // The dashboard header already shows the active model, so the notification
367
- // is redundant noise during auto-mode (#3719).
368
- const gsdDir = join(__dirname, "..");
369
- const src = readFileSync(join(gsdDir, "auto-model-selection.ts"), "utf-8");
370
-
371
- // Find the block where setModel succeeds (appliedModel = model) and
372
- // verify notify is inside an `if (verbose)` guard.
373
- const setModelBlock = src.match(
374
- /const ok = await pi\.setModel\(model[\s\S]*?appliedModel = model;([\s\S]*?)break;/,
375
- );
376
- assert.ok(setModelBlock, "should find the setModel success block");
364
+ test("model change notify in selectAndApplyModel is gated behind verbose flag", async (t) => {
365
+ const originalCwd = process.cwd();
366
+ const tempProject = makeTempDir("gsd-routing-verbose-project-");
367
+ const notifications: Array<{ message: string; level: string }> = [];
368
+ t.after(() => {
369
+ process.chdir(originalCwd);
370
+ rmSync(tempProject, { recursive: true, force: true });
371
+ });
377
372
 
378
- const blockBody = setModelBlock![1];
379
- // The notify call must be inside an if (verbose) block
380
- assert.ok(
381
- blockBody.includes("if (verbose)"),
382
- "Model change ctx.ui.notify must be gated behind if (verbose) to avoid auto-mode notification noise",
383
- );
384
- assert.ok(
385
- blockBody.includes("ctx.ui.notify"),
386
- "notify call should still exist (just verbose-gated)",
373
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
374
+ writeFileSync(
375
+ join(tempProject, ".gsd", "PREFERENCES.md"),
376
+ ["---", "models:", " planning: claude-sonnet-4-6", "---"].join("\n"),
377
+ "utf-8",
387
378
  );
388
- });
379
+ process.chdir(tempProject);
389
380
 
390
- test("model policy resolves candidates from the policy-eligible pool", () => {
391
- const src = readFileSync(join(__dirname, "..", "auto-model-selection.ts"), "utf-8");
392
- assert.ok(
393
- src.includes("const resolutionPool = uokFlags.modelPolicy ? routingEligibleModels : availableModels"),
394
- "selectAndApplyModel should resolve model IDs against policy-eligible models when model policy is enabled",
381
+ await selectAndApplyModel(
382
+ {
383
+ modelRegistry: { getAvailable: () => [{ id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" }] },
384
+ sessionManager: { getSessionId: () => "test-session" },
385
+ ui: { notify: (message: string, level: string) => notifications.push({ message, level }) },
386
+ model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
387
+ } as any,
388
+ {
389
+ setModel: async () => true,
390
+ emitBeforeModelSelect: async () => undefined,
391
+ getActiveTools: () => [],
392
+ emitAdjustToolSet: async () => undefined,
393
+ setActiveTools: () => {},
394
+ } as any,
395
+ "plan-slice",
396
+ "M001/S01",
397
+ tempProject,
398
+ undefined,
399
+ false,
400
+ { provider: "anthropic", id: "claude-sonnet-4-6" },
401
+ undefined,
402
+ true,
395
403
  );
396
- });
397
404
 
398
- test("model policy receives task metadata for requirement-vector decisions", () => {
399
- const src = readFileSync(join(__dirname, "..", "auto-model-selection.ts"), "utf-8");
400
- assert.ok(
401
- src.includes("taskMetadata: taskMetadataForPolicy"),
402
- "applyModelPolicyFilter should receive task metadata so requirement vectors are unit-aware",
403
- );
404
- assert.ok(
405
- src.includes("extractTaskMetadata(unitId, basePath)"),
406
- "execute-task dispatch should derive metadata before policy filtering",
407
- );
405
+ assert.deepEqual(notifications, []);
408
406
  });
409
407
 
410
- test("dynamic routing passes provider-qualified model keys to the router", () => {
411
- const src = readFileSync(join(__dirname, "..", "auto-model-selection.ts"), "utf-8");
412
- assert.ok(
413
- src.includes("routingEligibleModels.map(m => `${m.provider}/${m.id}`)"),
414
- "selectAndApplyModel should preserve provider prefixes for dynamic routing candidates",
415
- );
416
- assert.ok(
417
- !src.includes("routingEligibleModels.map(m => m.id)"),
418
- "selectAndApplyModel must not strip providers before resolving tier_models",
419
- );
420
- });
408
+ test("selectAndApplyModel re-applies captured thinking level after setModel success", async (t) => {
409
+ const originalCwd = process.cwd();
410
+ const tempProject = makeTempDir("gsd-routing-thinking-project-");
411
+ const thinkingLevels: unknown[] = [];
412
+ t.after(() => {
413
+ process.chdir(originalCwd);
414
+ rmSync(tempProject, { recursive: true, force: true });
415
+ });
421
416
 
422
- test("selectAndApplyModel re-applies captured thinking level after setModel success", () => {
423
- const src = readFileSync(join(__dirname, "..", "auto-model-selection.ts"), "utf-8");
424
- assert.ok(
425
- src.includes("autoModeStartThinkingLevel?: ReturnType<ExtensionAPI[\"getThinkingLevel\"]> | null"),
426
- "selectAndApplyModel should accept an autoModeStartThinkingLevel parameter",
417
+ mkdirSync(join(tempProject, ".gsd"), { recursive: true });
418
+ writeFileSync(
419
+ join(tempProject, ".gsd", "PREFERENCES.md"),
420
+ ["---", "models:", " planning: claude-sonnet-4-6", "---"].join("\n"),
421
+ "utf-8",
427
422
  );
428
- assert.ok(
429
- src.includes("reapplyThinkingLevel(pi, autoModeStartThinkingLevel)"),
430
- "selectAndApplyModel should re-apply captured thinking level after model changes",
423
+ process.chdir(tempProject);
424
+
425
+ await selectAndApplyModel(
426
+ {
427
+ modelRegistry: { getAvailable: () => [{ id: "claude-sonnet-4-6", provider: "anthropic", api: "anthropic-messages" }] },
428
+ sessionManager: { getSessionId: () => "test-session" },
429
+ ui: { notify: () => {} },
430
+ model: { provider: "anthropic", id: "claude-sonnet-4-6", api: "anthropic-messages" },
431
+ } as any,
432
+ {
433
+ setModel: async () => true,
434
+ setThinkingLevel: (level: unknown) => { thinkingLevels.push(level); },
435
+ emitBeforeModelSelect: async () => undefined,
436
+ getActiveTools: () => [],
437
+ emitAdjustToolSet: async () => undefined,
438
+ setActiveTools: () => {},
439
+ } as any,
440
+ "plan-slice",
441
+ "M001/S01",
442
+ tempProject,
443
+ undefined,
444
+ false,
445
+ { provider: "anthropic", id: "claude-sonnet-4-6" },
446
+ undefined,
447
+ true,
448
+ undefined,
449
+ { effort: "high" } as any,
431
450
  );
451
+
452
+ assert.deepEqual(thinkingLevels, [{ effort: "high" }]);
432
453
  });
433
454
 
434
455
  test("resolveModelId: anthropic wins over claude-code when session provider is not claude-code", () => {
@@ -2,63 +2,19 @@
2
2
  * auto-paused-session-validation.test.ts — Validates milestone existence
3
3
  * before restoring from paused-session.json (#1664).
4
4
  *
5
- * Two layers:
6
- * 1. Source-code regression: ensures auto.ts validates the milestone before
7
- * trusting paused-session.json (guards against accidental removal).
8
- * 2. Filesystem unit: confirms resolveMilestonePath / resolveMilestoneFile
9
- * correctly detect missing and completed milestones.
5
+ * Filesystem unit coverage confirms resolveMilestonePath / resolveMilestoneFile
6
+ * correctly detect missing and completed milestones.
10
7
  */
11
8
 
12
9
  import test from "node:test";
13
10
  import assert from "node:assert/strict";
14
- import { mkdirSync, writeFileSync, rmSync, readFileSync } from "node:fs";
11
+ import { mkdirSync, writeFileSync, rmSync } from "node:fs";
15
12
  import { join } from "node:path";
16
13
  import { tmpdir } from "node:os";
17
14
  import { randomUUID } from "node:crypto";
18
- import { fileURLToPath } from "node:url";
19
- import { dirname } from "node:path";
20
15
 
21
16
  import { resolveMilestonePath, resolveMilestoneFile } from "../paths.ts";
22
17
 
23
- const __dirname = dirname(fileURLToPath(import.meta.url));
24
- const AUTO_TS_PATH = join(__dirname, "..", "auto.ts");
25
-
26
- // ─── Source-code regression guard ───────────────────────────────────────────
27
-
28
- test("auto.ts validates milestone before restoring paused session (#1664)", () => {
29
- const source = readFileSync(AUTO_TS_PATH, "utf-8");
30
-
31
- // The resume block must call resolveMilestonePath to verify the milestone dir exists
32
- assert.ok(
33
- source.includes('resolveMilestonePath(base, meta.milestoneId)'),
34
- "auto.ts must call resolveMilestonePath to verify paused milestone exists",
35
- );
36
-
37
- // The resume block must check for a SUMMARY file to detect completed milestones
38
- assert.ok(
39
- source.includes('resolveMilestoneFile(base, meta.milestoneId, "SUMMARY")'),
40
- "auto.ts must check for SUMMARY file to detect completed milestones",
41
- );
42
-
43
- assert.ok(
44
- source.includes("await ensureDbOpen(base)") &&
45
- source.indexOf("await ensureDbOpen(base)") < source.indexOf('resolveMilestoneFile(base, meta.milestoneId, "SUMMARY")'),
46
- "auto.ts must open the canonical DB before using SUMMARY as a paused-session fallback",
47
- );
48
-
49
- // Resume path must sanitize paused session file metadata before unlink/recovery.
50
- assert.ok(
51
- source.includes("normalizeSessionFilePath(meta.sessionFile ?? null)"),
52
- "auto.ts must sanitize paused-session metadata sessionFile before using it",
53
- );
54
-
55
- // Pause path must sanitize live session file path before persisting metadata.
56
- assert.ok(
57
- source.includes("normalizeSessionFilePath(ctx?.sessionManager?.getSessionFile() ?? null)"),
58
- "auto.ts must sanitize sessionManager getSessionFile output before persisting",
59
- );
60
- });
61
-
62
18
  // ─── Filesystem validation unit tests ───────────────────────────────────────
63
19
 
64
20
  function makeTmpBase(): string {
@@ -1,27 +1,85 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Behavior tests for auto-loop cleanup after paused provider exits.
1
3
  import { test } from "node:test";
2
4
  import assert from "node:assert/strict";
3
- import { readFileSync } from "node:fs";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
5
+ import { mkdtempSync, rmSync } from "node:fs";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
6
8
 
7
- const __dirname = dirname(fileURLToPath(import.meta.url));
8
- const autoSource = readFileSync(join(__dirname, "..", "auto.ts"), "utf-8");
9
+ import { cleanupAfterLoopExit, rerootCommandSession } from "../auto.ts";
10
+ import { autoSession } from "../auto-runtime-state.ts";
9
11
 
10
- test("#3370: cleanupAfterLoopExit preserves paused auto badge after provider pause", () => {
11
- const cleanupIdx = autoSource.indexOf("function cleanupAfterLoopExit");
12
- assert.ok(cleanupIdx > -1, "auto.ts should define cleanupAfterLoopExit");
12
+ test("cleanupAfterLoopExit preserves paused auto badge after provider pause", async () => {
13
+ const base = mkdtempSync(join(tmpdir(), "gsd-paused-cleanup-"));
14
+ const previousCwd = process.cwd();
15
+ const statuses: Array<[string, string | undefined]> = [];
13
16
 
14
- const dispatchIdx = autoSource.indexOf("export async function dispatchHookUnit", cleanupIdx);
15
- assert.ok(dispatchIdx > cleanupIdx, "cleanupAfterLoopExit body should be bounded by the next export");
17
+ autoSession.reset();
18
+ autoSession.active = true;
19
+ autoSession.paused = true;
20
+ autoSession.basePath = join(base, ".gsd", "worktrees", "M001");
21
+ autoSession.originalBasePath = base;
16
22
 
17
- const cleanupBody = autoSource.slice(cleanupIdx, dispatchIdx);
18
- const pausedGuardIdx = cleanupBody.indexOf("if (!s.paused) {");
19
- const clearStatusIdx = cleanupBody.indexOf('ctx.ui.setStatus("gsd-auto", undefined);');
23
+ try {
24
+ await cleanupAfterLoopExit({
25
+ ui: {
26
+ setStatus: (key: string, value: string | undefined) => {
27
+ statuses.push([key, value]);
28
+ },
29
+ setWidget: () => {},
30
+ notify: () => {},
31
+ },
32
+ } as any);
20
33
 
21
- assert.ok(pausedGuardIdx > -1, "loop-exit cleanup must guard UI clearing when auto is paused");
22
- assert.ok(clearStatusIdx > pausedGuardIdx, "status clearing must live behind the paused guard");
23
- assert.ok(
24
- autoSource.includes('ctx?.ui.setStatus("gsd-auto", "paused");'),
25
- "pauseAuto must still set the paused badge for transient provider pauses",
34
+ assert.equal(statuses.some(([key]) => key === "gsd-auto"), false);
35
+ assert.equal(autoSession.active, false);
36
+ assert.equal(autoSession.paused, true);
37
+ } finally {
38
+ autoSession.reset();
39
+ process.chdir(previousCwd);
40
+ rmSync(base, { recursive: true, force: true });
41
+ }
42
+ });
43
+
44
+ test("cleanupAfterLoopExit clears status and widget when auto is not paused", async () => {
45
+ const statusCalls: unknown[] = [];
46
+ const widgetCalls: unknown[] = [];
47
+
48
+ autoSession.reset();
49
+ autoSession.active = true;
50
+ autoSession.paused = false;
51
+
52
+ try {
53
+ await cleanupAfterLoopExit({
54
+ hasUI: false,
55
+ ui: {
56
+ setStatus: (...args: unknown[]) => statusCalls.push(args),
57
+ setWidget: (...args: unknown[]) => widgetCalls.push(args),
58
+ notify: () => {},
59
+ },
60
+ } as any);
61
+
62
+ assert.deepEqual(statusCalls, [["gsd-auto", undefined]]);
63
+ assert.deepEqual(widgetCalls, [["gsd-progress", undefined]]);
64
+ assert.equal(autoSession.active, false);
65
+ assert.equal(autoSession.paused, false);
66
+ } finally {
67
+ autoSession.reset();
68
+ }
69
+ });
70
+
71
+ test("rerootCommandSession refreshes command workspace to project root", async () => {
72
+ const calls: string[] = [];
73
+ const result = await rerootCommandSession(
74
+ {
75
+ newSession: async ({ workspaceRoot }: { workspaceRoot: string }) => {
76
+ calls.push(workspaceRoot);
77
+ return { cancelled: false };
78
+ },
79
+ } as any,
80
+ "/project/root",
26
81
  );
82
+
83
+ assert.deepEqual(result, { status: "ok" });
84
+ assert.deepEqual(calls, ["/project/root"]);
27
85
  });
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Auto-loop phase lifecycle regression tests.
1
3
  import test from "node:test";
2
4
  import assert from "node:assert/strict";
3
5
  import { mkdtempSync, rmSync } from "node:fs";
@@ -60,6 +62,59 @@ async function runSuccessfulFinalize(s: AutoSession) {
60
62
  );
61
63
  }
62
64
 
65
+ async function runFinalizeWithDeps(s: AutoSession, depsOverrides: Record<string, unknown>) {
66
+ const unit = s.currentUnit;
67
+ assert.ok(unit, "test setup must provide currentUnit");
68
+
69
+ writeUnitRuntimeRecord(s.basePath, unit.type, unit.id, unit.startedAt, {
70
+ phase: "dispatched",
71
+ });
72
+
73
+ const deps = {
74
+ clearUnitTimeout() {},
75
+ buildSnapshotOpts() {
76
+ return {};
77
+ },
78
+ stopAuto: async () => {},
79
+ pauseAuto: async () => {},
80
+ updateProgressWidget() {},
81
+ postUnitPreVerification: async () => "continue",
82
+ runPostUnitVerification: async () => "continue",
83
+ postUnitPostVerification: async () => "continue",
84
+ ...depsOverrides,
85
+ };
86
+
87
+ return runFinalize(
88
+ {
89
+ ctx: { ui: { notify() {} } },
90
+ pi: {},
91
+ s,
92
+ deps,
93
+ prefs: undefined,
94
+ iteration: 1,
95
+ flowId: "flow-1",
96
+ nextSeq: () => 1,
97
+ } as any,
98
+ {
99
+ unitType: unit.type,
100
+ unitId: unit.id,
101
+ prompt: "",
102
+ finalPrompt: "",
103
+ pauseAfterUatDispatch: false,
104
+ state: {} as any,
105
+ mid: "M001",
106
+ midTitle: "Milestone",
107
+ isRetry: false,
108
+ previousTier: undefined,
109
+ },
110
+ {
111
+ recentUnits: [],
112
+ stuckRecoveryAttempts: 0,
113
+ consecutiveFinalizeTimeouts: 0,
114
+ },
115
+ );
116
+ }
117
+
63
118
  test("runFinalize clears currentUnit after successful finalize", async () => {
64
119
  const base = mkdtempSync(join(tmpdir(), "gsd-finalize-current-unit-"));
65
120
  const s = new AutoSession();
@@ -102,3 +157,54 @@ test("runFinalize marks unit runtime finalized after successful finalize", async
102
157
  rmSync(base, { recursive: true, force: true });
103
158
  }
104
159
  });
160
+
161
+ test("runFinalize merges a verified complete-milestone immediately and only once", async (t) => {
162
+ const base = mkdtempSync(join(tmpdir(), "gsd-finalize-merge-"));
163
+ t.after(() => {
164
+ rmSync(base, { recursive: true, force: true });
165
+ });
166
+
167
+ const s = new AutoSession();
168
+ const startedAt = Date.now();
169
+ let mergeCalls = 0;
170
+ s.basePath = base;
171
+ s.originalBasePath = base;
172
+ s.currentMilestoneId = "M001";
173
+ s.currentUnit = {
174
+ type: "complete-milestone",
175
+ id: "M001",
176
+ startedAt,
177
+ };
178
+
179
+ const result = await runFinalizeWithDeps(s, {
180
+ preflightCleanRoot: () => ({ stashPushed: false }),
181
+ postflightPopStash: () => ({ needsManualRecovery: false }),
182
+ resolver: {
183
+ mergeAndExit() {
184
+ mergeCalls++;
185
+ },
186
+ },
187
+ });
188
+
189
+ assert.equal(result.action, "next");
190
+ assert.equal(mergeCalls, 1);
191
+ assert.equal(s.milestoneMergedInPhases, true);
192
+
193
+ s.currentUnit = {
194
+ type: "complete-milestone",
195
+ id: "M001",
196
+ startedAt: startedAt + 1,
197
+ };
198
+ const second = await runFinalizeWithDeps(s, {
199
+ preflightCleanRoot: () => ({ stashPushed: false }),
200
+ postflightPopStash: () => ({ needsManualRecovery: false }),
201
+ resolver: {
202
+ mergeAndExit() {
203
+ mergeCalls++;
204
+ },
205
+ },
206
+ });
207
+
208
+ assert.equal(second.action, "next");
209
+ assert.equal(mergeCalls, 1);
210
+ });