gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.d0517ff

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 (316) hide show
  1. package/dist/mcp-server.js +6 -2
  2. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  3. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  4. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  5. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  7. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  9. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  10. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  11. package/dist/resources/extensions/gsd/auto-start.js +42 -11
  12. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  13. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  14. package/dist/resources/extensions/gsd/auto.js +21 -15
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  17. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -3
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +31 -1
  21. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  22. package/dist/resources/extensions/gsd/commands/handlers/core.js +20 -0
  23. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  24. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  25. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  26. package/dist/resources/extensions/gsd/detection.js +1 -1
  27. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  28. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  29. package/dist/resources/extensions/gsd/doctor.js +2 -1
  30. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  31. package/dist/resources/extensions/gsd/gsd-db.js +47 -4
  32. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  33. package/dist/resources/extensions/gsd/index.js +1 -1
  34. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  35. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  37. package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -5
  38. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  39. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  40. package/dist/resources/extensions/gsd/preferences.js +9 -2
  41. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  42. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  43. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  46. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  47. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  48. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  49. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  50. package/dist/resources/extensions/gsd/prompts/queue.md +2 -0
  51. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  52. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  53. package/dist/resources/extensions/gsd/quick.js +19 -15
  54. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  55. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  56. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  57. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  58. package/dist/resources/extensions/gsd/state.js +115 -28
  59. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  60. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  61. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  63. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  64. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  65. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  66. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  67. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  68. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  69. package/dist/resources/extensions/gsd/undo.js +3 -2
  70. package/dist/resources/extensions/gsd/workflow-events.js +1 -0
  71. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  72. package/dist/resources/extensions/gsd/workflow-projections.js +7 -9
  73. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  74. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  75. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  76. package/dist/resources/extensions/gsd/worktree.js +9 -0
  77. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  78. package/dist/web/standalone/.next/BUILD_ID +1 -1
  79. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  80. package/dist/web/standalone/.next/build-manifest.json +3 -3
  81. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  82. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  84. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.html +1 -1
  100. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  107. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  110. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  111. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  112. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  113. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  114. package/package.json +1 -1
  115. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  117. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  119. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  120. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  121. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  127. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  128. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  129. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  131. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  132. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  133. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  134. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  135. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  136. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  137. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  138. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  139. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  140. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  142. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  144. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  145. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  146. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  147. package/packages/pi-tui/dist/components/image.js +4 -0
  148. package/packages/pi-tui/dist/components/image.js.map +1 -1
  149. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  150. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  151. package/packages/pi-tui/dist/components/image.test.js +32 -0
  152. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  153. package/packages/pi-tui/src/components/image.test.ts +36 -0
  154. package/packages/pi-tui/src/components/image.ts +5 -0
  155. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  156. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  157. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  158. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  159. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  160. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  161. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  162. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  163. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  164. package/src/resources/extensions/gsd/auto-start.ts +45 -10
  165. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  166. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  167. package/src/resources/extensions/gsd/auto.ts +19 -8
  168. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  169. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  170. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  171. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  172. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -3
  173. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +36 -1
  174. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  175. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -0
  176. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  177. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  178. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  179. package/src/resources/extensions/gsd/detection.ts +1 -1
  180. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  181. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  182. package/src/resources/extensions/gsd/doctor.ts +2 -1
  183. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  184. package/src/resources/extensions/gsd/gsd-db.ts +46 -4
  185. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  186. package/src/resources/extensions/gsd/index.ts +1 -0
  187. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  188. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  189. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  190. package/src/resources/extensions/gsd/pre-execution-checks.ts +15 -7
  191. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  192. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  193. package/src/resources/extensions/gsd/preferences.ts +9 -2
  194. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  195. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  196. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  197. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  198. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  199. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  200. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +6 -1
  201. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  202. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  203. package/src/resources/extensions/gsd/prompts/queue.md +2 -0
  204. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  205. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  206. package/src/resources/extensions/gsd/quick.ts +20 -15
  207. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  208. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  209. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  210. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  211. package/src/resources/extensions/gsd/state.ts +115 -26
  212. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  213. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  214. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  215. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  216. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  217. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  218. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  219. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  220. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  221. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  222. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  223. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  224. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  225. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  226. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  227. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  228. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  229. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  230. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  231. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  232. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  233. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  234. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  235. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  236. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  237. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  238. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  239. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  240. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  241. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  242. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  243. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +218 -20
  244. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  245. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  246. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  247. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  248. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  249. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  250. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  251. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  252. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  253. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  254. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  255. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  256. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  257. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  258. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  259. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  260. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  261. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  262. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  263. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  264. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  265. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  266. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  267. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  268. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  269. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  270. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  271. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  272. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  273. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  274. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  275. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  276. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  277. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  278. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  279. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  280. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  281. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  282. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  283. package/src/resources/extensions/gsd/tests/wave5-consistency-regressions.test.ts +165 -0
  284. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  285. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  286. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  287. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  288. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  289. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  290. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  291. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  292. package/src/resources/extensions/gsd/tests/write-gate.test.ts +127 -2
  293. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  294. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  295. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  296. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  297. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  298. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  299. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  300. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  301. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  302. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  303. package/src/resources/extensions/gsd/types.ts +4 -0
  304. package/src/resources/extensions/gsd/undo.ts +3 -2
  305. package/src/resources/extensions/gsd/workflow-events.ts +5 -3
  306. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  307. package/src/resources/extensions/gsd/workflow-projections.ts +7 -8
  308. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  309. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  310. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  311. package/src/resources/extensions/gsd/worktree.ts +10 -0
  312. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  313. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  314. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  315. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_buildManifest.js +0 -0
  316. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → JwdBI3y1H8vtBKiYvWfEK}/_ssgManifest.js +0 -0
