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
@@ -0,0 +1,49 @@
1
+ /**
2
+ * terminated-transient.test.ts — Regression test for #2309.
3
+ *
4
+ * classifyProviderError should treat 'terminated' errors (process killed,
5
+ * connection reset) as transient with auto-resume, not permanent.
6
+ */
7
+
8
+ import test from "node:test";
9
+ import assert from "node:assert/strict";
10
+ import { classifyProviderError } from "../provider-error-pause.ts";
11
+
12
+ test("#2309: 'terminated' errors should be classified as transient", () => {
13
+ const result = classifyProviderError("terminated");
14
+ assert.equal(result.isTransient, true, "'terminated' should be transient");
15
+ assert.equal(result.isRateLimit, false, "'terminated' is not a rate limit");
16
+ assert.ok(result.suggestedDelayMs > 0, "'terminated' should have a retry delay");
17
+ });
18
+
19
+ test("#2309: 'connection reset' errors should be classified as transient", () => {
20
+ const result = classifyProviderError("connection reset by peer");
21
+ assert.equal(result.isTransient, true, "'connection reset' should be transient");
22
+ });
23
+
24
+ test("#2309: 'other side closed' errors should be classified as transient", () => {
25
+ const result = classifyProviderError("other side closed the connection");
26
+ assert.equal(result.isTransient, true, "'other side closed' should be transient");
27
+ });
28
+
29
+ test("#2309: 'fetch failed' errors should be classified as transient", () => {
30
+ const result = classifyProviderError("fetch failed: network error");
31
+ assert.equal(result.isTransient, true, "'fetch failed' should be transient");
32
+ });
33
+
34
+ test("#2309: 'connection refused' errors should be classified as transient", () => {
35
+ const result = classifyProviderError("ECONNREFUSED: connection refused");
36
+ assert.equal(result.isTransient, true, "'connection refused' should be transient");
37
+ });
38
+
39
+ test("#2309: permanent errors are still permanent", () => {
40
+ const authResult = classifyProviderError("unauthorized: invalid API key");
41
+ assert.equal(authResult.isTransient, false, "auth errors should stay permanent");
42
+ assert.equal(authResult.suggestedDelayMs, 0, "permanent errors have no delay");
43
+ });
44
+
45
+ test("#2309: rate limits are still transient", () => {
46
+ const rlResult = classifyProviderError("rate limit exceeded (429)");
47
+ assert.equal(rlResult.isTransient, true, "rate limits are still transient");
48
+ assert.equal(rlResult.isRateLimit, true, "rate limits are flagged as rate limits");
49
+ });
@@ -18,9 +18,9 @@ import {
18
18
  formatDecisionsForPrompt,
19
19
  formatRequirementsForPrompt,
20
20
  } from '../context-store.ts';
21
- import { createTestContext } from './test-helpers.ts';
21
+ import { test } from 'node:test';
22
+ import assert from 'node:assert/strict';
22
23
 
23
- const { assertEq, assertTrue, assertMatch, assertNoMatch, report } = createTestContext();
24
24
 
25
25
  // ─── Fixture Generators ────────────────────────────────────────────────────
26
26
 
