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
@@ -1,114 +1,125 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  // gsd-inspect — Tests for /gsd inspect output formatting
4
2
  //
5
3
  // Tests the pure formatInspectOutput function with known data.
6
4
 
5
+ import { createTestContext } from './test-helpers.ts';
7
6
  import { formatInspectOutput, type InspectData } from '../commands-inspect.ts';
8
7
 
9
- describe('gsd-inspect', () => {
10
- test('full output formatting', () => {
11
- const data: InspectData = {
12
- schemaVersion: 2,
13
- counts: { decisions: 12, requirements: 8, artifacts: 3 },
14
- recentDecisions: [
15
- { id: "D012", decision: "Use SQLite for persistence", choice: "node:sqlite with fallback" },
16
- { id: "D011", decision: "Markdown dual-write", choice: "DB-first then regenerate" },
17
- ],
18
- recentRequirements: [
19
- { id: "R015", status: "active", description: "Commands register via pi.registerCommand" },
20
- { id: "R014", status: "active", description: "DB writes use upsert pattern" },
21
- ],
22
- };
23
-
24
- const output = formatInspectOutput(data);
25
-
26
- assert.match(output, /=== GSD Database Inspect ===/, "contains header");
27
- assert.match(output, /Schema version: 2/, "contains schema version");
28
- assert.match(output, /Decisions:\s+12/, "contains decisions count");
29
- assert.match(output, /Requirements:\s+8/, "contains requirements count");
30
- assert.match(output, /Artifacts:\s+3/, "contains artifacts count");
31
- assert.match(output, /Recent decisions:/, "contains recent decisions header");
32
- assert.match(output, /D012: Use SQLite for persistence → node:sqlite with fallback/, "contains D012 entry");
33
- assert.match(output, /D011: Markdown dual-write → DB-first then regenerate/, "contains D011 entry");
34
- assert.match(output, /Recent requirements:/, "contains recent requirements header");
35
- assert.match(output, /R015 \[active\]: Commands register via pi\.registerCommand/, "contains R015 entry");
36
- assert.match(output, /R014 \[active\]: DB writes use upsert pattern/, "contains R014 entry");
37
- });
38
-
39
- test('empty data', () => {
40
- const data: InspectData = {
41
- schemaVersion: 1,
42
- counts: { decisions: 0, requirements: 0, artifacts: 0 },
43
- recentDecisions: [],
44
- recentRequirements: [],
45
- };
46
-
47
- const output = formatInspectOutput(data);
48
-
49
- assert.match(output, /Schema version: 1/, "contains schema version 1");
50
- assert.match(output, /Decisions:\s+0/, "zero decisions");
51
- assert.match(output, /Requirements:\s+0/, "zero requirements");
52
- assert.match(output, /Artifacts:\s+0/, "zero artifacts");
53
- assert.ok(!output.includes("Recent decisions:"), "no recent decisions section when empty");
54
- assert.ok(!output.includes("Recent requirements:"), "no recent requirements section when empty");
55
- });
56
-
57
- test('null schema version', () => {
58
- const data: InspectData = {
59
- schemaVersion: null,
60
- counts: { decisions: 0, requirements: 0, artifacts: 0 },
61
- recentDecisions: [],
62
- recentRequirements: [],
63
- };
64
-
65
- const output = formatInspectOutput(data);
66
- assert.match(output, /Schema version: unknown/, "null version shows as unknown");
67
- });
68
-
69
- test('five recent entries', () => {
70
- const data: InspectData = {
71
- schemaVersion: 2,
72
- counts: { decisions: 5, requirements: 5, artifacts: 0 },
73
- recentDecisions: [
74
- { id: "D005", decision: "Dec 5", choice: "C5" },
75
- { id: "D004", decision: "Dec 4", choice: "C4" },
76
- { id: "D003", decision: "Dec 3", choice: "C3" },
77
- { id: "D002", decision: "Dec 2", choice: "C2" },
78
- { id: "D001", decision: "Dec 1", choice: "C1" },
79
- ],
80
- recentRequirements: [
81
- { id: "R005", status: "active", description: "Req 5" },
82
- { id: "R004", status: "done", description: "Req 4" },
83
- { id: "R003", status: "active", description: "Req 3" },
84
- { id: "R002", status: "active", description: "Req 2" },
85
- { id: "R001", status: "done", description: "Req 1" },
86
- ],
87
- };
88
-
89
- const output = formatInspectOutput(data);
90
-
91
- for (let i = 1; i <= 5; i++) {
92
- assert.match(output, new RegExp(`D00${i}: Dec ${i} → C${i}`), `contains D00${i}`);
93
- }
94
- for (let i = 1; i <= 5; i++) {
95
- assert.match(output, new RegExp(`R00${i}`), `contains R00${i}`);
96
- }
97
- assert.match(output, /\[active\]/, "contains active status");
98
- assert.match(output, /\[done\]/, "contains done status");
99
- });
100
-
101
- test('output format', () => {
102
- const data: InspectData = {
103
- schemaVersion: 2,
104
- counts: { decisions: 1, requirements: 1, artifacts: 0 },
105
- recentDecisions: [{ id: "D001", decision: "Test", choice: "Yes" }],
106
- recentRequirements: [{ id: "R001", status: "active", description: "Test req" }],
107
- };
108
-
109
- const output = formatInspectOutput(data);
110
- const lines = output.split("\n");
111
- assert.ok(lines.length > 5, "output has multiple lines");
112
- assert.ok(!output.startsWith("{"), "output is not JSON");
113
- });
114
- });
8
+ const { assertEq, assertTrue, assertMatch, report } = createTestContext();
9
+
10
+ // ── formats output with schema version, counts, and recent entries ──
11
+ console.log("# === gsd-inspect: full output formatting ===");
12
+ {
13
+ const data: InspectData = {
14
+ schemaVersion: 2,
15
+ counts: { decisions: 12, requirements: 8, artifacts: 3 },
16
+ recentDecisions: [
17
+ { id: "D012", decision: "Use SQLite for persistence", choice: "node:sqlite with fallback" },
18
+ { id: "D011", decision: "Markdown dual-write", choice: "DB-first then regenerate" },
19
+ ],
20
+ recentRequirements: [
21
+ { id: "R015", status: "active", description: "Commands register via pi.registerCommand" },
22
+ { id: "R014", status: "active", description: "DB writes use upsert pattern" },
23
+ ],
24
+ };
25
+
26
+ const output = formatInspectOutput(data);
27
+
28
+ assertMatch(output, /=== GSD Database Inspect ===/, "contains header");
29
+ assertMatch(output, /Schema version: 2/, "contains schema version");
30
+ assertMatch(output, /Decisions:\s+12/, "contains decisions count");
31
+ assertMatch(output, /Requirements:\s+8/, "contains requirements count");
32
+ assertMatch(output, /Artifacts:\s+3/, "contains artifacts count");
33
+ assertMatch(output, /Recent decisions:/, "contains recent decisions header");
34
+ assertMatch(output, /D012: Use SQLite for persistence → node:sqlite with fallback/, "contains D012 entry");
35
+ assertMatch(output, /D011: Markdown dual-write DB-first then regenerate/, "contains D011 entry");
36
+ assertMatch(output, /Recent requirements:/, "contains recent requirements header");
37
+ assertMatch(output, /R015 \[active\]: Commands register via pi\.registerCommand/, "contains R015 entry");
38
+ assertMatch(output, /R014 \[active\]: DB writes use upsert pattern/, "contains R014 entry");
39
+ }
40
+
41
+ // ── handles zero counts and no recent entries ──
42
+ console.log("# === gsd-inspect: empty data ===");
43
+ {
44
+ const data: InspectData = {
45
+ schemaVersion: 1,
46
+ counts: { decisions: 0, requirements: 0, artifacts: 0 },
47
+ recentDecisions: [],
48
+ recentRequirements: [],
49
+ };
50
+
51
+ const output = formatInspectOutput(data);
52
+
53
+ assertMatch(output, /Schema version: 1/, "contains schema version 1");
54
+ assertMatch(output, /Decisions:\s+0/, "zero decisions");
55
+ assertMatch(output, /Requirements:\s+0/, "zero requirements");
56
+ assertMatch(output, /Artifacts:\s+0/, "zero artifacts");
57
+ assertTrue(!output.includes("Recent decisions:"), "no recent decisions section when empty");
58
+ assertTrue(!output.includes("Recent requirements:"), "no recent requirements section when empty");
59
+ }
60
+
61
+ // ── handles null schema version ──
62
+ console.log("# === gsd-inspect: null schema version ===");
63
+ {
64
+ const data: InspectData = {
65
+ schemaVersion: null,
66
+ counts: { decisions: 0, requirements: 0, artifacts: 0 },
67
+ recentDecisions: [],
68
+ recentRequirements: [],
69
+ };
70
+
71
+ const output = formatInspectOutput(data);
72
+ assertMatch(output, /Schema version: unknown/, "null version shows as unknown");
73
+ }
74
+
75
+ // ── formats up to 5 recent entries ──
76
+ console.log("# === gsd-inspect: five recent entries ===");
77
+ {
78
+ const data: InspectData = {
79
+ schemaVersion: 2,
80
+ counts: { decisions: 5, requirements: 5, artifacts: 0 },
81
+ recentDecisions: [
82
+ { id: "D005", decision: "Dec 5", choice: "C5" },
83
+ { id: "D004", decision: "Dec 4", choice: "C4" },
84
+ { id: "D003", decision: "Dec 3", choice: "C3" },
85
+ { id: "D002", decision: "Dec 2", choice: "C2" },
86
+ { id: "D001", decision: "Dec 1", choice: "C1" },
87
+ ],
88
+ recentRequirements: [
89
+ { id: "R005", status: "active", description: "Req 5" },
90
+ { id: "R004", status: "done", description: "Req 4" },
91
+ { id: "R003", status: "active", description: "Req 3" },
92
+ { id: "R002", status: "active", description: "Req 2" },
93
+ { id: "R001", status: "done", description: "Req 1" },
94
+ ],
95
+ };
96
+
97
+ const output = formatInspectOutput(data);
98
+
99
+ for (let i = 1; i <= 5; i++) {
100
+ assertMatch(output, new RegExp(`D00${i}: Dec ${i} → C${i}`), `contains D00${i}`);
101
+ }
102
+ for (let i = 1; i <= 5; i++) {
103
+ assertMatch(output, new RegExp(`R00${i}`), `contains R00${i}`);
104
+ }
105
+ assertMatch(output, /\[active\]/, "contains active status");
106
+ assertMatch(output, /\[done\]/, "contains done status");
107
+ }
108
+
109
+ // ── output is multiline text (not JSON) ──
110
+ console.log("# === gsd-inspect: output format ===");
111
+ {
112
+ const data: InspectData = {
113
+ schemaVersion: 2,
114
+ counts: { decisions: 1, requirements: 1, artifacts: 0 },
115
+ recentDecisions: [{ id: "D001", decision: "Test", choice: "Yes" }],
116
+ recentRequirements: [{ id: "R001", status: "active", description: "Test req" }],
117
+ };
118
+
119
+ const output = formatInspectOutput(data);
120
+ const lines = output.split("\n");
121
+ assertTrue(lines.length > 5, "output has multiple lines");
122
+ assertTrue(!output.startsWith("{"), "output is not JSON");
123
+ }
124
+
125
+ report();
@@ -1,5 +1,3 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  // gsd-recover.test.ts — Tests for the `gsd recover` recovery logic.
4
2
  // Verifies: populate DB → clear hierarchy → recover from markdown → state matches.
