gsd-pi 2.60.0-dev.d9052f5 → 2.61.0

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 (308) hide show
  1. package/dist/resources/extensions/ask-user-questions.js +7 -4
  2. package/dist/resources/extensions/gsd/auto/phases.js +15 -7
  3. package/dist/resources/extensions/gsd/auto-dashboard.js +21 -8
  4. package/dist/resources/extensions/gsd/auto-dispatch.js +6 -3
  5. package/dist/resources/extensions/gsd/auto-model-selection.js +58 -9
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +3 -2
  7. package/dist/resources/extensions/gsd/auto-prompts.js +36 -20
  8. package/dist/resources/extensions/gsd/auto-recovery.js +37 -18
  9. package/dist/resources/extensions/gsd/auto-start.js +9 -5
  10. package/dist/resources/extensions/gsd/auto-timers.js +11 -5
  11. package/dist/resources/extensions/gsd/auto-unit-closeout.js +5 -3
  12. package/dist/resources/extensions/gsd/auto-verification.js +3 -2
  13. package/dist/resources/extensions/gsd/auto-worktree.js +120 -55
  14. package/dist/resources/extensions/gsd/auto.js +39 -17
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +6 -3
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
  17. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +4 -10
  18. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +2 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  20. package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
  21. package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
  22. package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
  23. package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
  24. package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
  25. package/dist/resources/extensions/gsd/complexity-classifier.js +8 -4
  26. package/dist/resources/extensions/gsd/custom-verification.js +3 -2
  27. package/dist/resources/extensions/gsd/gsd-db.js +33 -13
  28. package/dist/resources/extensions/gsd/guided-flow.js +19 -9
  29. package/dist/resources/extensions/gsd/init-wizard.js +12 -0
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
  31. package/dist/resources/extensions/gsd/md-importer.js +5 -4
  32. package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
  33. package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
  34. package/dist/resources/extensions/gsd/model-router.js +156 -121
  35. package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
  36. package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
  37. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  38. package/dist/resources/extensions/gsd/preferences-validation.js +45 -0
  39. package/dist/resources/extensions/gsd/preferences.js +15 -3
  40. package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
  41. package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
  42. package/dist/resources/extensions/gsd/rule-registry.js +7 -6
  43. package/dist/resources/extensions/gsd/safe-fs.js +6 -8
  44. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  45. package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
  46. package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
  47. package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
  48. package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
  49. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
  50. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
  51. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
  52. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
  53. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
  54. package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
  55. package/dist/resources/extensions/gsd/triage-resolution.js +11 -4
  56. package/dist/resources/extensions/gsd/workflow-events.js +2 -1
  57. package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
  58. package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
  59. package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
  60. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
  61. package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
  62. package/dist/resources/extensions/shared/interview-ui.js +3 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  65. package/dist/web/standalone/.next/build-manifest.json +3 -3
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/required-server-files.json +3 -3
  68. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  69. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  71. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  79. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  89. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  91. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  92. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  93. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  94. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  95. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  97. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.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/dev-mode/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  107. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  135. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  141. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  155. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  157. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  159. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  161. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/index.html +1 -1
  171. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  172. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  173. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  174. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  175. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  176. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  177. package/dist/web/standalone/.next/server/app/page.js +2 -2
  178. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  180. package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
  181. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  182. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/middleware.js +2 -2
  184. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  186. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  187. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  188. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  189. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
  190. package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
  191. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +1 -0
  192. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
  193. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
  194. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  195. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  196. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  197. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  198. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  199. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  200. package/dist/web/standalone/server.js +1 -1
  201. package/package.json +1 -1
  202. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  204. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  206. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  207. package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
  208. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  209. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
  210. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  211. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  212. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
  214. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
  216. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
  217. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
  218. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
  219. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
  220. package/packages/pi-coding-agent/package.json +1 -1
  221. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  222. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
  223. package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
  224. package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
  225. package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
  226. package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
  227. package/pkg/package.json +1 -1
  228. package/src/resources/extensions/ask-user-questions.ts +7 -3
  229. package/src/resources/extensions/gsd/auto/phases.ts +17 -7
  230. package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
  231. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
  232. package/src/resources/extensions/gsd/auto-model-selection.ts +77 -15
  233. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
  234. package/src/resources/extensions/gsd/auto-prompts.ts +37 -20
  235. package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
  236. package/src/resources/extensions/gsd/auto-start.ts +10 -9
  237. package/src/resources/extensions/gsd/auto-timers.ts +12 -5
  238. package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
  239. package/src/resources/extensions/gsd/auto-verification.ts +3 -6
  240. package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
  241. package/src/resources/extensions/gsd/auto.ts +40 -17
  242. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
  243. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
  244. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
  245. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
  246. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
  247. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
  248. package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
  249. package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
  250. package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
  251. package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
  252. package/src/resources/extensions/gsd/complexity-classifier.ts +9 -4
  253. package/src/resources/extensions/gsd/custom-verification.ts +3 -2
  254. package/src/resources/extensions/gsd/gsd-db.ts +12 -14
  255. package/src/resources/extensions/gsd/guided-flow.ts +9 -8
  256. package/src/resources/extensions/gsd/init-wizard.ts +12 -0
  257. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
  258. package/src/resources/extensions/gsd/md-importer.ts +5 -4
  259. package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
  260. package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
  261. package/src/resources/extensions/gsd/model-router.ts +199 -173
  262. package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
  263. package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
  264. package/src/resources/extensions/gsd/preferences-types.ts +13 -0
  265. package/src/resources/extensions/gsd/preferences-validation.ts +45 -0
  266. package/src/resources/extensions/gsd/preferences.ts +16 -3
  267. package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
  268. package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
  269. package/src/resources/extensions/gsd/rule-registry.ts +7 -6
  270. package/src/resources/extensions/gsd/safe-fs.ts +6 -5
  271. package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
  272. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
  273. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
  274. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  275. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
  276. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
  277. package/src/resources/extensions/gsd/tests/model-router.test.ts +403 -3
  278. package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
  279. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
  280. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
  281. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
  282. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
  283. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
  284. package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
  285. package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
  286. package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
  287. package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
  288. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
  289. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
  290. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
  291. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
  292. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
  293. package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
  294. package/src/resources/extensions/gsd/triage-resolution.ts +11 -4
  295. package/src/resources/extensions/gsd/types.ts +1 -0
  296. package/src/resources/extensions/gsd/workflow-events.ts +2 -1
  297. package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
  298. package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
  299. package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
  300. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
  301. package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
  302. package/src/resources/extensions/shared/interview-ui.ts +3 -1
  303. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
  304. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +0 -1
  305. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
  306. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
  307. /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → 72_sVF0fdrMZX707jm10G}/_buildManifest.js +0 -0
  308. /package/dist/web/standalone/.next/static/{JVkoVYumy0cDhOQISEYdG → 72_sVF0fdrMZX707jm10G}/_ssgManifest.js +0 -0