@@ -154,8 +154,8 @@ console.log('\n=== token-savings: plan-slice prompt ≥30% character savings ===
154
154
  openDatabase(':memory:');
155
155
  const result = migrateFromMarkdown(base);
156
156
 
157
- assertTrue(result.decisions === DECISIONS_COUNT, `imported ${result.decisions} decisions, expected ${DECISIONS_COUNT}`);
158
- assertTrue(result.requirements === REQUIREMENTS_COUNT, `imported ${result.requirements} requirements, expected ${REQUIREMENTS_COUNT}`);
157
+ assert.ok(result.decisions === DECISIONS_COUNT, `imported ${result.decisions} decisions, expected ${DECISIONS_COUNT}`);
158
+ assert.ok(result.requirements === REQUIREMENTS_COUNT, `imported ${result.requirements} requirements, expected ${REQUIREMENTS_COUNT}`);
159
159
 
160
160
  // ── DB-scoped content for plan-slice (M001 decisions + S01 requirements) ──
161
161
  const scopedDecisions = queryDecisions({ milestoneId: 'M001' });
@@ -174,31 +174,31 @@ console.log('\n=== token-savings: plan-slice prompt ≥30% character savings ===
174
174
  const savingsPercent = ((fullTotal - dbTotal) / fullTotal) * 100;
175
175
  console.log(` Plan-slice savings: ${savingsPercent.toFixed(1)}% (DB: ${dbTotal} chars, full: ${fullTotal} chars)`);
176
176
 
177
- assertTrue(dbTotal > 0, 'DB-scoped content is non-empty');
178
- assertTrue(dbDecisionsContent.length > 0, 'DB-scoped decisions content is non-empty');
179
- assertTrue(dbRequirementsContent.length > 0, 'DB-scoped requirements content is non-empty');
180
- assertTrue(savingsPercent >= 30, `plan-slice savings ≥30% (actual: ${savingsPercent.toFixed(1)}%)`);
181
- assertTrue(dbTotal < fullTotal * 0.70, `DB total (${dbTotal}) < 70% of full total (${fullTotal})`);
177
+ assert.ok(dbTotal > 0, 'DB-scoped content is non-empty');
178
+ assert.ok(dbDecisionsContent.length > 0, 'DB-scoped decisions content is non-empty');
179
+ assert.ok(dbRequirementsContent.length > 0, 'DB-scoped requirements content is non-empty');
180
+ assert.ok(savingsPercent >= 30, `plan-slice savings ≥30% (actual: ${savingsPercent.toFixed(1)}%)`);
181
+ assert.ok(dbTotal < fullTotal * 0.70, `DB total (${dbTotal}) < 70% of full total (${fullTotal})`);
182
182
 
183
183
  // ── Verify correct scoping: decisions ──
184
184
  // M001 decisions: those with when_context containing 'M001' — indices 1,4,7,10,13,16,19,22
185
185
  // (24 decisions round-robin across M001/M002/M003 → 8 for M001)
186
- assertTrue(scopedDecisions.length === 8, `M001 decisions: expected 8, got ${scopedDecisions.length}`);
186
+ assert.ok(scopedDecisions.length === 8, `M001 decisions: expected 8, got ${scopedDecisions.length}`);
187
187
  for (const d of scopedDecisions) {
188
- assertTrue(d.when_context.includes('M001'), `decision ${d.id} should have M001 in when_context, got "${d.when_context}"`);
188
+ assert.ok(d.when_context.includes('M001'), `decision ${d.id} should have M001 in when_context, got "${d.when_context}"`);
189
189
  }
190
190
 
191
191
  // Verify NO decisions from other milestones leak in
192
192
  for (const d of scopedDecisions) {
193
- assertNoMatch(d.when_context, /M002|M003/, `decision ${d.id} should not contain M002 or M003`);
193
+ assert.doesNotMatch(d.when_context, /M002|M003/, `decision ${d.id} should not contain M002 or M003`);
194
194
  }
195
195
 
196
196
  // ── Verify correct scoping: requirements ──
197
197
  // S01 requirements: those assigned to S01 as primary_owner
198
198
  // S01 appears in positions 1,6,11,16,21 (5 assignments cycling, 21 reqs → indices 0,5,10,15,20)
199
- assertTrue(scopedRequirements.length > 0, 'S01 requirements non-empty');
199
+ assert.ok(scopedRequirements.length > 0, 'S01 requirements non-empty');
200
200
  for (const r of scopedRequirements) {
201
- assertTrue(
201
+ assert.ok(
202
202
  r.primary_owner.includes('S01') || r.supporting_slices.includes('S01'),
203
203
  `requirement ${r.id} should be owned by or support S01`,
204
204
  );
@@ -206,13 +206,13 @@ console.log('\n=== token-savings: plan-slice prompt ≥30% character savings ===
206
206
 
207
207
  // Verify specific expected IDs are present
208
208
  const scopedDecisionIds = scopedDecisions.map(d => d.id);
209
- assertTrue(scopedDecisionIds.includes('D001'), 'M001 scoped decisions includes D001');
210
- assertTrue(scopedDecisionIds.includes('D004'), 'M001 scoped decisions includes D004');
211
- assertTrue(!scopedDecisionIds.includes('D002'), 'M001 scoped decisions excludes D002 (M002)');
212
- assertTrue(!scopedDecisionIds.includes('D003'), 'M001 scoped decisions excludes D003 (M003)');
209
+ assert.ok(scopedDecisionIds.includes('D001'), 'M001 scoped decisions includes D001');
210
+ assert.ok(scopedDecisionIds.includes('D004'), 'M001 scoped decisions includes D004');
211
+ assert.ok(!scopedDecisionIds.includes('D002'), 'M001 scoped decisions excludes D002 (M002)');
212
+ assert.ok(!scopedDecisionIds.includes('D003'), 'M001 scoped decisions excludes D003 (M003)');
213
213
 
214
214
  const scopedReqIds = scopedRequirements.map(r => r.id);
215
- assertTrue(scopedReqIds.includes('R001'), 'S01 scoped requirements includes R001');
215
+ assert.ok(scopedReqIds.includes('R001'), 'S01 scoped requirements includes R001');
216
216
 
217
217
  closeDatabase();
218
218
  rmSync(base, { recursive: true, force: true });
@@ -246,9 +246,9 @@ console.log('\n=== token-savings: research-milestone prompt shows meaningful sav
246
246
  const decisionsSavings = ((fullDecisionsContent.length - dbDecisionsContent.length) / fullDecisionsContent.length) * 100;
247
247
  console.log(` Decisions savings (M001): ${decisionsSavings.toFixed(1)}% (DB: ${dbDecisionsContent.length}, full: ${fullDecisionsContent.length})`);
248
248
 
249
- assertTrue(decisionsSavings > 0, `decisions savings > 0% (actual: ${decisionsSavings.toFixed(1)}%)`);
250
- assertTrue(scopedDecisions.length === 8, `M001 decisions: 8 of 24 total`);
251
- assertTrue(allRequirements.length === REQUIREMENTS_COUNT, `all requirements returned: ${allRequirements.length}`);
249
+ assert.ok(decisionsSavings > 0, `decisions savings > 0% (actual: ${decisionsSavings.toFixed(1)}%)`);
250
+ assert.ok(scopedDecisions.length === 8, `M001 decisions: 8 of 24 total`);
251
+ assert.ok(allRequirements.length === REQUIREMENTS_COUNT, `all requirements returned: ${allRequirements.length}`);
252
252
 
253
253
  // Requirements: DB-formatted vs raw markdown — formatted output may differ in size
254
254
  // but decisions savings alone should make the composite meaningful
@@ -259,8 +259,8 @@ console.log('\n=== token-savings: research-milestone prompt shows meaningful sav
259
259
 
260
260
  // With 8/24 decisions = 66% reduction in decisions, even if requirements are equal,
261
261
  // the composite should show meaningful savings
262
- assertTrue(compositeSavings > 10, `research-milestone shows >10% composite savings (actual: ${compositeSavings.toFixed(1)}%)`);
263
- assertTrue(decisionsSavings >= 30, `decisions-only savings ≥30% for M001 scope (actual: ${decisionsSavings.toFixed(1)}%)`);
262
+ assert.ok(compositeSavings > 10, `research-milestone shows >10% composite savings (actual: ${compositeSavings.toFixed(1)}%)`);
263
+ assert.ok(decisionsSavings >= 30, `decisions-only savings ≥30% for M001 scope (actual: ${decisionsSavings.toFixed(1)}%)`);
264
264
 
265
265
  closeDatabase();
266
266
  rmSync(base, { recursive: true, force: true });
@@ -283,17 +283,17 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
283
283
 
284
284
  // ── M002-scoped decisions should not contain M001/M003 items ──
285
285
  const m002Decisions = queryDecisions({ milestoneId: 'M002' });
286
- assertTrue(m002Decisions.length === 8, `M002 decisions: expected 8, got ${m002Decisions.length}`);
286
+ assert.ok(m002Decisions.length === 8, `M002 decisions: expected 8, got ${m002Decisions.length}`);
287
287
  for (const d of m002Decisions) {
288
- assertTrue(d.when_context.includes('M002'), `M002 decision ${d.id} has M002 in when_context`);
289
- assertNoMatch(d.when_context, /M001|M003/, `M002 decision ${d.id} should not contain M001/M003`);
288
+ assert.ok(d.when_context.includes('M002'), `M002 decision ${d.id} has M002 in when_context`);
289
+ assert.doesNotMatch(d.when_context, /M001|M003/, `M002 decision ${d.id} should not contain M001/M003`);
290
290
  }
291
291
 
292
292
  // ── S04-scoped requirements should only include S04-related items ──
293
293
  const s04Requirements = queryRequirements({ sliceId: 'S04' });
294
- assertTrue(s04Requirements.length > 0, 'S04 requirements non-empty');
294
+ assert.ok(s04Requirements.length > 0, 'S04 requirements non-empty');
295
295
  for (const r of s04Requirements) {
296
- assertTrue(
296
+ assert.ok(
297
297
  r.primary_owner.includes('S04') || r.supporting_slices.includes('S04'),
298
298
  `S04 requirement ${r.id} should be owned by or support S04`,
299
299
  );
@@ -301,13 +301,13 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
301
301
 
302
302
  // ── Verify formatted output is well-formed and non-empty ──
303
303
  const formattedDecisions = formatDecisionsForPrompt(m002Decisions);
304
- assertTrue(formattedDecisions.length > 0, 'formatted M002 decisions is non-empty');
305
- assertMatch(formattedDecisions, /\| D/, 'formatted decisions contains decision rows');
306
- assertMatch(formattedDecisions, /\| # \|/, 'formatted decisions has table header');
304
+ assert.ok(formattedDecisions.length > 0, 'formatted M002 decisions is non-empty');
305
+ assert.match(formattedDecisions, /\| D/, 'formatted decisions contains decision rows');
306
+ assert.match(formattedDecisions, /\| # \|/, 'formatted decisions has table header');
307
307
 
308
308
  const formattedReqs = formatRequirementsForPrompt(s04Requirements);
309
- assertTrue(formattedReqs.length > 0, 'formatted S04 requirements is non-empty');
310
- assertMatch(formattedReqs, /### R\d+/, 'formatted requirements has requirement headings');
309
+ assert.ok(formattedReqs.length > 0, 'formatted S04 requirements is non-empty');
310
+ assert.match(formattedReqs, /### R\d+/, 'formatted requirements has requirement headings');
311
311
 
312
312
  // ── Verify all milestones have decisions and counts add up ──
313
313
  const m001Count = queryDecisions({ milestoneId: 'M001' }).length;
@@ -315,11 +315,11 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
315
315
  const m003Count = queryDecisions({ milestoneId: 'M003' }).length;
316
316
  const allCount = queryDecisions().length;
317
317
 
318
- assertTrue(m001Count === 8, `M001: 8 decisions (got ${m001Count})`);
319
- assertTrue(m002Count === 8, `M002: 8 decisions (got ${m002Count})`);
320
- assertTrue(m003Count === 8, `M003: 8 decisions (got ${m003Count})`);
321
- assertTrue(allCount === DECISIONS_COUNT, `all: ${DECISIONS_COUNT} decisions (got ${allCount})`);
322
- assertTrue(m001Count + m002Count + m003Count === allCount, 'milestone decision counts sum to total');
318
+ assert.ok(m001Count === 8, `M001: 8 decisions (got ${m001Count})`);
319
+ assert.ok(m002Count === 8, `M002: 8 decisions (got ${m002Count})`);
320
+ assert.ok(m003Count === 8, `M003: 8 decisions (got ${m003Count})`);
321
+ assert.ok(allCount === DECISIONS_COUNT, `all: ${DECISIONS_COUNT} decisions (got ${allCount})`);
322
+ assert.ok(m001Count + m002Count + m003Count === allCount, 'milestone decision counts sum to total');
323
323
 
324
324
  // ── Verify all slices have requirements ──
325
325
  const s01Reqs = queryRequirements({ sliceId: 'S01' });
@@ -328,11 +328,11 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
328
328
  const s04Reqs = queryRequirements({ sliceId: 'S04' });
329
329
  const s05Reqs = queryRequirements({ sliceId: 'S05' });
330
330
 
331
- assertTrue(s01Reqs.length > 0, 'S01 has requirements');
332
- assertTrue(s02Reqs.length > 0, 'S02 has requirements');
333
- assertTrue(s03Reqs.length > 0, 'S03 has requirements');
334
- assertTrue(s04Reqs.length > 0, 'S04 has requirements');
335
- assertTrue(s05Reqs.length > 0, 'S05 has requirements');
331
+ assert.ok(s01Reqs.length > 0, 'S01 has requirements');
332
+ assert.ok(s02Reqs.length > 0, 'S02 has requirements');
333
+ assert.ok(s03Reqs.length > 0, 'S03 has requirements');
334
+ assert.ok(s04Reqs.length > 0, 'S04 has requirements');
335
+ assert.ok(s05Reqs.length > 0, 'S05 has requirements');
336
336
 
337
337
  closeDatabase();
338
338
  rmSync(base, { recursive: true, force: true });
@@ -345,22 +345,20 @@ console.log('\n=== token-savings: quality — correct scoping, no cross-contamin
345
345
  console.log('\n=== token-savings: fixture data realism ===');
346
346
  {
347
347
  // Verify fixture generators produce sufficient volume
348
- assertTrue(DECISIONS_COUNT >= 20, `decisions count ≥ 20 (actual: ${DECISIONS_COUNT})`);
349
- assertTrue(REQUIREMENTS_COUNT >= 20, `requirements count ≥ 20 (actual: ${REQUIREMENTS_COUNT})`);
350
- assertTrue(MILESTONES.length >= 3, `milestones ≥ 3 (actual: ${MILESTONES.length})`);
351
- assertTrue(SLICE_ASSIGNMENTS.length >= 5, `slice assignments ≥ 5 (actual: ${SLICE_ASSIGNMENTS.length})`);
348
+ assert.ok(DECISIONS_COUNT >= 20, `decisions count ≥ 20 (actual: ${DECISIONS_COUNT})`);
349
+ assert.ok(REQUIREMENTS_COUNT >= 20, `requirements count ≥ 20 (actual: ${REQUIREMENTS_COUNT})`);
350
+ assert.ok(MILESTONES.length >= 3, `milestones ≥ 3 (actual: ${MILESTONES.length})`);
351
+ assert.ok(SLICE_ASSIGNMENTS.length >= 5, `slice assignments ≥ 5 (actual: ${SLICE_ASSIGNMENTS.length})`);
352
352
 
353
353
  // Verify markdown content is substantial
354
- assertTrue(decisionsMarkdown.length > 1000, `decisions markdown > 1000 chars (actual: ${decisionsMarkdown.length})`);
355
- assertTrue(requirementsMarkdown.length > 1000, `requirements markdown > 1000 chars (actual: ${requirementsMarkdown.length})`);
354
+ assert.ok(decisionsMarkdown.length > 1000, `decisions markdown > 1000 chars (actual: ${decisionsMarkdown.length})`);
355
+ assert.ok(requirementsMarkdown.length > 1000, `requirements markdown > 1000 chars (actual: ${requirementsMarkdown.length})`);
356
356
 
357
357
  // Verify content structure
358
- assertMatch(decisionsMarkdown, /\| D001 \|/, 'decisions markdown has D001');
359
- assertMatch(decisionsMarkdown, /\| D024 \|/, 'decisions markdown has D024');
360
- assertMatch(requirementsMarkdown, /### R001/, 'requirements markdown has R001');
361
- assertMatch(requirementsMarkdown, /### R021/, 'requirements markdown has R021');
358
+ assert.match(decisionsMarkdown, /\| D001 \|/, 'decisions markdown has D001');
359
+ assert.match(decisionsMarkdown, /\| D024 \|/, 'decisions markdown has D024');
360
+ assert.match(requirementsMarkdown, /### R001/, 'requirements markdown has R001');
361
+ assert.match(requirementsMarkdown, /### R021/, 'requirements markdown has R021');
362
362
  }
363
363
 
364
364
  // ─── Report ────────────────────────────────────────────────────────────────
365
-
366
- report();
@@ -3,7 +3,8 @@
3
3
  // Verifies that identical consecutive tool calls are detected and blocked
4
4
  // after exceeding the threshold, and that the guard resets properly.
5
5
 
6
- import { createTestContext } from './test-helpers.ts';
6
+ import { test } from 'node:test';
7
+ import assert from 'node:assert/strict';
7
8
  import {
8
9
  checkToolCallLoop,
9
10
  resetToolCallLoopGuard,
@@ -11,7 +12,6 @@ import {
11
12
  getToolCallLoopCount,
12
13
  } from '../bootstrap/tool-call-loop-guard.ts';
13
14
 
14
- const { assertEq, assertTrue, report } = createTestContext();
15
15
 
16
16
  // ═══════════════════════════════════════════════════════════════════════════
17
17
  // Allows first N calls, blocks after threshold
@@ -25,15 +25,15 @@ console.log('\n── Loop guard: blocks after threshold ──');
25
25
  // First 4 identical calls should be allowed (threshold is 4)
26
26
  for (let i = 1; i <= 4; i++) {
27
27
  const result = checkToolCallLoop('web_search', { query: 'same query' });
28
- assertTrue(result.block === false, `Call ${i} should be allowed`);
29
- assertEq(result.count, i, `Count should be ${i} after call ${i}`);
28
+ assert.ok(result.block === false, `Call ${i} should be allowed`);
29
+ assert.deepStrictEqual(result.count, i, `Count should be ${i} after call ${i}`);
30
30
  }
31
31
 
32
32
  // 5th identical call should be blocked
33
33
  const blocked = checkToolCallLoop('web_search', { query: 'same query' });
34
- assertTrue(blocked.block === true, '5th identical call should be blocked');
35
- assertTrue(blocked.reason!.includes('web_search'), 'Reason should mention tool name');
36
- assertTrue(blocked.reason!.includes('5'), 'Reason should mention count');
34
+ assert.ok(blocked.block === true, '5th identical call should be blocked');
35
+ assert.ok(blocked.reason!.includes('web_search'), 'Reason should mention tool name');
36
+ assert.ok(blocked.reason!.includes('5'), 'Reason should mention count');
37
37
  }
38
38
 
39
39
  // ═══════════════════════════════════════════════════════════════════════════
@@ -48,17 +48,17 @@ console.log('\n── Loop guard: different calls reset streak ──');
48
48
  checkToolCallLoop('web_search', { query: 'query A' });
49
49
  checkToolCallLoop('web_search', { query: 'query A' });
50
50
  checkToolCallLoop('web_search', { query: 'query A' });
51
- assertEq(getToolCallLoopCount(), 3, 'Count should be 3 after 3 identical calls');
51
+ assert.deepStrictEqual(getToolCallLoopCount(), 3, 'Count should be 3 after 3 identical calls');
52
52
 
53
53
  // A different call resets the streak
54
54
  const different = checkToolCallLoop('bash', { command: 'ls' });
55
- assertTrue(different.block === false, 'Different tool call should be allowed');
56
- assertEq(getToolCallLoopCount(), 1, 'Count should reset to 1 after different call');
55
+ assert.ok(different.block === false, 'Different tool call should be allowed');
56
+ assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Count should reset to 1 after different call');
57
57
 
58
58
  // Same tool but different args also resets
59
59
  checkToolCallLoop('web_search', { query: 'query A' });
60
60
  checkToolCallLoop('web_search', { query: 'query B' }); // different args
61
- assertEq(getToolCallLoopCount(), 1, 'Different args should reset count');
61
+ assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Different args should reset count');
62
62
  }
63
63
 
64
64
  // ═══════════════════════════════════════════════════════════════════════════
@@ -72,15 +72,15 @@ console.log('\n── Loop guard: reset clears state ──');
72
72
  checkToolCallLoop('web_search', { query: 'q' });
73
73
  checkToolCallLoop('web_search', { query: 'q' });
74
74
  checkToolCallLoop('web_search', { query: 'q' });
75
- assertEq(getToolCallLoopCount(), 3, 'Count should be 3 before reset');
75
+ assert.deepStrictEqual(getToolCallLoopCount(), 3, 'Count should be 3 before reset');
76
76
 
77
77
  resetToolCallLoopGuard();
78
- assertEq(getToolCallLoopCount(), 0, 'Count should be 0 after reset');
78
+ assert.deepStrictEqual(getToolCallLoopCount(), 0, 'Count should be 0 after reset');
79
79
 
80
80
  // After reset, the same call starts fresh
81
81
  const result = checkToolCallLoop('web_search', { query: 'q' });
82
- assertTrue(result.block === false, 'Call after reset should be allowed');
83
- assertEq(getToolCallLoopCount(), 1, 'Count should be 1 after first call post-reset');
82
+ assert.ok(result.block === false, 'Call after reset should be allowed');
83
+ assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Count should be 1 after first call post-reset');
84
84
  }
85
85
 
86
86
  // ═══════════════════════════════════════════════════════════════════════════
@@ -94,13 +94,13 @@ console.log('\n── Loop guard: disable allows everything ──');
94
94
 
95
95
  for (let i = 0; i < 10; i++) {
96
96
  const result = checkToolCallLoop('web_search', { query: 'same' });
97
- assertTrue(result.block === false, `Call ${i + 1} should be allowed when disabled`);
97
+ assert.ok(result.block === false, `Call ${i + 1} should be allowed when disabled`);
98
98
  }
99
99
 
100
100
  // Re-enable via reset
101
101
  resetToolCallLoopGuard();
102
102
  checkToolCallLoop('web_search', { query: 'q' });
103
- assertEq(getToolCallLoopCount(), 1, 'Guard should be active again after reset');
103
+ assert.deepStrictEqual(getToolCallLoopCount(), 1, 'Guard should be active again after reset');
104
104
  }
105
105
 
106
106
  // ═══════════════════════════════════════════════════════════════════════════
@@ -114,8 +114,8 @@ console.log('\n── Loop guard: arg order is normalized ──');
114
114
 
115
115
  checkToolCallLoop('web_search', { query: 'test', limit: 5 });
116
116
  const result = checkToolCallLoop('web_search', { limit: 5, query: 'test' }); // same args, different order
117
- assertTrue(result.block === false, 'Same args in different order should count as consecutive');
118
- assertEq(getToolCallLoopCount(), 2, 'Should detect as same call regardless of key order');
117
+ assert.ok(result.block === false, 'Same args in different order should count as consecutive');
118
+ assert.deepStrictEqual(getToolCallLoopCount(), 2, 'Should detect as same call regardless of key order');
119
119
  }
120
120
 
121
121
  // ═══════════════════════════════════════════════════════════════════════════
@@ -132,8 +132,8 @@ console.log('\n── Loop guard: nested args are not stripped ──');
132
132
  const result = checkToolCallLoop('ask_user_questions', {
133
133
  questions: [{ id: `q${i}`, question: `Question ${i}?` }],
134
134
  });
135
- assertTrue(result.block === false, `Nested call ${i} with unique content should be allowed`);
136
- assertEq(getToolCallLoopCount(), 1, `Each unique nested call should reset count to 1`);
135
+ assert.ok(result.block === false, `Nested call ${i} with unique content should be allowed`);
136
+ assert.deepStrictEqual(getToolCallLoopCount(), 1, `Each unique nested call should reset count to 1`);
137
137
  }
138
138
 
139
139
  // Truly identical nested calls should still be detected
@@ -146,7 +146,7 @@ console.log('\n── Loop guard: nested args are not stripped ──');
146
146
  const blocked = checkToolCallLoop('ask_user_questions', {
147
147
  questions: [{ id: 'same', question: 'Same?' }],
148
148
  });
149
- assertTrue(blocked.block === true, 'Identical nested calls should still be blocked');
149
+ assert.ok(blocked.block === true, 'Identical nested calls should still be blocked');
150
150
  }
151
151
 
152
152
  // ═══════════════════════════════════════════════════════════════════════════
@@ -160,9 +160,7 @@ console.log('\n── Loop guard: nested key order is normalized ──');
160
160
 
161
161
  checkToolCallLoop('tool', { outer: { b: 2, a: 1 } });
162
162
  const result = checkToolCallLoop('tool', { outer: { a: 1, b: 2 } });
163
- assertEq(getToolCallLoopCount(), 2, 'Same nested args in different key order should match');
163
+ assert.deepStrictEqual(getToolCallLoopCount(), 2, 'Same nested args in different key order should match');
164
164
  }
165
165
 
166
166
  // ═══════════════════════════════════════════════════════════════════════════
167
-
168
- report();
@@ -4,10 +4,10 @@
4
4
  // AND under a backward-compatible alias name.
5
5
  // The alias must share the exact same execute function reference as the canonical tool.
6
6
 
7
- import { createTestContext } from './test-helpers.ts';
7
+ import { test } from 'node:test';
8
+ import assert from 'node:assert/strict';
8
9
  import { registerDbTools } from '../bootstrap/db-tools.ts';
9
10
 
10
- const { assertEq, assertTrue, report } = createTestContext();
11
11
 
12
12
  // ─── Mock PI ──────────────────────────────────────────────────────────────────
13
13
 
@@ -34,6 +34,7 @@ const RENAME_MAP: Array<{ canonical: string; alias: string }> = [
34
34
  { canonical: "gsd_replan_slice", alias: "gsd_slice_replan" },
35
35
  { canonical: "gsd_reassess_roadmap", alias: "gsd_roadmap_reassess" },
36
36
  { canonical: "gsd_complete_milestone", alias: "gsd_milestone_complete" },
37
+ { canonical: "gsd_validate_milestone", alias: "gsd_milestone_validate" },
37
38
  ];
38
39
 
39
40
  // ─── Registration count ──────────────────────────────────────────────────────
@@ -43,7 +44,7 @@ console.log('\n── Tool naming: registration count ──');
43
44
  const pi = makeMockPi();
44
45
  registerDbTools(pi);
45
46
 
46
- assertEq(pi.tools.length, 24, 'Should register exactly 24 tools (12 canonical + 12 aliases)');
47
+ assert.deepStrictEqual(pi.tools.length, 26, 'Should register exactly 26 tools (13 canonical + 13 aliases)');
47
48
 
48
49
  // ─── Both names exist for each pair ──────────────────────────────────────────
49
50
 
@@ -53,8 +54,8 @@ for (const { canonical, alias } of RENAME_MAP) {
53
54
  const canonicalTool = pi.tools.find((t: any) => t.name === canonical);
54
55
  const aliasTool = pi.tools.find((t: any) => t.name === alias);
55
56
 
56
- assertTrue(canonicalTool !== undefined, `Canonical tool "${canonical}" should be registered`);
57
- assertTrue(aliasTool !== undefined, `Alias tool "${alias}" should be registered`);
57
+ assert.ok(canonicalTool !== undefined, `Canonical tool "${canonical}" should be registered`);
58
+ assert.ok(aliasTool !== undefined, `Alias tool "${alias}" should be registered`);
58
59
  }
59
60
 
60
61
  // ─── Execute function identity ───────────────────────────────────────────────
@@ -66,7 +67,7 @@ for (const { canonical, alias } of RENAME_MAP) {
66
67
  const aliasTool = pi.tools.find((t: any) => t.name === alias);
67
68
 
68
69
  if (canonicalTool && aliasTool) {
69
- assertTrue(
70
+ assert.ok(
70
71
  canonicalTool.execute === aliasTool.execute,
71
72
  `"${canonical}" and "${alias}" should share the same execute function reference`,
72
73
  );
@@ -81,7 +82,7 @@ for (const { canonical, alias } of RENAME_MAP) {
81
82
  const aliasTool = pi.tools.find((t: any) => t.name === alias);
82
83
 
83
84
  if (aliasTool) {
84
- assertTrue(
85
+ assert.ok(
85
86
  aliasTool.description.includes(`alias for ${canonical}`),
86
87
  `Alias "${alias}" description should include "alias for ${canonical}"`,
87
88
  );
@@ -97,7 +98,7 @@ for (const { canonical } of RENAME_MAP) {
97
98
 
98
99
  if (canonicalTool) {
99
100
  const guidelinesText = canonicalTool.promptGuidelines.join(' ');
100
- assertTrue(
101
+ assert.ok(
101
102
  guidelinesText.includes(canonical),
102
103
  `Canonical tool "${canonical}" promptGuidelines should reference its own name`,
103
104
  );
@@ -113,7 +114,7 @@ for (const { canonical, alias } of RENAME_MAP) {
113
114
 
114
115
  if (aliasTool) {
115
116
  const guidelinesText = aliasTool.promptGuidelines.join(' ');
116
- assertTrue(
117
+ assert.ok(
117
118
  guidelinesText.includes(`Alias for ${canonical}`),
118
119
  `Alias "${alias}" promptGuidelines should say "Alias for ${canonical}"`,
119
120
  );
@@ -121,5 +122,3 @@ for (const { canonical, alias } of RENAME_MAP) {
121
122
  }
122
123
 
123
124
  // ═══════════════════════════════════════════════════════════════════════════
124
-
125
- report();