gsd-pi 2.59.0 → 2.60.0-dev.2580e65

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 (354) hide show
  1. package/dist/resources/extensions/ask-user-questions.js +7 -4
  2. package/dist/resources/extensions/gsd/auto/phases.js +62 -1
  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 +57 -3
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +43 -3
  7. package/dist/resources/extensions/gsd/auto-prompts.js +49 -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 +72 -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 +58 -5
  20. package/dist/resources/extensions/gsd/bootstrap/system-context.js +11 -10
  21. package/dist/resources/extensions/gsd/captures.js +54 -1
  22. package/dist/resources/extensions/gsd/commands/catalog.js +2 -0
  23. package/dist/resources/extensions/gsd/commands-codebase.js +48 -21
  24. package/dist/resources/extensions/gsd/commands-inspect.js +2 -1
  25. package/dist/resources/extensions/gsd/commands-maintenance.js +32 -19
  26. package/dist/resources/extensions/gsd/complexity-classifier.js +9 -5
  27. package/dist/resources/extensions/gsd/context-masker.js +68 -0
  28. package/dist/resources/extensions/gsd/custom-verification.js +3 -2
  29. package/dist/resources/extensions/gsd/docs/preferences-reference.md +7 -0
  30. package/dist/resources/extensions/gsd/gsd-db.js +35 -15
  31. package/dist/resources/extensions/gsd/guided-flow.js +19 -9
  32. package/dist/resources/extensions/gsd/init-wizard.js +12 -0
  33. package/dist/resources/extensions/gsd/markdown-renderer.js +11 -9
  34. package/dist/resources/extensions/gsd/md-importer.js +5 -4
  35. package/dist/resources/extensions/gsd/milestone-actions.js +3 -2
  36. package/dist/resources/extensions/gsd/milestone-ids.js +2 -1
  37. package/dist/resources/extensions/gsd/model-router.js +199 -45
  38. package/dist/resources/extensions/gsd/parallel-merge.js +5 -3
  39. package/dist/resources/extensions/gsd/parallel-orchestrator.js +26 -14
  40. package/dist/resources/extensions/gsd/phase-anchor.js +56 -0
  41. package/dist/resources/extensions/gsd/preferences-types.js +2 -0
  42. package/dist/resources/extensions/gsd/preferences-validation.js +91 -0
  43. package/dist/resources/extensions/gsd/preferences.js +15 -3
  44. package/dist/resources/extensions/gsd/prompt-loader.js +3 -2
  45. package/dist/resources/extensions/gsd/prompts/execute-task.md +2 -0
  46. package/dist/resources/extensions/gsd/prompts/rethink.md +7 -0
  47. package/dist/resources/extensions/gsd/prompts/triage-captures.md +6 -1
  48. package/dist/resources/extensions/gsd/rethink.js +5 -2
  49. package/dist/resources/extensions/gsd/rule-registry.js +7 -6
  50. package/dist/resources/extensions/gsd/safe-fs.js +6 -8
  51. package/dist/resources/extensions/gsd/state.js +1 -1
  52. package/dist/resources/extensions/gsd/status-guards.js +4 -3
  53. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -2
  54. package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -2
  55. package/dist/resources/extensions/gsd/tools/complete-task.js +3 -2
  56. package/dist/resources/extensions/gsd/tools/plan-milestone.js +3 -2
  57. package/dist/resources/extensions/gsd/tools/plan-slice.js +3 -2
  58. package/dist/resources/extensions/gsd/tools/plan-task.js +2 -1
  59. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
  60. package/dist/resources/extensions/gsd/tools/reopen-slice.js +2 -1
  61. package/dist/resources/extensions/gsd/tools/reopen-task.js +2 -1
  62. package/dist/resources/extensions/gsd/tools/replan-slice.js +2 -1
  63. package/dist/resources/extensions/gsd/tools/validate-milestone.js +2 -1
  64. package/dist/resources/extensions/gsd/triage-resolution.js +135 -1
  65. package/dist/resources/extensions/gsd/triage-ui.js +12 -3
  66. package/dist/resources/extensions/gsd/workflow-events.js +2 -1
  67. package/dist/resources/extensions/gsd/workflow-logger.js +37 -4
  68. package/dist/resources/extensions/gsd/workflow-migration.js +14 -12
  69. package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
  70. package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
  71. package/dist/resources/extensions/gsd/worktree-manager.js +26 -14
  72. package/dist/resources/extensions/shared/interview-ui.js +3 -1
  73. package/dist/resources/skills/btw/SKILL.md +42 -0
  74. package/dist/web/standalone/.next/BUILD_ID +1 -1
  75. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  76. package/dist/web/standalone/.next/build-manifest.json +3 -3
  77. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  78. package/dist/web/standalone/.next/required-server-files.json +3 -3
  79. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  80. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  82. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  90. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  118. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  146. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  152. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  168. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  170. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  172. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/index.html +1 -1
  182. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  183. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  184. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  185. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  186. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  187. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  188. package/dist/web/standalone/.next/server/app/page.js +2 -2
  189. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  191. package/dist/web/standalone/.next/server/chunks/2229.js +1 -1
  192. package/dist/web/standalone/.next/server/chunks/7471.js +3 -3
  193. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/middleware.js +2 -2
  195. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  197. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  198. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  199. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  200. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  202. package/dist/web/standalone/.next/static/chunks/app/page-0c485498795110d6.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  205. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  206. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  207. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  208. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  209. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  210. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  211. package/dist/web/standalone/server.js +1 -1
  212. package/package.json +1 -1
  213. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  214. package/packages/pi-coding-agent/dist/core/extensions/loader.js +5 -0
  215. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  216. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  217. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  218. package/packages/pi-coding-agent/dist/core/extensions/runner.js +16 -0
  219. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +26 -0
  221. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/lsp/config.js +6 -1
  225. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  226. package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
  227. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +2 -0
  228. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +1 -0
  229. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +47 -0
  230. package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +1 -0
  231. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +1 -0
  232. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +6 -0
  234. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.d.ts +2 -0
  236. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.d.ts.map +1 -0
  237. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +122 -0
  238. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -0
  239. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
  240. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  241. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +30 -0
  242. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  243. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
  244. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +1 -7
  245. package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
  246. package/packages/pi-coding-agent/package.json +1 -1
  247. package/packages/pi-coding-agent/src/core/extensions/loader.ts +6 -0
  248. package/packages/pi-coding-agent/src/core/extensions/runner.ts +19 -0
  249. package/packages/pi-coding-agent/src/core/extensions/types.ts +26 -0
  250. package/packages/pi-coding-agent/src/core/lsp/config.ts +7 -1
  251. package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
  252. package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +70 -0
  253. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +156 -0
  254. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +7 -0
  255. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +38 -0
  256. package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +1 -8
  257. package/pkg/package.json +1 -1
  258. package/src/resources/extensions/ask-user-questions.ts +7 -3
  259. package/src/resources/extensions/gsd/auto/phases.ts +70 -1
  260. package/src/resources/extensions/gsd/auto-dashboard.ts +22 -8
  261. package/src/resources/extensions/gsd/auto-dispatch.ts +7 -3
  262. package/src/resources/extensions/gsd/auto-model-selection.ts +77 -6
  263. package/src/resources/extensions/gsd/auto-post-unit.ts +52 -5
  264. package/src/resources/extensions/gsd/auto-prompts.ts +54 -20
  265. package/src/resources/extensions/gsd/auto-recovery.ts +38 -18
  266. package/src/resources/extensions/gsd/auto-start.ts +10 -9
  267. package/src/resources/extensions/gsd/auto-timers.ts +12 -5
  268. package/src/resources/extensions/gsd/auto-unit-closeout.ts +6 -2
  269. package/src/resources/extensions/gsd/auto-verification.ts +3 -6
  270. package/src/resources/extensions/gsd/auto-worktree.ts +121 -55
  271. package/src/resources/extensions/gsd/auto.ts +40 -17
  272. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +4 -3
  273. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +80 -2
  274. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +4 -16
  275. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +2 -1
  276. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +61 -4
  277. package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -10
  278. package/src/resources/extensions/gsd/captures.ts +71 -2
  279. package/src/resources/extensions/gsd/commands/catalog.ts +2 -0
  280. package/src/resources/extensions/gsd/commands-codebase.ts +52 -20
  281. package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
  282. package/src/resources/extensions/gsd/commands-maintenance.ts +28 -19
  283. package/src/resources/extensions/gsd/complexity-classifier.ts +10 -5
  284. package/src/resources/extensions/gsd/context-masker.ts +74 -0
  285. package/src/resources/extensions/gsd/custom-verification.ts +3 -2
  286. package/src/resources/extensions/gsd/docs/preferences-reference.md +7 -0
  287. package/src/resources/extensions/gsd/gsd-db.ts +14 -16
  288. package/src/resources/extensions/gsd/guided-flow.ts +9 -8
  289. package/src/resources/extensions/gsd/init-wizard.ts +12 -0
  290. package/src/resources/extensions/gsd/markdown-renderer.ts +11 -17
  291. package/src/resources/extensions/gsd/md-importer.ts +5 -4
  292. package/src/resources/extensions/gsd/milestone-actions.ts +3 -2
  293. package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
  294. package/src/resources/extensions/gsd/model-router.ts +245 -56
  295. package/src/resources/extensions/gsd/parallel-merge.ts +5 -3
  296. package/src/resources/extensions/gsd/parallel-orchestrator.ts +18 -14
  297. package/src/resources/extensions/gsd/phase-anchor.ts +71 -0
  298. package/src/resources/extensions/gsd/preferences-types.ts +22 -0
  299. package/src/resources/extensions/gsd/preferences-validation.ts +83 -0
  300. package/src/resources/extensions/gsd/preferences.ts +16 -3
  301. package/src/resources/extensions/gsd/prompt-loader.ts +3 -2
  302. package/src/resources/extensions/gsd/prompts/execute-task.md +2 -0
  303. package/src/resources/extensions/gsd/prompts/rethink.md +7 -0
  304. package/src/resources/extensions/gsd/prompts/triage-captures.md +6 -1
  305. package/src/resources/extensions/gsd/rethink.ts +5 -2
  306. package/src/resources/extensions/gsd/rule-registry.ts +7 -6
  307. package/src/resources/extensions/gsd/safe-fs.ts +6 -5
  308. package/src/resources/extensions/gsd/state.ts +1 -1
  309. package/src/resources/extensions/gsd/status-guards.ts +4 -3
  310. package/src/resources/extensions/gsd/tests/capability-router.test.ts +347 -0
  311. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +63 -0
  312. package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +27 -2
  313. package/src/resources/extensions/gsd/tests/context-masker.test.ts +122 -0
  314. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  315. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +1188 -0
  316. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +841 -0
  317. package/src/resources/extensions/gsd/tests/model-router.test.ts +488 -2
  318. package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +83 -0
  319. package/src/resources/extensions/gsd/tests/preferences.test.ts +62 -0
  320. package/src/resources/extensions/gsd/tests/remote-questions.test.ts +21 -0
  321. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +284 -0
  322. package/src/resources/extensions/gsd/tests/status-guards.test.ts +4 -0
  323. package/src/resources/extensions/gsd/tests/stop-backtrack.test.ts +216 -0
  324. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
  325. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +120 -0
  326. package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
  327. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -6
  328. package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -6
  329. package/src/resources/extensions/gsd/tools/complete-task.ts +3 -6
  330. package/src/resources/extensions/gsd/tools/plan-milestone.ts +3 -6
  331. package/src/resources/extensions/gsd/tools/plan-slice.ts +3 -6
  332. package/src/resources/extensions/gsd/tools/plan-task.ts +2 -3
  333. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +4 -6
  334. package/src/resources/extensions/gsd/tools/reopen-slice.ts +2 -3
  335. package/src/resources/extensions/gsd/tools/reopen-task.ts +2 -3
  336. package/src/resources/extensions/gsd/tools/replan-slice.ts +2 -3
  337. package/src/resources/extensions/gsd/tools/validate-milestone.ts +2 -3
  338. package/src/resources/extensions/gsd/triage-resolution.ts +151 -1
  339. package/src/resources/extensions/gsd/triage-ui.ts +12 -3
  340. package/src/resources/extensions/gsd/types.ts +1 -0
  341. package/src/resources/extensions/gsd/workflow-events.ts +2 -1
  342. package/src/resources/extensions/gsd/workflow-logger.ts +52 -5
  343. package/src/resources/extensions/gsd/workflow-migration.ts +14 -12
  344. package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
  345. package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
  346. package/src/resources/extensions/gsd/worktree-manager.ts +16 -14
  347. package/src/resources/extensions/shared/interview-ui.ts +3 -1
  348. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +144 -0
  349. package/src/resources/skills/btw/SKILL.md +42 -0
  350. package/dist/web/standalone/.next/static/chunks/app/page-62be3b5fa91e4c8f.js +0 -1
  351. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  352. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  353. /package/dist/web/standalone/.next/static/{DGvT_c5Vb7Wu3X-fEOVUU → ogyMN7M-3bGGuRY08L5HR}/_buildManifest.js +0 -0
  354. /package/dist/web/standalone/.next/static/{DGvT_c5Vb7Wu3X-fEOVUU → ogyMN7M-3bGGuRY08L5HR}/_ssgManifest.js +0 -0
