gsd-pi 2.43.0 → 2.44.0-dev.0b97ffd

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 (693) hide show
  1. package/README.md +30 -12
  2. package/dist/cli.js +13 -1
  3. package/dist/help-text.js +24 -0
  4. package/dist/resources/extensions/bg-shell/overlay.js +3 -0
  5. package/dist/resources/extensions/github-sync/sync.js +2 -1
  6. package/dist/resources/extensions/gsd/auto/loop.js +0 -2
  7. package/dist/resources/extensions/gsd/auto/phases.js +7 -12
  8. package/dist/resources/extensions/gsd/auto-dashboard.js +19 -18
  9. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +34 -19
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +36 -21
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +128 -14
  12. package/dist/resources/extensions/gsd/auto-prompts.js +202 -92
  13. package/dist/resources/extensions/gsd/auto-recovery.js +83 -135
  14. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +14 -0
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -7
  17. package/dist/resources/extensions/gsd/auto-verification.js +5 -10
  18. package/dist/resources/extensions/gsd/auto-worktree.js +123 -30
  19. package/dist/resources/extensions/gsd/auto.js +1 -4
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +611 -0
  21. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +28 -3
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  23. package/dist/resources/extensions/gsd/commands/catalog.js +3 -1
  24. package/dist/resources/extensions/gsd/commands/handlers/ops.js +15 -1
  25. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  26. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  27. package/dist/resources/extensions/gsd/commands-maintenance.js +78 -3
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -31
  29. package/dist/resources/extensions/gsd/db-writer.js +95 -4
  30. package/dist/resources/extensions/gsd/dispatch-guard.js +35 -30
  31. package/dist/resources/extensions/gsd/doctor-checks.js +28 -11
  32. package/dist/resources/extensions/gsd/doctor-environment.js +28 -0
  33. package/dist/resources/extensions/gsd/doctor-types.js +0 -15
  34. package/dist/resources/extensions/gsd/doctor.js +46 -282
  35. package/dist/resources/extensions/gsd/file-watcher.js +5 -1
  36. package/dist/resources/extensions/gsd/files.js +14 -198
  37. package/dist/resources/extensions/gsd/git-service.js +4 -0
  38. package/dist/resources/extensions/gsd/gitignore.js +4 -0
  39. package/dist/resources/extensions/gsd/gsd-db.js +819 -197
  40. package/dist/resources/extensions/gsd/guided-flow.js +18 -8
  41. package/dist/resources/extensions/gsd/markdown-renderer.js +862 -0
  42. package/dist/resources/extensions/gsd/md-importer.js +182 -4
  43. package/dist/resources/extensions/gsd/native-git-bridge.js +10 -1
  44. package/dist/resources/extensions/gsd/parallel-eligibility.js +14 -19
  45. package/dist/resources/extensions/gsd/parallel-orchestrator.js +38 -0
  46. package/dist/resources/extensions/gsd/parsers-legacy.js +239 -0
  47. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +9 -0
  49. package/dist/resources/extensions/gsd/preferences.js +1 -0
  50. package/dist/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  51. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  52. package/dist/resources/extensions/gsd/prompts/execute-task.md +15 -5
  53. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  56. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  58. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  59. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  60. package/dist/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  61. package/dist/resources/extensions/gsd/reactive-graph.js +33 -3
  62. package/dist/resources/extensions/gsd/skill-health.js +3 -1
  63. package/dist/resources/extensions/gsd/state.js +484 -30
  64. package/dist/resources/extensions/gsd/tools/complete-milestone.js +128 -0
  65. package/dist/resources/extensions/gsd/tools/complete-slice.js +244 -0
  66. package/dist/resources/extensions/gsd/tools/complete-task.js +204 -0
  67. package/dist/resources/extensions/gsd/tools/plan-milestone.js +205 -0
  68. package/dist/resources/extensions/gsd/tools/plan-slice.js +155 -0
  69. package/dist/resources/extensions/gsd/tools/plan-task.js +94 -0
  70. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +152 -0
  71. package/dist/resources/extensions/gsd/tools/replan-slice.js +146 -0
  72. package/dist/resources/extensions/gsd/triage-resolution.js +17 -1
  73. package/dist/resources/extensions/gsd/undo.js +197 -3
  74. package/dist/resources/extensions/gsd/visualizer-data.js +53 -16
  75. package/dist/resources/extensions/gsd/workspace-index.js +63 -39
  76. package/dist/web/standalone/.next/BUILD_ID +1 -1
  77. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -17
  78. package/dist/web/standalone/.next/build-manifest.json +4 -4
  79. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  80. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  81. package/dist/web/standalone/.next/required-server-files.json +4 -4
  82. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  83. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  84. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  86. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  94. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  103. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  148. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  154. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -0
  166. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -0
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -0
  168. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  171. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  173. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  175. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/index.html +1 -1
  185. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  186. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  187. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  188. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  190. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  191. package/dist/web/standalone/.next/server/app/page.js +2 -2
  192. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -17
  194. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  195. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  196. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  197. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/middleware.js +2 -2
  200. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  202. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  203. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  204. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  205. package/dist/web/standalone/.next/static/alS4hoANx0TK4UVZY27da/_buildManifest.js +1 -0
  206. package/dist/web/standalone/.next/static/chunks/{4024.c195dc1fdd2adbea.js → 4024.0de81b543b28b9fe.js} +2 -2
  207. package/dist/web/standalone/.next/static/chunks/app/_global-error/{page-d07a2c023f1aef1e.js → page-d83ba70a25a85472.js} +1 -1
  208. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
  209. package/dist/web/standalone/.next/static/chunks/app/api/boot/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  210. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  211. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  212. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d83ba70a25a85472.js +1 -0
  213. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d83ba70a25a85472.js +1 -0
  214. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d83ba70a25a85472.js +1 -0
  215. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d83ba70a25a85472.js +1 -0
  216. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d83ba70a25a85472.js +1 -0
  217. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d83ba70a25a85472.js +1 -0
  218. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d83ba70a25a85472.js +1 -0
  219. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d83ba70a25a85472.js +1 -0
  220. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d83ba70a25a85472.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d83ba70a25a85472.js +1 -0
  222. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d83ba70a25a85472.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d83ba70a25a85472.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d83ba70a25a85472.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d83ba70a25a85472.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d83ba70a25a85472.js +1 -0
  227. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d83ba70a25a85472.js +1 -0
  228. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d83ba70a25a85472.js +1 -0
  229. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d83ba70a25a85472.js +1 -0
  230. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d83ba70a25a85472.js +1 -0
  231. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d83ba70a25a85472.js +1 -0
  232. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d83ba70a25a85472.js +1 -0
  233. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d83ba70a25a85472.js +1 -0
  234. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d83ba70a25a85472.js +1 -0
  235. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d83ba70a25a85472.js +1 -0
  236. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d83ba70a25a85472.js +1 -0
  237. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d83ba70a25a85472.js +1 -0
  238. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d83ba70a25a85472.js +1 -0
  239. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d83ba70a25a85472.js +1 -0
  240. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-d83ba70a25a85472.js +1 -0
  241. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d83ba70a25a85472.js +1 -0
  242. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d83ba70a25a85472.js +1 -0
  243. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d83ba70a25a85472.js +1 -0
  244. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d83ba70a25a85472.js +1 -0
  245. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d83ba70a25a85472.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d83ba70a25a85472.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d83ba70a25a85472.js +1 -0
  248. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d83ba70a25a85472.js +1 -0
  249. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
  250. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
  251. package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
  252. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d83ba70a25a85472.js +1 -0
  253. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d83ba70a25a85472.js +1 -0
  254. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  255. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d83ba70a25a85472.js +1 -0
  256. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d83ba70a25a85472.js +1 -0
  257. package/dist/web/standalone/.next/static/chunks/{webpack-fa307370fcf9fb2c.js → webpack-9014b5adb127a98a.js} +1 -1
  258. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +1 -0
  259. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  260. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  261. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  262. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  263. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  264. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  265. package/dist/web/standalone/server.js +1 -1
  266. package/package.json +4 -4
  267. package/packages/pi-ai/dist/models.custom.d.ts +173 -0
  268. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -0
  269. package/packages/pi-ai/dist/models.custom.js +170 -0
  270. package/packages/pi-ai/dist/models.custom.js.map +1 -0
  271. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  272. package/packages/pi-ai/dist/models.js +16 -1
  273. package/packages/pi-ai/dist/models.js.map +1 -1
  274. package/packages/pi-ai/dist/models.test.d.ts +2 -0
  275. package/packages/pi-ai/dist/models.test.d.ts.map +1 -0
  276. package/packages/pi-ai/dist/models.test.js +67 -0
  277. package/packages/pi-ai/dist/models.test.js.map +1 -0
  278. package/packages/pi-ai/src/models.custom.ts +172 -0
  279. package/packages/pi-ai/src/models.test.ts +85 -0
  280. package/packages/pi-ai/src/models.ts +17 -1
  281. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -3
  282. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/agent-session.js +21 -34
  284. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  286. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
  288. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
  291. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
  294. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  296. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  298. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +6 -0
  299. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/core/extensions/loader.js +80 -0
  301. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +63 -0
  303. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  304. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  305. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  306. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
  307. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  308. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  309. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
  310. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
  311. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
  312. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
  313. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
  314. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  315. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  316. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
  317. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
  318. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
  319. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
  320. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +5 -0
  321. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  322. package/packages/pi-coding-agent/dist/core/lsp/client.js +69 -21
  323. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  324. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
  325. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
  326. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
  327. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
  328. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
  329. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  331. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
  333. package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
  334. package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
  335. package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
  336. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
  337. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
  338. package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
  339. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  341. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  342. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
  344. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  346. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  348. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/index.d.ts +3 -1
  350. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/index.js +1 -0
  352. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  354. package/packages/pi-coding-agent/dist/main.js +11 -199
  355. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +6 -0
  357. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +21 -0
  359. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  361. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  362. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +8 -15
  363. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  364. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  365. package/packages/pi-coding-agent/dist/modes/print-mode.js +45 -34
  366. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  367. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +1 -0
  368. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +7 -2
  370. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  372. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -1
  373. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  375. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  376. package/packages/pi-coding-agent/package.json +1 -1
  377. package/packages/pi-coding-agent/src/core/agent-session.ts +26 -37
  378. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  379. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
  380. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
  381. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
  382. package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
  383. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +96 -0
  384. package/packages/pi-coding-agent/src/core/extensions/loader.ts +89 -0
  385. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  386. package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
  387. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
  388. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
  389. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  390. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
  391. package/packages/pi-coding-agent/src/core/lsp/client.ts +83 -21
  392. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
  393. package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
  394. package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
  395. package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
  396. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  397. package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
  398. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  399. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  400. package/packages/pi-coding-agent/src/index.ts +7 -0
  401. package/packages/pi-coding-agent/src/main.ts +11 -232
  402. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +20 -0
  403. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +9 -16
  404. package/packages/pi-coding-agent/src/modes/print-mode.ts +42 -32
  405. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +8 -2
  406. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -1
  407. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  408. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  409. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  410. package/pkg/dist/modes/interactive/theme/theme.js +8 -15
  411. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  412. package/pkg/package.json +1 -1
  413. package/src/resources/extensions/bg-shell/overlay.ts +4 -0
  414. package/src/resources/extensions/github-sync/sync.ts +2 -1
  415. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -8
  416. package/src/resources/extensions/gsd/auto/loop.ts +0 -2
  417. package/src/resources/extensions/gsd/auto/phases.ts +7 -20
  418. package/src/resources/extensions/gsd/auto/types.ts +0 -1
  419. package/src/resources/extensions/gsd/auto-dashboard.ts +20 -16
  420. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +34 -19
  421. package/src/resources/extensions/gsd/auto-dispatch.ts +38 -21
  422. package/src/resources/extensions/gsd/auto-post-unit.ts +150 -15
  423. package/src/resources/extensions/gsd/auto-prompts.ts +186 -103
  424. package/src/resources/extensions/gsd/auto-recovery.ts +77 -142
  425. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  426. package/src/resources/extensions/gsd/auto-supervisor.ts +14 -0
  427. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +6 -7
  428. package/src/resources/extensions/gsd/auto-verification.ts +4 -9
  429. package/src/resources/extensions/gsd/auto-worktree.ts +126 -30
  430. package/src/resources/extensions/gsd/auto.ts +0 -9
  431. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +675 -4
  432. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +31 -3
  433. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -0
  434. package/src/resources/extensions/gsd/commands/catalog.ts +3 -1
  435. package/src/resources/extensions/gsd/commands/handlers/ops.ts +15 -1
  436. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  437. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  438. package/src/resources/extensions/gsd/commands-maintenance.ts +86 -3
  439. package/src/resources/extensions/gsd/dashboard-overlay.ts +17 -13
  440. package/src/resources/extensions/gsd/db-writer.ts +105 -4
  441. package/src/resources/extensions/gsd/dispatch-guard.ts +32 -30
  442. package/src/resources/extensions/gsd/doctor-checks.ts +25 -11
  443. package/src/resources/extensions/gsd/doctor-environment.ts +31 -0
  444. package/src/resources/extensions/gsd/doctor-types.ts +0 -23
  445. package/src/resources/extensions/gsd/doctor.ts +45 -295
  446. package/src/resources/extensions/gsd/file-watcher.ts +4 -1
  447. package/src/resources/extensions/gsd/files.ts +16 -220
  448. package/src/resources/extensions/gsd/git-service.ts +4 -0
  449. package/src/resources/extensions/gsd/gitignore.ts +4 -0
  450. package/src/resources/extensions/gsd/gsd-db.ts +1157 -370
  451. package/src/resources/extensions/gsd/guided-flow.ts +20 -8
  452. package/src/resources/extensions/gsd/markdown-renderer.ts +1098 -0
  453. package/src/resources/extensions/gsd/md-importer.ts +211 -2
  454. package/src/resources/extensions/gsd/native-git-bridge.ts +12 -1
  455. package/src/resources/extensions/gsd/parallel-eligibility.ts +14 -18
  456. package/src/resources/extensions/gsd/parallel-orchestrator.ts +43 -0
  457. package/src/resources/extensions/gsd/parsers-legacy.ts +271 -0
  458. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  459. package/src/resources/extensions/gsd/preferences-validation.ts +9 -0
  460. package/src/resources/extensions/gsd/preferences.ts +1 -0
  461. package/src/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  462. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  463. package/src/resources/extensions/gsd/prompts/execute-task.md +15 -5
  464. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  465. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  466. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  467. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  468. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  469. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  470. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  471. package/src/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  472. package/src/resources/extensions/gsd/reactive-graph.ts +33 -3
  473. package/src/resources/extensions/gsd/skill-health.ts +2 -1
  474. package/src/resources/extensions/gsd/state.ts +547 -29
  475. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  476. package/src/resources/extensions/gsd/tests/atomic-task-closeout.test.ts +8 -120
  477. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  478. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +20 -11
  479. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  480. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  481. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +600 -513
  482. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  483. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  484. package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +121 -0
  485. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +540 -668
  486. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  487. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  488. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  489. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  490. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  491. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  492. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  493. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +39 -60
  494. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +375 -0
  495. package/src/resources/extensions/gsd/tests/complete-task.test.ts +387 -0
  496. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  497. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  498. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  499. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  500. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -2
  501. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  502. package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
  503. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  504. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +512 -0
  505. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +644 -84
  506. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  507. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  508. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  509. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  510. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +192 -161
  511. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  512. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  513. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +30 -90
  514. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  515. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +57 -80
  516. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +164 -0
  517. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  518. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +55 -153
  519. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  520. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  521. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +53 -97
  522. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  523. package/src/resources/extensions/gsd/tests/doctor.test.ts +109 -149
  524. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  525. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  526. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  527. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  528. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +278 -0
  529. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +232 -0
  530. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  531. package/src/resources/extensions/gsd/tests/git-service.test.ts +291 -390
  532. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  533. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  534. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  535. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  536. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +440 -0
  537. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  538. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  539. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  540. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -270
  541. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  542. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  543. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  544. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  545. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +643 -0
  546. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  547. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1161 -0
  548. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  549. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  550. package/src/resources/extensions/gsd/tests/memory-store.test.ts +81 -94
  551. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  552. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +429 -0
  553. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  554. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  555. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  556. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +89 -97
  557. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +127 -164
  558. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  559. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  560. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  561. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  562. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  563. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  564. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  565. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  566. package/src/resources/extensions/gsd/tests/parsers.test.ts +548 -612
  567. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  568. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +176 -113
  569. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +7 -0
  570. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +179 -0
  571. package/src/resources/extensions/gsd/tests/plan-task.test.ts +145 -0
  572. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +305 -0
  573. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  574. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +139 -0
  575. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  576. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  577. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  578. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  579. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  580. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  581. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +325 -0
  582. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  583. package/src/resources/extensions/gsd/tests/replan-handler.test.ts +410 -0
  584. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  585. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  586. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  587. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  588. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  589. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +1 -1
  590. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +296 -0
  591. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  592. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  593. package/src/resources/extensions/gsd/tests/schema-v9-sequence.test.ts +176 -0
  594. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  595. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  596. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +209 -0
  597. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  598. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  599. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  600. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +118 -0
  601. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  602. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  603. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +19 -13
  604. package/src/resources/extensions/gsd/tests/undo.test.ts +321 -1
  605. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  606. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  607. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -1
  608. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +0 -142
  609. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  610. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  611. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  612. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  613. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  614. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  615. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  616. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  617. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  618. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  619. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  620. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +12 -5
  621. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  622. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  623. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  624. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  625. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  626. package/src/resources/extensions/gsd/tools/complete-milestone.ts +176 -0
  627. package/src/resources/extensions/gsd/tools/complete-slice.ts +300 -0
  628. package/src/resources/extensions/gsd/tools/complete-task.ts +245 -0
  629. package/src/resources/extensions/gsd/tools/plan-milestone.ts +249 -0
  630. package/src/resources/extensions/gsd/tools/plan-slice.ts +194 -0
  631. package/src/resources/extensions/gsd/tools/plan-task.ts +116 -0
  632. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +203 -0
  633. package/src/resources/extensions/gsd/tools/replan-slice.ts +192 -0
  634. package/src/resources/extensions/gsd/triage-resolution.ts +20 -1
  635. package/src/resources/extensions/gsd/types.ts +50 -0
  636. package/src/resources/extensions/gsd/undo.ts +247 -3
  637. package/src/resources/extensions/gsd/visualizer-data.ts +54 -17
  638. package/src/resources/extensions/gsd/workspace-index.ts +64 -46
  639. package/dist/resources/extensions/gsd/auto-observability.js +0 -56
  640. package/dist/resources/extensions/gsd/observability-validator.js +0 -422
  641. package/dist/resources/extensions/gsd/roadmap-mutations.js +0 -110
  642. package/dist/web/standalone/.next/static/VvclDCW6TAWjEyLU-EYL1/_buildManifest.js +0 -1
  643. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
  644. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  645. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d07a2c023f1aef1e.js +0 -1
  646. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d07a2c023f1aef1e.js +0 -1
  647. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d07a2c023f1aef1e.js +0 -1
  648. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d07a2c023f1aef1e.js +0 -1
  649. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d07a2c023f1aef1e.js +0 -1
  650. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d07a2c023f1aef1e.js +0 -1
  651. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d07a2c023f1aef1e.js +0 -1
  652. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d07a2c023f1aef1e.js +0 -1
  653. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d07a2c023f1aef1e.js +0 -1
  654. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d07a2c023f1aef1e.js +0 -1
  655. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d07a2c023f1aef1e.js +0 -1
  656. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d07a2c023f1aef1e.js +0 -1
  657. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d07a2c023f1aef1e.js +0 -1
  658. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d07a2c023f1aef1e.js +0 -1
  659. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d07a2c023f1aef1e.js +0 -1
  660. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d07a2c023f1aef1e.js +0 -1
  661. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d07a2c023f1aef1e.js +0 -1
  662. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d07a2c023f1aef1e.js +0 -1
  663. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d07a2c023f1aef1e.js +0 -1
  664. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d07a2c023f1aef1e.js +0 -1
  665. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d07a2c023f1aef1e.js +0 -1
  666. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d07a2c023f1aef1e.js +0 -1
  667. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d07a2c023f1aef1e.js +0 -1
  668. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d07a2c023f1aef1e.js +0 -1
  669. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d07a2c023f1aef1e.js +0 -1
  670. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d07a2c023f1aef1e.js +0 -1
  671. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d07a2c023f1aef1e.js +0 -1
  672. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d07a2c023f1aef1e.js +0 -1
  673. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d07a2c023f1aef1e.js +0 -1
  674. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d07a2c023f1aef1e.js +0 -1
  675. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  676. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d07a2c023f1aef1e.js +0 -1
  677. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d07a2c023f1aef1e.js +0 -1
  678. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d07a2c023f1aef1e.js +0 -1
  679. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d07a2c023f1aef1e.js +0 -1
  680. package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
  681. package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
  682. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d07a2c023f1aef1e.js +0 -1
  683. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d07a2c023f1aef1e.js +0 -1
  684. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
  685. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d07a2c023f1aef1e.js +0 -1
  686. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d07a2c023f1aef1e.js +0 -1
  687. package/dist/web/standalone/.next/static/css/123c0bb039697968.css +0 -1
  688. package/src/resources/extensions/gsd/auto-observability.ts +0 -74
  689. package/src/resources/extensions/gsd/observability-validator.ts +0 -456
  690. package/src/resources/extensions/gsd/roadmap-mutations.ts +0 -134
  691. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +0 -174
  692. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +0 -474
  693. /package/dist/web/standalone/.next/static/{VvclDCW6TAWjEyLU-EYL1 → alS4hoANx0TK4UVZY27da}/_ssgManifest.js +0 -0
