gsd-pi 2.44.0 → 2.45.0-dev.6b9da3e

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 (463) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/activity-log.js +7 -0
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +37 -36
  5. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  6. package/dist/resources/extensions/gsd/auto-start.js +31 -2
  7. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  8. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  9. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  10. package/dist/resources/extensions/gsd/auto.js +30 -3
  11. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  12. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  13. package/dist/resources/extensions/gsd/commands/catalog.js +7 -1
  14. package/dist/resources/extensions/gsd/commands/handlers/core.js +2 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/ops.js +10 -0
  16. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  17. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  18. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  19. package/dist/resources/extensions/gsd/doctor.js +8 -0
  20. package/dist/resources/extensions/gsd/git-service.js +8 -3
  21. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  22. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  23. package/dist/resources/extensions/gsd/preferences.js +9 -1
  24. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  25. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  26. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  27. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  28. package/dist/resources/extensions/gsd/prompts/rethink.md +78 -0
  29. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  30. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  31. package/dist/resources/extensions/gsd/repo-identity.js +45 -7
  32. package/dist/resources/extensions/gsd/rethink.js +115 -0
  33. package/dist/resources/extensions/gsd/state.js +41 -3
  34. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  35. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  36. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  37. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  38. package/dist/resources/extensions/gsd/worktree-manager.js +32 -2
  39. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  40. package/dist/resources/extensions/mcp-client/index.js +14 -0
  41. package/dist/web/standalone/.next/BUILD_ID +1 -1
  42. package/dist/web/standalone/.next/app-path-routes-manifest.json +8 -8
  43. package/dist/web/standalone/.next/build-manifest.json +4 -4
  44. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  45. package/dist/web/standalone/.next/react-loadable-manifest.json +2 -2
  46. package/dist/web/standalone/.next/required-server-files.json +3 -3
  47. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  48. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  50. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  58. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.rsc +5 -5
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +5 -5
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  67. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  68. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  70. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  71. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  72. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  73. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  74. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  75. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  76. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  78. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  79. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  80. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  82. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  84. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  86. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  88. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  112. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  134. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  136. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  138. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/index.html +1 -1
  148. package/dist/web/standalone/.next/server/app/index.rsc +6 -6
  149. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  150. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +6 -6
  151. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  153. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  154. package/dist/web/standalone/.next/server/app/page.js +2 -2
  155. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app-paths-manifest.json +8 -8
  157. package/dist/web/standalone/.next/server/chunks/229.js +1 -1
  158. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  159. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/middleware.js +2 -2
  162. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  164. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  165. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  166. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  167. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +9 -0
  168. package/dist/web/standalone/.next/static/chunks/{3721.bf31263de6d5fa46.js → 485.243af25f0cdf50d6.js} +2 -2
  169. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  170. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  171. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  172. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  173. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  174. package/dist/web/standalone/.next/static/chunks/webpack-0a4cd455ec4197d2.js +1 -0
  175. package/dist/web/standalone/.next/static/css/dd4ae3f58ac9b600.css +1 -0
  176. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  177. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  178. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  179. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  180. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  181. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  182. package/dist/web/standalone/server.js +1 -1
  183. package/package.json +1 -1
  184. package/packages/native/dist/stream-process/index.js +2 -2
  185. package/packages/native/src/__tests__/stream-process.test.mjs +34 -0
  186. package/packages/native/src/stream-process/index.ts +2 -2
  187. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  188. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  190. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  192. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  194. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  195. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  196. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  198. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  199. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  200. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  201. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  202. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  204. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  206. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  208. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  210. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  212. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  214. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/main.js +17 -0
  217. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  229. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  233. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  236. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  237. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  238. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  239. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  240. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  242. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  243. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  245. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  246. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  247. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  248. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  249. package/packages/pi-coding-agent/package.json +1 -1
  250. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  251. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  252. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  253. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  254. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  255. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  256. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  257. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  258. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  259. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  260. package/packages/pi-coding-agent/src/main.ts +19 -0
  261. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  262. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  263. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  264. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  265. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  266. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  267. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  268. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  269. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  270. package/pkg/package.json +1 -1
  271. package/src/resources/extensions/gsd/activity-log.ts +1 -0
  272. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  273. package/src/resources/extensions/gsd/auto/phases.ts +46 -48
  274. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  275. package/src/resources/extensions/gsd/auto-start.ts +39 -2
  276. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  277. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  278. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  279. package/src/resources/extensions/gsd/auto.ts +37 -3
  280. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  281. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  282. package/src/resources/extensions/gsd/commands/catalog.ts +7 -1
  283. package/src/resources/extensions/gsd/commands/handlers/core.ts +2 -0
  284. package/src/resources/extensions/gsd/commands/handlers/ops.ts +10 -0
  285. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  286. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  287. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  288. package/src/resources/extensions/gsd/doctor.ts +7 -1
  289. package/src/resources/extensions/gsd/git-service.ts +6 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  291. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  292. package/src/resources/extensions/gsd/preferences.ts +11 -1
  293. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  294. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  295. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  296. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  297. package/src/resources/extensions/gsd/prompts/rethink.md +78 -0
  298. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  299. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  300. package/src/resources/extensions/gsd/repo-identity.ts +46 -7
  301. package/src/resources/extensions/gsd/rethink.ts +154 -0
  302. package/src/resources/extensions/gsd/state.ts +41 -1
  303. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  304. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  305. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  306. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  307. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  309. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  310. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  311. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  312. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  313. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  314. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  315. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  316. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  317. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  318. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  319. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  320. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  321. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  322. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  323. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  324. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  325. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  326. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  327. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  328. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  329. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  330. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  331. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +121 -0
  332. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  333. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  334. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  335. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  336. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  337. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  338. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  339. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  340. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  341. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  342. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  343. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  344. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  345. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  346. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  347. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  348. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  349. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  350. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  351. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  352. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  353. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  354. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  355. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  356. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  357. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  358. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  359. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  360. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  361. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  362. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  363. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  364. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  365. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  366. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  367. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  368. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  369. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  370. package/src/resources/extensions/gsd/tests/inherited-repo-home-dir.test.ts +121 -0
  371. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  372. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  373. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  374. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  375. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  376. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  377. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  378. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  379. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  380. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  381. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  382. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  383. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  384. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  385. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  386. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  387. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  388. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  389. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  390. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  391. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  392. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  393. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  394. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  395. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  396. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  397. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  398. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  399. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  400. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  401. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  402. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  403. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  404. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  405. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  406. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  407. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  408. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  409. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  410. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +176 -0
  411. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  412. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  413. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  414. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  415. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  416. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  417. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  418. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  419. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  420. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  421. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  422. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  423. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  424. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +108 -0
  425. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  426. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  427. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  428. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  429. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  430. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  431. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  432. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  433. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  434. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  435. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  436. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  437. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  438. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  439. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  440. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  441. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  442. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  443. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  444. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  445. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +65 -0
  446. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  447. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  448. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  449. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  450. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  451. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  452. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  453. package/src/resources/extensions/gsd/worktree-manager.ts +43 -2
  454. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  455. package/src/resources/extensions/mcp-client/index.ts +20 -0
  456. package/dist/web/standalone/.next/static/chunks/4024.0de81b543b28b9fe.js +0 -9
  457. package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +0 -1
  458. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/webpack-9014b5adb127a98a.js +0 -1
  461. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +0 -1
  462. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{mgkxN0mGP6gSUmGPEzbk_ → rzO54ZboyINyEt7cVM_uS}/_ssgManifest.js +0 -0
