gsd-pi 2.71.0 → 2.72.0-dev.8f83716

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 (465) hide show
  1. package/README.md +57 -17
  2. package/dist/cli.js +29 -3
  3. package/dist/headless-events.d.ts +2 -0
  4. package/dist/headless-events.js +7 -0
  5. package/dist/headless.js +16 -3
  6. package/dist/mcp-server.js +40 -17
  7. package/dist/provider-migrations.d.ts +10 -0
  8. package/dist/provider-migrations.js +12 -0
  9. package/dist/resource-loader.js +139 -13
  10. package/dist/resources/GSD-WORKFLOW.md +1 -1
  11. package/dist/resources/agents/debugger.md +58 -0
  12. package/dist/resources/agents/doc-writer.md +43 -0
  13. package/dist/resources/agents/git-ops.md +56 -0
  14. package/dist/resources/agents/javascript-pro.md +46 -271
  15. package/dist/resources/agents/planner.md +55 -0
  16. package/dist/resources/agents/refactorer.md +47 -0
  17. package/dist/resources/agents/reviewer.md +48 -0
  18. package/dist/resources/agents/security.md +59 -0
  19. package/dist/resources/agents/tester.md +50 -0
  20. package/dist/resources/agents/typescript-pro.md +41 -235
  21. package/dist/resources/extensions/claude-code-cli/partial-builder.js +40 -12
  22. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +133 -14
  23. package/dist/resources/extensions/gsd/auto/infra-errors.js +34 -0
  24. package/dist/resources/extensions/gsd/auto/loop.js +32 -1
  25. package/dist/resources/extensions/gsd/auto/phases.js +5 -1
  26. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  27. package/dist/resources/extensions/gsd/auto-dashboard.js +22 -16
  28. package/dist/resources/extensions/gsd/auto-model-selection.js +10 -2
  29. package/dist/resources/extensions/gsd/auto-prompts.js +88 -33
  30. package/dist/resources/extensions/gsd/auto-start.js +34 -7
  31. package/dist/resources/extensions/gsd/auto-tool-tracking.js +1 -1
  32. package/dist/resources/extensions/gsd/auto-worktree.js +1 -1
  33. package/dist/resources/extensions/gsd/auto.js +56 -0
  34. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +3 -3
  35. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +2 -0
  36. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +63 -51
  37. package/dist/resources/extensions/gsd/bootstrap/system-context.js +6 -0
  38. package/dist/resources/extensions/gsd/commands/context.js +15 -6
  39. package/dist/resources/extensions/gsd/commands/dispatcher.js +12 -2
  40. package/dist/resources/extensions/gsd/commands/handlers/auto.js +10 -33
  41. package/dist/resources/extensions/gsd/commands/handlers/core.js +56 -11
  42. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +15 -6
  43. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +4 -10
  44. package/dist/resources/extensions/gsd/dashboard-overlay.js +8 -3
  45. package/dist/resources/extensions/gsd/dispatch-guard.js +18 -1
  46. package/dist/resources/extensions/gsd/doctor-providers.js +23 -0
  47. package/dist/resources/extensions/gsd/error-classifier.js +5 -2
  48. package/dist/resources/extensions/gsd/forensics.js +19 -6
  49. package/dist/resources/extensions/gsd/gate-registry.js +208 -0
  50. package/dist/resources/extensions/gsd/gsd-db.js +41 -0
  51. package/dist/resources/extensions/gsd/guided-flow.js +5 -10
  52. package/dist/resources/extensions/gsd/metrics.js +1 -0
  53. package/dist/resources/extensions/gsd/milestone-actions.js +10 -4
  54. package/dist/resources/extensions/gsd/milestone-validation-gates.js +11 -12
  55. package/dist/resources/extensions/gsd/notification-overlay.js +42 -13
  56. package/dist/resources/extensions/gsd/notification-store.js +56 -5
  57. package/dist/resources/extensions/gsd/notification-widget.js +5 -13
  58. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +8 -3
  59. package/dist/resources/extensions/gsd/pre-execution-checks.js +35 -2
  60. package/dist/resources/extensions/gsd/prompt-validation.js +126 -0
  61. package/dist/resources/extensions/gsd/prompts/complete-slice.md +5 -3
  62. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -0
  63. package/dist/resources/extensions/gsd/prompts/execute-task.md +22 -19
  64. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  65. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  66. package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  67. package/dist/resources/extensions/gsd/prompts/queue.md +3 -2
  68. package/dist/resources/extensions/gsd/prompts/system.md +1 -0
  69. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
  70. package/dist/resources/extensions/gsd/session-model-override.js +25 -0
  71. package/dist/resources/extensions/gsd/shortcut-defs.js +40 -0
  72. package/dist/resources/extensions/gsd/state.js +9 -2
  73. package/dist/resources/extensions/gsd/tools/complete-slice.js +52 -1
  74. package/dist/resources/extensions/gsd/tools/complete-task.js +51 -1
  75. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +4 -1
  76. package/dist/resources/extensions/ollama/index.js +13 -5
  77. package/dist/resources/extensions/shared/gsd-phase-state.js +35 -0
  78. package/dist/resources/extensions/subagent/agents.js +8 -0
  79. package/dist/resources/extensions/subagent/index.js +17 -0
  80. package/dist/resources/skills/create-skill/SKILL.md +2 -0
  81. package/dist/startup-model-validation.d.ts +0 -1
  82. package/dist/startup-model-validation.js +6 -2
  83. package/dist/web/standalone/.next/BUILD_ID +1 -1
  84. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  85. package/dist/web/standalone/.next/build-manifest.json +3 -3
  86. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  87. package/dist/web/standalone/.next/required-server-files.json +3 -3
  88. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  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.js +2 -2
  99. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  115. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  127. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  147. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  157. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  163. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  179. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  183. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/index.html +1 -1
  193. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  194. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  195. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  196. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  197. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  198. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  199. package/dist/web/standalone/.next/server/app/page.js +2 -2
  200. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  202. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  203. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  204. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  205. package/dist/web/standalone/.next/server/middleware.js +2 -2
  206. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  207. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  208. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  209. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  210. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  211. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  212. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  213. package/dist/web/standalone/.next/static/chunks/app/page-f1e30ab6bb269149.js +1 -0
  214. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  215. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  216. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  217. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  218. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  219. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  220. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  221. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  222. package/dist/web/standalone/server.js +1 -1
  223. package/package.json +1 -1
  224. package/packages/mcp-server/dist/server.d.ts +12 -1
  225. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  226. package/packages/mcp-server/dist/server.js +90 -42
  227. package/packages/mcp-server/dist/server.js.map +1 -1
  228. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  229. package/packages/mcp-server/dist/workflow-tools.js +22 -12
  230. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  231. package/packages/mcp-server/src/server.ts +110 -38
  232. package/packages/mcp-server/src/workflow-tools.test.ts +110 -0
  233. package/packages/mcp-server/src/workflow-tools.ts +32 -12
  234. package/packages/pi-ai/dist/providers/amazon-bedrock.js +11 -2
  235. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  236. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts +2 -0
  237. package/packages/pi-ai/dist/providers/anthropic-auth.test.d.ts.map +1 -0
  238. package/packages/pi-ai/dist/providers/anthropic-auth.test.js +20 -0
  239. package/packages/pi-ai/dist/providers/anthropic-auth.test.js.map +1 -0
  240. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts +4 -1
  241. package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
  242. package/packages/pi-ai/dist/providers/anthropic-shared.js +8 -3
  243. package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
  244. package/packages/pi-ai/dist/providers/anthropic-shared.test.js +44 -1
  245. package/packages/pi-ai/dist/providers/anthropic-shared.test.js.map +1 -1
  246. package/packages/pi-ai/dist/providers/anthropic.d.ts +2 -1
  247. package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
  248. package/packages/pi-ai/dist/providers/anthropic.js +7 -4
  249. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  250. package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
  251. package/packages/pi-ai/dist/providers/openai-completions.js +11 -0
  252. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  253. package/packages/pi-ai/src/providers/amazon-bedrock.ts +13 -1
  254. package/packages/pi-ai/src/providers/anthropic-auth.test.ts +32 -0
  255. package/packages/pi-ai/src/providers/anthropic-shared.test.ts +55 -1
  256. package/packages/pi-ai/src/providers/anthropic-shared.ts +14 -3
  257. package/packages/pi-ai/src/providers/anthropic.ts +8 -4
  258. package/packages/pi-ai/src/providers/openai-completions.ts +14 -0
  259. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts +2 -0
  260. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.d.ts.map +1 -0
  261. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js +61 -0
  262. package/packages/pi-coding-agent/dist/core/agent-session-renderable-tools.test.js.map +1 -0
  263. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  264. package/packages/pi-coding-agent/dist/core/agent-session.js +2 -1
  265. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  266. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +10 -0
  267. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  268. package/packages/pi-coding-agent/dist/core/auth-storage.js +27 -0
  269. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  270. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +85 -0
  271. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  272. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts +2 -0
  273. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.d.ts.map +1 -0
  274. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js +64 -0
  275. package/packages/pi-coding-agent/dist/core/model-resolver-initial-model-auth.test.js.map +1 -0
  276. package/packages/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  277. package/packages/pi-coding-agent/dist/core/model-resolver.js +22 -18
  278. package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  279. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts +8 -0
  280. package/packages/pi-coding-agent/dist/core/model-resolver.test.d.ts.map +1 -0
  281. package/packages/pi-coding-agent/dist/core/model-resolver.test.js +75 -0
  282. package/packages/pi-coding-agent/dist/core/model-resolver.test.js.map +1 -0
  283. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts +5 -0
  284. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/retry-handler.js +55 -1
  286. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +57 -0
  288. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/sdk.d.ts +11 -0
  290. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  291. package/packages/pi-coding-agent/dist/core/sdk.js +38 -5
  292. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts +2 -0
  294. package/packages/pi-coding-agent/dist/core/sdk.test.d.ts.map +1 -0
  295. package/packages/pi-coding-agent/dist/core/sdk.test.js +71 -0
  296. package/packages/pi-coding-agent/dist/core/sdk.test.js.map +1 -0
  297. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  298. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  299. package/packages/pi-coding-agent/dist/index.js +1 -1
  300. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  301. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts +2 -0
  302. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.d.ts.map +1 -0
  303. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js +13 -0
  304. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/login-dialog.test.js.map +1 -0
  305. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -0
  306. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  307. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts +4 -0
  308. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  309. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js +24 -2
  310. package/packages/pi-coding-agent/dist/modes/interactive/components/login-dialog.js.map +1 -1
  311. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  312. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +9 -2
  313. package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js.map +1 -1
  314. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +4 -0
  315. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  316. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +130 -12
  317. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  318. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  319. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +7 -2
  320. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  321. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -1
  322. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +6 -1
  323. package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -1
  324. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  325. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -3
  326. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  327. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +4 -2
  328. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  329. package/packages/pi-coding-agent/package.json +1 -1
  330. package/packages/pi-coding-agent/src/core/agent-session-renderable-tools.test.ts +70 -0
  331. package/packages/pi-coding-agent/src/core/agent-session.ts +2 -1
  332. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +108 -0
  333. package/packages/pi-coding-agent/src/core/auth-storage.ts +30 -0
  334. package/packages/pi-coding-agent/src/core/model-resolver-initial-model-auth.test.ts +78 -0
  335. package/packages/pi-coding-agent/src/core/model-resolver.test.ts +85 -0
  336. package/packages/pi-coding-agent/src/core/model-resolver.ts +22 -18
  337. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +83 -0
  338. package/packages/pi-coding-agent/src/core/retry-handler.ts +60 -1
  339. package/packages/pi-coding-agent/src/core/sdk.test.ts +89 -0
  340. package/packages/pi-coding-agent/src/core/sdk.ts +45 -9
  341. package/packages/pi-coding-agent/src/index.ts +1 -0
  342. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/login-dialog.test.ts +24 -0
  343. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +72 -0
  344. package/packages/pi-coding-agent/src/modes/interactive/components/login-dialog.ts +30 -2
  345. package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +15 -6
  346. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +131 -12
  347. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +7 -2
  348. package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +6 -1
  349. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +4 -3
  350. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +4 -2
  351. package/pkg/package.json +1 -1
  352. package/src/resources/GSD-WORKFLOW.md +1 -1
  353. package/src/resources/agents/debugger.md +58 -0
  354. package/src/resources/agents/doc-writer.md +43 -0
  355. package/src/resources/agents/git-ops.md +56 -0
  356. package/src/resources/agents/javascript-pro.md +46 -271
  357. package/src/resources/agents/planner.md +55 -0
  358. package/src/resources/agents/refactorer.md +47 -0
  359. package/src/resources/agents/reviewer.md +48 -0
  360. package/src/resources/agents/security.md +59 -0
  361. package/src/resources/agents/tester.md +50 -0
  362. package/src/resources/agents/typescript-pro.md +41 -235
  363. package/src/resources/extensions/claude-code-cli/partial-builder.ts +45 -12
  364. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +143 -13
  365. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +91 -2
  366. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +281 -6
  367. package/src/resources/extensions/gsd/auto/infra-errors.ts +38 -0
  368. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -0
  369. package/src/resources/extensions/gsd/auto/loop.ts +45 -1
  370. package/src/resources/extensions/gsd/auto/phases.ts +6 -0
  371. package/src/resources/extensions/gsd/auto/session.ts +11 -0
  372. package/src/resources/extensions/gsd/auto-dashboard.ts +29 -18
  373. package/src/resources/extensions/gsd/auto-model-selection.ts +9 -1
  374. package/src/resources/extensions/gsd/auto-prompts.ts +111 -33
  375. package/src/resources/extensions/gsd/auto-start.ts +41 -7
  376. package/src/resources/extensions/gsd/auto-tool-tracking.ts +1 -1
  377. package/src/resources/extensions/gsd/auto-worktree.ts +1 -1
  378. package/src/resources/extensions/gsd/auto.ts +72 -0
  379. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +3 -3
  380. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +2 -0
  381. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +79 -60
  382. package/src/resources/extensions/gsd/bootstrap/system-context.ts +7 -0
  383. package/src/resources/extensions/gsd/commands/context.ts +16 -5
  384. package/src/resources/extensions/gsd/commands/dispatcher.ts +14 -2
  385. package/src/resources/extensions/gsd/commands/handlers/auto.ts +10 -36
  386. package/src/resources/extensions/gsd/commands/handlers/core.ts +58 -11
  387. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +17 -7
  388. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +4 -10
  389. package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -3
  390. package/src/resources/extensions/gsd/dispatch-guard.ts +18 -1
  391. package/src/resources/extensions/gsd/doctor-providers.ts +24 -0
  392. package/src/resources/extensions/gsd/error-classifier.ts +5 -2
  393. package/src/resources/extensions/gsd/forensics.ts +23 -7
  394. package/src/resources/extensions/gsd/gate-registry.ts +251 -0
  395. package/src/resources/extensions/gsd/gsd-db.ts +51 -0
  396. package/src/resources/extensions/gsd/guided-flow.ts +5 -10
  397. package/src/resources/extensions/gsd/interrupted-session.ts +1 -0
  398. package/src/resources/extensions/gsd/metrics.ts +12 -1
  399. package/src/resources/extensions/gsd/milestone-actions.ts +10 -3
  400. package/src/resources/extensions/gsd/milestone-validation-gates.ts +11 -13
  401. package/src/resources/extensions/gsd/notification-overlay.ts +47 -14
  402. package/src/resources/extensions/gsd/notification-store.ts +54 -5
  403. package/src/resources/extensions/gsd/notification-widget.ts +5 -14
  404. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -3
  405. package/src/resources/extensions/gsd/pre-execution-checks.ts +39 -2
  406. package/src/resources/extensions/gsd/prompt-validation.ts +157 -0
  407. package/src/resources/extensions/gsd/prompts/complete-slice.md +5 -3
  408. package/src/resources/extensions/gsd/prompts/discuss.md +2 -0
  409. package/src/resources/extensions/gsd/prompts/execute-task.md +22 -19
  410. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +2 -0
  411. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +2 -0
  412. package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
  413. package/src/resources/extensions/gsd/prompts/queue.md +3 -2
  414. package/src/resources/extensions/gsd/prompts/system.md +1 -0
  415. package/src/resources/extensions/gsd/prompts/validate-milestone.md +4 -1
  416. package/src/resources/extensions/gsd/session-model-override.ts +36 -0
  417. package/src/resources/extensions/gsd/shortcut-defs.ts +56 -0
  418. package/src/resources/extensions/gsd/state.ts +13 -2
  419. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +25 -9
  420. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +28 -0
  421. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +39 -0
  422. package/src/resources/extensions/gsd/tests/complete-slice-gate-closure.test.ts +167 -0
  423. package/src/resources/extensions/gsd/tests/complete-slice-prompt-task-summary-layout.test.ts +18 -0
  424. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  425. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +36 -0
  426. package/src/resources/extensions/gsd/tests/execute-task-prompt-existing-artifact-guard.test.ts +33 -0
  427. package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +62 -0
  428. package/src/resources/extensions/gsd/tests/format-shortcut.test.ts +31 -0
  429. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +27 -0
  430. package/src/resources/extensions/gsd/tests/gate-registry.test.ts +140 -0
  431. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +73 -0
  432. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +180 -0
  433. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +66 -1
  434. package/src/resources/extensions/gsd/tests/model-isolation.test.ts +36 -51
  435. package/src/resources/extensions/gsd/tests/notification-store.test.ts +35 -0
  436. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +26 -0
  437. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +90 -0
  438. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +1 -0
  439. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +18 -0
  440. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +49 -0
  441. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +19 -0
  442. package/src/resources/extensions/gsd/tests/prompt-system-gate-coverage.test.ts +208 -0
  443. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -0
  444. package/src/resources/extensions/gsd/tests/register-shortcuts.test.ts +63 -5
  445. package/src/resources/extensions/gsd/tests/session-model-override.test.ts +35 -0
  446. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +90 -0
  447. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +7 -0
  448. package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +18 -0
  449. package/src/resources/extensions/gsd/tools/complete-slice.ts +63 -0
  450. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -0
  451. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +4 -1
  452. package/src/resources/extensions/gsd/types.ts +26 -0
  453. package/src/resources/extensions/ollama/index.ts +13 -3
  454. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +28 -0
  455. package/src/resources/extensions/shared/gsd-phase-state.ts +42 -0
  456. package/src/resources/extensions/shared/tests/gsd-phase-state.test.ts +48 -0
  457. package/src/resources/extensions/subagent/agents.ts +10 -0
  458. package/src/resources/extensions/subagent/index.ts +18 -0
  459. package/src/resources/extensions/subagent/tests/agents-conflicts.test.ts +33 -0
  460. package/src/resources/skills/create-skill/SKILL.md +2 -0
  461. package/dist/web/standalone/.next/static/chunks/app/page-7115e62689b5fd84.js +0 -1
  462. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  463. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  464. /package/dist/web/standalone/.next/static/{nPky_WQC28aBD77eZsRAB → nCL1Ivw47iAAoKVgerXNi}/_buildManifest.js +0 -0
  465. /package/dist/web/standalone/.next/static/{nPky_WQC28aBD77eZsRAB → nCL1Ivw47iAAoKVgerXNi}/_ssgManifest.js +0 -0
