gsd-pi 2.71.0-dev.e17e0ce → 2.72.0-dev.3118184

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 (352) hide show
  1. package/README.md +46 -3
  2. package/dist/cli.js +76 -3
  3. package/dist/mcp-server.js +37 -14
  4. package/dist/onboarding.js +10 -0
  5. package/dist/resources/agents/debugger.md +58 -0
  6. package/dist/resources/agents/doc-writer.md +43 -0
  7. package/dist/resources/agents/git-ops.md +56 -0
  8. package/dist/resources/agents/javascript-pro.md +46 -271
  9. package/dist/resources/agents/planner.md +55 -0
  10. package/dist/resources/agents/refactorer.md +47 -0
  11. package/dist/resources/agents/reviewer.md +48 -0
  12. package/dist/resources/agents/security.md +59 -0
  13. package/dist/resources/agents/tester.md +50 -0
  14. package/dist/resources/agents/typescript-pro.md +41 -235
  15. package/dist/resources/extensions/async-jobs/await-tool.js +7 -4
  16. package/dist/resources/extensions/async-jobs/job-manager.js +28 -3
  17. package/dist/resources/extensions/claude-code-cli/partial-builder.js +40 -12
  18. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +132 -10
  19. package/dist/resources/extensions/gsd/auto/loop.js +84 -1
  20. package/dist/resources/extensions/gsd/auto/phases.js +4 -0
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +6 -0
  22. package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
  23. package/dist/resources/extensions/gsd/auto-recovery.js +11 -0
  24. package/dist/resources/extensions/gsd/auto-start.js +24 -4
  25. package/dist/resources/extensions/gsd/auto.js +29 -19
  26. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  27. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -11
  28. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +2 -5
  29. package/dist/resources/extensions/gsd/commands-handlers.js +4 -1
  30. package/dist/resources/extensions/gsd/context-injector.js +1 -1
  31. package/dist/resources/extensions/gsd/custom-workflow-engine.js +3 -7
  32. package/dist/resources/extensions/gsd/definition-io.js +15 -0
  33. package/dist/resources/extensions/gsd/dispatch-guard.js +4 -0
  34. package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
  35. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +6 -3
  36. package/dist/resources/extensions/gsd/error-classifier.js +4 -1
  37. package/dist/resources/extensions/gsd/gate-registry.js +208 -0
  38. package/dist/resources/extensions/gsd/git-service.js +11 -8
  39. package/dist/resources/extensions/gsd/gitignore.js +12 -6
  40. package/dist/resources/extensions/gsd/gsd-db.js +90 -6
  41. package/dist/resources/extensions/gsd/key-manager.js +2 -0
  42. package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
  43. package/dist/resources/extensions/gsd/notification-overlay.js +26 -12
  44. package/dist/resources/extensions/gsd/notification-store.js +5 -4
  45. package/dist/resources/extensions/gsd/preferences-skills.js +2 -34
  46. package/dist/resources/extensions/gsd/preferences-types.js +15 -0
  47. package/dist/resources/extensions/gsd/preferences.js +16 -3
  48. package/dist/resources/extensions/gsd/prompt-loader.js +4 -1
  49. package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
  50. package/dist/resources/extensions/gsd/prompts/complete-slice.md +3 -1
  51. package/dist/resources/extensions/gsd/prompts/discuss.md +122 -13
  52. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  53. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  55. package/dist/resources/extensions/gsd/shortcut-defs.js +7 -1
  56. package/dist/resources/extensions/gsd/state.js +29 -2
  57. package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
  58. package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
  59. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +4 -1
  60. package/dist/resources/extensions/gsd/workflow-projections.js +7 -0
  61. package/dist/resources/extensions/gsd/worktree-manager.js +30 -3
  62. package/dist/resources/extensions/gsd/write-intercept.js +10 -1
  63. package/dist/resources/extensions/ollama/index.js +17 -10
  64. package/dist/resources/extensions/ollama/ollama-client.js +35 -6
  65. package/dist/resources/extensions/ollama/ollama-discovery.js +32 -6
  66. package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
  67. package/dist/resources/extensions/subagent/agents.js +8 -0
  68. package/dist/resources/extensions/subagent/index.js +17 -0
  69. package/dist/startup-model-validation.d.ts +0 -1
  70. package/dist/startup-model-validation.js +6 -2
  71. package/dist/web/standalone/.next/BUILD_ID +1 -1
  72. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  73. package/dist/web/standalone/.next/build-manifest.json +2 -2
  74. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  75. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  76. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  86. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  99. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/forensics/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/history/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  115. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  120. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  123. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  130. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  131. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  132. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +3 -3
  133. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/index.html +1 -1
  138. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  139. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  140. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  141. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  142. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  143. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  144. package/dist/web/standalone/.next/server/app/page.js +2 -2
  145. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  147. package/dist/web/standalone/.next/server/chunks/2331.js +16 -16
  148. package/dist/web/standalone/.next/server/chunks/4741.js +12 -12
  149. package/dist/web/standalone/.next/server/chunks/5822.js +2 -2
  150. package/dist/web/standalone/.next/server/chunks/63.js +8 -8
  151. package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
  152. package/dist/web/standalone/.next/server/edge-runtime-webpack.js +2 -0
  153. package/dist/web/standalone/.next/server/functions-config-manifest.json +0 -9
  154. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/middleware-manifest.json +29 -2
  156. package/dist/web/standalone/.next/server/middleware.js +4 -12
  157. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  158. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  159. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  160. package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
  161. package/package.json +1 -1
  162. package/packages/mcp-server/dist/server.d.ts +12 -1
  163. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  164. package/packages/mcp-server/dist/server.js +90 -42
  165. package/packages/mcp-server/dist/server.js.map +1 -1
  166. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  167. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  168. package/packages/mcp-server/src/server.ts +110 -38
  169. package/packages/mcp-server/src/workflow-tools.ts +1 -1
  170. package/packages/pi-ai/dist/env-api-keys.js +1 -0
  171. package/packages/pi-ai/dist/env-api-keys.js.map +1 -1
  172. package/packages/pi-ai/dist/models.custom.d.ts +105 -0
  173. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -1
  174. package/packages/pi-ai/dist/models.custom.js +97 -0
  175. package/packages/pi-ai/dist/models.custom.js.map +1 -1
  176. package/packages/pi-ai/dist/models.generated.d.ts +648 -140
  177. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  178. package/packages/pi-ai/dist/models.generated.js +867 -370
  179. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  180. package/packages/pi-ai/dist/models.generated.test.d.ts +2 -0
  181. package/packages/pi-ai/dist/models.generated.test.d.ts.map +1 -0
  182. package/packages/pi-ai/dist/models.generated.test.js +334 -0
  183. package/packages/pi-ai/dist/models.generated.test.js.map +1 -0
  184. package/packages/pi-ai/dist/models.test.js +105 -0
  185. package/packages/pi-ai/dist/models.test.js.map +1 -1
  186. package/packages/pi-ai/dist/types.d.ts +1 -1
  187. package/packages/pi-ai/dist/types.d.ts.map +1 -1
  188. package/packages/pi-ai/dist/types.js.map +1 -1
  189. package/packages/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  190. package/packages/pi-ai/dist/utils/oauth/github-copilot.js +5 -1
  191. package/packages/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  192. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts +2 -0
  193. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.d.ts.map +1 -0
  194. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js +57 -0
  195. package/packages/pi-ai/dist/utils/oauth/github-copilot.test.js.map +1 -0
  196. package/packages/pi-ai/src/env-api-keys.ts +1 -0
  197. package/packages/pi-ai/src/models.custom.ts +98 -0
  198. package/packages/pi-ai/src/models.generated.test.ts +373 -0
  199. package/packages/pi-ai/src/models.generated.ts +867 -370
  200. package/packages/pi-ai/src/models.test.ts +135 -0
  201. package/packages/pi-ai/src/types.ts +1 -0
  202. package/packages/pi-ai/src/utils/oauth/github-copilot.test.ts +71 -0
  203. package/packages/pi-ai/src/utils/oauth/github-copilot.ts +4 -1
  204. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -0
  206. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  208. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  209. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  210. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  211. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +5 -0
  212. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/retry-handler.js +55 -1
  214. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +57 -0
  216. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  217. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/core/sdk.js +9 -0
  219. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  224. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  226. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +87 -12
  227. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  228. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  231. package/packages/pi-coding-agent/package.json +1 -1
  232. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  233. package/packages/pi-coding-agent/src/core/model-resolver.ts +1 -0
  234. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +83 -0
  235. package/packages/pi-coding-agent/src/core/retry-handler.ts +60 -1
  236. package/packages/pi-coding-agent/src/core/sdk.ts +10 -0
  237. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +72 -0
  238. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  239. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -12
  240. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  241. package/packages/pi-tui/dist/components/__tests__/editor.test.js +12 -0
  242. package/packages/pi-tui/dist/components/__tests__/editor.test.js.map +1 -1
  243. package/packages/pi-tui/dist/components/__tests__/input.test.js +12 -0
  244. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  245. package/packages/pi-tui/dist/keys.d.ts.map +1 -1
  246. package/packages/pi-tui/dist/keys.js +27 -0
  247. package/packages/pi-tui/dist/keys.js.map +1 -1
  248. package/packages/pi-tui/src/components/__tests__/editor.test.ts +18 -0
  249. package/packages/pi-tui/src/components/__tests__/input.test.ts +18 -0
  250. package/packages/pi-tui/src/keys.ts +32 -0
  251. package/pkg/package.json +1 -1
  252. package/src/resources/agents/debugger.md +58 -0
  253. package/src/resources/agents/doc-writer.md +43 -0
  254. package/src/resources/agents/git-ops.md +56 -0
  255. package/src/resources/agents/javascript-pro.md +46 -271
  256. package/src/resources/agents/planner.md +55 -0
  257. package/src/resources/agents/refactorer.md +47 -0
  258. package/src/resources/agents/reviewer.md +48 -0
  259. package/src/resources/agents/security.md +59 -0
  260. package/src/resources/agents/tester.md +50 -0
  261. package/src/resources/agents/typescript-pro.md +41 -235
  262. package/src/resources/extensions/async-jobs/await-tool.test.ts +40 -7
  263. package/src/resources/extensions/async-jobs/await-tool.ts +7 -4
  264. package/src/resources/extensions/async-jobs/job-manager.ts +33 -3
  265. package/src/resources/extensions/claude-code-cli/partial-builder.ts +45 -12
  266. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +139 -8
  267. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +91 -2
  268. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +245 -2
  269. package/src/resources/extensions/gsd/auto/loop.ts +89 -1
  270. package/src/resources/extensions/gsd/auto/phases.ts +4 -0
  271. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -0
  272. package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
  273. package/src/resources/extensions/gsd/auto-recovery.ts +10 -0
  274. package/src/resources/extensions/gsd/auto-start.ts +31 -4
  275. package/src/resources/extensions/gsd/auto.ts +29 -20
  276. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  277. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -10
  278. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +2 -5
  279. package/src/resources/extensions/gsd/commands-handlers.ts +5 -1
  280. package/src/resources/extensions/gsd/context-injector.ts +1 -1
  281. package/src/resources/extensions/gsd/custom-workflow-engine.ts +4 -8
  282. package/src/resources/extensions/gsd/definition-io.ts +18 -0
  283. package/src/resources/extensions/gsd/dispatch-guard.ts +5 -0
  284. package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
  285. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +6 -3
  286. package/src/resources/extensions/gsd/error-classifier.ts +4 -1
  287. package/src/resources/extensions/gsd/gate-registry.ts +251 -0
  288. package/src/resources/extensions/gsd/git-service.ts +11 -8
  289. package/src/resources/extensions/gsd/gitignore.ts +12 -6
  290. package/src/resources/extensions/gsd/gsd-db.ts +105 -6
  291. package/src/resources/extensions/gsd/key-manager.ts +2 -0
  292. package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
  293. package/src/resources/extensions/gsd/notification-overlay.ts +27 -11
  294. package/src/resources/extensions/gsd/notification-store.ts +5 -4
  295. package/src/resources/extensions/gsd/preferences-skills.ts +2 -36
  296. package/src/resources/extensions/gsd/preferences-types.ts +16 -0
  297. package/src/resources/extensions/gsd/preferences.ts +19 -6
  298. package/src/resources/extensions/gsd/prompt-loader.ts +6 -1
  299. package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
  300. package/src/resources/extensions/gsd/prompts/complete-slice.md +3 -1
  301. package/src/resources/extensions/gsd/prompts/discuss.md +122 -13
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  303. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  304. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -0
  305. package/src/resources/extensions/gsd/shortcut-defs.ts +8 -1
  306. package/src/resources/extensions/gsd/state.ts +33 -2
  307. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +27 -0
  308. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +14 -0
  309. package/src/resources/extensions/gsd/tests/block-db-writes.test.ts +63 -0
  310. package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
  311. package/src/resources/extensions/gsd/tests/definition-io.test.ts +57 -0
  312. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +26 -0
  313. package/src/resources/extensions/gsd/tests/doctor-heal-fixable-warnings.test.ts +14 -0
  314. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
  315. package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +104 -0
  316. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +16 -0
  317. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
  318. package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
  319. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +107 -5
  320. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +8 -6
  321. package/src/resources/extensions/gsd/tests/key-manager.test.ts +63 -0
  322. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +54 -0
  323. package/src/resources/extensions/gsd/tests/plan-milestone-artifact-verification.test.ts +62 -0
  324. package/src/resources/extensions/gsd/tests/post-unit-state-rebuild.test.ts +34 -0
  325. package/src/resources/extensions/gsd/tests/preferences-formatting.test.ts +87 -0
  326. package/src/resources/extensions/gsd/tests/preferences.test.ts +53 -0
  327. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +96 -1
  328. package/src/resources/extensions/gsd/tests/prompt-loader-working-directory.test.ts +19 -0
  329. package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
  330. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +9 -0
  331. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +97 -0
  332. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +3 -2
  333. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +41 -0
  334. package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
  335. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
  336. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +4 -1
  337. package/src/resources/extensions/gsd/types.ts +26 -0
  338. package/src/resources/extensions/gsd/workflow-projections.ts +8 -0
  339. package/src/resources/extensions/gsd/worktree-manager.ts +29 -3
  340. package/src/resources/extensions/gsd/write-intercept.ts +10 -1
  341. package/src/resources/extensions/ollama/index.ts +17 -8
  342. package/src/resources/extensions/ollama/ollama-client.ts +35 -6
  343. package/src/resources/extensions/ollama/ollama-discovery.ts +37 -6
  344. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  345. package/src/resources/extensions/ollama/tests/ollama-discovery.test.ts +54 -0
  346. package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
  347. package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
  348. package/src/resources/extensions/subagent/agents.ts +10 -0
  349. package/src/resources/extensions/subagent/index.ts +18 -0
  350. package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
  351. /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → NzO79SOz9jHX-VY5-0t2O}/_buildManifest.js +0 -0
  352. /package/dist/web/standalone/.next/static/{cYPZv_bAhZk2ms-Pz6vsY → NzO79SOz9jHX-VY5-0t2O}/_ssgManifest.js +0 -0