@@ -1,3 +1,5 @@
1
+ import { describe, test } from 'node:test';
2
+ import assert from 'node:assert/strict';
1
3
  import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
4
  import { join } from 'node:path';
3
5
  import { tmpdir } from 'node:os';
@@ -9,13 +11,10 @@ import {
9
11
  insertArtifact,
10
12
  isDbAvailable,
11
13
  insertMilestone,
14
+ getAllMilestones,
12
15
  insertSlice,
13
16
  insertTask,
14
17
  } from '../gsd-db.ts';
15
- import { createTestContext } from './test-helpers.ts';
16
-
17
- const { assertEq, assertTrue, report } = createTestContext();
18
-
19
18
  // ─── Fixture Helpers ───────────────────────────────────────────────────────
20
19
 
21
20
  function createFixtureBase(): string {
@@ -100,11 +99,10 @@ const REQUIREMENTS_CONTENT = `# Requirements
100
99
  - Description: Already validated.
101
100
  `;
102
101
 
103
- async function main(): Promise<void> {
102
+ describe('derive-state-db', async () => {
104
103
 
105
104
  // ─── Test 1: DB-backed deriveState produces identical GSDState ─────────
106
- console.log('\n=== derive-state-db: DB path matches file path ===');
107
- {
105
+ test('derive-state-db: DB path matches file path', async () => {
108
106
  const base = createFixtureBase();
109
107
  try {
110
108
  // Write files to disk (for file-only path)
@@ -120,7 +118,7 @@ async function main(): Promise<void> {
120
118
 
121
119
  // Now open DB, insert matching artifacts
122
120
  openDatabase(':memory:');
123
- assertTrue(isDbAvailable(), 'db-match: DB is available after open');
121
+ assert.ok(isDbAvailable(), 'db-match: DB is available after open');
124
122
 
125
123
  insertArtifactRow('milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT, {
126
124
  artifact_type: 'roadmap',
@@ -140,36 +138,35 @@ async function main(): Promise<void> {
140
138
  const dbState = await deriveState(base);
141
139
 
142
140
  // Field-by-field equality
143
- assertEq(dbState.phase, fileState.phase, 'db-match: phase matches');
144
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'db-match: activeMilestone.id matches');
145
- assertEq(dbState.activeMilestone?.title, fileState.activeMilestone?.title, 'db-match: activeMilestone.title matches');
146
- assertEq(dbState.activeSlice?.id, fileState.activeSlice?.id, 'db-match: activeSlice.id matches');
147
- assertEq(dbState.activeSlice?.title, fileState.activeSlice?.title, 'db-match: activeSlice.title matches');
148
- assertEq(dbState.activeTask?.id, fileState.activeTask?.id, 'db-match: activeTask.id matches');
149
- assertEq(dbState.activeTask?.title, fileState.activeTask?.title, 'db-match: activeTask.title matches');
150
- assertEq(dbState.blockers, fileState.blockers, 'db-match: blockers match');
151
- assertEq(dbState.registry.length, fileState.registry.length, 'db-match: registry length matches');
152
- assertEq(dbState.registry[0]?.status, fileState.registry[0]?.status, 'db-match: registry[0] status matches');
153
- assertEq(dbState.requirements?.active, fileState.requirements?.active, 'db-match: requirements.active matches');
154
- assertEq(dbState.requirements?.validated, fileState.requirements?.validated, 'db-match: requirements.validated matches');
155
- assertEq(dbState.requirements?.total, fileState.requirements?.total, 'db-match: requirements.total matches');
156
- assertEq(dbState.progress?.milestones?.done, fileState.progress?.milestones?.done, 'db-match: milestones.done matches');
157
- assertEq(dbState.progress?.milestones?.total, fileState.progress?.milestones?.total, 'db-match: milestones.total matches');
158
- assertEq(dbState.progress?.slices?.done, fileState.progress?.slices?.done, 'db-match: slices.done matches');
159
- assertEq(dbState.progress?.slices?.total, fileState.progress?.slices?.total, 'db-match: slices.total matches');
160
- assertEq(dbState.progress?.tasks?.done, fileState.progress?.tasks?.done, 'db-match: tasks.done matches');
161
- assertEq(dbState.progress?.tasks?.total, fileState.progress?.tasks?.total, 'db-match: tasks.total matches');
141
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'db-match: phase matches');
142
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'db-match: activeMilestone.id matches');
143
+ assert.deepStrictEqual(dbState.activeMilestone?.title, fileState.activeMilestone?.title, 'db-match: activeMilestone.title matches');
144
+ assert.deepStrictEqual(dbState.activeSlice?.id, fileState.activeSlice?.id, 'db-match: activeSlice.id matches');
145
+ assert.deepStrictEqual(dbState.activeSlice?.title, fileState.activeSlice?.title, 'db-match: activeSlice.title matches');
146
+ assert.deepStrictEqual(dbState.activeTask?.id, fileState.activeTask?.id, 'db-match: activeTask.id matches');
147
+ assert.deepStrictEqual(dbState.activeTask?.title, fileState.activeTask?.title, 'db-match: activeTask.title matches');
148
+ assert.deepStrictEqual(dbState.blockers, fileState.blockers, 'db-match: blockers match');
149
+ assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'db-match: registry length matches');
150
+ assert.deepStrictEqual(dbState.registry[0]?.status, fileState.registry[0]?.status, 'db-match: registry[0] status matches');
151
+ assert.deepStrictEqual(dbState.requirements?.active, fileState.requirements?.active, 'db-match: requirements.active matches');
152
+ assert.deepStrictEqual(dbState.requirements?.validated, fileState.requirements?.validated, 'db-match: requirements.validated matches');
153
+ assert.deepStrictEqual(dbState.requirements?.total, fileState.requirements?.total, 'db-match: requirements.total matches');
154
+ assert.deepStrictEqual(dbState.progress?.milestones?.done, fileState.progress?.milestones?.done, 'db-match: milestones.done matches');
155
+ assert.deepStrictEqual(dbState.progress?.milestones?.total, fileState.progress?.milestones?.total, 'db-match: milestones.total matches');
156
+ assert.deepStrictEqual(dbState.progress?.slices?.done, fileState.progress?.slices?.done, 'db-match: slices.done matches');
157
+ assert.deepStrictEqual(dbState.progress?.slices?.total, fileState.progress?.slices?.total, 'db-match: slices.total matches');
158
+ assert.deepStrictEqual(dbState.progress?.tasks?.done, fileState.progress?.tasks?.done, 'db-match: tasks.done matches');
159
+ assert.deepStrictEqual(dbState.progress?.tasks?.total, fileState.progress?.tasks?.total, 'db-match: tasks.total matches');
162
160
 
163
161
  closeDatabase();
164
162
  } finally {
165
163
  closeDatabase();
166
164
  cleanup(base);
167
165
  }
168
- }
166
+ });
169
167
 
170
168
  // ─── Test 2: Fallback when DB unavailable ─────────────────────────────
171
- console.log('\n=== derive-state-db: fallback when DB unavailable ===');
172
- {
169
+ test('derive-state-db: fallback when DB unavailable', async () => {
173
170
  const base = createFixtureBase();
174
171
  try {
175
172
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -178,22 +175,21 @@ async function main(): Promise<void> {
178
175
  writeFile(base, 'milestones/M001/slices/S01/tasks/T01-PLAN.md', '# T01 Plan');
179
176
 
180
177
  // No DB open — isDbAvailable() is false
181
- assertTrue(!isDbAvailable(), 'fallback: DB is not available');
178
+ assert.ok(!isDbAvailable(), 'fallback: DB is not available');
182
179
  invalidateStateCache();
183
180
  const state = await deriveState(base);
184
181
 
185
- assertEq(state.phase, 'executing', 'fallback: phase is executing');
186
- assertEq(state.activeMilestone?.id, 'M001', 'fallback: activeMilestone is M001');
187
- assertEq(state.activeSlice?.id, 'S01', 'fallback: activeSlice is S01');
188
- assertEq(state.activeTask?.id, 'T01', 'fallback: activeTask is T01');
182
+ assert.deepStrictEqual(state.phase, 'executing', 'fallback: phase is executing');
183
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'fallback: activeMilestone is M001');
184
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'fallback: activeSlice is S01');
185
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'fallback: activeTask is T01');
189
186
  } finally {
190
187
  cleanup(base);
191
188
  }
192
- }
189
+ });
193
190
 
194
191
  // ─── Test 3: Empty DB falls back to file reads ────────────────────────
195
- console.log('\n=== derive-state-db: empty DB falls back to files ===');
196
- {
192
+ test('derive-state-db: empty DB falls back to files', async () => {
197
193
  const base = createFixtureBase();
198
194
  try {
199
195
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -203,27 +199,26 @@ async function main(): Promise<void> {
203
199
 
204
200
  // Open DB but insert nothing — empty artifacts table
205
201
  openDatabase(':memory:');
206
- assertTrue(isDbAvailable(), 'empty-db: DB is available');
202
+ assert.ok(isDbAvailable(), 'empty-db: DB is available');
207
203
 
208
204
  invalidateStateCache();
209
205
  const state = await deriveState(base);
210
206
 
211
207
  // Should still work via cachedLoadFile → loadFile disk fallback
212
- assertEq(state.phase, 'executing', 'empty-db: phase is executing');
213
- assertEq(state.activeMilestone?.id, 'M001', 'empty-db: activeMilestone is M001');
214
- assertEq(state.activeSlice?.id, 'S01', 'empty-db: activeSlice is S01');
215
- assertEq(state.activeTask?.id, 'T01', 'empty-db: activeTask is T01');
208
+ assert.deepStrictEqual(state.phase, 'executing', 'empty-db: phase is executing');
209
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'empty-db: activeMilestone is M001');
210
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'empty-db: activeSlice is S01');
211
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'empty-db: activeTask is T01');
216
212
 
217
213
  closeDatabase();
218
214
  } finally {
219
215
  closeDatabase();
220
216
  cleanup(base);
221
217
  }
222
- }
218
+ });
223
219
 