@@ -47,3 +47,37 @@ export function isInfrastructureError(err) {
47
47
  return "SQLITE_CORRUPT";
48
48
  return null;
49
49
  }
50
+ /**
51
+ * Default wait duration when a cooldown error is detected but no specific
52
+ * expiry is available from AuthStorage (e.g., error propagated across
53
+ * process boundary without structured backoff data).
54
+ */
55
+ export const COOLDOWN_FALLBACK_WAIT_MS = 35_000; // 35s — slightly longer than the 30s rate-limit backoff
56
+ /** Maximum consecutive cooldown retries before the auto-loop gives up. */
57
+ export const MAX_COOLDOWN_RETRIES = 5;
58
+ /**
59
+ * Detect whether an error is a transient credential cooldown that should
60
+ * be waited out rather than counted as a consecutive failure.
61
+ *
62
+ * Prefers the structured `CredentialCooldownError` (code: AUTH_COOLDOWN)
63
+ * thrown by sdk.ts. Falls back to message matching for errors that
64
+ * propagated across process boundaries without the typed class.
65
+ */
66
+ export function isTransientCooldownError(err) {
67
+ if (err && typeof err === "object" && err.code === "AUTH_COOLDOWN") {
68
+ return true;
69
+ }
70
+ // Fallback: message match for cross-process error propagation
71
+ const msg = err instanceof Error ? err.message : String(err);
72
+ return /in a cooldown window/i.test(msg);
73
+ }
74
+ /**
75
+ * Extract retryAfterMs from a CredentialCooldownError, if available.
76
+ * Returns undefined for unstructured errors or when no retry hint exists.
77
+ */
78
+ export function getCooldownRetryAfterMs(err) {
79
+ if (err && typeof err === "object" && err.code === "AUTH_COOLDOWN") {
80
+ return err.retryAfterMs;
81
+ }
82
+ return undefined;
83
+ }
@@ -11,7 +11,7 @@ import { MAX_LOOP_ITERATIONS, } from "./types.js";
11
11
  import { _clearCurrentResolve } from "./resolve.js";
