gsd-pi 2.66.1-dev.ed243f2 → 2.67.0-dev.43b0159

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 (287) hide show
  1. package/dist/claude-cli-check.d.ts +8 -0
  2. package/dist/claude-cli-check.js +36 -0
  3. package/dist/cli.js +40 -0
  4. package/dist/onboarding.js +19 -2
  5. package/dist/resources/extensions/ask-user-questions.js +79 -11
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +4 -3
  7. package/dist/resources/extensions/claude-code-cli/readiness.js +63 -12
  8. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +10 -3
  9. package/dist/resources/extensions/gsd/auto/loop.js +13 -1
  10. package/dist/resources/extensions/gsd/auto/phases.js +22 -3
  11. package/dist/resources/extensions/gsd/auto/run-unit.js +10 -2
  12. package/dist/resources/extensions/gsd/auto/session.js +1 -1
  13. package/dist/resources/extensions/gsd/auto-dashboard.js +65 -15
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +30 -28
  15. package/dist/resources/extensions/gsd/auto-model-selection.js +12 -3
  16. package/dist/resources/extensions/gsd/auto-prompts.js +173 -25
  17. package/dist/resources/extensions/gsd/auto-recovery.js +11 -12
  18. package/dist/resources/extensions/gsd/auto.js +13 -1
  19. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +32 -1
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +18 -6
  21. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +5 -0
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +59 -5
  23. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +8 -5
  24. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +186 -14
  25. package/dist/resources/extensions/gsd/codebase-generator.js +4 -0
  26. package/dist/resources/extensions/gsd/commands/catalog.js +2 -1
  27. package/dist/resources/extensions/gsd/commands/dispatcher.js +1 -1
  28. package/dist/resources/extensions/gsd/commands/handlers/core.js +94 -4
  29. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +49 -9
  30. package/dist/resources/extensions/gsd/context-store.js +134 -2
  31. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -1
  32. package/dist/resources/extensions/gsd/detection.js +6 -0
  33. package/dist/resources/extensions/gsd/files.js +19 -2
  34. package/dist/resources/extensions/gsd/guided-flow.js +12 -8
  35. package/dist/resources/extensions/gsd/index.js +1 -1
  36. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +2 -0
  37. package/dist/resources/extensions/gsd/parsers-legacy.js +3 -1
  38. package/dist/resources/extensions/gsd/preferences.js +6 -1
  39. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  41. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  42. package/dist/resources/extensions/gsd/prompts/discuss.md +3 -3
  43. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  46. package/dist/resources/extensions/gsd/prompts/rethink.md +6 -2
  47. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  48. package/dist/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  49. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +11 -9
  50. package/dist/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  51. package/dist/resources/extensions/gsd/safety/file-change-validator.js +2 -1
  52. package/dist/resources/extensions/gsd/state.js +2 -1
  53. package/dist/resources/extensions/gsd/visualizer-overlay.js +27 -26
  54. package/dist/resources/extensions/gsd/workflow-reconcile.js +46 -7
  55. package/dist/resources/extensions/remote-questions/manager.js +8 -0
  56. package/dist/resources/extensions/shared/interview-ui.js +10 -0
  57. package/dist/web/standalone/.next/BUILD_ID +1 -1
  58. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  59. package/dist/web/standalone/.next/build-manifest.json +3 -3
  60. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  61. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  63. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  79. package/dist/web/standalone/.next/server/app/index.html +1 -1
  80. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  87. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  90. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  91. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  92. package/dist/web/standalone/.next/static/chunks/{6502.8874bcae249c02e1.js → 6502.b804e48b7919f55e.js} +3 -3
  93. package/dist/web/standalone/.next/static/chunks/{webpack-9fed74684e1c5bb1.js → webpack-65f0501b197d1c49.js} +1 -1
  94. package/package.json +1 -1
  95. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  96. package/packages/pi-ai/dist/providers/anthropic-shared.js +4 -3
  97. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  98. package/packages/pi-ai/dist/utils/json-parse.d.ts.map +1 -1
  99. package/packages/pi-ai/dist/utils/json-parse.js +11 -1
  100. package/packages/pi-ai/dist/utils/json-parse.js.map +1 -1
  101. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  102. package/packages/pi-ai/dist/utils/repair-tool-json.js +60 -1
  103. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  104. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts +2 -0
  105. package/packages/pi-ai/dist/utils/tests/json-parse.test.d.ts.map +1 -0
  106. package/packages/pi-ai/dist/utils/tests/json-parse.test.js +14 -0
  107. package/packages/pi-ai/dist/utils/tests/json-parse.test.js.map +1 -0
  108. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +10 -0
  109. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  110. package/packages/pi-ai/src/providers/anthropic-shared.ts +4 -3
  111. package/packages/pi-ai/src/utils/json-parse.ts +11 -1
  112. package/packages/pi-ai/src/utils/repair-tool-json.ts +69 -1
  113. package/packages/pi-ai/src/utils/tests/json-parse.test.ts +17 -0
  114. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +13 -0
  115. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +3 -0
  116. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  117. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -0
  118. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  119. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +16 -0
  120. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  121. package/packages/pi-coding-agent/dist/core/retry-handler.js +58 -1
  122. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  123. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +58 -0
  124. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/core/sdk.d.ts +3 -0
  126. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  127. package/packages/pi-coding-agent/dist/core/sdk.js +1 -0
  128. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts +2 -0
  130. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.d.ts.map +1 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js +17 -0
  132. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/provider-display-name.test.js.map +1 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +2 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  136. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +1 -0
  137. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  138. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  139. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  140. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +2 -1
  142. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  144. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -1
  145. package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  146. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +2 -2
  147. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  148. package/packages/pi-coding-agent/package.json +1 -1
  149. package/packages/pi-coding-agent/src/core/agent-session.ts +4 -0
  150. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +69 -0
  151. package/packages/pi-coding-agent/src/core/retry-handler.ts +66 -1
  152. package/packages/pi-coding-agent/src/core/sdk.ts +5 -0
  153. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/provider-display-name.test.ts +18 -0
  154. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +2 -1
  155. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +11 -2
  156. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +2 -1
  157. package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -1
  158. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +2 -2
  159. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +13 -0
  160. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  161. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts +2 -0
  162. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.d.ts.map +1 -0
  163. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +35 -0
  164. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -0
  165. package/packages/pi-tui/dist/__tests__/tui.test.d.ts +2 -0
  166. package/packages/pi-tui/dist/__tests__/tui.test.d.ts.map +1 -0
  167. package/packages/pi-tui/dist/__tests__/tui.test.js +43 -0
  168. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -0
  169. package/packages/pi-tui/dist/autocomplete.d.ts.map +1 -1
  170. package/packages/pi-tui/dist/autocomplete.js +9 -7
  171. package/packages/pi-tui/dist/autocomplete.js.map +1 -1
  172. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts +2 -0
  173. package/packages/pi-tui/dist/components/__tests__/editor.test.d.ts.map +1 -0
  174. package/packages/pi-tui/dist/components/__tests__/editor.test.js +54 -0
  175. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -0
  176. package/packages/pi-tui/dist/components/editor.d.ts +3 -1
  177. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  178. package/packages/pi-tui/dist/components/editor.js +14 -3
  179. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  180. package/packages/pi-tui/dist/stdin-buffer.d.ts.map +1 -1
  181. package/packages/pi-tui/dist/stdin-buffer.js +6 -0
  182. package/packages/pi-tui/dist/stdin-buffer.js.map +1 -1
  183. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  184. package/packages/pi-tui/dist/tui.js +8 -0
  185. package/packages/pi-tui/dist/tui.js.map +1 -1
  186. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +15 -0
  187. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +43 -0
  188. package/packages/pi-tui/src/__tests__/tui.test.ts +50 -0
  189. package/packages/pi-tui/src/autocomplete.ts +9 -7
  190. package/packages/pi-tui/src/components/__tests__/editor.test.ts +64 -0
  191. package/packages/pi-tui/src/components/editor.ts +14 -3
  192. package/packages/pi-tui/src/stdin-buffer.ts +7 -0
  193. package/packages/pi-tui/src/tui.ts +9 -0
  194. package/pkg/package.json +1 -1
  195. package/src/resources/extensions/ask-user-questions.ts +103 -11
  196. package/src/resources/extensions/claude-code-cli/partial-builder.ts +4 -3
  197. package/src/resources/extensions/claude-code-cli/readiness.ts +67 -12
  198. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +12 -3
  199. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +17 -0
  200. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +18 -0
  201. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -1
  202. package/src/resources/extensions/gsd/auto/loop.ts +14 -1
  203. package/src/resources/extensions/gsd/auto/phases.ts +27 -4
  204. package/src/resources/extensions/gsd/auto/run-unit.ts +14 -2
  205. package/src/resources/extensions/gsd/auto/session.ts +1 -1
  206. package/src/resources/extensions/gsd/auto-dashboard.ts +76 -16
  207. package/src/resources/extensions/gsd/auto-dispatch.ts +36 -35
  208. package/src/resources/extensions/gsd/auto-model-selection.ts +12 -3
  209. package/src/resources/extensions/gsd/auto-prompts.ts +195 -25
  210. package/src/resources/extensions/gsd/auto-recovery.ts +15 -15
  211. package/src/resources/extensions/gsd/auto.ts +12 -1
  212. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +34 -1
  213. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +27 -6
  214. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +6 -0
  215. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +67 -6
  216. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +11 -8
  217. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +209 -16
  218. package/src/resources/extensions/gsd/codebase-generator.ts +4 -0
  219. package/src/resources/extensions/gsd/commands/catalog.ts +2 -1
  220. package/src/resources/extensions/gsd/commands/dispatcher.ts +1 -2
  221. package/src/resources/extensions/gsd/commands/handlers/core.ts +113 -8
  222. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +49 -11
  223. package/src/resources/extensions/gsd/context-store.ts +167 -2
  224. package/src/resources/extensions/gsd/custom-workflow-engine.ts +3 -1
  225. package/src/resources/extensions/gsd/detection.ts +6 -0
  226. package/src/resources/extensions/gsd/files.ts +21 -2
  227. package/src/resources/extensions/gsd/guided-flow.ts +15 -8
  228. package/src/resources/extensions/gsd/index.ts +6 -0
  229. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +2 -0
  230. package/src/resources/extensions/gsd/parsers-legacy.ts +3 -1
  231. package/src/resources/extensions/gsd/preferences.ts +6 -1
  232. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  233. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -3
  234. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +7 -7
  235. package/src/resources/extensions/gsd/prompts/discuss.md +3 -3
  236. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  237. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -3
  238. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +3 -1
  239. package/src/resources/extensions/gsd/prompts/rethink.md +6 -2
  240. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  241. package/src/resources/extensions/gsd/prompts/triage-captures.md +1 -1
  242. package/src/resources/extensions/gsd/prompts/validate-milestone.md +11 -9
  243. package/src/resources/extensions/gsd/prompts/worktree-merge.md +3 -1
  244. package/src/resources/extensions/gsd/safety/file-change-validator.ts +4 -1
  245. package/src/resources/extensions/gsd/state.ts +2 -1
  246. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +52 -1
  247. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +50 -2
  248. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +21 -7
  249. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +48 -0
  250. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  251. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +12 -0
  252. package/src/resources/extensions/gsd/tests/context-store.test.ts +176 -0
  253. package/src/resources/extensions/gsd/tests/core-overlay-fallback.test.ts +76 -0
  254. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +7 -1
  255. package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +31 -0
  256. package/src/resources/extensions/gsd/tests/decision-scope-cascade.test.ts +370 -0
  257. package/src/resources/extensions/gsd/tests/detection.test.ts +37 -0
  258. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +50 -0
  259. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +35 -0
  260. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +34 -0
  261. package/src/resources/extensions/gsd/tests/health-widget.test.ts +45 -0
  262. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +53 -13
  263. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -2
  264. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +3 -3
  265. package/src/resources/extensions/gsd/tests/measurement.test.ts +531 -0
  266. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +3 -4
  267. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +21 -0
  268. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +71 -2
  269. package/src/resources/extensions/gsd/tests/parsers.test.ts +25 -0
  270. package/src/resources/extensions/gsd/tests/preferences.test.ts +37 -0
  271. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +26 -4
  272. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +60 -0
  273. package/src/resources/extensions/gsd/tests/queue-execution-guard.test.ts +9 -0
  274. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +19 -0
  275. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +73 -0
  276. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +98 -0
  277. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +2 -2
  278. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +26 -0
  279. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +59 -0
  280. package/src/resources/extensions/gsd/tests/workflow-reconcile.test.ts +91 -0
  281. package/src/resources/extensions/gsd/tests/write-gate.test.ts +210 -35
  282. package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -27
  283. package/src/resources/extensions/gsd/workflow-reconcile.ts +59 -8
  284. package/src/resources/extensions/remote-questions/manager.ts +9 -0
  285. package/src/resources/extensions/shared/interview-ui.ts +13 -0
  286. /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → CrKrzIIxk55witDF1eS0L}/_buildManifest.js +0 -0
  287. /package/dist/web/standalone/.next/static/{HAq0VE4k68rhRvJbQL1VW → CrKrzIIxk55witDF1eS0L}/_ssgManifest.js +0 -0