@@ -2,7 +2,7 @@
2
2
  // Maps complexity tiers to models, enforcing downgrade-only semantics.
3
3
  // The user's configured model is always the ceiling.
4
4
 
5
- import type { ComplexityTier, ClassificationResult } from "./complexity-classifier.js";
5
+ import type { ComplexityTier, ClassificationResult, TaskMetadata } from "./complexity-classifier.js";
6
6
  import { tierOrdinal } from "./complexity-classifier.js";
7
7
  import type { ResolvedModelConfig } from "./preferences.js";
8
8
 
@@ -33,14 +33,27 @@ export interface RoutingDecision {
33
33
  wasDowngraded: boolean;
34
34
  /** Human-readable reason for this decision */
35
35
  reason: string;
36
- /** How the model was selected. */
37
- selectionMethod?: "tier-only" | "capability-scored";
38
- /** Capability scores per model (when capability-scored). */
36
+ /** How the model was selected */
37
+ selectionMethod: "tier-only" | "capability-scored";
38
+ /** Capability scores per eligible model (capability-scored path only) */
39
39
  capabilityScores?: Record<string, number>;
40
- /** Task requirement vector (when capability-scored). */
40
+ /** Task requirement vector used for scoring */
41
41
  taskRequirements?: Partial<Record<string, number>>;
42
42
  }
43
43
 
44
+ // ─── Capability Profiles ─────────────────────────────────────────────────────
45
+
46
+ /** Seven-dimension capability profile for a model. All values in 0–100 range. */
47
+ export interface ModelCapabilities {
48
+ coding: number;
49
+ debugging: number;
50
+ research: number;
51
+ reasoning: number;
52
+ speed: number;
53
+ longContext: number;
54
+ instruction: number;
55
+ }
56
+
44
57
  // ─── Known Model Tiers ───────────────────────────────────────────────────────
45
58
  // Maps known model IDs to their capability tier. Used when tier_models is not
46
59
  // explicitly configured to pick the best available model for each tier.
@@ -121,33 +134,27 @@ const MODEL_COST_PER_1K_INPUT: Record<string, number> = {
121
134
  "deepseek-chat": 0.00014,
122
135
  };
123
136
 
124
- // ─── Capability Profiles (ADR-004 Phase 2) ──────────────────────────────────
125
- // 7-dimension profiles, 0–100 normalized. Models without a profile
126
- // score 50 uniformly capability scoring is a no-op for them.
127
-
128
- export interface ModelCapabilities {
129
- coding: number;
130
- debugging: number;
131
- research: number;
132
- reasoning: number;
133
- speed: number;
134
- longContext: number;
135
- instruction: number;
136
- }
137
+ // ─── Capability Profiles Data Table ──────────────────────────────────────────
138
+ // Per-model capability profiles (0–100 scale). Used for capability-aware
139
+ // model selection within an eligible tier set.
137
140
 