@@ -82,11 +82,14 @@ export default function AskUserQuestions(pi) {
82
82
  return errorResult(`Error: ask_user_questions requires non-empty options for every question (question "${q.id}" has none)`, params.questions);
83
83
  }
84
84
  }
85
+ // Try remote first if configured (works in both interactive and headless modes).
86
+ // tryRemoteQuestions returns null when no remote channel is configured, so
87
+ // this is a no-op when the user has not set up Slack/Discord/Telegram.
88
+ const { tryRemoteQuestions } = await import("./remote-questions/manager.js");
89
+ const remoteResult = await tryRemoteQuestions(params.questions, signal);
90
+ if (remoteResult)
91
+ return { ...remoteResult, details: remoteResult.details };
85
92
  if (!ctx.hasUI) {
86
- const { tryRemoteQuestions } = await import("./remote-questions/manager.js");
87
- const remoteResult = await tryRemoteQuestions(params.questions, signal);
88
- if (remoteResult)
89
- return { ...remoteResult, details: remoteResult.details };
90
93
  return errorResult("Error: UI not available (non-interactive mode)", params.questions);
91
94
  }
92
95
  // Delegate to shared interview UI
@@ -487,11 +487,53 @@ export async function runDispatch(ic, preData, loopState) {
487
487
  }
