gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.84a383f51

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 (383) hide show
  1. package/README.md +7 -7
  2. package/dist/claude-cli-check.js +64 -37
  3. package/dist/cli-policy.d.ts +13 -0
  4. package/dist/cli-policy.js +17 -0
  5. package/dist/cli.js +95 -55
  6. package/dist/headless-query.d.ts +22 -0
  7. package/dist/headless-query.js +24 -4
  8. package/dist/headless.d.ts +10 -0
  9. package/dist/headless.js +16 -1
  10. package/dist/loader.js +7 -10
  11. package/dist/onboarding.d.ts +10 -0
  12. package/dist/onboarding.js +2 -2
  13. package/dist/provider-migrations.d.ts +2 -2
  14. package/dist/provider-migrations.js +5 -2
  15. package/dist/resource-loader.d.ts +5 -2
  16. package/dist/resource-loader.js +28 -5
  17. package/dist/resources/.managed-resources-content-hash +1 -0
  18. package/dist/resources/extensions/claude-code-cli/readiness.js +77 -45
  19. package/dist/resources/extensions/gsd/auto/loop.js +23 -0
  20. package/dist/resources/extensions/gsd/auto/phases.js +2 -2
  21. package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
  22. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  23. package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
  24. package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
  25. package/dist/resources/extensions/gsd/auto-start.js +1 -1
  26. package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
  27. package/dist/resources/extensions/gsd/auto-worktree.js +30 -0
  28. package/dist/resources/extensions/gsd/auto.js +14 -5
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
  30. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
  31. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
  32. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
  33. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +94 -31
  34. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
  35. package/dist/resources/extensions/gsd/bootstrap/system-context.js +34 -8
  36. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -2
  37. package/dist/resources/extensions/gsd/commands/catalog.js +69 -5
  38. package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
  39. package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
  40. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
  41. package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
  42. package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -0
  43. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
  44. package/dist/resources/extensions/gsd/error-classifier.js +1 -1
  45. package/dist/resources/extensions/gsd/forensics.js +2 -2
  46. package/dist/resources/extensions/gsd/git-service.js +12 -5
  47. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  48. package/dist/resources/extensions/gsd/guided-flow.js +23 -23
  49. package/dist/resources/extensions/gsd/memory-store.js +66 -31
  50. package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
  51. package/dist/resources/extensions/gsd/model-router.js +114 -9
  52. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
  53. package/dist/resources/extensions/gsd/preferences-models.js +91 -15
  54. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  55. package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
  56. package/dist/resources/extensions/gsd/preferences.js +5 -3
  57. package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
  58. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
  59. package/dist/resources/extensions/gsd/state.js +42 -0
  60. package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  61. package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
  62. package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
  63. package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
  64. package/dist/resources/extensions/gsd/worktree-command.js +26 -46
  65. package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
  66. package/dist/resources/extensions/mcp-client/index.js +6 -3
  67. package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
  68. package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
  69. package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  70. package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  71. package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  72. package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  73. package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  74. package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  75. package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  76. package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  77. package/dist/rtk-shared.d.ts +3 -0
  78. package/dist/rtk-shared.js +17 -0
  79. package/dist/rtk.d.ts +2 -5
  80. package/dist/rtk.js +3 -20
  81. package/dist/runtime-checks.d.ts +27 -0
  82. package/dist/runtime-checks.js +38 -0
  83. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  84. package/dist/web/standalone/.next/BUILD_ID +1 -1
  85. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  86. package/dist/web/standalone/.next/build-manifest.json +3 -3
  87. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  88. package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
  89. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
  110. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/index.html +1 -1
  112. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  113. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  114. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  115. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  120. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  122. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  124. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  125. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  126. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  127. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
  128. package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
  129. package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
  130. package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
  131. package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
  132. package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
  133. package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
  134. package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
  135. package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
  136. package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
  137. package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
  138. package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
  139. package/dist/web/standalone/package.json +2 -1
  140. package/dist/worktree-status-banner.d.ts +1 -0
  141. package/dist/worktree-status-banner.js +132 -0
  142. package/package.json +1 -1
  143. package/packages/daemon/package.json +2 -2
  144. package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
  145. package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
  146. package/packages/mcp-server/dist/alias-telemetry.js +30 -0
  147. package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
  148. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  149. package/packages/mcp-server/dist/workflow-tools.js +74 -46
  150. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  151. package/packages/mcp-server/package.json +2 -2
  152. package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
  153. package/packages/mcp-server/src/alias-telemetry.ts +30 -0
  154. package/packages/mcp-server/src/workflow-tools.test.ts +26 -0
  155. package/packages/mcp-server/src/workflow-tools.ts +93 -58
  156. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  157. package/packages/native/package.json +1 -1
  158. package/packages/pi-agent-core/package.json +1 -1
  159. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  160. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
  161. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
  162. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
  163. package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
  164. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  165. package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
  166. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  167. package/packages/pi-ai/dist/types.d.ts +13 -0
  168. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  169. package/packages/pi-ai/dist/types.js.map +1 -1
  170. package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
  171. package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
  172. package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
  173. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
  174. package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
  175. package/packages/pi-ai/package.json +1 -1
  176. package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
  177. package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
  178. package/packages/pi-ai/src/types.ts +13 -0
  179. package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
  180. package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
  181. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  182. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
  184. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
  186. package/packages/pi-coding-agent/dist/core/messages.js +4 -0
  187. package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
  189. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  190. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
  191. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
  193. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
  195. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
  197. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  198. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
  199. package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
  201. package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
  202. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
  203. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
  204. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
  205. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
  206. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
  207. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
  209. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
  211. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
  212. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
  213. package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
  214. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
  215. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
  216. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
  217. package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
  218. package/packages/pi-coding-agent/package.json +1 -1
  219. package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
  220. package/packages/pi-coding-agent/src/core/messages.ts +4 -0
  221. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
  222. package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
  223. package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
  224. package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
  225. package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
  226. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
  227. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
  228. package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
  229. package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
  230. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  231. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
  232. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  233. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
  234. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
  235. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
  236. package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
  237. package/packages/pi-tui/package.json +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
  239. package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
  240. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  241. package/packages/rpc-client/package.json +1 -1
  242. package/pkg/package.json +1 -1
  243. package/src/resources/extensions/claude-code-cli/readiness.ts +78 -46
  244. package/src/resources/extensions/gsd/auto/loop.ts +24 -2
  245. package/src/resources/extensions/gsd/auto/phases.ts +3 -3
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
  247. package/src/resources/extensions/gsd/auto/session.ts +3 -0
  248. package/src/resources/extensions/gsd/auto/types.ts +1 -0
  249. package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
  250. package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
  251. package/src/resources/extensions/gsd/auto-start.ts +1 -1
  252. package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
  253. package/src/resources/extensions/gsd/auto-worktree.ts +38 -0
  254. package/src/resources/extensions/gsd/auto.ts +14 -4
  255. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
  256. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
  257. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
  258. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
  259. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +102 -31
  260. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
  261. package/src/resources/extensions/gsd/bootstrap/system-context.ts +39 -8
  262. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +39 -11
  263. package/src/resources/extensions/gsd/commands/catalog.ts +75 -5
  264. package/src/resources/extensions/gsd/commands/handlers/core.ts +22 -1
  265. package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
  266. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
  267. package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
  268. package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -0
  269. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
  270. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  271. package/src/resources/extensions/gsd/error-classifier.ts +1 -1
  272. package/src/resources/extensions/gsd/forensics.ts +2 -2
  273. package/src/resources/extensions/gsd/git-service.ts +13 -5
  274. package/src/resources/extensions/gsd/gsd-db.ts +12 -2
  275. package/src/resources/extensions/gsd/guided-flow.ts +25 -25
  276. package/src/resources/extensions/gsd/memory-store.ts +81 -28
  277. package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
  278. package/src/resources/extensions/gsd/model-router.ts +172 -9
  279. package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
  280. package/src/resources/extensions/gsd/preferences-models.ts +101 -15
  281. package/src/resources/extensions/gsd/preferences-types.ts +6 -0
  282. package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
  283. package/src/resources/extensions/gsd/preferences.ts +16 -2
  284. package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
  285. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
  286. package/src/resources/extensions/gsd/state.ts +42 -0
  287. package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
  288. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
  289. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
  290. package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +9 -5
  291. package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
  292. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
  293. package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
  294. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
  295. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
  296. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
  297. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
  298. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
  299. package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
  300. package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
  301. package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
  302. package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
  303. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
  304. package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
  305. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
  306. package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
  307. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
  308. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
  309. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
  310. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
  311. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
  312. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
  313. package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
  314. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
  315. package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
  316. package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
  317. package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
  318. package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
  319. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -0
  320. package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
  321. package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
  322. package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
  323. package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
  324. package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
  325. package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
  326. package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
  327. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
  328. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
  329. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
  330. package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
  331. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
  332. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
  334. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
  335. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
  336. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
  337. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
  338. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
  339. package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
  340. package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
  341. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
  342. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
  343. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
  344. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
  345. package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
  346. package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
  347. package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
  348. package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
  349. package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
  350. package/src/resources/extensions/gsd/worktree-command.ts +31 -44
  351. package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
  352. package/src/resources/extensions/mcp-client/index.ts +6 -3
  353. package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
  354. package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
  355. package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
  356. package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
  357. package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
  358. package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
  359. package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
  360. package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
  361. package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
  362. package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
  363. package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
  364. package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
  365. package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
  366. package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
  367. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
  368. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
  369. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
  370. package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
  371. package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
  372. package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
  373. package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
  374. package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
  375. package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
  376. package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
  377. package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
  378. package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
  379. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
  380. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
  381. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
  382. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_buildManifest.js +0 -0
  383. /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_ssgManifest.js +0 -0