138
141
  export const MODEL_CAPABILITY_PROFILES: Record<string, ModelCapabilities> = {
139
- "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
140
- "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
141
- "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
142
- "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
143
- "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
144
- "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
145
- "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
146
- "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
147
- "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
142
+ "claude-opus-4-6": { coding: 95, debugging: 90, research: 85, reasoning: 95, speed: 30, longContext: 80, instruction: 90 },
143
+ "claude-sonnet-4-6": { coding: 85, debugging: 80, research: 75, reasoning: 80, speed: 60, longContext: 75, instruction: 85 },
144
+ "claude-haiku-4-5": { coding: 60, debugging: 50, research: 45, reasoning: 50, speed: 95, longContext: 50, instruction: 75 },
145
+ "gpt-4o": { coding: 80, debugging: 75, research: 70, reasoning: 75, speed: 65, longContext: 70, instruction: 80 },
146
+ "gpt-4o-mini": { coding: 55, debugging: 45, research: 40, reasoning: 45, speed: 90, longContext: 45, instruction: 70 },
147
+ "gemini-2.5-pro": { coding: 75, debugging: 70, research: 85, reasoning: 75, speed: 55, longContext: 90, instruction: 75 },
148
+ "gemini-2.0-flash": { coding: 50, debugging: 40, research: 50, reasoning: 40, speed: 95, longContext: 60, instruction: 65 },
149
+ "deepseek-chat": { coding: 75, debugging: 65, research: 55, reasoning: 70, speed: 70, longContext: 55, instruction: 65 },
150
+ "o3": { coding: 80, debugging: 85, research: 80, reasoning: 92, speed: 25, longContext: 70, instruction: 85 },
148
151
  };
149
152
 