488
488
  // ─── runGuards ────────────────────────────────────────────────────────────────
489
489
  /**
490
- * Phase 2: Guards — budget ceiling, context window, secrets re-check.
490
+ * Phase 2: Guards — stop directives, budget ceiling, context window, secrets re-check.
491
491
  * Returns break to exit the loop, or next to proceed to dispatch.
492
492
  */
493
493
  export async function runGuards(ic, mid) {
494
494
  const { ctx, pi, s, deps, prefs } = ic;
495
+ // ── Stop/Backtrack directive guard (#3487) ──
496
+ // Check for unexecuted stop or backtrack captures BEFORE dispatching any unit.
497
+ // This ensures user "halt" directives are honored immediately.
498
+ // IMPORTANT: Fail-closed — any exception during stop handling still breaks the loop
499
+ // to ensure user halt intent is never silently dropped.
500
+ try {
501
+ const { loadStopCaptures, markCaptureExecuted } = await import("../captures.js");
502
+ const stopCaptures = loadStopCaptures(s.basePath);
503
+ if (stopCaptures.length > 0) {
504
+ const first = stopCaptures[0];
505
+ const isBacktrack = first.classification === "backtrack";
506
+ const label = isBacktrack
507
+ ? `Backtrack directive: ${first.text}`
508
+ : `Stop directive: ${first.text}`;
509
+ ctx.ui.notify(label, "warning");
510
+ deps.sendDesktopNotification("GSD", label, "warning", "stop-directive", basename(s.originalBasePath || s.basePath));
511
+ // Pause first — ensures auto-mode stops even if later steps fail
512
+ await deps.pauseAuto(ctx, pi);
513
+ // For backtrack captures, write the backtrack trigger after pausing
514
+ if (isBacktrack) {
515
+ try {
516
+ const { executeBacktrack } = await import("../triage-resolution.js");
517
+ executeBacktrack(s.basePath, mid, first);
518
+ }
519
+ catch (e) {
520
+ debugLog("guards", { phase: "backtrack-execution-error", error: String(e) });
521
+ }
522
+ }
523
+ // Mark captures as executed only after successful pause/transition
524
+ for (const cap of stopCaptures) {
525
+ markCaptureExecuted(s.basePath, cap.id);
526
+ }
527
+ debugLog("autoLoop", { phase: "exit", reason: isBacktrack ? "user-backtrack" : "user-stop" });
528
+ return { action: "break", reason: isBacktrack ? "user-backtrack" : "user-stop" };
529
+ }
530
+ }
531
+ catch (e) {
532
+ // Fail-closed: if anything in the stop guard throws, break the loop
533
+ // rather than silently continuing and dropping user halt intent
534
+ debugLog("guards", { phase: "stop-guard-error", error: String(e) });
535
+ return { action: "break", reason: "stop-guard-error" };
536
+ }
495
537
  // Budget ceiling guard
496
538
  const budgetCeiling = prefs?.budget_ceiling;
497
539
  if (budgetCeiling !== undefined && budgetCeiling > 0) {
@@ -843,6 +885,25 @@ export async function runUnitPhase(ic, iterData, loopState, sidecarItem) {
843
885
  s.unitDispatchCount.delete(`${unitType}/${unitId}`);
844
886
  s.unitRecoveryCount.delete(`${unitType}/${unitId}`);
845
887
  }
888
+ // Write phase handoff anchor after successful research/planning completion
889
+ const anchorPhases = new Set(["research-milestone", "research-slice", "plan-milestone", "plan-slice"]);
890
+ if (artifactVerified && mid && anchorPhases.has(unitType)) {
891
+ try {
892
+ const { writePhaseAnchor } = await import("../phase-anchor.js");
893
+ writePhaseAnchor(s.basePath, mid, {
894
+ phase: unitType,
895
+ milestoneId: mid,
896
+ generatedAt: new Date().toISOString(),
897
+ intent: `Completed ${unitType} for ${unitId}`,
898
+ decisions: [],
899
+ blockers: [],
900
+ nextSteps: [],
901
+ });
902
+ }
903
+ catch (err) { /* non-fatal — anchor is advisory */
904
+ logWarning("engine", `phase anchor failed: ${err instanceof Error ? err.message : String(err)}`);
905
+ }
906
+ }
846
907
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId: ic.flowId, seq: ic.nextSeq(), eventType: "unit-end", data: { unitType, unitId, status: unitResult.status, artifactVerified, ...(unitResult.errorContext ? { errorContext: unitResult.errorContext } : {}) }, causedBy: { flowId: ic.flowId, seq: unitStartSeq } });
847
908
  return { action: "next", data: { unitStartedAt: s.currentUnit?.startedAt } };