@@ -1,220 +1,147 @@
1
1
  /**
2
- * Budget Prediction — unit tests for M004/S04.
2
+ * budget-prediction.test.ts — unit tests for budget prediction math
3
+ * (`getAverageCostPerUnitType`, `predictRemainingCost`) exported from
4
+ * `metrics.ts`.
3
5
  *
4
- * Tests prediction math, auto-downgrade logic, and dashboard integration.
5
- * Uses extracted pure functions (avoiding module import chain) and
6
- * source-level structural checks for dashboard/auto.ts integration.
6
+ * The previous version (PR #582, `972dd05f4`) re-implemented both
7
+ * functions inline, citing a `paths.js @gsd/pi-coding-agent` import
8
+ * chain as the reason. That chain concern is no longer valid — the
9
+ * real exports import cleanly from `metrics.ts`. All 10 math tests
10
+ * were then exercising the test's own copy of the functions, not the
11
+ * product code. #4840 documented the false-coverage case.
12
+ *
13
+ * This rewrite imports the real functions, deletes the inline copies,
14
+ * drops the 3 source-grep tests (`metricsSrc.includes(...)`) and the
15
+ * 2 dashboard `includes` tests (dashboard integration should be a
16
+ * behaviour test against the dashboard builder, not a grep), and
17
+ * deletes the synthesized `downgrade:` test that exercised a local
18
+ * closure instead of product code. See #4784 / #4840.
7
19
  */