@@ -93,9 +93,6 @@ export function registerShortcuts(pi: ExtensionAPI): void {
93
93
  handler: openParallelOverlay,
94
94
  });
95
95
 
96
- // Fallback for terminals where Ctrl+Alt letter chords are not forwarded reliably.
97
- pi.registerShortcut(Key.ctrlShift(GSD_SHORTCUTS.parallel.key), {
98
- description: shortcutDesc(`${GSD_SHORTCUTS.parallel.action} (fallback)`, GSD_SHORTCUTS.parallel.command),
99
- handler: openParallelOverlay,
100
- });
96
+ // No Ctrl+Shift+P fallback conflicts with cycleModelBackward (shift+ctrl+p).
97
+ // Use Ctrl+Alt+P or /gsd parallel watch instead.
101
98
  }
@@ -78,6 +78,10 @@ export function parseDoctorArgs(args: string) {
78
78
  return { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope };
79
79
  }
80
80
 
81
+ export function isDoctorHealActionable(issue: { fixable: boolean; severity: string }): boolean {
82
+ return issue.fixable && issue.severity !== "info";
83
+ }
84
+
81
85
  export async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
82
86
  const { jsonMode, dryRun, fixFlag, includeBuild, includeTests, mode, requestedScope } = parseDoctorArgs(args);