848
909
  }
@@ -20,6 +20,7 @@ import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath } from "./pref
20
20
  import { resolveServiceTierIcon, getEffectiveServiceTier } from "./service-tier.js";
21
21
  import { parseUnitId } from "./unit-id.js";
22
22
  import { formatRtkSavingsLabel, getRtkSessionSavings, } from "../shared/rtk-session-stats.js";
23
+ import { logWarning } from "./workflow-logger.js";
23
24
  // ─── UAT Slice Extraction ─────────────────────────────────────────────────────
24
25
  /**
25
26
  * Extract the target slice ID from a run-uat unit ID (e.g. "M001/S01" → "S01").
@@ -220,8 +221,9 @@ export function updateSliceProgressCache(base, mid, activeSid) {
220
221
  }
221
222
  }
222
223
  }
223
- catch {
224
+ catch (err) {
224
225
  // Non-fatal — just omit task count
226
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
225
227
  }
226
228
  }
227
229
  cachedSliceProgress = {
@@ -232,8 +234,9 @@ export function updateSliceProgressCache(base, mid, activeSid) {
232
234
  taskDetails,
233
235
  };
234
236
  }
235
- catch {
237
+ catch (err) {
236
238
  // Non-fatal — widget just won't show progress bar
239
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
237
240
  }
238
241
  }
239
242
  export function getRoadmapSlicesSync() {
@@ -263,8 +266,9 @@ function refreshLastCommit(basePath) {
263
266
  }
264
267
  lastCommitFetchedAt = Date.now();
265
268
  }
266
- catch {
269
+ catch (err) {
267
270
  // Non-fatal — just skip last commit display
271
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
268
272
  }
269
273
  }
270
274
  function getLastCommit(basePath) {
@@ -300,7 +304,9 @@ function ensureWidgetModeLoaded() {
300
304
  widgetMode = saved;
301
305
  }
302
306
  }
303
- catch { /* non-fatal — use default */ }
307
+ catch (err) { /* non-fatal — use default */
308
+ logWarning("dashboard", `operation failed: ${err instanceof Error ? err.message : String(err)}`);
309
+ }
304
310
  }
305
311
  /** Persist widget mode to global preferences YAML. */
306
312
  function persistWidgetMode(mode) {
@@ -320,7 +326,9 @@ function persistWidgetMode(mode) {
320
326
  }
321
327
  writeFileSync(prefsPath, content, "utf-8");
322
328
  }
323
- catch { /* non-fatal — mode still set in memory */ }
329
+ catch (err) { /* non-fatal — mode still set in memory */
330
+ logWarning("dashboard", `file write failed: ${err instanceof Error ? err.message : String(err)}`);
331
+ }
324
332
  }
325
333
  /** Cycle to the next widget mode. Returns the new mode. */
326
334
  export function cycleWidgetMode() {
@@ -360,7 +368,9 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
360
368
  try {
361
369
  cachedBranch = getCurrentBranch(accessors.getBasePath());
362
370
  }
363
- catch { /* not in git repo */ }
371
+ catch (err) { /* not in git repo */
372
+ logWarning("dashboard", `git branch detection failed: ${err instanceof Error ? err.message : String(err)}`);
373
+ }
364
374
  // Cache short pwd (last 2 path segments only) + worktree/branch info
365
375
  let widgetPwd;
366
376
  {
@@ -394,7 +404,8 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
394
404
  const savings = sessionId ? getRtkSessionSavings(accessors.getBasePath(), sessionId) : null;
395
405
  cachedRtkLabel = formatRtkSavingsLabel(savings);
396
406
  }
397
- catch {
407
+ catch (err) {
408
+ logWarning("dashboard", `RTK savings lookup failed: ${err instanceof Error ? err.message : String(err)}`);
398
409
  cachedRtkLabel = null;
399
410
  }
400
411
  };
@@ -416,7 +427,9 @@ export function updateProgressWidget(ctx, unitType, unitId, state, accessors, ti
416
427
  refreshRtkLabel();
417
428
  cachedLines = undefined;
418
429
  }
419
- catch { /* non-fatal */ }
430
+ catch (err) { /* non-fatal */
431
+ logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
432
+ }
420
433
  }, 15_000);