@@ -11,15 +11,8 @@ import { dirname } from "node:path";
11
11
  import type { Decision, Requirement } from "./types.js";
12
12
  import { GSDError, GSD_STALE_STATE } from "./errors.js";
13
13
 
14
- // Create a require function for loading native modules in ESM context
15
14
  const _require = createRequire(import.meta.url);
16
15
 
17
- // ─── Provider Abstraction ──────────────────────────────────────────────────
18
-
19
- /**
20
- * Minimal interface over both node:sqlite DatabaseSync and better-sqlite3 Database.
21
- * Both expose prepare().run/get/all — the adapter normalizes row objects.
22
- */
23
16
  interface DbStatement {
24
17
  run(...params: unknown[]): unknown;
25
18
  get(...params: unknown[]): Record<string, unknown> | undefined;
@@ -38,13 +31,9 @@ let providerName: ProviderName | null = null;
38
31
  let providerModule: unknown = null;
39
32
  let loadAttempted = false;
40
33
 
41
- /**
42
- * Suppress the ExperimentalWarning for SQLite from node:sqlite.
43
- * Must be called before require('node:sqlite').
44
- */
45
34
  function suppressSqliteWarning(): void {
46
35
  const origEmit = process.emit;
47
- // @ts-expect-error overriding process.emit with filtered version
36
+ // @ts-expect-error overriding process.emit for warning filter
48
37
  process.emit = function (event: string, ...args: unknown[]): boolean {
49
38
  if (
50
39
  event === "warning" &&
@@ -58,9 +47,7 @@ function suppressSqliteWarning(): void {
58
47
  ) {
59
48
  return false;
60
49
  }
61
- return origEmit.apply(process, [event, ...args] as Parameters<
62
- typeof process.emit
63
- >) as unknown as boolean;
50
+ return origEmit.apply(process, [event, ...args] as Parameters<typeof process.emit>) as unknown as boolean;
64
51
  };
65
52
  }
66
53
 
@@ -68,7 +55,6 @@ function loadProvider(): void {
68
55
  if (loadAttempted) return;
69
56
  loadAttempted = true;
70
57
 
71
- // Try node:sqlite first
72
58
  try {
73
59
  suppressSqliteWarning();
74
60
  const mod = _require("node:sqlite");
@@ -78,10 +64,9 @@ function loadProvider(): void {
78
64
  return;
79
65
  }
80
66
  } catch {
81
- // node:sqlite not available
67
+ // unavailable
82
68
  }
83
69
 
84
- // Try better-sqlite3
85
70
  try {
86
71
  const mod = _require("better-sqlite3");
87
72
  if (typeof mod === "function" || (mod && mod.default)) {
@@ -90,7 +75,7 @@ function loadProvider(): void {
90
75
  return;
91
76
  }
92
77
  } catch {
93
- // better-sqlite3 not available
78
+ // unavailable
94
79
  }
95
80
 
96
81
  process.stderr.write(
@@ -98,11 +83,6 @@ function loadProvider(): void {
98
83
  );
99
84
  }
100
85
 
101
- // ─── Database Adapter ──────────────────────────────────────────────────────
102
-
103
- /**
104
- * Normalize a row from node:sqlite (null-prototype) to a plain object.
105
- */
106
86
  function normalizeRow(row: unknown): Record<string, unknown> | undefined {
107
87
  if (row == null) return undefined;
108
88
  if (Object.getPrototypeOf(row) === null) {
@@ -161,20 +141,16 @@ function openRawDb(path: string): unknown {
161
141
  return new DatabaseSync(path);
162
142
  }
163
143
 
164
- // better-sqlite3
165
144
  const Database = providerModule as new (path: string) => unknown;
166
145
  return new Database(path);
167
146
  }
168
147
 
169
- // ─── Schema ────────────────────────────────────────────────────────────────
170
-
171
- const SCHEMA_VERSION = 4;
148
+ const SCHEMA_VERSION = 10;
172
149
 
173
150
  function initSchema(db: DbAdapter, fileBacked: boolean): void {
174
- // WAL mode for file-backed databases (must be outside transaction)
175
- if (fileBacked) {
176
- db.exec("PRAGMA journal_mode=WAL");
177
- }
151
+ if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
152
+ if (fileBacked) db.exec("PRAGMA busy_timeout = 5000");
153
+ db.exec("PRAGMA foreign_keys = ON");
178
154
 
179
155
  db.exec("BEGIN");
180
156
  try {
@@ -253,25 +229,135 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
253
229
  )
254
230
  `);
255
231
 
256
- db.exec(
257
- "CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)",
258
- );
259
-
260
- // Views DROP + CREATE since CREATE VIEW IF NOT EXISTS doesn't update definitions
261
- db.exec(
262
- `CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`,
263
- );
264
- db.exec(
265
- `CREATE VIEW IF NOT EXISTS active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL`,
266
- );
267
- db.exec(
268
- `CREATE VIEW IF NOT EXISTS active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL`,
269
- );
270
-
271
- // Insert schema version if not already present
272
- const existing = db
273
- .prepare("SELECT count(*) as cnt FROM schema_version")
274
- .get();
232
+ db.exec(`
233
+ CREATE TABLE IF NOT EXISTS milestones (
234
+ id TEXT PRIMARY KEY,
235
+ title TEXT NOT NULL DEFAULT '',
236
+ status TEXT NOT NULL DEFAULT 'active',
237
+ depends_on TEXT NOT NULL DEFAULT '[]',
238
+ created_at TEXT NOT NULL DEFAULT '',
239
+ completed_at TEXT DEFAULT NULL,
240
+ vision TEXT NOT NULL DEFAULT '',
241
+ success_criteria TEXT NOT NULL DEFAULT '[]',
242
+ key_risks TEXT NOT NULL DEFAULT '[]',
243
+ proof_strategy TEXT NOT NULL DEFAULT '[]',
244
+ verification_contract TEXT NOT NULL DEFAULT '',
245
+ verification_integration TEXT NOT NULL DEFAULT '',
246
+ verification_operational TEXT NOT NULL DEFAULT '',
247
+ verification_uat TEXT NOT NULL DEFAULT '',
248
+ definition_of_done TEXT NOT NULL DEFAULT '[]',
249
+ requirement_coverage TEXT NOT NULL DEFAULT '',
250
+ boundary_map_markdown TEXT NOT NULL DEFAULT ''
251
+ )
252
+ `);
253
+
254
+ db.exec(`
255
+ CREATE TABLE IF NOT EXISTS slices (
256
+ milestone_id TEXT NOT NULL,
257
+ id TEXT NOT NULL,
258
+ title TEXT NOT NULL DEFAULT '',
259
+ status TEXT NOT NULL DEFAULT 'pending',
260
+ risk TEXT NOT NULL DEFAULT 'medium',
261
+ depends TEXT NOT NULL DEFAULT '[]',
262
+ demo TEXT NOT NULL DEFAULT '',
263
+ created_at TEXT NOT NULL DEFAULT '',
264
+ completed_at TEXT DEFAULT NULL,
265
+ full_summary_md TEXT NOT NULL DEFAULT '',
266
+ full_uat_md TEXT NOT NULL DEFAULT '',
267
+ goal TEXT NOT NULL DEFAULT '',
268
+ success_criteria TEXT NOT NULL DEFAULT '',
269
+ proof_level TEXT NOT NULL DEFAULT '',
270
+ integration_closure TEXT NOT NULL DEFAULT '',
271
+ observability_impact TEXT NOT NULL DEFAULT '',
272
+ sequence INTEGER DEFAULT 0, -- DEAD CODE: no tool exposes sequence — always 0
273
+ replan_triggered_at TEXT DEFAULT NULL,
274
+ PRIMARY KEY (milestone_id, id),
275
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
276
+ )
277
+ `);
278
+
279
+ db.exec(`
280
+ CREATE TABLE IF NOT EXISTS tasks (
281
+ milestone_id TEXT NOT NULL,
282
+ slice_id TEXT NOT NULL,
283
+ id TEXT NOT NULL,
284
+ title TEXT NOT NULL DEFAULT '',
285
+ status TEXT NOT NULL DEFAULT 'pending',
286
+ one_liner TEXT NOT NULL DEFAULT '',
287
+ narrative TEXT NOT NULL DEFAULT '',
288
+ verification_result TEXT NOT NULL DEFAULT '',
289
+ duration TEXT NOT NULL DEFAULT '',
290
+ completed_at TEXT DEFAULT NULL,
291
+ blocker_discovered INTEGER DEFAULT 0,
292
+ deviations TEXT NOT NULL DEFAULT '',
293
+ known_issues TEXT NOT NULL DEFAULT '',
294
+ key_files TEXT NOT NULL DEFAULT '[]',
295
+ key_decisions TEXT NOT NULL DEFAULT '[]',
296
+ full_summary_md TEXT NOT NULL DEFAULT '',
297
+ description TEXT NOT NULL DEFAULT '',
298
+ estimate TEXT NOT NULL DEFAULT '',
299
+ files TEXT NOT NULL DEFAULT '[]',
300
+ verify TEXT NOT NULL DEFAULT '',
301
+ inputs TEXT NOT NULL DEFAULT '[]',
302
+ expected_output TEXT NOT NULL DEFAULT '[]',
303
+ observability_impact TEXT NOT NULL DEFAULT '',
304
+ sequence INTEGER DEFAULT 0, -- DEAD CODE: no tool exposes sequence — always 0
305
+ PRIMARY KEY (milestone_id, slice_id, id),
306
+ FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
307
+ )
308
+ `);
309
+
310
+ db.exec(`
311
+ CREATE TABLE IF NOT EXISTS verification_evidence (
312
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
313
+ task_id TEXT NOT NULL DEFAULT '',
314
+ slice_id TEXT NOT NULL DEFAULT '',
315
+ milestone_id TEXT NOT NULL DEFAULT '',
316
+ command TEXT NOT NULL DEFAULT '',
317
+ exit_code INTEGER DEFAULT 0,
318
+ verdict TEXT NOT NULL DEFAULT '',
319
+ duration_ms INTEGER DEFAULT 0,
320
+ created_at TEXT NOT NULL DEFAULT '',
321
+ FOREIGN KEY (milestone_id, slice_id, task_id) REFERENCES tasks(milestone_id, slice_id, id)
322
+ )
323
+ `);
324
+
325
+ db.exec(`
326
+ CREATE TABLE IF NOT EXISTS replan_history (
327
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
328
+ milestone_id TEXT NOT NULL DEFAULT '',
329
+ slice_id TEXT DEFAULT NULL,
330
+ task_id TEXT DEFAULT NULL,
331
+ summary TEXT NOT NULL DEFAULT '',
332
+ previous_artifact_path TEXT DEFAULT NULL,
333
+ replacement_artifact_path TEXT DEFAULT NULL,
334
+ created_at TEXT NOT NULL DEFAULT '',
335
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
336
+ )
337
+ `);
338
+
339
+ db.exec(`
340
+ CREATE TABLE IF NOT EXISTS assessments (
341
+ path TEXT PRIMARY KEY,
342
+ milestone_id TEXT NOT NULL DEFAULT '',
343
+ slice_id TEXT DEFAULT NULL,
344
+ task_id TEXT DEFAULT NULL,
345
+ status TEXT NOT NULL DEFAULT '',
346
+ scope TEXT NOT NULL DEFAULT '',
347
+ full_content TEXT NOT NULL DEFAULT '',
348
+ created_at TEXT NOT NULL DEFAULT '',
349
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
350
+ )
351
+ `);
352
+
353
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)");
354
+ db.exec("CREATE INDEX IF NOT EXISTS idx_replan_history_milestone ON replan_history(milestone_id, created_at)");
355
+
356
+ db.exec(`CREATE VIEW IF NOT EXISTS active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL`);
357
+ db.exec(`CREATE VIEW IF NOT EXISTS active_requirements AS SELECT * FROM requirements WHERE superseded_by IS NULL`);
358
+ db.exec(`CREATE VIEW IF NOT EXISTS active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL`);
359
+
360
+ const existing = db.prepare("SELECT count(*) as cnt FROM schema_version").get();
275
361
  if (existing && (existing["cnt"] as number) === 0) {
276
362
  db.prepare(
277
363
  "INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
@@ -287,23 +373,25 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
287
373
  throw err;
288
374
  }
289
375
 
290
- // Run incremental migrations for existing databases
291
376
  migrateSchema(db);
292
377
  }
293
378
 
294
- /**
295
- * Incremental schema migration. Reads current version from schema_version table
296
- * and applies DDL for each version step up to SCHEMA_VERSION.
297
- */
379
+ function columnExists(db: DbAdapter, table: string, column: string): boolean {
380
+ const rows = db.prepare(`PRAGMA table_info(${table})`).all();
381
+ return rows.some((row) => row["name"] === column);
382
+ }
383
+
384
+ function ensureColumn(db: DbAdapter, table: string, column: string, ddl: string): void {
385
+ if (!columnExists(db, table, column)) db.exec(ddl);
386
+ }
387
+
298
388
  function migrateSchema(db: DbAdapter): void {
299
389
  const row = db.prepare("SELECT MAX(version) as v FROM schema_version").get();
300
390
  const currentVersion = row ? (row["v"] as number) : 0;
301
-
302
391
  if (currentVersion >= SCHEMA_VERSION) return;
303
392
 
304
393
  db.exec("BEGIN");
305
394
  try {
306
- // v1 → v2: add artifacts table
307
395
  if (currentVersion < 2) {
308
396
  db.exec(`
309
397
  CREATE TABLE IF NOT EXISTS artifacts (
@@ -316,13 +404,12 @@ function migrateSchema(db: DbAdapter): void {
316
404
  imported_at TEXT NOT NULL DEFAULT ''
317
405
  )
318
406
  `);
319
-
320
- db.prepare(
321
- "INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
322
- ).run({ ":version": 2, ":applied_at": new Date().toISOString() });
407
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
408
+ ":version": 2,
409
+ ":applied_at": new Date().toISOString(),
410
+ });
323
411
  }
324
412
 
325
- // v2 → v3: add memories + memory_processed_units tables
326
413
  if (currentVersion < 3) {
327
414
  db.exec(`
328
415
  CREATE TABLE IF NOT EXISTS memories (
@@ -339,7 +426,6 @@ function migrateSchema(db: DbAdapter): void {
339
426
  hit_count INTEGER NOT NULL DEFAULT 0
340
427
  )
341
428
  `);
342
-
343
429
  db.exec(`
344
430
  CREATE TABLE IF NOT EXISTS memory_processed_units (
345
431
  unit_key TEXT PRIMARY KEY,
@@ -347,34 +433,187 @@ function migrateSchema(db: DbAdapter): void {
347
433
  processed_at TEXT NOT NULL
348
434
  )
349
435
  `);
350
-
351
- db.exec(
352
- "CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)",
353
- );
436
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_active ON memories(superseded_by)");
354
437
  db.exec("DROP VIEW IF EXISTS active_memories");
355
- db.exec(
356
- "CREATE VIEW active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL",
357
- );
358
-
359
- db.prepare(
360
- "INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
361
- ).run({ ":version": 3, ":applied_at": new Date().toISOString() });
438
+ db.exec("CREATE VIEW active_memories AS SELECT * FROM memories WHERE superseded_by IS NULL");
439
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
440
+ ":version": 3,
441
+ ":applied_at": new Date().toISOString(),
442
+ });
362
443
  }
363
444
 
364
- // v3 → v4: add made_by column to decisions table
365
445
  if (currentVersion < 4) {
366
- // Add made_by column default 'agent' for existing rows (pre-attribution decisions)
367
- db.exec(`ALTER TABLE decisions ADD COLUMN made_by TEXT NOT NULL DEFAULT 'agent'`);
368
-
369
- // Recreate views to pick up new columns (SQLite expands SELECT * at view creation time)
446
+ ensureColumn(db, "decisions", "made_by", `ALTER TABLE decisions ADD COLUMN made_by TEXT NOT NULL DEFAULT 'agent'`);
370
447
  db.exec("DROP VIEW IF EXISTS active_decisions");
371
- db.exec(
372
- "CREATE VIEW active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL",
373
- );
448
+ db.exec("CREATE VIEW active_decisions AS SELECT * FROM decisions WHERE superseded_by IS NULL");
449
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
450
+ ":version": 4,
451
+ ":applied_at": new Date().toISOString(),
452
+ });
453
+ }
374
454
 
375
- db.prepare(
376
- "INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)",
377
- ).run({ ":version": 4, ":applied_at": new Date().toISOString() });
455
+ if (currentVersion < 5) {
456
+ db.exec(`
457
+ CREATE TABLE IF NOT EXISTS milestones (
458
+ id TEXT PRIMARY KEY,
459
+ title TEXT NOT NULL DEFAULT '',
460
+ status TEXT NOT NULL DEFAULT 'active',
461
+ created_at TEXT NOT NULL,
462
+ completed_at TEXT DEFAULT NULL
463
+ )
464
+ `);
465
+ db.exec(`
466
+ CREATE TABLE IF NOT EXISTS slices (
467
+ milestone_id TEXT NOT NULL,
468
+ id TEXT NOT NULL,
469
+ title TEXT NOT NULL DEFAULT '',
470
+ status TEXT NOT NULL DEFAULT 'pending',
471
+ risk TEXT NOT NULL DEFAULT 'medium',
472
+ created_at TEXT NOT NULL DEFAULT '',
473
+ completed_at TEXT DEFAULT NULL,
474
+ PRIMARY KEY (milestone_id, id),
475
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
476
+ )
477
+ `);
478
+ db.exec(`
479
+ CREATE TABLE IF NOT EXISTS tasks (
480
+ milestone_id TEXT NOT NULL,
481
+ slice_id TEXT NOT NULL,
482
+ id TEXT NOT NULL,
483
+ title TEXT NOT NULL DEFAULT '',
484
+ status TEXT NOT NULL DEFAULT 'pending',
485
+ one_liner TEXT NOT NULL DEFAULT '',
486
+ narrative TEXT NOT NULL DEFAULT '',
487
+ verification_result TEXT NOT NULL DEFAULT '',
488
+ duration TEXT NOT NULL DEFAULT '',
489
+ completed_at TEXT DEFAULT NULL,
490
+ blocker_discovered INTEGER DEFAULT 0,
491
+ deviations TEXT NOT NULL DEFAULT '',
492
+ known_issues TEXT NOT NULL DEFAULT '',
493
+ key_files TEXT NOT NULL DEFAULT '[]',
494
+ key_decisions TEXT NOT NULL DEFAULT '[]',
495
+ full_summary_md TEXT NOT NULL DEFAULT '',
496
+ PRIMARY KEY (milestone_id, slice_id, id),
497
+ FOREIGN KEY (milestone_id, slice_id) REFERENCES slices(milestone_id, id)
498
+ )
499
+ `);
500
+ db.exec(`
501
+ CREATE TABLE IF NOT EXISTS verification_evidence (
502
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
503
+ task_id TEXT NOT NULL DEFAULT '',
504
+ slice_id TEXT NOT NULL DEFAULT '',
505
+ milestone_id TEXT NOT NULL DEFAULT '',
506
+ command TEXT NOT NULL DEFAULT '',
507
+ exit_code INTEGER DEFAULT 0,
508
+ verdict TEXT NOT NULL DEFAULT '',
509
+ duration_ms INTEGER DEFAULT 0,
510
+ created_at TEXT NOT NULL DEFAULT '',
511
+ FOREIGN KEY (milestone_id, slice_id, task_id) REFERENCES tasks(milestone_id, slice_id, id)
512
+ )
513
+ `);
514
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
515
+ ":version": 5,
516
+ ":applied_at": new Date().toISOString(),
517
+ });
518
+ }
519
+
520
+ if (currentVersion < 6) {
521
+ ensureColumn(db, "slices", "full_summary_md", `ALTER TABLE slices ADD COLUMN full_summary_md TEXT NOT NULL DEFAULT ''`);
522
+ ensureColumn(db, "slices", "full_uat_md", `ALTER TABLE slices ADD COLUMN full_uat_md TEXT NOT NULL DEFAULT ''`);
523
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
524
+ ":version": 6,
525
+ ":applied_at": new Date().toISOString(),
526
+ });
527
+ }
528
+
529
+ if (currentVersion < 7) {
530
+ ensureColumn(db, "slices", "depends", `ALTER TABLE slices ADD COLUMN depends TEXT NOT NULL DEFAULT '[]'`);
531
+ ensureColumn(db, "slices", "demo", `ALTER TABLE slices ADD COLUMN demo TEXT NOT NULL DEFAULT ''`);
532
+ ensureColumn(db, "milestones", "depends_on", `ALTER TABLE milestones ADD COLUMN depends_on TEXT NOT NULL DEFAULT '[]'`);
533
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
534
+ ":version": 7,
535
+ ":applied_at": new Date().toISOString(),
536
+ });
537
+ }
538
+
539
+ if (currentVersion < 8) {
540
+ ensureColumn(db, "milestones", "vision", `ALTER TABLE milestones ADD COLUMN vision TEXT NOT NULL DEFAULT ''`);
541
+ ensureColumn(db, "milestones", "success_criteria", `ALTER TABLE milestones ADD COLUMN success_criteria TEXT NOT NULL DEFAULT '[]'`);
542
+ ensureColumn(db, "milestones", "key_risks", `ALTER TABLE milestones ADD COLUMN key_risks TEXT NOT NULL DEFAULT '[]'`);
543
+ ensureColumn(db, "milestones", "proof_strategy", `ALTER TABLE milestones ADD COLUMN proof_strategy TEXT NOT NULL DEFAULT '[]'`);
544
+ ensureColumn(db, "milestones", "verification_contract", `ALTER TABLE milestones ADD COLUMN verification_contract TEXT NOT NULL DEFAULT ''`);
545
+ ensureColumn(db, "milestones", "verification_integration", `ALTER TABLE milestones ADD COLUMN verification_integration TEXT NOT NULL DEFAULT ''`);
546
+ ensureColumn(db, "milestones", "verification_operational", `ALTER TABLE milestones ADD COLUMN verification_operational TEXT NOT NULL DEFAULT ''`);
547
+ ensureColumn(db, "milestones", "verification_uat", `ALTER TABLE milestones ADD COLUMN verification_uat TEXT NOT NULL DEFAULT ''`);
548
+ ensureColumn(db, "milestones", "definition_of_done", `ALTER TABLE milestones ADD COLUMN definition_of_done TEXT NOT NULL DEFAULT '[]'`);
549
+ ensureColumn(db, "milestones", "requirement_coverage", `ALTER TABLE milestones ADD COLUMN requirement_coverage TEXT NOT NULL DEFAULT ''`);
550
+ ensureColumn(db, "milestones", "boundary_map_markdown", `ALTER TABLE milestones ADD COLUMN boundary_map_markdown TEXT NOT NULL DEFAULT ''`);
551
+
552
+ ensureColumn(db, "slices", "goal", `ALTER TABLE slices ADD COLUMN goal TEXT NOT NULL DEFAULT ''`);
553
+ ensureColumn(db, "slices", "success_criteria", `ALTER TABLE slices ADD COLUMN success_criteria TEXT NOT NULL DEFAULT ''`);
554
+ ensureColumn(db, "slices", "proof_level", `ALTER TABLE slices ADD COLUMN proof_level TEXT NOT NULL DEFAULT ''`);
555
+ ensureColumn(db, "slices", "integration_closure", `ALTER TABLE slices ADD COLUMN integration_closure TEXT NOT NULL DEFAULT ''`);
556
+ ensureColumn(db, "slices", "observability_impact", `ALTER TABLE slices ADD COLUMN observability_impact TEXT NOT NULL DEFAULT ''`);
557
+
558
+ ensureColumn(db, "tasks", "description", `ALTER TABLE tasks ADD COLUMN description TEXT NOT NULL DEFAULT ''`);
559
+ ensureColumn(db, "tasks", "estimate", `ALTER TABLE tasks ADD COLUMN estimate TEXT NOT NULL DEFAULT ''`);
560
+ ensureColumn(db, "tasks", "files", `ALTER TABLE tasks ADD COLUMN files TEXT NOT NULL DEFAULT '[]'`);
561
+ ensureColumn(db, "tasks", "verify", `ALTER TABLE tasks ADD COLUMN verify TEXT NOT NULL DEFAULT ''`);
562
+ ensureColumn(db, "tasks", "inputs", `ALTER TABLE tasks ADD COLUMN inputs TEXT NOT NULL DEFAULT '[]'`);
563
+ ensureColumn(db, "tasks", "expected_output", `ALTER TABLE tasks ADD COLUMN expected_output TEXT NOT NULL DEFAULT '[]'`);
564
+ ensureColumn(db, "tasks", "observability_impact", `ALTER TABLE tasks ADD COLUMN observability_impact TEXT NOT NULL DEFAULT ''`);
565
+
566
+ db.exec(`
567
+ CREATE TABLE IF NOT EXISTS replan_history (
568
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
569
+ milestone_id TEXT NOT NULL DEFAULT '',
570
+ slice_id TEXT DEFAULT NULL,
571
+ task_id TEXT DEFAULT NULL,
572
+ summary TEXT NOT NULL DEFAULT '',
573
+ previous_artifact_path TEXT DEFAULT NULL,
574
+ replacement_artifact_path TEXT DEFAULT NULL,
575
+ created_at TEXT NOT NULL DEFAULT '',
576
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
577
+ )
578
+ `);
579
+ db.exec(`
580
+ CREATE TABLE IF NOT EXISTS assessments (
581
+ path TEXT PRIMARY KEY,
582
+ milestone_id TEXT NOT NULL DEFAULT '',
583
+ slice_id TEXT DEFAULT NULL,
584
+ task_id TEXT DEFAULT NULL,
585
+ status TEXT NOT NULL DEFAULT '',
586
+ scope TEXT NOT NULL DEFAULT '',
587
+ full_content TEXT NOT NULL DEFAULT '',
588
+ created_at TEXT NOT NULL DEFAULT '',
589
+ FOREIGN KEY (milestone_id) REFERENCES milestones(id)
590
+ )
591
+ `);
592
+ db.exec("CREATE INDEX IF NOT EXISTS idx_replan_history_milestone ON replan_history(milestone_id, created_at)");
593
+
594
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
595
+ ":version": 8,
596
+ ":applied_at": new Date().toISOString(),
597
+ });
598
+ }
599
+
600
+ if (currentVersion < 9) {
601
+ ensureColumn(db, "slices", "sequence", `ALTER TABLE slices ADD COLUMN sequence INTEGER DEFAULT 0`);
602
+ ensureColumn(db, "tasks", "sequence", `ALTER TABLE tasks ADD COLUMN sequence INTEGER DEFAULT 0`);
603
+
604
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
605
+ ":version": 9,
606
+ ":applied_at": new Date().toISOString(),
607
+ });
608
+ }
609
+
610
+ if (currentVersion < 10) {
611
+ ensureColumn(db, "slices", "replan_triggered_at", `ALTER TABLE slices ADD COLUMN replan_triggered_at TEXT DEFAULT NULL`);
612
+
613
+ db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
614
+ ":version": 10,
615
+ ":applied_at": new Date().toISOString(),
616
+ });
378
617
  }
379
618
 
380
619
  db.exec("COMMIT");
@@ -384,72 +623,53 @@ function migrateSchema(db: DbAdapter): void {
384
623
  }
385
624
  }
386
625
 
387
- // ─── Module State ──────────────────────────────────────────────────────────
388
-
389
626
  let currentDb: DbAdapter | null = null;
390
627
  let currentPath: string | null = null;
391
- /** PID that opened the current connection — used for diagnostic logging. */
392
628
  let currentPid: number = 0;
629
+ let _exitHandlerRegistered = false;
393
630
 
394
- // ─── Public API ────────────────────────────────────────────────────────────
395
-
396
- /**
397
- * Returns which SQLite provider is available, or null if none.
398
- */
399
631
  export function getDbProvider(): ProviderName | null {
400
632
  loadProvider();
401
633
  return providerName;
402
634
  }
403
635
 
404
- /**
405
- * Returns true if a database is currently open and usable.
406
- */
407
636
  export function isDbAvailable(): boolean {
408
637
  return currentDb !== null;
409
638
  }
410
639
 
411
- /**
412
- * Opens (or creates) a SQLite database at the given path.
413
- * Initializes schema if needed. Sets WAL mode for file-backed DBs.
414
- * Returns true on success, false if no provider is available.
415
- */
416
640
  export function openDatabase(path: string): boolean {
417
- // Close existing if different path
418
- if (currentDb && currentPath !== path) {
419
- closeDatabase();
420
- }
421
- if (currentDb && currentPath === path) {
422
- return true; // already open
423
- }
641
+ if (currentDb && currentPath !== path) closeDatabase();
642
+ if (currentDb && currentPath === path) return true;
424
643
 
425
644
  const rawDb = openRawDb(path);
426
645
  if (!rawDb) return false;
427
646
 
428
647
  const adapter = createAdapter(rawDb);
429
648
  const fileBacked = path !== ":memory:";
430
-
431
649
  try {
432
650
  initSchema(adapter, fileBacked);
433
651
  } catch (err) {
434
- try {
435
- adapter.close();
436
- } catch {
437
- /* swallow */
438
- }
652
+ try { adapter.close(); } catch { /* swallow */ }
439
653
  throw err;
440
654
  }
441
655
 
442
656
  currentDb = adapter;
443
657
  currentPath = path;
444
658
  currentPid = process.pid;
659
+
660
+ if (!_exitHandlerRegistered) {
661
+ _exitHandlerRegistered = true;
662
+ process.on("exit", () => { try { closeDatabase(); } catch {} });
663
+ }
664
+
445
665
  return true;
446
666
  }
447
667
 
448
- /**
449
- * Closes the current database connection.
450
- */
451
668
  export function closeDatabase(): void {
452
669
  if (currentDb) {
670
+ try {
671
+ currentDb.exec('PRAGMA wal_checkpoint(TRUNCATE)');
672
+ } catch { /* non-fatal — best effort before close */ }
453
673
  try {
454
674
  currentDb.close();
455
675
  } catch {
@@ -461,12 +681,8 @@ export function closeDatabase(): void {
461
681
  }
462
682
  }
463
683
 
464
- /**
465
- * Runs a function inside a transaction. Rolls back on error.
466
- */
467
684
  export function transaction<T>(fn: () => T): T {
468
- if (!currentDb)
469
- throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
685
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
470
686
  currentDb.exec("BEGIN");
471
687
  try {
472
688
  const result = fn();
@@ -478,35 +694,24 @@ export function transaction<T>(fn: () => T): T {
478
694
  }
479
695
  }
480
696
 
481
- // ─── Decision Wrappers ────────────────────────────────────────────────────
482
-
483
- /**
484
- * Insert a decision. The `seq` field is auto-generated.
485
- */
486
697
  export function insertDecision(d: Omit<Decision, "seq">): void {
487
- if (!currentDb)
488
- throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
489
- currentDb
490
- .prepare(
491
- `INSERT INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
698
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
699
+ currentDb.prepare(
700
+ `INSERT INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
492
701
  VALUES (:id, :when_context, :scope, :decision, :choice, :rationale, :revisable, :made_by, :superseded_by)`,
493
- )
494
- .run({
495
- ":id": d.id,
496
- ":when_context": d.when_context,
497
- ":scope": d.scope,
498
- ":decision": d.decision,
499
- ":choice": d.choice,
500
- ":rationale": d.rationale,
501
- ":revisable": d.revisable,
502
- ":made_by": d.made_by ?? "agent",
503
- ":superseded_by": d.superseded_by,
504
- });
505
- }
506
-
507
- /**
508
- * Get a decision by its ID (e.g. "D001"). Returns null if not found.
509
- */
702
+ ).run({
703
+ ":id": d.id,
704
+ ":when_context": d.when_context,
705
+ ":scope": d.scope,
706
+ ":decision": d.decision,
707
+ ":choice": d.choice,
708
+ ":rationale": d.rationale,
709
+ ":revisable": d.revisable,
710
+ ":made_by": d.made_by ?? "agent",
711
+ ":superseded_by": d.superseded_by,
712
+ });
713
+ }
714
+
510
715
  export function getDecisionById(id: string): Decision | null {
511
716
  if (!currentDb) return null;
512
717
  const row = currentDb.prepare("SELECT * FROM decisions WHERE id = ?").get(id);
@@ -525,9 +730,6 @@ export function getDecisionById(id: string): Decision | null {
525
730
  };
526
731
  }
527
732
 
528
- /**
529
- * Get all active (non-superseded) decisions.
530
- */
531
733
  export function getActiveDecisions(): Decision[] {
532
734
  if (!currentDb) return [];
533
735
  const rows = currentDb.prepare("SELECT * FROM active_decisions").all();
@@ -545,43 +747,30 @@ export function getActiveDecisions(): Decision[] {
545
747
  }));
546
748
  }
547
749
 
548
- // ─── Requirement Wrappers ─────────────────────────────────────────────────
549
-
550
- /**
551
- * Insert a requirement.
552
- */
553
750
  export function insertRequirement(r: Requirement): void {
554
- if (!currentDb)
555
- throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
556
- currentDb
557
- .prepare(
558
- `INSERT INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
751
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
752
+ currentDb.prepare(
753
+ `INSERT INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
559
754
  VALUES (:id, :class, :status, :description, :why, :source, :primary_owner, :supporting_slices, :validation, :notes, :full_content, :superseded_by)`,
560
- )
561
- .run({
562
- ":id": r.id,
563
- ":class": r.class,
564
- ":status": r.status,
565
- ":description": r.description,
566
- ":why": r.why,
567
- ":source": r.source,
568
- ":primary_owner": r.primary_owner,
569
- ":supporting_slices": r.supporting_slices,
570
- ":validation": r.validation,
571
- ":notes": r.notes,
572
- ":full_content": r.full_content,
573
- ":superseded_by": r.superseded_by,
574
- });
575
- }
576
-
577
- /**
578
- * Get a requirement by its ID (e.g. "R001"). Returns null if not found.
579
- */
755
+ ).run({
756
+ ":id": r.id,
757
+ ":class": r.class,
758
+ ":status": r.status,
759
+ ":description": r.description,
760
+ ":why": r.why,
761
+ ":source": r.source,
762
+ ":primary_owner": r.primary_owner,
763
+ ":supporting_slices": r.supporting_slices,
764
+ ":validation": r.validation,
765
+ ":notes": r.notes,
766
+ ":full_content": r.full_content,
767
+ ":superseded_by": r.superseded_by,
768
+ });
769
+ }
770
+
580
771
  export function getRequirementById(id: string): Requirement | null {
581
772
  if (!currentDb) return null;
582
- const row = currentDb
583
- .prepare("SELECT * FROM requirements WHERE id = ?")
584
- .get(id);
773
+ const row = currentDb.prepare("SELECT * FROM requirements WHERE id = ?").get(id);
585
774
  if (!row) return null;
586
775
  return {
587
776
  id: row["id"] as string,
@@ -599,9 +788,6 @@ export function getRequirementById(id: string): Requirement | null {
599
788
  };
600
789
  }
601
790
 
602
- /**
603
- * Get all active (non-superseded) requirements.
604
- */
605
791
  export function getActiveRequirements(): Requirement[] {
606
792
  if (!currentDb) return [];
607
793
  const rows = currentDb.prepare("SELECT * FROM active_requirements").all();
@@ -621,108 +807,66 @@ export function getActiveRequirements(): Requirement[] {
621
807
  }));
622
808
  }
623
809
 
624
- /**
625
- * Returns the PID of the process that opened the current DB connection.
626
- * Returns 0 if no connection is open.
627
- */
628
810
  export function getDbOwnerPid(): number {
629
811
  return currentPid;
630
812
  }
631
813
 
632
- /**
633
- * Returns the path of the currently open database, or null if none.
634
- */
635
814
  export function getDbPath(): string | null {
636
815
  return currentPath;
637
816
  }
638
817
 
639
- // ─── Internal Access (for testing) ─────────────────────────────────────────
640
-
641
- /**
642
- * Get the raw adapter for direct queries (testing only).
643
- */
644
818
  export function _getAdapter(): DbAdapter | null {
645
819
  return currentDb;
646
820
  }
647
821
 
648
- /**
649
- * Reset provider state (testing only — allows re-detection).
650
- */
651
822
  export function _resetProvider(): void {
652
823
  loadAttempted = false;
653
824
  providerModule = null;
654
825
  providerName = null;
655
826
  }
656
827
 
657
- // ─── Upsert Wrappers (for idempotent import) ─────────────────────────────
658
-
659
- /**
660
- * Insert or replace a decision. Uses the `id` UNIQUE constraint for idempotency.
661
- */
662
828
  export function upsertDecision(d: Omit<Decision, "seq">): void {
663
- if (!currentDb)
664
- throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
665
- currentDb
666
- .prepare(
667
- `INSERT OR REPLACE INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
829
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
830
+ currentDb.prepare(
831
+ `INSERT OR REPLACE INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
668
832
  VALUES (:id, :when_context, :scope, :decision, :choice, :rationale, :revisable, :made_by, :superseded_by)`,
669
- )
670
- .run({
671
- ":id": d.id,
672
- ":when_context": d.when_context,
673
- ":scope": d.scope,
674
- ":decision": d.decision,
675
- ":choice": d.choice,
676
- ":rationale": d.rationale,
677
- ":revisable": d.revisable,
678
- ":made_by": d.made_by ?? "agent",
679
- ":superseded_by": d.superseded_by ?? null,
680
- });
681
- }
682
-
683
- /**
684
- * Insert or replace a requirement. Uses the `id` PK for idempotency.
685
- */
833
+ ).run({
834
+ ":id": d.id,
835
+ ":when_context": d.when_context,
836
+ ":scope": d.scope,
837
+ ":decision": d.decision,
838
+ ":choice": d.choice,
839
+ ":rationale": d.rationale,
840
+ ":revisable": d.revisable,
841
+ ":made_by": d.made_by ?? "agent",
842
+ ":superseded_by": d.superseded_by ?? null,
843
+ });
844
+ }
845
+
686
846
  export function upsertRequirement(r: Requirement): void {
687
- if (!currentDb)
688
- throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
689
- currentDb
690
- .prepare(
691
- `INSERT OR REPLACE INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
847
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
848
+ currentDb.prepare(
849
+ `INSERT OR REPLACE INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
692
850
  VALUES (:id, :class, :status, :description, :why, :source, :primary_owner, :supporting_slices, :validation, :notes, :full_content, :superseded_by)`,
693
- )
694
- .run({
695
- ":id": r.id,
696
- ":class": r.class,
697
- ":status": r.status,
698
- ":description": r.description,
699
- ":why": r.why,
700
- ":source": r.source,
701
- ":primary_owner": r.primary_owner,
702
- ":supporting_slices": r.supporting_slices,
703
- ":validation": r.validation,
704
- ":notes": r.notes,
705
- ":full_content": r.full_content,
706
- ":superseded_by": r.superseded_by ?? null,
707
- });
708
- }
709
-
710
- /**
711
- * Insert or replace an artifact. Uses the `path` PK for idempotency.
712
- */
713
- /**
714
- * Delete all rows from the artifacts table.
715
- * The artifacts table is a read cache — clearing it forces the next
716
- * deriveState() to fall through to disk reads (native Rust batch parse).
717
- * Safe to call when no database is open (no-op).
718
- */
851
+ ).run({
852
+ ":id": r.id,
853
+ ":class": r.class,
854
+ ":status": r.status,
855
+ ":description": r.description,
856
+ ":why": r.why,
857
+ ":source": r.source,
858
+ ":primary_owner": r.primary_owner,
859
+ ":supporting_slices": r.supporting_slices,
860
+ ":validation": r.validation,
861
+ ":notes": r.notes,
862
+ ":full_content": r.full_content,
863
+ ":superseded_by": r.superseded_by ?? null,
864
+ });
865
+ }
866
+
719
867
  export function clearArtifacts(): void {
720
868
  if (!currentDb) return;
721
- try {
722
- currentDb.exec("DELETE FROM artifacts");
723
- } catch {
724
- // Clearing a cache should never be fatal
725
- }
869
+ try { currentDb.exec("DELETE FROM artifacts"); } catch { /* cache clear is best effort */ }
726
870
  }
727
871
 
728
872
  export function insertArtifact(a: {
@@ -733,22 +877,598 @@ export function insertArtifact(a: {
733
877
  task_id: string | null;
734
878
  full_content: string;
735
879
  }): void {
736
- if (!currentDb)
737
- throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
738
- currentDb
739
- .prepare(
740
- `INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
880
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
881
+ currentDb.prepare(
882
+ `INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
741
883
  VALUES (:path, :artifact_type, :milestone_id, :slice_id, :task_id, :full_content, :imported_at)`,
884
+ ).run({
885
+ ":path": a.path,
886
+ ":artifact_type": a.artifact_type,
887
+ ":milestone_id": a.milestone_id,
888
+ ":slice_id": a.slice_id,
889
+ ":task_id": a.task_id,
890
+ ":full_content": a.full_content,
891
+ ":imported_at": new Date().toISOString(),
892
+ });
893
+ }
894
+
895
+ export interface MilestonePlanningRecord {
896
+ vision: string;
897
+ successCriteria: string[];
898
+ keyRisks: Array<{ risk: string; whyItMatters: string }>;
899
+ proofStrategy: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
900
+ verificationContract: string;
901
+ verificationIntegration: string;
902
+ verificationOperational: string;
903
+ verificationUat: string;
904
+ definitionOfDone: string[];
905
+ requirementCoverage: string;
906
+ boundaryMapMarkdown: string;
907
+ }
908
+
909
+ export interface SlicePlanningRecord {
910
+ goal: string;
911
+ successCriteria: string;
912
+ proofLevel: string;
913
+ integrationClosure: string;
914
+ observabilityImpact: string;
915
+ }
916
+
917
+ export interface TaskPlanningRecord {
918
+ title?: string;
919
+ description: string;
920
+ estimate: string;
921
+ files: string[];
922
+ verify: string;
923
+ inputs: string[];
924
+ expectedOutput: string[];
925
+ observabilityImpact: string;
926
+ }
927
+
928
+ export function insertMilestone(m: {
929
+ id: string;
930
+ title?: string;
931
+ status?: string;
932
+ depends_on?: string[];
933
+ planning?: Partial<MilestonePlanningRecord>;
934
+ }): void {
935
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
936
+ currentDb.prepare(
937
+ `INSERT OR IGNORE INTO milestones (
938
+ id, title, status, depends_on, created_at,
939
+ vision, success_criteria, key_risks, proof_strategy,
940
+ verification_contract, verification_integration, verification_operational, verification_uat,
941
+ definition_of_done, requirement_coverage, boundary_map_markdown
942
+ ) VALUES (
943
+ :id, :title, :status, :depends_on, :created_at,
944
+ :vision, :success_criteria, :key_risks, :proof_strategy,
945
+ :verification_contract, :verification_integration, :verification_operational, :verification_uat,
946
+ :definition_of_done, :requirement_coverage, :boundary_map_markdown
947
+ )`,
948
+ ).run({
949
+ ":id": m.id,
950
+ ":title": m.title ?? "",
951
+ ":status": m.status ?? "active",
952
+ ":depends_on": JSON.stringify(m.depends_on ?? []),
953
+ ":created_at": new Date().toISOString(),
954
+ ":vision": m.planning?.vision ?? "",
955
+ ":success_criteria": JSON.stringify(m.planning?.successCriteria ?? []),
956
+ ":key_risks": JSON.stringify(m.planning?.keyRisks ?? []),
957
+ ":proof_strategy": JSON.stringify(m.planning?.proofStrategy ?? []),
958
+ ":verification_contract": m.planning?.verificationContract ?? "",
959
+ ":verification_integration": m.planning?.verificationIntegration ?? "",
960
+ ":verification_operational": m.planning?.verificationOperational ?? "",
961
+ ":verification_uat": m.planning?.verificationUat ?? "",
962
+ ":definition_of_done": JSON.stringify(m.planning?.definitionOfDone ?? []),
963
+ ":requirement_coverage": m.planning?.requirementCoverage ?? "",
964
+ ":boundary_map_markdown": m.planning?.boundaryMapMarkdown ?? "",
965
+ });
966
+ }
967
+
968
+ export function upsertMilestonePlanning(milestoneId: string, planning: Partial<MilestonePlanningRecord>): void {
969
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
970
+ currentDb.prepare(
971
+ `UPDATE milestones SET
972
+ vision = COALESCE(:vision, vision),
973
+ success_criteria = COALESCE(:success_criteria, success_criteria),
974
+ key_risks = COALESCE(:key_risks, key_risks),
975
+ proof_strategy = COALESCE(:proof_strategy, proof_strategy),
976
+ verification_contract = COALESCE(:verification_contract, verification_contract),
977
+ verification_integration = COALESCE(:verification_integration, verification_integration),
978
+ verification_operational = COALESCE(:verification_operational, verification_operational),
979
+ verification_uat = COALESCE(:verification_uat, verification_uat),
980
+ definition_of_done = COALESCE(:definition_of_done, definition_of_done),
981
+ requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
982
+ boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
983
+ WHERE id = :id`,
984
+ ).run({
985
+ ":id": milestoneId,
986
+ ":vision": planning.vision ?? null,
987
+ ":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
988
+ ":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
989
+ ":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
990
+ ":verification_contract": planning.verificationContract ?? null,
991
+ ":verification_integration": planning.verificationIntegration ?? null,
992
+ ":verification_operational": planning.verificationOperational ?? null,
993
+ ":verification_uat": planning.verificationUat ?? null,
994
+ ":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
995
+ ":requirement_coverage": planning.requirementCoverage ?? null,
996
+ ":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
997
+ });
998
+ }
999
+
1000
+ export function insertSlice(s: {
1001
+ id: string;
1002
+ milestoneId: string;
1003
+ title?: string;
1004
+ status?: string;
1005
+ risk?: string;
1006
+ depends?: string[];
1007
+ demo?: string;
1008
+ sequence?: number;
1009
+ planning?: Partial<SlicePlanningRecord>;
1010
+ }): void {
1011
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1012
+ currentDb.prepare(
1013
+ `INSERT OR IGNORE INTO slices (
1014
+ milestone_id, id, title, status, risk, depends, demo, created_at,
1015
+ goal, success_criteria, proof_level, integration_closure, observability_impact, sequence
1016
+ ) VALUES (
1017
+ :milestone_id, :id, :title, :status, :risk, :depends, :demo, :created_at,
1018
+ :goal, :success_criteria, :proof_level, :integration_closure, :observability_impact, :sequence
1019
+ )`,
1020
+ ).run({
1021
+ ":milestone_id": s.milestoneId,
1022
+ ":id": s.id,
1023
+ ":title": s.title ?? "",
1024
+ ":status": s.status ?? "pending",
1025
+ ":risk": s.risk ?? "medium",
1026
+ ":depends": JSON.stringify(s.depends ?? []),
1027
+ ":demo": s.demo ?? "",
1028
+ ":created_at": new Date().toISOString(),
1029
+ ":goal": s.planning?.goal ?? "",
1030
+ ":success_criteria": s.planning?.successCriteria ?? "",
1031
+ ":proof_level": s.planning?.proofLevel ?? "",
1032
+ ":integration_closure": s.planning?.integrationClosure ?? "",
1033
+ ":observability_impact": s.planning?.observabilityImpact ?? "",
1034
+ ":sequence": s.sequence ?? 0,
1035
+ });
1036
+ }
1037
+
1038
+ export function upsertSlicePlanning(milestoneId: string, sliceId: string, planning: Partial<SlicePlanningRecord>): void {
1039
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1040
+ currentDb.prepare(
1041
+ `UPDATE slices SET
1042
+ goal = COALESCE(:goal, goal),
1043
+ success_criteria = COALESCE(:success_criteria, success_criteria),
1044
+ proof_level = COALESCE(:proof_level, proof_level),
1045
+ integration_closure = COALESCE(:integration_closure, integration_closure),
1046
+ observability_impact = COALESCE(:observability_impact, observability_impact)
1047
+ WHERE milestone_id = :milestone_id AND id = :id`,
1048
+ ).run({
1049
+ ":milestone_id": milestoneId,
1050
+ ":id": sliceId,
1051
+ ":goal": planning.goal ?? null,
1052
+ ":success_criteria": planning.successCriteria ?? null,
1053
+ ":proof_level": planning.proofLevel ?? null,
1054
+ ":integration_closure": planning.integrationClosure ?? null,
1055
+ ":observability_impact": planning.observabilityImpact ?? null,
1056
+ });
1057
+ }
1058
+
1059
+ export function insertTask(t: {
1060
+ id: string;
1061
+ sliceId: string;
1062
+ milestoneId: string;
1063
+ title?: string;
1064
+ status?: string;
1065
+ oneLiner?: string;
1066
+ narrative?: string;
1067
+ verificationResult?: string;
1068
+ duration?: string;
1069
+ blockerDiscovered?: boolean;
1070
+ deviations?: string;
1071
+ knownIssues?: string;
1072
+ keyFiles?: string[];
1073
+ keyDecisions?: string[];
1074
+ fullSummaryMd?: string;
1075
+ sequence?: number;
1076
+ planning?: Partial<TaskPlanningRecord>;
1077
+ }): void {
1078
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1079
+ currentDb.prepare(
1080
+ `INSERT INTO tasks (
1081
+ milestone_id, slice_id, id, title, status, one_liner, narrative,
1082
+ verification_result, duration, completed_at, blocker_discovered,
1083
+ deviations, known_issues, key_files, key_decisions, full_summary_md,
1084
+ description, estimate, files, verify, inputs, expected_output, observability_impact, sequence
1085
+ ) VALUES (
1086
+ :milestone_id, :slice_id, :id, :title, :status, :one_liner, :narrative,
1087
+ :verification_result, :duration, :completed_at, :blocker_discovered,
1088
+ :deviations, :known_issues, :key_files, :key_decisions, :full_summary_md,
1089
+ :description, :estimate, :files, :verify, :inputs, :expected_output, :observability_impact, :sequence
742
1090
  )
743
- .run({
744
- ":path": a.path,
745
- ":artifact_type": a.artifact_type,
746
- ":milestone_id": a.milestone_id,
747
- ":slice_id": a.slice_id,
748
- ":task_id": a.task_id,
749
- ":full_content": a.full_content,
750
- ":imported_at": new Date().toISOString(),
751
- });
1091
+ ON CONFLICT(milestone_id, slice_id, id) DO UPDATE SET
1092
+ title = CASE WHEN NULLIF(:title, '') IS NOT NULL THEN :title ELSE tasks.title END,
1093
+ status = :status,
1094
+ one_liner = :one_liner,
1095
+ narrative = :narrative,
1096
+ verification_result = :verification_result,
1097
+ duration = :duration,
1098
+ completed_at = :completed_at,
1099
+ blocker_discovered = :blocker_discovered,
1100
+ deviations = :deviations,
1101
+ known_issues = :known_issues,
1102
+ key_files = :key_files,
1103
+ key_decisions = :key_decisions,
1104
+ full_summary_md = :full_summary_md,
1105
+ description = CASE WHEN NULLIF(:description, '') IS NOT NULL THEN :description ELSE tasks.description END,
1106
+ estimate = CASE WHEN NULLIF(:estimate, '') IS NOT NULL THEN :estimate ELSE tasks.estimate END,
1107
+ files = CASE WHEN NULLIF(:files, '[]') IS NOT NULL THEN :files ELSE tasks.files END,
1108
+ verify = CASE WHEN NULLIF(:verify, '') IS NOT NULL THEN :verify ELSE tasks.verify END,
1109
+ inputs = CASE WHEN NULLIF(:inputs, '[]') IS NOT NULL THEN :inputs ELSE tasks.inputs END,
1110
+ expected_output = CASE WHEN NULLIF(:expected_output, '[]') IS NOT NULL THEN :expected_output ELSE tasks.expected_output END,
1111
+ observability_impact = CASE WHEN NULLIF(:observability_impact, '') IS NOT NULL THEN :observability_impact ELSE tasks.observability_impact END,
1112
+ sequence = :sequence`,
1113
+ ).run({
1114
+ ":milestone_id": t.milestoneId,
1115
+ ":slice_id": t.sliceId,
1116
+ ":id": t.id,
1117
+ ":title": t.title ?? "",
1118
+ ":status": t.status ?? "pending",
1119
+ ":one_liner": t.oneLiner ?? "",
1120
+ ":narrative": t.narrative ?? "",
1121
+ ":verification_result": t.verificationResult ?? "",
1122
+ ":duration": t.duration ?? "",
1123
+ ":completed_at": t.status === "done" || t.status === "complete" ? new Date().toISOString() : null,
1124
+ ":blocker_discovered": t.blockerDiscovered ? 1 : 0,
1125
+ ":deviations": t.deviations ?? "",
1126
+ ":known_issues": t.knownIssues ?? "",
1127
+ ":key_files": JSON.stringify(t.keyFiles ?? []),
1128
+ ":key_decisions": JSON.stringify(t.keyDecisions ?? []),
1129
+ ":full_summary_md": t.fullSummaryMd ?? "",
1130
+ ":description": t.planning?.description ?? "",
1131
+ ":estimate": t.planning?.estimate ?? "",
1132
+ ":files": JSON.stringify(t.planning?.files ?? []),
1133
+ ":verify": t.planning?.verify ?? "",
1134
+ ":inputs": JSON.stringify(t.planning?.inputs ?? []),
1135
+ ":expected_output": JSON.stringify(t.planning?.expectedOutput ?? []),
1136
+ ":observability_impact": t.planning?.observabilityImpact ?? "",
1137
+ ":sequence": t.sequence ?? 0,
1138
+ });
1139
+ }
1140
+
1141
+ export function updateTaskStatus(milestoneId: string, sliceId: string, taskId: string, status: string, completedAt?: string): void {
1142
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1143
+ currentDb.prepare(
1144
+ `UPDATE tasks SET status = :status, completed_at = :completed_at
1145
+ WHERE milestone_id = :milestone_id AND slice_id = :slice_id AND id = :id`,
1146
+ ).run({
1147
+ ":status": status,
1148
+ ":completed_at": completedAt ?? null,
1149
+ ":milestone_id": milestoneId,
1150
+ ":slice_id": sliceId,
1151
+ ":id": taskId,
1152
+ });
1153
+ }
1154
+
1155
+ export function upsertTaskPlanning(milestoneId: string, sliceId: string, taskId: string, planning: Partial<TaskPlanningRecord>): void {
1156
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1157
+ currentDb.prepare(
1158
+ `UPDATE tasks SET
1159
+ title = COALESCE(:title, title),
1160
+ description = COALESCE(:description, description),
1161
+ estimate = COALESCE(:estimate, estimate),
1162
+ files = COALESCE(:files, files),
1163
+ verify = COALESCE(:verify, verify),
1164
+ inputs = COALESCE(:inputs, inputs),
1165
+ expected_output = COALESCE(:expected_output, expected_output),
1166
+ observability_impact = COALESCE(:observability_impact, observability_impact)
1167
+ WHERE milestone_id = :milestone_id AND slice_id = :slice_id AND id = :id`,
1168
+ ).run({
1169
+ ":milestone_id": milestoneId,
1170
+ ":slice_id": sliceId,
1171
+ ":id": taskId,
1172
+ ":title": planning.title ?? null,
1173
+ ":description": planning.description ?? null,
1174
+ ":estimate": planning.estimate ?? null,
1175
+ ":files": planning.files ? JSON.stringify(planning.files) : null,
1176
+ ":verify": planning.verify ?? null,
1177
+ ":inputs": planning.inputs ? JSON.stringify(planning.inputs) : null,
1178
+ ":expected_output": planning.expectedOutput ? JSON.stringify(planning.expectedOutput) : null,
1179
+ ":observability_impact": planning.observabilityImpact ?? null,
1180
+ });
1181
+ }
1182
+
1183
+ export interface SliceRow {
1184
+ milestone_id: string;
1185
+ id: string;
1186
+ title: string;
1187
+ status: string;
1188
+ risk: string;
1189
+ depends: string[];
1190
+ demo: string;
1191
+ created_at: string;
1192
+ completed_at: string | null;
1193
+ full_summary_md: string;
1194
+ full_uat_md: string;
1195
+ goal: string;
1196
+ success_criteria: string;
1197
+ proof_level: string;
1198
+ integration_closure: string;
1199
+ observability_impact: string;
1200
+ sequence: number;
1201
+ replan_triggered_at: string | null;
1202
+ }
1203
+
1204
+ function rowToSlice(row: Record<string, unknown>): SliceRow {
1205
+ return {
1206
+ milestone_id: row["milestone_id"] as string,
1207
+ id: row["id"] as string,
1208
+ title: row["title"] as string,
1209
+ status: row["status"] as string,
1210
+ risk: row["risk"] as string,
1211
+ depends: JSON.parse((row["depends"] as string) || "[]"),
1212
+ demo: (row["demo"] as string) ?? "",
1213
+ created_at: row["created_at"] as string,
1214
+ completed_at: (row["completed_at"] as string) ?? null,
1215
+ full_summary_md: (row["full_summary_md"] as string) ?? "",
1216
+ full_uat_md: (row["full_uat_md"] as string) ?? "",
1217
+ goal: (row["goal"] as string) ?? "",
1218
+ success_criteria: (row["success_criteria"] as string) ?? "",
1219
+ proof_level: (row["proof_level"] as string) ?? "",
1220
+ integration_closure: (row["integration_closure"] as string) ?? "",
1221
+ observability_impact: (row["observability_impact"] as string) ?? "",
1222
+ sequence: (row["sequence"] as number) ?? 0,
1223
+ replan_triggered_at: (row["replan_triggered_at"] as string) ?? null,
1224
+ };
1225
+ }
1226
+
1227
+ export function getSlice(milestoneId: string, sliceId: string): SliceRow | null {
1228
+ if (!currentDb) return null;
1229
+ const row = currentDb.prepare("SELECT * FROM slices WHERE milestone_id = :mid AND id = :sid").get({ ":mid": milestoneId, ":sid": sliceId });
1230
+ if (!row) return null;
1231
+ return rowToSlice(row);
1232
+ }
1233
+
1234
+ export function updateSliceStatus(milestoneId: string, sliceId: string, status: string, completedAt?: string): void {
1235
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1236
+ currentDb.prepare(
1237
+ `UPDATE slices SET status = :status, completed_at = :completed_at
1238
+ WHERE milestone_id = :milestone_id AND id = :id`,
1239
+ ).run({
1240
+ ":status": status,
1241
+ ":completed_at": completedAt ?? null,
1242
+ ":milestone_id": milestoneId,
1243
+ ":id": sliceId,
1244
+ });
1245
+ }
1246
+
1247
+ export interface TaskRow {
1248
+ milestone_id: string;
1249
+ slice_id: string;
1250
+ id: string;
1251
+ title: string;
1252
+ status: string;
1253
+ one_liner: string;
1254
+ narrative: string;
1255
+ verification_result: string;
1256
+ duration: string;
1257
+ completed_at: string | null;
1258
+ blocker_discovered: boolean;
1259
+ deviations: string;
1260
+ known_issues: string;
1261
+ key_files: string[];
1262
+ key_decisions: string[];
1263
+ full_summary_md: string;
1264
+ description: string;
1265
+ estimate: string;
1266
+ files: string[];
1267
+ verify: string;
1268
+ inputs: string[];
1269
+ expected_output: string[];
1270
+ observability_impact: string;
1271
+ sequence: number;
1272
+ }
1273
+
1274
+ function rowToTask(row: Record<string, unknown>): TaskRow {
1275
+ return {
1276
+ milestone_id: row["milestone_id"] as string,
1277
+ slice_id: row["slice_id"] as string,
1278
+ id: row["id"] as string,
1279
+ title: row["title"] as string,
1280
+ status: row["status"] as string,
1281
+ one_liner: row["one_liner"] as string,
1282
+ narrative: row["narrative"] as string,
1283
+ verification_result: row["verification_result"] as string,
1284
+ duration: row["duration"] as string,
1285
+ completed_at: (row["completed_at"] as string) ?? null,
1286
+ blocker_discovered: (row["blocker_discovered"] as number) === 1,
1287
+ deviations: row["deviations"] as string,
1288
+ known_issues: row["known_issues"] as string,
1289
+ key_files: JSON.parse((row["key_files"] as string) || "[]"),
1290
+ key_decisions: JSON.parse((row["key_decisions"] as string) || "[]"),
1291
+ full_summary_md: row["full_summary_md"] as string,
1292
+ description: (row["description"] as string) ?? "",
1293
+ estimate: (row["estimate"] as string) ?? "",
1294
+ files: JSON.parse((row["files"] as string) || "[]"),
1295
+ verify: (row["verify"] as string) ?? "",
1296
+ inputs: JSON.parse((row["inputs"] as string) || "[]"),
1297
+ expected_output: JSON.parse((row["expected_output"] as string) || "[]"),
1298
+ observability_impact: (row["observability_impact"] as string) ?? "",
1299
+ sequence: (row["sequence"] as number) ?? 0,
1300
+ };
1301
+ }
1302
+
1303
+ export function getTask(milestoneId: string, sliceId: string, taskId: string): TaskRow | null {
1304
+ if (!currentDb) return null;
1305
+ const row = currentDb.prepare(
1306
+ "SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid",
1307
+ ).get({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
1308
+ if (!row) return null;
1309
+ return rowToTask(row);
1310
+ }
1311
+
1312
+ export function getSliceTasks(milestoneId: string, sliceId: string): TaskRow[] {
1313
+ if (!currentDb) return [];
1314
+ const rows = currentDb.prepare(
1315
+ "SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid ORDER BY sequence, id",
1316
+ ).all({ ":mid": milestoneId, ":sid": sliceId });
1317
+ return rows.map(rowToTask);
1318
+ }
1319
+
1320
+ export function insertVerificationEvidence(e: {
1321
+ taskId: string;
1322
+ sliceId: string;
1323
+ milestoneId: string;
1324
+ command: string;
1325
+ exitCode: number;
1326
+ verdict: string;
1327
+ durationMs: number;
1328
+ }): void {
1329
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1330
+ currentDb.prepare(
1331
+ `INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
1332
+ VALUES (:task_id, :slice_id, :milestone_id, :command, :exit_code, :verdict, :duration_ms, :created_at)`,
1333
+ ).run({
1334
+ ":task_id": e.taskId,
1335
+ ":slice_id": e.sliceId,
1336
+ ":milestone_id": e.milestoneId,
1337
+ ":command": e.command,
1338
+ ":exit_code": e.exitCode,
1339
+ ":verdict": e.verdict,
1340
+ ":duration_ms": e.durationMs,
1341
+ ":created_at": new Date().toISOString(),
1342
+ });
1343
+ }
1344
+
1345
+ export interface MilestoneRow {
1346
+ id: string;
1347
+ title: string;
1348
+ status: string;
1349
+ depends_on: string[];
1350
+ created_at: string;
1351
+ completed_at: string | null;
1352
+ vision: string;
1353
+ success_criteria: string[];
1354
+ key_risks: Array<{ risk: string; whyItMatters: string }>;
1355
+ proof_strategy: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
1356
+ verification_contract: string;
1357
+ verification_integration: string;
1358
+ verification_operational: string;
1359
+ verification_uat: string;
1360
+ definition_of_done: string[];
1361
+ requirement_coverage: string;
1362
+ boundary_map_markdown: string;
1363
+ }
1364
+
1365
+ function rowToMilestone(row: Record<string, unknown>): MilestoneRow {
1366
+ return {
1367
+ id: row["id"] as string,
1368
+ title: row["title"] as string,
1369
+ status: row["status"] as string,
1370
+ depends_on: JSON.parse((row["depends_on"] as string) || "[]"),
1371
+ created_at: row["created_at"] as string,
1372
+ completed_at: (row["completed_at"] as string) ?? null,
1373
+ vision: (row["vision"] as string) ?? "",
1374
+ success_criteria: JSON.parse((row["success_criteria"] as string) || "[]"),
1375
+ key_risks: JSON.parse((row["key_risks"] as string) || "[]"),
1376
+ proof_strategy: JSON.parse((row["proof_strategy"] as string) || "[]"),
1377
+ verification_contract: (row["verification_contract"] as string) ?? "",
1378
+ verification_integration: (row["verification_integration"] as string) ?? "",
1379
+ verification_operational: (row["verification_operational"] as string) ?? "",
1380
+ verification_uat: (row["verification_uat"] as string) ?? "",
1381
+ definition_of_done: JSON.parse((row["definition_of_done"] as string) || "[]"),
1382
+ requirement_coverage: (row["requirement_coverage"] as string) ?? "",
1383
+ boundary_map_markdown: (row["boundary_map_markdown"] as string) ?? "",
1384
+ };
1385
+ }
1386
+
1387
+ export interface ArtifactRow {
1388
+ path: string;
1389
+ artifact_type: string;
1390
+ milestone_id: string | null;
1391
+ slice_id: string | null;
1392
+ task_id: string | null;
1393
+ full_content: string;
1394
+ imported_at: string;
1395
+ }
1396
+
1397
+ function rowToArtifact(row: Record<string, unknown>): ArtifactRow {
1398
+ return {
1399
+ path: row["path"] as string,
1400
+ artifact_type: row["artifact_type"] as string,
1401
+ milestone_id: (row["milestone_id"] as string) ?? null,
1402
+ slice_id: (row["slice_id"] as string) ?? null,
1403
+ task_id: (row["task_id"] as string) ?? null,
1404
+ full_content: row["full_content"] as string,
1405
+ imported_at: row["imported_at"] as string,
1406
+ };
1407
+ }
1408
+
1409
+ export function getAllMilestones(): MilestoneRow[] {
1410
+ if (!currentDb) return [];
1411
+ const rows = currentDb.prepare("SELECT * FROM milestones ORDER BY id").all();
1412
+ return rows.map(rowToMilestone);
1413
+ }
1414
+
1415
+ export function getMilestone(id: string): MilestoneRow | null {
1416
+ if (!currentDb) return null;
1417
+ const row = currentDb.prepare("SELECT * FROM milestones WHERE id = :id").get({ ":id": id });
1418
+ if (!row) return null;
1419
+ return rowToMilestone(row);
1420
+ }
1421
+
1422
+ export function getActiveMilestoneFromDb(): MilestoneRow | null {
1423
+ if (!currentDb) return null;
1424
+ const row = currentDb.prepare(
1425
+ "SELECT * FROM milestones WHERE status NOT IN ('complete', 'parked') ORDER BY id LIMIT 1",
1426
+ ).get();
1427
+ if (!row) return null;
1428
+ return rowToMilestone(row);
1429
+ }
1430
+
1431
+ export function getActiveSliceFromDb(milestoneId: string): SliceRow | null {
1432
+ if (!currentDb) return null;
1433
+ const rows = currentDb.prepare(
1434
+ "SELECT * FROM slices WHERE milestone_id = :mid AND status NOT IN ('complete', 'done') ORDER BY sequence, id",
1435
+ ).all({ ":mid": milestoneId });
1436
+ if (rows.length === 0) return null;
1437
+
1438
+ const completedRows = currentDb.prepare(
1439
+ "SELECT id FROM slices WHERE milestone_id = :mid AND status IN ('complete', 'done')",
1440
+ ).all({ ":mid": milestoneId });
1441
+ const completedIds = new Set(completedRows.map((r) => r["id"] as string));
1442
+
1443
+ for (const row of rows) {
1444
+ const slice = rowToSlice(row);
1445
+ if (slice.depends.length === 0 || slice.depends.every((d) => completedIds.has(d))) {
1446
+ return slice;
1447
+ }
1448
+ }
1449
+ return null;
1450
+ }
1451
+
1452
+ export function getActiveTaskFromDb(milestoneId: string, sliceId: string): TaskRow | null {
1453
+ if (!currentDb) return null;
1454
+ const row = currentDb.prepare(
1455
+ "SELECT * FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND status NOT IN ('complete', 'done') ORDER BY sequence, id LIMIT 1",
1456
+ ).get({ ":mid": milestoneId, ":sid": sliceId });
1457
+ if (!row) return null;
1458
+ return rowToTask(row);
1459
+ }
1460
+
1461
+ export function getMilestoneSlices(milestoneId: string): SliceRow[] {
1462
+ if (!currentDb) return [];
1463
+ const rows = currentDb.prepare("SELECT * FROM slices WHERE milestone_id = :mid ORDER BY sequence, id").all({ ":mid": milestoneId });
1464
+ return rows.map(rowToSlice);
1465
+ }
1466
+
1467
+ export function getArtifact(path: string): ArtifactRow | null {
1468
+ if (!currentDb) return null;
1469
+ const row = currentDb.prepare("SELECT * FROM artifacts WHERE path = :path").get({ ":path": path });
1470
+ if (!row) return null;
1471
+ return rowToArtifact(row);
752
1472
  }
753
1473
 
754
1474
  // ─── Worktree DB Helpers ──────────────────────────────────────────────────
@@ -761,9 +1481,7 @@ export function copyWorktreeDb(srcDbPath: string, destDbPath: string): boolean {
761
1481
  copyFileSync(srcDbPath, destDbPath);
762
1482
  return true;
763
1483
  } catch (err) {
764
- process.stderr.write(
765
- `gsd-db: failed to copy DB to worktree: ${(err as Error).message}\n`,
766
- );
1484
+ process.stderr.write(`gsd-db: failed to copy DB to worktree: ${(err as Error).message}\n`);
767
1485
  return false;
768
1486
  }
769
1487
  }
@@ -777,25 +1495,16 @@ export function reconcileWorktreeDb(
777
1495
  artifacts: number;
778
1496
  conflicts: string[];
779
1497
  } {
780
- const zero = {
781
- decisions: 0,
782
- requirements: 0,
783
- artifacts: 0,
784
- conflicts: [] as string[],
785
- };
1498
+ const zero = { decisions: 0, requirements: 0, artifacts: 0, conflicts: [] as string[] };
786
1499
  if (!existsSync(worktreeDbPath)) return zero;
787
1500
  if (worktreeDbPath.includes("'")) {
788
- process.stderr.write(
789
- `gsd-db: worktree DB reconciliation failed: path contains unsafe characters\n`,
790
- );
1501
+ process.stderr.write("gsd-db: worktree DB reconciliation failed: path contains unsafe characters\n");
791
1502
  return zero;
792
1503
  }
793
1504
  if (!currentDb) {
794
1505
  const opened = openDatabase(mainDbPath);
795
1506
  if (!opened) {
796
- process.stderr.write(
797
- `gsd-db: worktree DB reconciliation failed: cannot open main DB\n`,
798
- );
1507
+ process.stderr.write("gsd-db: worktree DB reconciliation failed: cannot open main DB\n");
799
1508
  return zero;
800
1509
  }
801
1510
  }
@@ -804,106 +1513,184 @@ export function reconcileWorktreeDb(
804
1513
  try {
805
1514
  adapter.exec(`ATTACH DATABASE '${worktreeDbPath}' AS wt`);
806
1515
  try {
807
- // Check if attached wt database has the made_by column (legacy v3 worktrees won't)
808
1516
  const wtInfo = adapter.prepare("PRAGMA wt.table_info('decisions')").all();
809
1517
  const hasMadeBy = wtInfo.some((col) => col["name"] === "made_by");
810
1518
 
811
- const decConf = adapter
812
- .prepare(
813
- `SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${
814
- hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"
815
- } OR m.superseded_by IS NOT w.superseded_by`,
816
- )
817
- .all();
818
- for (const row of decConf)
819
- conflicts.push(
820
- `decision ${(row as Record<string, unknown>)["id"]}: modified in both`,
821
- );
822
- const reqConf = adapter
823
- .prepare(
824
- `SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`,
825
- )
826
- .all();
827
- for (const row of reqConf)
828
- conflicts.push(
829
- `requirement ${(row as Record<string, unknown>)["id"]}: modified in both`,
830
- );
1519
+ const decConf = adapter.prepare(
1520
+ `SELECT m.id FROM decisions m INNER JOIN wt.decisions w ON m.id = w.id WHERE m.decision != w.decision OR m.choice != w.choice OR m.rationale != w.rationale OR ${
1521
+ hasMadeBy ? "m.made_by != w.made_by" : "'agent' != 'agent'"
1522
+ } OR m.superseded_by IS NOT w.superseded_by`,
1523
+ ).all();
1524
+ for (const row of decConf) conflicts.push(`decision ${(row as Record<string, unknown>)["id"]}: modified in both`);
1525
+
1526
+ const reqConf = adapter.prepare(
1527
+ `SELECT m.id FROM requirements m INNER JOIN wt.requirements w ON m.id = w.id WHERE m.description != w.description OR m.status != w.status OR m.notes != w.notes OR m.superseded_by IS NOT w.superseded_by`,
1528
+ ).all();
1529
+ for (const row of reqConf) conflicts.push(`requirement ${(row as Record<string, unknown>)["id"]}: modified in both`);
1530
+
831
1531
  const merged = { decisions: 0, requirements: 0, artifacts: 0 };
832
1532
  adapter.exec("BEGIN");
833
1533
  try {
834
- const dR = adapter
835
- .prepare(
836
- `
1534
+ const dR = adapter.prepare(`
837
1535
  INSERT OR REPLACE INTO decisions (
838
1536
  id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by
839
1537
  )
840
- SELECT
841
- id, when_context, scope, decision, choice, rationale, revisable, ${
842
- hasMadeBy ? "made_by" : "'agent'"
843
- }, superseded_by
844
- FROM wt.decisions
845
- `,
846
- )
847
- .run();
848
- merged.decisions =
849
- typeof dR === "object" && dR !== null
850
- ? ((dR as { changes?: number }).changes ?? 0)
851
- : 0;
852
- const rR = adapter
853
- .prepare(
854
- `
1538
+ SELECT id, when_context, scope, decision, choice, rationale, revisable, ${
1539
+ hasMadeBy ? "made_by" : "'agent'"
1540
+ }, superseded_by FROM wt.decisions
1541
+ `).run();
1542
+ merged.decisions = typeof dR === "object" && dR !== null ? ((dR as { changes?: number }).changes ?? 0) : 0;
1543
+
1544
+ const rR = adapter.prepare(`
855
1545
  INSERT OR REPLACE INTO requirements (
856
1546
  id, class, status, description, why, source, primary_owner,
857
1547
  supporting_slices, validation, notes, full_content, superseded_by
858
1548
  )
859
- SELECT
860
- id, class, status, description, why, source, primary_owner,
861
- supporting_slices, validation, notes, full_content, superseded_by
1549
+ SELECT id, class, status, description, why, source, primary_owner,
1550
+ supporting_slices, validation, notes, full_content, superseded_by
862
1551
  FROM wt.requirements
863
- `,
864
- )
865
- .run();
866
- merged.requirements =
867
- typeof rR === "object" && rR !== null
868
- ? ((rR as { changes?: number }).changes ?? 0)
869
- : 0;
870
- const aR = adapter
871
- .prepare(
872
- `
1552
+ `).run();
1553
+ merged.requirements = typeof rR === "object" && rR !== null ? ((rR as { changes?: number }).changes ?? 0) : 0;
1554
+
1555
+ const aR = adapter.prepare(`
873
1556
  INSERT OR REPLACE INTO artifacts (
874
1557
  path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
875
1558
  )
876
- SELECT
877
- path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
1559
+ SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at
878
1560
  FROM wt.artifacts
879
- `,
880
- )
881
- .run();
882
- merged.artifacts =
883
- typeof aR === "object" && aR !== null
884
- ? ((aR as { changes?: number }).changes ?? 0)
885
- : 0;
1561
+ `).run();
1562
+ merged.artifacts = typeof aR === "object" && aR !== null ? ((aR as { changes?: number }).changes ?? 0) : 0;
1563
+
886
1564
  adapter.exec("COMMIT");
887
1565
  } catch (txErr) {
888
- try {
889
- adapter.exec("ROLLBACK");
890
- } catch {
891
- /* best-effort */
892
- }
1566
+ try { adapter.exec("ROLLBACK"); } catch { /* best effort */ }
893
1567
  throw txErr;
894
1568
  }
895
1569
  return { ...merged, conflicts };
896
1570
  } finally {
897
- try {
898
- adapter.exec("DETACH DATABASE wt");
899
- } catch {
900
- /* best-effort */
901
- }
1571
+ try { adapter.exec("DETACH DATABASE wt"); } catch { /* best effort */ }
902
1572
  }
903
1573
  } catch (err) {
904
- process.stderr.write(
905
- `gsd-db: worktree DB reconciliation failed: ${(err as Error).message}\n`,
906
- );
1574
+ process.stderr.write(`gsd-db: worktree DB reconciliation failed: ${(err as Error).message}\n`);
907
1575
  return { ...zero, conflicts };
908
1576
  }
909
1577
  }
1578
+
1579
+ // ─── Replan & Assessment Helpers ──────────────────────────────────────────
1580
+
1581
+ export function insertReplanHistory(entry: {
1582
+ milestoneId: string;
1583
+ sliceId?: string | null;
1584
+ taskId?: string | null;
1585
+ summary: string;
1586
+ previousArtifactPath?: string | null;
1587
+ replacementArtifactPath?: string | null;
1588
+ }): void {
1589
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1590
+ currentDb.prepare(
1591
+ `INSERT INTO replan_history (milestone_id, slice_id, task_id, summary, previous_artifact_path, replacement_artifact_path, created_at)
1592
+ VALUES (:milestone_id, :slice_id, :task_id, :summary, :previous_artifact_path, :replacement_artifact_path, :created_at)`,
1593
+ ).run({
1594
+ ":milestone_id": entry.milestoneId,
1595
+ ":slice_id": entry.sliceId ?? null,
1596
+ ":task_id": entry.taskId ?? null,
1597
+ ":summary": entry.summary,
1598
+ ":previous_artifact_path": entry.previousArtifactPath ?? null,
1599
+ ":replacement_artifact_path": entry.replacementArtifactPath ?? null,
1600
+ ":created_at": new Date().toISOString(),
1601
+ });
1602
+ }
1603
+
1604
+ export function insertAssessment(entry: {
1605
+ path: string;
1606
+ milestoneId: string;
1607
+ sliceId?: string | null;
1608
+ taskId?: string | null;
1609
+ status: string;
1610
+ scope: string;
1611
+ fullContent: string;
1612
+ }): void {
1613
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1614
+ currentDb.prepare(
1615
+ `INSERT OR REPLACE INTO assessments (path, milestone_id, slice_id, task_id, status, scope, full_content, created_at)
1616
+ VALUES (:path, :milestone_id, :slice_id, :task_id, :status, :scope, :full_content, :created_at)`,
1617
+ ).run({
1618
+ ":path": entry.path,
1619
+ ":milestone_id": entry.milestoneId,
1620
+ ":slice_id": entry.sliceId ?? null,
1621
+ ":task_id": entry.taskId ?? null,
1622
+ ":status": entry.status,
1623
+ ":scope": entry.scope,
1624
+ ":full_content": entry.fullContent,
1625
+ ":created_at": new Date().toISOString(),
1626
+ });
1627
+ }
1628
+
1629
+ export function deleteTask(milestoneId: string, sliceId: string, taskId: string): void {
1630
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1631
+ // Must delete verification_evidence first (FK constraint)
1632
+ currentDb.prepare(
1633
+ `DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid AND task_id = :tid`,
1634
+ ).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
1635
+ currentDb.prepare(
1636
+ `DELETE FROM tasks WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
1637
+ ).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
1638
+ }
1639
+
1640
+ export function deleteSlice(milestoneId: string, sliceId: string): void {
1641
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1642
+ // Cascade-style manual deletion: evidence → tasks → slice
1643
+ currentDb.prepare(
1644
+ `DELETE FROM verification_evidence WHERE milestone_id = :mid AND slice_id = :sid`,
1645
+ ).run({ ":mid": milestoneId, ":sid": sliceId });
1646
+ currentDb.prepare(
1647
+ `DELETE FROM tasks WHERE milestone_id = :mid AND slice_id = :sid`,
1648
+ ).run({ ":mid": milestoneId, ":sid": sliceId });
1649
+ currentDb.prepare(
1650
+ `DELETE FROM slices WHERE milestone_id = :mid AND id = :sid`,
1651
+ ).run({ ":mid": milestoneId, ":sid": sliceId });
1652
+ }
1653
+
1654
+ export function updateSliceFields(milestoneId: string, sliceId: string, fields: {
1655
+ title?: string;
1656
+ risk?: string;
1657
+ depends?: string[];
1658
+ demo?: string;
1659
+ }): void {
1660
+ if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1661
+ currentDb.prepare(
1662
+ `UPDATE slices SET
1663
+ title = COALESCE(:title, title),
1664
+ risk = COALESCE(:risk, risk),
1665
+ depends = COALESCE(:depends, depends),
1666
+ demo = COALESCE(:demo, demo)
1667
+ WHERE milestone_id = :milestone_id AND id = :id`,
1668
+ ).run({
1669
+ ":milestone_id": milestoneId,
1670
+ ":id": sliceId,
1671
+ ":title": fields.title ?? null,
1672
+ ":risk": fields.risk ?? null,
1673
+ ":depends": fields.depends ? JSON.stringify(fields.depends) : null,
1674
+ ":demo": fields.demo ?? null,
1675
+ });
1676
+ }
1677
+
1678
+ export function getReplanHistory(milestoneId: string, sliceId?: string): Array<Record<string, unknown>> {
1679
+ if (!currentDb) return [];
1680
+ if (sliceId) {
1681
+ return currentDb.prepare(
1682
+ `SELECT * FROM replan_history WHERE milestone_id = :mid AND slice_id = :sid ORDER BY created_at DESC`,
1683
+ ).all({ ":mid": milestoneId, ":sid": sliceId });
1684
+ }
1685
+ return currentDb.prepare(
1686
+ `SELECT * FROM replan_history WHERE milestone_id = :mid ORDER BY created_at DESC`,
1687
+ ).all({ ":mid": milestoneId });
1688
+ }
1689
+
1690
+ export function getAssessment(path: string): Record<string, unknown> | null {
1691
+ if (!currentDb) return null;
1692
+ const row = currentDb.prepare(
1693
+ `SELECT * FROM assessments WHERE path = :path`,
1694
+ ).get({ ":path": path });
1695
+ return row ?? null;
1696
+ }