150
- const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities, number>>> = {
153
+ // ─── Base Task Requirements Data Table ───────────────────────────────────────
154
+ // Per-unit-type base requirement vectors. Weights indicate how important each
155
+ // capability dimension is for this unit type.
156
+
157
+ export const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities, number>>> = {
151
158
  "execute-task": { coding: 0.9, instruction: 0.7, speed: 0.3 },
152
159
  "research-milestone": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
153
160
  "research-slice": { research: 0.9, longContext: 0.7, reasoning: 0.5 },
@@ -161,15 +168,36 @@ const BASE_REQUIREMENTS: Record<string, Partial<Record<keyof ModelCapabilities,
161
168
  "complete-milestone": { instruction: 0.8, reasoning: 0.5 },
162
169
  };
163
170
 
171
+ // ─── Public API ──────────────────────────────────────────────────────────────
172
+
164
173
  /**
165
- * Compute a task requirement vector from unit type and optional metadata.
174
+ * Score a model's suitability for a task given a requirement vector.
175
+ * Returns a weighted average of capability dimensions (0–100).
176
+ * Returns 50 if requirements are empty (neutral score).
177
+ */
178
+ export function scoreModel(
179
+ model: ModelCapabilities,
180
+ requirements: Partial<Record<keyof ModelCapabilities, number>>,
181
+ ): number {
182
+ let weightedSum = 0;
183
+ let weightSum = 0;
184
+ for (const [dim, weight] of Object.entries(requirements)) {
185
+ const capability = model[dim as keyof ModelCapabilities] ?? 50;
186
+ weightedSum += weight * capability;
187
+ weightSum += weight;
188
+ }
189
+ return weightSum > 0 ? weightedSum / weightSum : 50;
190
+ }
191
+
192
+ /**
193
+ * Compute dynamic task requirements from unit type and optional task metadata.
194
+ * Returns a requirement vector refined by task-specific signals.
166
195
  */
167
196
  export function computeTaskRequirements(
168
197
  unitType: string,
169
- metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
198
+ metadata?: TaskMetadata,
170
199
  ): Partial<Record<keyof ModelCapabilities, number>> {
171
- const base = { ...(BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 }) };
172
-
200
+ const base = BASE_REQUIREMENTS[unitType] ?? { reasoning: 0.5 };
173
201
  if (unitType === "execute-task" && metadata) {
174
202
  if (metadata.tags?.some(t => /^(docs?|readme|comment|config|typo|rename)$/i.test(t))) {
175
203
  return { ...base, instruction: 0.9, coding: 0.3, speed: 0.7 };
@@ -184,29 +212,101 @@ export function computeTaskRequirements(
184
212
  return { ...base, coding: 0.9, reasoning: 0.7 };
185
213
  }
186
214
  }
187
-
188
215
  return base;
189
216
  }
190
217
 
191
218
  /**
192
- * Score a model against a task requirement vector.
193
- * Returns weighted average in range 0–100. Returns 50 for empty requirements.
219
+ * Score all eligible models against a requirement vector and return them
220
+ * sorted by score descending. Within 2 points: prefer cheaper; equal cost:
221
+ * lexicographic tie-break by model ID.
194
222
  */
195
- export function scoreModel(
196
- capabilities: ModelCapabilities,
223
+ export function scoreEligibleModels(
224
+ eligibleModelIds: string[],
197
225
  requirements: Partial<Record<keyof ModelCapabilities, number>>,
198
- ): number {
199
- let weightedSum = 0;
200
- let weightSum = 0;
201
- for (const [dim, weight] of Object.entries(requirements)) {
202
- const capability = capabilities[dim as keyof ModelCapabilities] ?? 50;
203
- weightedSum += weight * capability;
204
- weightSum += weight;
226
+ capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
227
+ ): Array<{ modelId: string; score: number }> {
228
+ const scored = eligibleModelIds.map(modelId => {
229
+ const builtin = MODEL_CAPABILITY_PROFILES[modelId];
230
+ const override = capabilityOverrides?.[modelId];
231
+ const profile: ModelCapabilities = builtin
232
+ ? override ? { ...builtin, ...override } : builtin
233
+ : { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
234
+ return { modelId, score: scoreModel(profile, requirements) };
235
+ });
236
+ scored.sort((a, b) => {
237
+ const scoreDiff = b.score - a.score;
238
+ if (Math.abs(scoreDiff) > 2) return scoreDiff;
239
+ const costA = MODEL_COST_PER_1K_INPUT[a.modelId] ?? Infinity;
240
+ const costB = MODEL_COST_PER_1K_INPUT[b.modelId] ?? Infinity;
241
+ if (costA !== costB) return costA - costB;
242
+ return a.modelId.localeCompare(b.modelId);
243
+ });
244
+ return scored;
245
+ }
246
+
247
+ /**
248
+ * Return all models eligible for a given tier, sorted cheapest first.
249
+ * If routingConfig.tier_models[tier] is set and available, returns only that
250
+ * model. Otherwise filters availableModelIds by tier from MODEL_CAPABILITY_TIER.
251
+ */
252
+ export function getEligibleModels(
253
+ tier: ComplexityTier,
254
+ availableModelIds: string[],
255
+ routingConfig: DynamicRoutingConfig,
256
+ ): string[] {
257
+ // 1. Check explicit tier_models config
258
+ const explicitModel = routingConfig.tier_models?.[tier];
259
+ if (explicitModel) {
260
+ // Exact match
261
+ if (availableModelIds.includes(explicitModel)) return [explicitModel];
262
+ // Provider-prefix-stripped match
263
+ const match = availableModelIds.find(id => {
264
+ const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
265
+ const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
266
+ return bareAvail === bareExplicit;
267
+ });
268
+ if (match) return [match];
205
269
  }
206
- return weightSum > 0 ? weightedSum / weightSum : 50;
270
+
271
+ // 2. Auto-detect: filter by tier, sort cheapest first
272
+ return availableModelIds
273
+ .filter(id => getModelTier(id) === tier)
274
+ .sort((a, b) => {
275
+ const costA = getModelCost(a);
276
+ const costB = getModelCost(b);
277
+ return costA - costB;
278
+ });
207
279
  }
208
280
 
209
- // ─── Public API ──────────────────────────────────────────────────────────────
281
+ /**
282
+ * Build a fallback chain for a selected model: [selectedModel, ...configuredFallbacks, configuredPrimary]
283
+ * Deduplicates entries while preserving order.
284
+ */
285
+ function buildFallbackChain(selectedModelId: string, phaseConfig: ResolvedModelConfig): string[] {
286
+ return [
287
+ ...phaseConfig.fallbacks.filter(f => f !== selectedModelId),
288
+ phaseConfig.primary,
289
+ ].filter(f => f !== selectedModelId);
290
+ }
291
+
292
+ /**
293
+ * Load capability overrides from user preferences' modelOverrides section.
294
+ * Returns a map of model ID → partial capability overrides to deep-merge with built-in profiles.
295
+ *
296
+ * Per D-17: partial capability overrides via models.json modelOverrides, deep-merged with defaults.
297
+ */
298
+ export function loadCapabilityOverrides(
299
+ prefs: { modelOverrides?: Record<string, { capabilities?: Partial<ModelCapabilities> }> },
300
+ ): Record<string, Partial<ModelCapabilities>> {
301
+ const result: Record<string, Partial<ModelCapabilities>> = {};
302
+ if (!prefs.modelOverrides) return result;
303
+ for (const [modelId, overrideEntry] of Object.entries(prefs.modelOverrides)) {
304
+ if (overrideEntry.capabilities) {
305
+ result[modelId] = overrideEntry.capabilities;
306
+ }
307
+ }
308
+ return result;
309
+ }
210
310
 
211
311
  /**
212
312
  * Resolve the model to use for a given complexity tier.
@@ -214,10 +314,18 @@ export function scoreModel(
214
314
  * Downgrade-only: the returned model is always equal to or cheaper than
215
315
  * the user's configured primary model. Never upgrades beyond configuration.
216
316
  *
217
- * @param classification The complexity classification result
218
- * @param phaseConfig The user's configured model for this phase (ceiling)
219
- * @param routingConfig Dynamic routing configuration
220
- * @param availableModelIds List of available model IDs (from registry)
317
+ * STEP 1: Filter to eligible models for the requested tier.
318
+ * STEP 2: Capability scoring ranks eligible models by task-capability match
319
+ * when capability_routing is enabled and multiple eligible models exist.
320
+ * STEP 3: Fallback chain assembly.
321
+ *
322
+ * @param classification The complexity classification result
323
+ * @param phaseConfig The user's configured model for this phase (ceiling)
324
+ * @param routingConfig Dynamic routing configuration
325
+ * @param availableModelIds List of available model IDs (from registry)
326
+ * @param unitType The unit type for capability requirement computation (optional)
327
+ * @param taskMetadata Task metadata for refined requirement vectors (optional)
328
+ * @param capabilityOverrides User-provided capability overrides (deep-merged with built-in profiles, optional)
221
329
  */
222
330
  export function resolveModelForComplexity(
223
331
  classification: ClassificationResult,
@@ -225,7 +333,8 @@ export function resolveModelForComplexity(
225
333
  routingConfig: DynamicRoutingConfig,
226
334
  availableModelIds: string[],
227
335
  unitType?: string,
228
- metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
336
+ taskMetadata?: TaskMetadata,
337
+ capabilityOverrides?: Record<string, Partial<ModelCapabilities>>,
229
338
  ): RoutingDecision {
230
339
  // If no phase config or routing disabled, pass through
231
340
  if (!phaseConfig || !routingConfig.enabled) {
@@ -235,6 +344,7 @@ export function resolveModelForComplexity(
235
344
  tier: classification.tier,
236
345
  wasDowngraded: false,
237
346
  reason: "dynamic routing disabled or no phase config",
347
+ selectionMethod: "tier-only",
238
348
  };
239
349
  }
240
350
 
@@ -254,6 +364,7 @@ export function resolveModelForComplexity(
254
364
  tier: requestedTier,
255
365
  wasDowngraded: false,
256
366
  reason: `configured model "${configuredPrimary}" is not in the known tier map — honoring explicit config`,
367
+ selectionMethod: "tier-only",
257
368
  };
258
369
  }
259
370
 
@@ -265,48 +376,52 @@ export function resolveModelForComplexity(
265
376
  tier: requestedTier,
266
377
  wasDowngraded: false,
267
378
  reason: `tier ${requestedTier} >= configured ${configuredTier}`,
379
+ selectionMethod: "tier-only",
268
380
  };
269
381
  }
270
382
 
271
- // Find the best model for the requested tier
272
- const useCapabilityScoring = routingConfig.capability_routing && unitType;
273
-
274
- let targetModelId: string | null;
275
- let capabilityScores: Record<string, number> | undefined;
276
- let taskRequirements: Partial<Record<string, number>> | undefined;
277
- let selectionMethod: "tier-only" | "capability-scored" = "tier-only";
278
-
279
- if (useCapabilityScoring) {
280
- const result = findModelForTierWithCapability(
281
- requestedTier, routingConfig, availableModelIds,
282
- routingConfig.cross_provider !== false, unitType, metadata,
283
- );
284
- targetModelId = result.modelId;
285
- capabilityScores = Object.keys(result.scores).length > 0 ? result.scores : undefined;
286
- taskRequirements = Object.keys(result.requirements).length > 0 ? result.requirements : undefined;
287
- selectionMethod = capabilityScores ? "capability-scored" : "tier-only";
288
- } else {
289
- targetModelId = findModelForTier(
290
- requestedTier, routingConfig, availableModelIds,
291
- routingConfig.cross_provider !== false,
292
- );
293
- }
383
+ // STEP 1: Get all eligible models for the requested tier
384
+ const eligible = getEligibleModels(requestedTier, availableModelIds, routingConfig);
294
385
 
295
- if (!targetModelId) {
386
+ if (eligible.length === 0) {
387
+ // No suitable model found — use configured primary
296
388
  return {
297
389
  modelId: configuredPrimary,
298
390
  fallbacks: phaseConfig.fallbacks,
299
391
  tier: requestedTier,
300
392
  wasDowngraded: false,
301
393
  reason: `no ${requestedTier}-tier model available`,
302
- selectionMethod,
394
+ selectionMethod: "tier-only",
303
395
  };
304
396
  }
305
397
 
306
- const fallbacks = [
307
- ...phaseConfig.fallbacks.filter(f => f !== targetModelId),
308
- configuredPrimary,
309
- ].filter(f => f !== targetModelId);
398
+ // STEP 2: Capability scoring (when enabled and multiple eligible models exist)
399
+ if (routingConfig.capability_routing !== false && eligible.length > 1 && unitType) {
400
+ const requirements = computeTaskRequirements(unitType, taskMetadata);
401
+ const scored = scoreEligibleModels(eligible, requirements, capabilityOverrides);
402
+ const winner = scored[0];
403
+ if (winner) {
404
+ const capScores: Record<string, number> = {};
405
+ for (const s of scored) capScores[s.modelId] = s.score;
406
+ const fallbacks = buildFallbackChain(winner.modelId, phaseConfig);
407
+ return {
408
+ modelId: winner.modelId,
409
+ fallbacks,
410
+ tier: requestedTier,
411
+ wasDowngraded: true,
412
+ reason: `capability-scored: ${winner.modelId} (${winner.score.toFixed(1)}) for ${unitType}`,
413
+ capabilityScores: capScores,
414
+ taskRequirements: requirements,
415
+ selectionMethod: "capability-scored",
416
+ };
417
+ }
418
+ }
419
+
420
+ // STEP 3: Fallback — use first eligible model (cheapest in tier, or single eligible)
421
+ const targetModelId = eligible[0];
422
+
423
+ // Build fallback chain: [downgraded_model, ...configured_fallbacks, configured_primary]
424
+ const fallbacks = buildFallbackChain(targetModelId, phaseConfig);
310
425
 
311
426
  return {
312
427
  modelId: targetModelId,
@@ -314,9 +429,7 @@ export function resolveModelForComplexity(
314
429
  tier: requestedTier,
315
430
  wasDowngraded: true,
316
431
  reason: classification.reason,
317
- selectionMethod,
318
- capabilityScores,
319
- taskRequirements,
432
+ selectionMethod: "tier-only",
320
433
  };
321
434
  }
322
435
 
@@ -338,7 +451,7 @@ export function escalateTier(currentTier: ComplexityTier): ComplexityTier | null
338
451
  export function defaultRoutingConfig(): DynamicRoutingConfig {
339
452
  return {
340
453
  enabled: true,
341
- capability_routing: false,
454
+ capability_routing: true,
342
455
  escalate_on_failure: true,
343
456
  budget_pressure: true,
344
457
  cross_provider: true,
@@ -360,8 +473,8 @@ function getModelTier(modelId: string): ComplexityTier {
360
473
  if (bareId.includes(knownId) || knownId.includes(bareId)) return tier;
361
474
  }
362
475
 
363
- // Unknown models are assumed heavy (safest assumption)
364
- return "heavy";
476
+ // Unknown models are assumed standard (per D-15: avoids silently ignoring user config)
477
+ return "standard";
365
478
  }
366
479
 
367
480
  /** Check if a model ID has a known capability tier mapping. (#2192) */
@@ -374,93 +487,6 @@ function isKnownModel(modelId: string): boolean {
374
487
  return false;
375
488
  }
376
489
 
377
- function findModelForTier(
378
- tier: ComplexityTier,
379
- config: DynamicRoutingConfig,
380
- availableModelIds: string[],
381
- crossProvider: boolean,
382
- ): string | null {
383
- // 1. Check explicit tier_models config
384
- const explicitModel = config.tier_models?.[tier];
385
- if (explicitModel && availableModelIds.includes(explicitModel)) {
386
- return explicitModel;
387
- }
388
- // Also check with provider prefix stripped
389
- if (explicitModel) {
390
- const match = availableModelIds.find(id => {
391
- const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
392
- const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
393
- return bareAvail === bareExplicit;
394
- });
395
- if (match) return match;
396
- }
397
-
398
- // 2. Auto-detect: find the cheapest available model in the requested tier
399
- const candidates = availableModelIds
400
- .filter(id => {
401
- const modelTier = getModelTier(id);
402
- return modelTier === tier;
403
- })
404
- .sort((a, b) => {
405
- if (!crossProvider) return 0;
406
- const costA = getModelCost(a);
407
- const costB = getModelCost(b);
408
- return costA - costB;
409
- });
410
-
411
- return candidates[0] ?? null;
412
- }
413
-
414
- function findModelForTierWithCapability(
415
- tier: ComplexityTier,
416
- config: DynamicRoutingConfig,
417
- availableModelIds: string[],
418
- crossProvider: boolean,
419
- unitType: string,
420
- metadata?: { tags?: string[]; complexityKeywords?: string[]; fileCount?: number; estimatedLines?: number },
421
- ): { modelId: string | null; scores: Record<string, number>; requirements: Partial<Record<string, number>> } {
422
- const explicitModel = config.tier_models?.[tier];
423
- if (explicitModel) {
424
- const match = availableModelIds.find(id => {
425
- const bareAvail = id.includes("/") ? id.split("/").pop()! : id;
426
- const bareExplicit = explicitModel.includes("/") ? explicitModel.split("/").pop()! : explicitModel;
427
- return bareAvail === bareExplicit || id === explicitModel;
428
- });
429
- if (match) return { modelId: match, scores: {}, requirements: {} };
430
- }
431
-
432
- const requirements = computeTaskRequirements(unitType, metadata);
433
- const candidates = availableModelIds.filter(id => getModelTier(id) === tier);
434
- if (candidates.length === 0) return { modelId: null, scores: {}, requirements };
435
-
436
- const scores: Record<string, number> = {};
437
- for (const id of candidates) {
438
- const bareId = id.includes("/") ? id.split("/").pop()! : id;
439
- const profile = getModelProfile(bareId);
440
- scores[id] = scoreModel(profile, requirements);
441
- }
442
-
443
- candidates.sort((a, b) => {
444
- const scoreDiff = scores[b] - scores[a];
445
- if (Math.abs(scoreDiff) > 2) return scoreDiff;
446
- if (crossProvider) {
447
- const costDiff = getModelCost(a) - getModelCost(b);
448
- if (costDiff !== 0) return costDiff;
449
- }
450
- return a.localeCompare(b);
451
- });
452
-
453
- return { modelId: candidates[0], scores, requirements };
454
- }
455
-
456
- function getModelProfile(bareId: string): ModelCapabilities {
457
- if (MODEL_CAPABILITY_PROFILES[bareId]) return MODEL_CAPABILITY_PROFILES[bareId];
458
- for (const [knownId, profile] of Object.entries(MODEL_CAPABILITY_PROFILES)) {
459
- if (bareId.includes(knownId) || knownId.includes(bareId)) return profile;
460
- }
461
- return { coding: 50, debugging: 50, research: 50, reasoning: 50, speed: 50, longContext: 50, instruction: 50 };
462
- }
463
-
464
490
  function getModelCost(modelId: string): number {
465
491
  const bareId = modelId.includes("/") ? modelId.split("/").pop()! : modelId;
466
492
 
@@ -15,6 +15,7 @@ import { MergeConflictError } from "./git-service.js";
15
15
  import { removeSessionStatus } from "./session-status-io.js";
16
16
  import type { WorkerInfo } from "./parallel-orchestrator.js";
17
17
  import { getErrorMessage } from "./error-utils.js";
18
+ import { logWarning } from "./workflow-logger.js";
18
19
 
19
20
  // ─── Types ─────────────────────────────────────────────────────────────────
20
21
 
@@ -47,7 +48,8 @@ export function isMilestoneCompleteInWorktreeDb(basePath: string, mid: string):
47
48
  { timeout: 3000, encoding: "utf-8" },
48
49
  );
49
50
  return (result.stdout || "").trim() === "complete";
50
- } catch {
51
+ } catch (e) {
52
+ logWarning("parallel", `spawnSync milestone completion check failed for ${mid}: ${(e as Error).message}`);
51
53
  return false;
52
54
  }
53
55
  }
@@ -65,8 +67,8 @@ function discoverDbCompletedMilestones(basePath: string): Set<string> {
65
67
  completed.add(entry);
66
68
  }
67
69
  }
68
- } catch {
69
- // worktrees dir may not exist
70
+ } catch (e) {
71
+ logWarning("parallel", `readdirSync for completed set failed: ${(e as Error).message}`);
70
72
  }
71
73
  return completed;
72
74
  }
@@ -41,6 +41,7 @@ import {
41
41
  type ParallelCandidates,
42
42
  } from "./parallel-eligibility.js";
43
43
  import { getErrorMessage } from "./error-utils.js";
44
+ import { logWarning } from "./workflow-logger.js";
44
45
 
45
46
  // ─── Types ─────────────────────────────────────────────────────────────────
46
47
 
@@ -126,7 +127,7 @@ export function persistState(basePath: string): void {
126
127
  const tmp = dest + TMP_SUFFIX;
127
128
  writeFileSync(tmp, JSON.stringify(persisted, null, 2), "utf-8");
128
129
  renameSync(tmp, dest);
129
- } catch { /* non-fatal */ }
130
+ } catch (e) { logWarning("parallel", `persist parallel state failed: ${(e as Error).message}`); }
130
131
  }