224
220
  // ─── Test 4: Partial DB content fills gaps from disk ──────────────────
225
- console.log('\n=== derive-state-db: partial DB fills gaps from disk ===');
226
- {
221
+ test('derive-state-db: partial DB fills gaps from disk', async () => {
227
222
  const base = createFixtureBase();
228
223
  try {
229
224
  // Write all files to disk
@@ -244,25 +239,24 @@ async function main(): Promise<void> {
244
239
  const state = await deriveState(base);
245
240
 
246
241
  // Should work: roadmap from DB, plan from disk fallback
247
- assertEq(state.phase, 'executing', 'partial-db: phase is executing');
248
- assertEq(state.activeMilestone?.id, 'M001', 'partial-db: activeMilestone is M001');
249
- assertEq(state.activeSlice?.id, 'S01', 'partial-db: activeSlice is S01');
250
- assertEq(state.activeTask?.id, 'T01', 'partial-db: activeTask is T01');
242
+ assert.deepStrictEqual(state.phase, 'executing', 'partial-db: phase is executing');
243
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'partial-db: activeMilestone is M001');
244
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'partial-db: activeSlice is S01');
245
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'partial-db: activeTask is T01');
251
246
  // Requirements loaded from disk fallback
252
- assertEq(state.requirements?.active, 2, 'partial-db: requirements.active from disk');
253
- assertEq(state.requirements?.validated, 1, 'partial-db: requirements.validated from disk');
254
- assertEq(state.requirements?.total, 3, 'partial-db: requirements.total from disk');
247
+ assert.deepStrictEqual(state.requirements?.active, 2, 'partial-db: requirements.active from disk');
248
+ assert.deepStrictEqual(state.requirements?.validated, 1, 'partial-db: requirements.validated from disk');
249
+ assert.deepStrictEqual(state.requirements?.total, 3, 'partial-db: requirements.total from disk');
255
250
 
256
251
  closeDatabase();
257
252
  } finally {
258
253
  closeDatabase();
259
254
  cleanup(base);
260
255
  }
261
- }
256
+ });
262
257
 