421
434
  return {
422
435
  render(width) {
@@ -13,7 +13,7 @@ import { isDbAvailable, getMilestoneSlices, getPendingGates, markAllGatesOmitted
13
13
  import { extractVerdict, isAcceptableUatVerdict } from "./verdict-parser.js";
14
14
  import { gsdRoot, resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile, relSliceFile, buildMilestoneFileName, } from "./paths.js";
15
15
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
16
- import { logError } from "./workflow-logger.js";
16
+ import { logWarning, logError } from "./workflow-logger.js";
17
17
  import { join } from "node:path";
18
18
  import { hasImplementationArtifacts } from "./auto-recovery.js";
19
19
  import { buildDiscussMilestonePrompt, buildResearchMilestonePrompt, buildPlanMilestonePrompt, buildResearchSlicePrompt, buildPlanSlicePrompt, buildExecuteTaskPrompt, buildCompleteSlicePrompt, buildCompleteMilestonePrompt, buildValidateMilestonePrompt, buildReplanSlicePrompt, buildRunUatPrompt, buildReassessRoadmapPrompt, buildRewriteDocsPrompt, buildReactiveExecutePrompt, buildGateEvaluatePrompt, checkNeedsReassessment, checkNeedsRunUat, } from "./auto-prompts.js";
@@ -564,7 +564,9 @@ export const DISPATCH_RULES = [
564
564
  }
565
565
  }
566
566
  }
567
- catch { /* fall through — don't block on DB errors */ }
567
+ catch (err) { /* fall through — don't block on DB errors */
568
+ logWarning("dispatch", `verification class check failed: ${err instanceof Error ? err.message : String(err)}`);
569
+ }
568
570
  return {
569
571
  action: "dispatch",
570
572
  unitType: "complete-milestone",
@@ -602,8 +604,9 @@ export async function resolveDispatch(ctx) {
602
604
  const registry = getRegistry();
603
605
  return await registry.evaluateDispatch(ctx);
604
606
  }
605
- catch {
607
+ catch (err) {
606
608
  // Registry not initialized — fall back to inline loop
609
+ logWarning("dispatch", `registry dispatch failed, falling back to inline rules: ${err instanceof Error ? err.message : String(err)}`);
607
610
  }
608
611
  for (const rule of DISPATCH_RULES) {
609
612
  const result = await rule.match(ctx);
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { resolveModelWithFallbacksForUnit, resolveDynamicRoutingConfig } from "./preferences.js";
7
7
  import { classifyUnitComplexity, tierLabel } from "./complexity-classifier.js";
8
- import { resolveModelForComplexity, escalateTier } from "./model-router.js";
8
+ import { resolveModelForComplexity, escalateTier, getEligibleModels, loadCapabilityOverrides } from "./model-router.js";
9
9
  import { getLedger, getProjectTotals } from "./metrics.js";
10
10
  import { unitPhaseLabel } from "./auto-dashboard.js";
11
11
  export function resolvePreferredModelConfig(unitType, autoModeStartModel) {
@@ -68,14 +68,68 @@ export async function selectAndApplyModel(ctx, pi, unitType, unitId, basePath, p
68
68
  }
69
69
  }
70
70
  }
71
- const routingResult = resolveModelForComplexity(classification, modelConfig, routingConfig, availableModelIds);
71
+ // Load user capability overrides from preferences (D-17: deep-merged with built-in profiles)
72
+ const capabilityOverrides = loadCapabilityOverrides(prefs ?? {});
73
+ // Fire before_model_select hook (ADR-004, D-03)
74
+ // Hook can override model selection entirely by returning { modelId }
75
+ let hookOverride;
76
+ if (routingConfig.hooks !== false) {
77
+ const eligible = getEligibleModels(classification.tier, availableModelIds, routingConfig);
78
+ const hookResult = await pi.emitBeforeModelSelect({
79
+ unitType,
80
+ unitId,
81
+ classification: {
82
+ tier: classification.tier,
83
+ reason: classification.reason,
84
+ downgraded: classification.downgraded,
85
+ },
86
+ taskMetadata: classification.taskMetadata,
87
+ eligibleModels: eligible,
88
+ phaseConfig: modelConfig ? {
89
+ primary: modelConfig.primary,
90
+ fallbacks: modelConfig.fallbacks ?? [],
91
+ } : undefined,
92
+ });
93
+ if (hookResult?.modelId) {
94
+ hookOverride = hookResult.modelId;
95
+ }
96
+ }
97
+ let routingResult;
98
+ if (hookOverride) {
99
+ // Hook override bypasses capability scoring entirely
100
+ routingResult = {
101
+ modelId: hookOverride,
102
+ fallbacks: [
103
+ ...(modelConfig?.fallbacks ?? []).filter(f => f !== hookOverride),
104
+ ...(modelConfig?.primary && modelConfig.primary !== hookOverride ? [modelConfig.primary] : []),
105
+ ],
106
+ tier: classification.tier,
107
+ wasDowngraded: hookOverride !== modelConfig?.primary,
108
+ reason: `hook override: ${hookOverride}`,
109
+ selectionMethod: "tier-only",
110
+ };
111
+ }
112
+ else {
113
+ routingResult = resolveModelForComplexity(classification, modelConfig, routingConfig, availableModelIds, unitType, classification.taskMetadata, capabilityOverrides);
114
+ }
72
115
  if (routingResult.wasDowngraded) {
73
116
  effectiveModelConfig = {
74
117
  primary: routingResult.modelId,
75
118
  fallbacks: routingResult.fallbacks,
76
119
  };
77
120
  if (verbose) {
78
- ctx.ui.notify(`Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`, "info");
121
+ if (routingResult.selectionMethod === "capability-scored" && routingResult.capabilityScores) {
122
+ // Verbose scoring breakdown for capability-scored decisions (D-20)
123
+ const tierLbl = tierLabel(classification.tier);
124
+ const scores = Object.entries(routingResult.capabilityScores)
125
+ .sort(([, a], [, b]) => b - a)
126
+ .map(([id, score]) => `${id}: ${score.toFixed(1)}`)
127
+ .join(", ");
128
+ ctx.ui.notify(`Dynamic routing [${tierLbl}]: ${routingResult.modelId} (capability-scored) — ${scores}`, "info");
129
+ }
130
+ else {
131
+ ctx.ui.notify(`Dynamic routing [${tierLabel(classification.tier)}]: ${routingResult.modelId} (${classification.reason})`, "info");
132
+ }
79
133
  }
80
134
  }
81
135
  routingTierLabel = ` [${tierLabel(classification.tier)}]`;
@@ -26,7 +26,7 @@ import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getA
26
26
  import { renderPlanCheckboxes } from "./markdown-renderer.js";
27
27
  import { consumeSignal } from "./session-status-io.js";
28
28
  import { checkPostUnitHooks, isRetryPending, consumeRetryTrigger, persistHookState, resolveHookArtifactPath, } from "./post-unit-hooks.js";
29
- import { hasPendingCaptures, loadPendingCaptures } from "./captures.js";
29
+ import { hasPendingCaptures, loadPendingCaptures, revertExecutorResolvedCaptures } from "./captures.js";
30
30
  import { debugLog } from "./debug-logger.js";
31
31
  import { runSafely } from "./auto-utils.js";
32
32
  /** Enqueue a sidecar item (hook, triage, or quick-task) for the main loop to
@@ -208,8 +208,9 @@ export async function postUnitPreVerification(pctx, opts) {
208
208
  const { getTaskIssueNumberForCommit } = await import("../github-sync/sync.js");
209
209
  ghIssueNumber = getTaskIssueNumberForCommit(s.basePath, mid, sid, tid) ?? undefined;
210
210
  }
211
- catch {
211
+ catch (err) {
212
212
  // GitHub sync not available — skip
213
+ logWarning("engine", `GitHub issue lookup failed: ${err instanceof Error ? err.message : String(err)}`);
213
214
  }
214
215
  taskContext = {
215
216
  taskId: `${sid}/${tid}`,
@@ -448,7 +449,7 @@ export async function postUnitPostVerification(pctx) {
448
449
  catch (dbErr) {
449
450
  // DB unavailable — fail explicitly rather than silently reverting to markdown mutation.
450
451
  // Use 'gsd recover' to rebuild DB state from disk if needed.
451
- process.stderr.write(`gsd: retry state-reset failed (DB unavailable): ${dbErr.message}. Run 'gsd recover' to reconcile.\n`);
452
+ logError("engine", `retry state-reset failed (DB unavailable): ${dbErr.message}. Run 'gsd recover' to reconcile.`);
452
453
  }
453
454
  }
454
455
  // 2. Delete SUMMARY.md for the task
@@ -478,6 +479,45 @@ export async function postUnitPostVerification(pctx) {
478
479
  }
479
480
  }
480
481
  }
482
+ // ── Fast-path stop detection (#3487) ──
483
+ // Before waiting for triage, check if any PENDING captures contain explicit
484
+ // stop/halt language. If so, pause immediately — don't wait for triage.
485
+ if (s.currentUnit && s.currentUnit.type !== "triage-captures") {
486
+ try {
487
+ const pending = loadPendingCaptures(s.basePath);
488
+ // Match only when the capture text starts with a stop/halt directive word,
489
+ // or the entire text is short and dominated by such a word. This avoids
490
+ // false positives on captures like "add a pause button" or "stop the timer
491
+ // from re-rendering" — those are feature descriptions, not halt directives.
492
+ const STOP_PATTERN = /^(stop|halt|abort|don'?t continue|pause|cease)\b/i;
493
+ const stopCapture = pending.find(c => STOP_PATTERN.test(c.text.trim()));
494
+ if (stopCapture) {
495
+ ctx.ui.notify(`Stop directive detected in pending capture ${stopCapture.id}: "${stopCapture.text}" — pausing auto-mode.`, "warning");
496
+ debugLog("postUnit", { phase: "fast-stop", captureId: stopCapture.id });
497
+ await pauseAuto(ctx, pi);
498
+ return "stopped";
499
+ }
500
+ }
501
+ catch (e) {
502
+ debugLog("postUnit", { phase: "fast-stop-error", error: String(e) });
503
+ }
504
+ }
505
+ // ── Capture protection: revert executor-silenced captures (#3487) ──
506
+ // Non-triage agents can write **Status:** resolved to CAPTURES.md, bypassing
507
+ // the triage pipeline. Revert those to pending before the triage check.
508
+ if (s.currentUnit &&
509
+ s.currentUnit.type !== "triage-captures") {
510
+ try {
511
+ const reverted = revertExecutorResolvedCaptures(s.basePath);
512
+ if (reverted > 0) {
513
+ debugLog("postUnit", { phase: "capture-protection", reverted });
514
+ ctx.ui.notify(`Reverted ${reverted} capture${reverted === 1 ? "" : "s"} silenced by executor — re-queuing for triage.`, "warning");
515
+ }
516
+ }
517
+ catch (e) {
518
+ debugLog("postUnit", { phase: "capture-protection-error", error: String(e) });
519
+ }
520
+ }
481
521
  // ── Triage check ──
482
522
  if (!s.stepMode &&
483
523
  s.currentUnit &&
@@ -17,6 +17,8 @@ import { existsSync } from "node:fs";
17
17
  import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary } from "./context-budget.js";
18
18
  import { getPendingGates } from "./gsd-db.js";
19
19
  import { formatDecisionsCompact, formatRequirementsCompact } from "./structured-data-formatter.js";
20
+ import { readPhaseAnchor, formatAnchorForPrompt } from "./phase-anchor.js";
21
+ import { logWarning } from "./workflow-logger.js";
20
22
  // ─── Preamble Cap ─────────────────────────────────────────────────────────────
21
23
  const MAX_PREAMBLE_CHARS = 30_000;
22
24
  function capPreamble(preamble) {
@@ -36,7 +38,8 @@ function formatExecutorConstraints() {
36
38
  const prefs = loadEffectiveGSDPreferences();
37
39
  windowTokens = resolveExecutorContextWindow(undefined, prefs?.preferences);
38
40
  }
39
- catch {
41
+ catch (e) {
42
+ logWarning("prompt", `resolveExecutorContextWindow failed: ${e.message}`);
40
43
  windowTokens = 200_000; // safe default
41
44
  }
42
45
  const budgets = computeBudgets(windowTokens);
@@ -160,7 +163,9 @@ export async function inlineDependencySummaries(mid, sid, base, budgetChars) {
160
163
  // If slice not found in DB, fall through to file-based parsing
161
164
  }
162
165
  }
163
- catch { /* fall through */ }
166
+ catch (err) {
167
+ logWarning("prompt", `inlineDependencySummaries DB lookup failed: ${err instanceof Error ? err.message : String(err)}`);
168
+ }
164
169
  // If DB didn't provide depends, fall back to roadmap parsing
165
170
  if (!depends) {
166
171
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
@@ -232,8 +237,8 @@ export async function inlineDecisionsFromDb(base, milestoneId, scope, level) {
232
237
  }
233
238
  }
234
239
  }
235
- catch {
236
- // DB not available fall through to filesystem
240
+ catch (err) {
241
+ logWarning("prompt", `inlineDecisionsFromDb failed: ${err instanceof Error ? err.message : String(err)}`);
237
242
  }
238
243
  return inlineGsdRootFile(base, "decisions.md", "Decisions");
239
244
  }
@@ -257,8 +262,8 @@ export async function inlineRequirementsFromDb(base, sliceId, level) {
257
262
  }
258
263
  }