@@ -0,0 +1,531 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import {
4
+ queryKnowledge,
5
+ formatRoadmapExcerpt,
6
+ } from '../context-store.ts';
7
+
8
+ // ═══════════════════════════════════════════════════════════════════════════
9
+ // measurement.test.ts — Verify ≥40% context reduction from scoped injection
10
+ //
11
+ // Tests queryKnowledge() and formatRoadmapExcerpt() with realistic synthetic
12
+ // fixtures to confirm the context reduction target is met.
13
+ // ═══════════════════════════════════════════════════════════════════════════
14
+
15
+ // ─── Synthetic KNOWLEDGE.md Fixture (~8KB, 9 H2 sections) ──────────────────
16
+
17
+ const syntheticKnowledge = `# Project Knowledge Base
18
+
19
+ ## Database Patterns
20
+ SQLite is the primary persistence layer, using WAL mode for concurrent reads.
21
+ All queries use prepared statements for SQL injection prevention.
22
+ Connection pooling is handled by better-sqlite3's synchronous API.
23
+ Schema migrations are versioned and applied at startup.
24
+
25
+ Example patterns:
26
+ - Use transactions for multi-statement operations
27
+ - Prefer RETURNING clause for insert/update
28
+ - Index foreign keys for join performance
29
+ - Use CHECK constraints for data validation
30
+
31
+ Performance considerations:
32
+ - WAL checkpoint every 1000 writes
33
+ - Vacuum on shutdown for space reclamation
34
+ - Page size 4096 for SSD optimization
35
+
36
+ Database schema evolution:
37
+ - Migrations stored in migrations/ directory
38
+ - Each migration has up/down scripts
39
+ - Version table tracks applied migrations
40
+ - Rollback supported for last N migrations
41
+
42
+ Connection management:
43
+ - Single connection for write operations
44
+ - Read connections pooled for concurrency
45
+ - Connection timeout set to 5 seconds
46
+ - Busy timeout handles lock contention
47
+
48
+ Query patterns:
49
+ - Use prepared statements for parameterization
50
+ - Batch inserts via INSERT ... VALUES syntax
51
+ - Upserts via INSERT OR REPLACE
52
+ - Pagination via LIMIT/OFFSET or cursor
53
+
54
+ ## API Design Principles
55
+ REST endpoints follow OpenAPI 3.0 specification.
56
+ Versioned paths use /v1/resource pattern.
57
+ Authentication uses Bearer tokens in Authorization header.
58
+ Rate limiting applies per-client with sliding window algorithm.
59
+
60
+ Response formats:
61
+ - Success: { data: T, meta?: { pagination } }
62
+ - Error: { error: { code, message, details? } }
63
+ - Pagination: cursor-based for large collections
64
+
65
+ Content negotiation:
66
+ - Accept: application/json (default)
67
+ - Accept: text/plain (for CLI consumers)
68
+ - Accept: text/event-stream (for SSE endpoints)
69
+
70
+ API versioning strategy:
71
+ - Major versions in URL path (/v1, /v2)
72
+ - Minor versions via Accept-Version header
73
+ - Deprecation warnings in response headers
74
+ - 12-month sunset period for old versions
75
+
76
+ Endpoint naming conventions:
77
+ - Nouns for resources (users, projects)
78
+ - Verbs only for non-CRUD actions (login, export)
79
+ - Plural form for collections
80
+ - Singular for singletons (me, config)
81
+
82
+ HTTP method semantics:
83
+ - GET: read-only, cacheable
84
+ - POST: create or non-idempotent action
85
+ - PUT: full replacement
86
+ - PATCH: partial update
87
+ - DELETE: remove resource
88
+
89
+ ## Testing Strategy
90
+ Unit tests use node:test with strict assertions.
91
+ Integration tests mock external services via msw.
92
+ E2E tests use Playwright for browser automation.
93
+ Test coverage target is 80% line coverage.
94
+
95
+ Test organization:
96
+ - Unit tests adjacent to source files (*.test.ts)
97
+ - Integration tests in __tests__/integration/
98
+ - E2E tests in e2e/ directory
99
+ - Fixtures in __fixtures__/ subdirectories
100
+
101
+ Mocking guidelines:
102
+ - Prefer dependency injection over global mocks
103
+ - Use vi.mock() sparingly, only for ES module boundaries
104
+ - Reset mocks in afterEach hooks
105
+
106
+ Test data management:
107
+ - Factories generate realistic test data
108
+ - Seeds populate database for integration tests
109
+ - Snapshots capture expected output
110
+ - Golden files for complex comparisons
111
+
112
+ Assertion patterns:
113
+ - Use strict equality for primitives
114
+ - Deep equality for objects/arrays
115
+ - Regex matching for dynamic content
116
+ - Snapshot testing for UI components
117
+
118
+ Test isolation:
119
+ - Each test gets fresh database state
120
+ - Environment variables reset between tests
121
+ - File system operations use temp directories
122
+ - Network calls intercepted by mock server
123
+
124
+ ## Error Handling
125
+ Errors are typed using discriminated unions.
126
+ Application errors extend BaseError class.
127
+ HTTP errors map to standard status codes.
128
+ Unhandled rejections trigger graceful shutdown.
129
+
130
+ Error codes follow domain prefixes:
131
+ - AUTH_xxx: Authentication/authorization errors
132
+ - DB_xxx: Database operation failures
133
+ - NET_xxx: Network/external service errors
134
+ - VAL_xxx: Validation errors
135
+
136
+ Logging integration:
137
+ - Error instances auto-serialize to JSON
138
+ - Stack traces included in development
139
+ - Correlation IDs propagate through request chain
140
+
141
+ Error recovery strategies:
142
+ - Retry with exponential backoff for transient errors
143
+ - Circuit breaker for external service failures
144
+ - Fallback values for non-critical operations
145
+ - Graceful degradation for partial failures
146
+
147
+ User-facing error messages:
148
+ - Generic messages for security-sensitive errors
149
+ - Actionable guidance for recoverable errors
150
+ - Reference codes for support escalation
151
+ - Localized messages via i18n
152
+
153
+ Error boundary patterns:
154
+ - Component-level boundaries in UI
155
+ - Route-level error handlers in API
156
+ - Global unhandled rejection handlers
157
+ - Process-level crash recovery
158
+
159
+ ## Observability Patterns
160
+ Structured logging uses pino with JSON output.
161
+ Metrics collected via OpenTelemetry SDK.
162
+ Traces propagate context through async boundaries.
163
+ Health checks exposed at /health and /ready endpoints.
164
+
165
+ Log levels:
166
+ - ERROR: Unrecoverable failures
167
+ - WARN: Degraded operation
168
+ - INFO: Significant state changes
169
+ - DEBUG: Detailed diagnostic data
170
+
171
+ Metric types:
172
+ - Counters for request counts
173
+ - Histograms for latency distribution
174
+ - Gauges for resource utilization
175
+
176
+ Trace context propagation:
177
+ - W3C Trace Context headers
178
+ - Baggage for cross-service metadata
179
+ - Span attributes for searchability
180
+ - Events for significant moments
181
+
182
+ Dashboard design:
183
+ - SLO dashboards for reliability
184
+ - Request flow visualization
185
+ - Error rate trends
186
+ - Resource saturation alerts
187
+
188
+ Alerting strategy:
189
+ - Page for customer-impacting issues
190
+ - Ticket for degraded performance
191
+ - Notification for capacity planning
192
+ - Silence during maintenance windows
193
+
194
+ ## Security Guidelines
195
+ Secrets never appear in logs or error messages.
196
+ Environment variables validated at startup.
197
+ CORS configured per-environment whitelist.
198
+ CSP headers enforced for web responses.
199
+
200
+ Input validation:
201
+ - Zod schemas for request body parsing
202
+ - Path parameters validated against patterns
203
+ - Query parameters have default/max values
204
+
205
+ Output encoding:
206
+ - HTML entities escaped in templates
207
+ - JSON stringification for API responses
208
+ - URL encoding for redirect targets
209
+
210
+ Authentication patterns:
211
+ - JWT tokens with short expiry
212
+ - Refresh token rotation
213
+ - Session invalidation on logout
214
+ - Multi-factor authentication support
215
+
216
+ Authorization model:
217
+ - Role-based access control (RBAC)
218
+ - Resource-level permissions
219
+ - Attribute-based policies (ABAC)
220
+ - Principle of least privilege
221
+
222
+ Secure communication:
223
+ - TLS 1.3 minimum
224
+ - Certificate pinning for mobile
225
+ - HSTS preload list
226
+ - Certificate transparency logging
227
+
228
+ ## Performance Optimization
229
+ Critical paths target sub-10ms latency.
230
+ Database queries use covering indexes.
231
+ Response compression enabled for > 1KB bodies.
232
+ Static assets served with immutable caching.
233
+
234
+ Caching strategy:
235
+ - Redis for session data
236
+ - In-memory LRU for hot paths
237
+ - CDN for static assets
238
+ - Stale-while-revalidate for API responses
239
+
240
+ Memory management:
241
+ - Stream large payloads instead of buffering
242
+ - Weak references for disposable caches
243
+ - Manual GC hints for batch operations
244
+
245
+ Query optimization:
246
+ - Explain plans for complex queries
247
+ - Index usage analysis
248
+ - Query result caching
249
+ - Connection pooling tuning
250
+
251
+ Frontend performance:
252
+ - Code splitting for lazy loading
253
+ - Image optimization and lazy loading
254
+ - Critical CSS inlining
255
+ - Prefetching for likely navigations
256
+
257
+ Backend performance:
258
+ - Async I/O for non-blocking operations
259
+ - Worker threads for CPU-bound tasks
260
+ - Connection keep-alive
261
+ - Response streaming
262
+
263
+ ## Deployment Architecture
264
+ Containers built with multi-stage Dockerfiles.
265
+ Kubernetes manifests in deploy/ directory.
266
+ Horizontal pod autoscaling on CPU/memory.
267
+ Rolling updates with zero-downtime.
268
+
269
+ Environment hierarchy:
270
+ - development: local Docker Compose
271
+ - staging: shared k8s namespace
272
+ - production: isolated k8s cluster
273
+
274
+ Configuration:
275
+ - ConfigMaps for non-sensitive config
276
+ - Secrets for credentials
277
+ - Environment-specific overlays via Kustomize
278
+
279
+ Container best practices:
280
+ - Non-root user in container
281
+ - Read-only filesystem where possible
282
+ - Resource limits and requests
283
+ - Liveness and readiness probes
284
+
285
+ Service mesh integration:
286
+ - Istio for traffic management
287
+ - mTLS for service-to-service auth
288
+ - Retry and timeout policies
289
+ - Circuit breaking configuration
290
+
291
+ Disaster recovery:
292
+ - Database replication across zones
293
+ - Point-in-time recovery capability
294
+ - Regular backup verification
295
+ - Documented runbooks
296
+
297
+ ## Development Workflow
298
+ Feature branches follow conventional commits.
299
+ PRs require CI pass and code review.
300
+ Main branch deploys to staging automatically.
301
+ Release tags trigger production deployment.
302
+
303
+ CI pipeline stages:
304
+ 1. Install dependencies
305
+ 2. Lint and type check
306
+ 3. Unit tests with coverage
307
+ 4. Build artifacts
308
+ 5. Integration tests
309
+ 6. Security scan
310
+
311
+ Local development:
312
+ - pnpm for package management
313
+ - Turborepo for monorepo orchestration
314
+ - Docker Compose for service dependencies
315
+
316
+ Code review guidelines:
317
+ - Focus on correctness and clarity
318
+ - Security-sensitive changes require security review
319
+ - Performance-critical paths need benchmarks
320
+ - Breaking changes need migration guide
321
+
322
+ Branch strategy:
323
+ - main: production-ready code
324
+ - develop: integration branch (optional)
325
+ - feature/*: new functionality
326
+ - fix/*: bug fixes
327
+ - release/*: release preparation
328
+
329
+ Documentation requirements:
330
+ - README for project overview
331
+ - API docs auto-generated from OpenAPI
332
+ - Architecture decision records (ADRs)
333
+ - Runbooks for operational procedures
334
+ `;
335
+
336
+ // ─── Synthetic Roadmap Fixture (~1KB, 4 slices) ────────────────────────────
337
+
338
+ const syntheticRoadmap = `# M005: Tiered Context Injection
339
+
340
+ ## Vision
341
+ Refactor prompt builders to inject relevance-scoped context instead of full files.
342
+ This reduces token consumption and improves agent focus on relevant information.
343
+
344
+ ## Success Criteria
345
+ - [ ] 40% reduction in injected context size
346
+ - [ ] No regression in agent task completion rate
347
+ - [ ] Measurable test confirms reduction target
348
+
349
+ ## Slice Overview
350
+ | ID | Slice | Risk | Depends | Done | After this |
351
+ |----|-------|------|---------|------|------------|
352
+ | S01 | Scope existing DB queries | low | — | ✅ | planSlice and researchSlice use milestone+slice filters for decisions/requirements. |
353
+ | S02 | KNOWLEDGE scoping + roadmap excerpt | medium | S01 | ⬜ | KNOWLEDGE sections filtered by keywords. Roadmap injected as excerpt. |
354
+ | S03 | Measurement test suite | low | S02 | ⬜ | Automated tests confirm 40% reduction vs baseline. |
355
+ | S04 | Documentation and rollout | low | S03 | ⬜ | Updated docs. Feature flag for gradual rollout. |
356
+
357
+ ## Key Risks
358
+ 1. Keyword extraction may miss relevant sections — mitigate with fallback to full content
359
+ 2. Excerpt parsing fragile to roadmap format changes — mitigate with graceful degradation
360
+
361
+ ## Definition of Done
362
+ - [ ] All slices complete with passing verification
363
+ - [ ] Measurement tests in CI
364
+ - [ ] No increase in prompt build latency
365
+ `;
366
+
367
+ // ═══════════════════════════════════════════════════════════════════════════
368
+ // Measurement Tests
369
+ // ═══════════════════════════════════════════════════════════════════════════
370
+
371
+ describe("measurement: context reduction verification", () => {
372
+ test("synthetic KNOWLEDGE fixture is ~8KB as specified", () => {
373
+ const sizeKB = syntheticKnowledge.length / 1024;
374
+ assert.ok(
375
+ sizeKB >= 7 && sizeKB <= 10,
376
+ `KNOWLEDGE fixture should be ~8KB, got ${sizeKB.toFixed(2)}KB`
377
+ );
378
+ });
379
+
380
+ test("synthetic KNOWLEDGE has 9 H2 sections", () => {
381
+ const h2Count = (syntheticKnowledge.match(/^## /gm) || []).length;
382
+ assert.strictEqual(h2Count, 9, `KNOWLEDGE fixture should have 9 H2 sections, got ${h2Count}`);
383
+ });
384
+
385
+ test("queryKnowledge achieves ≥40% reduction with targeted keywords", async () => {
386
+ // Keywords targeting 2 sections: "Database Patterns" and "Testing Strategy"
387
+ const keywords = ['database', 'testing'];
388
+
389
+ const scopedResult = await queryKnowledge(syntheticKnowledge, keywords);
390
+
391
+ const fullSize = syntheticKnowledge.length;
392
+ const scopedSize = scopedResult.length;
393
+ const reductionPct = ((fullSize - scopedSize) / fullSize) * 100;
394
+
395
+ // Verify we got matching sections
396
+ assert.match(scopedResult, /## Database Patterns/, 'should include Database section');
397
+ assert.match(scopedResult, /## Testing Strategy/, 'should include Testing section');
398
+
399
+ // Verify we excluded other sections
400
+ assert.ok(!scopedResult.includes('## API Design'), 'should exclude API section');
401
+ assert.ok(!scopedResult.includes('## Observability'), 'should exclude Observability section');
402
+ assert.ok(!scopedResult.includes('## Deployment'), 'should exclude Deployment section');
403
+
404
+ // Verify ≥40% reduction (2/9 sections = ~78% reduction expected)
405
+ assert.ok(
406
+ reductionPct >= 40,
407
+ `queryKnowledge should achieve ≥40% reduction, got ${reductionPct.toFixed(1)}% (${scopedSize} chars vs ${fullSize} chars)`
408
+ );
409
+
410
+ console.log(` → queryKnowledge: ${reductionPct.toFixed(1)}% reduction (${scopedSize} → ${fullSize} chars)`);
411
+ });
412
+
413
+ test("queryKnowledge with single keyword achieves ≥40% reduction", async () => {
414
+ // Single keyword targeting 1 section
415
+ const keywords = ['security'];
416
+
417
+ const scopedResult = await queryKnowledge(syntheticKnowledge, keywords);
418
+
419
+ const fullSize = syntheticKnowledge.length;
420
+ const scopedSize = scopedResult.length;
421
+ const reductionPct = ((fullSize - scopedSize) / fullSize) * 100;
422
+
423
+ // Verify we got matching section
424
+ assert.match(scopedResult, /## Security Guidelines/, 'should include Security section');
425
+
426
+ // Verify ≥40% reduction (1/9 sections = ~89% reduction expected)
427
+ assert.ok(
428
+ reductionPct >= 40,
429
+ `single keyword should achieve ≥40% reduction, got ${reductionPct.toFixed(1)}%`
430
+ );
431
+ });
432
+
433
+ test("formatRoadmapExcerpt achieves ≥40% reduction", () => {
434
+ const sliceId = 'S02';
435
+
436
+ const excerptResult = formatRoadmapExcerpt(syntheticRoadmap, sliceId, '.gsd/milestones/M005/M005-ROADMAP.md');
437
+
438
+ const fullSize = syntheticRoadmap.length;
439
+ const excerptSize = excerptResult.length;
440
+ const reductionPct = ((fullSize - excerptSize) / fullSize) * 100;
441
+
442
+ // Verify excerpt contains required elements
443
+ assert.match(excerptResult, /\| ID \| Slice \|/, 'should have table header');
444
+ assert.match(excerptResult, /\| S01 \|/, 'should have predecessor S01');
445
+ assert.match(excerptResult, /\| S02 \|/, 'should have target S02');
446
+ assert.match(excerptResult, /See full roadmap:/, 'should have reference directive');
447
+
448
+ // Verify we excluded other slices
449
+ assert.ok(!excerptResult.includes('| S03 |'), 'should exclude S03');
450
+ assert.ok(!excerptResult.includes('| S04 |'), 'should exclude S04');
451
+
452
+ // Verify ≥40% reduction (2 rows + overhead vs full roadmap = significant reduction)
453
+ assert.ok(
454
+ reductionPct >= 40,
455
+ `formatRoadmapExcerpt should achieve ≥40% reduction, got ${reductionPct.toFixed(1)}% (${excerptSize} chars vs ${fullSize} chars)`
456
+ );
457
+
458
+ console.log(` → formatRoadmapExcerpt: ${reductionPct.toFixed(1)}% reduction (${excerptSize} → ${fullSize} chars)`);
459
+ });
460
+
461
+ test("combined KNOWLEDGE + roadmap reduction exceeds 40%", async () => {
462
+ // Simulate what happens in buildPlanSlicePrompt
463
+ const keywords = ['database', 'testing'];
464
+
465
+ const scopedKnowledge = await queryKnowledge(syntheticKnowledge, keywords);
466
+ const scopedRoadmap = formatRoadmapExcerpt(syntheticRoadmap, 'S02');
467
+
468
+ const fullKnowledgeSize = syntheticKnowledge.length;
469
+ const fullRoadmapSize = syntheticRoadmap.length;
470
+ const fullTotal = fullKnowledgeSize + fullRoadmapSize;
471
+
472
+ const scopedKnowledgeSize = scopedKnowledge.length;
473
+ const scopedRoadmapSize = scopedRoadmap.length;
474
+ const scopedTotal = scopedKnowledgeSize + scopedRoadmapSize;
475
+
476
+ const combinedReductionPct = ((fullTotal - scopedTotal) / fullTotal) * 100;
477
+
478
+ // Combined reduction should easily exceed 40%
479
+ assert.ok(
480
+ combinedReductionPct >= 40,
481
+ `combined reduction should be ≥40%, got ${combinedReductionPct.toFixed(1)}%`
482
+ );
483
+
484
+ console.log(` → Combined: ${combinedReductionPct.toFixed(1)}% reduction`);
485
+ console.log(` - KNOWLEDGE: ${fullKnowledgeSize} → ${scopedKnowledgeSize} chars`);
486
+ console.log(` - Roadmap: ${fullRoadmapSize} → ${scopedRoadmapSize} chars`);
487
+ console.log(` - Total: ${fullTotal} → ${scopedTotal} chars`);
488
+ });
489
+ });
490
+
491
+ describe("measurement: edge cases maintain reduction target", () => {
492
+ test("three keywords still achieves ≥40% reduction", async () => {
493
+ // Even with 3 matching sections (3/9 = 33%), we should hit target
494
+ const keywords = ['database', 'api', 'security'];
495
+
496
+ const scopedResult = await queryKnowledge(syntheticKnowledge, keywords);
497
+
498
+ const fullSize = syntheticKnowledge.length;
499
+ const scopedSize = scopedResult.length;
500
+ const reductionPct = ((fullSize - scopedSize) / fullSize) * 100;
501
+
502
+ // Verify matches (3 sections)
503
+ assert.match(scopedResult, /## Database Patterns/, 'should include Database');
504
+ assert.match(scopedResult, /## API Design/, 'should include API');
505
+ assert.match(scopedResult, /## Security Guidelines/, 'should include Security');
506
+
507
+ // With 3/9 sections, reduction should be ~67%
508
+ assert.ok(
509
+ reductionPct >= 40,
510
+ `3 keywords should still achieve ≥40% reduction, got ${reductionPct.toFixed(1)}%`
511
+ );
512
+ });
513
+
514
+ test("excerpt for S01 (no dependencies) achieves ≥40% reduction", () => {
515
+ const excerptResult = formatRoadmapExcerpt(syntheticRoadmap, 'S01');
516
+
517
+ const fullSize = syntheticRoadmap.length;
518
+ const excerptSize = excerptResult.length;
519
+ const reductionPct = ((fullSize - excerptSize) / fullSize) * 100;
520
+
521
+ // S01 has no predecessor, so just 1 row + header + reference
522
+ assert.match(excerptResult, /\| S01 \|/, 'should have S01');
523
+ assert.ok(!excerptResult.includes('| S02 |'), 'should not have S02');
524
+
525
+ // Single row should still achieve significant reduction
526
+ assert.ok(
527
+ reductionPct >= 40,
528
+ `S01 excerpt should achieve ≥40% reduction, got ${reductionPct.toFixed(1)}%`
529
+ );
530
+ });
531
+ });
@@ -273,9 +273,9 @@ test('Scenario 2: Fully complete project — deriveState phase', async () => {
273
273
  invalidateAllCaches();
274
274
  const state = await deriveState(base);
275
275
  assert.deepStrictEqual(state.phase, 'complete', 'complete: deriveState phase is complete (validation + summary written by migration)');
276
- // When all milestones are complete, activeMilestone points to the last entry (for display)
277
- assert.ok(state.activeMilestone !== null, 'complete: deriveState has activeMilestone (last entry)');
278
- assert.deepStrictEqual(state.activeMilestone!.id, 'M001', 'complete: deriveState activeMilestone is M001');
276
+ assert.equal(state.activeMilestone, null, 'complete: deriveState has no activeMilestone');
277
+ assert.ok(state.lastCompletedMilestone !== null, 'complete: deriveState exposes lastCompletedMilestone');
278
+ assert.deepStrictEqual(state.lastCompletedMilestone!.id, 'M001', 'complete: deriveState lastCompletedMilestone is M001');
279
279
 
280
280
  // generatePreview for complete project
281
281
  const preview = generatePreview(project);
@@ -292,4 +292,3 @@ test('Scenario 2: Fully complete project — deriveState phase', async () => {
292
292
  rmSync(base, { recursive: true, force: true });
293
293
  }
294
294
  });
295
-
@@ -57,4 +57,25 @@ describe("parallel-monitor-overlay", () => {
57
57
  assert.ok(closed, "pressing q should trigger onClose");
58
58
  overlay2.dispose();
59
59
  });
60
+
61
+ it("ParallelMonitorOverlay clamps scrollOffset during render", async () => {
62
+ const mod = await import("../parallel-monitor-overlay.js");
63
+
64
+ const mockTui = { requestRender: () => {} };
65
+ const mockTheme = {
66
+ fg: (_color: string, text: string) => text,
67
+ bold: (text: string) => text,
68
+ };
69
+ const overlay = new mod.ParallelMonitorOverlay(
70
+ mockTui,
71
+ mockTheme as any,
72
+ () => {},
73
+ "/nonexistent/path",
74
+ );
75
+
76
+ (overlay as any).scrollOffset = 999;
77
+ overlay.render(80);
78
+ assert.equal((overlay as any).scrollOffset, 0, "empty overlays clamp scroll to zero");
79
+ overlay.dispose();
80
+ });
60
81
  });
@@ -4,12 +4,15 @@
4
4
  * Verifies the dispatch rule and prompt builder exist with correct structure.
5
5
  */
6
6
 
7
- import test from "node:test";
7
+ import test, { afterEach } from "node:test";
8
8
  import assert from "node:assert/strict";
9
- import { readFileSync } from "node:fs";
9
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { join, dirname } from "node:path";
11
+ import { tmpdir } from "node:os";
11
12
  import { fileURLToPath } from "node:url";
12
13
 
14
+ import { resolveDispatch } from "../auto-dispatch.ts";
15
+
13
16
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
17
 
15
18
  const dispatchSrc = readFileSync(join(__dirname, "..", "auto-dispatch.ts"), "utf-8");
@@ -17,6 +20,47 @@ const promptsSrc = readFileSync(join(__dirname, "..", "auto-prompts.ts"), "utf-8
17
20
  const templatePath = join(__dirname, "..", "prompts", "parallel-research-slices.md");
18
21
  const templateSrc = readFileSync(templatePath, "utf-8");
19
22
 
23
+ const tmpDirs: string[] = [];
24
+
25
+ function makeTmpProject(): string {
26
+ const base = mkdtempSync(join(tmpdir(), "parallel-research-"));
27
+ tmpDirs.push(base);
28
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
29
+ mkdirSync(milestoneDir, { recursive: true });
30
+ writeFileSync(
31
+ join(milestoneDir, "M001-ROADMAP.md"),
32
+ [
33
+ "# M001: Parallel Research Milestone",
34
+ "",
35
+ "**Vision:** Research-ready slices.",
36
+ "",
37
+ "**Success Criteria:**",
38
+ "- Research both slices",
39
+ "",
40
+ "## Slices",
41
+ "",
42
+ "- [ ] **S01: Alpha** `risk:low` `depends:[]`",
43
+ "- [ ] **S02: Beta** `risk:low` `depends:[]`",
44
+ "",
45
+ "## Boundary Map",
46
+ "",
47
+ ].join("\n"),
48
+ "utf-8",
49
+ );
50
+ return base;
51
+ }
52
+
53
+ afterEach(() => {
54
+ for (const dir of tmpDirs) {
55
+ try {
56
+ rmSync(dir, { recursive: true, force: true });
57
+ } catch {
58
+ // Best-effort cleanup only.
59
+ }
60
+ }
61
+ tmpDirs.length = 0;
62
+ });
63
+
20
64
  // ─── Dispatch rule ────────────────────────────────────────────────────────
21
65
 
22
66
  test("dispatch: parallel-research-slices rule exists", () => {
@@ -75,3 +119,28 @@ test("template: validate-milestone uses parallel reviewers", () => {
75
119
  "validate-milestone should dispatch 3 parallel reviewers",
76
120
  );
77
121
  });
122
+
123
+ test("resolveDispatch prefers parallel research when multiple slices are ready", async () => {
124
+ const base = makeTmpProject();
125
+
126
+ const action = await resolveDispatch({
127
+ basePath: base,
128
+ mid: "M001",
129
+ midTitle: "Parallel Research Milestone",
130
+ state: {
131
+ phase: "planning",
132
+ activeMilestone: { id: "M001", title: "Parallel Research Milestone", status: "active" },
133
+ activeSlice: { id: "S01", title: "Alpha" },
134
+ activeTask: null,
135
+ registry: [],
136
+ blockers: [],
137
+ } as any,
138
+ prefs: undefined,
139
+ });
140
+
141
+ assert.equal(action.action, "dispatch");
142
+ if (action.action === "dispatch") {
143
+ assert.equal(action.unitType, "research-slice");
144
+ assert.equal(action.unitId, "M001/parallel-research");
145
+ }
146
+ });