263
258
  // ─── Test 5: Requirements counting from disk (DB no longer used for content) ─
264
- console.log('\n=== derive-state-db: requirements from disk content ===');
265
- {
259
+ test('derive-state-db: requirements from disk content', async () => {
266
260
  const base = createFixtureBase();
267
261
  try {
268
262
  // Write minimal milestone dir (needed for milestone discovery)
@@ -274,17 +268,16 @@ async function main(): Promise<void> {
274
268
  const state = await deriveState(base);
275
269
 
276
270
  // Requirements should come from disk
277
- assertEq(state.requirements?.active, 2, 'req-from-disk: requirements.active = 2');
278
- assertEq(state.requirements?.validated, 1, 'req-from-disk: requirements.validated = 1');
279
- assertEq(state.requirements?.total, 3, 'req-from-disk: requirements.total = 3');
271
+ assert.deepStrictEqual(state.requirements?.active, 2, 'req-from-disk: requirements.active = 2');
272
+ assert.deepStrictEqual(state.requirements?.validated, 1, 'req-from-disk: requirements.validated = 1');
273
+ assert.deepStrictEqual(state.requirements?.total, 3, 'req-from-disk: requirements.total = 3');
280
274
  } finally {
281
275
  cleanup(base);
282
276
  }
283
- }
277
+ });
284
278
 
285
279
  // ─── Test 6: DB content with multi-milestone registry ─────────────────
286
- console.log('\n=== derive-state-db: multi-milestone from DB ===');
287
- {
280
+ test('derive-state-db: multi-milestone from DB', async () => {
288
281
  const base = createFixtureBase();
289
282
 
290
283
  const completedRoadmap = `# M001: First Milestone
@@ -337,24 +330,23 @@ async function main(): Promise<void> {
337
330
  invalidateStateCache();
338
331
  const state = await deriveState(base);
339
332
 
340
- assertEq(state.registry.length, 2, 'multi-ms-db: registry has 2 entries');
341
- assertEq(state.registry[0]?.id, 'M001', 'multi-ms-db: registry[0] is M001');
342
- assertEq(state.registry[0]?.status, 'complete', 'multi-ms-db: M001 is complete');
343
- assertEq(state.registry[1]?.id, 'M002', 'multi-ms-db: registry[1] is M002');
344
- assertEq(state.registry[1]?.status, 'active', 'multi-ms-db: M002 is active');
345
- assertEq(state.activeMilestone?.id, 'M002', 'multi-ms-db: activeMilestone is M002');
346
- assertEq(state.phase, 'planning', 'multi-ms-db: phase is planning (no plan for S01)');
333
+ assert.deepStrictEqual(state.registry.length, 2, 'multi-ms-db: registry has 2 entries');
334
+ assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'multi-ms-db: registry[0] is M001');
335
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'multi-ms-db: M001 is complete');
336
+ assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'multi-ms-db: registry[1] is M002');
337
+ assert.deepStrictEqual(state.registry[1]?.status, 'active', 'multi-ms-db: M002 is active');
338
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'multi-ms-db: activeMilestone is M002');
339
+ assert.deepStrictEqual(state.phase, 'planning', 'multi-ms-db: phase is planning (no plan for S01)');
347
340
 
348
341
  closeDatabase();
349
342
  } finally {
350
343
  closeDatabase();
351
344
  cleanup(base);
352
345
  }
353
- }
346
+ });
354
347
 
355
348
  // ─── Test 7: Cache invalidation works for DB path ─────────────────────
356
- console.log('\n=== derive-state-db: cache invalidation ===');
357
- {
349
+ test('derive-state-db: cache invalidation', async () => {
358
350
  const base = createFixtureBase();
359
351
  try {
360
352
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -375,7 +367,7 @@ async function main(): Promise<void> {
375
367
 
376
368
  invalidateStateCache();
377
369
  const state1 = await deriveState(base);
378
- assertEq(state1.activeTask?.id, 'T01', 'cache-inv: first call gets T01');
370
+ assert.deepStrictEqual(state1.activeTask?.id, 'T01', 'cache-inv: first call gets T01');
379
371
 
380
372
  // Simulate task completion by updating the plan in DB
381
373
  const updatedPlan = PLAN_CONTENT.replace('- [ ] **T01:', '- [x] **T01:');
@@ -389,28 +381,27 @@ async function main(): Promise<void> {
389
381
 
390
382
  // Without invalidation, should return cached result (T01 still active)
391
383
  const state2 = await deriveState(base);
392
- assertEq(state2.activeTask?.id, 'T01', 'cache-inv: cached result still has T01');
384
+ assert.deepStrictEqual(state2.activeTask?.id, 'T01', 'cache-inv: cached result still has T01');
393
385
 
394
386
  // After invalidation, should pick up updated content
395
387
  invalidateStateCache();
396
388
  const state3 = await deriveState(base);
397
- assertEq(state3.phase, 'summarizing', 'cache-inv: after invalidation, phase is summarizing (all tasks done)');
398
- assertEq(state3.activeTask, null, 'cache-inv: activeTask is null after all done');
389
+ assert.deepStrictEqual(state3.phase, 'summarizing', 'cache-inv: after invalidation, phase is summarizing (all tasks done)');
390
+ assert.deepStrictEqual(state3.activeTask, null, 'cache-inv: activeTask is null after all done');
399
391
 
400
392
  closeDatabase();
401
393
  } finally {
402
394
  closeDatabase();
403
395
  cleanup(base);
404
396
  }
405
- }
397
+ });
406
398
 
407
399
  // ═════════════════════════════════════════════════════════════════════════
408
400
  // New: deriveStateFromDb() cross-validation tests
409
401
  // ═════════════════════════════════════════════════════════════════════════
410
402
 
411
403
  // ─── Test 8: Pre-planning — milestone exists, no roadmap, no slices ───
412
- console.log('\n=== derive-state-db: pre-planning via DB ===');
413
- {
404
+ test('derive-state-db: pre-planning via DB', async () => {
414
405
  const base = createFixtureBase();
415
406
  try {
416
407
  // Create milestone dir on disk with a CONTEXT file (not a ghost)
@@ -427,23 +418,22 @@ async function main(): Promise<void> {
427
418
  invalidateStateCache();
428
419
  const dbState = await deriveStateFromDb(base);
429
420
 
430
- assertEq(dbState.phase, fileState.phase, 'pre-plan-db: phase matches');
431
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'pre-plan-db: activeMilestone.id matches');
432
- assertEq(dbState.activeSlice, fileState.activeSlice, 'pre-plan-db: activeSlice matches');
433
- assertEq(dbState.activeTask, fileState.activeTask, 'pre-plan-db: activeTask matches');
434
- assertEq(dbState.registry.length, fileState.registry.length, 'pre-plan-db: registry length matches');
435
- assertEq(dbState.registry[0]?.status, fileState.registry[0]?.status, 'pre-plan-db: registry[0] status matches');
421
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'pre-plan-db: phase matches');
422
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'pre-plan-db: activeMilestone.id matches');
423
+ assert.deepStrictEqual(dbState.activeSlice, fileState.activeSlice, 'pre-plan-db: activeSlice matches');
424
+ assert.deepStrictEqual(dbState.activeTask, fileState.activeTask, 'pre-plan-db: activeTask matches');
425
+ assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'pre-plan-db: registry length matches');
426
+ assert.deepStrictEqual(dbState.registry[0]?.status, fileState.registry[0]?.status, 'pre-plan-db: registry[0] status matches');
436
427
 
437
428
  closeDatabase();
438
429
  } finally {
439
430
  closeDatabase();
440
431
  cleanup(base);
441
432
  }
442
- }
433
+ });
443
434
 
444
435
  // ─── Test 9: Executing — active task with partial completion ──────────
445
- console.log('\n=== derive-state-db: executing via DB ===');
446
- {
436
+ test('derive-state-db: executing via DB', async () => {
447
437
  const base = createFixtureBase();
448
438
  try {
449
439
  // Build filesystem fixture
@@ -466,24 +456,23 @@ async function main(): Promise<void> {
466
456
  invalidateStateCache();
467
457
  const dbState = await deriveStateFromDb(base);
468
458
 
469
- assertEq(dbState.phase, 'executing', 'exec-db: phase is executing');
470
- assertEq(dbState.activeMilestone?.id, 'M001', 'exec-db: activeMilestone is M001');
471
- assertEq(dbState.activeSlice?.id, 'S01', 'exec-db: activeSlice is S01');
472
- assertEq(dbState.activeTask?.id, 'T01', 'exec-db: activeTask is T01');
473
- assertEq(dbState.progress?.tasks?.done, 1, 'exec-db: tasks.done = 1');
474
- assertEq(dbState.progress?.tasks?.total, 2, 'exec-db: tasks.total = 2');
475
- assertEq(dbState.phase, fileState.phase, 'exec-db: phase matches filesystem');
459
+ assert.deepStrictEqual(dbState.phase, 'executing', 'exec-db: phase is executing');
460
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M001', 'exec-db: activeMilestone is M001');
461
+ assert.deepStrictEqual(dbState.activeSlice?.id, 'S01', 'exec-db: activeSlice is S01');
462
+ assert.deepStrictEqual(dbState.activeTask?.id, 'T01', 'exec-db: activeTask is T01');
463
+ assert.deepStrictEqual(dbState.progress?.tasks?.done, 1, 'exec-db: tasks.done = 1');
464
+ assert.deepStrictEqual(dbState.progress?.tasks?.total, 2, 'exec-db: tasks.total = 2');
465
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'exec-db: phase matches filesystem');
476
466
 
477
467
  closeDatabase();
478
468
  } finally {
479
469
  closeDatabase();
480
470
  cleanup(base);
481
471
  }
482
- }
472
+ });
483
473
 
484
474
  // ─── Test 10: Summarizing — all tasks complete, no slice summary ──────
485
- console.log('\n=== derive-state-db: summarizing via DB ===');
486
- {
475
+ test('derive-state-db: summarizing via DB', async () => {
487
476
  const base = createFixtureBase();
488
477
  try {
489
478
  const allDonePlan = `# S01: First Slice
@@ -517,21 +506,20 @@ async function main(): Promise<void> {
517
506
  invalidateStateCache();
518
507
  const dbState = await deriveStateFromDb(base);
519
508
 
520
- assertEq(dbState.phase, 'summarizing', 'summarize-db: phase is summarizing');
521
- assertEq(dbState.phase, fileState.phase, 'summarize-db: phase matches filesystem');
522
- assertEq(dbState.activeSlice?.id, 'S01', 'summarize-db: activeSlice is S01');
523
- assertEq(dbState.activeTask, null, 'summarize-db: activeTask is null');
509
+ assert.deepStrictEqual(dbState.phase, 'summarizing', 'summarize-db: phase is summarizing');
510
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'summarize-db: phase matches filesystem');
511
+ assert.deepStrictEqual(dbState.activeSlice?.id, 'S01', 'summarize-db: activeSlice is S01');
512
+ assert.deepStrictEqual(dbState.activeTask, null, 'summarize-db: activeTask is null');
524
513
 
525
514
  closeDatabase();
526
515
  } finally {
527
516
  closeDatabase();
528
517
  cleanup(base);
529
518
  }
530
- }
519
+ });
531
520
 