8
20
 
9
- import test from "node:test";
21
+ import { describe, test } from "node:test";
10
22
  import assert from "node:assert/strict";
11
- import { readFileSync } from "node:fs";
12
- import { join, dirname } from "node:path";
13
- import { fileURLToPath } from "node:url";
14
-
15
- const __dirname = dirname(fileURLToPath(import.meta.url));
16
- const metricsSrc = readFileSync(join(__dirname, "..", "metrics.ts"), "utf-8");
17
- const dashboardSrc = readFileSync(join(__dirname, "..", "auto-dashboard.ts"), "utf-8");
18
-
19
- // ─── Extract pure functions from metrics.ts source ────────────────────────
20
- // Can't import directly due to paths.js → @gsd/pi-coding-agent import chain.
21
- // Extract and evaluate the pure math functions.
22
-
23
- interface MockUnitMetrics {
24
- type: string;
25
- cost: number;
26
- }
27
-
28
- // Re-implement the functions under test (verified against source below)
29
- function getAverageCostPerUnitType(units: MockUnitMetrics[]): Map<string, number> {
30
- const sums = new Map<string, { total: number; count: number }>();
31
- for (const u of units) {
32
- const entry = sums.get(u.type) ?? { total: 0, count: 0 };
33
- entry.total += u.cost;
34
- entry.count += 1;
35
- sums.set(u.type, entry);
36
- }
37
- const avgs = new Map<string, number>();
38
- for (const [type, { total, count }] of sums) {
39
- avgs.set(type, total / count);
40
- }
41
- return avgs;
42
- }
43
23
 
