gsd-pi 2.44.0-dev.848dd4c → 2.44.0

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 (298) hide show
  1. package/README.md +12 -30
  2. package/dist/resources/extensions/gsd/auto-start.js +0 -10
  3. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +0 -5
  4. package/dist/web/standalone/.next/BUILD_ID +1 -1
  5. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  6. package/dist/web/standalone/.next/build-manifest.json +3 -3
  7. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  8. package/dist/web/standalone/.next/required-server-files.json +3 -3
  9. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  10. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  11. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  12. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  13. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  14. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  15. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  16. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  20. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  21. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  22. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  23. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  24. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  30. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  31. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  32. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  33. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  34. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  35. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  36. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  37. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  38. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  39. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  40. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  41. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  42. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  43. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  44. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  45. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  46. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  47. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  48. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  50. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  51. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  52. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  54. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  55. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  56. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  57. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  58. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  60. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  61. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  62. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  63. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  64. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  65. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  66. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  74. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  80. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  94. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  96. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  98. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  100. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/index.html +1 -1
  110. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  111. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  112. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  113. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  115. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/page.js +2 -2
  117. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  119. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  120. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  121. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/middleware.js +2 -2
  123. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  125. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  126. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  127. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  128. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  129. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  130. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +1 -0
  131. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  132. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  133. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  134. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  135. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  136. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  137. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  138. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  139. package/dist/web/standalone/server.js +1 -1
  140. package/package.json +1 -1
  141. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +8 -6
  142. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +26 -24
  144. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +48 -29
  146. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +44 -34
  148. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/session-manager.test.js +34 -30
  150. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +12 -10
  152. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  153. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +47 -43
  154. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  155. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  156. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  157. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +43 -31
  158. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +45 -40
  159. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  160. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  161. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  162. package/src/resources/extensions/gsd/auto-start.ts +0 -14
  163. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +0 -8
  164. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  165. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +16 -14
  166. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +57 -43
  167. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +13 -11
  168. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +523 -465
  169. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +75 -73
  170. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +56 -34
  171. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +656 -533
  172. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +143 -165
  173. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +52 -29
  174. package/src/resources/extensions/gsd/tests/captures.test.ts +176 -148
  175. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +33 -32
  176. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +143 -141
  177. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  178. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  179. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +59 -38
  180. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +263 -228
  181. package/src/resources/extensions/gsd/tests/complete-task.test.ts +302 -250
  182. package/src/resources/extensions/gsd/tests/context-store.test.ts +367 -354
  183. package/src/resources/extensions/gsd/tests/continue-here.test.ts +72 -68
  184. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +106 -92
  185. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +35 -27
  186. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +237 -220
  187. package/src/resources/extensions/gsd/tests/db-writer.test.ts +420 -390
  188. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +92 -76
  189. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +83 -68
  190. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -152
  191. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +101 -78
  192. package/src/resources/extensions/gsd/tests/derive-state.test.ts +227 -192
  193. package/src/resources/extensions/gsd/tests/detection.test.ts +278 -232
  194. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +34 -30
  195. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +180 -164
  196. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +49 -43
  197. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +32 -28
  198. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +29 -27
  199. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +38 -34
  200. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +75 -54
  201. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +32 -21
  202. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +97 -72
  203. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +44 -38
  204. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +145 -104
  205. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +106 -84
  206. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +60 -54
  207. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +93 -72
  208. package/src/resources/extensions/gsd/tests/doctor.test.ts +134 -104
  209. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +131 -123
  210. package/src/resources/extensions/gsd/tests/exit-command.test.ts +24 -20
  211. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +57 -48
  212. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +7 -5
  213. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +42 -30
  214. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +206 -198
  215. package/src/resources/extensions/gsd/tests/git-locale.test.ts +27 -13
  216. package/src/resources/extensions/gsd/tests/git-service.test.ts +388 -285
  217. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +39 -31
  218. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +69 -63
  219. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +264 -255
  220. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +119 -108
  221. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +103 -81
  222. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +262 -229
  223. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  224. package/src/resources/extensions/gsd/tests/health-widget.test.ts +37 -29
  225. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +102 -81
  226. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +18 -16
  227. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +46 -41
  228. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +53 -42
  229. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +91 -75
  230. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  231. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +194 -150
  232. package/src/resources/extensions/gsd/tests/md-importer.test.ts +125 -101
  233. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +54 -45
  234. package/src/resources/extensions/gsd/tests/memory-store.test.ts +93 -80
  235. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +66 -57
  236. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +93 -83
  237. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +170 -161
  238. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +141 -125
  239. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +131 -107
  240. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +96 -87
  241. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +164 -125
  242. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +94 -81
  243. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +36 -35
  244. package/src/resources/extensions/gsd/tests/overrides.test.ts +106 -99
  245. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +47 -40
  246. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +28 -25
  247. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +83 -66
  248. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +77 -54
  249. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +115 -68
  250. package/src/resources/extensions/gsd/tests/parsers.test.ts +611 -546
  251. package/src/resources/extensions/gsd/tests/paths.test.ts +87 -72
  252. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +117 -77
  253. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  254. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +119 -93
  255. package/src/resources/extensions/gsd/tests/queue-order.test.ts +82 -70
  256. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +55 -42
  257. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +73 -45
  258. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +38 -28
  259. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +80 -73
  260. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +74 -71
  261. package/src/resources/extensions/gsd/tests/requirements.test.ts +75 -70
  262. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +66 -44
  263. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +181 -114
  264. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +65 -63
  265. package/src/resources/extensions/gsd/tests/run-uat.test.ts +128 -66
  266. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +25 -18
  267. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +44 -37
  268. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +26 -19
  269. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +8 -6
  270. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +28 -22
  271. package/src/resources/extensions/gsd/tests/token-savings.test.ts +56 -54
  272. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +25 -23
  273. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +11 -9
  274. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +82 -66
  275. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +47 -46
  276. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +22 -20
  277. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +86 -84
  278. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +43 -41
  279. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +96 -94
  280. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +13 -11
  281. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +29 -27
  282. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +52 -50
  283. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +13 -10
  284. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +18 -14
  285. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +39 -38
  286. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +21 -17
  287. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +30 -25
  288. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +37 -30
  289. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +22 -15
  290. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +66 -59
  291. package/src/resources/extensions/gsd/tests/worktree.test.ts +50 -44
  292. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +0 -1
  293. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  294. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  295. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +0 -100
  296. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +0 -63
  297. /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_buildManifest.js +0 -0
  298. /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_ssgManifest.js +0 -0
