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,5 +1,3 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
1
  /**
4
2
  * flag-file-db.test.ts — Verify that REPLAN.md and REPLAN-TRIGGER.md
5
3
  * flag-file detection in deriveStateFromDb() works from DB-only data
@@ -26,6 +24,10 @@ import {
26
24
  insertReplanHistory,
27
25
  _getAdapter,
28
26
  } from '../gsd-db.ts';
27
+ import { createTestContext } from './test-helpers.ts';
28
+
29
+ const { assertEq, assertTrue, report } = createTestContext();
30
+
29
31
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
30
32
 
31
33
  function createFixtureBase(): string {
@@ -76,10 +78,11 @@ const TASK_SUMMARY_STUB = `---\nblocker_discovered: false\n---\n# T01 Summary\nD
76
78
  // Tests
77
79
  // ═══════════════════════════════════════════════════════════════════════════
78
80
 
79
- describe('flag-file-db', async () => {
81
+ async function main(): Promise<void> {
80
82
 
81
83
  // ─── Test 1: blocker_discovered + no replan_history → replanning-slice ──
82
- test('flag-file-db: blocker + no history → replanning', async () => {
84
+ console.log('\n=== flag-file-db: blocker + no history → replanning ===');
85
+ {
83
86
  const base = createFixtureBase();
84
87
  try {
85
88
  // Write disk files needed by deriveStateFromDb (roadmap check, task dir check)
@@ -88,7 +91,7 @@ describe('flag-file-db', async () => {
88
91
  writeFile(base, 'milestones/M001/slices/S01/tasks/T02-PLAN.md', TASK_PLAN_STUB);
89
92
 
90
93
  openDatabase(':memory:');
91
- assert.ok(isDbAvailable(), 'test1: DB is available');
94
+ assertTrue(isDbAvailable(), 'test1: DB is available');
92
95
 
93
96
  insertMilestone({ id: 'M001', title: 'Flag-File DB Test', status: 'active' });
94
97
  insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', status: 'active', risk: 'low', depends: [] });
@@ -99,19 +102,20 @@ describe('flag-file-db', async () => {
99
102
  invalidateStateCache();
100
103
  const state = await deriveStateFromDb(base);
101
104
 
102
- assert.deepStrictEqual(state.phase, 'replanning-slice', 'test1: phase is replanning-slice');
103
- assert.ok(state.blockers.length > 0, 'test1: has blockers');
104
- assert.ok(state.blockers[0]?.includes('blocker'), 'test1: blocker message mentions blocker');
105
+ assertEq(state.phase, 'replanning-slice', 'test1: phase is replanning-slice');
106
+ assertTrue(state.blockers.length > 0, 'test1: has blockers');
107
+ assertTrue(state.blockers[0]?.includes('blocker'), 'test1: blocker message mentions blocker');
105
108
 
106
109
  closeDatabase();
107
110
  } finally {
108
111
  closeDatabase();
109
112
  cleanup(base);
110
113
  }
111
- });
114
+ }
112
115
 
113
116
  // ─── Test 2: blocker_discovered + replan_history exists → loop protection → executing ──
114
- test('flag-file-db: blocker + history → loop protection', async () => {
117
+ console.log('\n=== flag-file-db: blocker + history → loop protection ===');
118
+ {
115
119
  const base = createFixtureBase();
116
120
  try {
117
121
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -135,17 +139,18 @@ describe('flag-file-db', async () => {
135
139
  invalidateStateCache();
136
140
  const state = await deriveStateFromDb(base);
137
141
 
138
- assert.deepStrictEqual(state.phase, 'executing', 'test2: phase is executing (loop protection)');
142
+ assertEq(state.phase, 'executing', 'test2: phase is executing (loop protection)');
139
143
 
140
144
  closeDatabase();
141
145
  } finally {
142
146
  closeDatabase();
143
147
  cleanup(base);
144
148
  }
145
- });
149
+ }
146
150
 
147
151
  // ─── Test 3: replan_triggered_at set + no replan_history → replanning-slice ──
148
- test('flag-file-db: trigger column + no history → replanning', async () => {
152
+ console.log('\n=== flag-file-db: trigger column + no history → replanning ===');
153
+ {
149
154
  const base = createFixtureBase();
150
155
  try {
151
156
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -168,19 +173,20 @@ describe('flag-file-db', async () => {
168
173
  invalidateStateCache();
169
174
  const state = await deriveStateFromDb(base);
170
175
 
171
- assert.deepStrictEqual(state.phase, 'replanning-slice', 'test3: phase is replanning-slice');
172
- assert.ok(state.blockers.length > 0, 'test3: has blockers');
173
- assert.ok(state.blockers[0]?.includes('Triage replan trigger'), 'test3: blocker message mentions triage trigger');
176
+ assertEq(state.phase, 'replanning-slice', 'test3: phase is replanning-slice');
177
+ assertTrue(state.blockers.length > 0, 'test3: has blockers');
178
+ assertTrue(state.blockers[0]?.includes('Triage replan trigger'), 'test3: blocker message mentions triage trigger');
174
179
 
175
180
  closeDatabase();
176
181
  } finally {
177
182
  closeDatabase();
178
183
  cleanup(base);
179
184
  }
180
- });
185
+ }
181
186
 
182
187
  // ─── Test 4: replan_triggered_at set + replan_history exists → loop protection ──
183
- test('flag-file-db: trigger column + history → loop protection', async () => {
188
+ console.log('\n=== flag-file-db: trigger column + history → loop protection ===');
189
+ {
184
190
  const base = createFixtureBase();
185
191
  try {
186
192
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -210,17 +216,18 @@ describe('flag-file-db', async () => {
210
216
  invalidateStateCache();
211
217
  const state = await deriveStateFromDb(base);
212
218
 
213
- assert.deepStrictEqual(state.phase, 'executing', 'test4: phase is executing (loop protection)');
219
+ assertEq(state.phase, 'executing', 'test4: phase is executing (loop protection)');
214
220
 
215
221
  closeDatabase();
216
222
  } finally {
217
223
  closeDatabase();
218
224
  cleanup(base);
219
225
  }
220
- });
226
+ }
221
227
 
222
228
  // ─── Test 5: no blocker, no trigger → phase is executing ──────────────
223
- test('flag-file-db: no blocker, no trigger → executing', async () => {
229
+ console.log('\n=== flag-file-db: no blocker, no trigger → executing ===');
230
+ {
224
231
  const base = createFixtureBase();
225
232
  try {
226
233
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -238,19 +245,20 @@ describe('flag-file-db', async () => {
238
245
  invalidateStateCache();
239
246
  const state = await deriveStateFromDb(base);
240
247
 
241
- assert.deepStrictEqual(state.phase, 'executing', 'test5: phase is executing');
242
- assert.deepStrictEqual(state.activeTask?.id, 'T02', 'test5: activeTask is T02');
243
- assert.deepStrictEqual(state.blockers.length, 0, 'test5: no blockers');
248
+ assertEq(state.phase, 'executing', 'test5: phase is executing');
249
+ assertEq(state.activeTask?.id, 'T02', 'test5: activeTask is T02');
250
+ assertEq(state.blockers.length, 0, 'test5: no blockers');
244
251
 
245
252
  closeDatabase();
246
253
  } finally {
247
254
  closeDatabase();
248
255
  cleanup(base);
249
256
  }
250
- });
257
+ }
251
258
 
252
259
  // ─── Diagnostic test: DB column inspection ──────────────────────────
253
- test('flag-file-db: replan_triggered_at column is queryable', () => {
260
+ console.log('\n=== flag-file-db: replan_triggered_at column is queryable ===');
261
+ {
254
262
  openDatabase(':memory:');
255
263
 
256
264
  insertMilestone({ id: 'M001', title: 'Diagnostic', status: 'active' });
@@ -261,7 +269,7 @@ describe('flag-file-db', async () => {
261
269
  const before = adapter!.prepare(
262
270
  "SELECT id, replan_triggered_at FROM slices WHERE milestone_id = :mid",
263
271
  ).get({ ":mid": "M001" }) as Record<string, unknown>;
264
- assert.deepStrictEqual(before["replan_triggered_at"], null, 'diagnostic: replan_triggered_at initially null');
272
+ assertEq(before["replan_triggered_at"], null, 'diagnostic: replan_triggered_at initially null');
265
273
 
266
274
  // After setting
267
275
  adapter!.prepare(
@@ -271,8 +279,12 @@ describe('flag-file-db', async () => {
271
279
  const after = adapter!.prepare(
272
280
  "SELECT id, replan_triggered_at FROM slices WHERE milestone_id = :mid",
273
281
  ).get({ ":mid": "M001" }) as Record<string, unknown>;
274
- assert.deepStrictEqual(after["replan_triggered_at"], "2025-01-01T00:00:00Z", 'diagnostic: replan_triggered_at is set');
282
+ assertEq(after["replan_triggered_at"], "2025-01-01T00:00:00Z", 'diagnostic: replan_triggered_at is set');
275
283
 
276
284
  closeDatabase();
277
- });
278
- });
285
+ }
286
+
287
+ report();
288
+ }
289
+
290
+ main();
@@ -1,5 +1,4 @@
1
- import { describe, test } from 'node:test';
2
- import assert from 'node:assert/strict';
1
+ import { createTestContext } from './test-helpers.ts';
3
2
  import * as path from 'node:path';
4
3
  import * as os from 'node:os';
5
4
  import * as fs from 'node:fs';
@@ -14,6 +13,8 @@ import {
14
13
  saveDecisionToDb,
15
14
  } from '../db-writer.ts';
16
15
 
16
+ const { assertEq, assertTrue, report } = createTestContext();
17
+
17
18
  // ═══════════════════════════════════════════════════════════════════════════
18
19
  // Helpers
19
20
  // ═══════════════════════════════════════════════════════════════════════════
@@ -34,199 +35,206 @@ function cleanupDir(dir: string): void {
34
35
  // Bug reproduction: freeform DECISIONS.md content destroyed (#2301)
35
36
  // ═══════════════════════════════════════════════════════════════════════════
36
37
 
37
- describe('freeform-decisions', () => {
38
- test('parseDecisionsTable silently drops freeform content', () => {
39
- const freeform = `# Project Decisions
40
-
41
- ## Architecture
42
- We decided to use a microservices architecture because monoliths don't scale.
43
-
44
- ## Database
45
- PostgreSQL was chosen for its reliability and JSONB support.
46
-
47
- ## Deployment
48
- - Kubernetes for orchestration
49
- - Helm charts for packaging
50
- `;
51
-
52
- const parsed = parseDecisionsTable(freeform);
53
- assert.deepStrictEqual(parsed.length, 0, 'freeform content yields zero parsed decisions (expected — it is not a table)');
54
- });
55
-
56
- test('saveDecisionToDb destroys freeform DECISIONS.md content', async () => {
57
- const tmpDir = makeTmpDir();
58
- const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
59
- const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
60
- openDatabase(dbPath);
61
-
62
- const freeformContent = `# Project Decisions
63
-
64
- ## Architecture
65
- We decided to use a microservices architecture because monoliths don't scale.
66
-
67
- ## Database
68
- PostgreSQL was chosen for its reliability and JSONB support.
69
-
70
- ## Deployment
71
- - Kubernetes for orchestration
72
- - Helm charts for packaging
73
- `;
74
-
75
- // Pre-populate DECISIONS.md with freeform content
76
- fs.writeFileSync(mdPath, freeformContent, 'utf-8');
77
-
78
- try {
79
- // Save a new decision — this should NOT destroy the freeform content
80
- const result = await saveDecisionToDb({
81
- scope: 'testing',
82
- decision: 'Use Jest for unit tests',
83
- choice: 'Jest',
84
- rationale: 'Well-known, good DX',
85
- when_context: 'M001',
86
- }, tmpDir);
87
-
88
- assert.deepStrictEqual(result.id, 'D001', 'decision ID assigned correctly');
89
-
90
- // Read back the file
91
- const afterContent = fs.readFileSync(mdPath, 'utf-8');
92
-
93
- // The freeform content MUST still be present
94
- assert.ok(
95
- afterContent.includes('microservices architecture'),
96
- 'freeform architecture section preserved after saveDecisionToDb',
97
- );
98
- assert.ok(
99
- afterContent.includes('PostgreSQL was chosen'),
100
- 'freeform database section preserved after saveDecisionToDb',
101
- );
102
- assert.ok(
103
- afterContent.includes('Kubernetes for orchestration'),
104
- 'freeform deployment section preserved after saveDecisionToDb',
105
- );
106
-
107
- // The new decision MUST also be present
108
- assert.ok(
109
- afterContent.includes('D001'),
110
- 'new decision D001 present in file',
111
- );
112
- assert.ok(
113
- afterContent.includes('Use Jest for unit tests'),
114
- 'new decision text present in file',
115
- );
116
-
117
- // Save a second decision freeform content must still survive
118
- const result2 = await saveDecisionToDb({
119
- scope: 'ci',
120
- decision: 'Use GitHub Actions for CI',
121
- choice: 'GitHub Actions',
122
- rationale: 'Native integration',
123
- when_context: 'M001',
124
- }, tmpDir);
125
-
126
- assert.deepStrictEqual(result2.id, 'D002', 'second decision ID assigned correctly');
127
-
128
- const afterContent2 = fs.readFileSync(mdPath, 'utf-8');
129
-
130
- assert.ok(
131
- afterContent2.includes('microservices architecture'),
132
- 'freeform content still preserved after second save',
133
- );
134
- assert.ok(
135
- afterContent2.includes('D001'),
136
- 'first decision still present after second save',
137
- );
138
- assert.ok(
139
- afterContent2.includes('D002'),
140
- 'second decision present after second save',
141
- );
142
- assert.ok(
143
- afterContent2.includes('Use GitHub Actions for CI'),
144
- 'second decision text present in file',
145
- );
146
- } finally {
147
- closeDatabase();
148
- cleanupDir(tmpDir);
149
- }
150
- });
151
-
152
- test('saveDecisionToDb with table-format DECISIONS.md still regenerates normally', async () => {
153
- const tmpDir = makeTmpDir();
154
- const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
155
- const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
156
- openDatabase(dbPath);
157
-
158
- // Pre-populate with canonical table format
159
- const tableContent = `# Decisions Register
160
-
161
- <!-- Append-only. Never edit or remove existing rows.
162
- To reverse a decision, add a new row that supersedes it.
163
- Read this file at the start of any planning or research phase. -->
164
-
165
- | # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |
166
- |---|------|-------|----------|--------|-----------|------------|---------|
167
- | D001 | M001 | arch | Use REST API | REST | Simpler | Yes | human |
168
- `;
169
-
170
- fs.writeFileSync(mdPath, tableContent, 'utf-8');
171
-
172
- try {
173
- const result = await saveDecisionToDb({
174
- scope: 'testing',
175
- decision: 'Use Vitest',
176
- choice: 'Vitest',
177
- rationale: 'Fast',
178
- when_context: 'M001',
179
- }, tmpDir);
180
-
181
- // The pre-existing table decision was NOT in DB, so it won't appear after regen.
182
- // But the new decision should be there.
183
- assert.deepStrictEqual(result.id, 'D001', 'gets D001 since DB was empty');
184
-
185
- const afterContent = fs.readFileSync(mdPath, 'utf-8');
186
- // Table-format file gets fully regenerated this is the normal path
187
- assert.ok(
188
- afterContent.includes('# Decisions Register'),
189
- 'table-format file still has header after save',
190
- );
191
- assert.ok(
192
- afterContent.includes('Use Vitest'),
193
- 'new decision present in regenerated table',
194
- );
195
- } finally {
196
- closeDatabase();
197
- cleanupDir(tmpDir);
198
- }
199
- });
200
-
201
- test('saveDecisionToDb with no existing DECISIONS.md creates table', async () => {
202
- const tmpDir = makeTmpDir();
203
- const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
204
- const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
205
- openDatabase(dbPath);
206
-
207
- // No DECISIONS.md exists at all
208
- assert.ok(!fs.existsSync(mdPath), 'DECISIONS.md does not exist initially');
209
-
210
- try {
211
- const result = await saveDecisionToDb({
212
- scope: 'arch',
213
- decision: 'Brand new decision',
214
- choice: 'Option A',
215
- rationale: 'Best fit',
216
- }, tmpDir);
217
-
218
- assert.deepStrictEqual(result.id, 'D001', 'first decision gets D001');
219
- assert.ok(fs.existsSync(mdPath), 'DECISIONS.md created');
220
-
221
- const content = fs.readFileSync(mdPath, 'utf-8');
222
- assert.ok(content.includes('# Decisions Register'), 'new file has header');
223
- assert.ok(content.includes('Brand new decision'), 'new file has decision');
224
- } finally {
225
- closeDatabase();
226
- cleanupDir(tmpDir);
227
- }
228
- });
229
-
230
- // ═══════════════════════════════════════════════════════════════════════════
231
-
232
- });
38
+ console.log('\n── parseDecisionsTable silently drops freeform content ──');
39
+
40
+ {
41
+ const freeform = `# Project Decisions
42
+
43
+ ## Architecture
44
+ We decided to use a microservices architecture because monoliths don't scale.
45
+
46
+ ## Database
47
+ PostgreSQL was chosen for its reliability and JSONB support.
48
+
49
+ ## Deployment
50
+ - Kubernetes for orchestration
51
+ - Helm charts for packaging
52
+ `;
53
+
54
+ const parsed = parseDecisionsTable(freeform);
55
+ assertEq(parsed.length, 0, 'freeform content yields zero parsed decisions (expected — it is not a table)');
56
+ }
57
+
58
+ console.log('\n── saveDecisionToDb destroys freeform DECISIONS.md content ──');
59
+
60
+ {
61
+ const tmpDir = makeTmpDir();
62
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
63
+ const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
64
+ openDatabase(dbPath);
65
+
66
+ const freeformContent = `# Project Decisions
67
+
68
+ ## Architecture
69
+ We decided to use a microservices architecture because monoliths don't scale.
70
+
71
+ ## Database
72
+ PostgreSQL was chosen for its reliability and JSONB support.
73
+
74
+ ## Deployment
75
+ - Kubernetes for orchestration
76
+ - Helm charts for packaging
77
+ `;
78
+
79
+ // Pre-populate DECISIONS.md with freeform content
80
+ fs.writeFileSync(mdPath, freeformContent, 'utf-8');
81
+
82
+ try {
83
+ // Save a new decision this should NOT destroy the freeform content
84
+ const result = await saveDecisionToDb({
85
+ scope: 'testing',
86
+ decision: 'Use Jest for unit tests',
87
+ choice: 'Jest',
88
+ rationale: 'Well-known, good DX',
89
+ when_context: 'M001',
90
+ }, tmpDir);
91
+
92
+ assertEq(result.id, 'D001', 'decision ID assigned correctly');
93
+
94
+ // Read back the file
95
+ const afterContent = fs.readFileSync(mdPath, 'utf-8');
96
+
97
+ // The freeform content MUST still be present
98
+ assertTrue(
99
+ afterContent.includes('microservices architecture'),
100
+ 'freeform architecture section preserved after saveDecisionToDb',
101
+ );
102
+ assertTrue(
103
+ afterContent.includes('PostgreSQL was chosen'),
104
+ 'freeform database section preserved after saveDecisionToDb',
105
+ );
106
+ assertTrue(
107
+ afterContent.includes('Kubernetes for orchestration'),
108
+ 'freeform deployment section preserved after saveDecisionToDb',
109
+ );
110
+
111
+ // The new decision MUST also be present
112
+ assertTrue(
113
+ afterContent.includes('D001'),
114
+ 'new decision D001 present in file',
115
+ );
116
+ assertTrue(
117
+ afterContent.includes('Use Jest for unit tests'),
118
+ 'new decision text present in file',
119
+ );
120
+
121
+ // Save a second decision freeform content must still survive
122
+ const result2 = await saveDecisionToDb({
123
+ scope: 'ci',
124
+ decision: 'Use GitHub Actions for CI',
125
+ choice: 'GitHub Actions',
126
+ rationale: 'Native integration',
127
+ when_context: 'M001',
128
+ }, tmpDir);
129
+
130
+ assertEq(result2.id, 'D002', 'second decision ID assigned correctly');
131
+
132
+ const afterContent2 = fs.readFileSync(mdPath, 'utf-8');
133
+
134
+ assertTrue(
135
+ afterContent2.includes('microservices architecture'),
136
+ 'freeform content still preserved after second save',
137
+ );
138
+ assertTrue(
139
+ afterContent2.includes('D001'),
140
+ 'first decision still present after second save',
141
+ );
142
+ assertTrue(
143
+ afterContent2.includes('D002'),
144
+ 'second decision present after second save',
145
+ );
146
+ assertTrue(
147
+ afterContent2.includes('Use GitHub Actions for CI'),
148
+ 'second decision text present in file',
149
+ );
150
+ } finally {
151
+ closeDatabase();
152
+ cleanupDir(tmpDir);
153
+ }
154
+ }
155
+
156
+ console.log('\n── saveDecisionToDb with table-format DECISIONS.md still regenerates normally ──');
157
+
158
+ {
159
+ const tmpDir = makeTmpDir();
160
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
161
+ const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
162
+ openDatabase(dbPath);
163
+
164
+ // Pre-populate with canonical table format
165
+ const tableContent = `# Decisions Register
166
+
167
+ <!-- Append-only. Never edit or remove existing rows.
168
+ To reverse a decision, add a new row that supersedes it.
169
+ Read this file at the start of any planning or research phase. -->
170
+
171
+ | # | When | Scope | Decision | Choice | Rationale | Revisable? | Made By |
172
+ |---|------|-------|----------|--------|-----------|------------|---------|
173
+ | D001 | M001 | arch | Use REST API | REST | Simpler | Yes | human |
174
+ `;
175
+
176
+ fs.writeFileSync(mdPath, tableContent, 'utf-8');
177
+
178
+ try {
179
+ const result = await saveDecisionToDb({
180
+ scope: 'testing',
181
+ decision: 'Use Vitest',
182
+ choice: 'Vitest',
183
+ rationale: 'Fast',
184
+ when_context: 'M001',
185
+ }, tmpDir);
186
+
187
+ // The pre-existing table decision was NOT in DB, so it won't appear after regen.
188
+ // But the new decision should be there.
189
+ assertEq(result.id, 'D001', 'gets D001 since DB was empty');
190
+
191
+ const afterContent = fs.readFileSync(mdPath, 'utf-8');
192
+ // Table-format file gets fully regenerated — this is the normal path
193
+ assertTrue(
194
+ afterContent.includes('# Decisions Register'),
195
+ 'table-format file still has header after save',
196
+ );
197
+ assertTrue(
198
+ afterContent.includes('Use Vitest'),
199
+ 'new decision present in regenerated table',
200
+ );
201
+ } finally {
202
+ closeDatabase();
203
+ cleanupDir(tmpDir);
204
+ }
205
+ }
206
+
207
+ console.log('\n── saveDecisionToDb with no existing DECISIONS.md creates table ──');
208
+
209
+ {
210
+ const tmpDir = makeTmpDir();
211
+ const dbPath = path.join(tmpDir, '.gsd', 'gsd.db');
212
+ const mdPath = path.join(tmpDir, '.gsd', 'DECISIONS.md');
213
+ openDatabase(dbPath);
214
+
215
+ // No DECISIONS.md exists at all
216
+ assertTrue(!fs.existsSync(mdPath), 'DECISIONS.md does not exist initially');
217
+
218
+ try {
219
+ const result = await saveDecisionToDb({
220
+ scope: 'arch',
221
+ decision: 'Brand new decision',
222
+ choice: 'Option A',
223
+ rationale: 'Best fit',
224
+ }, tmpDir);
225
+
226
+ assertEq(result.id, 'D001', 'first decision gets D001');
227
+ assertTrue(fs.existsSync(mdPath), 'DECISIONS.md created');
228
+
229
+ const content = fs.readFileSync(mdPath, 'utf-8');
230
+ assertTrue(content.includes('# Decisions Register'), 'new file has header');
231
+ assertTrue(content.includes('Brand new decision'), 'new file has decision');
232
+ } finally {
233
+ closeDatabase();
234
+ cleanupDir(tmpDir);
235
+ }
236
+ }
237
+
238
+ // ═══════════════════════════════════════════════════════════════════════════
239
+
240
+ report();