259
264
  }
260
- catch {
261
- // DB not available fall through to filesystem
265
+ catch (err) {
266
+ logWarning("prompt", `inlineRequirementsFromDb failed: ${err instanceof Error ? err.message : String(err)}`);
262
267
  }
263
268
  return inlineGsdRootFile(base, "requirements.md", "Requirements");
264
269
  }
@@ -277,8 +282,8 @@ export async function inlineProjectFromDb(base) {
277
282
  }
278
283
  }
279
284
  }
280
- catch {
281
- // DB not available fall through to filesystem
285
+ catch (err) {
286
+ logWarning("prompt", `inlineProjectFromDb failed: ${err instanceof Error ? err.message : String(err)}`);
282
287
  }
283
288
  return inlineGsdRootFile(base, "project.md", "Project");
284
289
  }
@@ -398,8 +403,8 @@ export function buildSkillActivationBlock(params) {
398
403
  matched.add(normalizeSkillReference(skillName));
399
404
  }
400
405
  }
401
- catch {
402
- // Non-fatal malformed task plan should not break prompt construction
406
+ catch (err) {
407
+ logWarning("prompt", `parseTaskPlanFile failed: ${err instanceof Error ? err.message : String(err)}`);
403
408
  }
404
409
  }
405
410
  const ordered = [...matched]