532
521
  // ─── Test 11: Complete — all milestones complete ──────────────────────
533
- console.log('\n=== derive-state-db: all complete via DB ===');
534
- {
522
+ test('derive-state-db: all complete via DB', async () => {
535
523
  const base = createFixtureBase();
536
524
  try {
537
525
  const completedRoadmap = `# M001: Done Milestone
@@ -557,21 +545,20 @@ async function main(): Promise<void> {
557
545
  invalidateStateCache();
558
546
  const dbState = await deriveStateFromDb(base);
559
547
 
560
- assertEq(dbState.phase, 'complete', 'complete-db: phase is complete');
561
- assertEq(dbState.phase, fileState.phase, 'complete-db: phase matches filesystem');
562
- assertEq(dbState.registry.length, 1, 'complete-db: registry has 1 entry');
563
- assertEq(dbState.registry[0]?.status, 'complete', 'complete-db: M001 is complete');
548
+ assert.deepStrictEqual(dbState.phase, 'complete', 'complete-db: phase is complete');
549
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'complete-db: phase matches filesystem');
550
+ assert.deepStrictEqual(dbState.registry.length, 1, 'complete-db: registry has 1 entry');
551
+ assert.deepStrictEqual(dbState.registry[0]?.status, 'complete', 'complete-db: M001 is complete');
564
552
 
565
553
  closeDatabase();
566
554
  } finally {
567
555
  closeDatabase();
568
556
  cleanup(base);
569
557
  }
570
- }
558
+ });
571
559
 
572
560
  // ─── Test 12: Blocked — slice deps unmet ──────────────────────────────
573
- console.log('\n=== derive-state-db: blocked slice via DB ===');
574
- {
561
+ test('derive-state-db: blocked slice via DB', async () => {
575
562
  const base = createFixtureBase();
576
563
  try {
577
564
  // Roadmap with S02 depending on S01, but S01 not done
@@ -601,20 +588,19 @@ async function main(): Promise<void> {
601
588
  invalidateStateCache();
602
589
  const dbState = await deriveStateFromDb(base);
603
590
 
604
- assertEq(dbState.phase, 'blocked', 'blocked-db: phase is blocked');
605
- assertEq(dbState.phase, fileState.phase, 'blocked-db: phase matches filesystem');
606
- assertTrue(dbState.blockers.length > 0, 'blocked-db: has blockers');
591
+ assert.deepStrictEqual(dbState.phase, 'blocked', 'blocked-db: phase is blocked');
592
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'blocked-db: phase matches filesystem');
593
+ assert.ok(dbState.blockers.length > 0, 'blocked-db: has blockers');
607
594
 
608
595
  closeDatabase();
609
596
  } finally {
610
597
  closeDatabase();
611
598
  cleanup(base);
612
599
  }
613
- }
600
+ });
614
601
 
615
602
  // ─── Test 13: Parked milestone ────────────────────────────────────────
616
- console.log('\n=== derive-state-db: parked milestone via DB ===');
617
- {
603
+ test('derive-state-db: parked milestone via DB', async () => {
618
604
  const base = createFixtureBase();
619
605
  try {
620
606
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -631,20 +617,19 @@ async function main(): Promise<void> {
631
617
  invalidateStateCache();
632
618
  const dbState = await deriveStateFromDb(base);
633
619
 
634
- assertEq(dbState.phase, fileState.phase, 'parked-db: phase matches filesystem');
635
- assertEq(dbState.activeMilestone?.id, 'M002', 'parked-db: activeMilestone is M002');
636
- assertTrue(dbState.registry.some(e => e.id === 'M001' && e.status === 'parked'), 'parked-db: M001 is parked in registry');
620
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'parked-db: phase matches filesystem');
621
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'parked-db: activeMilestone is M002');
622
+ assert.ok(dbState.registry.some(e => e.id === 'M001' && e.status === 'parked'), 'parked-db: M001 is parked in registry');
637
623
 
638
624
  closeDatabase();
639
625
  } finally {
640
626
  closeDatabase();
641
627
  cleanup(base);
642
628
  }
643
- }
629
+ });
644
630
 
645
631
  // ─── Test 14: Validating-milestone — all slices done, no terminal validation ─
646
- console.log('\n=== derive-state-db: validating-milestone via DB ===');
647
- {
632
+ test('derive-state-db: validating-milestone via DB', async () => {
648
633
  const base = createFixtureBase();
649
634
  try {
650
635
  const doneRoadmap = `# M001: Validate Test
@@ -669,20 +654,19 @@ async function main(): Promise<void> {
669
654
  invalidateStateCache();
670
655
  const dbState = await deriveStateFromDb(base);
671
656
 
672
- assertEq(dbState.phase, 'validating-milestone', 'validate-db: phase is validating-milestone');
673
- assertEq(dbState.phase, fileState.phase, 'validate-db: phase matches filesystem');
674
- assertEq(dbState.activeMilestone?.id, 'M001', 'validate-db: activeMilestone is M001');
657
+ assert.deepStrictEqual(dbState.phase, 'validating-milestone', 'validate-db: phase is validating-milestone');
658
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'validate-db: phase matches filesystem');
659
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M001', 'validate-db: activeMilestone is M001');
675
660
 
676
661
  closeDatabase();
677
662
  } finally {
678
663
  closeDatabase();
679
664
  cleanup(base);
680
665
  }
681
- }
666
+ });
682
667
 