131
132
 
132
133
  /**
@@ -136,7 +137,7 @@ function removeStateFile(basePath: string): void {
136
137
  try {
137
138
  const p = stateFilePath(basePath);
138
139
  if (existsSync(p)) unlinkSync(p);
139
- } catch { /* non-fatal */ }
140
+ } catch (e) { logWarning("parallel", `clear parallel state file failed: ${(e as Error).message}`); }
140
141
  }
141
142
 
142
143
  function isPidAlive(pid: number): boolean {
@@ -144,7 +145,8 @@ function isPidAlive(pid: number): boolean {
144
145
  try {
145
146
  process.kill(pid, 0);
146
147
  return true;
147
- } catch {
148
+ } catch (e) {
149
+ logWarning("parallel", `pid alive check failed for pid ${pid}: ${(e as Error).message}`);
148
150
  return false;
149
151
  }
150
152
  }
@@ -176,7 +178,8 @@ export function restoreState(basePath: string): PersistedState | null {
176
178
  }
177
179
 
178
180
  return persisted;
179
- } catch {
181
+ } catch (e) {
182
+ logWarning("parallel", `readParallelState JSON parse failed: ${(e as Error).message}`);
180
183
  return null;
181
184
  }
182
185
  }
@@ -190,8 +193,8 @@ function appendWorkerLog(basePath: string, milestoneId: string, chunk: string):
190
193
  const dir = join(gsdRoot(basePath), "parallel");
