gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.73f2fd5

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 (326) hide show
  1. package/README.md +30 -12
  2. package/dist/resources/extensions/gsd/auto/infra-errors.js +3 -0
  3. package/dist/resources/extensions/gsd/auto/phases.js +36 -36
  4. package/dist/resources/extensions/gsd/auto-prompts.js +24 -1
  5. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  6. package/dist/resources/extensions/gsd/auto-timers.js +57 -3
  7. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -0
  8. package/dist/resources/extensions/gsd/auto-worktree.js +9 -6
  9. package/dist/resources/extensions/gsd/auto.js +30 -3
  10. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +156 -0
  11. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -12
  12. package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
  13. package/dist/resources/extensions/gsd/commands/handlers/core.js +1 -0
  14. package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
  15. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  16. package/dist/resources/extensions/gsd/commands-mcp-status.js +187 -0
  17. package/dist/resources/extensions/gsd/db-writer.js +34 -16
  18. package/dist/resources/extensions/gsd/doctor.js +8 -0
  19. package/dist/resources/extensions/gsd/git-service.js +8 -3
  20. package/dist/resources/extensions/gsd/gsd-db.js +12 -1
  21. package/dist/resources/extensions/gsd/markdown-renderer.js +1 -1
  22. package/dist/resources/extensions/gsd/preferences.js +9 -1
  23. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  24. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  25. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  26. package/dist/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  27. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  28. package/dist/resources/extensions/gsd/provider-error-pause.js +7 -0
  29. package/dist/resources/extensions/gsd/state.js +19 -2
  30. package/dist/resources/extensions/gsd/tools/plan-slice.js +1 -0
  31. package/dist/resources/extensions/gsd/tools/plan-task.js +1 -0
  32. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -0
  33. package/dist/resources/extensions/gsd/tools/validate-milestone.js +88 -0
  34. package/dist/resources/extensions/gsd/worktree-resolver.js +6 -0
  35. package/dist/resources/extensions/mcp-client/index.js +14 -0
  36. package/dist/web/standalone/.next/BUILD_ID +1 -1
  37. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  38. package/dist/web/standalone/.next/build-manifest.json +2 -2
  39. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  40. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  41. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  42. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  49. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/index.html +1 -1
  57. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  64. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  65. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  66. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  67. package/package.json +1 -1
  68. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +3 -1
  69. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  70. package/packages/pi-coding-agent/dist/core/auth-storage.js +15 -1
  71. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  72. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  73. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  74. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  75. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  76. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  77. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  78. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts +15 -0
  79. package/packages/pi-coding-agent/dist/core/local-model-check.d.ts.map +1 -0
  80. package/packages/pi-coding-agent/dist/core/local-model-check.js +41 -0
  81. package/packages/pi-coding-agent/dist/core/local-model-check.js.map +1 -0
  82. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +11 -0
  83. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  84. package/packages/pi-coding-agent/dist/core/model-registry.js +20 -1
  85. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  86. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  87. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  89. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  90. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  91. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  92. package/packages/pi-coding-agent/dist/core/settings-manager.js +6 -0
  93. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  94. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  95. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  96. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  97. package/packages/pi-coding-agent/dist/main.js +17 -0
  98. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  99. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts +2 -0
  100. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.d.ts.map +1 -0
  101. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js +32 -0
  102. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/timestamp.test.js.map +1 -0
  103. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  104. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  105. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +8 -1
  106. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  107. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  108. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  109. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +12 -0
  110. package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  111. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts +15 -0
  112. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.d.ts.map +1 -0
  113. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js +40 -0
  114. package/packages/pi-coding-agent/dist/modes/interactive/components/timestamp.js.map +1 -0
  115. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  116. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +4 -1
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +5 -2
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +13 -2
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  122. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +17 -8
  124. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -3
  127. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  128. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  129. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  130. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  131. package/packages/pi-coding-agent/src/core/auth-storage.ts +15 -1
  132. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  133. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  134. package/packages/pi-coding-agent/src/core/local-model-check.ts +45 -0
  135. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -1
  136. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  137. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  138. package/packages/pi-coding-agent/src/core/settings-manager.ts +9 -0
  139. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  140. package/packages/pi-coding-agent/src/main.ts +19 -0
  141. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/timestamp.test.ts +38 -0
  142. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +10 -0
  143. package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
  144. package/packages/pi-coding-agent/src/modes/interactive/components/timestamp.ts +48 -0
  145. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +3 -1
  146. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +18 -3
  147. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +16 -7
  148. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +8 -1
  149. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  150. package/src/resources/extensions/gsd/auto/infra-errors.ts +3 -0
  151. package/src/resources/extensions/gsd/auto/phases.ts +45 -48
  152. package/src/resources/extensions/gsd/auto-prompts.ts +24 -1
  153. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  154. package/src/resources/extensions/gsd/auto-timers.ts +64 -3
  155. package/src/resources/extensions/gsd/auto-worktree-sync.ts +5 -0
  156. package/src/resources/extensions/gsd/auto-worktree.ts +9 -6
  157. package/src/resources/extensions/gsd/auto.ts +37 -3
  158. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +148 -0
  159. package/src/resources/extensions/gsd/bootstrap/system-context.ts +48 -11
  160. package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
  161. package/src/resources/extensions/gsd/commands/handlers/core.ts +1 -0
  162. package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
  163. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  164. package/src/resources/extensions/gsd/commands-mcp-status.ts +247 -0
  165. package/src/resources/extensions/gsd/db-writer.ts +39 -17
  166. package/src/resources/extensions/gsd/doctor.ts +7 -1
  167. package/src/resources/extensions/gsd/git-service.ts +6 -2
  168. package/src/resources/extensions/gsd/gsd-db.ts +16 -1
  169. package/src/resources/extensions/gsd/markdown-renderer.ts +1 -1
  170. package/src/resources/extensions/gsd/preferences.ts +11 -1
  171. package/src/resources/extensions/gsd/prompts/complete-milestone.md +2 -4
  172. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  173. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -6
  174. package/src/resources/extensions/gsd/prompts/replan-slice.md +3 -14
  175. package/src/resources/extensions/gsd/prompts/validate-milestone.md +7 -37
  176. package/src/resources/extensions/gsd/provider-error-pause.ts +9 -0
  177. package/src/resources/extensions/gsd/state.ts +19 -1
  178. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  179. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  180. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  181. package/src/resources/extensions/gsd/tests/auto-pr-bugs.test.ts +88 -0
  182. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  183. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
  184. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  185. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  186. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
  187. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  188. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  189. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  190. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  191. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  192. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  193. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  194. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
  195. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
  196. package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
  197. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +114 -0
  198. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  199. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  200. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  201. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  202. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  203. package/src/resources/extensions/gsd/tests/db-writer.test.ts +465 -416
  204. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  205. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
  206. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +210 -181
  207. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  208. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  209. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  210. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  211. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
  212. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  213. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  214. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
  215. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  216. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
  217. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
  218. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  219. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
  220. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  221. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  222. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
  223. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  224. package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
  225. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  226. package/src/resources/extensions/gsd/tests/est-annotation-timeout.test.ts +120 -0
  227. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  228. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  229. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  230. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
  231. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
  232. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  233. package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
  234. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  235. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  236. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  237. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  238. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
  239. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  240. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  241. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  242. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
  243. package/src/resources/extensions/gsd/tests/infra-error.test.ts +20 -2
  244. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  245. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  246. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  247. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  248. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
  249. package/src/resources/extensions/gsd/tests/knowledge.test.ts +89 -0
  250. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
  251. package/src/resources/extensions/gsd/tests/mcp-status.test.ts +103 -0
  252. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  253. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  254. package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
  255. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +66 -0
  256. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  257. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
  258. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  259. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  260. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  261. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
  262. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
  263. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  264. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  265. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  266. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  267. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  268. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  269. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  270. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  271. package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
  272. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  273. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  274. package/src/resources/extensions/gsd/tests/preferences.test.ts +27 -0
  275. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +11 -7
  276. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  277. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  278. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  279. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  280. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  281. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  282. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  283. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  284. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  285. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  286. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  287. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  288. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  289. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  290. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  291. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  292. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
  293. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  294. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  295. package/src/resources/extensions/gsd/tests/stop-auto-merge-back.test.ts +67 -0
  296. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  297. package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +49 -0
  298. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  299. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  300. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +10 -11
  301. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  302. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  303. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  304. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  305. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  306. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  307. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  308. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  309. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  310. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  311. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  312. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  313. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  314. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  315. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  316. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  317. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  318. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  319. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  320. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -0
  321. package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -0
  322. package/src/resources/extensions/gsd/tools/validate-milestone.ts +127 -0
  323. package/src/resources/extensions/gsd/worktree-resolver.ts +7 -0
  324. package/src/resources/extensions/mcp-client/index.ts +20 -0
  325. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_buildManifest.js +0 -0
  326. /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → kxxAA66bah_yhPYqLBHE2}/_ssgManifest.js +0 -0