683
668
  // ─── Test 15: Completing-milestone — terminal validation, no summary ──
684
- console.log('\n=== derive-state-db: completing-milestone via DB ===');
685
- {
669
+ test('derive-state-db: completing-milestone via DB', async () => {
686
670
  const base = createFixtureBase();
687
671
  try {
688
672
  const doneRoadmap = `# M001: Complete Test
@@ -707,19 +691,18 @@ async function main(): Promise<void> {
707
691
  invalidateStateCache();
708
692
  const dbState = await deriveStateFromDb(base);
709
693
 
710
- assertEq(dbState.phase, 'completing-milestone', 'completing-db: phase is completing-milestone');
711
- assertEq(dbState.phase, fileState.phase, 'completing-db: phase matches filesystem');
694
+ assert.deepStrictEqual(dbState.phase, 'completing-milestone', 'completing-db: phase is completing-milestone');
695
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'completing-db: phase matches filesystem');
712
696
 
713
697
  closeDatabase();
714
698
  } finally {
715
699
  closeDatabase();
716
700
  cleanup(base);
717
701
  }
718
- }
702
+ });
719
703
 
720
704
  // ─── Test 16: Replanning-slice — REPLAN-TRIGGER file exists ───────────
721
- console.log('\n=== derive-state-db: replanning-slice via DB ===');
722
- {
705
+ test('derive-state-db: replanning-slice via DB', async () => {
723
706
  const base = createFixtureBase();
724
707
  try {
725
708
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -749,19 +732,18 @@ async function main(): Promise<void> {
749
732
  invalidateStateCache();
750
733
  const dbState = await deriveStateFromDb(base);
751
734
 
752
- assertEq(dbState.phase, 'replanning-slice', 'replan-db: phase is replanning-slice');
753
- assertEq(dbState.phase, fileState.phase, 'replan-db: phase matches filesystem');
735
+ assert.deepStrictEqual(dbState.phase, 'replanning-slice', 'replan-db: phase is replanning-slice');
736
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'replan-db: phase matches filesystem');
754
737
 
755
738
  closeDatabase();
756
739
  } finally {
757
740
  closeDatabase();
758
741
  cleanup(base);
759
742
  }
760
- }
743
+ });
761
744
 
762
745
  // ─── Test 17: Performance — deriveStateFromDb < 1ms on populated DB ───
763
- console.log('\n=== derive-state-db: performance assertion ===');
764
- {
746
+ test('derive-state-db: performance assertion', async () => {
765
747
  const base = createFixtureBase();
766
748
  try {
767
749
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -789,18 +771,17 @@ async function main(): Promise<void> {
789
771
  console.log(` deriveStateFromDb() took ${elapsed.toFixed(3)}ms`);
790
772
  // Use 10ms threshold — catches real regressions without flaking on
791
773
  // CI runners under load (1ms threshold failed at 1.050ms on GitHub Actions)
792
- assertTrue(elapsed < 10, `perf-db: deriveStateFromDb() <10ms (got ${elapsed.toFixed(3)}ms)`);
774
+ assert.ok(elapsed < 10, `perf-db: deriveStateFromDb() <10ms (got ${elapsed.toFixed(3)}ms)`);
793
775
 
794
776
  closeDatabase();
795
777
  } finally {
796
778
  closeDatabase();
797
779
  cleanup(base);
798
780
  }
799
- }
781
+ });
800
782
 
801
783
  // ─── Test 18: Multi-milestone with deps — M001 complete, M002 depends on M001, M003 depends on M002 ─
802
- console.log('\n=== derive-state-db: multi-milestone deps via DB ===');
803
- {
784
+ test('derive-state-db: multi-milestone deps via DB', async () => {
804
785
  const base = createFixtureBase();
805
786
  try {
806
787
  const m1Roadmap = `# M001: First
@@ -841,29 +822,28 @@ async function main(): Promise<void> {
841
822
  invalidateStateCache();
842
823
  const dbState = await deriveStateFromDb(base);
843
824
 
844
- assertEq(dbState.registry.length, fileState.registry.length, 'multi-deps-db: registry length matches');
845
- assertEq(dbState.activeMilestone?.id, 'M002', 'multi-deps-db: activeMilestone is M002 (M001 complete, M003 dep unmet)');
846
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'multi-deps-db: activeMilestone matches filesystem');
847
- assertEq(dbState.phase, fileState.phase, 'multi-deps-db: phase matches filesystem');
825
+ assert.deepStrictEqual(dbState.registry.length, fileState.registry.length, 'multi-deps-db: registry length matches');
826
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'multi-deps-db: activeMilestone is M002 (M001 complete, M003 dep unmet)');
827
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'multi-deps-db: activeMilestone matches filesystem');
828
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'multi-deps-db: phase matches filesystem');
848
829
 
849
830
  // Check registry statuses
850
831
  const m1reg = dbState.registry.find(e => e.id === 'M001');
851
832
  const m2reg = dbState.registry.find(e => e.id === 'M002');
852
833
  const m3reg = dbState.registry.find(e => e.id === 'M003');
853
- assertEq(m1reg?.status, 'complete', 'multi-deps-db: M001 is complete');
854
- assertEq(m2reg?.status, 'active', 'multi-deps-db: M002 is active');
855
- assertEq(m3reg?.status, 'pending', 'multi-deps-db: M003 is pending (dep M002 unmet)');
834
+ assert.deepStrictEqual(m1reg?.status, 'complete', 'multi-deps-db: M001 is complete');
835
+ assert.deepStrictEqual(m2reg?.status, 'active', 'multi-deps-db: M002 is active');
836
+ assert.deepStrictEqual(m3reg?.status, 'pending', 'multi-deps-db: M003 is pending (dep M002 unmet)');
856
837
 
857
838
  closeDatabase();
858
839
  } finally {
859
840
  closeDatabase();
860
841
  cleanup(base);
861
842
  }