191
194
  if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
192
195
  appendFileSync(workerLogPath(basePath, milestoneId), chunk, "utf-8");
193
- } catch {
194
- // Non-fatal diagnostics should never break orchestration.
196
+ } catch (e) {
197
+ logWarning("parallel", `appendFileSync worker log failed for ${milestoneId}: ${(e as Error).message}`);
195
198
  }
196
199
  }
197
200
 
@@ -430,9 +433,8 @@ export async function startParallel(
430
433
  let wtPath: string;
431
434
  try {
432
435
  wtPath = createMilestoneWorktree(basePath, mid);
433
- } catch {
434
- // Worktree creation may fail in test environments or when git
435
- // is not available. Fall back to a placeholder path.
436
+ } catch (e) {
437
+ logWarning("parallel", `createMilestoneWorktree fallback for ${mid}: ${(e as Error).message}`);
436
438
  wtPath = worktreePath(basePath, mid);
437
439
  }
438
440
 
@@ -564,7 +566,8 @@ export function spawnWorker(
564
566
  stdio: ["ignore", "pipe", "pipe"],
565
567
  detached: false,
566
568
  });
567
- } catch {
569
+ } catch (e) {
570
+ logWarning("parallel", `spawnSync worker failed for ${milestoneId}: ${(e as Error).message}`);
568
571
  return false;
569
572
  }