@@ -1,4 +1,5 @@
1
- import { createTestContext } from './test-helpers.ts';
1
+ import { describe, test, afterEach } from "node:test";
2
+ import assert from "node:assert/strict";
2
3
  import {
3
4
  openDatabase,
4
5
  closeDatabase,
@@ -16,452 +17,438 @@ import {
16
17
  queryProject,
17
18
  } from '../context-store.ts';
18
19
 
19
- const { assertEq, assertTrue, assertMatch, report } = createTestContext();
20
-
21
20
  // ═══════════════════════════════════════════════════════════════════════════
22
21
  // context-store: fallback when DB not open
23
22
  // ═══════════════════════════════════════════════════════════════════════════
24
23
 
25
- console.log('\n=== context-store: fallback returns empty when DB not open ===');
26
- {
27
- closeDatabase();
28
- assertTrue(!isDbAvailable(), 'DB should not be available');
24
+ describe("context-store: fallback when DB not open", () => {
25
+ test("returns empty when DB not open", () => {
26
+ closeDatabase();
27
+ assert.ok(!isDbAvailable(), 'DB should not be available');
29
28
 
30
- const d = queryDecisions();
31
- assertEq(d, [], 'queryDecisions returns [] when DB closed');
29
+ const d = queryDecisions();
30
+ assert.deepStrictEqual(d, [], 'queryDecisions returns [] when DB closed');
32
31
 
33
- const r = queryRequirements();
34
- assertEq(r, [], 'queryRequirements returns [] when DB closed');
32
+ const r = queryRequirements();
33
+ assert.deepStrictEqual(r, [], 'queryRequirements returns [] when DB closed');
35
34
 
36
- const df = queryDecisions({ milestoneId: 'M001' });
37
- assertEq(df, [], 'queryDecisions with opts returns [] when DB closed');
35
+ const df = queryDecisions({ milestoneId: 'M001' });
36
+ assert.deepStrictEqual(df, [], 'queryDecisions with opts returns [] when DB closed');
38
37
 
39
- const rf = queryRequirements({ sliceId: 'S01' });
40
- assertEq(rf, [], 'queryRequirements with opts returns [] when DB closed');
41
- }
38
+ const rf = queryRequirements({ sliceId: 'S01' });
39
+ assert.deepStrictEqual(rf, [], 'queryRequirements with opts returns [] when DB closed');
40
+ });
41
+ });
42
42
 
43
43
  // ═══════════════════════════════════════════════════════════════════════════
44
44
  // context-store: query decisions
45
45
  // ═══════════════════════════════════════════════════════════════════════════
46
46
 
47
- console.log('\n=== context-store: query all active decisions ===');
48
- {
49
- openDatabase(':memory:');
47
+ describe("context-store: query decisions", () => {
48
+ afterEach(() => closeDatabase());
50
49
 
51
- insertDecision({
52
- id: 'D001', when_context: 'M001/S01', scope: 'architecture',
53
- decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
54
- revisable: 'yes', made_by: 'agent', superseded_by: 'D003', // superseded!
55
- });
56
- insertDecision({
57
- id: 'D002', when_context: 'M001/S01', scope: 'architecture',
58
- decision: 'use WAL mode', choice: 'WAL', rationale: 'concurrent reads',
59
- revisable: 'no', made_by: 'agent', superseded_by: null,
60
- });
61
- insertDecision({
62
- id: 'D003', when_context: 'M002/S01', scope: 'performance',
63
- decision: 'use better-sqlite3', choice: 'better-sqlite3', rationale: 'faster',
64
- revisable: 'yes', made_by: 'agent', superseded_by: null,
65
- });
50
+ test("query all active decisions", () => {
51
+ openDatabase(':memory:');
66
52
 
67
- const all = queryDecisions();
68
- assertEq(all.length, 2, 'query all active decisions returns 2 (superseded excluded)');
69
- const ids = all.map(d => d.id);
70
- assertTrue(ids.includes('D002'), 'D002 should be in active results');
71
- assertTrue(ids.includes('D003'), 'D003 should be in active results');
72
- assertTrue(!ids.includes('D001'), 'D001 (superseded) should NOT be in active results');
73
-
74
- closeDatabase();
75
- }
76
-
77
- console.log('\n=== context-store: query decisions by milestone ===');
78
- {
79
- openDatabase(':memory:');
80
-
81
- insertDecision({
82
- id: 'D001', when_context: 'M001/S01', scope: 'architecture',
83
- decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
84
- made_by: 'agent',
85
- superseded_by: null,
86
- });
87
- insertDecision({
88
- id: 'D002', when_context: 'M002/S02', scope: 'architecture',
89
- decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
90
- made_by: 'agent',
91
- superseded_by: null,
92
- });
53
+ insertDecision({
54
+ id: 'D001', when_context: 'M001/S01', scope: 'architecture',
55
+ decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
56
+ revisable: 'yes', made_by: 'agent', superseded_by: 'D003', // superseded!
57
+ });
58
+ insertDecision({
59
+ id: 'D002', when_context: 'M001/S01', scope: 'architecture',
60
+ decision: 'use WAL mode', choice: 'WAL', rationale: 'concurrent reads',
61
+ revisable: 'no', made_by: 'agent', superseded_by: null,
62
+ });
63
+ insertDecision({
64
+ id: 'D003', when_context: 'M002/S01', scope: 'performance',
65
+ decision: 'use better-sqlite3', choice: 'better-sqlite3', rationale: 'faster',
66
+ revisable: 'yes', made_by: 'agent', superseded_by: null,
67
+ });
93
68
 
94
- const m1 = queryDecisions({ milestoneId: 'M001' });
95
- assertEq(m1.length, 1, 'milestone filter M001 returns 1');
96
- assertEq(m1[0]?.id, 'D001', 'milestone filter returns D001');
69
+ const all = queryDecisions();
70
+ assert.strictEqual(all.length, 2, 'query all active decisions returns 2 (superseded excluded)');
71
+ const ids = all.map(d => d.id);
72
+ assert.ok(ids.includes('D002'), 'D002 should be in active results');
73
+ assert.ok(ids.includes('D003'), 'D003 should be in active results');
74
+ assert.ok(!ids.includes('D001'), 'D001 (superseded) should NOT be in active results');
75
+ });
97
76
 
98
- const m2 = queryDecisions({ milestoneId: 'M002' });
99
- assertEq(m2.length, 1, 'milestone filter M002 returns 1');
100
- assertEq(m2[0]?.id, 'D002', 'milestone filter returns D002');
77
+ test("query decisions by milestone", () => {
78
+ openDatabase(':memory:');
101
79
 
102
- closeDatabase();
103
- }
80
+ insertDecision({
81
+ id: 'D001', when_context: 'M001/S01', scope: 'architecture',
82
+ decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
83
+ made_by: 'agent',
84
+ superseded_by: null,
85
+ });
86
+ insertDecision({
87
+ id: 'D002', when_context: 'M002/S02', scope: 'architecture',
88
+ decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
89
+ made_by: 'agent',
90
+ superseded_by: null,
91
+ });
104
92
 
105
- console.log('\n=== context-store: query decisions by scope ===');
106
- {
107
- openDatabase(':memory:');
93
+ const m1 = queryDecisions({ milestoneId: 'M001' });
94
+ assert.strictEqual(m1.length, 1, 'milestone filter M001 returns 1');
95
+ assert.strictEqual(m1[0]?.id, 'D001', 'milestone filter returns D001');
108
96
 
109
- insertDecision({
110
- id: 'D001', when_context: 'M001/S01', scope: 'architecture',
111
- decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
112
- made_by: 'agent',
113
- superseded_by: null,
114
- });
115
- insertDecision({
116
- id: 'D002', when_context: 'M001/S01', scope: 'performance',
117
- decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
118
- made_by: 'agent',
119
- superseded_by: null,
97
+ const m2 = queryDecisions({ milestoneId: 'M002' });
98
+ assert.strictEqual(m2.length, 1, 'milestone filter M002 returns 1');
99
+ assert.strictEqual(m2[0]?.id, 'D002', 'milestone filter returns D002');
120
100
  });
121
101
 
122
- const arch = queryDecisions({ scope: 'architecture' });
123
- assertEq(arch.length, 1, 'scope filter architecture returns 1');
124
- assertEq(arch[0]?.id, 'D001', 'scope filter returns D001');
102
+ test("query decisions by scope", () => {
103
+ openDatabase(':memory:');
104
+
105
+ insertDecision({
106
+ id: 'D001', when_context: 'M001/S01', scope: 'architecture',
107
+ decision: 'decision A', choice: 'A', rationale: 'r', revisable: 'yes',
108
+ made_by: 'agent',
109
+ superseded_by: null,
110
+ });
111
+ insertDecision({
112
+ id: 'D002', when_context: 'M001/S01', scope: 'performance',
113
+ decision: 'decision B', choice: 'B', rationale: 'r', revisable: 'yes',
114
+ made_by: 'agent',
115
+ superseded_by: null,
116
+ });
125
117
 
126
- const perf = queryDecisions({ scope: 'performance' });
127
- assertEq(perf.length, 1, 'scope filter performance returns 1');
128
- assertEq(perf[0]?.id, 'D002', 'scope filter returns D002');
118
+ const arch = queryDecisions({ scope: 'architecture' });
119
+ assert.strictEqual(arch.length, 1, 'scope filter architecture returns 1');
120
+ assert.strictEqual(arch[0]?.id, 'D001', 'scope filter returns D001');
129
121
 
130
- const none = queryDecisions({ scope: 'nonexistent' });
131
- assertEq(none.length, 0, 'scope filter nonexistent returns 0');
122
+ const perf = queryDecisions({ scope: 'performance' });
123
+ assert.strictEqual(perf.length, 1, 'scope filter performance returns 1');
124
+ assert.strictEqual(perf[0]?.id, 'D002', 'scope filter returns D002');
132
125
 
133
- closeDatabase();
134
- }
126
+ const none = queryDecisions({ scope: 'nonexistent' });
127
+ assert.strictEqual(none.length, 0, 'scope filter nonexistent returns 0');
128
+ });
129
+ });
135
130
 
136
131
  // ═══════════════════════════════════════════════════════════════════════════
137
132
  // context-store: query requirements
138
133
  // ═══════════════════════════════════════════════════════════════════════════
139
134
 
140
- console.log('\n=== context-store: query all active requirements ===');
141
- {
142
- openDatabase(':memory:');
135
+ describe("context-store: query requirements", () => {
136
+ afterEach(() => closeDatabase());
143
137
 
144
- insertRequirement({
145
- id: 'R001', class: 'functional', status: 'active',
146
- description: 'req A', why: 'w', source: 'M001', primary_owner: 'S01',
147
- supporting_slices: 'S02', validation: 'v', notes: '', full_content: '',
148
- superseded_by: 'R003', // superseded!
149
- });
150
- insertRequirement({
151
- id: 'R002', class: 'non-functional', status: 'active',
152
- description: 'req B', why: 'w', source: 'M001', primary_owner: 'S01',
153
- supporting_slices: '', validation: 'v', notes: '', full_content: '',
154
- superseded_by: null,
155
- });
156
- insertRequirement({
157
- id: 'R003', class: 'functional', status: 'validated',
158
- description: 'req C', why: 'w', source: 'M001', primary_owner: 'S02',
159
- supporting_slices: 'S01', validation: 'v', notes: '', full_content: '',
160
- superseded_by: null,
161
- });
138
+ test("query all active requirements", () => {
139
+ openDatabase(':memory:');
162
140
 
163
- const all = queryRequirements();
164
- assertEq(all.length, 2, 'query all active requirements returns 2 (superseded excluded)');
165
- const ids = all.map(r => r.id);
166
- assertTrue(ids.includes('R002'), 'R002 should be active');
167
- assertTrue(ids.includes('R003'), 'R003 should be active');
168
- assertTrue(!ids.includes('R001'), 'R001 (superseded) should NOT be active');
169
-
170
- closeDatabase();
171
- }
172
-
173
- console.log('\n=== context-store: query requirements by slice ===');
174
- {
175
- openDatabase(':memory:');
176
-
177
- insertRequirement({
178
- id: 'R001', class: 'functional', status: 'active',
179
- description: 'req A', why: 'w', source: 'M001', primary_owner: 'S01',
180
- supporting_slices: '', validation: 'v', notes: '', full_content: '',
181
- superseded_by: null,
182
- });
183
- insertRequirement({
184
- id: 'R002', class: 'functional', status: 'active',
185
- description: 'req B', why: 'w', source: 'M001', primary_owner: 'S02',
186
- supporting_slices: 'S01', validation: 'v', notes: '', full_content: '',
187
- superseded_by: null,
188
- });
189
- insertRequirement({
190
- id: 'R003', class: 'functional', status: 'active',
191
- description: 'req C', why: 'w', source: 'M001', primary_owner: 'S03',
192
- supporting_slices: '', validation: 'v', notes: '', full_content: '',
193
- superseded_by: null,
194
- });
141
+ insertRequirement({
142
+ id: 'R001', class: 'functional', status: 'active',
143
+ description: 'req A', why: 'w', source: 'M001', primary_owner: 'S01',
144
+ supporting_slices: 'S02', validation: 'v', notes: '', full_content: '',
145
+ superseded_by: 'R003', // superseded!
146
+ });
147
+ insertRequirement({
148
+ id: 'R002', class: 'non-functional', status: 'active',
149
+ description: 'req B', why: 'w', source: 'M001', primary_owner: 'S01',
150
+ supporting_slices: '', validation: 'v', notes: '', full_content: '',
151
+ superseded_by: null,
152
+ });
153
+ insertRequirement({
154
+ id: 'R003', class: 'functional', status: 'validated',
155
+ description: 'req C', why: 'w', source: 'M001', primary_owner: 'S02',
156
+ supporting_slices: 'S01', validation: 'v', notes: '', full_content: '',
157
+ superseded_by: null,
158
+ });
195
159
 
196
- const s01 = queryRequirements({ sliceId: 'S01' });
197
- assertEq(s01.length, 2, 'slice filter S01 returns 2 (primary + supporting)');
198
- const s01ids = s01.map(r => r.id).sort();
199
- assertEq(s01ids, ['R001', 'R002'], 'S01 owns R001 and supports R002');
160
+ const all = queryRequirements();
161
+ assert.strictEqual(all.length, 2, 'query all active requirements returns 2 (superseded excluded)');
162
+ const ids = all.map(r => r.id);
163
+ assert.ok(ids.includes('R002'), 'R002 should be active');
164
+ assert.ok(ids.includes('R003'), 'R003 should be active');
165
+ assert.ok(!ids.includes('R001'), 'R001 (superseded) should NOT be active');
166
+ });
200
167
 
201
- const s03 = queryRequirements({ sliceId: 'S03' });
202
- assertEq(s03.length, 1, 'slice filter S03 returns 1');
203
- assertEq(s03[0]?.id, 'R003', 'S03 owns R003');
168
+ test("query requirements by slice", () => {
169
+ openDatabase(':memory:');
204
170
 
205
- closeDatabase();
206
- }
171
+ insertRequirement({
172
+ id: 'R001', class: 'functional', status: 'active',
173
+ description: 'req A', why: 'w', source: 'M001', primary_owner: 'S01',
174
+ supporting_slices: '', validation: 'v', notes: '', full_content: '',
175
+ superseded_by: null,
176
+ });
177
+ insertRequirement({
178
+ id: 'R002', class: 'functional', status: 'active',
179
+ description: 'req B', why: 'w', source: 'M001', primary_owner: 'S02',
180
+ supporting_slices: 'S01', validation: 'v', notes: '', full_content: '',
181
+ superseded_by: null,
182
+ });
183
+ insertRequirement({
184
+ id: 'R003', class: 'functional', status: 'active',
185
+ description: 'req C', why: 'w', source: 'M001', primary_owner: 'S03',
186
+ supporting_slices: '', validation: 'v', notes: '', full_content: '',
187
+ superseded_by: null,
188
+ });
207
189
 
208
- console.log('\n=== context-store: query requirements by status ===');
209
- {
210
- openDatabase(':memory:');
190
+ const s01 = queryRequirements({ sliceId: 'S01' });
191
+ assert.strictEqual(s01.length, 2, 'slice filter S01 returns 2 (primary + supporting)');
192
+ const s01ids = s01.map(r => r.id).sort();
193
+ assert.deepStrictEqual(s01ids, ['R001', 'R002'], 'S01 owns R001 and supports R002');
211
194
 
212
- insertRequirement({
213
- id: 'R001', class: 'functional', status: 'active',
214
- description: 'req A', why: 'w', source: 'M001', primary_owner: 'S01',
215
- supporting_slices: '', validation: 'v', notes: '', full_content: '',
216
- superseded_by: null,
217
- });
218
- insertRequirement({
219
- id: 'R002', class: 'functional', status: 'validated',
220
- description: 'req B', why: 'w', source: 'M001', primary_owner: 'S01',
221
- supporting_slices: '', validation: 'v', notes: '', full_content: '',
222
- superseded_by: null,
223
- });
224
- insertRequirement({
225
- id: 'R003', class: 'functional', status: 'deferred',
226
- description: 'req C', why: 'w', source: 'M001', primary_owner: 'S01',
227
- supporting_slices: '', validation: 'v', notes: '', full_content: '',
228
- superseded_by: null,
195
+ const s03 = queryRequirements({ sliceId: 'S03' });
196
+ assert.strictEqual(s03.length, 1, 'slice filter S03 returns 1');
197
+ assert.strictEqual(s03[0]?.id, 'R003', 'S03 owns R003');
229
198
  });
230
199
 
231
- const active = queryRequirements({ status: 'active' });
232
- assertEq(active.length, 1, 'status filter active returns 1');
233
- assertEq(active[0]?.id, 'R001', 'active returns R001');
200
+ test("query requirements by status", () => {
201
+ openDatabase(':memory:');
202
+
203
+ insertRequirement({
204
+ id: 'R001', class: 'functional', status: 'active',
205
+ description: 'req A', why: 'w', source: 'M001', primary_owner: 'S01',
206
+ supporting_slices: '', validation: 'v', notes: '', full_content: '',
207
+ superseded_by: null,
208
+ });
209
+ insertRequirement({
210
+ id: 'R002', class: 'functional', status: 'validated',
211
+ description: 'req B', why: 'w', source: 'M001', primary_owner: 'S01',
212
+ supporting_slices: '', validation: 'v', notes: '', full_content: '',
213
+ superseded_by: null,
214
+ });
215
+ insertRequirement({
216
+ id: 'R003', class: 'functional', status: 'deferred',
217
+ description: 'req C', why: 'w', source: 'M001', primary_owner: 'S01',
218
+ supporting_slices: '', validation: 'v', notes: '', full_content: '',
219
+ superseded_by: null,
220
+ });
234
221
 
235
- const validated = queryRequirements({ status: 'validated' });
236
- assertEq(validated.length, 1, 'status filter validated returns 1');
237
- assertEq(validated[0]?.id, 'R002', 'validated returns R002');
222
+ const active = queryRequirements({ status: 'active' });
223
+ assert.strictEqual(active.length, 1, 'status filter active returns 1');
224
+ assert.strictEqual(active[0]?.id, 'R001', 'active returns R001');
238
225
 
239
- closeDatabase();
240
- }
226
+ const validated = queryRequirements({ status: 'validated' });
227
+ assert.strictEqual(validated.length, 1, 'status filter validated returns 1');
228
+ assert.strictEqual(validated[0]?.id, 'R002', 'validated returns R002');
229
+ });
230
+ });
241
231
 
242
232
  // ═══════════════════════════════════════════════════════════════════════════
243
233
  // context-store: format decisions
244
234
  // ═══════════════════════════════════════════════════════════════════════════
245
235
 
246
- console.log('\n=== context-store: formatDecisionsForPrompt ===');
247
- {
248
- const empty = formatDecisionsForPrompt([]);
249
- assertEq(empty, '', 'empty input returns empty string');
236
+ describe("context-store: formatDecisionsForPrompt", () => {
237
+ test("empty input returns empty string", () => {
238
+ const empty = formatDecisionsForPrompt([]);
239
+ assert.strictEqual(empty, '', 'empty input returns empty string');
240
+ });
250
241
 
251
- const result = formatDecisionsForPrompt([
252
- {
253
- seq: 1, id: 'D001', when_context: 'M001/S01', scope: 'architecture',
254
- decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
255
- revisable: 'yes', made_by: 'agent', superseded_by: null,
256
- },
257
- {
258
- seq: 2, id: 'D002', when_context: 'M001/S02', scope: 'performance',
259
- decision: 'use WAL', choice: 'WAL', rationale: 'concurrent',
260
- revisable: 'no', made_by: 'human', superseded_by: null,
261
- },
262
- ]);
263
-
264
- // Should be a markdown table
265
- assertMatch(result, /^\| # \| When \| Scope/, 'has table header');
266
- assertMatch(result, /\|---\|/, 'has separator row');
267
- assertMatch(result, /\| D001 \|/, 'has D001 row');
268
- assertMatch(result, /\| D002 \|/, 'has D002 row');
269
- const lines = result.split('\n');
270
- assertEq(lines.length, 4, 'table has 4 lines (header + separator + 2 rows)');
271
- }
242
+ test("formats decisions as markdown table", () => {
243
+ const result = formatDecisionsForPrompt([
244
+ {
245
+ seq: 1, id: 'D001', when_context: 'M001/S01', scope: 'architecture',
246
+ decision: 'use SQLite', choice: 'node:sqlite', rationale: 'built-in',
247
+ revisable: 'yes', made_by: 'agent', superseded_by: null,
248
+ },
249
+ {
250
+ seq: 2, id: 'D002', when_context: 'M001/S02', scope: 'performance',
251
+ decision: 'use WAL', choice: 'WAL', rationale: 'concurrent',
252
+ revisable: 'no', made_by: 'human', superseded_by: null,
253
+ },
254
+ ]);
255
+
256
+ // Should be a markdown table
257
+ assert.match(result, /^\| # \| When \| Scope/, 'has table header');
258
+ assert.match(result, /\|---\|/, 'has separator row');
259
+ assert.match(result, /\| D001 \|/, 'has D001 row');
260
+ assert.match(result, /\| D002 \|/, 'has D002 row');
261
+ const lines = result.split('\n');
262
+ assert.strictEqual(lines.length, 4, 'table has 4 lines (header + separator + 2 rows)');
263
+ });
264
+ });
272
265
 
273
266
  // ═══════════════════════════════════════════════════════════════════════════
274
267
  // context-store: format requirements
275
268
  // ═══════════════════════════════════════════════════════════════════════════
276
269
 
277
- console.log('\n=== context-store: formatRequirementsForPrompt ===');
278
- {
279
- const empty = formatRequirementsForPrompt([]);
280
- assertEq(empty, '', 'empty input returns empty string');
270
+ describe("context-store: formatRequirementsForPrompt", () => {
271
+ test("empty input returns empty string", () => {
272
+ const empty = formatRequirementsForPrompt([]);
273
+ assert.strictEqual(empty, '', 'empty input returns empty string');
274
+ });
281
275
 
282
- const result = formatRequirementsForPrompt([
283
- {
284
- id: 'R001', class: 'functional', status: 'active',
285
- description: 'System must persist decisions', why: 'agent memory',
286
- source: 'M001', primary_owner: 'S01', supporting_slices: 'S02',
287
- validation: 'roundtrip test', notes: 'high priority',
288
- full_content: '', superseded_by: null,
289
- },
290
- {
291
- id: 'R002', class: 'non-functional', status: 'active',
292
- description: 'Sub-5ms query latency', why: 'prompt injection speed',
293
- source: 'M001', primary_owner: 'S01', supporting_slices: '',
294
- validation: 'timing test', notes: '',
295
- full_content: '', superseded_by: null,
296
- },
297
- ]);
298
-
299
- assertMatch(result, /### R001: System must persist decisions/, 'has R001 section header');
300
- assertMatch(result, /### R002: Sub-5ms query latency/, 'has R002 section header');
301
- assertMatch(result, /\*\*Class:\*\* functional/, 'has class field');
302
- assertMatch(result, /\*\*Status:\*\* active/, 'has status field');
303
- assertMatch(result, /\*\*Supporting Slices:\*\* S02/, 'has supporting slices when present');
304
- // R002 has no supporting_slices should not have that line
305
- // R002 has no notes — should not have notes line
306
- const r002Section = result.split('### R002')[1] || '';
307
- assertTrue(!r002Section.includes('**Supporting Slices:**'), 'no supporting slices line when empty');
308
- assertTrue(!r002Section.includes('**Notes:**'), 'no notes line when empty');
309
- }
276
+ test("formats requirements as markdown sections", () => {
277
+ const result = formatRequirementsForPrompt([
278
+ {
279
+ id: 'R001', class: 'functional', status: 'active',
280
+ description: 'System must persist decisions', why: 'agent memory',
281
+ source: 'M001', primary_owner: 'S01', supporting_slices: 'S02',
282
+ validation: 'roundtrip test', notes: 'high priority',
283
+ full_content: '', superseded_by: null,
284
+ },
285
+ {
286
+ id: 'R002', class: 'non-functional', status: 'active',
287
+ description: 'Sub-5ms query latency', why: 'prompt injection speed',
288
+ source: 'M001', primary_owner: 'S01', supporting_slices: '',
289
+ validation: 'timing test', notes: '',
290
+ full_content: '', superseded_by: null,
291
+ },
292
+ ]);
293
+
294
+ assert.match(result, /### R001: System must persist decisions/, 'has R001 section header');
295
+ assert.match(result, /### R002: Sub-5ms query latency/, 'has R002 section header');
296
+ assert.match(result, /\*\*Class:\*\* functional/, 'has class field');
297
+ assert.match(result, /\*\*Status:\*\* active/, 'has status field');
298
+ assert.match(result, /\*\*Supporting Slices:\*\* S02/, 'has supporting slices when present');
299
+ // R002 has no supporting_slices — should not have that line
300
+ // R002 has no notes should not have notes line
301
+ const r002Section = result.split('### R002')[1] || '';
302
+ assert.ok(!r002Section.includes('**Supporting Slices:**'), 'no supporting slices line when empty');
303
+ assert.ok(!r002Section.includes('**Notes:**'), 'no notes line when empty');
304
+ });
305
+ });
310
306
 
311
307
  // ═══════════════════════════════════════════════════════════════════════════
312
308
  // context-store: sub-5ms timing assertion
313
309
  // ═══════════════════════════════════════════════════════════════════════════
314
310
 
315
- console.log('\n=== context-store: sub-5ms query timing ===');
316
- {
317
- openDatabase(':memory:');
318
-
319
- // Insert 50 decisions
320
- for (let i = 1; i <= 50; i++) {
321
- const id = `D${String(i).padStart(3, '0')}`;
322
- insertDecision({
323
- id,
324
- when_context: `M00${(i % 3) + 1}/S0${(i % 5) + 1}`,
325
- scope: i % 2 === 0 ? 'architecture' : 'performance',
326
- decision: `decision ${i}`,
327
- choice: `choice ${i}`,
328
- rationale: `rationale ${i}`,
329
- revisable: i % 3 === 0 ? 'no' : 'yes',
330
- made_by: 'agent',
331
- superseded_by: null,
332
- });
333
- }
334
-
335
- // Insert 50 requirements
336
- for (let i = 1; i <= 50; i++) {
337
- const id = `R${String(i).padStart(3, '0')}`;
338
- insertRequirement({
339
- id,
340
- class: i % 2 === 0 ? 'functional' : 'non-functional',
341
- status: i % 4 === 0 ? 'validated' : 'active',
342
- description: `requirement ${i}`,
343
- why: `why ${i}`,
344
- source: 'M001',
345
- primary_owner: `S0${(i % 5) + 1}`,
346
- supporting_slices: i % 3 === 0 ? 'S01, S02' : '',
347
- validation: `validation ${i}`,
348
- notes: '',
349
- full_content: '',
350
- superseded_by: null,
351
- });
352
- }
353
-
354
- // Time the queries — warm up first
355
- queryDecisions();
356
- queryRequirements();
357
-
358
- const start = performance.now();
359
- const decisions = queryDecisions();
360
- const requirements = queryRequirements();
361
- const elapsed = performance.now() - start;
362
-
363
- assertTrue(decisions.length === 50, `got ${decisions.length} decisions (expected 50)`);
364
- assertTrue(requirements.length === 50, `got ${requirements.length} requirements (expected 50)`);
365
- assertTrue(elapsed < 5, `query latency ${elapsed.toFixed(2)}ms should be < 5ms`);
366
- console.log(` timing: ${elapsed.toFixed(2)}ms for 50+50 row queries`);
367
-
368
- closeDatabase();
369
- }
311
+ describe("context-store: sub-5ms query timing", () => {
312
+ afterEach(() => closeDatabase());
313
+
314
+ test("queries complete under 5ms for 50+50 rows", () => {
315
+ openDatabase(':memory:');
316
+
317
+ // Insert 50 decisions
318
+ for (let i = 1; i <= 50; i++) {
319
+ const id = `D${String(i).padStart(3, '0')}`;
320
+ insertDecision({
321
+ id,
322
+ when_context: `M00${(i % 3) + 1}/S0${(i % 5) + 1}`,
323
+ scope: i % 2 === 0 ? 'architecture' : 'performance',
324
+ decision: `decision ${i}`,
325
+ choice: `choice ${i}`,
326
+ rationale: `rationale ${i}`,
327
+ revisable: i % 3 === 0 ? 'no' : 'yes',
328
+ made_by: 'agent',
329
+ superseded_by: null,
330
+ });
331
+ }
332
+
333
+ // Insert 50 requirements
334
+ for (let i = 1; i <= 50; i++) {
335
+ const id = `R${String(i).padStart(3, '0')}`;
336
+ insertRequirement({
337
+ id,
338
+ class: i % 2 === 0 ? 'functional' : 'non-functional',
339
+ status: i % 4 === 0 ? 'validated' : 'active',
340
+ description: `requirement ${i}`,
341
+ why: `why ${i}`,
342
+ source: 'M001',
343
+ primary_owner: `S0${(i % 5) + 1}`,
344
+ supporting_slices: i % 3 === 0 ? 'S01, S02' : '',
345
+ validation: `validation ${i}`,
346
+ notes: '',
347
+ full_content: '',
348
+ superseded_by: null,
349
+ });
350
+ }
351
+
352
+ // Time the queries — warm up first
353
+ queryDecisions();
354
+ queryRequirements();
355
+
356
+ const start = performance.now();
357
+ const decisions = queryDecisions();
358
+ const requirements = queryRequirements();
359
+ const elapsed = performance.now() - start;
360
+
361
+ assert.strictEqual(decisions.length, 50, `got ${decisions.length} decisions (expected 50)`);
362
+ assert.strictEqual(requirements.length, 50, `got ${requirements.length} requirements (expected 50)`);
363
+ assert.ok(elapsed < 5, `query latency ${elapsed.toFixed(2)}ms should be < 5ms`);
364
+ });
365
+ });
370
366
 
371
367
  // ═══════════════════════════════════════════════════════════════════════════
372
368
  // context-store: queryArtifact
373
369
  // ═══════════════════════════════════════════════════════════════════════════
374
370
 
375
- console.log('\n=== context-store: queryArtifact returns content for existing path ===');
376
- {
377
- openDatabase(':memory:');
378
-
379
- insertArtifact({
380
- path: 'PROJECT.md',
381
- artifact_type: 'project',
382
- milestone_id: null,
383
- slice_id: null,
384
- task_id: null,
385
- full_content: '# My Project\n\nProject description here.',
386
- });
387
- insertArtifact({
388
- path: '.gsd/milestones/M001/M001-PLAN.md',
389
- artifact_type: 'milestone_plan',
390
- milestone_id: 'M001',
391
- slice_id: null,
392
- task_id: null,
393
- full_content: '# M001 Plan\n\nMilestone content.',
394
- });
371
+ describe("context-store: queryArtifact", () => {
372
+ afterEach(() => closeDatabase());
395
373
 
396
- const project = queryArtifact('PROJECT.md');
397
- assertEq(project, '# My Project\n\nProject description here.', 'queryArtifact returns full_content for PROJECT.md');
374
+ test("returns content for existing path", () => {
375
+ openDatabase(':memory:');
398
376
 
399
- const plan = queryArtifact('.gsd/milestones/M001/M001-PLAN.md');
400
- assertEq(plan, '# M001 Plan\n\nMilestone content.', 'queryArtifact returns full_content for milestone plan');
377
+ insertArtifact({
378
+ path: 'PROJECT.md',
379
+ artifact_type: 'project',
380
+ milestone_id: null,
381
+ slice_id: null,
382
+ task_id: null,
383
+ full_content: '# My Project\n\nProject description here.',
384
+ });
385
+ insertArtifact({
386
+ path: '.gsd/milestones/M001/M001-PLAN.md',
387
+ artifact_type: 'milestone_plan',
388
+ milestone_id: 'M001',
389
+ slice_id: null,
390
+ task_id: null,
391
+ full_content: '# M001 Plan\n\nMilestone content.',
392
+ });
401
393
 
402
- closeDatabase();
403
- }
394
+ const project = queryArtifact('PROJECT.md');
395
+ assert.strictEqual(project, '# My Project\n\nProject description here.', 'queryArtifact returns full_content for PROJECT.md');
404
396
 
405
- console.log('\n=== context-store: queryArtifact returns null for missing path ===');
406
- {
407
- openDatabase(':memory:');
397
+ const plan = queryArtifact('.gsd/milestones/M001/M001-PLAN.md');
398
+ assert.strictEqual(plan, '# M001 Plan\n\nMilestone content.', 'queryArtifact returns full_content for milestone plan');
399
+ });
408
400
 
409
- const missing = queryArtifact('nonexistent.md');
410
- assertEq(missing, null, 'queryArtifact returns null for path not in DB');
401
+ test("returns null for missing path", () => {
402
+ openDatabase(':memory:');
411
403
 
412
- closeDatabase();
413
- }
404
+ const missing = queryArtifact('nonexistent.md');
405
+ assert.strictEqual(missing, null, 'queryArtifact returns null for path not in DB');
406
+ });
414
407
 
415
- console.log('\n=== context-store: queryArtifact returns null when DB unavailable ===');
416
- {
417
- closeDatabase();
418
- assertTrue(!isDbAvailable(), 'DB should not be available');
408
+ test("returns null when DB unavailable", () => {
409
+ closeDatabase();
410
+ assert.ok(!isDbAvailable(), 'DB should not be available');
419
411
 
420
- const result = queryArtifact('PROJECT.md');
421
- assertEq(result, null, 'queryArtifact returns null when DB closed');
422
- }
412
+ const result = queryArtifact('PROJECT.md');
413
+ assert.strictEqual(result, null, 'queryArtifact returns null when DB closed');
414
+ });
415
+ });
423
416
 
424
417
  // ═══════════════════════════════════════════════════════════════════════════
425
418
  // context-store: queryProject
426
419
  // ═══════════════════════════════════════════════════════════════════════════
427
420
 
428
- console.log('\n=== context-store: queryProject returns PROJECT.md content ===');
429
- {
430
- openDatabase(':memory:');
431
-
432
- insertArtifact({
433
- path: 'PROJECT.md',
434
- artifact_type: 'project',
435
- milestone_id: null,
436
- slice_id: null,
437
- task_id: null,
438
- full_content: '# Test Project\n\nThis is the project description.',
439
- });
440
-
441
- const content = queryProject();
442
- assertEq(content, '# Test Project\n\nThis is the project description.', 'queryProject returns PROJECT.md content');
421
+ describe("context-store: queryProject", () => {
422
+ afterEach(() => closeDatabase());
443
423
 
444
- closeDatabase();
445
- }
424
+ test("returns PROJECT.md content", () => {
425
+ openDatabase(':memory:');
446
426
 
447
- console.log('\n=== context-store: queryProject returns null when no PROJECT.md ===');
448
- {
449
- openDatabase(':memory:');
427
+ insertArtifact({
428
+ path: 'PROJECT.md',
429
+ artifact_type: 'project',
430
+ milestone_id: null,
431
+ slice_id: null,
432
+ task_id: null,
433
+ full_content: '# Test Project\n\nThis is the project description.',
434
+ });
450
435
 
451
- const content = queryProject();
452
- assertEq(content, null, 'queryProject returns null when PROJECT.md not imported');
436
+ const content = queryProject();
437
+ assert.strictEqual(content, '# Test Project\n\nThis is the project description.', 'queryProject returns PROJECT.md content');
438
+ });
453
439
 
454
- closeDatabase();
455
- }
440
+ test("returns null when no PROJECT.md", () => {
441
+ openDatabase(':memory:');
456
442
 
457
- console.log('\n=== context-store: queryProject returns null when DB unavailable ===');
458
- {
459
- closeDatabase();
460
- assertTrue(!isDbAvailable(), 'DB should not be available');
443
+ const content = queryProject();
444
+ assert.strictEqual(content, null, 'queryProject returns null when PROJECT.md not imported');
445
+ });
461
446
 
462
- const content = queryProject();
463
- assertEq(content, null, 'queryProject returns null when DB closed');
464
- }
447
+ test("returns null when DB unavailable", () => {
448
+ closeDatabase();
449
+ assert.ok(!isDbAvailable(), 'DB should not be available');
465
450
 
466
- // ─── Final Report ──────────────────────────────────────────────────────────
467
- report();
451
+ const content = queryProject();
452
+ assert.strictEqual(content, null, 'queryProject returns null when DB closed');
453
+ });
454
+ });