12
12
  import { runPreDispatch, runDispatch, runGuards, runUnitPhase, runFinalize, } from "./phases.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
- import { isInfrastructureError } from "./infra-errors.js";
14
+ import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
15
15
  import { resolveEngine } from "../engine-resolver.js";
16
16
  /**
17
17
  * Main auto-mode execution loop. Iterates: derive → dispatch → guards →
@@ -26,6 +26,7 @@ export async function autoLoop(ctx, pi, s, deps) {
26
26
  let iteration = 0;
27
27
  const loopState = { recentUnits: [], stuckRecoveryAttempts: 0, consecutiveFinalizeTimeouts: 0 };
28
28
  let consecutiveErrors = 0;
29
+ let consecutiveCooldowns = 0;
29
30
  const recentErrorMessages = [];
30
31
  while (s.active) {
31
32
  iteration++;
@@ -158,6 +159,7 @@ export async function autoLoop(ctx, pi, s, deps) {
158
159
  });
159
160
  deps.clearUnitTimeout();
160
161
  consecutiveErrors = 0;
162
+ consecutiveCooldowns = 0;
161
163
  recentErrorMessages.length = 0;
162
164
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
163
165
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
@@ -220,6 +222,7 @@ export async function autoLoop(ctx, pi, s, deps) {
220
222
  if (finalizeResult.action === "continue")
221
223
  continue;
222
224
  consecutiveErrors = 0; // Iteration completed successfully
225
+ consecutiveCooldowns = 0;
223
226
  recentErrorMessages.length = 0;
224
227
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration } });
225
228
  debugLog("autoLoop", { phase: "iteration-complete", iteration });
@@ -246,6 +249,34 @@ export async function autoLoop(ctx, pi, s, deps) {
246
249
  await deps.stopAuto(ctx, pi, `Infrastructure error (${infraCode}): not recoverable by retry`);
247
250
  break;
248
251
  }
252
+ // ── Credential cooldown: wait and retry with bounded budget ──
253
+ // A 429 triggers a 30s credential backoff in AuthStorage. If the SDK's
254
+ // getApiKey() retries couldn't outlast the window, the error surfaces
255
+ // here. Wait for the cooldown to clear rather than counting it as a
256
+ // consecutive failure — but cap retries so we don't spin for hours
257
+ // on persistent quota exhaustion.
258
+ if (isTransientCooldownError(loopErr)) {
259
+ consecutiveCooldowns++;
260
+ const retryAfterMs = getCooldownRetryAfterMs(loopErr);
261
+ debugLog("autoLoop", {
262
+ phase: "cooldown-wait",
263
+ iteration,
264
+ consecutiveCooldowns,
265
+ retryAfterMs,
266
+ error: msg,
267
+ });
268
+ if (consecutiveCooldowns > MAX_COOLDOWN_RETRIES) {
269
+ ctx.ui.notify(`Auto-mode stopped: ${consecutiveCooldowns} consecutive credential cooldowns — rate limit or quota may be persistently exhausted.`, "error");
270
+ await deps.stopAuto(ctx, pi, `${consecutiveCooldowns} consecutive credential cooldowns exceeded retry budget`);
271
+ break;
272
+ }
273
+ const waitMs = (retryAfterMs !== undefined && retryAfterMs > 0 && retryAfterMs <= 60_000)
274
+ ? retryAfterMs + 500 // Use structured hint + small buffer
275
+ : COOLDOWN_FALLBACK_WAIT_MS;
276
+ ctx.ui.notify(`Credentials in cooldown (${consecutiveCooldowns}/${MAX_COOLDOWN_RETRIES}) — waiting ${Math.round(waitMs / 1000)}s before retrying.`, "warning");
277
+ await new Promise(resolve => setTimeout(resolve, waitMs));
278
+ continue; // Retry iteration without incrementing consecutiveErrors
279
+ }
249
280
  consecutiveErrors++;
250
281
  recentErrorMessages.push(msg.length > 120 ? msg.slice(0, 120) + "..." : msg);
251
282
  debugLog("autoLoop", {
@@ -13,6 +13,7 @@ import { runUnit } from "./run-unit.js";
13
13
  import { debugLog } from "../debug-logger.js";
14
14
  import { PROJECT_FILES } from "../detection.js";
15
15
  import { MergeConflictError } from "../git-service.js";
16
+ import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
16
17
  import { join, basename, dirname, parse as parsePath } from "node:path";
17
18
  import { existsSync, cpSync, readdirSync } from "node:fs";
18
19
  import { logWarning, logError } from "../workflow-logger.js";
@@ -770,6 +771,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
770
771
  s.currentUnit.id === unitId);
771
772
  const previousTier = s.currentUnitRouting?.tier;
772
773
  s.currentUnit = { type: unitType, id: unitId, startedAt: Date.now() };
774
+ setCurrentPhase(unitType);
773
775
  s.lastToolInvocationError = null; // #2883: clear stale error from previous unit
774
776
  const unitStartSeq = ic.nextSeq();
775
777
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: unitStartSeq, eventType: "unit-start", data: { unitType, unitId } });
@@ -857,7 +859,7 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
857
859
  logWarning("engine", "Prompt reorder failed", { error: msg });
858
860
  }
859
861
  // Select and apply model (with tier escalation on retry — normal units only)
860
- const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier });
862
+ const modelResult = await deps.selectAndApplyModel(ctx, pi, unitType, unitId, s.basePath, prefs, s.verbose, s.autoModeStartModel, sidecarItem ? undefined : { isRetry, previousTier }, undefined, s.manualSessionModelOverride);
861
863
  s.currentUnitRouting =
862
864
  modelResult.routing;
863
865
  s.currentUnitModel =
@@ -1115,6 +1117,7 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
1115
1117
  // Detach session from the timed-out unit so late async completions
1116
1118
  // cannot mutate state for the next unit (#3757).
1117
1119
  s.currentUnit = null;
1120
+ clearCurrentPhase();
1118
1121
  loopState.consecutiveFinalizeTimeouts++;
1119
1122
  debugLog("autoLoop", {
1120
1123
  phase: "pre-verification-timeout",
@@ -1189,6 +1192,7 @@ export async function runFinalize(ic, iterData, loopState, sidecarItem) {
1189
1192
  // Detach session from the timed-out unit so late async completions
1190
1193
  // cannot mutate state for the next unit (#3757).
1191
1194
  s.currentUnit = null;
1195
+ clearCurrentPhase();
1192
1196
  loopState.consecutiveFinalizeTimeouts++;
1193
1197
  debugLog("autoLoop", {
1194
1198
  phase: "post-verification-timeout",
@@ -36,6 +36,10 @@ export class AutoSession {
36
36
  previousProjectRootEnv = null;
37
37
  hadProjectRootEnv = false;
38
38
  projectRootEnvCaptured = false;
39
+ previousMilestoneLockEnv = null;
40
+ hadMilestoneLockEnv = false;
41
+ milestoneLockEnvCaptured = false;
42
+ sessionMilestoneLock = null;
39
43
  gitService = null;
40
44
  // ── Dispatch counters ────────────────────────────────────────────────────
41
45
  unitDispatchCount = new Map();
@@ -52,6 +56,8 @@ export class AutoSession {
52
56
  currentMilestoneId = null;
53
57
  // ── Model state ──────────────────────────────────────────────────────────
54
58
  autoModeStartModel = null;
59
+ /** Explicit /gsd model pin captured at bootstrap (session-scoped policy override). */
60
+ manualSessionModelOverride = null;
55
61
  currentUnitModel = null;