@@ -618,7 +623,9 @@ export async function checkNeedsReassessment(base, mid, state) {
618
623
  }
619
624
  }
620
625
  }
621
- catch { /* fall through */ }
626
+ catch (err) {
627
+ logWarning("prompt", `checkNeedsReassessment DB lookup failed: ${err instanceof Error ? err.message : String(err)}`);
628
+ }
622
629
  // File-based fallback using roadmap checkboxes
623
630
  const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
624
631
  if (!roadmapPath)
@@ -693,7 +700,9 @@ export async function checkNeedsRunUat(base, mid, state, prefs) {
693
700
  }
694
701
  }
695
702
  }
696
- catch { /* fall through */ }
703
+ catch (err) {
704
+ logWarning("prompt", `checkNeedsRunUat DB lookup failed: ${err instanceof Error ? err.message : String(err)}`);
705
+ }
697
706
  // File-based fallback using roadmap checkboxes
698
707
  if (!prefs?.uat_dispatch)
699
708
  return null;
@@ -797,6 +806,10 @@ export async function buildPlanMilestonePrompt(mid, midTitle, base, level) {
797
806
  const researchPath = resolveMilestoneFile(base, mid, "RESEARCH");
798
807
  const researchRel = relMilestoneFile(base, mid, "RESEARCH");
799
808
  const inlined = [];
809
+ // Inject phase handoff anchor from research phase (if available)
810
+ const researchAnchor = readPhaseAnchor(base, mid, "research-milestone");
811
+ if (researchAnchor)
812
+ inlined.push(formatAnchorForPrompt(researchAnchor));
800
813
  inlined.push(await inlineFile(contextPath, contextRel, "Milestone Context"));
801
814
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Milestone Research");
802
815
  if (researchInline)
@@ -919,6 +932,10 @@ export async function buildPlanSlicePrompt(mid, _midTitle, sid, sTitle, base, le
919
932
  const researchPath = resolveSliceFile(base, mid, sid, "RESEARCH");
920
933
  const researchRel = relSliceFile(base, mid, sid, "RESEARCH");
921
934
  const inlined = [];
935
+ // Inject phase handoff anchor from research phase (if available)
936
+ const researchSliceAnchor = readPhaseAnchor(base, mid, "research-slice");
937
+ if (researchSliceAnchor)
938
+ inlined.push(formatAnchorForPrompt(researchSliceAnchor));
922
939
  inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
923
940
  const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
924
941
  if (researchInline)
@@ -974,6 +991,8 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
974
991
  ? level
975
992
  : { level: level };
976
993
  const inlineLevel = opts.level ?? resolveInlineLevel();
994
+ // Inject phase handoff anchor from planning phase (if available)
995
+ const planAnchor = readPhaseAnchor(base, mid, "plan-slice");
977
996
  const priorSummaries = opts.carryForwardPaths ?? await getPriorTaskSummaryPaths(mid, sid, tid, base);
978
997
  const priorLines = priorSummaries.length > 0
979
998
  ? priorSummaries.map(p => `- \`${p}\``).join("\n")
@@ -1042,9 +1061,11 @@ export async function buildExecuteTaskPrompt(mid, sid, sTitle, tid, tTitle, base
1042
1061
  const runtimeContext = runtimeContent
1043
1062
  ? `### Runtime Context\nSource: \`.gsd/RUNTIME.md\`\n\n${runtimeContent.trim()}`
1044
1063
  : "";
1064
+ const phaseAnchorSection = planAnchor ? formatAnchorForPrompt(planAnchor) : "";
1045
1065
  return loadPrompt("execute-task", {
1046
1066
  overridesSection,
1047
1067
  runtimeContext,
1068
+ phaseAnchorSection,
1048
1069
  workingDirectory: base,
1049
1070
  milestoneId: mid, sliceId: sid, sliceTitle: sTitle, taskId: tid, taskTitle: tTitle,
1050
1071
  planPath: join(base, relSliceFile(base, mid, sid, "PLAN")),
@@ -1137,7 +1158,9 @@ export async function buildCompleteMilestonePrompt(mid, midTitle, base, level) {
1137
1158
  sliceIds = getMilestoneSlices(mid).map(s => s.id);
1138
1159
  }
1139
1160
  }
1140
- catch { /* fall through */ }
1161
+ catch (err) {
1162
+ logWarning("prompt", `buildCompleteMilestonePrompt DB lookup failed: ${err instanceof Error ? err.message : String(err)}`);
1163
+ }
1141
1164
  // File-based fallback: parse roadmap for slice IDs when DB has no data
1142
1165
  if (sliceIds.length === 0 && roadmapPath) {
1143
1166
  const roadmapContent = await loadFile(roadmapPath);
@@ -1220,7 +1243,9 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1220
1243
  }
1221
1244
  }
1222
1245
  }
1223
- catch { /* fall through */ }
1246
+ catch (err) {
1247
+ logWarning("prompt", `buildValidateMilestonePrompt verification classes lookup failed: ${err instanceof Error ? err.message : String(err)}`);
1248
+ }
1224
1249
  // Inline all slice summaries and UAT results
1225
1250
  let valSliceIds = [];
1226
1251
  try {
@@ -1229,7 +1254,9 @@ export async function buildValidateMilestonePrompt(mid, midTitle, base, level) {
1229
1254
  valSliceIds = getMilestoneSlices(mid).map(s => s.id);
1230
1255
  }
1231
1256
  }
1232
- catch { /* fall through */ }
1257
+ catch (err) {
1258
+ logWarning("prompt", `buildValidateMilestonePrompt slice IDs lookup failed: ${err instanceof Error ? err.message : String(err)}`);
1259
+ }
1233
1260
  // File-based fallback: parse roadmap for slice IDs when DB has no data
1234
1261
  if (valSliceIds.length === 0 && roadmapPath) {
1235
1262
  const roadmapContent = await loadFile(roadmapPath);
@@ -1365,8 +1392,8 @@ export async function buildReplanSlicePrompt(mid, midTitle, sid, sTitle, base) {
1365
1392
  captureContext = replanCaptures.map(c => `- **${c.id}**: "${c.text}" — ${c.rationale ?? "no rationale"}`).join("\n");
1366
1393
  }
1367
1394
  }
1368
- catch {
1369
- // Non-fatal captures module may not be available
1395
+ catch (err) {
1396
+ logWarning("prompt", `loadReplanCaptures failed: ${err instanceof Error ? err.message : String(err)}`);
1370
1397
  }
1371
1398
  return loadPrompt("replan-slice", {
1372
1399
  workingDirectory: base,
@@ -1455,8 +1482,8 @@ export async function buildReassessRoadmapPrompt(mid, midTitle, completedSliceId
1455
1482
  deferredCaptures = deferred.map(c => `- **${c.id}**: "${c.text}" — ${c.rationale ?? "deferred during triage"}`).join("\n");
1456
1483
  }
1457
1484
  }
1458
- catch {
1459
- // Non-fatal captures module may not be available
1485
+ catch (err) {
1486
+ logWarning("prompt", `loadDeferredCaptures failed: ${err instanceof Error ? err.message : String(err)}`);
1460
1487
  }
1461
1488
  const reassessCommitInstruction = "Do not commit — .gsd/ planning docs are managed externally and not tracked in git.";
1462
1489
  return loadPrompt("reassess-roadmap", {
@@ -1633,7 +1660,9 @@ export async function buildRewriteDocsPrompt(mid, midTitle, activeSlice, base, o
1633
1660
  .map(t => ({ id: t.id }));
1634
1661
  }
1635
1662
  }
1636
- catch { /* fall through */ }
1663
+ catch (err) {
1664
+ logWarning("prompt", `buildRewriteDocsPrompt DB task lookup failed: ${err instanceof Error ? err.message : String(err)}`);
1665
+ }
1637
1666
  if (!incompleteTasks) {
1638
1667
  // DB unavailable — no task data to inline
1639
1668
  incompleteTasks = [];