862
- }
843
+ });
863
844
 
864
845
  // ─── Test 19: K002 — both 'complete' and 'done' treated as done ───────
865
- console.log('\n=== derive-state-db: K002 status handling ===');
866
- {
846
+ test('derive-state-db: K002 status handling', async () => {
867
847
  const base = createFixtureBase();
868
848
  try {
869
849
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -882,20 +862,19 @@ async function main(): Promise<void> {
882
862
  invalidateStateCache();
883
863
  const dbState = await deriveStateFromDb(base);
884
864
 
885
- assertEq(dbState.phase, 'executing', 'k002-db: phase is executing');
886
- assertEq(dbState.activeTask?.id, 'T01', 'k002-db: activeTask is T01 (T02 done)');
887
- assertEq(dbState.progress?.tasks?.done, 1, 'k002-db: tasks.done counts done status');
865
+ assert.deepStrictEqual(dbState.phase, 'executing', 'k002-db: phase is executing');
866
+ assert.deepStrictEqual(dbState.activeTask?.id, 'T01', 'k002-db: activeTask is T01 (T02 done)');
867
+ assert.deepStrictEqual(dbState.progress?.tasks?.done, 1, 'k002-db: tasks.done counts done status');
888
868
 
889
869
  closeDatabase();
890
870
  } finally {
891
871
  closeDatabase();
892
872
  cleanup(base);
893
873
  }
894
- }
874
+ });
895
875
 
896
876
  // ─── Test 20: Dual-path wiring — deriveState() uses DB when populated ─
897
- console.log('\n=== derive-state-db: dual-path wiring ===');
898
- {
877
+ test('derive-state-db: dual-path wiring', async () => {
899
878
  const base = createFixtureBase();
900
879
  try {
901
880
  writeFile(base, 'milestones/M001/M001-ROADMAP.md', ROADMAP_CONTENT);
@@ -914,21 +893,20 @@ async function main(): Promise<void> {
914
893
  invalidateStateCache();
915
894
  const state = await deriveState(base);
916
895
 
917
- assertEq(state.phase, 'executing', 'dual-path: phase is executing');
918
- assertEq(state.activeMilestone?.id, 'M001', 'dual-path: activeMilestone is M001');
919
- assertEq(state.activeSlice?.id, 'S01', 'dual-path: activeSlice is S01');
920
- assertEq(state.activeTask?.id, 'T01', 'dual-path: activeTask is T01');
896
+ assert.deepStrictEqual(state.phase, 'executing', 'dual-path: phase is executing');
897
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M001', 'dual-path: activeMilestone is M001');
898
+ assert.deepStrictEqual(state.activeSlice?.id, 'S01', 'dual-path: activeSlice is S01');
899
+ assert.deepStrictEqual(state.activeTask?.id, 'T01', 'dual-path: activeTask is T01');
921
900
 
922
901
  closeDatabase();
923
902
  } finally {
924
903
  closeDatabase();
925
904
  cleanup(base);
926
905
  }
927
- }
906
+ });
928
907
 
929
908
  // ─── Test 21: Ghost milestone skipped ─────────────────────────────────
930
- console.log('\n=== derive-state-db: ghost milestone skipped ===');
931
- {
909
+ test('derive-state-db: ghost milestone skipped', async () => {
932
910
  const base = createFixtureBase();
933
911
  try {
934
912
  // Ghost: milestone dir exists with only META.json, no context/roadmap/summary
@@ -949,21 +927,20 @@ async function main(): Promise<void> {
949
927
  const dbState = await deriveStateFromDb(base);
950
928
 
951
929
  // Ghost should be skipped — M002 should be active
952
- assertEq(dbState.activeMilestone?.id, 'M002', 'ghost-db: activeMilestone is M002 (ghost skipped)');
953
- assertEq(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'ghost-db: matches filesystem');
930
+ assert.deepStrictEqual(dbState.activeMilestone?.id, 'M002', 'ghost-db: activeMilestone is M002 (ghost skipped)');
931
+ assert.deepStrictEqual(dbState.activeMilestone?.id, fileState.activeMilestone?.id, 'ghost-db: matches filesystem');
954
932
  // Ghost should not appear in registry
955
- assertTrue(!dbState.registry.some(e => e.id === 'M001'), 'ghost-db: M001 not in registry');
933
+ assert.ok(!dbState.registry.some(e => e.id === 'M001'), 'ghost-db: M001 not in registry');
956
934
 
957
935
  closeDatabase();
958
936
  } finally {
959
937
  closeDatabase();
960
938
  cleanup(base);
961
939
  }
962
- }
940
+ });
963
941
 
964
942
  // ─── Test 22: Needs-discussion — CONTEXT-DRAFT exists ─────────────────
965
- console.log('\n=== derive-state-db: needs-discussion via DB ===');
966
- {
943
+ test('derive-state-db: needs-discussion via DB', async () => {
967
944
  const base = createFixtureBase();
968
945
  try {
969
946
  writeFile(base, 'milestones/M001/M001-CONTEXT-DRAFT.md', '# M001: Draft\n\nDraft content.');
@@ -977,20 +954,72 @@ async function main(): Promise<void> {
977
954
  invalidateStateCache();
978
955
  const dbState = await deriveStateFromDb(base);
979
956
 
980
- assertEq(dbState.phase, 'needs-discussion', 'discuss-db: phase is needs-discussion');
981
- assertEq(dbState.phase, fileState.phase, 'discuss-db: phase matches filesystem');
957
+ assert.deepStrictEqual(dbState.phase, 'needs-discussion', 'discuss-db: phase is needs-discussion');
958
+ assert.deepStrictEqual(dbState.phase, fileState.phase, 'discuss-db: phase matches filesystem');
982
959
 
983
960
  closeDatabase();
984
961
  } finally {
985
962
  closeDatabase();
986
963
  cleanup(base);
987
964
  }
988
- }
965
+ });
989
966
 
990
- report();
991
- }
967
+ // ─── Regression: disk-only milestones synced into DB (#2416) ─────────
968
+ test('derive-state-db: disk-only milestone auto-synced into DB (#2416)', async () => {
969
+ const base = createFixtureBase();
970
+ try {
971
+ // M001 is complete and exists in DB. M002 was queued on disk only — no DB row.
972
+ writeFile(base, 'milestones/M001/M001-SUMMARY.md', '# M001 Summary\n\nDone.');
973
+ writeFile(base, 'milestones/M002/M002-CONTEXT.md', '# M002: Queued\n\nQueued milestone.');
974
+
975
+ openDatabase(':memory:');
976
+ // Only insert M001 — simulates the state after migration guard ran then /gsd queue added M002
977
+ insertMilestone({ id: 'M001', title: 'First', status: 'complete' });
978
+
979
+ invalidateStateCache();
980
+ const state = await deriveStateFromDb(base);
981
+
982
+ // Before the fix, M002 was invisible: getAllMilestones() returned only M001
983
+ // (complete) → phase='complete' → auto-mode stopped.
984
+ // After the fix, deriveStateFromDb reconciles disk dirs and inserts M002.
985
+ assert.deepStrictEqual(state.phase, 'pre-planning', 'disk-sync-2416: phase is pre-planning, not complete');
986
+ assert.deepStrictEqual(state.registry.length, 2, 'disk-sync-2416: both milestones visible in registry');
987
+ assert.deepStrictEqual(state.registry[0]?.id, 'M001', 'disk-sync-2416: registry[0] is M001');
988
+ assert.deepStrictEqual(state.registry[0]?.status, 'complete', 'disk-sync-2416: M001 is complete');
989
+ assert.deepStrictEqual(state.registry[1]?.id, 'M002', 'disk-sync-2416: registry[1] is M002');
990
+ assert.deepStrictEqual(state.registry[1]?.status, 'active', 'disk-sync-2416: M002 is active');
991
+ assert.deepStrictEqual(state.activeMilestone?.id, 'M002', 'disk-sync-2416: activeMilestone is M002');
992
+
993
+ closeDatabase();
994
+ } finally {
995
+ closeDatabase();
996
+ cleanup(base);
997
+ }
998
+ });
992
999
 
993
- main().catch((error) => {
994
- console.error(error);
995
- process.exit(1);
1000
+ // ─── Queued milestone row not clobbered by later plan (#2416 root cause) ──
1001
+ test('derive-state-db: queued milestone row survives gsd_plan_milestone INSERT OR IGNORE', async () => {
1002
+ try {
1003
+ openDatabase(':memory:');
1004
+
1005
+ // Simulates gsd_milestone_generate_id inserting a minimal queued row
1006
+ insertMilestone({ id: 'M001', status: 'queued' });
1007
+
1008
+ const before = getAllMilestones();
1009
+ assert.equal(before.length, 1, 'queued-row: one row after generate_id');
1010
+ assert.equal(before[0]!.status, 'queued', 'queued-row: status is queued');
1011
+
1012
+ // Simulates gsd_plan_milestone calling insertMilestone (INSERT OR IGNORE)
1013
+ insertMilestone({ id: 'M001', title: 'Planned Title', status: 'active' });
1014
+
1015
+ const after = getAllMilestones();
1016
+ assert.equal(after.length, 1, 'queued-row: still one row after plan');
1017
+ // INSERT OR IGNORE keeps the original row — status stays 'queued'
1018
+ assert.equal(after[0]!.status, 'queued', 'queued-row: INSERT OR IGNORE preserves original status');
1019
+
1020
+ closeDatabase();
1021
+ } finally {
1022
+ closeDatabase();
1023
+ }
1024
+ });
996
1025
  });