5
3
 
@@ -24,6 +22,10 @@ import {
24
22
  } from '../gsd-db.ts';
25
23
  import { migrateHierarchyToDb } from '../md-importer.ts';
26
24
  import { deriveStateFromDb, invalidateStateCache } from '../state.ts';
25
+ import { createTestContext } from './test-helpers.ts';
26
+
27
+ const { assertEq, assertTrue, report } = createTestContext();
28
+
27
29
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
28
30
 
29
31
  function createFixtureBase(): string {
@@ -146,8 +148,10 @@ function clearHierarchyTables(): void {
146
148
 
147
149
  // ─── Tests ────────────────────────────────────────────────────────────────
148
150
 
149
- describe('gsd-recover', async () => {
150
- test('full round-trip (populate, clear, recover, verify)', async () => {
151
+ async function main() {
152
+ // ─── Test (a): Full recovery round-trip ─────────────────────────────────
153
+ console.log('\n=== recover: full round-trip (populate → clear → recover → verify) ===');
154
+ {
151
155
  const base = createFixtureBase();
152
156
  try {
153
157
  // Set up markdown fixtures
@@ -159,14 +163,14 @@ describe('gsd-recover', async () => {
159
163
  // Step 1: Open DB and populate from markdown
160
164
  openDatabase(':memory:');
161
165
  const counts1 = migrateHierarchyToDb(base);
162
- assert.deepStrictEqual(counts1.milestones, 1, 'round-trip: initial migration - 1 milestone');
163
- assert.deepStrictEqual(counts1.slices, 2, 'round-trip: initial migration - 2 slices');
164
- assert.ok(counts1.tasks >= 5, 'round-trip: initial migration - at least 5 tasks');
166
+ assertEq(counts1.milestones, 1, 'round-trip: initial migration 1 milestone');
167
+ assertEq(counts1.slices, 2, 'round-trip: initial migration 2 slices');
168
+ assertTrue(counts1.tasks >= 5, 'round-trip: initial migration at least 5 tasks');
165
169
 
166
170
  // Step 2: Capture state from DB before clearing
167
171
  invalidateStateCache();
168
172
  const stateBefore = await deriveStateFromDb(base);
169
- assert.ok(stateBefore.activeMilestone !== null, 'round-trip: state before has active milestone');
173
+ assertTrue(stateBefore.activeMilestone !== null, 'round-trip: state before has active milestone');
170
174
  const milestonesBefore = getAllMilestones();
171
175
  const slicesBefore = getMilestoneSlices('M001');
172
176
  const s01TasksBefore = getSliceTasks('M001', 'S01');
@@ -175,30 +179,30 @@ describe('gsd-recover', async () => {
175
179
  // Step 3: Clear hierarchy tables
176
180
  clearHierarchyTables();
177
181
  const milestonesAfterClear = getAllMilestones();
178
- assert.deepStrictEqual(milestonesAfterClear.length, 0, 'round-trip: milestones cleared');
182
+ assertEq(milestonesAfterClear.length, 0, 'round-trip: milestones cleared');
179
183
 
180
184
  // Step 4: Recover from markdown
181
185
  const counts2 = migrateHierarchyToDb(base);
182
- assert.deepStrictEqual(counts2.milestones, counts1.milestones, 'round-trip: recovery milestone count matches');
183
- assert.deepStrictEqual(counts2.slices, counts1.slices, 'round-trip: recovery slice count matches');
184
- assert.deepStrictEqual(counts2.tasks, counts1.tasks, 'round-trip: recovery task count matches');
186
+ assertEq(counts2.milestones, counts1.milestones, 'round-trip: recovery milestone count matches');
187
+ assertEq(counts2.slices, counts1.slices, 'round-trip: recovery slice count matches');
188
+ assertEq(counts2.tasks, counts1.tasks, 'round-trip: recovery task count matches');
185
189
 
186
190
  // Step 5: Verify state matches
187
191
  invalidateStateCache();
188
192
  const stateAfter = await deriveStateFromDb(base);
189
193
 
190
- assert.deepStrictEqual(stateAfter.phase, stateBefore.phase, 'round-trip: phase matches');
191
- assert.deepStrictEqual(
194
+ assertEq(stateAfter.phase, stateBefore.phase, 'round-trip: phase matches');
195
+ assertEq(
192
196
  stateAfter.activeMilestone?.id,
193
197
  stateBefore.activeMilestone?.id,
194
198
  'round-trip: active milestone ID matches',
195
199
  );
196
- assert.deepStrictEqual(
200
+ assertEq(
197
201
  stateAfter.activeSlice?.id,
198
202
  stateBefore.activeSlice?.id,
199
203
  'round-trip: active slice ID matches',
200
204
  );
201
- assert.deepStrictEqual(
205
+ assertEq(
202
206
  stateAfter.activeTask?.id,
203
207
  stateBefore.activeTask?.id,
204
208
  'round-trip: active task ID matches',
@@ -206,30 +210,32 @@ describe('gsd-recover', async () => {
206
210
 
207
211
  // Verify row-level data matches
208
212
  const milestonesAfter = getAllMilestones();
209
- assert.deepStrictEqual(milestonesAfter.length, milestonesBefore.length, 'round-trip: milestone row count');
210
- assert.deepStrictEqual(milestonesAfter[0]?.id, milestonesBefore[0]?.id, 'round-trip: milestone ID');
211
- assert.deepStrictEqual(milestonesAfter[0]?.title, milestonesBefore[0]?.title, 'round-trip: milestone title');
213
+ assertEq(milestonesAfter.length, milestonesBefore.length, 'round-trip: milestone row count');
214
+ assertEq(milestonesAfter[0]?.id, milestonesBefore[0]?.id, 'round-trip: milestone ID');
215
+ assertEq(milestonesAfter[0]?.title, milestonesBefore[0]?.title, 'round-trip: milestone title');
212
216
 
213
217
  const slicesAfter = getMilestoneSlices('M001');
214
- assert.deepStrictEqual(slicesAfter.length, slicesBefore.length, 'round-trip: slice row count');
215
- assert.deepStrictEqual(slicesAfter[0]?.id, slicesBefore[0]?.id, 'round-trip: S01 ID');
216
- assert.deepStrictEqual(slicesAfter[0]?.status, slicesBefore[0]?.status, 'round-trip: S01 status');
217
- assert.deepStrictEqual(slicesAfter[1]?.id, slicesBefore[1]?.id, 'round-trip: S02 ID');
218
+ assertEq(slicesAfter.length, slicesBefore.length, 'round-trip: slice row count');
219
+ assertEq(slicesAfter[0]?.id, slicesBefore[0]?.id, 'round-trip: S01 ID');
220
+ assertEq(slicesAfter[0]?.status, slicesBefore[0]?.status, 'round-trip: S01 status');
221
+ assertEq(slicesAfter[1]?.id, slicesBefore[1]?.id, 'round-trip: S02 ID');
218
222
 
219
223
  const s01TasksAfter = getSliceTasks('M001', 'S01');
220
- assert.deepStrictEqual(s01TasksAfter.length, s01TasksBefore.length, 'round-trip: S01 task count');
224
+ assertEq(s01TasksAfter.length, s01TasksBefore.length, 'round-trip: S01 task count');
221
225
 
222
226
  const s02TasksAfter = getSliceTasks('M001', 'S02');
223
- assert.deepStrictEqual(s02TasksAfter.length, s02TasksBefore.length, 'round-trip: S02 task count');
227
+ assertEq(s02TasksAfter.length, s02TasksBefore.length, 'round-trip: S02 task count');
224
228
 
225
229
  closeDatabase();
226
230
  } finally {
227
231
  closeDatabase();
228
232
  cleanup(base);
229
233
  }
230
- });
234
+ }
231
235
 
232
- test('v8 planning columns populated', async () => {
236
+ // ─── Test (a2): v8 planning columns populated after recovery ───────────
237
+ console.log('\n=== recover: v8 planning columns populated ===');
238
+ {
233
239
  const base = createFixtureBase();
234
240
  try {
235
241
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_M001);
@@ -242,70 +248,75 @@ describe('gsd-recover', async () => {
242
248
 
243
249
  // Milestone planning columns
244
250
  const milestone = getMilestone('M001');
245
- assert.ok(milestone !== null, 'v8: milestone exists');
246
- assert.deepStrictEqual(milestone!.vision, 'Test recovery round-trip.', 'v8: milestone vision populated');
247
- assert.ok(milestone!.success_criteria.length >= 2, 'v8: milestone success_criteria has entries');
248
- assert.deepStrictEqual(milestone!.success_criteria[0], 'All recovery tests pass', 'v8: first success criterion');
249
- assert.ok(milestone!.boundary_map_markdown.includes('Boundary Map'), 'v8: boundary_map_markdown populated');
250
- assert.ok(milestone!.boundary_map_markdown.includes('S01'), 'v8: boundary_map_markdown has S01');
251
+ assertTrue(milestone !== null, 'v8: milestone exists');
252
+ assertEq(milestone!.vision, 'Test recovery round-trip.', 'v8: milestone vision populated');
253
+ assertTrue(milestone!.success_criteria.length >= 2, 'v8: milestone success_criteria has entries');
254
+ assertEq(milestone!.success_criteria[0], 'All recovery tests pass', 'v8: first success criterion');
255
+ assertTrue(milestone!.boundary_map_markdown.includes('Boundary Map'), 'v8: boundary_map_markdown populated');
256
+ assertTrue(milestone!.boundary_map_markdown.includes('S01'), 'v8: boundary_map_markdown has S01');
251
257
 
252
258
  // Tool-only fields left empty per D004
253
- assert.deepStrictEqual(milestone!.key_risks.length, 0, 'v8: key_risks left empty (tool-only per D004)');
254
- assert.deepStrictEqual(milestone!.requirement_coverage, '', 'v8: requirement_coverage left empty (tool-only per D004)');
259
+ assertEq(milestone!.key_risks.length, 0, 'v8: key_risks left empty (tool-only per D004)');
260
+ assertEq(milestone!.requirement_coverage, '', 'v8: requirement_coverage left empty (tool-only per D004)');
255
261
 
256
262
  // Slice planning columns
257
263
  const sliceS01 = getSlice('M001', 'S01');
258
- assert.ok(sliceS01 !== null, 'v8: slice S01 exists');
259
- assert.deepStrictEqual(sliceS01!.goal, 'Setup fixtures.', 'v8: S01 goal populated');
264
+ assertTrue(sliceS01 !== null, 'v8: slice S01 exists');
265
+ assertEq(sliceS01!.goal, 'Setup fixtures.', 'v8: S01 goal populated');
260
266
 
261
267
  const sliceS02 = getSlice('M001', 'S02');
262
- assert.ok(sliceS02 !== null, 'v8: slice S02 exists');
263
- assert.deepStrictEqual(sliceS02!.goal, 'Build core.', 'v8: S02 goal populated');
268
+ assertTrue(sliceS02 !== null, 'v8: slice S02 exists');
269
+ assertEq(sliceS02!.goal, 'Build core.', 'v8: S02 goal populated');
264
270
 
265
271
  // Slice tool-only fields left empty per D004
266
- assert.deepStrictEqual(sliceS01!.proof_level, '', 'v8: S01 proof_level left empty (tool-only per D004)');
272
+ assertEq(sliceS01!.proof_level, '', 'v8: S01 proof_level left empty (tool-only per D004)');
267
273
 
268
- // Task planning columns - S01/T01
274
+ // Task planning columns S01/T01
269
275
  const taskS01T01 = getTask('M001', 'S01', 'T01');
270
- assert.ok(taskS01T01 !== null, 'v8: task S01/T01 exists');
271
- assert.ok(taskS01T01!.files.length >= 2, 'v8: S01/T01 files populated');
272
- assert.ok(taskS01T01!.files.includes('init.ts'), 'v8: S01/T01 files includes init.ts');
273
- assert.ok(taskS01T01!.files.includes('config.ts'), 'v8: S01/T01 files includes config.ts');
274
- assert.deepStrictEqual(taskS01T01!.verify, '`node test-init.ts`', 'v8: S01/T01 verify populated');
276
+ assertTrue(taskS01T01 !== null, 'v8: task S01/T01 exists');
277
+ assertTrue(taskS01T01!.files.length >= 2, 'v8: S01/T01 files populated');
278
+ assertTrue(taskS01T01!.files.includes('init.ts'), 'v8: S01/T01 files includes init.ts');
279
+ assertTrue(taskS01T01!.files.includes('config.ts'), 'v8: S01/T01 files includes config.ts');
280
+ assertEq(taskS01T01!.verify, '`node test-init.ts`', 'v8: S01/T01 verify populated');
275
281
 
276
- // Task planning columns - S02/T02
282
+ // Task planning columns S02/T02
277
283
  const taskS02T02 = getTask('M001', 'S02', 'T02');
278
- assert.ok(taskS02T02 !== null, 'v8: task S02/T02 exists');
279
- assert.ok(taskS02T02!.files.length >= 2, 'v8: S02/T02 files populated');
280
- assert.ok(taskS02T02!.files.includes('test-core.ts'), 'v8: S02/T02 files includes test-core.ts');
281
- assert.deepStrictEqual(taskS02T02!.verify, '`npm test`', 'v8: S02/T02 verify populated');
284
+ assertTrue(taskS02T02 !== null, 'v8: task S02/T02 exists');
285
+ assertTrue(taskS02T02!.files.length >= 2, 'v8: S02/T02 files populated');
286
+ assertTrue(taskS02T02!.files.includes('test-core.ts'), 'v8: S02/T02 files includes test-core.ts');
287
+ assertEq(taskS02T02!.verify, '`npm test`', 'v8: S02/T02 verify populated');
282
288
 
289
+ // Task with no Files/Verify — not applicable since all fixtures now have them,
290
+ // but confirm a task from S02 has correct data
283
291
  const taskS02T03 = getTask('M001', 'S02', 'T03');
284
- assert.ok(taskS02T03 !== null, 'v8: task S02/T03 exists');
285
- assert.ok(taskS02T03!.files.includes('polish.ts'), 'v8: S02/T03 files includes polish.ts');
286
- assert.deepStrictEqual(taskS02T03!.verify, '`node test-polish.ts`', 'v8: S02/T03 verify populated');
292
+ assertTrue(taskS02T03 !== null, 'v8: task S02/T03 exists');
293
+ assertTrue(taskS02T03!.files.includes('polish.ts'), 'v8: S02/T03 files includes polish.ts');
294
+ assertEq(taskS02T03!.verify, '`node test-polish.ts`', 'v8: S02/T03 verify populated');
287
295
 
288
296
  // Diagnostic: v8 planning columns queryable via SQL
289
297
  const db = _getAdapter()!;
290
298
  const milestoneRow = db.prepare("SELECT vision, success_criteria, boundary_map_markdown FROM milestones WHERE id = 'M001'").get() as any;
291
- assert.ok(milestoneRow.vision.length > 0, 'v8-diag: vision column queryable');
292
- assert.ok(milestoneRow.boundary_map_markdown.length > 0, 'v8-diag: boundary_map_markdown column queryable');
299
+ assertTrue(milestoneRow.vision.length > 0, 'v8-diag: vision column queryable');
300
+ assertTrue(milestoneRow.boundary_map_markdown.length > 0, 'v8-diag: boundary_map_markdown column queryable');
293
301
 
294
302
  const sliceRow = db.prepare("SELECT goal FROM slices WHERE milestone_id = 'M001' AND id = 'S01'").get() as any;
295
- assert.ok(sliceRow.goal.length > 0, 'v8-diag: goal column queryable');
303
+ assertTrue(sliceRow.goal.length > 0, 'v8-diag: goal column queryable');
296
304
 
297
305
  const taskRow = db.prepare("SELECT files, verify FROM tasks WHERE milestone_id = 'M001' AND slice_id = 'S01' AND id = 'T01'").get() as any;
298
- assert.ok(taskRow.files.length > 2, 'v8-diag: files column queryable (JSON array)');
299
- assert.ok(taskRow.verify.length > 0, 'v8-diag: verify column queryable');
306
+ assertTrue(taskRow.files.length > 2, 'v8-diag: files column queryable (JSON array)');
307
+ assertTrue(taskRow.verify.length > 0, 'v8-diag: verify column queryable');
300
308
 
301
309
  closeDatabase();
302
310
  } finally {
303
311
  closeDatabase();
304
312
  cleanup(base);
305
313
  }
306
- });
314
+ }
315
+
307
316
 
308
- test('idempotent - double recovery produces same state', async () => {
317
+ // ─── Test (b): Idempotent recovery double recover ────────────────────
318
+ console.log('\n=== recover: idempotent — double recovery produces same state ===');
319
+ {
309
320
  const base = createFixtureBase();
310
321
  try {
311
322
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_M001);
@@ -326,18 +337,18 @@ describe('gsd-recover', async () => {
326
337
  invalidateStateCache();
327
338
  const state2 = await deriveStateFromDb(base);
328
339
 
329
- assert.deepStrictEqual(state2.phase, state1.phase, 'idempotent: phase matches');
330
- assert.deepStrictEqual(
340
+ assertEq(state2.phase, state1.phase, 'idempotent: phase matches');
341
+ assertEq(
331
342
  state2.activeMilestone?.id,
332
343
  state1.activeMilestone?.id,
333
344
  'idempotent: active milestone matches',
334
345
  );
335
- assert.deepStrictEqual(
346
+ assertEq(
336
347
  state2.activeSlice?.id,
337
348
  state1.activeSlice?.id,
338
349
  'idempotent: active slice matches',
339
350
  );
340
- assert.deepStrictEqual(
351
+ assertEq(
341
352
  state2.activeTask?.id,
342
353
  state1.activeTask?.id,
343
354
  'idempotent: active task matches',
@@ -348,9 +359,11 @@ describe('gsd-recover', async () => {
348
359
  closeDatabase();
349
360
  cleanup(base);
350
361
  }
351
- });
362
+ }
352
363
 
353
- test('preserves decisions/requirements', async () => {
364
+ // ─── Test (c): Recovery preserves non-hierarchy data ───────────────────
365
+ console.log('\n=== recover: preserves decisions/requirements ===');
366
+ {
354
367
  const base = createFixtureBase();
355
368
  try {
356
369
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_M001);
@@ -389,33 +402,35 @@ describe('gsd-recover', async () => {
389
402
 
390
403
  // Verify decisions and requirements survived
391
404
  const decisions = db.prepare('SELECT * FROM decisions').all();
392
- assert.deepStrictEqual(decisions.length, 1, 'preserve: decision survives clear');
393
- assert.deepStrictEqual((decisions[0] as any).id, 'D001', 'preserve: decision ID intact');
405
+ assertEq(decisions.length, 1, 'preserve: decision survives clear');
406
+ assertEq((decisions[0] as any).id, 'D001', 'preserve: decision ID intact');
394
407
 
395
408
  const requirements = db.prepare('SELECT * FROM requirements').all();
396
- assert.deepStrictEqual(requirements.length, 1, 'preserve: requirement survives clear');
397
- assert.deepStrictEqual((requirements[0] as any).id, 'R001', 'preserve: requirement ID intact');
409
+ assertEq(requirements.length, 1, 'preserve: requirement survives clear');
410
+ assertEq((requirements[0] as any).id, 'R001', 'preserve: requirement ID intact');
398
411
 
399
412
  // Recover hierarchy
400
413
  migrateHierarchyToDb(base);
401
414
  const milestones = getAllMilestones();
402
- assert.ok(milestones.length > 0, 'preserve: milestones recovered after clear');
415
+ assertTrue(milestones.length > 0, 'preserve: milestones recovered after clear');
403
416
 
404
417
  // Verify non-hierarchy data still intact after recovery
405
418
  const decisionsAfter = db.prepare('SELECT * FROM decisions').all();
406
- assert.deepStrictEqual(decisionsAfter.length, 1, 'preserve: decision still present after recovery');
419
+ assertEq(decisionsAfter.length, 1, 'preserve: decision still present after recovery');
407
420
 
408
421
  closeDatabase();
409
422
  } finally {
410
423
  closeDatabase();
411
424
  cleanup(base);
412
425
  }
413
- });
426
+ }
414
427
 
415
- test('empty milestones dir', async () => {
428
+ // ─── Test (d): Recovery from empty markdown dir ────────────────────────
429
+ console.log('\n=== recover: empty milestones dir ===');
430
+ {
416
431
  const base = createFixtureBase();
417
432
  try {
418
- // No milestones written - just the empty dir
433
+ // No milestones written just the empty dir
419
434
  openDatabase(':memory:');
420
435
 
421
436
  // Pre-populate to simulate existing state
@@ -424,17 +439,24 @@ describe('gsd-recover', async () => {
424
439
  // Clear and recover from empty
425
440
  clearHierarchyTables();
426
441
  const counts = migrateHierarchyToDb(base);
427
- assert.deepStrictEqual(counts.milestones, 0, 'empty: zero milestones recovered');
428
- assert.deepStrictEqual(counts.slices, 0, 'empty: zero slices recovered');
429
- assert.deepStrictEqual(counts.tasks, 0, 'empty: zero tasks recovered');
442
+ assertEq(counts.milestones, 0, 'empty: zero milestones recovered');
443
+ assertEq(counts.slices, 0, 'empty: zero slices recovered');
444
+ assertEq(counts.tasks, 0, 'empty: zero tasks recovered');
430
445
 
431
446
  const all = getAllMilestones();
432
- assert.deepStrictEqual(all.length, 0, 'empty: no milestones in DB after recovery');
447
+ assertEq(all.length, 0, 'empty: no milestones in DB after recovery');
433
448
 
434
449
  closeDatabase();
435
450
  } finally {
436
451
  closeDatabase();
437
452
  cleanup(base);
438
453
  }
439
- });
454
+ }
455
+
456
+ report();
457
+ }
458
+
459
+ main().catch((error) => {
460
+ console.error(error);
461
+ process.exit(1);
440
462
  });