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
@@ -25,9 +25,9 @@ import {
25
25
  isSessionLockHeld,
26
26
  } from '../session-lock.ts';
27
27
  import { gsdRoot } from '../paths.ts';
28
- import { describe, test } from 'node:test';
29
- import assert from 'node:assert/strict';
28
+ import { createTestContext } from './test-helpers.ts';
30
29
 
30
+ const { assertEq, assertTrue, report } = createTestContext();
31
31
  const require = createRequire(import.meta.url);
32
32
 
33
33
  function hasProperLockfile(): boolean {
@@ -41,7 +41,7 @@ function hasProperLockfile(): boolean {
41
41
 
42
42
  const properLockfileAvailable = hasProperLockfile();
43
43
 
44
- describe('session-lock-regression', async () => {
44
+ async function main(): Promise<void> {
45
45
 
46
46
  // ─── 1. Basic acquire/release lifecycle ───────────────────────────────
47
47
  console.log('\n=== 1. acquire → validate → release lifecycle ===');
@@ -51,22 +51,22 @@ describe('session-lock-regression', async () => {
51
51
 
52
52
  try {
53
53
  const result = acquireSessionLock(base);
54
- assert.ok(result.acquired, 'lock acquired successfully');
54
+ assertTrue(result.acquired, 'lock acquired successfully');
55
55
 
56
56
  const valid = validateSessionLock(base);
57
- assert.ok(valid, 'lock validates after acquisition');
57
+ assertTrue(valid, 'lock validates after acquisition');
58
58
 
59
- assert.ok(isSessionLockHeld(base), 'isSessionLockHeld returns true');
59
+ assertTrue(isSessionLockHeld(base), 'isSessionLockHeld returns true');
60
60
 
61
61
  releaseSessionLock(base);
62
62
 
63
63
  // After release, the lock file should be cleaned up
64
64
  const lockFile = join(gsdRoot(base), 'auto.lock');
65
- assert.ok(!existsSync(lockFile), 'lock file removed after release');
65
+ assertTrue(!existsSync(lockFile), 'lock file removed after release');
66
66
 
67
67
  // The .gsd.lock/ directory should be cleaned up
68
68
  const lockDir = gsdRoot(base) + '.lock';
69
- assert.ok(!existsSync(lockDir), '.gsd.lock/ directory removed after release (#1245)');
69
+ assertTrue(!existsSync(lockDir), '.gsd.lock/ directory removed after release (#1245)');
70
70
  } finally {
71
71
  rmSync(base, { recursive: true, force: true });
72
72
  }
@@ -88,7 +88,7 @@ describe('session-lock-regression', async () => {
88
88
  } catch {
89
89
  threw = true;
90
90
  }
91
- assert.ok(!threw, 'double release does not throw');
91
+ assertTrue(!threw, 'double release does not throw');
92
92
  } finally {
93
93
  rmSync(base, { recursive: true, force: true });
94
94
  }
@@ -106,13 +106,13 @@ describe('session-lock-regression', async () => {
106
106
  updateSessionLock(base, 'execute-task', 'M001/S01/T01', 5, '/tmp/session.json');
107
107
 
108
108
  const data = readSessionLockData(base);
109
- assert.ok(data !== null, 'lock data readable after update');
109
+ assertTrue(data !== null, 'lock data readable after update');
110
110
  if (data) {
111
- assert.deepStrictEqual(data.pid, process.pid, 'lock data has correct PID');
112
- assert.deepStrictEqual(data.unitType, 'execute-task', 'lock data has correct unit type');
113
- assert.deepStrictEqual(data.unitId, 'M001/S01/T01', 'lock data has correct unit ID');
114
- assert.deepStrictEqual(data.completedUnits, 5, 'lock data has correct completed count');
115
- assert.deepStrictEqual(data.sessionFile, '/tmp/session.json', 'lock data has session file');
111
+ assertEq(data.pid, process.pid, 'lock data has correct PID');
112
+ assertEq(data.unitType, 'execute-task', 'lock data has correct unit type');
113
+ assertEq(data.unitId, 'M001/S01/T01', 'lock data has correct unit ID');
114
+ assertEq(data.completedUnits, 5, 'lock data has correct completed count');
115
+ assertEq(data.sessionFile, '/tmp/session.json', 'lock data has session file');
116
116
  }
117
117
 
118
118
  releaseSessionLock(base);
@@ -142,7 +142,7 @@ describe('session-lock-regression', async () => {
142
142
 
143
143
  // Should be able to acquire despite the stale lock
144
144
  const result = acquireSessionLock(base);
145
- assert.ok(result.acquired, '#1245: stale lock from dead PID → re-acquirable');
145
+ assertTrue(result.acquired, '#1245: stale lock from dead PID → re-acquirable');
146
146
 
147
147
  releaseSessionLock(base);
148
148
  } finally {
@@ -158,7 +158,7 @@ describe('session-lock-regression', async () => {
158
158
 
159
159
  try {
160
160
  const data = readSessionLockData(base);
161
- assert.deepStrictEqual(data, null, 'no lock file → null');
161
+ assertEq(data, null, 'no lock file → null');
162
162
  } finally {
163
163
  rmSync(base, { recursive: true, force: true });
164
164
  }
@@ -176,7 +176,7 @@ describe('session-lock-regression', async () => {
176
176
  // Multiple validations should all return true (regression for #1257)
177
177
  for (let i = 0; i < 5; i++) {
178
178
  const valid = validateSessionLock(base);
179
- assert.ok(valid, `#1257: validation ${i + 1} returns true for own lock`);
179
+ assertTrue(valid, `#1257: validation ${i + 1} returns true for own lock`);
180
180
  }
181
181
 
182
182
  releaseSessionLock(base);
@@ -196,7 +196,7 @@ describe('session-lock-regression', async () => {
196
196
  writeFileSync(lockFile, 'NOT VALID JSON {{{');
197
197
 
198
198
  const data = readSessionLockData(base);
199
- assert.deepStrictEqual(data, null, 'corrupt JSON → null');
199
+ assertEq(data, null, 'corrupt JSON → null');
200
200
  } finally {
201
201
  rmSync(base, { recursive: true, force: true });
202
202
  }
@@ -210,9 +210,9 @@ describe('session-lock-regression', async () => {
210
210
 
211
211
  try {
212
212
  const status = getSessionLockStatus(base);
213
- assert.deepStrictEqual(status.valid, false, 'missing lock metadata is invalid');
214
- assert.deepStrictEqual(status.failureReason, 'missing-metadata', 'missing metadata reason is surfaced');
215
- assert.deepStrictEqual(status.expectedPid, process.pid, 'expected PID is included');
213
+ assertEq(status.valid, false, 'missing lock metadata is invalid');
214
+ assertEq(status.failureReason, 'missing-metadata', 'missing metadata reason is surfaced');
215
+ assertEq(status.expectedPid, process.pid, 'expected PID is included');
216
216
  } finally {
217
217
  rmSync(base, { recursive: true, force: true });
218
218
  }
@@ -237,10 +237,10 @@ describe('session-lock-regression', async () => {
237
237
  }, null, 2));
238
238
 
239
239
  const status = getSessionLockStatus(base);
240
- assert.deepStrictEqual(status.valid, false, 'foreign PID lock is invalid');
241
- assert.deepStrictEqual(status.failureReason, 'pid-mismatch', 'PID mismatch reason is surfaced');
242
- assert.deepStrictEqual(status.existingPid, foreignPid, 'existing PID is included');
243
- assert.deepStrictEqual(status.expectedPid, process.pid, 'expected PID is included');
240
+ assertEq(status.valid, false, 'foreign PID lock is invalid');
241
+ assertEq(status.failureReason, 'pid-mismatch', 'PID mismatch reason is surfaced');
242
+ assertEq(status.existingPid, foreignPid, 'existing PID is included');
243
+ assertEq(status.expectedPid, process.pid, 'expected PID is included');
244
244
  } finally {
245
245
  rmSync(base, { recursive: true, force: true });
246
246
  }
@@ -254,11 +254,11 @@ describe('session-lock-regression', async () => {
254
254
 
255
255
  try {
256
256
  const r1 = acquireSessionLock(base);
257
- assert.ok(r1.acquired, 'first acquisition');
257
+ assertTrue(r1.acquired, 'first acquisition');
258
258
  releaseSessionLock(base);
259
259
 
260
260
  const r2 = acquireSessionLock(base);
261
- assert.ok(r2.acquired, 're-acquisition after release');
261
+ assertTrue(r2.acquired, 're-acquisition after release');
262
262
  releaseSessionLock(base);
263
263
  } finally {
264
264
  rmSync(base, { recursive: true, force: true });
@@ -273,13 +273,13 @@ describe('session-lock-regression', async () => {
273
273
 
274
274
  try {
275
275
  const r1 = acquireSessionLock(base);
276
- assert.ok(r1.acquired, 'first acquisition succeeds');
276
+ assertTrue(r1.acquired, 'first acquisition succeeds');
277
277
 
278
278
  const r2 = acquireSessionLock(base);
279
- assert.ok(r2.acquired, 're-entrant acquisition succeeds');
279
+ assertTrue(r2.acquired, 're-entrant acquisition succeeds');
280
280
 
281
281
  const valid = validateSessionLock(base);
282
- assert.ok(valid, 're-entrant acquisition does not corrupt validation state');
282
+ assertTrue(valid, 're-entrant acquisition does not corrupt validation state');
283
283
 
284
284
  releaseSessionLock(base);
285
285
  } finally {
@@ -295,24 +295,31 @@ describe('session-lock-regression', async () => {
295
295
 
296
296
  try {
297
297
  const r1 = acquireSessionLock(base);
298
- assert.ok(r1.acquired, 'first acquisition succeeds');
298
+ assertTrue(r1.acquired, 'first acquisition succeeds');
299
299
 
300
300
  const lockDir = gsdRoot(base) + '.lock';
301
301
  if (properLockfileAvailable) {
302
- assert.ok(existsSync(lockDir), '.gsd.lock/ exists after first acquisition');
302
+ assertTrue(existsSync(lockDir), '.gsd.lock/ exists after first acquisition');
303
303
  }
304
304
 
305
305
  const r2 = acquireSessionLock(base);
306
- assert.ok(r2.acquired, 'second acquisition succeeds');
306
+ assertTrue(r2.acquired, 'second acquisition succeeds');
307
307
  if (properLockfileAvailable) {
308
- assert.ok(existsSync(lockDir), '.gsd.lock/ exists after re-entrant acquisition');
308
+ assertTrue(existsSync(lockDir), '.gsd.lock/ exists after re-entrant acquisition');
309
309
  }
310
- assert.ok(validateSessionLock(base), 'lock remains valid after re-entrant acquisition');
310
+ assertTrue(validateSessionLock(base), 'lock remains valid after re-entrant acquisition');
311
311
 
312
312
  releaseSessionLock(base);
313
- assert.ok(!existsSync(lockDir), '.gsd.lock/ is removed after release');
313
+ assertTrue(!existsSync(lockDir), '.gsd.lock/ is removed after release');
314
314
  } finally {
315
315
  rmSync(base, { recursive: true, force: true });
316
316
  }
317
317
  }
318
+
319
+ report();
320
+ }
321
+
322
+ main().catch((error) => {
323
+ console.error(error);
324
+ process.exit(1);
318
325
  });
@@ -14,9 +14,9 @@ import {
14
14
  getAllMilestones,
15
15
  _getAdapter,
16
16
  } from '../gsd-db.ts';
17
- import { describe, test } from 'node:test';
18
- import assert from 'node:assert/strict';
17
+ import { createTestContext } from './test-helpers.ts';
19
18
 
19
+ const { assertEq, assertTrue, report } = createTestContext();
20
20
 
21
21
  // ─── Helpers ──────────────────────────────────────────────────────────────
22
22
 
@@ -30,14 +30,14 @@ function cleanup(dir: string): void {
30
30
 
31
31
  // ─── Tests ────────────────────────────────────────────────────────────────
32
32
 
33
- describe('shared-wal', async () => {
33
+ async function main() {
34
34
  // ─── Test (a): resolveProjectRootDbPath returns project root DB for worktree path ───
35
35
  console.log('\n=== shared-wal: resolve worktree path to project root DB ===');
36
36
  {
37
37
  const projectRoot = '/home/user/myproject';
38
38
  const worktreePath = join(projectRoot, '.gsd', 'worktrees', 'M001');
39
39
  const result = resolveProjectRootDbPath(worktreePath);
40
- assert.deepStrictEqual(result, join(projectRoot, '.gsd', 'gsd.db'),
40
+ assertEq(result, join(projectRoot, '.gsd', 'gsd.db'),
41
41
  'worktree path resolves to project root DB');
42
42
  }
43
43
 
@@ -46,7 +46,7 @@ describe('shared-wal', async () => {
46
46
  {
47
47
  const projectRoot = '/home/user/myproject';
48
48
  const result = resolveProjectRootDbPath(projectRoot);
49
- assert.deepStrictEqual(result, join(projectRoot, '.gsd', 'gsd.db'),
49
+ assertEq(result, join(projectRoot, '.gsd', 'gsd.db'),
50
50
  'project root path stays at project root DB');
51
51
  }
52
52
 
@@ -56,7 +56,7 @@ describe('shared-wal', async () => {
56
56
  const projectRoot = '/home/user/myproject';
57
57
  const nestedPath = join(projectRoot, '.gsd', 'worktrees', 'M002', 'src', 'lib');
58
58
  const result = resolveProjectRootDbPath(nestedPath);
59
- assert.deepStrictEqual(result, join(projectRoot, '.gsd', 'gsd.db'),
59
+ assertEq(result, join(projectRoot, '.gsd', 'gsd.db'),
60
60
  'nested worktree subdir resolves to project root DB');
61
61
  }
62
62
 
@@ -64,7 +64,7 @@ describe('shared-wal', async () => {
64
64
  console.log('\n=== shared-wal: resolve forward-slash path ===');
65
65
  {
66
66
  const result = resolveProjectRootDbPath('/proj/.gsd/worktrees/M001');
67
- assert.deepStrictEqual(result, join('/proj', '.gsd', 'gsd.db'),
67
+ assertEq(result, join('/proj', '.gsd', 'gsd.db'),
68
68
  'forward-slash worktree path resolves correctly');
69
69
  }
70
70
 
@@ -99,9 +99,9 @@ describe('shared-wal', async () => {
99
99
 
100
100
  // Verify all 3 milestones are visible
101
101
  const all = getAllMilestones();
102
- assert.deepStrictEqual(all.length, 3, 'concurrent: all 3 milestones visible');
102
+ assertEq(all.length, 3, 'concurrent: all 3 milestones visible');
103
103
  const ids = all.map(m => m.id).sort();
104
- assert.deepStrictEqual(ids, ['M001', 'M002', 'M003'], 'concurrent: correct IDs');
104
+ assertEq(ids, ['M001', 'M002', 'M003'], 'concurrent: correct IDs');
105
105
 
106
106
  closeDatabase();
107
107
  } finally {
@@ -132,7 +132,7 @@ describe('shared-wal', async () => {
132
132
  // Connection 2: write M002, verify sees M001
133
133
  openDatabase(dbPath);
134
134
  const afterConn2Before = getAllMilestones();
135
- assert.ok(afterConn2Before.some(m => m.id === 'M001'),
135
+ assertTrue(afterConn2Before.some(m => m.id === 'M001'),
136
136
  'rawconc: conn2 sees M001 from conn1');
137
137
  insertMilestone({ id: 'M002', title: 'Writer 2', status: 'active' });
138
138
  closeDatabase();
@@ -140,16 +140,16 @@ describe('shared-wal', async () => {
140
140
  // Connection 3: write M003, verify sees M001 + M002
141
141
  openDatabase(dbPath);
142
142
  const afterConn3Before = getAllMilestones();
143
- assert.ok(afterConn3Before.some(m => m.id === 'M001'),
143
+ assertTrue(afterConn3Before.some(m => m.id === 'M001'),
144
144
  'rawconc: conn3 sees M001');
145
- assert.ok(afterConn3Before.some(m => m.id === 'M002'),
145
+ assertTrue(afterConn3Before.some(m => m.id === 'M002'),
146
146
  'rawconc: conn3 sees M002');
147
147
  insertMilestone({ id: 'M003', title: 'Writer 3', status: 'active' });
148
148
 
149
149
  // Final read: all 3 visible
150
150
  const finalAll = getAllMilestones();
151
- assert.deepStrictEqual(finalAll.length, 3, 'rawconc: all 3 milestones visible');
152
- assert.deepStrictEqual(
151
+ assertEq(finalAll.length, 3, 'rawconc: all 3 milestones visible');
152
+ assertEq(
153
153
  finalAll.map(m => m.id).sort(),
154
154
  ['M001', 'M002', 'M003'],
155
155
  'rawconc: all IDs present',
@@ -177,7 +177,7 @@ describe('shared-wal', async () => {
177
177
 
178
178
  // Verify it committed
179
179
  const all = getAllMilestones();
180
- assert.deepStrictEqual(all.length, 1, 'busy: M001 committed via transaction');
180
+ assertEq(all.length, 1, 'busy: M001 committed via transaction');
181
181
 
182
182
  // Verify transaction rolls back on error
183
183
  let errorCaught = false;
@@ -188,17 +188,17 @@ describe('shared-wal', async () => {
188
188
  });
189
189
  } catch (err) {
190
190
  errorCaught = true;
191
- assert.ok(
191
+ assertTrue(
192
192
  (err as Error).message.includes('Simulated failure'),
193
193
  'busy: error propagated from transaction',
194
194
  );
195
195
  }
196
- assert.ok(errorCaught, 'busy: transaction threw on error');
196
+ assertTrue(errorCaught, 'busy: transaction threw on error');
197
197
 
198
198
  // M002 should NOT be visible (rolled back)
199
199
  const afterRollback = getAllMilestones();
200
- assert.deepStrictEqual(afterRollback.length, 1, 'busy: M002 rolled back — still only 1 milestone');
201
- assert.deepStrictEqual(afterRollback[0]!.id, 'M001', 'busy: only M001 survives');
200
+ assertEq(afterRollback.length, 1, 'busy: M002 rolled back — still only 1 milestone');
201
+ assertEq(afterRollback[0]!.id, 'M001', 'busy: only M001 survives');
202
202
 
203
203
  closeDatabase();
204
204
  } finally {
@@ -206,4 +206,11 @@ describe('shared-wal', async () => {
206
206
  cleanup(tmp);
207
207
  }
208
208
  }
209
+
210
+ report();
211
+ }
212
+
213
+ main().catch((error) => {
214
+ console.error(error);
215
+ process.exit(1);
209
216
  });
@@ -19,9 +19,9 @@ import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
19
19
  import { join } from "node:path";
20
20
  import { tmpdir } from "node:os";
21
21
  import { recoverTimedOutUnit, type RecoveryContext } from "../auto-timeout-recovery.ts";
22
- import { test } from 'node:test';
23
- import assert from 'node:assert/strict';
22
+ import { createTestContext } from './test-helpers.ts';
24
23
 
24
+ const { assertTrue, report } = createTestContext();
25
25
 
26
26
  // Minimal mock for ExtensionContext — only the fields recoverTimedOutUnit touches.
27
27
  function makeMockCtx() {
@@ -55,12 +55,12 @@ function makeMockPi() {
55
55
  await recoverTimedOutUnit(ctx, pi, "execute-task", "M001/S01/T01", "idle", emptyRctx);
56
56
  } catch (err: any) {
57
57
  crashed = true;
58
- assert.ok(
58
+ assertTrue(
59
59
  err.message.includes("path") || err.message.includes("string") || err.code === "ERR_INVALID_ARG_TYPE",
60
60
  `should crash with path/type error, got: ${err.message}`,
61
61
  );
62
62
  }
63
- assert.ok(crashed, "should crash when basePath is undefined (reproduces #1855)");
63
+ assertTrue(crashed, "should crash when basePath is undefined (reproduces #1855)");
64
64
  }
65
65
 
66
66
  // ═══ #1855: valid RecoveryContext does not crash ═════════════════════════════
@@ -90,11 +90,13 @@ function makeMockPi() {
90
90
  crashed = true;
91
91
  console.error(` Unexpected crash: ${err.message}`);
92
92
  }
93
- assert.ok(!crashed, "should not crash with valid basePath");
93
+ assertTrue(!crashed, "should not crash with valid basePath");
94
94
  // With no runtime record on disk and recoveryAttempts=0, the function
95
95
  // should attempt steering recovery (sendMessage) and return "recovered".
96
- assert.ok(result === "recovered", `should return 'recovered', got '${result}'`);
96
+ assertTrue(result === "recovered", `should return 'recovered', got '${result}'`);
97
97
  } finally {
98
98
  rmSync(base, { recursive: true, force: true });
99
99
  }
100
100
  }
101
+
102
+ report();
@@ -23,15 +23,15 @@ import { tmpdir } from "node:os";
23
23
  import { execSync } from "node:child_process";
24
24
 
25
25
  import { ensureGsdSymlink, externalGsdRoot } from "../repo-identity.ts";
26
- import { describe, test } from 'node:test';
27
- import assert from 'node:assert/strict';
26
+ import { createTestContext } from "./test-helpers.ts";
28
27
 
28
+ const { assertEq, assertTrue, report } = createTestContext();
29
29
 
30
30
  function run(command: string, cwd: string): string {
31
31
  return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
32
32
  }
33
33
 
34
- describe('symlink-numbered-variants', async () => {
34
+ async function main(): Promise<void> {
35
35
  const base = realpathSync(mkdtempSync(join(tmpdir(), "gsd-symlink-variants-")));
36
36
  const stateDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-state-variants-")));
37
37
 
@@ -58,14 +58,14 @@ describe('symlink-numbered-variants', async () => {
58
58
  mkdirSync(join(base, ".gsd 4"), { recursive: true });
59
59
 
60
60
  const result = ensureGsdSymlink(base);
61
- assert.deepStrictEqual(result, externalPath, "ensureGsdSymlink returns external path");
62
- assert.ok(existsSync(join(base, ".gsd")), ".gsd exists after ensureGsdSymlink");
63
- assert.ok(lstatSync(join(base, ".gsd")).isSymbolicLink(), ".gsd is a symlink");
61
+ assertEq(result, externalPath, "ensureGsdSymlink returns external path");
62
+ assertTrue(existsSync(join(base, ".gsd")), ".gsd exists after ensureGsdSymlink");
63
+ assertTrue(lstatSync(join(base, ".gsd")).isSymbolicLink(), ".gsd is a symlink");
64
64
 
65
65
  // The numbered variants must have been removed
66
- assert.ok(!existsSync(join(base, ".gsd 2")), '".gsd 2" directory was cleaned up');
67
- assert.ok(!existsSync(join(base, ".gsd 3")), '".gsd 3" directory was cleaned up');
68
- assert.ok(!existsSync(join(base, ".gsd 4")), '".gsd 4" directory was cleaned up');
66
+ assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" directory was cleaned up');
67
+ assertTrue(!existsSync(join(base, ".gsd 3")), '".gsd 3" directory was cleaned up');
68
+ assertTrue(!existsSync(join(base, ".gsd 4")), '".gsd 4" directory was cleaned up');
69
69
  }
70
70
 
71
71
  // ── Test: numbered variant symlinks are cleaned up ─────────────────
@@ -82,12 +82,12 @@ describe('symlink-numbered-variants', async () => {
82
82
  symlinkSync(staleTarget, join(base, ".gsd 3"), "junction");
83
83
 
84
84
  const result = ensureGsdSymlink(base);
85
- assert.deepStrictEqual(result, externalPath, "ensureGsdSymlink returns external path when variants exist");
86
- assert.ok(existsSync(join(base, ".gsd")), ".gsd exists");
87
- assert.ok(lstatSync(join(base, ".gsd")).isSymbolicLink(), ".gsd is a symlink");
85
+ assertEq(result, externalPath, "ensureGsdSymlink returns external path when variants exist");
86
+ assertTrue(existsSync(join(base, ".gsd")), ".gsd exists");
87
+ assertTrue(lstatSync(join(base, ".gsd")).isSymbolicLink(), ".gsd is a symlink");
88
88
 
89
- assert.ok(!existsSync(join(base, ".gsd 2")), '".gsd 2" symlink variant was cleaned up');
90
- assert.ok(!existsSync(join(base, ".gsd 3")), '".gsd 3" symlink variant was cleaned up');
89
+ assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" symlink variant was cleaned up');
90
+ assertTrue(!existsSync(join(base, ".gsd 3")), '".gsd 3" symlink variant was cleaned up');
91
91
  }
92
92
 
93
93
  // ── Test: real .gsd directory blocks symlink, but variants still cleaned ──
@@ -104,12 +104,12 @@ describe('symlink-numbered-variants', async () => {
104
104
 
105
105
  const result = ensureGsdSymlink(base);
106
106
  // When .gsd is a real directory, ensureGsdSymlink preserves it
107
- assert.deepStrictEqual(result, join(base, ".gsd"), "real .gsd directory preserved");
108
- assert.ok(lstatSync(join(base, ".gsd")).isDirectory(), ".gsd remains a directory");
107
+ assertEq(result, join(base, ".gsd"), "real .gsd directory preserved");
108
+ assertTrue(lstatSync(join(base, ".gsd")).isDirectory(), ".gsd remains a directory");
109
109
 
110
110
  // But the numbered variants should still be cleaned up
111
- assert.ok(!existsSync(join(base, ".gsd 2")), '".gsd 2" cleaned even when .gsd is a directory');
112
- assert.ok(!existsSync(join(base, ".gsd 3")), '".gsd 3" cleaned even when .gsd is a directory');
111
+ assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" cleaned even when .gsd is a directory');
112
+ assertTrue(!existsSync(join(base, ".gsd 3")), '".gsd 3" cleaned even when .gsd is a directory');
113
113
  }
114
114
 
115
115
  // ── Test: only numeric-suffixed variants are removed ───────────────
@@ -127,10 +127,10 @@ describe('symlink-numbered-variants', async () => {
127
127
 
128
128
  ensureGsdSymlink(base);
129
129
 
130
- assert.ok(existsSync(join(base, ".gsd-backup")), ".gsd-backup is NOT removed");
131
- assert.ok(existsSync(join(base, ".gsd_old")), ".gsd_old is NOT removed");
132
- assert.ok(!existsSync(join(base, ".gsd 2")), '".gsd 2" removed');
133
- assert.ok(!existsSync(join(base, ".gsd 10")), '".gsd 10" removed');
130
+ assertTrue(existsSync(join(base, ".gsd-backup")), ".gsd-backup is NOT removed");
131
+ assertTrue(existsSync(join(base, ".gsd_old")), ".gsd_old is NOT removed");
132
+ assertTrue(!existsSync(join(base, ".gsd 2")), '".gsd 2" removed');
133
+ assertTrue(!existsSync(join(base, ".gsd 10")), '".gsd 10" removed');
134
134
 
135
135
  // Cleanup non-variant dirs
136
136
  rmSync(join(base, ".gsd-backup"), { recursive: true, force: true });
@@ -141,5 +141,11 @@ describe('symlink-numbered-variants', async () => {
141
141
  delete process.env.GSD_STATE_DIR;
142
142
  try { rmSync(base, { recursive: true, force: true }); } catch { /* ignore */ }
143
143
  try { rmSync(stateDir, { recursive: true, force: true }); } catch { /* ignore */ }
144
+ report();
144
145
  }
146
+ }
147
+
148
+ main().catch((error) => {
149
+ console.error(error);
150
+ process.exit(1);
145
151
  });