44
- function predictRemainingCost(
45
- avgCosts: Map<string, number>,
46
- remainingUnits: string[],
47
- fallbackAvg?: number,
48
- ): number {
49
- const allAvgs = [...avgCosts.values()];
50
- const overallAvg = fallbackAvg ?? (allAvgs.length > 0 ? allAvgs.reduce((a, b) => a + b, 0) / allAvgs.length : 0);
51
- let total = 0;
52
- for (const unitType of remainingUnits) {
53
- total += avgCosts.get(unitType) ?? overallAvg;
54
- }
55
- return total;
24
+ import {
25
+ getAverageCostPerUnitType,
26
+ predictRemainingCost,
27
+ } from "../metrics.ts";
28
+ import type { UnitMetrics } from "../metrics.ts";
29
+
30
+ // ─── Fixture helper ───────────────────────────────────────────────────────
31
+ // UnitMetrics has several required fields; this builder lets each test
32
+ // specify only the fields that matter for the cost math.
33
+
34
+ function makeUnit(partial: Partial<UnitMetrics> & { type: string; cost: number }): UnitMetrics {
35
+ return {
36
+ id: "M001/S01/T01",
37
+ model: "test-model",
38
+ startedAt: 0,
39
+ finishedAt: 0,
40
+ tokens: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
41
+ toolCalls: 0,
42
+ assistantMessages: 0,
43
+ userMessages: 0,
44
+ ...partial,
45
+ };
56
46
  }
57
47
 
58
- // ═══════════════════════════════════════════════════════════════════════════
59
- // Source Verification confirm our re-implementation matches
60
- // ═══════════════════════════════════════════════════════════════════════════
61
-
62
- test("source: metrics.ts exports getAverageCostPerUnitType", () => {
63
- assert.ok(metricsSrc.includes("export function getAverageCostPerUnitType"), "should be exported");
64
- });
65
-
66
- test("source: metrics.ts exports predictRemainingCost", () => {
67
- assert.ok(metricsSrc.includes("export function predictRemainingCost"), "should be exported");
68
- });
69
-
70
- test("source: getAverageCostPerUnitType uses Map<string, number>", () => {
71
- assert.ok(
72
- metricsSrc.includes("Map<string, number>") && metricsSrc.includes("getAverageCostPerUnitType"),
73
- "should return Map<string, number>",
74
- );
75
- });
76
-
77
- // ═══════════════════════════════════════════════════════════════════════════
78
- // Average Cost Per Unit Type
79
- // ═══════════════════════════════════════════════════════════════════════════
80
-
81
- test("avgCost: returns correct averages per unit type", () => {
82
- const units: MockUnitMetrics[] = [
83
- { type: "execute-task", cost: 0.10 },
84
- { type: "execute-task", cost: 0.20 },
85
- { type: "plan-slice", cost: 0.05 },
86
- { type: "plan-slice", cost: 0.15 },
87
- { type: "complete-slice", cost: 0.08 },
88
- ];
89
- const avgs = getAverageCostPerUnitType(units);
90
- assert.ok(Math.abs(avgs.get("execute-task")! - 0.15) < 0.001, "execute-task avg should be 0.15");
91
- assert.ok(Math.abs(avgs.get("plan-slice")! - 0.10) < 0.001, "plan-slice avg should be 0.10");
92
- assert.ok(Math.abs(avgs.get("complete-slice")! - 0.08) < 0.001, "complete-slice avg should be 0.08");
93
- });
94
-
95
- test("avgCost: returns empty map for empty input", () => {
96
- const avgs = getAverageCostPerUnitType([]);
97
- assert.equal(avgs.size, 0);
98
- });
99
-
100
- test("avgCost: single unit per type returns exact cost", () => {
101
- const avgs = getAverageCostPerUnitType([{ type: "execute-task", cost: 0.42 }]);
102
- assert.ok(Math.abs(avgs.get("execute-task")! - 0.42) < 0.001);
103
- });
104
-
105
- // ═══════════════════════════════════════════════════════════════════════════
106
- // Predict Remaining Cost
107
- // ═══════════════════════════════════════════════════════════════════════════
108
-
109
- test("predict: calculates remaining cost from averages", () => {
110
- const avgs = new Map([
111
- ["execute-task", 0.15],
112
- ["plan-slice", 0.10],
113
- ["complete-slice", 0.08],
114
- ]);
115
- const remaining = ["execute-task", "execute-task", "complete-slice"];
116
- const cost = predictRemainingCost(avgs, remaining);
117
- assert.ok(Math.abs(cost - 0.38) < 0.001);
118
- });
119
-
120
- test("predict: uses overall average for unknown unit types", () => {
121
- const avgs = new Map([
122
- ["execute-task", 0.10],
123
- ["plan-slice", 0.20],
124
- ]);
125
- const remaining = ["execute-task", "unknown-type"];
126
- const cost = predictRemainingCost(avgs, remaining);
127
- // unknown: (0.10 + 0.20) / 2 = 0.15 → total 0.10 + 0.15 = 0.25
128
- assert.ok(Math.abs(cost - 0.25) < 0.001);
129
- });
130
-
131
- test("predict: returns 0 for empty remaining", () => {
132
- const avgs = new Map([["execute-task", 0.15]]);
133
- assert.equal(predictRemainingCost(avgs, []), 0);
134
- });
135
-
136
- test("predict: handles no averages with fallback", () => {
137
- const avgs = new Map<string, number>();
138
- const cost = predictRemainingCost(avgs, ["execute-task", "plan-slice"], 0.10);
139
- assert.ok(Math.abs(cost - 0.20) < 0.001);
140
- });
141
-
142
- test("predict: handles no averages and no fallback", () => {
143
- const avgs = new Map<string, number>();
144
- const cost = predictRemainingCost(avgs, ["execute-task"]);
145
- assert.equal(cost, 0);
146
- });
147
-
148
- // ═══════════════════════════════════════════════════════════════════════════
149
- // Dashboard Integration
150
- // ═══════════════════════════════════════════════════════════════════════════
151
-
152
- test("dashboard: AutoDashboardData includes projectedRemainingCost field", () => {
153
- assert.ok(
154
- dashboardSrc.includes("projectedRemainingCost"),
155
- "AutoDashboardData should have projectedRemainingCost field",
156
- );
157
- });
158
-
159
- test("dashboard: AutoDashboardData includes profileDowngraded field", () => {
160
- assert.ok(
161
- dashboardSrc.includes("profileDowngraded"),
162
- "AutoDashboardData should have profileDowngraded field",
163
- );
164
- });
165
-
166
- // ═══════════════════════════════════════════════════════════════════════════
167
- // Budget Prediction — End-to-End Math
168
- // ═══════════════════════════════════════════════════════════════════════════
169
-
170
- test("e2e: budget ceiling exceeded triggers downgrade prediction", () => {
171
- const units: MockUnitMetrics[] = [
172
- { type: "execute-task", cost: 0.50 },
173
- { type: "execute-task", cost: 0.60 },
174
- { type: "plan-slice", cost: 0.30 },
175
- { type: "complete-slice", cost: 0.20 },
176
- ];
177
- const totalSpent = units.reduce((sum, u) => sum + u.cost, 0); // 1.60
178
- const avgs = getAverageCostPerUnitType(units);
179
- const remaining = ["execute-task", "execute-task", "execute-task"];
180
- const predictedRemaining = predictRemainingCost(avgs, remaining);
181
- const predictedTotal = totalSpent + predictedRemaining;
182
- const budgetCeiling = 2.50;
183
- assert.ok(predictedTotal > budgetCeiling, "should predict budget exhaustion");
184
- });
185
-
186
- test("e2e: budget ceiling not exceeded does not trigger", () => {
187
- const units: MockUnitMetrics[] = [
188
- { type: "execute-task", cost: 0.10 },
189
- { type: "plan-slice", cost: 0.05 },
190
- ];
191
- const totalSpent = units.reduce((sum, u) => sum + u.cost, 0); // 0.15
192
- const avgs = getAverageCostPerUnitType(units);
193
- const remaining = ["execute-task", "complete-slice"];
194
- const predictedRemaining = predictRemainingCost(avgs, remaining);
195
- const predictedTotal = totalSpent + predictedRemaining;
196
- const budgetCeiling = 5.00;
197
- assert.ok(predictedTotal <= budgetCeiling, "should not predict budget exhaustion");
198
- });
199
-
200
- // ═══════════════════════════════════════════════════════════════════════════
201
- // Downgrade Logic
202
- // ═══════════════════════════════════════════════════════════════════════════
203
-
204
- test("downgrade: one-way per D048 — downgrade should not be reversible", () => {
205
- // Simulate: first prediction triggers downgrade, second doesn't reverse it
206
- let downgraded = false;
207
-
208
- function checkDowngrade(predictedTotal: number, ceiling: number) {
209
- if (!downgraded && predictedTotal > ceiling) {
210
- downgraded = true;
211
- }
212
- // Never reverse — per D048
213
- }
214
-
215
- checkDowngrade(3.00, 2.50); // triggers
216
- assert.ok(downgraded, "should downgrade when prediction exceeds ceiling");
217
-
218
- checkDowngrade(1.50, 2.50); // doesn't reverse
219
- assert.ok(downgraded, "should stay downgraded (one-way per D048)");
48
+ describe("getAverageCostPerUnitType (metrics.ts)", () => {
49
+ test("returns correct averages per unit type", () => {
50
+ const units = [
51
+ makeUnit({ type: "execute-task", cost: 0.1 }),
52
+ makeUnit({ type: "execute-task", cost: 0.2 }),
53
+ makeUnit({ type: "plan-slice", cost: 0.05 }),
54
+ makeUnit({ type: "plan-slice", cost: 0.15 }),
55
+ makeUnit({ type: "complete-slice", cost: 0.08 }),
56
+ ];
57
+ const avgs = getAverageCostPerUnitType(units);
58
+ assert.ok(Math.abs(avgs.get("execute-task")! - 0.15) < 0.001, "execute-task avg");
59
+ assert.ok(Math.abs(avgs.get("plan-slice")! - 0.10) < 0.001, "plan-slice avg");
60
+ assert.ok(Math.abs(avgs.get("complete-slice")! - 0.08) < 0.001, "complete-slice avg");
61
+ });
62
+
63
+ test("returns empty map for empty input", () => {
64
+ assert.equal(getAverageCostPerUnitType([]).size, 0);
65
+ });
66
+
67
+ test("single unit per type returns exact cost", () => {
68
+ const avgs = getAverageCostPerUnitType([makeUnit({ type: "execute-task", cost: 0.42 })]);
69
+ assert.ok(Math.abs(avgs.get("execute-task")! - 0.42) < 0.001);
70
+ });
71
+ });
72
+
73
+ describe("predictRemainingCost (metrics.ts)", () => {
74
+ test("calculates remaining cost from known averages", () => {
75
+ const avgs = new Map([
76
+ ["execute-task", 0.15],
77
+ ["plan-slice", 0.10],
78
+ ["complete-slice", 0.08],
79
+ ]);
80
+ const remaining = ["execute-task", "execute-task", "complete-slice"];
81
+ const cost = predictRemainingCost(avgs, remaining);
82
+ // 2 × 0.15 + 0.08 = 0.38
83
+ assert.ok(Math.abs(cost - 0.38) < 0.001);
84
+ });
85
+
86
+ test("uses overall average for unknown unit types", () => {
87
+ const avgs = new Map([
88
+ ["execute-task", 0.10],
89
+ ["plan-slice", 0.20],
90
+ ]);
91
+ const remaining = ["execute-task", "unknown-type"];
92
+ // unknown: (0.10 + 0.20) / 2 = 0.15 total 0.10 + 0.15 = 0.25
93
+ assert.ok(Math.abs(predictRemainingCost(avgs, remaining) - 0.25) < 0.001);
94
+ });
95
+
96
+ test("returns 0 for empty remaining list", () => {
97
+ const avgs = new Map([["execute-task", 0.15]]);
98
+ assert.equal(predictRemainingCost(avgs, []), 0);
99
+ });
100
+
101
+ test("uses fallback when no averages are known", () => {
102
+ const cost = predictRemainingCost(new Map(), ["execute-task", "plan-slice"], 0.10);
103
+ assert.ok(Math.abs(cost - 0.20) < 0.001);
104
+ });
105
+
106
+ test("returns 0 when no averages and no fallback", () => {
107
+ assert.equal(predictRemainingCost(new Map(), ["execute-task"]), 0);
108
+ });
109
+ });
110
+
111
+ describe("end-to-end budget prediction (composes the real functions)", () => {
112
+ test("budget ceiling exceeded is detectable from real averages + projection", () => {
113
+ const units = [
114
+ makeUnit({ type: "execute-task", cost: 0.5 }),
115
+ makeUnit({ type: "execute-task", cost: 0.6 }),
116
+ makeUnit({ type: "plan-slice", cost: 0.3 }),
117
+ makeUnit({ type: "complete-slice", cost: 0.2 }),
118
+ ];
119
+ const totalSpent = units.reduce((sum, u) => sum + u.cost, 0); // 1.60
120
+ const avgs = getAverageCostPerUnitType(units);
121
+ const predicted = predictRemainingCost(avgs, [
122
+ "execute-task",
123
+ "execute-task",
124
+ "execute-task",
125
+ ]);
126
+ // avg execute-task = 0.55, predicted remaining = 3 × 0.55 = 1.65
127
+ // total = 1.60 + 1.65 = 3.25 > 2.50 ceiling
128
+ assert.ok(
129
+ totalSpent + predicted > 2.5,
130
+ "spent + predicted should exceed test ceiling",
131
+ );
132
+ });
133
+
134
+ test("budget ceiling not exceeded when averages stay low", () => {
135
+ const units = [
136
+ makeUnit({ type: "execute-task", cost: 0.1 }),
137
+ makeUnit({ type: "plan-slice", cost: 0.05 }),
138
+ ];
139
+ const totalSpent = units.reduce((sum, u) => sum + u.cost, 0); // 0.15
140
+ const avgs = getAverageCostPerUnitType(units);
141
+ const predicted = predictRemainingCost(avgs, ["execute-task", "complete-slice"]);
142
+ assert.ok(
143
+ totalSpent + predicted <= 5.0,
144
+ "spent + predicted should stay under test ceiling",
145
+ );
146
+ });
220
147
  });
@@ -1,73 +1,156 @@
1
1
  /**
2
- * Regression test for #3580 — complete-slice verification gate
2
+ * Behavioural regression test for #3580 — complete-slice verification gate.
3
3
  *
4
- * Without the gate, a prompt regression could silently advance a blocked
5
- * or failed slice to "complete" status. The fix adds a BLOCKED_SIGNALS
6
- * regex that rejects completion when verification/UAT content clearly
7
- * indicates blocked or failed state.
4
+ * The gate must reject completion when the verification or UAT content
5
+ * indicates a blocked or failed slice. Drives the real handler with
6
+ * blocked-signal fixtures and asserts on the returned error. Replaces an
7
+ * earlier test file that only string-matched the BLOCKED_SIGNALS regex
8
+ * literal in the source (Refs #4826/#4831).
8
9
  */
9
10
 
10
- import { describe, it } from 'node:test'
11
- import assert from 'node:assert/strict'
12
- import { readFileSync } from 'node:fs'
13
- import { resolve } from 'node:path'
14
- import { extractSourceRegion } from "./test-helpers.ts";
11
+ import { describe, test, beforeEach, afterEach } from 'node:test';
12
+ import assert from 'node:assert/strict';
13
+ import * as fs from 'node:fs';
14
+ import * as path from 'node:path';
15
+ import * as os from 'node:os';
15
16
 
16
- const src = readFileSync(
17
- resolve(process.cwd(), 'src', 'resources', 'extensions', 'gsd', 'tools', 'complete-slice.ts'),
18
- 'utf-8',
19
- )
17
+ import {
18
+ openDatabase,
19
+ closeDatabase,
20
+ insertMilestone,
21
+ insertSlice,
22
+ insertTask,
23
+ } from '../gsd-db.ts';
24
+ import { handleCompleteSlice } from '../tools/complete-slice.ts';
25
+ import type { CompleteSliceParams } from '../types.ts';
26
+
27
+ function tempDbPath(): string {
28
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-blocked-gate-'));
29
+ return path.join(dir, 'test.db');
30
+ }
31
+
32
+ function cleanupDb(dbPath: string): void {
33
+ closeDatabase();
34
+ try { fs.rmSync(path.dirname(dbPath), { recursive: true, force: true }); } catch { /* */ }
35
+ }
36
+
37
+ function makeProject(): string {
38
+ const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-gate-proj-'));
39
+ fs.mkdirSync(path.join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks'), { recursive: true });
40
+ fs.writeFileSync(
41
+ path.join(basePath, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md'),
42
+ `# M001\n\n## Slices\n- [ ] **S01: Test** \`risk:low\` \`depends:[]\`\n - After this: works\n`,
43
+ );
44
+ return basePath;
45
+ }
46
+
47
+ function makeParams(overrides: Partial<CompleteSliceParams>): CompleteSliceParams {
48
+ return {
49
+ sliceId: 'S01',
50
+ milestoneId: 'M001',
51
+ sliceTitle: 'Test Slice',
52
+ oneLiner: 'one liner',
53
+ narrative: 'narrative',
54
+ verification: 'all green',
55
+ deviations: 'None.',
56
+ knownLimitations: 'None.',
57
+ followUps: 'None.',
58
+ keyFiles: [],
59
+ keyDecisions: [],
60
+ patternsEstablished: [],
61
+ observabilitySurfaces: [],
62
+ provides: [],
63
+ requirementsSurfaced: [],
64
+ drillDownPaths: [],
65
+ affects: [],
66
+ requirementsAdvanced: [],
67
+ requirementsValidated: [],
68
+ requirementsInvalidated: [],
69
+ filesModified: [],
70
+ requires: [],
71
+ uatContent: 'UAT body.',
72
+ ...overrides,
73
+ };
74
+ }
20
75
 
21
76
  describe('complete-slice verification gate (#3580)', () => {
22
- it('BLOCKED_SIGNALS regex is defined', () => {
23
- assert.ok(
24
- src.includes('BLOCKED_SIGNALS'),
25
- 'BLOCKED_SIGNALS constant must be defined in complete-slice.ts',
26
- )
27
- })
77
+ let dbPath: string;
78
+ let basePath: string;
79
+
80
+ beforeEach(() => {
81
+ dbPath = tempDbPath();
82
+ openDatabase(dbPath);
83
+ basePath = makeProject();
84
+ insertMilestone({ id: 'M001' });
85
+ insertSlice({ id: 'S01', milestoneId: 'M001' });
86
+ insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete', title: 'T1' });
87
+ });
28
88
 
29
- it('BLOCKED_SIGNALS is a regex that tests verification content', () => {
30
- // Extract the BLOCKED_SIGNALS definition line
31
- const idx = src.indexOf('BLOCKED_SIGNALS')
32
- assert.ok(idx !== -1)
33
- const lineEnd = src.indexOf(';', idx)
34
- const definition = src.slice(idx, lineEnd)
89
+ afterEach(() => {
90
+ cleanupDb(dbPath);
91
+ try { fs.rmSync(basePath, { recursive: true, force: true }); } catch { /* */ }
92
+ });
35
93
 
36
- // Must be a regex (starts with /)
37
- assert.ok(
38
- definition.includes('= /'),
39
- 'BLOCKED_SIGNALS must be assigned a regex literal',
40
- )
94
+ test('rejects when verification text contains "verification failed"', async () => {
95
+ const result = await handleCompleteSlice(
96
+ makeParams({ verification: 'verification failed: the regression came back' }),
97
+ basePath,
98
+ );
99
+ assert.ok('error' in result, 'expected handler to return an error');
100
+ assert.match((result as { error: string }).error, /blocked|failed|do not complete/i);
101
+ });
41
102
 
42
- // Must match key blocked/failed signals
43
- assert.ok(definition.includes('blocked'), 'regex must match "blocked" signals')
44
- assert.ok(definition.includes('failed'), 'regex must match "failed" signals')
45
- })
103
+ test('rejects when uatContent contains "verification_result: failed"', async () => {
104
+ const result = await handleCompleteSlice(
105
+ makeParams({ uatContent: '## Result\nverification_result: failed\n' }),
106
+ basePath,
107
+ );
108
+ assert.ok('error' in result, 'expected handler to return an error');
109
+ assert.match((result as { error: string }).error, /blocked|failed|do not complete/i);
110
+ });
46
111
 
47
- it('gate checks params.verification and params.uatContent', () => {
48
- // Find usage of BLOCKED_SIGNALS.test
49
- const testCalls = src.match(/BLOCKED_SIGNALS\.test\([^)]+\)/g)
50
- assert.ok(testCalls, 'BLOCKED_SIGNALS.test() must be called')
51
- assert.ok(testCalls.length >= 2, 'must check at least verification and uatContent')
112
+ test('rejects when verification declares "status: blocked"', async () => {
113
+ const result = await handleCompleteSlice(
114
+ makeParams({ verification: 'status: blocked — db unavailable' }),
115
+ basePath,
116
+ );
117
+ assert.ok('error' in result, 'expected handler to return an error');
118
+ assert.match((result as { error: string }).error, /blocked|failed|do not complete/i);
119
+ });
52
120
 
53
- const joined = testCalls.join(' ')
54
- assert.ok(joined.includes('verification'), 'must test params.verification')
55
- assert.ok(joined.includes('uatContent'), 'must test params.uatContent')
56
- })
121
+ test('rejects when uatContent says "slice is blocked"', async () => {
122
+ const result = await handleCompleteSlice(
123
+ makeParams({ uatContent: 'slice is blocked on upstream' }),
124
+ basePath,
125
+ );
126
+ assert.ok('error' in result, 'expected handler to return an error');
127
+ assert.match((result as { error: string }).error, /blocked|failed|do not complete/i);
128
+ });
57
129
 
58
- it('gate returns an error message when blocked signals detected', () => {
59
- // Find the return statement after BLOCKED_SIGNALS check
60
- const gateIdx = src.indexOf('BLOCKED_SIGNALS.test(')
61
- assert.ok(gateIdx !== -1)
130
+ test('rejects when verification says "cannot complete"', async () => {
131
+ const result = await handleCompleteSlice(
132
+ makeParams({ verification: 'cannot complete: requirements unmet' }),
133
+ basePath,
134
+ );
135
+ assert.ok('error' in result, 'expected handler to return an error');
136
+ assert.match((result as { error: string }).error, /blocked|failed|do not complete/i);
137
+ });
62
138
 
63
- const afterGate = extractSourceRegion(src, 'BLOCKED_SIGNALS.test(')
64
- assert.ok(
65
- afterGate.includes('return { error:'),
66
- 'blocked signal detection must return an error',
67
- )
68
- assert.ok(
69
- afterGate.includes('do not complete'),
70
- 'error message must explain why completion is rejected',
71
- )
72
- })
73
- })
139
+ test('passes the gate when verification + uatContent are clean', async () => {
140
+ // Sanity: the gate is not over-eager. Clean inputs reach the rest of
141
+ // the handler. (This call may still fail downstream because we provide
142
+ // a thin fixture; the only guarantee here is that the error — if any —
143
+ // is NOT the blocked-signals error.)
144
+ const result = await handleCompleteSlice(
145
+ makeParams({ verification: 'all 8 sections pass', uatContent: 'green across the board' }),
146
+ basePath,
147
+ );
148
+ if ('error' in result) {
149
+ assert.doesNotMatch(
150
+ result.error,
151
+ /blocked\/failed state — do not complete/,
152
+ `clean inputs should not be rejected by the BLOCKED_SIGNALS gate, got: ${result.error}`,
153
+ );
154
+ }
155
+ });
156
+ });
@@ -13,6 +13,7 @@ import {
13
13
  getSlice,
14
14
  updateSliceStatus,
15
15
  getSliceTasks,
16
+ SCHEMA_VERSION,
16
17
  } from '../gsd-db.ts';
17
18
  import { handleCompleteSlice } from '../tools/complete-slice.ts';
18
19
  import type { CompleteSliceParams } from '../types.ts';
@@ -115,19 +116,21 @@ Run the test suite and verify all assertions pass.
115
116
  }
116
117
 
117
118
  // ═══════════════════════════════════════════════════════════════════════════
118
- // complete-slice: Schema v6 migration
119
+ // complete-slice: fresh DB migrates to current schema version
119
120
  // ═══════════════════════════════════════════════════════════════════════════
120
121
 
121
- console.log('\n=== complete-slice: schema v6 migration ===');
122
+ console.log('\n=== complete-slice: fresh DB migrates to current schema version ===');
122
123
  {
123
124
  const dbPath = tempDbPath();
124
125
  openDatabase(dbPath);
125
126
 
126
127
  const adapter = _getAdapter()!;
127
128
 
128
- // Verify schema version is current (v22 quality_gates DDL fix)
129
+ // Pin schema version against the source-of-truth constant so this test
130
+ // survives migration bumps but still catches a "fresh DB was not migrated"
131
+ // regression.
129
132
  const versionRow = adapter.prepare('SELECT MAX(version) as v FROM schema_version').get();
130
- assertEq(versionRow?.['v'], 22, 'schema version should be 22');
133
+ assertEq(versionRow?.['v'], SCHEMA_VERSION, 'fresh DB should be migrated to current SCHEMA_VERSION');
131
134
 
132
135
  // Verify slices table has full_summary_md and full_uat_md columns
133
136
  const cols = adapter.prepare("PRAGMA table_info(slices)").all();