@@ -11,6 +11,8 @@
11
11
 
12
12
  // GSD State Machine Live Validation (#3161)
13
13
 
14
+
15
+
14
16
  import { describe, test, beforeEach, afterEach } from "node:test";
15
17
  import assert from "node:assert/strict";
16
18
  import {
@@ -721,10 +723,10 @@ describe("state-machine-live-validation", () => {
721
723
  const result = await handleCompleteTask(makeTaskParams("T01", "S99", "M099") as any, base);
722
724
  assert.ok(!("error" in result), `expected success: ${JSON.stringify(result)}`);
723
725
 
724
- // Phantom milestone created
726
+ // Phantom milestone created — H6 fix: now uses ID as title instead of empty string
725
727
  const milestone = getMilestone("M099");
726
728
  assert.ok(milestone, "phantom milestone M099 should exist");
727
- assert.equal(milestone!.title, "", "phantom milestone has empty title");
729
+ assert.equal(milestone!.title, "M099", "H6 fix: phantom milestone uses ID as title");
728
730
 
729
731
  // Phantom slice created
730
732
  const slice = getSlice("M099", "S99");
@@ -895,13 +897,10 @@ describe("state-machine-live-validation", () => {
895
897
  // ─────────────────────────────────────────────────────────────────────────
896
898
 
897
899
  describe("reopen-then-redo cycle", () => {
898
- test("complete → reopen → M12: stale SUMMARY causes immediate auto-reconcile", async () => {
899
- // Finding M12: reopen-task does NOT delete the SUMMARY.md from disk.
900
- // The reopen handler's own post-mutation hook calls renderAllProjections
901
- // which triggers deriveStateFromDb, which sees the stale SUMMARY.md and
902
- // auto-reconciles the task BACK to "complete" (#2514) within the same call.
903
- //
904
- // Result: the reopen is effectively a no-op when filesystem artifacts exist.
900
+ test("complete → reopen → re-complete task works end-to-end (M12 fixed)", async () => {
901
+ // M12 fix: reopen-task now deletes SUMMARY.md from disk before the
902
+ // post-mutation hook runs, preventing the reconciler from auto-correcting
903
+ // the task back to "complete".
905
904
  base = createFullFixture();
906
905
  openDatabase(join(base, ".gsd", "gsd.db"));
907
906
  insertMilestone({ id: "M001", title: "Active", status: "active" });
@@ -915,23 +914,23 @@ describe("state-machine-live-validation", () => {
915
914
  const summaryPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks", "T01-SUMMARY.md");
916
915
  assert.ok(existsSync(summaryPath), "SUMMARY.md exists after completion");
917
916
 
918
- // Reopen — handler sets DB to "pending" in transaction, but post-mutation
919
- // hook triggers reconciler which immediately sets it back to "complete"
917
+ // Reopen — now deletes SUMMARY.md from disk (M12 fix)
920
918
  const r2 = await handleReopenTask({ milestoneId: "M001", sliceId: "S01", taskId: "T01" }, base);
921
- assert.ok(!("error" in r2), `reopen handler succeeded: ${JSON.stringify(r2)}`);
919
+ assert.ok(!("error" in r2), `reopen: ${JSON.stringify(r2)}`);
922
920
 
923
- // M12: After reopen completes, DB shows "complete" not "pending" because
924
- // the reconciler auto-corrected it from the stale SUMMARY.md
925
- const task = getTask("M001", "S01", "T01");
926
- assert.equal(task!.status, "complete", "M12: reconciler overrides reopen — task is back to complete");
927
- assert.ok(existsSync(summaryPath), "M12: SUMMARY.md was never cleaned up");
921
+ // Task is now properly pending SUMMARY.md was cleaned up
922
+ assert.equal(getTask("M001", "S01", "T01")!.status, "pending");
923
+ assert.ok(!existsSync(summaryPath), "M12 fix: SUMMARY.md cleaned up by reopen");
924
+
925
+ // Re-complete succeeds
926
+ const r3 = await handleCompleteTask(makeTaskParams("T01", "S01", "M001") as any, base);
927
+ assert.ok(!("error" in r3), `re-complete: ${JSON.stringify(r3)}`);
928
+ assert.ok(isClosedStatus(getTask("M001", "S01", "T01")!.status));
928
929
  });
929
930
 
930
- test("complete slice → reopen → M12: reconciler overrides task reset via stale SUMMARY", async () => {
931
- // Same M12 pattern at the slice level: reopen-slice resets all tasks to
932
- // "pending" in DB, but task SUMMARY.md artifacts remain on disk. The
933
- // reopen handler's post-mutation hook triggers reconciler which sees the
934
- // stale artifacts and auto-corrects tasks back to "complete".
931
+ test("complete slice → reopen → re-complete all works end-to-end (M12 fixed)", async () => {
932
+ // M12 fix: reopen-slice now deletes all SUMMARY.md and UAT.md artifacts
933
+ // from disk, preventing reconciler interference.
935
934
  base = createFullFixture();
936
935
  openDatabase(join(base, ".gsd", "gsd.db"));
937
936
  insertMilestone({ id: "M001", title: "Active", status: "active" });
@@ -943,17 +942,16 @@ describe("state-machine-live-validation", () => {
943
942
  await handleCompleteSlice(makeSliceParams("S01", "M001") as any, base);
944
943
  assert.ok(isClosedStatus(getSlice("M001", "S01")!.status));
945
944
 
946
- // Reopen slice — transaction resets slice to in_progress and task to pending,
947
- // but post-mutation hook triggers reconciler which sees stale SUMMARY.md
945
+ // Reopen slice — now cleans up all artifacts (M12 fix)
948
946
  await handleReopenSlice({ milestoneId: "M001", sliceId: "S01" }, base);
949
-
950
- // Slice status is correctly in_progress (no slice SUMMARY reconciliation)
951
947
  assert.equal(getSlice("M001", "S01")!.status, "in_progress");
948
+ assert.equal(getTask("M001", "S01", "T01")!.status, "pending");
952
949
 
953
- // M12: Task was reset to "pending" in the transaction, but reconciler
954
- // already corrected it back to "complete" from the stale SUMMARY.md
955
- const task = getTask("M001", "S01", "T01");
956
- assert.equal(task!.status, "complete", "M12: reconciler overrides reopen — task back to complete");
950
+ // Re-complete task + slice succeeds
951
+ await handleCompleteTask(makeTaskParams("T01", "S01", "M001") as any, base);
952
+ const r = await handleCompleteSlice(makeSliceParams("S01", "M001") as any, base);
953
+ assert.ok(!("error" in r), `re-complete slice: ${JSON.stringify(r)}`);
954
+ assert.ok(isClosedStatus(getSlice("M001", "S01")!.status));
957
955
  });
958
956
  });
959
957
  });
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Test isolation utilities for integration tests.
3
+ *
4
+ * Integration tests often call `mergeMilestoneToMain` and other functions that
5
+ * load preferences. If the user's global ~/.gsd/preferences.md has
6
+ * `git.main_branch: master`, tests fail because test repos use `main`.
7
+ *
8
+ * These utilities isolate tests from the user's global environment.
9
+ */
10
+
11
+ import { mkdtempSync, rmSync, realpathSync } from "node:fs";
12
+ import { tmpdir } from "node:os";
13
+ import { join } from "node:path";
14
+
15
+ import { _resetServiceCache } from "../../worktree.ts";
16
+ import { _clearGsdRootCache } from "../../paths.ts";
17
+
18
+ let originalHome: string | undefined;
19
+ let fakeHome: string | null = null;
20
+
21
+ /**
22
+ * Isolate the test environment from user's global preferences.
23
+ * Creates a fake HOME directory so loadEffectiveGSDPreferences() returns
24
+ * empty global preferences instead of the user's ~/.gsd/preferences.md.
25
+ *
26
+ * Call this in a test.before() hook.
27
+ */
28
+ export function isolateFromGlobalPreferences(): void {
29
+ originalHome = process.env.HOME;
30
+ fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "gsd-test-home-")));
31
+ process.env.HOME = fakeHome;
32
+ _clearGsdRootCache();
33
+ _resetServiceCache();
34
+ }
35
+
36
+ /**
37
+ * Restore the original HOME and clean up the fake home directory.
38
+ *
39
+ * Call this in a test.after() hook.
40
+ */
41
+ export function restoreGlobalPreferences(): void {
42
+ if (originalHome !== undefined) {
43
+ process.env.HOME = originalHome;
44
+ } else {
45
+ delete process.env.HOME;
46
+ }
47
+ _clearGsdRootCache();
48
+ _resetServiceCache();
49
+ if (fakeHome) {
50
+ rmSync(fakeHome, { recursive: true, force: true });
51
+ fakeHome = null;
52
+ }
53
+ }
@@ -0,0 +1,525 @@
1
+ /**
2
+ * Integration tests for the prepared discussion system.
3
+ *
4
+ * Exercises the full preparation pipeline against the real GSD-2 codebase:
5
+ * - runPreparation() produces valid briefs
6
+ * - TypeScript is detected as primary language
7
+ * - Module structure includes top-level directories
8
+ * - Completes within R112 timing requirement (<60s)
9
+ * - prepareAndBuildDiscussPrompt() uses discuss-prepared template when enabled
10
+ * - Fallback to standard prompt when preparation is disabled
11
+ */
12
+
13
+ import test from "node:test";
14
+ import assert from "node:assert/strict";
15
+ import { join } from "node:path";
16
+ import { existsSync } from "node:fs";
17
+ import {
18
+ runPreparation,
19
+ formatCodebaseBrief,
20
+ formatPriorContextBrief,
21
+ formatEcosystemBrief,
22
+ type PreparationUIContext,
23
+ type PreparationPreferences,
24
+ type PreparationResult,
25
+ } from "../preparation.ts";
26
+ import { validateEnhancedContext } from "../prompt-validation.ts";
27
+ import { getLastPreparationResult, clearPreparationResult } from "../guided-flow.ts";
28
+
29
+ // ─── Test Helpers ───────────────────────────────────────────────────────────────
30
+
31
+ /**
32
+ * Mock UI context that captures notifications for testing.
33
+ * Follows the pattern from preparation.test.ts.
34
+ */
35
+ function createMockUI(): PreparationUIContext & { notifications: Array<{ message: string; type?: string }> } {
36
+ const notifications: Array<{ message: string; type?: string }> = [];
37
+ return {
38
+ notifications,
39
+ notify(message: string, type?: "info" | "warning" | "error" | "success") {
40
+ notifications.push({ message, type });
41
+ },
42
+ };
43
+ }
44
+
45
+ /**
46
+ * Get the GSD extension source directory for integration testing.
47
+ * This is the real codebase we'll analyze.
48
+ */
49
+ function getGsdExtensionDir(): string {
50
+ // Navigate from tests/ up to gsd/ directory
51
+ return join(import.meta.dirname, "..");
52
+ }
53
+
54
+ /**
55
+ * Get the GSD-2 project root for full codebase analysis.
56
+ */
57
+ function getProjectRoot(): string {
58
+ // Navigate from tests/ up to the project root
59
+ // tests/ -> gsd/ -> extensions/ -> resources/ -> src/ -> gsd-2/
60
+ return join(import.meta.dirname, "..", "..", "..", "..", "..");
61
+ }
62
+
63
+ // ─── R111 Validation: runPreparation against real codebase ──────────────────────
64
+
65
+ test("R111: runPreparation() produces valid codebase brief for GSD extension", async (t) => {
66
+ const dir = getGsdExtensionDir();
67
+ const ui = createMockUI();
68
+ const prefs: PreparationPreferences = {
69
+ discuss_preparation: true,
70
+ discuss_web_research: false, // Skip web research to avoid API key requirement
71
+ discuss_depth: "standard",
72
+ };
73
+
74
+ const result = await runPreparation(dir, ui, prefs);
75
+
76
+ // Verify preparation completed successfully
77
+ assert.equal(result.enabled, true, "preparation should be enabled");
78
+ assert.ok(result.codebase, "should have codebase brief");
79
+ assert.ok(result.codebaseBrief, "should have formatted codebase brief");
80
+
81
+ // Verify TypeScript is detected as primary language
82
+ assert.equal(
83
+ result.codebase.techStack.primaryLanguage,
84
+ "javascript/typescript",
85
+ "should detect TypeScript as primary language",
86
+ );
87
+
88
+ // Verify module structure includes top-level directories
89
+ const topLevelDirs = result.codebase.moduleStructure.topLevelDirs;
90
+ assert.ok(topLevelDirs.length > 0, "should detect top-level directories");
91
+
92
+ // Common directories in the GSD extension
93
+ const expectedDirs = ["tests", "prompts", "templates", "migrate"];
94
+ const foundExpected = expectedDirs.filter(d => topLevelDirs.includes(d));
95
+ assert.ok(
96
+ foundExpected.length >= 2,
97
+ `should detect common directories, found: ${topLevelDirs.join(", ")}`,
98
+ );
99
+
100
+ // Verify sampled files exist
101
+ assert.ok(result.codebase.sampledFiles.length > 0, "should sample source files");
102
+ });
103
+
104
+ test("R111: runPreparation() produces valid prior context brief", async (t) => {
105
+ const dir = getGsdExtensionDir();
106
+ const ui = createMockUI();
107
+ const prefs: PreparationPreferences = {
108
+ discuss_preparation: true,
109
+ discuss_web_research: false,
110
+ };
111
+
112
+ const result = await runPreparation(dir, ui, prefs);
113
+
114
+ // Verify prior context brief structure
115
+ assert.ok(result.priorContext, "should have prior context");
116
+ assert.ok(result.priorContextBrief, "should have formatted prior context brief");
117
+
118
+ // Prior context aggregates decisions, requirements, knowledge, summaries
119
+ assert.ok("decisions" in result.priorContext, "should have decisions");
120
+ assert.ok("requirements" in result.priorContext, "should have requirements");
121
+ assert.ok("knowledge" in result.priorContext, "should have knowledge");
122
+ assert.ok("summaries" in result.priorContext, "should have summaries");
123
+ });
124
+
125
+ test("R111: runPreparation() produces valid ecosystem brief (skipped without API key)", async (t) => {
126
+ const dir = getGsdExtensionDir();
127
+ const ui = createMockUI();
128
+ const prefs: PreparationPreferences = {
129
+ discuss_preparation: true,
130
+ discuss_web_research: false, // Explicitly disable
131
+ };
132
+
133
+ const result = await runPreparation(dir, ui, prefs);
134
+
135
+ // Verify ecosystem brief structure
136
+ assert.ok(result.ecosystem, "should have ecosystem brief");
137
+ assert.ok(result.ecosystemBrief, "should have formatted ecosystem brief");
138
+ assert.equal(result.ecosystem.available, false, "ecosystem should be unavailable when web research disabled");
139
+ assert.ok(result.ecosystem.skippedReason, "should have skip reason");
140
+ });
141
+
142
+ test("R112: runPreparation() completes within 60s requirement", async (t) => {
143
+ const dir = getGsdExtensionDir();
144
+ const prefs: PreparationPreferences = {
145
+ discuss_preparation: true,
146
+ discuss_web_research: false,
147
+ discuss_depth: "standard",
148
+ };
149
+
150
+ const startTime = performance.now();
151
+ const result = await runPreparation(dir, null, prefs);
152
+ const elapsed = performance.now() - startTime;
153
+
154
+ // R112 requirement: preparation must complete within 60 seconds
155
+ assert.ok(result.durationMs < 60000, `should complete within 60s, took ${result.durationMs}ms`);
156
+ assert.ok(elapsed < 60000, `wall-clock time should be under 60s, was ${elapsed}ms`);
157
+
158
+ // Should be much faster for a local directory analysis
159
+ assert.ok(result.durationMs < 10000, `should typically complete within 10s, took ${result.durationMs}ms`);
160
+ });
161
+
162
+ // ─── Codebase Pattern Detection ─────────────────────────────────────────────────
163
+
164
+ test("runPreparation() detects code patterns from GSD extension", async (t) => {
165
+ const dir = getGsdExtensionDir();
166
+ const prefs: PreparationPreferences = {
167
+ discuss_preparation: true,
168
+ discuss_web_research: false,
169
+ };
170
+
171
+ const result = await runPreparation(dir, null, prefs);
172
+
173
+ // The GSD extension uses async/await extensively
174
+ assert.ok(
175
+ result.codebase.patterns.asyncStyle === "async/await" || result.codebase.patterns.asyncStyle === "mixed",
176
+ `should detect async/await or mixed, got ${result.codebase.patterns.asyncStyle}`,
177
+ );
178
+
179
+ // The GSD extension uses try/catch for error handling
180
+ assert.ok(
181
+ result.codebase.patterns.errorHandling === "try/catch" || result.codebase.patterns.errorHandling === "mixed",
182
+ `should detect try/catch or mixed, got ${result.codebase.patterns.errorHandling}`,
183
+ );
184
+
185
+ // TypeScript uses camelCase or mixed naming
186
+ assert.ok(
187
+ result.codebase.patterns.namingConvention === "camelCase" || result.codebase.patterns.namingConvention === "mixed",
188
+ `should detect camelCase or mixed, got ${result.codebase.patterns.namingConvention}`,
189
+ );
190
+
191
+ // Evidence should be populated
192
+ assert.ok(result.codebase.patterns.evidence.asyncStyle.length > 0, "should have async style evidence");
193
+ });
194
+
195
+ test("runPreparation() samples TypeScript files from src/ or project root", async (t) => {
196
+ const dir = getGsdExtensionDir();
197
+ const prefs: PreparationPreferences = {
198
+ discuss_preparation: true,
199
+ discuss_web_research: false,
200
+ };
201
+
202
+ const result = await runPreparation(dir, null, prefs);
203
+
204
+ // Should sample TypeScript files
205
+ const tsFiles = result.codebase.sampledFiles.filter(
206
+ f => f.endsWith(".ts") || f.endsWith(".tsx"),
207
+ );
208
+ assert.ok(tsFiles.length > 0, "should sample TypeScript files");
209
+
210
+ // Should exclude test files
211
+ const testFiles = result.codebase.sampledFiles.filter(
212
+ f => f.includes(".test.") || f.includes(".spec."),
213
+ );
214
+ assert.equal(testFiles.length, 0, "should not sample test files");
215
+ });
216
+
217
+ // ─── Brief Formatting ───────────────────────────────────────────────────────────
218
+
219
+ test("formatCodebaseBrief() produces LLM-readable markdown", async (t) => {
220
+ const dir = getGsdExtensionDir();
221
+ const prefs: PreparationPreferences = {
222
+ discuss_preparation: true,
223
+ discuss_web_research: false,
224
+ };
225
+
226
+ const result = await runPreparation(dir, null, prefs);
227
+ const formatted = formatCodebaseBrief(result.codebase);
228
+
229
+ // Should contain expected sections
230
+ assert.ok(formatted.includes("## Tech Stack"), "should have Tech Stack section");
231
+ assert.ok(formatted.includes("## Module Structure"), "should have Module Structure section");
232
+ assert.ok(formatted.includes("## Code Patterns"), "should have Code Patterns section");
233
+
234
+ // Should contain detected tech
235
+ assert.ok(formatted.includes("javascript/typescript"), "should include detected language");
236
+
237
+ // Should be within character limit
238
+ assert.ok(formatted.length <= 3000, `should cap at 3000 chars, got ${formatted.length}`);
239
+ });
240
+
241
+ test("formatPriorContextBrief() produces structured prior context output", async (t) => {
242
+ const dir = getGsdExtensionDir();
243
+ const prefs: PreparationPreferences = {
244
+ discuss_preparation: true,
245
+ discuss_web_research: false,
246
+ };
247
+
248
+ const result = await runPreparation(dir, null, prefs);
249
+ const formatted = formatPriorContextBrief(result.priorContext);
250
+
251
+ // Should contain expected sections
252
+ assert.ok(formatted.includes("## Prior Decisions"), "should have Prior Decisions section");
253
+ assert.ok(formatted.includes("## Prior Requirements"), "should have Prior Requirements section");
254
+ assert.ok(formatted.includes("## Prior Knowledge"), "should have Prior Knowledge section");
255
+ assert.ok(formatted.includes("## Prior Milestone Summaries"), "should have Prior Milestone Summaries section");
256
+
257
+ // Should be within character limit
258
+ assert.ok(formatted.length <= 6000, `should cap at 6000 chars, got ${formatted.length}`);
259
+ });
260
+
261
+ test("formatEcosystemBrief() returns simplified message (research happens during discussion)", async (t) => {
262
+ const dir = getGsdExtensionDir();
263
+ const prefs: PreparationPreferences = {
264
+ discuss_preparation: true,
265
+ discuss_web_research: false,
266
+ };
267
+
268
+ const result = await runPreparation(dir, null, prefs);
269
+ const formatted = formatEcosystemBrief(result.ecosystem);
270
+
271
+ // Should contain section header
272
+ assert.ok(formatted.includes("## Ecosystem Research"), "should have Ecosystem Research section");
273
+
274
+ // Should indicate research happens during discussion
275
+ assert.ok(formatted.includes("during the discussion"), "should mention research happens during discussion");
276
+ assert.ok(formatted.includes("web search tools"), "should mention web search tools");
277
+
278
+ // Should be within character limit
279
+ assert.ok(formatted.length <= 4000, `should cap at 4000 chars, got ${formatted.length}`);
280
+ });
281
+
282
+ // ─── Preparation Result Storage ─────────────────────────────────────────────────
283
+
284
+ test("getLastPreparationResult() returns null initially", async (t) => {
285
+ // Clear any existing state
286
+ clearPreparationResult();
287
+
288
+ const result = getLastPreparationResult();
289
+ assert.equal(result, null, "should return null when no preparation has run");
290
+ });
291
+
292
+ test("clearPreparationResult() clears stored result", async (t) => {
293
+ // This test verifies the clear function works
294
+ // We can't easily test the set behavior without running the full guided-flow
295
+ clearPreparationResult();
296
+ const result = getLastPreparationResult();
297
+ assert.equal(result, null, "should be null after clear");
298
+ });
299
+
300
+ // ─── TUI Progress Notifications ─────────────────────────────────────────────────
301
+
302
+ test("runPreparation() emits TUI progress notifications", async (t) => {
303
+ const dir = getGsdExtensionDir();
304
+ const ui = createMockUI();
305
+ const prefs: PreparationPreferences = {
306
+ discuss_preparation: true,
307
+ discuss_web_research: false,
308
+ };
309
+
310
+ await runPreparation(dir, ui, prefs);
311
+
312
+ // Should have notifications for each phase
313
+ assert.ok(ui.notifications.length > 0, "should have notifications");
314
+
315
+ // Verify codebase analysis notifications
316
+ assert.ok(
317
+ ui.notifications.some(n => n.message.includes("Analyzing codebase")),
318
+ "should show codebase analysis start",
319
+ );
320
+ assert.ok(
321
+ ui.notifications.some(n => n.message.includes("✓ Analyzed codebase")),
322
+ "should show codebase analysis complete",
323
+ );
324
+
325
+ // Verify prior context notifications
326
+ assert.ok(
327
+ ui.notifications.some(n => n.message.includes("Reviewing prior context")),
328
+ "should show prior context start",
329
+ );
330
+ assert.ok(
331
+ ui.notifications.some(n => n.message.includes("✓ Reviewed prior context")),
332
+ "should show prior context complete",
333
+ );
334
+ });
335
+
336
+ test("runPreparation() works in silent mode (no UI)", async (t) => {
337
+ const dir = getGsdExtensionDir();
338
+ const prefs: PreparationPreferences = {
339
+ discuss_preparation: true,
340
+ discuss_web_research: false,
341
+ };
342
+
343
+ // Pass null for UI
344
+ const result = await runPreparation(dir, null, prefs);
345
+
346
+ // Should complete without error
347
+ assert.equal(result.enabled, true, "should work without UI");
348
+ assert.ok(result.codebase, "should have codebase");
349
+ assert.ok(result.priorContext, "should have priorContext");
350
+ assert.ok(result.durationMs > 0, "should have duration");
351
+ });
352
+
353
+ // ─── Preference-Controlled Behavior ─────────────────────────────────────────────
354
+
355
+ test("runPreparation() returns early when discuss_preparation is false", async (t) => {
356
+ const dir = getGsdExtensionDir();
357
+ const ui = createMockUI();
358
+ const prefs: PreparationPreferences = {
359
+ discuss_preparation: false,
360
+ };
361
+
362
+ const result = await runPreparation(dir, ui, prefs);
363
+
364
+ assert.equal(result.enabled, false, "should indicate preparation disabled");
365
+ assert.equal(result.codebaseBrief, "", "should have empty codebase brief");
366
+ assert.equal(result.priorContextBrief, "", "should have empty prior context brief");
367
+ assert.equal(result.ecosystemBrief, "", "should have empty ecosystem brief");
368
+ assert.equal(ui.notifications.length, 0, "should not show any notifications");
369
+ });
370
+
371
+ test("runPreparation() ecosystem research always returns unavailable (happens during discussion)", async (t) => {
372
+ const dir = getGsdExtensionDir();
373
+ const ui = createMockUI();
374
+ const prefs: PreparationPreferences = {
375
+ discuss_preparation: true,
376
+ discuss_web_research: true, // Even with this enabled, ecosystem research returns unavailable
377
+ };
378
+
379
+ const result = await runPreparation(dir, ui, prefs);
380
+
381
+ assert.equal(result.enabled, true);
382
+ assert.equal(result.ecosystemResearchPerformed, false, "should not perform ecosystem research from preparation");
383
+ assert.equal(result.ecosystem.available, false);
384
+ assert.ok(
385
+ result.ecosystem.skippedReason?.includes("during the discussion"),
386
+ "should indicate research happens during discussion",
387
+ );
388
+
389
+ // Should NOT have ecosystem research notifications (no longer part of preparation)
390
+ assert.ok(
391
+ !ui.notifications.some(n => n.message.includes("Researching ecosystem")),
392
+ "should not show ecosystem research notification",
393
+ );
394
+ });
395
+
396
+ // ─── validateEnhancedContext Integration ────────────────────────────────────────
397
+
398
+ test("validateEnhancedContext() validates required sections", async (t) => {
399
+ // Test with valid enhanced context
400
+ const validContext = `# M001 — Test Milestone
401
+
402
+ ## Scope
403
+
404
+ This milestone covers X, Y, Z.
405
+
406
+ ## Architectural Decisions
407
+
408
+ ### Decision 1: Use TypeScript
409
+
410
+ We will use TypeScript for type safety.
411
+
412
+ ## Acceptance Criteria
413
+
414
+ - [ ] Feature A works
415
+ - [ ] Feature B works
416
+ `;
417
+
418
+ const validResult = validateEnhancedContext(validContext);
419
+ assert.equal(validResult.valid, true, "should validate complete context");
420
+ assert.deepEqual(validResult.missing, [], "should have no missing sections");
421
+
422
+ // Test with missing sections
423
+ const invalidContext = `# M001 — Test Milestone
424
+
425
+ ## Scope
426
+
427
+ This milestone covers X, Y, Z.
428
+ `;
429
+
430
+ const invalidResult = validateEnhancedContext(invalidContext);
431
+ assert.equal(invalidResult.valid, false, "should reject incomplete context");
432
+ assert.ok(invalidResult.missing.length > 0, "should list missing sections");
433
+ assert.ok(
434
+ invalidResult.missing.some(m => m.includes("Architectural Decisions")),
435
+ "should report missing Architectural Decisions",
436
+ );
437
+ assert.ok(
438
+ invalidResult.missing.some(m => m.includes("Acceptance Criteria")),
439
+ "should report missing Acceptance Criteria",
440
+ );
441
+ });
442
+
443
+ test("validateEnhancedContext() requires decision entries in Architectural Decisions", async (t) => {
444
+ // Empty architectural decisions section
445
+ const emptyDecisions = `# M001 — Test Milestone
446
+
447
+ ## Scope
448
+
449
+ This milestone covers X, Y, Z.
450
+
451
+ ## Architectural Decisions
452
+
453
+ (No decisions yet)
454
+
455
+ ## Acceptance Criteria
456
+
457
+ - [ ] Feature A works
458
+ `;
459
+
460
+ const result = validateEnhancedContext(emptyDecisions);
461
+ assert.equal(result.valid, false, "should reject empty decisions section");
462
+ assert.ok(
463
+ result.missing.some(m => m.includes("decision entry")),
464
+ "should report missing decision entry",
465
+ );
466
+ });
467
+
468
+ // ─── Full Pipeline Integration ──────────────────────────────────────────────────
469
+
470
+ test("Full pipeline: preparation produces consistent results across runs", async (t) => {
471
+ const dir = getGsdExtensionDir();
472
+ const prefs: PreparationPreferences = {
473
+ discuss_preparation: true,
474
+ discuss_web_research: false,
475
+ };
476
+
477
+ // Run preparation twice
478
+ const result1 = await runPreparation(dir, null, prefs);
479
+ const result2 = await runPreparation(dir, null, prefs);
480
+
481
+ // Results should be consistent (same codebase, same analysis)
482
+ assert.equal(
483
+ result1.codebase.techStack.primaryLanguage,
484
+ result2.codebase.techStack.primaryLanguage,
485
+ "primary language should be consistent",
486
+ );
487
+
488
+ assert.deepEqual(
489
+ result1.codebase.moduleStructure.topLevelDirs.sort(),
490
+ result2.codebase.moduleStructure.topLevelDirs.sort(),
491
+ "top-level directories should be consistent",
492
+ );
493
+
494
+ assert.equal(
495
+ result1.codebase.patterns.asyncStyle,
496
+ result2.codebase.patterns.asyncStyle,
497
+ "async style should be consistent",
498
+ );
499
+ });
500
+
501
+ test("Full pipeline: preparation handles empty .gsd directory gracefully", async (t) => {
502
+ // The GSD extension directory may or may not have a .gsd subdirectory
503
+ // Either way, preparation should not crash
504
+ const dir = getGsdExtensionDir();
505
+ const prefs: PreparationPreferences = {
506
+ discuss_preparation: true,
507
+ discuss_web_research: false,
508
+ };
509
+
510
+ let result: PreparationResult | undefined;
511
+ let error: unknown;
512
+
513
+ try {
514
+ result = await runPreparation(dir, null, prefs);
515
+ } catch (e) {
516
+ error = e;
517
+ }
518
+
519
+ assert.equal(error, undefined, "should not throw");
520
+ assert.ok(result, "should return result");
521
+ assert.equal(result!.enabled, true, "should be enabled");
522
+
523
+ // Prior context should gracefully handle missing files
524
+ assert.ok(result!.priorContext, "should have prior context even if files missing");
525
+ });