570
573
 
@@ -694,7 +697,8 @@ function resolveGsdBin(): string | null {
694
697
  let thisDir: string;
695
698
  try {
696
699
  thisDir = dirname(fileURLToPath(import.meta.url));
697
- } catch {
700
+ } catch (e) {
701
+ logWarning("parallel", `dirname(fileURLToPath) failed: ${(e as Error).message}`);
698
702
  thisDir = process.cwd();
699
703
  }
700
704
  const candidates = [
@@ -722,7 +726,7 @@ function processWorkerLine(basePath: string, milestoneId: string, line: string):
722
726
  try {
723
727
  event = JSON.parse(line);
724
728
  } catch {
725
- return; // Not valid JSON — skip (stderr leakage, debug output, etc.)
729
+ return; // Non-NDJSON lines (progress text, tool output) are expected — silent drop
726
730
  }
727
731
 
728
732
  const type = String(event.type ?? "");
@@ -817,7 +821,7 @@ export async function stopParallel(
817
821
  } else if (worker.pid !== process.pid) {
818
822
  process.kill(worker.pid, "SIGTERM");
819
823
  }
820
- } catch { /* process may already be dead */ }
824
+ } catch (e) { logWarning("parallel", `process.kill SIGTERM failed for pid ${worker.pid}: ${(e as Error).message}`); }
821
825
  }
822
826
 
823
827
  // Wait for the headless process to cascade SIGTERM to its RPC child.
@@ -833,7 +837,7 @@ export async function stopParallel(
833
837
  } else if (worker.pid !== process.pid) {
834
838
  process.kill(worker.pid, "SIGKILL");
835
839
  }
836
- } catch { /* process may already be dead */ }
840
+ } catch (e) { logWarning("parallel", `process.kill SIGKILL failed for pid ${worker.pid}: ${(e as Error).message}`); }
837
841
  await waitForWorkerExit(worker, 250);
838
842
  }
839
843