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
@@ -11,99 +11,15 @@
11
11
  import test from "node:test";
12
12
  import assert from "node:assert/strict";
13
13
  import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, rmSync, realpathSync } from "node:fs";
14
- import { join, dirname } from "node:path";
14
+ import { join } from "node:path";
15
15
  import { tmpdir } from "node:os";
16
- import { fileURLToPath } from "node:url";
16
+ import { AutoSession } from "../auto/session.ts";
17
+ import { runPreDispatch } from "../auto/phases.ts";
17
18
 
18
- const __dirname = dirname(fileURLToPath(import.meta.url));
19
-
20
- // ─── Source-level checks ──────────────────────────────────────────────────────
21
-
22
- test("auto/phases.ts milestone transition block calls rebuildState", () => {
23
- const phasesSrc = readFileSync(
24
- join(__dirname, "..", "auto", "phases.ts"),
25
- "utf-8",
26
- );
27
-
28
- // rebuildState must be called within the milestone transition block
29
- assert.ok(
30
- phasesSrc.includes("deps.rebuildState(s.basePath)"),
31
- "auto/phases.ts should call deps.rebuildState(s.basePath) during milestone transition",
32
- );
33
-
34
- // The rebuildState call must appear AFTER the pruneQueueOrder call
35
- // (i.e. after all transition cleanup is done)
36
- const pruneIdx = phasesSrc.indexOf("deps.pruneQueueOrder(s.basePath, pendingIds)");
37
- const rebuildIdx = phasesSrc.indexOf("deps.rebuildState(s.basePath)");
38
- assert.ok(pruneIdx > 0, "pruneQueueOrder should exist in phases.ts");
39
- assert.ok(rebuildIdx > 0, "rebuildState should exist in phases.ts");
40
- assert.ok(
41
- rebuildIdx > pruneIdx,
42
- "rebuildState should be called after pruneQueueOrder in the milestone transition block",
43
- );
44
- });
45
-
46
- test("auto/phases.ts milestone transition block resets completed-units.json", () => {
47
- const phasesSrc = readFileSync(
48
- join(__dirname, "..", "auto", "phases.ts"),
49
- "utf-8",
50
- );
51
-
52
- // completed-units.json must be archived and cleared during milestone transition
53
- const transitionStart = phasesSrc.indexOf("Milestone transition");
54
- assert.ok(transitionStart > 0, "Milestone transition block should exist");
55
-
56
- // The old file is archived before being cleared (#2313)
57
- const archiveSection = phasesSrc.indexOf("completed-units-", transitionStart);
58
- assert.ok(
59
- archiveSection > 0,
60
- "auto/phases.ts should archive completed-units.json during milestone transition",
61
- );
62
-
63
- // The disk file should be cleared to an empty array
64
- assert.ok(
65
- phasesSrc.includes('atomicWriteSync(completedKeysPath, JSON.stringify([], null, 2))'),
66
- "auto/phases.ts should write empty array to completed-units.json during milestone transition",
67
- );
68
- });
69
-
70
- test("auto/loop-deps.ts LoopDeps interface includes rebuildState", () => {
71
- const loopDepsSrc = readFileSync(
72
- join(__dirname, "..", "auto", "loop-deps.ts"),
73
- "utf-8",
74
- );
75
-
76
- assert.ok(
77
- loopDepsSrc.includes("rebuildState: (basePath: string) => Promise<void>"),
78
- "LoopDeps interface should declare rebuildState method",
79
- );
80
- });
81
-
82
- test("auto.ts buildLoopDeps wires rebuildState", () => {
83
- const autoSrc = readFileSync(
84
- join(__dirname, "..", "auto.ts"),
85
- "utf-8",
86
- );
87
-
88
- // rebuildState should be in the LoopDeps object literal.
89
- // Match the signature prefix so the test survives future param changes
90
- // (e.g. the DECOUPLE-02 change from `()` to `(pi: ExtensionAPI)`).
91
- const buildLoopDepsIdx = autoSrc.indexOf("function buildLoopDeps(");
92
- assert.ok(buildLoopDepsIdx > 0, "buildLoopDeps function should exist");
93
-
94
- const afterBuild = autoSrc.slice(buildLoopDepsIdx);
95
- assert.ok(
96
- afterBuild.includes("rebuildState,") || afterBuild.includes("rebuildState:"),
97
- "buildLoopDeps should include rebuildState in the returned deps object",
98
- );
99
- });
100
-
101
- // ─── Functional test: completed-units.json reset ─────────────────────────────
102
-
103
- test("completed-units.json is cleared on milestone transition (functional)", () => {
19
+ test("milestone transition archives completed units and rebuilds state", async () => {
104
20
  const tempDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-cu-reset-")));
21
+ const calls: string[] = [];
105
22
  try {
106
- // Create .gsd directory with a populated completed-units.json
107
23
  const gsdDir = join(tempDir, ".gsd");
108
24
  mkdirSync(gsdDir, { recursive: true });
109
25
 
@@ -116,16 +32,81 @@ test("completed-units.json is cleared on milestone transition (functional)", ()
116
32
  ];
117
33
  writeFileSync(completedKeysPath, JSON.stringify(staleEntries, null, 2));
118
34
 
119
- // Verify stale entries exist
120
- const before = JSON.parse(readFileSync(completedKeysPath, "utf-8"));
121
- assert.equal(before.length, 4, "Should have 4 stale entries before reset");
122
-
123
- // Simulate what phases.ts does: write empty array
124
- writeFileSync(completedKeysPath, JSON.stringify([], null, 2));
125
-
126
- // Verify reset
35
+ const s = new AutoSession();
36
+ s.basePath = tempDir;
37
+ s.originalBasePath = tempDir;
38
+ s.currentMilestoneId = "M001";
39
+ s.unitDispatchCount.set("old", 1);
40
+ s.unitRecoveryCount.set("old", 1);
41
+ s.unitLifetimeDispatches.set("old", 1);
42
+
43
+ const state = {
44
+ phase: "planning",
45
+ activeMilestone: { id: "M002", title: "Next" },
46
+ activeSlice: null,
47
+ activeTask: null,
48
+ recentDecisions: [],
49
+ blockers: [],
50
+ nextAction: "Plan M002",
51
+ registry: [
52
+ { id: "M001", title: "Done", status: "complete" },
53
+ { id: "M002", title: "Next", status: "active" },
54
+ ],
55
+ };
56
+
57
+ const result = await runPreDispatch({
58
+ ctx: { ui: { notify() {} } },
59
+ pi: {},
60
+ s,
61
+ prefs: undefined,
62
+ iteration: 1,
63
+ flowId: "test-flow",
64
+ nextSeq: () => 1,
65
+ deps: {
66
+ checkResourcesStale: () => null,
67
+ invalidateAllCaches: () => calls.push("invalidate"),
68
+ preDispatchHealthGate: async () => ({ proceed: true, fixesApplied: [] }),
69
+ syncProjectRootToWorktree: () => {},
70
+ deriveState: async () => state,
71
+ syncCmuxSidebar: () => {},
72
+ preflightCleanRoot: () => ({ ok: true, stashPushed: false }),
73
+ postflightPopStash: () => ({ ok: true, needsManualRecovery: false }),
74
+ resolver: {
75
+ mergeAndExit: () => calls.push("merge"),
76
+ enterMilestone: (mid: string) => calls.push(`enter:${mid}`),
77
+ },
78
+ sendDesktopNotification: () => {},
79
+ logCmuxEvent: () => {},
80
+ getIsolationMode: () => "none",
81
+ captureIntegrationBranch: () => {},
82
+ pruneQueueOrder: (_base: string, pending: string[]) => calls.push(`prune:${pending.join(",")}`),
83
+ rebuildState: async () => calls.push("rebuild"),
84
+ setActiveMilestoneId: (_base: string, mid: string) => calls.push(`active:${mid}`),
85
+ reconcileMergeState: () => "clean",
86
+ emitJournalEvent: () => {},
87
+ stopAuto: async () => {},
88
+ pauseAuto: async () => {},
89
+ closeoutUnit: async () => {},
90
+ buildSnapshotOpts: () => ({}),
91
+ },
92
+ } as any, {
93
+ recentUnits: [{ key: "stale" }],
94
+ stuckRecoveryAttempts: 2,
95
+ consecutiveFinalizeTimeouts: 0,
96
+ });
97
+
98
+ assert.equal(result.action, "next");
99
+ assert.equal(s.currentMilestoneId, "M002");
100
+ assert.equal(s.unitDispatchCount.size, 0);
101
+ assert.equal(s.unitRecoveryCount.size, 0);
102
+ assert.equal(s.unitLifetimeDispatches.size, 0);
103
+ assert.ok(existsSync(join(gsdDir, "completed-units-M001.json")));
127
104
  const after = JSON.parse(readFileSync(completedKeysPath, "utf-8"));
128
- assert.deepEqual(after, [], "completed-units.json should be empty after milestone transition");
105
+ assert.deepEqual(after, []);
106
+ assert.ok(
107
+ calls.indexOf("prune:M002") < calls.indexOf("rebuild"),
108
+ `expected prune before rebuild, got ${calls.join(" > ")}`,
109
+ );
129
110
  } finally {
130
111
  rmSync(tempDir, { recursive: true, force: true });
131
112
  }
@@ -1,298 +1,90 @@
1
1
  /**
2
- * Model UnitType Mapping — regression tests for #2865.
2
+ * Model UnitType Mapping — behavior tests for #2865 / #2900 / ADR-011.
3
3
  *
4
- * Verifies that all auto-dispatch unitTypes have corresponding entries in:
5
- * - resolveModelWithFallbacksForUnit (preferences-models.ts)
6
- * - classifyUnitPhase (metrics.ts)
7
- * - LIFECYCLE_ONLY_UNITS (auto-post-unit.ts)
8
- * - unitVerb / unitPhaseLabel (auto-dashboard.ts)
9
- * - resolveExpectedArtifactPath (auto-artifact-paths.ts)
10
- *
11
- * Uses source-level checks to avoid import resolution issues in dev.
12
- *
13
- * Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
4
+ * Verifies model routing, metrics/dashboard labels, and artifact resolution
5
+ * through exported runtime APIs instead of inspecting source text.
14
6
  */
15
7
 
16
8
  import test from "node:test";
17
9
  import assert from "node:assert/strict";
18
- import { readFileSync } from "node:fs";
19
- import { join, dirname } from "node:path";
20
- import { fileURLToPath } from "node:url";
21
-
22
- const __dirname = dirname(fileURLToPath(import.meta.url));
23
- const gsdDir = join(__dirname, "..");
24
-
25
- function readSrc(file: string): string {
26
- return readFileSync(join(gsdDir, file), "utf-8");
27
- }
28
-
29
- const preferencesSrc = readSrc("preferences-models.ts");
30
- const metricsSrc = readSrc("metrics.ts");
31
- const postUnitSrc = readSrc("auto-post-unit.ts");
32
- const dashboardSrc = readSrc("auto-dashboard.ts");
33
- const artifactSrc = readSrc("auto-artifact-paths.ts");
34
- const guidedFlowSrc = readSrc("guided-flow.ts");
35
- const autoDispatchSrc = readSrc("auto-dispatch.ts");
36
-
37
- // Derive unitTypes directly from auto-dispatch.ts source so the test
38
- // automatically tracks dispatch rule changes (Copilot review feedback).
39
- const AUTO_DISPATCH_UNIT_TYPES = (() => {
40
- const unitTypeRegex = /unitType:\s*["']([^"']+)["']/g;
41
- const unitTypes = new Set<string>();
42
- let match: RegExpExecArray | null;
43
- while ((match = unitTypeRegex.exec(autoDispatchSrc)) !== null) {
44
- unitTypes.add(match[1]);
45
- }
46
- return Array.from(unitTypes);
47
- })();
48
-
49
- // Additionally include unitTypes used by guided-flow but not auto-dispatch
50
- // (e.g., discuss-slice is dispatched by guided-flow but not auto-dispatch).
51
- const ALL_KNOWN_UNIT_TYPES = [
52
- ...new Set([...AUTO_DISPATCH_UNIT_TYPES, "discuss-slice"]),
53
- ];
54
-
55
- // ═══════════════════════════════════════════════════════════════════════════
56
- // #2865: discuss dispatches must NOT alias to plan unitTypes
57
- // ═══════════════════════════════════════════════════════════════════════════
58
-
59
- test("#2865: no dispatchWorkflow with gsd-discuss customType uses plan-milestone", () => {
60
- // Match dispatchWorkflow calls where "gsd-discuss" appears before "plan-milestone"
61
- // in the same call (the 5 args are on consecutive lines).
62
- const blocks = guidedFlowSrc.split(/dispatchWorkflow\(/);
63
- for (const block of blocks) {
64
- const callEnd = block.indexOf(");");
65
- if (callEnd === -1) continue;
66
- const call = block.slice(0, callEnd);
67
- if (call.includes('"gsd-discuss"') && call.includes('"plan-milestone"')) {
68
- assert.fail(`Discuss dispatch should not use plan-milestone: ...dispatchWorkflow(${call.slice(0, 120).trim()}...`);
69
- }
70
- }
71
- });
72
-
73
- test("#2865: no dispatchWorkflow with gsd-discuss customType uses plan-slice", () => {
74
- const blocks = guidedFlowSrc.split(/dispatchWorkflow\(/);
75
- for (const block of blocks) {
76
- const callEnd = block.indexOf(");");
77
- if (callEnd === -1) continue;
78
- const call = block.slice(0, callEnd);
79
- if (call.includes('"gsd-discuss"') && call.includes('"plan-slice"')) {
80
- assert.fail(`Discuss slice dispatch should not use plan-slice: ...dispatchWorkflow(${call.slice(0, 120).trim()}...`);
81
- }
10
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { tmpdir } from "node:os";
13
+
14
+ import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
15
+ import { unitPhaseLabel, unitVerb } from "../auto-dashboard.ts";
16
+ import { classifyUnitPhase } from "../metrics.ts";
17
+ import { resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
18
+ import { KNOWN_UNIT_LABELS } from "../preferences-types.ts";
19
+
20
+ function withModelPreferences<T>(fn: () => T): T {
21
+ const oldHome = process.env.GSD_HOME;
22
+ const home = mkdtempSync(join(tmpdir(), "gsd-model-map-"));
23
+ try {
24
+ process.env.GSD_HOME = home;
25
+ writeFileSync(join(home, "preferences.md"), [
26
+ "---",
27
+ "models:",
28
+ " research: research-model",
29
+ " planning: planning-model",
30
+ " discuss: discuss-model",
31
+ " execution: execution-model",
32
+ " execution_simple: simple-model",
33
+ " completion: completion-model",
34
+ " validation: validation-model",
35
+ " subagent: subagent-model",
36
+ "---",
37
+ "",
38
+ ].join("\n"));
39
+ return fn();
40
+ } finally {
41
+ if (oldHome === undefined) delete process.env.GSD_HOME;
42
+ else process.env.GSD_HOME = oldHome;
43
+ rmSync(home, { recursive: true, force: true });
82
44
  }
83
- });
84
-
85
- test("#2865: no buildDiscussPrompt call dispatches with plan-milestone", () => {
86
- const blocks = guidedFlowSrc.split(/dispatchWorkflow\(/);
87
- for (const block of blocks) {
88
- const callEnd = block.indexOf(");");
89
- if (callEnd === -1) continue;
90
- const call = block.slice(0, callEnd);
91
- if (call.includes("buildDiscussPrompt") && call.includes('"plan-milestone"')) {
92
- assert.fail(`buildDiscussPrompt dispatch should not use plan-milestone`);
93
- }
94
- }
95
- });
96
-
97
- test("#2865: no buildDiscussSlicePrompt call dispatches with plan-slice", () => {
98
- const blocks = guidedFlowSrc.split(/dispatchWorkflow\(/);
99
- for (const block of blocks) {
100
- const callEnd = block.indexOf(");");
101
- if (callEnd === -1) continue;
102
- const call = block.slice(0, callEnd);
103
- if (call.includes("buildDiscussSlicePrompt") && call.includes('"plan-slice"')) {
104
- assert.fail(`buildDiscussSlicePrompt dispatch should not use plan-slice`);
105
- }
106
- }
107
- });
108
-
109
- test("#2865: no guided-discuss-milestone loadPrompt dispatches with plan-milestone", () => {
110
- const blocks = guidedFlowSrc.split(/dispatchWorkflow\(/);
111
- for (const block of blocks) {
112
- const callEnd = block.indexOf(");");
113
- if (callEnd === -1) continue;
114
- const call = block.slice(0, callEnd);
115
- if (call.includes("guided-discuss-milestone") && call.includes('"plan-milestone"')) {
116
- assert.fail(`guided-discuss-milestone dispatch should not use plan-milestone`);
117
- }
118
- }
119
- });
120
-
121
- // ═══════════════════════════════════════════════════════════════════════════
122
- // preferences-models.ts: resolveModelWithFallbacksForUnit coverage
123
- // ═══════════════════════════════════════════════════════════════════════════
124
-
125
- test("resolveModelWithFallbacksForUnit handles discuss-milestone", () => {
126
- assert.ok(preferencesSrc.includes('"discuss-milestone"'), "missing discuss-milestone case");
127
- });
128
-
129
- test("resolveModelWithFallbacksForUnit handles discuss-slice", () => {
130
- assert.ok(preferencesSrc.includes('"discuss-slice"'), "missing discuss-slice case");
131
- });
132
-
133
- test("discuss unitTypes fall back to planning when models.discuss is unset", () => {
134
- assert.ok(
135
- preferencesSrc.includes("m.discuss ?? m.planning"),
136
- "discuss should fall back to m.planning",
137
- );
138
- });
139
-
140
- test("validation unitTypes fall back to planning when models.validation is unset", () => {
141
- assert.ok(
142
- preferencesSrc.includes("m.validation ?? m.planning"),
143
- "validation should fall back to m.planning",
144
- );
145
- });
146
-
147
- test("all auto-dispatch unitTypes have preference mapping or subagent handling", () => {
148
- const unmapped: string[] = [];
149
- for (const ut of ALL_KNOWN_UNIT_TYPES) {
150
- if (!preferencesSrc.includes(`"${ut}"`)) {
151
- unmapped.push(ut);
152
- }
153
- }
154
- assert.deepEqual(unmapped, [], `Unmapped unitTypes in preferences-models.ts: ${unmapped.join(", ")}`);
155
- });
156
-
157
- // ═══════════════════════════════════════════════════════════════════════════
158
- // #2900: worktree-merge must map to completion phase
159
- // ═══════════════════════════════════════════════════════════════════════════
160
-
161
- test("#2900: resolveModelWithFallbacksForUnit handles worktree-merge", () => {
162
- assert.ok(preferencesSrc.includes('"worktree-merge"'), "missing worktree-merge case in switch");
163
- });
164
-
165
- // ═══════════════════════════════════════════════════════════════════════════
166
- // #2900: KNOWN_UNIT_TYPES must include all dispatched unit types
167
- // ═══════════════════════════════════════════════════════════════════════════
168
-
169
- const preferenceTypesSrc = readSrc("preferences-types.ts");
170
-
171
- test("#2900: KNOWN_UNIT_TYPES includes all auto-dispatch unit types", () => {
172
- const missing: string[] = [];
173
- for (const ut of ALL_KNOWN_UNIT_TYPES) {
174
- if (!preferenceTypesSrc.includes(`"${ut}"`)) {
175
- missing.push(ut);
176
- }
177
- }
178
- assert.deepEqual(missing, [], `Missing from KNOWN_UNIT_TYPES: ${missing.join(", ")}`);
179
- });
45
+ }
180
46
 
181
- test("#2900: KNOWN_UNIT_TYPES includes worktree-merge", () => {
182
- assert.ok(preferenceTypesSrc.includes('"worktree-merge"'), "worktree-merge missing from KNOWN_UNIT_TYPES");
47
+ test("discuss unit types route to the discuss model bucket", () => {
48
+ withModelPreferences(() => {
49
+ assert.equal(resolveModelWithFallbacksForUnit("discuss-milestone")?.primary, "discuss-model");
50
+ assert.equal(resolveModelWithFallbacksForUnit("discuss-slice")?.primary, "discuss-model");
51
+ });
183
52
  });
184
53
 
185
- // ═══════════════════════════════════════════════════════════════════════════
186
- // metrics.ts: classifyUnitPhase coverage
187
- // ═══════════════════════════════════════════════════════════════════════════
188
-
189
- test("classifyUnitPhase includes discussion phase", () => {
190
- assert.ok(metricsSrc.includes('"discussion"'), "MetricsPhase should include discussion");
54
+ test("validation unit types route to the validation model bucket", () => {
55
+ withModelPreferences(() => {
56
+ assert.equal(resolveModelWithFallbacksForUnit("validate-milestone")?.primary, "validation-model");
57
+ assert.equal(resolveModelWithFallbacksForUnit("gate-evaluate")?.primary, "validation-model");
58
+ });
191
59
  });
192
60
 
193
- test("classifyUnitPhase maps discuss-milestone and discuss-slice", () => {
194
- assert.ok(metricsSrc.includes('"discuss-milestone"'), "missing discuss-milestone in metrics");
195
- assert.ok(metricsSrc.includes('"discuss-slice"'), "missing discuss-slice in metrics");
61
+ test("worktree-merge routes to completion and is recognized as a unit label", () => {
62
+ withModelPreferences(() => {
63
+ assert.ok(KNOWN_UNIT_LABELS.includes("worktree-merge"));
64
+ assert.equal(resolveModelWithFallbacksForUnit("worktree-merge")?.primary, "completion-model");
65
+ });
196
66
  });
197
67
 
198
- // ═══════════════════════════════════════════════════════════════════════════
199
- // auto-post-unit.ts: LIFECYCLE_ONLY_UNITS
200
- // ═══════════════════════════════════════════════════════════════════════════
201
-
202
- test("LIFECYCLE_ONLY_UNITS includes discuss-slice", () => {
203
- assert.ok(postUnitSrc.includes('"discuss-slice"'), "discuss-slice should be lifecycle-only");
68
+ test("every known unit label with a dispatch phase resolves when all model buckets are configured", () => {
69
+ withModelPreferences(() => {
70
+ const missing = KNOWN_UNIT_LABELS.filter((unitType) => !resolveModelWithFallbacksForUnit(unitType));
71
+ assert.deepEqual(missing, []);
72
+ });
204
73
  });
205
74
 
206
- // ═══════════════════════════════════════════════════════════════════════════
207
- // auto-dashboard.ts: display label coverage
208
- // ═══════════════════════════════════════════════════════════════════════════
209
-
210
- test("unitVerb handles discuss-slice", () => {
211
- assert.ok(dashboardSrc.includes('"discuss-slice"'), "missing discuss-slice in dashboard");
75
+ test("discuss-slice has discussion metrics and dashboard labels", () => {
76
+ assert.equal(classifyUnitPhase("discuss-slice"), "discussion");
77
+ assert.equal(unitVerb("discuss-slice"), "discussing");
78
+ assert.equal(unitPhaseLabel("discuss-slice"), "DISCUSS");
212
79
  });
213
80
 
214
- // ═══════════════════════════════════════════════════════════════════════════
215
- // auto-artifact-paths.ts: artifact resolution
216
- // ═══════════════════════════════════════════════════════════════════════════
217
-
218
- // ═══════════════════════════════════════════════════════════════════════════
219
- // ADR-011: meta-test every KNOWN_UNIT_TYPES entry must appear in all four
220
- // downstream registries so a future unit type added to KNOWN_UNIT_TYPES can't
221
- // silently fall through to wrong defaults in metrics/dashboard/artifacts/post-unit.
222
- // ═══════════════════════════════════════════════════════════════════════════
223
-
224
- // Intentional exceptions — unit types that legitimately rely on default/null
225
- // behavior in a specific registry. Entries captured here reflect the current
226
- // baseline at the time ADR-011 landed; adding to this allowlist for a NEW unit
227
- // type requires explicit justification in the commit.
228
- //
229
- // Test intent: catch the case where someone adds a new unit type to
230
- // KNOWN_UNIT_TYPES but forgets to wire it into one of the four registries.
231
- // The allowlist freezes the baseline so pre-existing omissions do not block
232
- // the test, but any brand-new addition must be either handled or justified.
233
- // Deep-mode project-level units (workflow-preferences, discuss-project,
234
- // discuss-requirements, research-decision, research-project) operate above
235
- // the milestone/slice/task model. They do not produce slice-shaped artifacts,
236
- // they do not have per-unit dashboard renderers, and they do not have
237
- // post-unit hooks that mirror execute/complete patterns. They legitimately
238
- // rely on default behavior in all four registries. Adding them to the
239
- // allowlist with this justification.
240
- const DEEP_MODE_PROJECT_UNITS = [
241
- "workflow-preferences",
242
- "discuss-project",
243
- "discuss-requirements",
244
- "research-decision",
245
- "research-project",
246
- ];
247
-
248
- const REGISTRY_EXCEPTIONS: Record<string, Set<string>> = {
249
- // metrics.ts classifyUnitPhase uses default → "execution" for most unit types.
250
- "metrics.ts": new Set([
251
- "worktree-merge", "custom-step",
252
- "rewrite-docs", "run-uat", "gate-evaluate", "replan-slice",
253
- "reactive-execute", "validate-milestone", "complete-milestone",
254
- ...DEEP_MODE_PROJECT_UNITS,
255
- ]),
256
- "auto-dashboard.ts": new Set([
257
- "worktree-merge",
258
- "gate-evaluate", "reactive-execute", "validate-milestone", "complete-milestone",
259
- ...DEEP_MODE_PROJECT_UNITS,
260
- ]),
261
- "auto-artifact-paths.ts": new Set([
262
- "rewrite-docs", "gate-evaluate", "reactive-execute", "discuss-slice", "worktree-merge",
263
- ...DEEP_MODE_PROJECT_UNITS,
264
- ]),
265
- "auto-post-unit.ts": new Set([
266
- "execute-task", "reactive-execute", "gate-evaluate", "worktree-merge",
267
- ...DEEP_MODE_PROJECT_UNITS,
268
- ]),
269
- };
270
-
271
- const REGISTRY_SOURCES: Array<[string, string]> = [
272
- ["metrics.ts", metricsSrc],
273
- ["auto-dashboard.ts", dashboardSrc],
274
- ["auto-artifact-paths.ts", artifactSrc],
275
- ["auto-post-unit.ts", postUnitSrc],
276
- ];
277
-
278
- test("ADR-011 meta: every KNOWN_UNIT_TYPES entry appears in all 4 downstream registries", () => {
279
- const missing: Array<{ registry: string; unitType: string }> = [];
280
- for (const [registry, src] of REGISTRY_SOURCES) {
281
- for (const ut of ALL_KNOWN_UNIT_TYPES) {
282
- if (REGISTRY_EXCEPTIONS[registry]?.has(ut)) continue;
283
- if (!src.includes(`"${ut}"`)) {
284
- missing.push({ registry, unitType: ut });
285
- }
286
- }
81
+ test("discuss-slice resolves to the slice context artifact path", () => {
82
+ const base = mkdtempSync(join(tmpdir(), "gsd-discuss-artifact-"));
83
+ try {
84
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
85
+ const path = resolveExpectedArtifactPath("discuss-slice", "M001/S01", base);
86
+ assert.equal(path, join(realpathSync(base), ".gsd", "milestones", "M001", "slices", "S01", "S01-CONTEXT.md"));
87
+ } finally {
88
+ rmSync(base, { recursive: true, force: true });
287
89
  }
288
- assert.deepEqual(
289
- missing,
290
- [],
291
- "Each listed unit type is absent from the given registry — either add a handler or add to REGISTRY_EXCEPTIONS with justification:\n" +
292
- missing.map((m) => ` ${m.registry}: "${m.unitType}"`).join("\n"),
293
- );
294
- });
295
-
296
- test("resolveExpectedArtifactPath handles discuss-slice", () => {
297
- assert.ok(artifactSrc.includes('"discuss-slice"'), "missing discuss-slice in artifact paths");
298
90
  });
@@ -6,43 +6,50 @@
6
6
  * dispatch blocks completion for needs-remediation while state derives
7
7
  * completing-milestone, creating a permanent deadlock.
8
8
  *
9
- * This structural test verifies the verdict === 'needs-remediation' guard
10
- * exists at all three derivation paths in state.ts.
9
+ * This behavior test verifies DB-backed state derivation does not route a
10
+ * needs-remediation validation verdict into milestone completion.
11
11
  */
12
12
 
13
13
  import { describe, test } from 'node:test';
14
14
  import assert from 'node:assert/strict';
15
- import { readFileSync } from 'node:fs';
16
- import { fileURLToPath } from 'node:url';
17
- import { dirname, join } from 'node:path';
18
-
19
- const __filename = fileURLToPath(import.meta.url);
20
- const __dirname = dirname(__filename);
21
-
22
- const source = readFileSync(join(__dirname, '..', 'state.ts'), 'utf-8');
15
+ import { mkdtempSync, rmSync } from 'node:fs';
16
+ import { join } from 'node:path';
17
+ import { tmpdir } from 'node:os';
18
+ import {
19
+ closeDatabase,
20
+ insertAssessment,
21
+ insertMilestone,
22
+ insertSlice,
23
+ openDatabase,
24
+ } from '../gsd-db.ts';
25
+ import { deriveStateFromDb } from '../state.ts';
23
26
 
24
27
  describe('needs-remediation revalidation guard (#3670)', () => {
25
- test('verdict === needs-remediation guard exists in state.ts', () => {
26
- const matches = source.match(/verdict\s*===\s*['"]needs-remediation['"]/g);
27
- assert.ok(matches, 'verdict === "needs-remediation" check must exist in state.ts');
28
- assert.ok(matches.length >= 2,
29
- `Expected at least 2 needs-remediation guards (deriveStateFromDb + _deriveStateImpl), found ${matches.length}`);
30
- });
28
+ test('needs-remediation assessment blocks completion when every slice is done', async () => {
29
+ const base = mkdtempSync(join(tmpdir(), 'gsd-needs-remediation-'));
30
+ try {
31
+ openDatabase(':memory:');
32
+ insertMilestone({ id: 'M001', title: 'Needs remediation', status: 'active' });
33
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Done slice', status: 'complete' });
34
+ insertAssessment({
35
+ path: join(base, '.gsd', 'milestones', 'M001', 'M001-VALIDATION.md'),
36
+ milestoneId: 'M001',
37
+ status: 'needs-remediation',
38
+ scope: 'milestone-validation',
39
+ fullContent: 'Verdict: needs-remediation',
40
+ });
31
41
 
32
- test('needsRevalidation variable is derived from verdict', () => {
33
- assert.match(source, /needsRevalidation.*=.*verdict\s*===\s*['"]needs-remediation['"]/,
34
- 'needsRevalidation should incorporate verdict === "needs-remediation"');
35
- });
36
-
37
- test('deriveStateFromDb path uses needs-remediation guard', () => {
38
- assert.match(source, /!validationTerminal\s*\|\|\s*verdict\s*===\s*['"]needs-remediation['"]/,
39
- 'deriveStateFromDb should check !validationTerminal || verdict === "needs-remediation"');
40
- });
42
+ const state = await deriveStateFromDb(base);
41
43
 
42
- test('extractVerdict is called on validation content', () => {
43
- const extractCalls = source.match(/extractVerdict\(validationContent\)/g);
44
- assert.ok(extractCalls, 'extractVerdict should be called on validation content');
45
- assert.ok(extractCalls.length >= 2,
46
- `Expected at least 2 extractVerdict calls, found ${extractCalls.length}`);
44
+ assert.equal(state.phase, 'blocked');
45
+ assert.match(state.nextAction, /remediation/i);
46
+ assert.ok(
47
+ state.blockers.some((blocker) => blocker.includes('needs-remediation')),
48
+ 'blocked state explains the remediation verdict',
49
+ );
50
+ } finally {
51
+ closeDatabase();
52
+ rmSync(base, { recursive: true, force: true });
53
+ }
47
54
  });
48
55
  });