56
62
  /** Fully-qualified model ID (provider/id) set after selectAndApplyModel + hook overrides (#2899). */
57
63
  currentDispatchedModelId = null;
@@ -140,6 +146,10 @@ export class AutoSession {
140
146
  this.previousProjectRootEnv = null;
141
147
  this.hadProjectRootEnv = false;
142
148
  this.projectRootEnvCaptured = false;
149
+ this.previousMilestoneLockEnv = null;
150
+ this.hadMilestoneLockEnv = false;
151
+ this.milestoneLockEnvCaptured = false;
152
+ this.sessionMilestoneLock = null;
143
153
  this.gitService = null;
144
154
  // Dispatch
145
155
  this.unitDispatchCount.clear();
@@ -151,6 +161,7 @@ export class AutoSession {
151
161
  this.currentMilestoneId = null;
152
162
  // Model
153
163
  this.autoModeStartModel = null;
164
+ this.manualSessionModelOverride = null;
154
165
  this.currentUnitModel = null;
155
166
  this.currentDispatchedModelId = null;
156
167
  this.originalModelId = null;
@@ -10,7 +10,6 @@ import { getActiveHook } from "./post-unit-hooks.js";
10
10
  import { getLedger, getProjectTotals } from "./metrics.js";
11
11
  import { getErrorMessage } from "./error-utils.js";
12
12
  import { isDbAvailable, getMilestoneSlices, getSliceTasks } from "./gsd-db.js";
13
- import { formatShortcut } from "./files.js";
14
13
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
15
14
  import { execFileSync } from "node:child_process";
16
15
  import { truncateToWidth, visibleWidth } from "@gsd/pi-tui";
@@ -23,6 +22,7 @@ import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.
23
22
  import { parseUnitId } from "./unit-id.js";
24
23
  import { formatRtkSavingsLabel, getRtkSessionSavings, } from "../shared/rtk-session-stats.js";
25
24
  import { logWarning } from "./workflow-logger.js";
25
+ import { formattedShortcutPair } from "./shortcut-defs.js";
26
26
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
27
27
  /**
28
28
  * Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01").
@@ -282,12 +282,23 @@ function getLastCommit(basePath) {
282
282
  }
283
283
  // ─── Footer Factory ───────────────────────────────────────────────────────────
284
284
  /**
285
- * Footer factory that renders zero lines — hides the built-in footer entirely.
286
- * All footer info (pwd, branch, tokens, cost, model) is shown inside the
287
- * progress widget instead, so there's no gap or redundancy.
285
+ * Footer factory used by auto-mode.
286
+ * Keep footer minimal but preserve extension status context from setStatus().
288
287
  */
289
- export const hideFooter = () => ({
290
- render(_width) { return []; },
288
+ function sanitizeFooterStatus(text) {
289
+ return text.replace(/\s+/g, " ").trim();
290
+ }
291
+ export const hideFooter = (_tui, theme, footerData) => ({
292
+ render(width) {
293
+ const extensionStatuses = footerData.getExtensionStatuses();
294
+ if (extensionStatuses.size === 0)
295
+ return [];
296
+ const statusLine = Array.from(extensionStatuses.entries())
297
+ .sort(([a], [b]) => a.localeCompare(b))
298
+ .map(([, text]) => sanitizeFooterStatus(text))
299
+ .join(" ");
300
+ return [truncateToWidth(theme.fg("dim", statusLine), width, theme.fg("dim", "..."))];
301
+ },
291
302
  invalidate() { },
292
303
  dispose() { },
293
304
  });
@@ -522,13 +533,6 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
522
533
  : theme.fg("dim", elapsed))
523
534
  : "";
524
535
  lines.push(rightAlign(headerLeft, headerRight, width));
525
- // Worktree/branch right-aligned below header
526
- const branchLabel = worktreeName && cachedBranch
527
- ? `${worktreeName} (${cachedBranch})`
528
- : cachedBranch ?? "";
529
- if (branchLabel) {
530
- lines.push(rightAlign("", theme.fg("dim", branchLabel), width));
531
- }
532
536
  // Show health signal details when degraded (yellow/red)
533
537
  if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
534
538
  // Show up to 3 most relevant signals in compact form
@@ -776,16 +780,18 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
776
780
  // Hints line
777
781
  const hintParts = [];
778
782
  hintParts.push("esc pause");
779
- hintParts.push(`${formatShortcut("Ctrl+Alt+G")} dashboard`);
783
+ hintParts.push(`${formattedShortcutPair("dashboard")} dashboard`);
784
+ hintParts.push(`${formattedShortcutPair("parallel")} parallel`);
780
785
  const hintStr = theme.fg("dim", hintParts.join(" | "));
781
786
  const commitStr = lastCommit
782
787
  ? theme.fg("dim", `${lastCommit.timeAgo} ago: ${commitMsg}`)
783
788
  : "";
789
+ const locationStr = theme.fg("dim", widgetPwd);
784
790
  if (commitStr) {
785
- lines.push(rightAlign(`${pad}${commitStr}`, hintStr, width));
791
+ lines.push(rightAlign(`${pad}${locationStr} · ${commitStr}`, hintStr, width));
786
792
  }
787
793
  else {
788
- lines.push(rightAlign("", hintStr, width));
794
+ lines.push(rightAlign(`${pad}${locationStr}`, hintStr, width));
789
795
  }
790
796
  lines.push(...ui.bar());
791
797
  cachedLines = lines;
@@ -8,6 +8,7 @@ import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
8
8
  import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides, adjustToolSet } from "./model-router.js";
9
9
  import { getLedger, getProjectTotals } from "./metrics.js";
10
10
  import { unitPhaseLabel } from "./auto-dashboard.js";
11
+ import { getSessionModelOverride } from "./session-model-override.js";
11
12
  export function resolvePreferredModelConfig(unitType, autoModeStartModel,
12
13
  /** When false, only return explicit per-phase model configs — do not
13
14
  * synthesize a routing ceiling from dynamic_routing.tier_models (#3962). */
@@ -44,8 +45,15 @@ isAutoMode = true) {
44
45
  export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, prefs, verbose, autoModeStartModel, retryContext,
45
46
  /** When false (interactive/guided-flow), skip dynamic routing and use the session model.
46
47
  * Dynamic routing only applies in auto-mode where cost optimization is expected. (#3962) */
47
- isAutoMode = true) {
48
- const modelConfig = resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
48
+ isAutoMode = true,
49
+ /** Explicit /gsd model pin captured at bootstrap for long-running auto loops. */
50
+ sessionModelOverride) {
51
+ const effectiveSessionModelOverride = sessionModelOverride === undefined
52
+ ? getSessionModelOverride(ctx.sessionManager.getSessionId())
53
+ : (sessionModelOverride ?? undefined);
54
+ const modelConfig = effectiveSessionModelOverride
55
+ ? undefined
56
+ : resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
49
57
  let routing = null;
50
58
  let appliedModel = null;
51
59
  if (modelConfig) {
@@ -15,7 +15,8 @@ import { getLoadedSkills } from "@gsd/pi-coding-agent";
15
15
  import { join, basename } from "node:path";
16
16
  import { existsSync } from "node:fs";
17
17
  import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
18
- import { getPendingGates } from "./gsd-db.js";
18
+ import { getPendingGatesForTurn } from "./gsd-db.js";
19
+ import { assertGateCoverage, getGatesForTurn, } from "./gate-registry.js";
19
20
  import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
20
21
  import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
21
22
  import { logWarning } from "./workflow-logger.js";
@@ -1221,6 +1222,13 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
1221
1222
  ? `### Runtime Context\nSource: \`.gsd/RUNTIME.md\`\n\n${runtimeContent.trim()}`
1222
1223
  : "";
1223
1224
  const phaseAnchorSection = planAnchor ? formatAnchorForPrompt(planAnchor) : "";
1225
+ // Task-scoped gates owned by execute-task (Q5/Q6/Q7). Pull only the
1226
+ // gates that plan-slice actually seeded for this task — tasks with no
1227
+ // external dependencies legitimately skip Q5, tasks with no runtime
1228
+ // load dimension skip Q6, etc.
1229
+ const etPending = getPendingGatesForTurn(mid, sid, "execute-task", tid);
1230
+ assertGateCoverage(etPending, "execute-task", { requireAll: false });
1231
+ const gatesToClose = renderGatesToCloseBlock(getGatesForTurn("execute-task"), { pending: new Set(etPending.map((g) => g.gate_id)), allowOmit: true });
1224
1232
  return loadPrompt("execute-task", {
1225
1233
  overridesSection,
1226
1234
  runtimeContext,
@@ -1238,6 +1246,7 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
1238
1246
  taskSummaryPath,
1239
1247
  inlinedTemplates,
1240
1248
  verificationBudget,
1249
+ gatesToClose,
1241
1250
  skillActivation: buildSkillActivationBlock({
1242
1251
  base,
1243
1252
  milestoneId: mid,
@@ -1298,6 +1307,15 @@ export async function buildCompleteSlicePrompt(mid, _midTitle, sid, sTitle, base
1298
1307
  const sliceRel = relSlicePath(base, mid, sid);
1299
1308
  const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
1300
1309
  const sliceUatPath = join(base, `${sliceRel}/${sid}-UAT.md`);
1310
+ // Gates owned by complete-slice (e.g. Q8). Pull from the DB so the
1311
+ // prompt only prompts for gates the plan actually seeded. The tool
1312
+ // handler closes each gate based on the SUMMARY.md section content
1313
+ // after the assistant calls gsd_complete_slice.
1314
+ const csPending = getPendingGatesForTurn(mid, sid, "complete-slice");
1315
+ // coverage check: every pending row must be owned by complete-slice.
1316
+ // requireAll:false because a slice may have already closed some gates.
1317
+ assertGateCoverage(csPending, "complete-slice", { requireAll: false });
1318
+ const gatesToClose = renderGatesToCloseBlock(getGatesForTurn("complete-slice"), { pending: new Set(csPending.map((g) => g.gate_id)), allowOmit: true });
1301
1319
  return loadPrompt("complete-slice", {
1302
1320
  workingDirectory: base,
1303
1321
  milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
@@ -1306,6 +1324,7 @@ export async function buildCompleteSlicePrompt(mid, _midTitle, sid, sTitle, base
1306
1324
  inlinedContext,
1307
1325
  sliceSummaryPath,
1308
1326
  sliceUatPath,
1327
+ gatesToClose,
1309
1328
  });
1310
1329
  }
1311
1330
  export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
@@ -1498,6 +1517,15 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1498
1517
  const inlinedContext = capPreamble(`## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`);
1499
1518
  const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
1500
1519
  const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
1520
+ // Every milestone validation turn owns MV01–MV04 unconditionally: the
1521
+ // registry is the source of truth for which gates the validator must
1522
+ // address, and the block below is what the template renders so the
1523
+ // assistant can never accidentally skip one.
1524
+ const mvGates = getGatesForTurn("validate-milestone");
1525
+ const gatesToEvaluate = renderGatesToCloseBlock(mvGates, {
1526
+ pending: new Set(mvGates.map((g) => g.id)),
1527
+ allowOmit: false,
1528
+ });
1501
1529
  return loadPrompt("validate-milestone", {
1502
1530
  workingDirectory: base,
1503
1531
  milestoneId: mid,
@@ -1506,6 +1534,7 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1506
1534
  inlinedContext,
1507
1535
  validationPath: validationOutputPath,
1508
1536
  remediationRound: String(remediationRound),
1537
+ gatesToEvaluate,
1509
1538
  skillActivation: buildSkillActivationBlock({
1510
1539
  base,
1511
1540
  milestoneId: mid,
@@ -1740,26 +1769,43 @@ export async function buildReactiveExecutePrompt(mid, midTitle, sid, sTitle, rea
1740
1769
  });
1741
1770
  }
1742
1771
  // ─── Gate Evaluation ──────────────────────────────────────────────────────
1743
- const GATE_QUESTIONS = {
1744
- Q3: {
1745
- question: "How can this be exploited?",
1746
- guidance: [
1747
- "Identify abuse scenarios: parameter tampering, replay attacks, privilege escalation.",
1748
- "Map data exposure risks: PII, tokens, secrets accessible through this slice.",
1749
- "Define input trust boundaries: untrusted user input reaching DB, API, or filesystem.",
1750
- "If none apply, return verdict 'omitted' with rationale explaining why.",
1751
- ].join("\n"),
1752
- },
1753
- Q4: {
1754
- question: "What existing promises does this break?",
1755
- guidance: [
1756
- "List which existing requirements (R001, R003, etc.) are touched by this slice.",
1757
- "Identify what must be re-tested after shipping.",
1758
- "Flag decisions that should be revisited given the new scope.",
1759
- "If no existing requirements are affected, return verdict 'omitted'.",
1760
- ].join("\n"),
1761
- },
1762
- };
1772
+ //
1773
+ // Gate definitions (question, guidance, owner turn) now live in
1774
+ // gate-registry.ts so that prompt builders, dispatch rules, state
1775
+ // derivation, and tool handlers all consult the same source of truth.
1776
+ // See gate-registry.ts for the full ownership map.
1777
+ /**
1778
+ * Render a "Gates to Close" block for turns like `complete-slice` and
1779
+ * `validate-milestone` that own gates which are closed as a side-effect
1780
+ * of writing artifact sections (not via a dedicated gate-evaluate
1781
+ * subagent loop).
1782
+ *
1783
+ * Returns a plain-text block or an empty string if there are no gates to
1784
+ * close, so callers can drop it straight into a template variable.
1785
+ */
1786
+ function renderGatesToCloseBlock(gates, opts) {
1787
+ const applicable = gates.filter((g) => opts.pending.has(g.id));
1788
+ if (applicable.length === 0)
1789
+ return "";
1790
+ const lines = [];
1791
+ lines.push("## Gates to Close");
1792
+ lines.push("");
1793
+ lines.push("These quality gates are still pending for this unit. You MUST address every one before calling the closing tool — the handler closes the DB row based on whether the corresponding artifact section is present.");
1794
+ lines.push("");
1795
+ for (const def of applicable) {
1796
+ lines.push(`### ${def.id} — ${def.promptSection}`);
1797
+ lines.push("");
1798
+ lines.push(`**Question:** ${def.question}`);
1799
+ lines.push("");
1800
+ lines.push(def.guidance);
1801
+ if (opts.allowOmit) {
1802
+ lines.push("");
1803
+ lines.push(`If this gate genuinely does not apply to this unit, leave the **${def.promptSection}** section empty and the handler will record it as \`omitted\`. Otherwise, fill the section with concrete evidence.`);
1804
+ }
1805
+ lines.push("");
1806
+ }
1807
+ return lines.join("\n").trimEnd();
1808
+ }
1763
1809
  export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, basePath) {
1764
1810
  // Build individual research-slice prompts for each slice
1765
1811
  const subagentSections = [];
@@ -1784,24 +1830,33 @@ export async function buildParallelResearchSlicesPrompt(mid, midTitle, slices, b
1784
1830
  });
1785
1831
  }
1786
1832
  export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base) {
1787
- const pending = getPendingGates(mid, sid, "slice");
1833
+ // Pull only the gates this turn actually owns (Q3/Q4). Filter via the
1834
+ // registry so that scope:"slice" gates owned by other turns (Q8) can't
1835
+ // leak into this prompt and can't block dispatch via silent skip.
1836
+ const pending = getPendingGatesForTurn(mid, sid, "gate-evaluate");
1837
+ // Fails loudly if the pending list contains a gate id the registry
1838
+ // doesn't own for this turn. Missing owned gates is allowed here —
1839
+ // `gate-evaluate` is dispatched whenever *any* of its owned gates are
1840
+ // pending, not only when all of them are.
1841
+ assertGateCoverage(pending, "gate-evaluate", { requireAll: false });
1788
1842
  // Load the slice plan for context
1789
1843
  const planFile = resolveSliceFile(base, mid, sid, "PLAN");
1790
1844
  const planContent = planFile ? (await loadFile(planFile)) ?? "(plan file empty)" : "(plan file not found)";
1791
- // Build per-gate subagent prompts
1845
+ // Build per-gate subagent prompts from the pending rows. Because the
1846
+ // registry has already validated every row, `getGateDefinition` cannot
1847
+ // return undefined here.
1848
+ const pendingIds = new Set(pending.map((g) => g.gate_id));
1849
+ const gateDefs = getGatesForTurn("gate-evaluate").filter((def) => pendingIds.has(def.id));
1792
1850
  const subagentSections = [];
1793
1851
  const gateListLines = [];
1794
- for (const gate of pending) {
1795
- const meta = GATE_QUESTIONS[gate.gate_id];
1796
- if (!meta)
1797
- continue;
1798
- gateListLines.push(`- **${gate.gate_id}**: ${meta.question}`);
1852
+ for (const def of gateDefs) {
1853
+ gateListLines.push(`- **${def.id}**: ${def.question}`);
1799
1854
  const subPrompt = [
1800
- `You are evaluating quality gate **${gate.gate_id}** for slice ${sid} (${sTitle}).`,
1855
+ `You are evaluating quality gate **${def.id}** for slice ${sid} (${sTitle}).`,
1801
1856
  "",
1802
- `## Question: ${meta.question}`,
1857
+ `## Question: ${def.question}`,
1803
1858
  "",
1804
- meta.guidance,
1859
+ def.guidance,
1805
1860
  "",
1806
1861
  "## Slice Plan",
1807
1862
  "",
@@ -1813,13 +1868,13 @@ export async function buildGateEvaluatePrompt(mid, midTitle, sid, sTitle, base)
1813
1868
  `Call the \`gsd_save_gate_result\` tool with:`,
1814
1869
  `- \`milestoneId\`: "${mid}"`,
1815
1870
  `- \`sliceId\`: "${sid}"`,
1816
- `- \`gateId\`: "${gate.gate_id}"`,
1871
+ `- \`gateId\`: "${def.id}"`,
1817
1872
  "- `verdict`: \"pass\" (no concerns), \"flag\" (concerns found), or \"omitted\" (not applicable)",
1818
1873
  "- `rationale`: one-sentence justification",
1819
1874
  "- `findings`: detailed markdown findings (or empty if omitted)",
1820
1875
  ].join("\n");
1821
1876
  subagentSections.push([
1822
- `### ${gate.gate_id}: ${meta.question}`,
1877
+ `### ${def.id}: ${def.question}`,
1823
1878
  "",
1824
1879
  "Use this as the prompt for a `subagent` call:",
1825
1880
  "",
@@ -39,6 +39,7 @@ import { join } from "node:path";
39
39
  import { sep as pathSep } from "node:path";
40
40
  import { resolveProjectRootDbPath } from "./bootstrap/dynamic-tools.js";
41
41
  import { resolveDefaultSessionModel, resolveDynamicRoutingConfig } from "./preferences-models.js";
42
+ import { getSessionModelOverride } from "./session-model-override.js";
42
43
  /**
43
44
  * Bootstrap a fresh auto-mode session. Handles everything from git init
44
45
  * through secrets collection, returning when ready for the first
@@ -187,13 +188,35 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
187
188
  // Capture the user's session model before guided-flow dispatch can apply a
188
189
  // phase-specific planning model for a discuss turn (#2829).
189
190
  //
190
- // GSD PREFERENCES.md takes priority over the session model from settings.json
191
- // (#3517). The session model (ctx.model) comes from findInitialModel() which
192
- // reads defaultProvider/defaultModel from ~/.gsd/agent/settings.json. When
193
- // the user has explicit model preferences in PREFERENCES.md, those should win.
191
+ // Precedence:
192
+ // 1) Explicit session override via /gsd model (this session)
193
+ // 2) GSD model preferences from PREFERENCES.md (validated against live auth)
194
+ // 3) Current session model from settings/session restore (if provider ready)
195
+ //
196
+ // This preserves #3517 defaults while honoring explicit runtime model
197
+ // selection for subsequent /gsd runs in the same session.
198
+ const manualSessionOverride = getSessionModelOverride(ctx.sessionManager.getSessionId());
194
199
  const preferredModel = resolveDefaultSessionModel(ctx.model?.provider);
195
- const startModelSnapshot = preferredModel
196
- ?? (ctx.model
200
+ // Validate the preferred model against the live registry + provider auth so
201
+ // an unconfigured PREFERENCES.md entry (no API key / OAuth) can't become the
202
+ // start-model snapshot. Without this, every subsequent unit would try to
203
+ // fall back to an unusable model.
204
+ let validatedPreferredModel;
205
+ if (preferredModel) {
206
+ const { resolveModelId } = await import("./auto-model-selection.js");
207
+ const available = ctx.modelRegistry.getAvailable();
208
+ const match = resolveModelId(`${preferredModel.provider}/${preferredModel.id}`, available, ctx.model?.provider);
209
+ if (match) {
210
+ validatedPreferredModel = { provider: match.provider, id: match.id };
211
+ }
212
+ else {
213
+ ctx.ui.notify(`Preferred model ${preferredModel.provider}/${preferredModel.id} from PREFERENCES.md is not configured; falling back to session default.`, "warning");
214
+ }
215
+ }
216
+ const sessionModelReady = ctx.model && ctx.modelRegistry.isProviderRequestReady(ctx.model.provider);
217
+ const startModelSnapshot = manualSessionOverride
218
+ ?? validatedPreferredModel
219
+ ?? (sessionModelReady && ctx.model
197
220
  ? { provider: ctx.model.provider, id: ctx.model.id }
198
221
  : null);
199
222
  try {
@@ -447,6 +470,9 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
447
470
  // Successfully resolved an active milestone — reset the re-entry guard
448
471
  s.consecutiveCompleteBootstraps = 0;
449
472
  // ── Initialize session state ──
473
+ // Notify shared phase state so subagent conflict checks can fire
474
+ const { activateGSD: activateGSDPhaseState } = await import("../shared/gsd-phase-state.js");
475
+ activateGSDPhaseState();
450
476
  s.active = true;
451
477
  s.stepMode = requestedStepMode;
452
478
  s.verbose = verboseMode;
@@ -523,7 +549,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
523
549
  }
524
550
  }
525
551
  // ── DB lifecycle ──
526
- const gsdDbPath = join(s.basePath, ".gsd", "gsd.db");
552
+ const gsdDbPath = resolveProjectRootDbPath(s.basePath);
527
553
  const gsdDirPath = join(s.basePath, ".gsd");
528
554
  if (existsSync(gsdDirPath) && !existsSync(gsdDbPath)) {
529
555
  const hasDecisions = existsSync(join(gsdDirPath, "DECISIONS.md"));
@@ -571,6 +597,7 @@ export async function bootstrapAutoSession(s, ctx, pi, base, verboseMode, reques
571
597
  id: startModelSnapshot.id,
572
598
  };
573
599
  }
600
+ s.manualSessionModelOverride = manualSessionOverride ?? null;
574
601
  // Apply worker model override from parallel orchestrator (#worker-model).
575
602
  // GSD_WORKER_MODEL is injected by the coordinator when parallel.worker_model
576
603
  // is configured, so parallel milestone workers use a cheaper model than the
@@ -82,7 +82,7 @@ export function clearInFlightTools() {
82
82
  * handler. When these errors occur, retrying the same unit will produce the same
83
83
  * failure, so the retry loop must be broken.
84
84
  */
85
- const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}' in JSON|Unexpected end of JSON|Unexpected token.*in JSON/i;
85
+ const TOOL_INVOCATION_ERROR_RE = /Validation failed for tool|Expected ',' or '\}'(?: after property value)?(?: in JSON)?|Unexpected end of JSON|Unexpected token.*in JSON/i;
86
86
  /**
87
87
  * Returns true if the error message indicates a tool invocation failure due to
88
88
  * malformed/truncated arguments (as opposed to a normal tool execution error).
@@ -1795,7 +1795,7 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1795
1795
  // 12. Remove worktree directory first (must happen before branch deletion)
1796
1796
  try {
1797
1797
  removeWorktree(originalBasePath_, milestoneId, {
1798
- branch: null,
1798
+ branch: milestoneBranch,
1799
1799
  deleteBranch: false,
1800
1800
  });
1801
1801
  }