@@ -5,8 +5,7 @@
5
5
  // (b) Helpers fall back to non-null output when DB unavailable
6
6
  // (c) Scoped filtering actually reduces content
7
7
 
8
- import { describe, test } from 'node:test';
9
- import assert from 'node:assert/strict';
8
+ import { createTestContext } from './test-helpers.ts';
10
9
  import {
11
10
  openDatabase,
12
11
  closeDatabase,
@@ -23,6 +22,8 @@ import {
23
22
  formatRequirementsForPrompt,
24
23
  } from '../context-store.ts';
25
24
 
25
+ const { assertEq, assertTrue, assertMatch, assertNoMatch, report } = createTestContext();
26
+
26
27
  // ═══════════════════════════════════════════════════════════════════════════
27
28
  // prompt-db: DB-aware decisions helper returns scoped content
28
29
  // ═══════════════════════════════════════════════════════════════════════════
@@ -49,23 +50,23 @@ console.log('\n=== prompt-db: scoped decisions from DB ===');
49
50
 
50
51
  // Query scoped to M001
51
52
  const m001Decisions = queryDecisions({ milestoneId: 'M001' });
52
- assert.ok(m001Decisions.length > 0, 'M001 decisions should exist');
53
- assert.ok(m001Decisions.length < 10, `scoped query should return fewer than 10 (got ${m001Decisions.length})`);
53
+ assertTrue(m001Decisions.length > 0, 'M001 decisions should exist');
54
+ assertTrue(m001Decisions.length < 10, `scoped query should return fewer than 10 (got ${m001Decisions.length})`);
54
55
 
55
56
  // Verify all returned decisions are for M001
56
57
  for (const d of m001Decisions) {
57
- assert.match(d.when_context, /M001/, `decision ${d.id} should be for M001`);
58
+ assertMatch(d.when_context, /M001/, `decision ${d.id} should be for M001`);
58
59
  }
59
60
 
60
61
  // Format and verify wrapping
61
62
  const formatted = formatDecisionsForPrompt(m001Decisions);
62
- assert.ok(formatted.length > 0, 'formatted decisions should be non-empty');
63
- assert.match(formatted, /\| # \| When \| Scope/, 'formatted decisions have table header');
63
+ assertTrue(formatted.length > 0, 'formatted decisions should be non-empty');
64
+ assertMatch(formatted, /\| # \| When \| Scope/, 'formatted decisions have table header');
64
65
 
65
66
  // Verify the expected wrapper format that inlineDecisionsFromDb would produce
66
67
  const wrapped = `### Decisions\nSource: \`.gsd/DECISIONS.md\`\n\n${formatted}`;
67
- assert.match(wrapped, /^### Decisions/, 'wrapped decisions start with ### Decisions');
68
- assert.match(wrapped, /Source:.*DECISIONS\.md/, 'wrapped decisions have source path');
68
+ assertMatch(wrapped, /^### Decisions/, 'wrapped decisions start with ### Decisions');
69
+ assertMatch(wrapped, /Source:.*DECISIONS\.md/, 'wrapped decisions have source path');
69
70
 
70
71
  closeDatabase();
71
72
  }
@@ -100,25 +101,25 @@ console.log('\n=== prompt-db: scoped requirements from DB ===');
100
101
 
101
102
  // Query scoped to S01 — should get R001 (primary) and R002 (supporting)
102
103
  const s01Reqs = queryRequirements({ sliceId: 'S01' });
103
- assert.deepStrictEqual(s01Reqs.length, 2, 'S01 requirements should be 2 (primary + supporting)');
104
+ assertEq(s01Reqs.length, 2, 'S01 requirements should be 2 (primary + supporting)');
104
105
  const ids = s01Reqs.map(r => r.id).sort();
105
- assert.deepStrictEqual(ids, ['R001', 'R002'], 'S01 owns R001 and supports R002');
106
+ assertEq(ids, ['R001', 'R002'], 'S01 owns R001 and supports R002');
106
107
 
107
108
  // Unscoped query returns all 3
108
109
  const allReqs = queryRequirements();
109
- assert.deepStrictEqual(allReqs.length, 3, 'unscoped requirements should return all 3');
110
+ assertEq(allReqs.length, 3, 'unscoped requirements should return all 3');
110
111
 
111
112
  // Format and verify wrapping
112
113
  const formatted = formatRequirementsForPrompt(s01Reqs);
113
- assert.ok(formatted.length > 0, 'formatted requirements should be non-empty');
114
- assert.match(formatted, /### R001/, 'formatted requirements include R001');
115
- assert.match(formatted, /### R002/, 'formatted requirements include R002');
116
- assert.doesNotMatch(formatted, /### R003/, 'formatted requirements exclude R003');
114
+ assertTrue(formatted.length > 0, 'formatted requirements should be non-empty');
115
+ assertMatch(formatted, /### R001/, 'formatted requirements include R001');
116
+ assertMatch(formatted, /### R002/, 'formatted requirements include R002');
117
+ assertNoMatch(formatted, /### R003/, 'formatted requirements exclude R003');
117
118
 
118
119
  // Verify the expected wrapper format that inlineRequirementsFromDb would produce
119
120
  const wrapped = `### Requirements\nSource: \`.gsd/REQUIREMENTS.md\`\n\n${formatted}`;
120
- assert.match(wrapped, /^### Requirements/, 'wrapped requirements start with ### Requirements');
121
- assert.match(wrapped, /Source:.*REQUIREMENTS\.md/, 'wrapped requirements have source path');
121
+ assertMatch(wrapped, /^### Requirements/, 'wrapped requirements start with ### Requirements');
122
+ assertMatch(wrapped, /Source:.*REQUIREMENTS\.md/, 'wrapped requirements have source path');
122
123
 
123
124
  closeDatabase();
124
125
  }
@@ -141,13 +142,13 @@ console.log('\n=== prompt-db: project content from DB ===');
141
142
  });
142
143
 
143
144
  const content = queryProject();
144
- assert.deepStrictEqual(content, '# Test Project\n\nThis is the project description.', 'queryProject returns content');
145
+ assertEq(content, '# Test Project\n\nThis is the project description.', 'queryProject returns content');
145
146
 
146
147
  // Verify the expected wrapper format that inlineProjectFromDb would produce
147
148
  const wrapped = `### Project\nSource: \`.gsd/PROJECT.md\`\n\n${content}`;
148
- assert.match(wrapped, /^### Project/, 'wrapped project starts with ### Project');
149
- assert.match(wrapped, /Source:.*PROJECT\.md/, 'wrapped project has source path');
150
- assert.match(wrapped, /# Test Project/, 'wrapped project includes content');
149
+ assertMatch(wrapped, /^### Project/, 'wrapped project starts with ### Project');
150
+ assertMatch(wrapped, /Source:.*PROJECT\.md/, 'wrapped project has source path');
151
+ assertMatch(wrapped, /# Test Project/, 'wrapped project includes content');
151
152
 
152
153
  closeDatabase();
153
154
  }
@@ -159,27 +160,27 @@ console.log('\n=== prompt-db: project content from DB ===');
159
160
  console.log('\n=== prompt-db: fallback when DB unavailable ===');
160
161
  {
161
162
  closeDatabase();
162
- assert.ok(!isDbAvailable(), 'DB should not be available');
163
+ assertTrue(!isDbAvailable(), 'DB should not be available');
163
164
 
164
165
  // queryDecisions returns [] when DB closed — helper would fall back
165
166
  const decisions = queryDecisions({ milestoneId: 'M001' });
166
- assert.deepStrictEqual(decisions, [], 'queryDecisions returns [] when DB closed');
167
+ assertEq(decisions, [], 'queryDecisions returns [] when DB closed');
167
168
 
168
169
  // queryRequirements returns [] when DB closed — helper would fall back
169
170
  const requirements = queryRequirements({ sliceId: 'S01' });
170
- assert.deepStrictEqual(requirements, [], 'queryRequirements returns [] when DB closed');
171
+ assertEq(requirements, [], 'queryRequirements returns [] when DB closed');
171
172
 
172
173
  // queryProject returns null when DB closed — helper would fall back
173
174
  const project = queryProject();
174
- assert.deepStrictEqual(project, null, 'queryProject returns null when DB closed');
175
+ assertEq(project, null, 'queryProject returns null when DB closed');
175
176
 
176
177
  // formatDecisionsForPrompt returns '' for empty input
177
178
  const formatted = formatDecisionsForPrompt([]);
178
- assert.deepStrictEqual(formatted, '', 'formatDecisionsForPrompt returns empty for empty input');
179
+ assertEq(formatted, '', 'formatDecisionsForPrompt returns empty for empty input');
179
180
 
180
181
  // formatRequirementsForPrompt returns '' for empty input
181
182
  const formattedReqs = formatRequirementsForPrompt([]);
182
- assert.deepStrictEqual(formattedReqs, '', 'formatRequirementsForPrompt returns empty for empty input');
183
+ assertEq(formattedReqs, '', 'formatRequirementsForPrompt returns empty for empty input');
183
184
  }
184
185
 
185
186
  // ═══════════════════════════════════════════════════════════════════════════
@@ -209,15 +210,15 @@ console.log('\n=== prompt-db: scoped filtering reduces content ===');
209
210
  const allDecisions = queryDecisions();
210
211
  const m001Decisions = queryDecisions({ milestoneId: 'M001' });
211
212
 
212
- assert.deepStrictEqual(allDecisions.length, 10, 'unscoped returns all 10 decisions');
213
- assert.ok(m001Decisions.length < 10, `M001-scoped returns fewer than 10 (got ${m001Decisions.length})`);
214
- assert.ok(m001Decisions.length > 0, 'M001-scoped returns at least 1');
213
+ assertEq(allDecisions.length, 10, 'unscoped returns all 10 decisions');
214
+ assertTrue(m001Decisions.length < 10, `M001-scoped returns fewer than 10 (got ${m001Decisions.length})`);
215
+ assertTrue(m001Decisions.length > 0, 'M001-scoped returns at least 1');
215
216
 
216
217
  // Format both and compare sizes — scoped should be shorter
217
218
  const allFormatted = formatDecisionsForPrompt(allDecisions);
218
219
  const scopedFormatted = formatDecisionsForPrompt(m001Decisions);
219
220
 
220
- assert.ok(
221
+ assertTrue(
221
222
  scopedFormatted.length < allFormatted.length,
222
223
  `scoped content (${scopedFormatted.length} chars) should be shorter than unscoped (${allFormatted.length} chars)`,
223
224
  );
@@ -244,14 +245,14 @@ console.log('\n=== prompt-db: scoped filtering reduces content ===');
244
245
  const allReqs = queryRequirements();
245
246
  const s01Reqs = queryRequirements({ sliceId: 'S01' });
246
247
 
247
- assert.deepStrictEqual(allReqs.length, 8, 'unscoped returns all 8 requirements');
248
- assert.ok(s01Reqs.length < 8, `S01-scoped returns fewer than 8 (got ${s01Reqs.length})`);
249
- assert.ok(s01Reqs.length > 0, 'S01-scoped returns at least 1');
248
+ assertEq(allReqs.length, 8, 'unscoped returns all 8 requirements');
249
+ assertTrue(s01Reqs.length < 8, `S01-scoped returns fewer than 8 (got ${s01Reqs.length})`);
250
+ assertTrue(s01Reqs.length > 0, 'S01-scoped returns at least 1');
250
251
 
251
252
  const allReqsFormatted = formatRequirementsForPrompt(allReqs);
252
253
  const scopedReqsFormatted = formatRequirementsForPrompt(s01Reqs);
253
254
 
254
- assert.ok(
255
+ assertTrue(
255
256
  scopedReqsFormatted.length < allReqsFormatted.length,
256
257
  `scoped requirements (${scopedReqsFormatted.length} chars) should be shorter than unscoped (${allReqsFormatted.length} chars)`,
257
258
  );
@@ -291,23 +292,23 @@ console.log('\n=== prompt-db: DB helpers wrapper format matches expected pattern
291
292
 
292
293
  // Simulate what inlineDecisionsFromDb does
293
294
  const decisions = queryDecisions({ milestoneId: 'M001' });
294
- assert.ok(decisions.length === 1, 'got 1 decision for M001');
295
+ assertTrue(decisions.length === 1, 'got 1 decision for M001');
295
296
  const dFormatted = formatDecisionsForPrompt(decisions);
296
297
  const dWrapped = `### Decisions\nSource: \`.gsd/DECISIONS.md\`\n\n${dFormatted}`;
297
- assert.match(dWrapped, /^### Decisions\nSource: `.gsd\/DECISIONS\.md`\n\n\| #/, 'decisions wrapper format correct');
298
+ assertMatch(dWrapped, /^### Decisions\nSource: `.gsd\/DECISIONS\.md`\n\n\| #/, 'decisions wrapper format correct');
298
299
 
299
300
  // Simulate what inlineRequirementsFromDb does
300
301
  const reqs = queryRequirements({ sliceId: 'S01' });
301
- assert.ok(reqs.length === 1, 'got 1 requirement for S01');
302
+ assertTrue(reqs.length === 1, 'got 1 requirement for S01');
302
303
  const rFormatted = formatRequirementsForPrompt(reqs);
303
304
  const rWrapped = `### Requirements\nSource: \`.gsd/REQUIREMENTS.md\`\n\n${rFormatted}`;
304
- assert.match(rWrapped, /^### Requirements\nSource: `.gsd\/REQUIREMENTS\.md`\n\n### R001/, 'requirements wrapper format correct');
305
+ assertMatch(rWrapped, /^### Requirements\nSource: `.gsd\/REQUIREMENTS\.md`\n\n### R001/, 'requirements wrapper format correct');
305
306
 
306
307
  // Simulate what inlineProjectFromDb does
307
308
  const project = queryProject();
308
- assert.ok(project !== null, 'project content exists');
309
+ assertTrue(project !== null, 'project content exists');
309
310
  const pWrapped = `### Project\nSource: \`.gsd/PROJECT.md\`\n\n${project}`;
310
- assert.match(pWrapped, /^### Project\nSource: `.gsd\/PROJECT\.md`\n\n# Project Name/, 'project wrapper format correct');
311
+ assertMatch(pWrapped, /^### Project\nSource: `.gsd\/PROJECT\.md`\n\n# Project Name/, 'project wrapper format correct');
311
312
 
312
313
  closeDatabase();
313
314
  }
@@ -321,9 +322,8 @@ import { join } from 'node:path';
321
322
  import { tmpdir } from 'node:os';
322
323
  import { migrateFromMarkdown } from '../md-importer.ts';
323
324
 
324
-
325
- describe('prompt-db', () => {
326
- test('prompt-db: re-import updates DB when source markdown changes', () => {
325
+ console.log('\n=== prompt-db: re-import updates DB when source markdown changes ===');
326
+ {
327
327
  // Create a temp dir simulating a project with .gsd/DECISIONS.md
328
328
  const tmpDir = mkdtempSync(join(tmpdir(), 'prompt-db-reimport-'));
329
329
  const gsdDir = join(tmpDir, '.gsd');
@@ -345,9 +345,9 @@ test('prompt-db: re-import updates DB when source markdown changes', () => {
345
345
 
346
346
  // Verify initial state: 2 decisions
347
347
  const initial = queryDecisions();
348
- assert.deepStrictEqual(initial.length, 2, 're-import: initial import has 2 decisions');
348
+ assertEq(initial.length, 2, 're-import: initial import has 2 decisions');
349
349
  const initialIds = initial.map(d => d.id).sort();
350
- assert.deepStrictEqual(initialIds, ['D001', 'D002'], 're-import: initial decisions are D001, D002');
350
+ assertEq(initialIds, ['D001', 'D002'], 're-import: initial decisions are D001, D002');
351
351
 
352
352
  // Now "the LLM modifies DECISIONS.md" — add a third decision
353
353
  const updatedDecisions = `# Decisions Register
@@ -365,23 +365,23 @@ test('prompt-db: re-import updates DB when source markdown changes', () => {
365
365
 
366
366
  // Verify DB now has 3 decisions
367
367
  const afterReimport = queryDecisions();
368
- assert.deepStrictEqual(afterReimport.length, 3, 're-import: after re-import has 3 decisions');
368
+ assertEq(afterReimport.length, 3, 're-import: after re-import has 3 decisions');
369
369
  const afterIds = afterReimport.map(d => d.id).sort();
370
- assert.deepStrictEqual(afterIds, ['D001', 'D002', 'D003'], 're-import: decisions are D001, D002, D003');
370
+ assertEq(afterIds, ['D001', 'D002', 'D003'], 're-import: decisions are D001, D002, D003');
371
371
 
372
372
  // Verify the new decision has correct data
373
373
  const d003 = afterReimport.find(d => d.id === 'D003');
374
- assert.ok(d003 !== undefined, 're-import: D003 exists');
375
- assert.deepStrictEqual(d003!.when_context, 'M001/S02', 're-import: D003 when_context is M001/S02');
376
- assert.deepStrictEqual(d003!.scope, 'runtime', 're-import: D003 scope is runtime');
377
- assert.deepStrictEqual(d003!.choice, 'D014 pattern', 're-import: D003 choice is D014 pattern');
374
+ assertTrue(d003 !== undefined, 're-import: D003 exists');
375
+ assertEq(d003!.when_context, 'M001/S02', 're-import: D003 when_context is M001/S02');
376
+ assertEq(d003!.scope, 'runtime', 're-import: D003 scope is runtime');
377
+ assertEq(d003!.choice, 'D014 pattern', 're-import: D003 choice is D014 pattern');
378
378
 
379
379
  // Verify scoped query picks up the new decision
380
380
  const m001Scoped = queryDecisions({ milestoneId: 'M001' });
381
- assert.ok(m001Scoped.length === 3, 're-import: all 3 decisions are for M001');
381
+ assertTrue(m001Scoped.length === 3, 're-import: all 3 decisions are for M001');
382
382
 
383
383
  closeDatabase();
384
- });
384
+ }
385
385
 
386
386
  // ─── Final Report ──────────────────────────────────────────────────────────
387
- });
387
+ report();
@@ -1,5 +1,3 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
2
  import { join } from "node:path";
5
3
  import { tmpdir } from "node:os";
@@ -7,94 +5,122 @@ import { tmpdir } from "node:os";
7
5
  import { deriveState } from "../state.js";
8
6
  import { buildExistingMilestonesContext } from "../guided-flow.js";
9
7
 
10
- describe('queue-draft-detection', () => {
11
- test('draft and context milestone detection', async () => {
12
- const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-draft-test-"));
13
- const gsd = join(tmpBase, ".gsd");
14
-
15
- try {
16
- // M001: has only CONTEXT-DRAFT.md (draft milestone)
17
- mkdirSync(join(gsd, "milestones", "M001"), { recursive: true });
18
- writeFileSync(
19
- join(gsd, "milestones", "M001", "M001-CONTEXT-DRAFT.md"),
20
- "# M001: Draft Milestone\n\nSeed material from prior discussion.\n",
21
- );
22
-
23
- // M002: has full CONTEXT.md (ready milestone)
24
- mkdirSync(join(gsd, "milestones", "M002"), { recursive: true });
25
- writeFileSync(
26
- join(gsd, "milestones", "M002", "M002-CONTEXT.md"),
27
- "# M002: Ready Milestone\n\nFull context from deep discussion.\n",
28
- );
29
-
30
- // M003: has both CONTEXT.md and CONTEXT-DRAFT.md (CONTEXT wins)
31
- mkdirSync(join(gsd, "milestones", "M003"), { recursive: true });
32
- writeFileSync(
33
- join(gsd, "milestones", "M003", "M003-CONTEXT.md"),
34
- "# M003: Full Context\n\nThis is the real context.\n",
35
- );
36
- writeFileSync(
37
- join(gsd, "milestones", "M003", "M003-CONTEXT-DRAFT.md"),
38
- "# M003: Draft\n\nThis should be ignored.\n",
39
- );
40
-
41
- // M004: has neither (empty milestone dir)
42
- mkdirSync(join(gsd, "milestones", "M004"), { recursive: true });
43
-
44
- // Build context
45
- const state = await deriveState(tmpBase);
46
- const milestoneIds = ["M001", "M002", "M003", "M004"];
47
- const context = await buildExistingMilestonesContext(tmpBase, milestoneIds, state);
48
-
49
- // draft-only milestone includes "Draft context available"
50
- assert.ok(
51
- context.includes("Draft context available"),
52
- "M001 (draft-only) should include 'Draft context available' label",
53
- );
54
- assert.ok(
55
- context.includes("Seed material from prior discussion"),
56
- "M001 draft content should be included in context output",
57
- );
58
-
59
- // full-context milestone uses "Context:" label
60
- assert.ok(
61
- context.includes("**Context:**"),
62
- "M002 (full context) should use 'Context:' label",
63
- );
64
- assert.ok(
65
- context.includes("Full context from deep discussion"),
66
- "M002 context content should be included",
67
- );
68
-
69
- // both files: CONTEXT.md wins, no draft label
70
- const m003Idx = context.indexOf("M003:");
71
- const m003Section = context.slice(m003Idx, m003Idx + 500);
72
- assert.ok(
73
- m003Section.includes("**Context:**"),
74
- "M003 (both files) should use 'Context:' label (CONTEXT.md wins)",
75
- );
76
- assert.ok(
77
- !m003Section.includes("Draft context available"),
78
- "M003 (both files) should NOT show draft label — CONTEXT.md takes precedence",
79
- );
80
- assert.ok(
81
- m003Section.includes("This is the real context"),
82
- "M003 should show CONTEXT.md content, not draft content",
83
- );
84
-
85
- // neither file: no context section
86
- const m004Idx = context.indexOf("M004:");
87
- const m004Section = context.slice(m004Idx, m004Idx + 500);
88
- assert.ok(
89
- !m004Section.includes("**Context:**"),
90
- "M004 (neither file) should not have Context: label",
91
- );
92
- assert.ok(
93
- !m004Section.includes("Draft context available"),
94
- "M004 (neither file) should not have Draft label",
95
- );
96
- } finally {
97
- rmSync(tmpBase, { recursive: true, force: true });
98
- }
99
- });
100
- });
8
+ let passed = 0;
9
+ let failed = 0;
10
+
11
+ function assert(condition: boolean, message: string): void {
12
+ if (condition) {
13
+ passed++;
14
+ } else {
15
+ failed++;
16
+ console.error(` FAIL: ${message}`);
17
+ }
18
+ }
19
+
20
+ // ─── Fixture setup ──────────────────────────────────────────────────────
21
+
22
+ const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-draft-test-"));
23
+ const gsd = join(tmpBase, ".gsd");
24
+
25
+ // M001: has only CONTEXT-DRAFT.md (draft milestone)
26
+ mkdirSync(join(gsd, "milestones", "M001"), { recursive: true });
27
+ writeFileSync(
28
+ join(gsd, "milestones", "M001", "M001-CONTEXT-DRAFT.md"),
29
+ "# M001: Draft Milestone\n\nSeed material from prior discussion.\n",
30
+ );
31
+
32
+ // M002: has full CONTEXT.md (ready milestone)
33
+ mkdirSync(join(gsd, "milestones", "M002"), { recursive: true });
34
+ writeFileSync(
35
+ join(gsd, "milestones", "M002", "M002-CONTEXT.md"),
36
+ "# M002: Ready Milestone\n\nFull context from deep discussion.\n",
37
+ );
38
+
39
+ // M003: has both CONTEXT.md and CONTEXT-DRAFT.md (CONTEXT wins)
40
+ mkdirSync(join(gsd, "milestones", "M003"), { recursive: true });
41
+ writeFileSync(
42
+ join(gsd, "milestones", "M003", "M003-CONTEXT.md"),
43
+ "# M003: Full Context\n\nThis is the real context.\n",
44
+ );
45
+ writeFileSync(
46
+ join(gsd, "milestones", "M003", "M003-CONTEXT-DRAFT.md"),
47
+ "# M003: Draft\n\nThis should be ignored.\n",
48
+ );
49
+
50
+ // M004: has neither (empty milestone dir)
51
+ mkdirSync(join(gsd, "milestones", "M004"), { recursive: true });
52
+
53
+ // ─── Build context ──────────────────────────────────────────────────────
54
+
55
+ const state = await deriveState(tmpBase);
56
+ const milestoneIds = ["M001", "M002", "M003", "M004"];
57
+ const context = await buildExistingMilestonesContext(tmpBase, milestoneIds, state);
58
+
59
+ // ─── Test: draft-only milestone includes "Draft context available" ──────
60
+
61
+ assert(
62
+ context.includes("Draft context available"),
63
+ "M001 (draft-only) should include 'Draft context available' label",
64
+ );
65
+
66
+ assert(
67
+ context.includes("Seed material from prior discussion"),
68
+ "M001 draft content should be included in context output",
69
+ );
70
+
71
+ // ─── Test: full-context milestone uses "Context:" label ────────────────
72
+
73
+ assert(
74
+ context.includes("**Context:**"),
75
+ "M002 (full context) should use 'Context:' label",
76
+ );
77
+
78
+ assert(
79
+ context.includes("Full context from deep discussion"),
80
+ "M002 context content should be included",
81
+ );
82
+
83
+ // ─── Test: both files → CONTEXT.md wins, no draft label ────────────────
84
+
85
+ // Find M003's section and check it has Context: but not Draft
86
+ const m003Idx = context.indexOf("M003:");
87
+ const m003Section = context.slice(m003Idx, m003Idx + 500);
88
+
89
+ assert(
90
+ m003Section.includes("**Context:**"),
91
+ "M003 (both files) should use 'Context:' label (CONTEXT.md wins)",
92
+ );
93
+
94
+ assert(
95
+ !m003Section.includes("Draft context available"),
96
+ "M003 (both files) should NOT show draft label — CONTEXT.md takes precedence",
97
+ );
98
+
99
+ assert(
100
+ m003Section.includes("This is the real context"),
101
+ "M003 should show CONTEXT.md content, not draft content",
102
+ );
103
+
104
+ // ─── Test: neither file → no context section ───────────────────────────
105
+
106
+ const m004Idx = context.indexOf("M004:");
107
+ const m004Section = context.slice(m004Idx, m004Idx + 500);
108
+
109
+ assert(
110
+ !m004Section.includes("**Context:**"),
111
+ "M004 (neither file) should not have Context: label",
112
+ );
113
+
114
+ assert(
115
+ !m004Section.includes("Draft context available"),
116
+ "M004 (neither file) should not have Draft label",
117
+ );
118
+
119
+ // ─── Cleanup ──────────────────────────────────────────────────────────
120
+
121
+ rmSync(tmpBase, { recursive: true, force: true });
122
+
123
+ // ─── Results ──────────────────────────────────────────────────────────
124
+
125
+ console.log(`\nqueue-draft-detection: ${passed} passed, ${failed} failed`);
126
+ if (failed > 0) process.exit(1);