83
87
  const scope = await selectDoctorScope(projectRoot(), requestedScope);
@@ -109,7 +113,7 @@ export async function handleDoctor(args: string, ctx: ExtensionCommandContext, p
109
113
  scope: effectiveScope,
110
114
  includeWarnings: true,
111
115
  });
112
- const actionable = unresolved.filter(issue => issue.severity === "error");
116
+ const actionable = unresolved.filter(isDoctorHealActionable);
113
117
  if (actionable.length === 0) {
114
118
  ctx.ui.notify("Doctor heal found nothing actionable to hand off to the LLM.", "info");
115
119
  return;
@@ -16,7 +16,7 @@
16
16
  import { readFileSync, existsSync } from "node:fs";
17
17
  import { join, resolve, sep } from "node:path";
18
18
  import type { StepDefinition } from "./definition-loader.js";
19
- import { readFrozenDefinition } from "./custom-workflow-engine.js";
19
+ import { readFrozenDefinition } from "./definition-io.js";
20
20
 
21
21
  /** Maximum characters per artifact to prevent context window blowout. */
22
22
  const MAX_CONTEXT_CHARS = 10_000;
@@ -22,7 +22,6 @@ import type {
22
22
  } from "./engine-types.js";
23
23
  import { readFileSync } from "node:fs";
24
24
  import { join } from "node:path";
25
- import { parse } from "yaml";
26
25
  import {
27
26
  readGraph,
28
27
  writeGraph,
@@ -32,16 +31,13 @@ import {
32
31
  type WorkflowGraph,
33
32
  } from "./graph.js";
34
33
  import { injectContext } from "./context-injector.js";
35
- import type { WorkflowDefinition, StepDefinition } from "./definition-loader.js";
34
+ import type { StepDefinition } from "./definition-loader.js";
35
+ import { readFrozenDefinition } from "./definition-io.js";
36
36
  import { parseUnitId } from "./unit-id.js";
37
37
  import { withFileLock } from "./file-lock.js";
38
38
 
39
- /** Read and parse the frozen DEFINITION.yaml from a run directory. */
40
- export function readFrozenDefinition(runDir: string): WorkflowDefinition {
41
- const defPath = join(runDir, "DEFINITION.yaml");
42
- const raw = readFileSync(defPath, "utf-8");
43
- return parse(raw, { schema: "core" }) as WorkflowDefinition;
44
- }
39
+ // Re-export for downstream consumers
40
+ export { readFrozenDefinition } from "./definition-io.js";
45
41
 
46
42
  export class CustomWorkflowEngine implements WorkflowEngine {
47
43
  readonly engineId = "custom";
@@ -0,0 +1,18 @@
1
+ /**
2
+ * definition-io.ts — Read frozen DEFINITION.yaml from a run directory.
3
+ *
4
+ * Extracted from custom-workflow-engine.ts to break the circular dependency
5
+ * between context-injector.ts and custom-workflow-engine.ts.
6
+ */
7
+
8
+ import { readFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { parse } from "yaml";
11
+ import type { WorkflowDefinition } from "./definition-loader.js";
12
+
13
+ /** Read and parse the frozen DEFINITION.yaml from a run directory. */
14
+ export function readFrozenDefinition(runDir: string): WorkflowDefinition {
15
+ const defPath = join(runDir, "DEFINITION.yaml");
16
+ const raw = readFileSync(defPath, "utf-8");
17
+ return parse(raw, { schema: "core" }) as WorkflowDefinition;
18
+ }
@@ -107,6 +107,11 @@ export function getPriorSliceCompletionBlocker(
107
107
  // it may be a cross-milestone reference handled elsewhere.
108
108
  }
109
109
  } else {
110
+ const milestoneUsesExplicitDeps = slices.some((slice) => slice.depends.length > 0);
111
+ if (milestoneUsesExplicitDeps) {
112
+ return null;
113
+ }
114
+
110
115
  // Positional fallback is only a heuristic for legacy slices with no
111
116
  // declared dependencies. Skip any earlier slice that depends on the
112
117
  // target, directly or transitively, or we can deadlock a valid zero-dep
@@ -185,11 +185,35 @@ const PROVIDER_ROUTES: Record<string, string[]> = {
185
185
  google: ["google-gemini-cli"],
186
186
  };
187
187
 
188
+ /**
189
+ * Providers that use external CLI authentication (not API keys).
190
+ * These are always considered "ok" — the host CLI handles auth.
191
+ */
192
+ const CLI_AUTH_PROVIDERS = new Set([
193
+ "claude-code",
194
+ "openai-codex",
195
+ "google-gemini-cli",
196
+ "google-antigravity",
197
+ ]);
198
+
188
199
  function checkLlmProviders(): ProviderCheckResult[] {
189
200
  const required = collectConfiguredModelProviders();
190
201
  const results: ProviderCheckResult[] = [];
191
202
 
192
203
  for (const providerId of required) {
204
+ // CLI-authenticated providers don't need API keys — skip key check
205
+ if (CLI_AUTH_PROVIDERS.has(providerId)) {
206
+ const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
207
+ results.push({
208
+ name: providerId,
209
+ label: info?.label ?? providerId,
210
+ category: "llm",
211
+ status: "ok",
212
+ message: `${info?.label ?? providerId} — CLI auth (no key needed)`,
213
+ required: true,
214
+ });
215
+ continue;
216
+ }
193
217
  const info = PROVIDER_REGISTRY.find(p => p.id === providerId);
194
218
  const label = providerId === "anthropic-vertex"
195
219
  ? "Anthropic Vertex"
@@ -303,13 +303,16 @@ export async function checkRuntimeHealth(
303
303
  content.split("\n").map(l => l.trim()).filter(l => l && !l.startsWith("#")),
304
304
  );
305
305
 
306
- // Check for critical runtime patterns that must be present
306
+ // Check for critical runtime patterns that must be present.
307
+ // NOTE: GSD_RUNTIME_PATTERNS in gitignore.ts is the canonical source of truth.
308
+ // This is a minimal subset for the doctor check.
307
309
  const criticalPatterns = [
308
310
  ".gsd/activity/",
309
311
  ".gsd/runtime/",
310
312
  ".gsd/auto.lock",
311
- ".gsd/gsd.db",
312
- ".gsd/completed-units.json",
313
+ ".gsd/gsd.db*",
314
+ ".gsd/completed-units*.json",
315
+ ".gsd/event-log.jsonl",
313
316
  ];
314
317
 
315
318
  // If blanket .gsd/ or .gsd is present, all patterns are covered
@@ -44,6 +44,9 @@ export function resetRetryState(state: RetryState): void {
44
44
 
45
45
  const PERMANENT_RE = /auth|unauthorized|forbidden|invalid.*key|invalid.*api|billing|quota exceeded|account/i;
46
46
  const RATE_LIMIT_RE = /rate.?limit|too many requests|429/i;
47
+ // OpenRouter affordability-style quota errors should be treated as transient
48
+ // so core retry logic can lower maxTokens and continue in-session.
49
+ const AFFORDABILITY_RE = /requires more credits|can only afford|insufficient credits|not enough credits|fewer max_tokens/i;
47
50
  const NETWORK_RE = /network|ECONNRESET|ETIMEDOUT|ECONNREFUSED|socket hang up|fetch failed|connection.*reset|dns/i;
48
51
  const SERVER_RE = /internal server error|500|502|503|overloaded|server_error|api_error|service.?unavailable/i;
49
52
  // ECONNRESET/ECONNREFUSED are in NETWORK_RE (same-model retry first).
@@ -67,7 +70,7 @@ const RESET_DELAY_RE = /reset in (\d+)s/i;
67
70
  */
68
71
  export function classifyError(errorMsg: string, retryAfterMs?: number): ErrorClass {
69
72
  const isPermanent = PERMANENT_RE.test(errorMsg);
70
- const isRateLimit = RATE_LIMIT_RE.test(errorMsg);
73
+ const isRateLimit = RATE_LIMIT_RE.test(errorMsg) || AFFORDABILITY_RE.test(errorMsg);
71
74
 
72
75
  // 1. Permanent — but rate limit takes precedence
73
76
  if (isPermanent && !isRateLimit) {
@@ -0,0 +1,251 @@
1
+ /**
2
+ * GSD Gate Registry — single source of truth for quality-gate ownership.
3
+ *
4
+ * Each gate declares which workflow turn owns it, the scope at which it is
5
+ * persisted in the `quality_gates` table, and the question/guidance text used
6
+ * in the prompt that turn sends. The registry replaces the ad-hoc
7
+ * `GATE_QUESTIONS` table that used to live in `auto-prompts.ts`, and every
8
+ * layer of the prompt system (prompt builders, dispatch rules, state
9
+ * derivation, tool handlers) consults it so a pending gate can never be
10
+ * silently dropped.
11
+ *
12
+ * Design notes:
13
+ * - `GATE_REGISTRY` is exhaustiveness-checked against `GateId` via
14
+ * `satisfies Record<GateId, GateDefinition>`, so adding a new GateId
15
+ * without a registry entry is a compile error.
16
+ * - `getGatesForTurn(turn)` returns the definitions a turn owns.
17
+ * - `assertGateCoverage(pending, turn)` throws a GSDError if the pending
18
+ * list for a turn contains unknown gates, or if any gate owned by the
19
+ * turn is missing from the pending list.
20
+ */
21
+
22
+ import { GSDError, GSD_PARSE_ERROR } from "./errors.js";
23
+ import type { GateId, GateRow, GateScope } from "./types.js";
24
+
25
+ /** Which workflow turn is responsible for evaluating / closing a gate. */
26
+ export type OwnerTurn =
27
+ | "gate-evaluate"
28
+ | "execute-task"
29
+ | "complete-slice"
30
+ | "validate-milestone";
31
+
32
+ export interface GateDefinition {
33
+ id: GateId;
34
+ scope: GateScope;
35
+ ownerTurn: OwnerTurn;
36
+ /** One-line question the assistant must answer. */
37
+ question: string;
38
+ /** Markdown guidance describing what a good answer looks like. */
39
+ guidance: string;
40
+ /** H3 section header used in the artifact the turn writes
41
+ * (e.g. "Operational Readiness" for Q8 in the slice summary). */
42
+ promptSection: string;
43
+ }
44
+
45
+ export const GATE_REGISTRY = {
46
+ Q3: {
47
+ id: "Q3",
48
+ scope: "slice",
49
+ ownerTurn: "gate-evaluate",
50
+ question: "How can this be exploited?",
51
+ guidance: [
52
+ "Identify abuse scenarios: parameter tampering, replay attacks, privilege escalation.",
53
+ "Map data exposure risks: PII, tokens, secrets accessible through this slice.",
54
+ "Define input trust boundaries: untrusted user input reaching DB, API, or filesystem.",
55
+ "If none apply, return verdict 'omitted' with rationale explaining why.",
56
+ ].join("\n"),
57
+ promptSection: "Abuse Surface",
58
+ },
59
+ Q4: {
60
+ id: "Q4",
61
+ scope: "slice",
62
+ ownerTurn: "gate-evaluate",
63
+ question: "What existing promises does this break?",
64
+ guidance: [
65
+ "List which existing requirements (R001, R003, etc.) are touched by this slice.",
66
+ "Identify what must be re-tested after shipping.",
67
+ "Flag decisions that should be revisited given the new scope.",
68
+ "If no existing requirements are affected, return verdict 'omitted'.",
69
+ ].join("\n"),
70
+ promptSection: "Broken Promises",
71
+ },
72
+ Q5: {
73
+ id: "Q5",
74
+ scope: "task",
75
+ ownerTurn: "execute-task",
76
+ question: "What breaks when dependencies fail?",
77
+ guidance: [
78
+ "Enumerate the task's external dependencies (APIs, filesystem, network, subprocesses).",
79
+ "Describe the failure path for each: timeout, malformed response, connection loss.",
80
+ "Verify the implementation handles each failure or explicitly bubbles the error.",
81
+ "Return verdict 'omitted' only if the task has no external dependencies.",
82
+ ].join("\n"),
83
+ promptSection: "Failure Modes",
84
+ },
85
+ Q6: {
86
+ id: "Q6",
87
+ scope: "task",
88
+ ownerTurn: "execute-task",
89
+ question: "What is the 10x load breakpoint?",
90
+ guidance: [
91
+ "Identify the resource that saturates first at 10x the expected load.",
92
+ "Describe the protection applied (pool sizing, rate limiting, pagination, caching).",
93
+ "Return verdict 'omitted' if the task has no runtime load dimension.",
94
+ ].join("\n"),
95
+ promptSection: "Load Profile",
96
+ },
97
+ Q7: {
98
+ id: "Q7",
99
+ scope: "task",
100
+ ownerTurn: "execute-task",
101
+ question: "What negative tests protect this task?",
102
+ guidance: [
103
+ "List malformed inputs, error paths, and boundary conditions the tests cover.",
104
+ "Point to the specific test files or cases that assert each negative scenario.",
105
+ "Return verdict 'omitted' only if the task has no meaningful negative surface.",
106
+ ].join("\n"),
107
+ promptSection: "Negative Tests",
108
+ },
109
+ Q8: {
110
+ id: "Q8",
111
+ scope: "slice",
112
+ ownerTurn: "complete-slice",
113
+ question: "How will ops know this slice is healthy or broken?",
114
+ guidance: [
115
+ "Describe the health signal (metric, log line, dashboard) that proves the slice works.",
116
+ "Describe the failure signal that triggers an alert or paging.",
117
+ "Document the recovery procedure and any monitoring gaps.",
118
+ "Return verdict 'omitted' only for slices with no runtime behavior at all.",
119
+ ].join("\n"),
120
+ promptSection: "Operational Readiness",
121
+ },
122
+ MV01: {
123
+ id: "MV01",
124
+ scope: "milestone",
125
+ ownerTurn: "validate-milestone",
126
+ question: "Is every success criterion in the milestone roadmap satisfied?",
127
+ guidance: [
128
+ "Walk the success-criteria checklist from the milestone roadmap.",
129
+ "For each criterion, point to the slice / assessment / verification evidence that proves it.",
130
+ "Return verdict 'flag' if any criterion is unmet or unverifiable.",
131
+ ].join("\n"),
132
+ promptSection: "Success Criteria Checklist",
133
+ },
134
+ MV02: {
135
+ id: "MV02",
136
+ scope: "milestone",
137
+ ownerTurn: "validate-milestone",
138
+ question: "Does every slice have a SUMMARY.md and a passing assessment?",
139
+ guidance: [
140
+ "Confirm every slice listed in the roadmap has a SUMMARY.md.",
141
+ "Confirm each slice has an ASSESSMENT verdict of 'pass' (or justified 'omitted').",
142
+ "Flag missing artifacts and slices with outstanding follow-ups or known limitations.",
143
+ ].join("\n"),
144
+ promptSection: "Slice Delivery Audit",
145
+ },
146
+ MV03: {
147
+ id: "MV03",
148
+ scope: "milestone",
149
+ ownerTurn: "validate-milestone",
150
+ question: "Do the slices integrate end-to-end?",
151
+ guidance: [
152
+ "Trace at least one cross-slice flow proving the pieces compose.",
153
+ "Flag gaps where two slices were built in isolation with no integration evidence.",
154
+ ].join("\n"),
155
+ promptSection: "Cross-Slice Integration",
156
+ },
157
+ MV04: {
158
+ id: "MV04",
159
+ scope: "milestone",
160
+ ownerTurn: "validate-milestone",
161
+ question: "Are all touched requirements covered and still coherent?",
162
+ guidance: [
163
+ "For each requirement advanced, validated, surfaced, or invalidated across the milestone's slices, confirm the milestone-level evidence matches.",
164
+ "Flag requirements that slices claim to advance but no artifact proves.",
165
+ ].join("\n"),
166
+ promptSection: "Requirement Coverage",
167
+ },
168
+ } as const satisfies Record<GateId, GateDefinition>;
169
+
170
+ export type GateRegistry = typeof GATE_REGISTRY;
171
+
172
+ /** Stable ordered lists per owner turn — iteration order matches declaration. */
173
+ const ORDERED_GATES: readonly GateDefinition[] = Object.values(GATE_REGISTRY) as readonly GateDefinition[];
174
+
175
+ /** Return every gate owned by a turn, in stable declaration order. */
176
+ export function getGatesForTurn(turn: OwnerTurn): GateDefinition[] {
177
+ return ORDERED_GATES.filter((g) => g.ownerTurn === turn);
178
+ }
179
+
180
+ /** Return the set of gate ids a turn owns. */
181
+ export function getGateIdsForTurn(turn: OwnerTurn): Set<GateId> {
182
+ return new Set(getGatesForTurn(turn).map((g) => g.id));
183
+ }
184
+
185
+ /** Look up a definition by gate id, or undefined if unknown. */
186
+ export function getGateDefinition(id: string): GateDefinition | undefined {
187
+ return (GATE_REGISTRY as Record<string, GateDefinition>)[id];
188
+ }
189
+
190
+ /** Look up the owner turn for a gate id. Throws if the gate is unknown. */
191
+ export function getOwnerTurn(id: GateId): OwnerTurn {
192
+ const def = GATE_REGISTRY[id];
193
+ if (!def) {
194
+ throw new GSDError(GSD_PARSE_ERROR, `gate-registry: unknown gate id "${id}"`);
195
+ }
196
+ return def.ownerTurn;
197
+ }
198
+
199
+ /**
200
+ * Assert that the pending gate rows for a turn match what the registry says
201
+ * the turn owns. Fails loudly rather than silently skipping.
202
+ *
203
+ * - Every row in `pending` must have a definition whose `ownerTurn` matches `turn`.
204
+ * (The caller is responsible for scoping the pending list — e.g. filtering
205
+ * by slice scope before passing it in.)
206
+ * - `options.requireAll` (default true): every gate the turn owns must appear
207
+ * in `pending`. Set to false for turns like `execute-task` that only need
208
+ * coverage for the subset of gates that were seeded (e.g. tasks with no
209
+ * external dependencies have no Q5 row).
210
+ */
211
+ export function assertGateCoverage(
212
+ pending: ReadonlyArray<Pick<GateRow, "gate_id">>,
213
+ turn: OwnerTurn,
214
+ options: { requireAll?: boolean } = {},
215
+ ): void {
216
+ const requireAll = options.requireAll ?? true;
217
+ const expected = getGateIdsForTurn(turn);
218
+ const pendingIds = new Set(pending.map((g) => g.gate_id));
219
+
220
+ const unknown: string[] = [];
221
+ for (const id of pendingIds) {
222
+ const def = getGateDefinition(id);
223
+ if (!def) {
224
+ unknown.push(id);
225
+ continue;
226
+ }
227
+ if (def.ownerTurn !== turn) {
228
+ unknown.push(`${id} (owned by ${def.ownerTurn}, not ${turn})`);
229
+ }
230
+ }
231
+
232
+ if (unknown.length > 0) {
233
+ throw new GSDError(
234
+ GSD_PARSE_ERROR,
235
+ `assertGateCoverage: turn "${turn}" received pending gates it does not own: ${unknown.join(", ")}`,
236
+ );
237
+ }
238
+
239
+ if (requireAll) {
240
+ const missing: GateId[] = [];
241
+ for (const id of expected) {
242
+ if (!pendingIds.has(id)) missing.push(id);
243
+ }
244
+ if (missing.length > 0) {
245
+ throw new GSDError(
246
+ GSD_PARSE_ERROR,
247
+ `assertGateCoverage: turn "${turn}" is missing required gates: ${missing.join(", ")}`,
248
+ );
249
+ }
250
+ }
251
+ }
@@ -192,22 +192,25 @@ export interface PreMergeCheckResult {
192
192
  /**
193
193
  * GSD runtime paths that should be excluded from smart staging.
194
194
  * These are transient/generated artifacts that should never be committed.
195
- * Matches the union of SKIP_PATHS + SKIP_EXACT in worktree-manager.ts
196
- * and the first 7 entries in gitignore.ts BASELINE_PATTERNS.
195
+ *
196
+ * NOTE: GSD_RUNTIME_PATTERNS in gitignore.ts is the canonical source of truth.
197
+ * This array must stay synchronized with it.
197
198
  */
198
199
  export const RUNTIME_EXCLUSION_PATHS: readonly string[] = [
199
200
  ".gsd/activity/",
201
+ ".gsd/forensics/",
200
202
  ".gsd/runtime/",
201
203
  ".gsd/worktrees/",
204
+ ".gsd/parallel/",
202
205
  ".gsd/auto.lock",
203
206
  ".gsd/metrics.json",
204
- ".gsd/completed-units.json",
207
+ ".gsd/completed-units*.json", // covers completed-units.json and archived completed-units-{MID}.json
208
+ ".gsd/state-manifest.json",
205
209
  ".gsd/STATE.md",
206
- ".gsd/gsd.db",
207
- ".gsd/gsd.db-shm", // SQLite WAL sidecar — always created alongside gsd.db (#2296)
208
- ".gsd/gsd.db-wal", // SQLite WAL sidecar — always created alongside gsd.db (#2296)
209
- ".gsd/journal/", // daily-rotated JSONL event journal (#2296)
210
- ".gsd/doctor-history.jsonl", // doctor run history (#2296)
210
+ ".gsd/gsd.db*",
211
+ ".gsd/journal/",
212
+ ".gsd/doctor-history.jsonl",
213
+ ".gsd/event-log.jsonl",
211
214
  ".gsd/DISCUSSION-MANIFEST.json",
212
215
  ];
213
216
 
@@ -15,6 +15,12 @@ import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
15
15
 
16
16
  /**
17
17
  * GSD runtime patterns for git index cleanup.
18
+ *
19
+ * CANONICAL SOURCE OF TRUTH: This array is the authoritative list of runtime
20
+ * ignore patterns. Other modules (RUNTIME_EXCLUSION_PATHS in git-service.ts,
21
+ * SKIP_* arrays in worktree-manager.ts, criticalPatterns in doctor-runtime-checks.ts)
22
+ * must stay synchronized with this list.
23
+ *
18
24
  * With external state (symlink), these are a no-op in most cases,
19
25
  * but retained for backwards compatibility during migration.
20
26
  */
@@ -26,13 +32,13 @@ const GSD_RUNTIME_PATTERNS = [
26
32
  ".gsd/parallel/",
27
33
  ".gsd/auto.lock",
28
34
  ".gsd/metrics.json",
29
- ".gsd/completed-units.json",
35
+ ".gsd/completed-units*.json", // covers completed-units.json and archived completed-units-{MID}.json
36
+ ".gsd/state-manifest.json",
30
37
  ".gsd/STATE.md",
31
- ".gsd/gsd.db",
32
- ".gsd/gsd.db-shm", // SQLite WAL sidecar — always created alongside gsd.db (#2296)
33
- ".gsd/gsd.db-wal", // SQLite WAL sidecar — always created alongside gsd.db (#2296)
34
- ".gsd/journal/", // daily-rotated JSONL event journal (#2296)
35
- ".gsd/doctor-history.jsonl", // doctor run history (#2296)
38
+ ".gsd/gsd.db*",
39
+ ".gsd/journal/",
40
+ ".gsd/doctor-history.jsonl",
41
+ ".gsd/event-log.jsonl",
36
42
  ".gsd/DISCUSSION-MANIFEST.json",
37
43
  ".gsd/milestones/**/*-CONTINUE.md",
38
44
  ".gsd/milestones/**/continue.md",
@@ -10,6 +10,7 @@ import { existsSync, copyFileSync, mkdirSync, realpathSync } from "node:fs";
10
10
  import { dirname } from "node:path";
11
11
  import type { Decision, Requirement, GateRow, GateId, GateScope, GateStatus, GateVerdict } from "./types.js";
12
12
  import { GSDError, GSD_STALE_STATE } from "./errors.js";
13
+ import { getGateIdsForTurn, type OwnerTurn } from "./gate-registry.js";
13
14
  import { logError, logWarning } from "./workflow-logger.js";
14
15
 
15
16
  const _require = createRequire(import.meta.url);
@@ -162,13 +163,36 @@ function openRawDb(path: string): unknown {
162
163
 
163
164
  const SCHEMA_VERSION = 14;
164
165
 
166
+ function indexExists(db: DbAdapter, name: string): boolean {
167
+ return !!db.prepare(
168
+ "SELECT 1 as present FROM sqlite_master WHERE type = 'index' AND name = ?",
169
+ ).get(name);
170
+ }
171
+
172
+ function dedupeVerificationEvidenceRows(db: DbAdapter): void {
173
+ db.exec(`
174
+ DELETE FROM verification_evidence
175
+ WHERE rowid NOT IN (
176
+ SELECT MIN(rowid)
177
+ FROM verification_evidence
178
+ GROUP BY task_id, slice_id, milestone_id, command, verdict
179
+ )
180
+ `);
181
+ }
182
+
183
+ function ensureVerificationEvidenceDedupIndex(db: DbAdapter): void {
184
+ if (indexExists(db, "idx_verification_evidence_dedup")) return;
185
+ dedupeVerificationEvidenceRows(db);
186
+ db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
187
+ }
188
+
165
189
  function initSchema(db: DbAdapter, fileBacked: boolean): void {
166
190
  if (fileBacked) db.exec("PRAGMA journal_mode=WAL");
167
191
  if (fileBacked) db.exec("PRAGMA busy_timeout = 5000");
168
192
  if (fileBacked) db.exec("PRAGMA synchronous = NORMAL");
169
193
  if (fileBacked) db.exec("PRAGMA auto_vacuum = INCREMENTAL");
170
194
  if (fileBacked) db.exec("PRAGMA cache_size = -8000"); // 8 MB page cache
171
- if (fileBacked) db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
195
+ if (fileBacked && process.platform !== "darwin") db.exec("PRAGMA mmap_size = 67108864"); // 64 MB mmap
172
196
  db.exec("PRAGMA temp_store = MEMORY");
173
197
  db.exec("PRAGMA foreign_keys = ON");
174
198
 
@@ -409,7 +433,7 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
409
433
  db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
410
434
  db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
411
435
  db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
412
- db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
436
+ ensureVerificationEvidenceDedupIndex(db);
413
437
 
414
438
  // v14 index — slice dependency lookups
415
439
  db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
@@ -742,7 +766,7 @@ function migrateSchema(db: DbAdapter): void {
742
766
  db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
743
767
  db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
744
768
  db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
745
- db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
769
+ ensureVerificationEvidenceDedupIndex(db);
746
770
  db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
747
771
  ":version": 13,
748
772
  ":applied_at": new Date().toISOString(),
@@ -856,6 +880,7 @@ export function closeDatabase(): void {
856
880
  currentDb = null;
857
881
  currentPath = null;
858
882
  currentPid = 0;
883
+ _dbOpenAttempted = false;
859
884
  }
860
885
  }
861
886
 
@@ -1540,6 +1565,30 @@ export interface TaskRow {
1540
1565
  }
1541
1566
 
1542
1567
  function rowToTask(row: Record<string, unknown>): TaskRow {
1568
+ const parseTaskArray = (value: unknown): string[] => {
1569
+ if (Array.isArray(value)) {
1570
+ return value.filter((entry): entry is string => typeof entry === "string");
1571
+ }
1572
+ if (typeof value !== "string") return [];
1573
+
1574
+ const trimmed = value.trim();
1575
+ if (!trimmed) return [];
1576
+
1577
+ try {
1578
+ const parsed = JSON.parse(trimmed);
1579
+ if (Array.isArray(parsed)) {
1580
+ return parsed.filter((entry): entry is string => typeof entry === "string");
1581
+ }
1582
+ if (typeof parsed === "string" && parsed.trim()) {
1583
+ return [parsed.trim()];
1584
+ }
1585
+ } catch {
1586
+ // Older/corrupt DB rows may contain raw comma-separated paths instead of JSON arrays.
1587
+ }
1588
+
1589
+ return trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
1590
+ };
1591
+
1543
1592
  return {
1544
1593
  milestone_id: row["milestone_id"] as string,
1545
1594
  slice_id: row["slice_id"] as string,
@@ -1559,10 +1608,10 @@ function rowToTask(row: Record<string, unknown>): TaskRow {
1559
1608
  full_summary_md: row["full_summary_md"] as string,
1560
1609
  description: (row["description"] as string) ?? "",
1561
1610
  estimate: (row["estimate"] as string) ?? "",
1562
- files: JSON.parse((row["files"] as string) || "[]"),
1611
+ files: parseTaskArray(row["files"]),
1563
1612
  verify: (row["verify"] as string) ?? "",
1564
- inputs: JSON.parse((row["inputs"] as string) || "[]"),
1565
- expected_output: JSON.parse((row["expected_output"] as string) || "[]"),
1613
+ inputs: parseTaskArray(row["inputs"]),
1614
+ expected_output: parseTaskArray(row["expected_output"]),
1566
1615
  observability_impact: (row["observability_impact"] as string) ?? "",
1567
1616
  full_plan_md: (row["full_plan_md"] as string) ?? "",
1568
1617
  sequence: (row["sequence"] as number) ?? 0,
@@ -2302,3 +2351,53 @@ export function getPendingSliceGateCount(milestoneId: string, sliceId: string):
2302
2351
  ).get({ ":mid": milestoneId, ":sid": sliceId });
2303
2352
  return row ? (row["cnt"] as number) : 0;
2304
2353
  }
2354
+
2355
+ /**
2356
+ * Return pending gate rows owned by a specific workflow turn.
2357
+ *
2358
+ * Unlike `getPendingGates(..., scope)`, this filters by the registry's
2359
+ * `ownerTurn` metadata so callers can distinguish Q3/Q4 (owned by
2360
+ * gate-evaluate) from Q8 (owned by complete-slice) even though both are
2361
+ * scope:"slice". Pass `taskId` to narrow task-scoped results to one task.
2362
+ */
2363
+ export function getPendingGatesForTurn(
2364
+ milestoneId: string,
2365
+ sliceId: string,
2366
+ turn: OwnerTurn,
2367
+ taskId?: string,
2368
+ ): GateRow[] {
2369
+ if (!currentDb) return [];
2370
+ const ids = getGateIdsForTurn(turn);
2371
+ if (ids.size === 0) return [];
2372
+ const idList = [...ids];
2373
+ const placeholders = idList.map((_, i) => `:gid${i}`).join(",");
2374
+ const params: Record<string, unknown> = {
2375
+ ":mid": milestoneId,
2376
+ ":sid": sliceId,
2377
+ };
2378
+ idList.forEach((id, i) => {
2379
+ params[`:gid${i}`] = id;
2380
+ });
2381
+ let sql =
2382
+ `SELECT * FROM quality_gates
2383
+ WHERE milestone_id = :mid AND slice_id = :sid
2384
+ AND status = 'pending'
2385
+ AND gate_id IN (${placeholders})`;
2386
+ if (taskId !== undefined) {
2387
+ sql += ` AND task_id = :tid`;
2388
+ params[":tid"] = taskId;
2389
+ }
2390
+ return currentDb.prepare(sql).all(params).map(rowToGate);
2391
+ }
2392
+
2393
+ /**
2394
+ * Count pending gates for a turn. Convenience wrapper used by state
2395
+ * derivation to decide whether a phase transition should pause.
2396
+ */
2397
+ export function getPendingGateCountForTurn(
2398
+ milestoneId: string,
2399
+ sliceId: string,
2400
+ turn: OwnerTurn,
2401
+ ): number {
2402
+ return getPendingGatesForTurn(milestoneId, sliceId, turn).length;
2403
+ }