gsd-pi 2.77.0-dev.58d3d4d6c → 2.77.0-dev.cfd69e714

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 (429) hide show
  1. package/README.md +1 -1
  2. package/dist/claude-cli-check.js +5 -1
  3. package/dist/headless.js +49 -4
  4. package/dist/resource-loader.d.ts +40 -0
  5. package/dist/resource-loader.js +32 -13
  6. package/dist/resources/extensions/browser-tools/capture.js +9 -0
  7. package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  8. package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  9. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  10. package/dist/resources/extensions/browser-tools/tools/forms.js +5 -1
  11. package/dist/resources/extensions/browser-tools/tools/intent.js +5 -1
  12. package/dist/resources/extensions/claude-code-cli/readiness.js +5 -1
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/gsd/auto/loop.js +43 -0
  15. package/dist/resources/extensions/gsd/auto/phases.js +15 -21
  16. package/dist/resources/extensions/gsd/auto/session.js +0 -2
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +102 -24
  18. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  19. package/dist/resources/extensions/gsd/auto-post-unit.js +71 -64
  20. package/dist/resources/extensions/gsd/auto-prompts.js +329 -102
  21. package/dist/resources/extensions/gsd/auto-recovery.js +195 -23
  22. package/dist/resources/extensions/gsd/auto-start.js +34 -24
  23. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  24. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  25. package/dist/resources/extensions/gsd/auto.js +31 -20
  26. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +9 -1
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  28. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  29. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  30. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  31. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  32. package/dist/resources/extensions/gsd/component-types.js +69 -0
  33. package/dist/resources/extensions/gsd/detection.js +49 -1
  34. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  35. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  36. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  37. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  38. package/dist/resources/extensions/gsd/git-service.js +126 -2
  39. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  40. package/dist/resources/extensions/gsd/guided-flow.js +17 -5
  41. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  42. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  43. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  44. package/dist/resources/extensions/gsd/model-router.js +6 -0
  45. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  46. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  47. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  48. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  49. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  50. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  51. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  52. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  53. package/dist/resources/extensions/gsd/state.js +44 -33
  54. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  55. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  56. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  57. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  58. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  59. package/dist/resources/extensions/gsd/worktree-manager.js +34 -8
  60. package/dist/resources/extensions/mcp-client/index.js +3 -1
  61. package/dist/resources/extensions/ollama/index.js +5 -1
  62. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  63. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  64. package/dist/web/standalone/.next/BUILD_ID +1 -1
  65. package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
  66. package/dist/web/standalone/.next/build-manifest.json +2 -2
  67. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  85. package/dist/web/standalone/.next/server/app/index.html +1 -1
  86. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
  93. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  94. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  95. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  96. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  97. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  98. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  99. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  100. package/package.json +2 -3
  101. package/packages/daemon/src/logger.ts +4 -3
  102. package/packages/mcp-server/dist/server.d.ts +24 -0
  103. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  104. package/packages/mcp-server/dist/server.js +88 -87
  105. package/packages/mcp-server/dist/server.js.map +1 -1
  106. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  107. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  108. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  109. package/packages/mcp-server/src/server.ts +131 -105
  110. package/packages/mcp-server/src/workflow-tools.test.ts +80 -39
  111. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  112. package/packages/native/package.json +1 -1
  113. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  114. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  115. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  116. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  117. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  118. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  119. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  120. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  121. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  122. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  123. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  124. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  125. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  127. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  128. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  129. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  130. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  131. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  132. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  133. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  134. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  135. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  136. package/packages/pi-ai/dist/models.test.js +36 -11
  137. package/packages/pi-ai/dist/models.test.js.map +1 -1
  138. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  139. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  140. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  141. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  142. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  143. package/packages/pi-ai/src/models.test.ts +48 -11
  144. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  145. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  146. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  148. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  150. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  151. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  152. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  153. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  154. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  155. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  156. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  157. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  158. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  159. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  160. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  161. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  163. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  165. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  167. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  168. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  169. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  170. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  171. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  173. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  175. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/index.js +1 -0
  177. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  179. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  180. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  182. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  184. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  186. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  187. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  188. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  189. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  190. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  191. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  192. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  193. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  194. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  195. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  196. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  197. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  198. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  199. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  200. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  201. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  202. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  203. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  204. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  205. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  206. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  207. package/packages/pi-coding-agent/src/index.ts +1 -0
  208. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  209. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  210. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  211. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  212. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  213. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  214. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  215. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  216. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  217. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  218. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  219. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +36 -12
  220. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  221. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  222. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  223. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  224. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  225. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  226. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  227. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  228. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  229. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  230. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  231. package/packages/pi-tui/dist/components/editor.js +19 -0
  232. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  233. package/packages/pi-tui/dist/components/image.test.js +6 -5
  234. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  235. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  236. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  237. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  238. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  239. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  240. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +41 -12
  241. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  242. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  243. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  244. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  245. package/packages/pi-tui/src/components/editor.ts +22 -0
  246. package/packages/pi-tui/src/components/image.test.ts +10 -5
  247. package/packages/pi-tui/src/editor-component.ts +3 -0
  248. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  249. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  250. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  251. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  252. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  253. package/scripts/install.js +15 -1
  254. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  255. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  256. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  257. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  258. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  259. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  260. package/src/resources/extensions/claude-code-cli/readiness.ts +5 -1
  261. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  262. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  263. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  264. package/src/resources/extensions/github-sync/tests/templates.test.ts +33 -1
  265. package/src/resources/extensions/gsd/auto/loop.ts +47 -0
  266. package/src/resources/extensions/gsd/auto/phases.ts +16 -20
  267. package/src/resources/extensions/gsd/auto/session.ts +0 -2
  268. package/src/resources/extensions/gsd/auto-dispatch.ts +113 -24
  269. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  270. package/src/resources/extensions/gsd/auto-post-unit.ts +82 -73
  271. package/src/resources/extensions/gsd/auto-prompts.ts +330 -90
  272. package/src/resources/extensions/gsd/auto-recovery.ts +225 -24
  273. package/src/resources/extensions/gsd/auto-start.ts +54 -6
  274. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  275. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  276. package/src/resources/extensions/gsd/auto.ts +43 -22
  277. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +9 -1
  278. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  279. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  280. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  281. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  282. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  283. package/src/resources/extensions/gsd/component-types.ts +362 -0
  284. package/src/resources/extensions/gsd/detection.ts +58 -1
  285. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  286. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  287. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  288. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  289. package/src/resources/extensions/gsd/git-service.ts +133 -2
  290. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  291. package/src/resources/extensions/gsd/guided-flow.ts +20 -5
  292. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  293. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  294. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  295. package/src/resources/extensions/gsd/model-router.ts +6 -0
  296. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  297. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  298. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  299. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  300. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  301. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  302. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  303. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  304. package/src/resources/extensions/gsd/state.ts +49 -44
  305. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  306. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  307. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  308. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  309. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +94 -289
  310. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  311. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  312. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  313. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  314. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -197
  315. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  316. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  317. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  318. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  319. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  320. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  321. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  322. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  323. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  324. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  325. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  326. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  327. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -3
  328. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  329. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  330. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +9 -105
  331. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  332. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  333. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  334. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -57
  335. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  336. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  337. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  338. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  339. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  340. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  341. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  342. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  343. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  344. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -62
  345. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  346. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  347. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  348. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -49
  349. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  350. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -133
  351. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  352. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  353. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  354. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  355. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  356. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  357. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  358. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  359. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  360. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  361. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  362. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  363. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  364. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  365. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  366. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  367. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  368. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +5 -5
  369. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  370. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  371. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  372. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +12 -61
  373. package/src/resources/extensions/gsd/tests/test-helpers.ts +21 -8
  374. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  375. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  376. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  377. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  378. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  379. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  380. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -0
  381. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -81
  382. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  383. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  384. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  385. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  386. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  387. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  388. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  389. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  390. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  391. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  392. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  393. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  394. package/src/resources/extensions/gsd/types.ts +3 -3
  395. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  396. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  397. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  398. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  399. package/src/resources/extensions/gsd/worktree-manager.ts +55 -7
  400. package/src/resources/extensions/mcp-client/index.ts +3 -1
  401. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  402. package/src/resources/extensions/ollama/index.ts +5 -1
  403. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  404. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  405. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  406. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  407. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  408. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  409. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  410. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  411. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  412. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  413. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  414. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -144
  415. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  416. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  417. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  418. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  419. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  420. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  421. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -75
  422. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  423. package/src/resources/extensions/gsd/tests/forensics-worktree-telemetry.test.ts +0 -145
  424. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  425. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  426. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -130
  427. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -43
  428. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_buildManifest.js +0 -0
  429. /package/dist/web/standalone/.next/static/{Cev5xrAYA3ZGTRLyjR2fX → SvCJDZPQW104bR1KnBQg1}/_ssgManifest.js +0 -0
@@ -16,9 +16,7 @@
16
16
  * `let` or `var` declarations.
17
17
  */
18
18
  // ─── Constants ───────────────────────────────────────────────────────────────
19
- export const MAX_UNIT_DISPATCHES = 3;
20
19
  export const STUB_RECOVERY_THRESHOLD = 2;
21
- export const MAX_LIFETIME_DISPATCHES = 6;
22
20
  export const NEW_SESSION_TIMEOUT_MS = 120_000;
23
21
  // ─── AutoSession ─────────────────────────────────────────────────────────────
24
22
  export class AutoSession {
@@ -23,7 +23,41 @@ import { buildDiscussMilestonePrompt, buildResearchMilestonePrompt, buildPlanMil
23
23
  import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
24
24
  import { resolveUokFlags } from "./uok/flags.js";
25
25
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
26
+ import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
26
27
  import { EXECUTION_ENTRY_PHASES, hasFinalizedMilestoneContext } from "./uok/plan-v2.js";
28
+ import { isAutoActive } from "./auto.js";
29
+ import { markDepthVerified } from "./bootstrap/write-gate.js";
30
+ let reassessmentChecker = checkNeedsReassessment;
31
+ export function setReassessmentCheckerForTest(checker) {
32
+ const previous = reassessmentChecker;
33
+ reassessmentChecker = checker;
34
+ return () => {
35
+ reassessmentChecker = previous;
36
+ };
37
+ }
38
+ async function readUatGateVerdict(basePath, mid, sliceId) {
39
+ const uatFile = resolveSliceFile(basePath, mid, sliceId, "UAT");
40
+ const assessmentFile = resolveSliceFile(basePath, mid, sliceId, "ASSESSMENT");
41
+ const uatContent = uatFile ? await loadFile(uatFile) : null;
42
+ const uatType = uatContent ? extractUatType(uatContent) : undefined;
43
+ const assessmentContent = assessmentFile ? await loadFile(assessmentFile) : null;
44
+ if (assessmentContent) {
45
+ const assessmentVerdict = extractVerdict(assessmentContent);
46
+ if (assessmentVerdict) {
47
+ return {
48
+ verdict: assessmentVerdict,
49
+ uatType: uatType ?? extractUatType(assessmentContent),
50
+ };
51
+ }
52
+ }
53
+ if (uatContent) {
54
+ const legacyUatVerdict = extractVerdict(uatContent);
55
+ if (legacyUatVerdict) {
56
+ return { verdict: legacyUatVerdict, uatType };
57
+ }
58
+ }
59
+ return null;
60
+ }
27
61
  function missingSliceStop(mid, phase) {
28
62
  return {
29
63
  action: "stop",
@@ -178,6 +212,16 @@ export const DISPATCH_RULES = [
178
212
  // as missing, and an auto worktree may fall back to GSD_PROJECT_ROOT.
179
213
  if (hasFinalizedMilestoneContext(basePath, mid))
180
214
  return null;
215
+ // H6 fix (#4973): In auto-mode there is no human to answer the
216
+ // depth-verification ask_user_questions, so the write-gate deadlocks.
217
+ // Pre-mark the milestone as depth-verified so gsd_summary_save({artifact_type:"CONTEXT"})
218
+ // is not blocked. Safe ordering: session_switch fires clearDiscussionFlowState()
219
+ // (register-hooks.ts:106) before before_agent_start, which fires before resolveDispatch
220
+ // reaches this match fn — so this call always happens after any session-switch reset.
221
+ // Interactive sessions (isAutoActive()===false) are unaffected.
222
+ if (isAutoActive()) {
223
+ markDepthVerified(mid, basePath);
224
+ }
181
225
  return {
182
226
  action: "dispatch",
183
227
  unitType: "discuss-milestone",
@@ -236,27 +280,29 @@ export const DISPATCH_RULES = [
236
280
  // Only applies when UAT dispatch is enabled
237
281
  if (!prefs?.uat_dispatch)
238
282
  return null;
239
- const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
240
- // DB-first: get completed slices from DB
241
- let completedSliceIds;
283
+ // DB-first: prefer closed slices from DB; fall back to ROADMAP on disk.
284
+ let closedSliceIds;
242
285
  if (isDbAvailable()) {
243
- completedSliceIds = getMilestoneSlices(mid)
244
- .filter(s => s.status === "complete")
286
+ closedSliceIds = getMilestoneSlices(mid)
287
+ .filter(s => isClosedStatus(s.status))
245
288
  .map(s => s.id);
246
289
  }
247
290
  else {
248
- return null;
291
+ // Filesystem fallback for degraded / unmigrated projects.
292
+ // `slice.done` in the parsed ROADMAP is the disk-level closed signal.
293
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
294
+ const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
295
+ if (!roadmapContent)
296
+ return null;
297
+ const roadmap = parseRoadmap(roadmapContent);
298
+ closedSliceIds = roadmap.slices.filter(s => s.done).map(s => s.id);
249
299
  }
250
- for (const sliceId of completedSliceIds) {
251
- const resultFile = resolveSliceFile(basePath, mid, sliceId, "UAT");
252
- if (!resultFile)
253
- continue;
254
- const content = await loadFile(resultFile);
255
- if (!content)
300
+ for (const sliceId of closedSliceIds) {
301
+ const result = await readUatGateVerdict(basePath, mid, sliceId);
302
+ if (!result)
256
303
  continue;
257
- const verdict = extractVerdict(content);
258
- const uatType = extractUatType(content);
259
- if (verdict && !isAcceptableUatVerdict(verdict, uatType)) {
304
+ const { verdict, uatType } = result;
305
+ if (!isAcceptableUatVerdict(verdict, uatType)) {
260
306
  return {
261
307
  action: "stop",
262
308
  reason: `UAT verdict for ${sliceId} is "${verdict}" — blocking progression until resolved.\nReview the UAT result and update the verdict to PASS, or re-run /gsd auto after fixing.`,
@@ -272,12 +318,16 @@ export const DISPATCH_RULES = [
272
318
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
273
319
  if (prefs?.phases?.skip_reassess)
274
320
  return null;
275
- // Default reassess_after_slice to true reassessment after slice completion
276
- // is essential for roadmap integrity. Opt-out via explicit `false`.
277
- const reassessEnabled = prefs?.phases?.reassess_after_slice ?? true;
321
+ // Default reassess_after_slice to false per ADR-003 §4 most reassess
322
+ // units conclude "roadmap is fine" and burn a session for no change.
323
+ // The plan-slice prompt now carries a reassessment preamble so the
324
+ // next slice's planner does JIT roadmap verification at zero extra
325
+ // cost. Opt-in via explicit `reassess_after_slice: true` (e.g.
326
+ // burn-max profile) when you want the dedicated reassess session.
327
+ const reassessEnabled = prefs?.phases?.reassess_after_slice ?? false;
278
328
  if (!reassessEnabled)
279
329
  return null;
280
- const needsReassess = await checkNeedsReassessment(basePath, mid, state);
330
+ const needsReassess = await reassessmentChecker(basePath, mid, state);
281
331
  if (!needsReassess)
282
332
  return null;
283
333
  return {
@@ -293,6 +343,12 @@ export const DISPATCH_RULES = [
293
343
  match: async ({ state, mid, midTitle, basePath, structuredQuestionsAvailable }) => {
294
344
  if (state.phase !== "needs-discussion")
295
345
  return null;
346
+ // H6 fix (#4973): auto-mark depth-verified so the write-gate does not
347
+ // deadlock in non-interactive (auto-mode) runs. See ordering note at
348
+ // "execution-entry phase (no context) → discuss-milestone" above.
349
+ if (isAutoActive()) {
350
+ markDepthVerified(mid, basePath);
351
+ }
296
352
  return {
297
353
  action: "dispatch",
298
354
  unitType: "discuss-milestone",
@@ -310,6 +366,12 @@ export const DISPATCH_RULES = [
310
366
  const hasContext = !!(contextFile && (await loadFile(contextFile)));
311
367
  if (hasContext)
312
368
  return null; // fall through to next rule
369
+ // H6 fix (#4973): auto-mark depth-verified so the write-gate does not
370
+ // deadlock in non-interactive (auto-mode) runs. See ordering note at
371
+ // "execution-entry phase (no context) → discuss-milestone" above.
372
+ if (isAutoActive()) {
373
+ markDepthVerified(mid, basePath);
374
+ }
313
375
  return {
314
376
  action: "dispatch",
315
377
  unitType: "discuss-milestone",
@@ -383,6 +445,12 @@ export const DISPATCH_RULES = [
383
445
  return null;
384
446
  if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
385
447
  return null;
448
+ // #4781 phase 2: trivial-scope milestones skip dedicated slice research.
449
+ // plan-slice absorbs the lightweight discovery a trivial deliverable
450
+ // needs. Null result (DB unavailable / unknown) falls through to today's
451
+ // behavior.
452
+ if (await getMilestonePipelineVariant(mid) === "trivial")
453
+ return null;
386
454
  // Load roadmap to find all slices
387
455
  const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
388
456
  const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
@@ -432,6 +500,9 @@ export const DISPATCH_RULES = [
432
500
  // Phase skip: skip research when preference or profile says so
433
501
  if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
434
502
  return null;
503
+ // #4781 phase 2: trivial-scope milestones skip dedicated slice research.
504
+ if (await getMilestonePipelineVariant(mid) === "trivial")
505
+ return null;
435
506
  if (!state.activeSlice)
436
507
  return missingSliceStop(mid, state.phase);
437
508
  const sid = state.activeSlice.id;
@@ -713,22 +784,29 @@ export const DISPATCH_RULES = [
713
784
  level: "error",
714
785
  };
715
786
  }
716
- // Skip preference: write a minimal pass-through VALIDATION file
717
- if (prefs?.phases?.skip_milestone_validation) {
787
+ // #4781 phase 2: trivial-scope milestones skip the dedicated validate
788
+ // unit — complete-milestone's own verification steps (3/4/5 in the
789
+ // closer prompt) are sufficient proof for contained deliverables.
790
+ const trivialVariant = await getMilestonePipelineVariant(mid) === "trivial";
791
+ // Skip preference OR trivial scope: write a minimal pass-through VALIDATION file.
792
+ if (prefs?.phases?.skip_milestone_validation || trivialVariant) {
718
793
  const mDir = resolveMilestonePath(basePath, mid);
719
794
  if (mDir) {
720
795
  if (!existsSync(mDir))
721
796
  mkdirSync(mDir, { recursive: true });
722
797
  const validationPath = join(mDir, buildMilestoneFileName(mid, "VALIDATION"));
798
+ const skipSource = trivialVariant
799
+ ? "trivial-scope pipeline variant (#4781)"
800
+ : "`skip_milestone_validation` preference";
723
801
  const content = [
724
802
  "---",
725
803
  "verdict: pass",
726
804
  "remediation_round: 0",
727
805
  "---",
728
806
  "",
729
- "# Milestone Validation (skipped by preference)",
807
+ "# Milestone Validation (skipped)",
730
808
  "",
731
- "Milestone validation was skipped via `skip_milestone_validation` preference.",
809
+ `Milestone validation was skipped via ${skipSource}.`,
732
810
  ].join("\n");
733
811
  writeFileSync(validationPath, content, "utf-8");
734
812
  }
@@ -795,7 +873,7 @@ export const DISPATCH_RULES = [
795
873
  // Safety guard (#1703): verify the milestone produced implementation
796
874
  // artifacts (non-.gsd/ files). A milestone with only plan files and
797
875
  // zero implementation code should not be marked complete.
798
- const artifactCheck = hasImplementationArtifacts(basePath);
876
+ const artifactCheck = hasImplementationArtifacts(basePath, mid);
799
877
  if (artifactCheck === "absent") {
800
878
  return {
801
879
  action: "stop",
@@ -13,6 +13,75 @@ import { logWarning } from "./workflow-logger.js";
13
13
  import { resolveUokFlags } from "./uok/flags.js";
14
14
  import { applyModelPolicyFilter } from "./uok/model-policy.js";
15
15
  import { isModelBlocked } from "./blocked-models.js";
16
+ import { getRequiredWorkflowToolsForAutoUnit } from "./workflow-mcp.js";
17
+ /**
18
+ * Thrown when the model-policy gate rejects every candidate model for a unit
19
+ * dispatch (#4959 / #4681 / #4850). The auto-loop catches this specifically
20
+ * to classify the unit as `blocked` rather than counting it as a retryable
21
+ * iteration error — pre-send policy denial is a configuration problem, not a
22
+ * transient runtime failure, so retrying just burns the consecutive-error
23
+ * budget toward a hard stop.
24
+ */
25
+ export class ModelPolicyDispatchBlockedError extends Error {
26
+ unitType;
27
+ unitId;
28
+ reasons;
29
+ constructor(unitType, unitId, reasons) {
30
+ const summary = reasons.length === 0
31
+ ? "no candidate models"
32
+ : reasons
33
+ .slice(0, 4)
34
+ .map((r) => `${r.provider}/${r.modelId} (${r.reason})`)
35
+ .join("; ");
36
+ super(`Model policy denied dispatch for ${unitType}/${unitId} before prompt send. Rejected: ${summary}`);
37
+ this.name = "ModelPolicyDispatchBlockedError";
38
+ this.unitType = unitType;
39
+ this.unitId = unitId;
40
+ this.reasons = reasons;
41
+ }
42
+ }
43
+ // Baseline active-tool set per-`pi` instance, captured the first time
44
+ // `selectAndApplyModel` runs against that instance during an auto session
45
+ // and re-applied before each subsequent dispatch. WeakMap so that test
46
+ // fakes / disposed sessions are garbage-collected normally. See
47
+ // #4959 / #4681 cross-unit poisoning notes at the call site below.
48
+ //
49
+ // LIFECYCLE: the baseline is tied to a single auto session, NOT to the
50
+ // lifetime of the `pi` instance (which can outlive many auto runs and have
51
+ // the user mutate tools between them). `clearToolBaseline` MUST be called
52
+ // at auto start AND auto stop so that a second `/gsd auto` run on the same
53
+ // `pi` does not silently restore a stale snapshot from the prior run and
54
+ // undo any tool changes the user made between sessions.
55
+ const TOOL_BASELINE = new WeakMap();
56
+ /**
57
+ * Drop the captured tool baseline for `pi` so the next `selectAndApplyModel`
58
+ * call re-captures from the live active set. Wired into `startAuto` and
59
+ * `stopAuto` in `auto.ts` to bound the baseline to a single auto session.
60
+ *
61
+ * Safe to call when no baseline is recorded (no-op).
62
+ */
63
+ export function clearToolBaseline(pi) {
64
+ TOOL_BASELINE.delete(pi);
65
+ }
66
+ function restoreToolBaseline(pi) {
67
+ const key = pi;
68
+ const baseline = TOOL_BASELINE.get(key);
69
+ if (baseline === undefined) {
70
+ // First call: capture the canonical pre-dispatch tool set. At auto-mode
71
+ // start the active set has not yet been narrowed for any provider.
72
+ // Guarded against test fakes that omit getActiveTools — record an empty
73
+ // baseline so subsequent calls don't keep re-probing.
74
+ const initial = typeof pi.getActiveTools === "function" ? pi.getActiveTools() : [];
75
+ TOOL_BASELINE.set(key, [...initial]);
76
+ return;
77
+ }
78
+ // Restore baseline before the next unit reads getActiveTools / applies
79
+ // post-selection adjustToolSet. Older fakes that omit setActiveTools are
80
+ // tolerated — the test asserts call order on real fakes.
81
+ if (typeof pi.setActiveTools === "function") {
82
+ pi.setActiveTools([...baseline]);
83
+ }
84
+ }
16
85
  function reapplyThinkingLevel(pi, level) {
17
86
  if (!level)
18
87
  return;
@@ -86,6 +155,29 @@ autoModeStartThinkingLevel) {
86
155
  : resolvePreferredModelConfig(unitType, autoModeStartModel, isAutoMode);
87
156
  let routing = null;
88
157
  let appliedModel = null;
158
+ // ── Restore active-tool baseline before policy evaluation (#4959, #4681, #4850) ──
159
+ // Per-unit narrowing at the bottom of this function (line ~417) calls
160
+ // `pi.setActiveTools(finalToolNames)` and monotonically narrows the active
161
+ // set across units. Without restoration, a previously-dispatched unit on a
162
+ // narrow-API provider (e.g. openai-completions) leaves the active set
163
+ // missing tools that the next unit's selected model fully supports, but
164
+ // `pi.getActiveTools()` snapshot-as-hard-gate (the old behaviour) blocked
165
+ // dispatch with "tool policy denied" anyway.
166
+ //
167
+ // The baseline is captured once per `pi` instance via a WeakMap and
168
+ // re-applied here so each unit starts from a clean slate. Soft adaptation
169
+ // (adjustToolSet at the bottom of this function) still trims for the
170
+ // selected model.
171
+ //
172
+ // Auto-mode only (#4965): `guided-flow.ts:dispatchWorkflow` also calls
173
+ // `selectAndApplyModel` with `isAutoMode=false`. Guided-flow has its own
174
+ // narrow/restore via discuss-tool-scoping (guided-flow.ts:587-622) and no
175
+ // baseline-clear hook of its own, so an unconditional restore here would
176
+ // resurrect an auto-era baseline on guided-flow dispatches — silently
177
+ // overwriting any tool changes made interactively between auto sessions.
178
+ // The baseline is structurally an auto-mode concept; gate it accordingly.
179
+ if (isAutoMode)
180
+ restoreToolBaseline(pi);
89
181
  if (modelConfig) {
90
182
  const availableModels = ctx.modelRegistry.getAvailable();
91
183
  const modelPolicyTraceId = `model:${ctx.sessionManager.getSessionId()}:${Date.now()}`;
@@ -114,7 +206,16 @@ autoModeStartThinkingLevel) {
114
206
  const taskMetadataForPolicy = unitType === "execute-task"
115
207
  ? extractTaskMetadata(unitId, basePath)
116
208
  : undefined;
209
+ let policyDenyReasons = [];
117
210
  if (uokFlags.modelPolicy) {
211
+ // Use the workflow-spec required-tool subset for the unit type rather
212
+ // than the live `pi.getActiveTools()` snapshot (#4959). The active set
213
+ // is poisoned by per-unit narrowing for narrow-API providers — using it
214
+ // as a hard gate promotes soft adaptation (adjustToolSet at line ~417)
215
+ // into a layering violation that throws before dispatch. The smaller
216
+ // workflow-required subset reflects what the unit actually needs; soft
217
+ // adaptation post-selection still trims provider-incompatible tools.
218
+ const requiredTools = getRequiredWorkflowToolsForAutoUnit(unitType);
118
219
  const policy = applyModelPolicyFilter(availableModels, {
119
220
  basePath,
120
221
  traceId: modelPolicyTraceId,
@@ -123,12 +224,15 @@ autoModeStartThinkingLevel) {
123
224
  taskMetadata: taskMetadataForPolicy,
124
225
  currentProvider: ctx.model?.provider,
125
226
  allowCrossProvider: routingConfig.cross_provider !== false,
126
- requiredTools: pi.getActiveTools(),
227
+ requiredTools,
127
228
  });
128
229
  routingEligibleModels = policy.eligible;
129
230
  policyAllowedModelKeys = new Set(policy.eligible.map((m) => `${m.provider.toLowerCase()}/${m.id.toLowerCase()}`));
231
+ policyDenyReasons = policy.decisions
232
+ .filter((d) => !d.allowed)
233
+ .map((d) => ({ provider: d.provider, modelId: d.modelId, reason: d.reason }));
130
234
  if (routingEligibleModels.length === 0) {
131
- throw new Error(`Model policy denied all candidate models for ${unitType}/${unitId}`);
235
+ throw new ModelPolicyDispatchBlockedError(unitType, unitId, policyDenyReasons);
132
236
  }
133
237
  }
134
238
  // Disable routing for flat-rate providers like GitHub Copilot (#3453).
@@ -169,7 +273,11 @@ autoModeStartThinkingLevel) {
169
273
  if (shouldClassify) {
170
274
  let classification = classifyUnitComplexity(unitType, unitId, basePath, budgetPct, taskMetadataForPolicy);
171
275
  const availableModelIds = routingEligibleModels.map(m => `${m.provider}/${m.id}`);
172
- // Escalate tier on retry when escalate_on_failure is enabled (default: true)
276
+ // Escalate tier on retry when escalate_on_failure is enabled (default: true).
277
+ // #4973: Deterministic policy errors are short-circuited at the postUnit
278
+ // level (auto-post-unit.ts writes a placeholder and returns "continue"),
279
+ // so this code path only runs for legitimate model-quality retries where
280
+ // tier escalation is the right response.
173
281
  if (retryContext?.isRetry &&
174
282
  retryContext.previousTier &&
175
283
  routingConfig.escalate_on_failure !== false) {
@@ -179,6 +287,18 @@ autoModeStartThinkingLevel) {
179
287
  // Always notify on tier escalation — model changes should be visible (#3962)
180
288
  ctx.ui.notify(`Tier escalation: ${retryContext.previousTier} → ${escalated} (retry after failure)`, "info");
181
289
  }
290
+ else {
291
+ // #4973: Already at max tier — keep previousTier rather than letting
292
+ // fresh classification silently downgrade the model back to a lower tier.
293
+ // Without this, a light-start unit on retry 3 would revert to the light
294
+ // model after escalating to heavy on retries 1 and 2.
295
+ const tierOrder = { light: 0, standard: 1, heavy: 2 };
296
+ const prevOrder = tierOrder[retryContext.previousTier] ?? 0;
297
+ const freshOrder = tierOrder[classification.tier] ?? 0;
298
+ if (prevOrder > freshOrder) {
299
+ classification = { ...classification, tier: retryContext.previousTier, reason: "retained escalated tier from retry" };
300
+ }
301
+ }
182
302
  }
183
303
  // Load user capability overrides from preferences (D-17: deep-merged with built-in profiles)
184
304
  const capabilityOverrides = loadCapabilityOverrides(prefs ?? {});
@@ -335,7 +455,7 @@ autoModeStartThinkingLevel) {
335
455
  }
336
456
  }
337
457
  if (uokFlags.modelPolicy && policyAllowedModelKeys && !attemptedPolicyEligible) {
338
- throw new Error(`Model policy denied dispatch for ${unitType}/${unitId} before prompt send`);
458
+ throw new ModelPolicyDispatchBlockedError(unitType, unitId, policyDenyReasons);
339
459
  }
340
460
  }
341
461
  else if (autoModeStartModel) {
@@ -47,8 +47,18 @@ import { UokGateRunner } from "./uok/gate-runner.js";
47
47
  import { writeTurnGitTransaction } from "./uok/gitops.js";
48
48
  import { isClosedStatus } from "./status-guards.js";
49
49
  import { detectAbandonMilestone } from "./abandon-detect.js";
50
+ import { isDeterministicPolicyError } from "./auto-tool-tracking.js";
50
51
  /** Maximum verification retry attempts before escalating to blocker placeholder (#2653). */
51
52
  const MAX_VERIFICATION_RETRIES = 3;
53
+ /** Keep failure toasts short while still showing concrete examples. */
54
+ const MAX_NOTIFICATION_DETAILS = 3;
55
+ const NOTIFICATION_BULLET = "•";
56
+ function formatPreExecutionCheckDetail(check) {
57
+ const category = check.category?.trim() || "unknown category";
58
+ const target = check.target?.trim() || "unknown target";
59
+ const message = check.message.split(/\r?\n/, 1)[0]?.trim() || "No details provided";
60
+ return ` ${NOTIFICATION_BULLET} [${category}] ${target}: ${message}`;
61
+ }
52
62
  const COMPLETE_MILESTONE_DB_SETTLE_MS = 1500;
53
63
  const COMPLETE_MILESTONE_DB_SETTLE_POLL_MS = 100;
54
64
  async function waitForMilestoneDbClose(mid) {
@@ -88,7 +98,7 @@ const LIFECYCLE_ONLY_UNITS = new Set([
88
98
  ]);
89
99
  import { describeNextUnit, } from "./auto-dashboard.js";
90
100
  import { existsSync, unlinkSync } from "node:fs";
91
- import { join } from "node:path";
101
+ import { join, relative } from "node:path";
92
102
  import { _resetHasChangesCache } from "./native-git-bridge.js";
93
103
  import { autoCommitCurrentBranch } from "./worktree.js";
94
104
  /**
@@ -200,6 +210,13 @@ export function detectRogueFileWrites(unitType, unitId, basePath) {
200
210
  }
201
211
  return rogues;
202
212
  }
213
+ /**
214
+ * Maximum number of times to retry a unit whose expected artifact is missing
215
+ * after execution. Matches the bounded pattern used by runPostUnitVerification
216
+ * in auto-verification.ts. Exceeding this limit pauses auto-mode instead of
217
+ * looping indefinitely (#2007).
218
+ */
219
+ const MAX_ARTIFACT_VERIFICATION_RETRIES = 3;
203
220
  export const STEP_COMPLETE_FALLBACK_MESSAGE = "Step complete. Run /clear, then /gsd to continue (or /gsd auto to run continuously).";
204
221
  export function buildStepCompleteMessage(nextState) {
205
222
  if (nextState.phase === "complete") {
@@ -782,24 +799,38 @@ export async function postUnitPreVerification(pctx, opts) {
782
799
  // When artifact verification fails for a unit type that has a known expected
783
800
  // artifact, return "retry" so the caller re-dispatches with failure context
784
801
  // instead of blindly re-dispatching the same unit (#1571).
785
- // After MAX_VERIFICATION_RETRIES, escalate to writeBlockerPlaceholder so the
786
- // pipeline can advance instead of looping forever (#2653).
802
+ // Retries are capped at MAX_ARTIFACT_VERIFICATION_RETRIES to prevent
803
+ // unbounded loops (#2007).
804
+ //
805
+ // Pre-checks short-circuit retry for known-unrecoverable failures:
806
+ // - Deterministic policy rejection (#4973): structural write-gate failure
807
+ // that will recur on every retry, so write a blocker placeholder.
808
+ // - DB infra failure (#2517): completion tool returned db_unavailable, so
809
+ // the artifact was never written. Retrying can never succeed.
810
+ // - Tool invocation error (#2883/#3595): malformed JSON args or queued
811
+ // user message — retry will produce the same failure.
787
812
  //
788
- // HOWEVER, if the DB is unavailable (db_unavailable), the artifact was never
789
- // written because the completion tool failed at the infra level. Retrying
790
- // can never succeed and produces a costly re-dispatch loop (#2517).
791
- if (!triggerArtifactVerified && !isDbAvailable()) {
792
- // DB infra failure do NOT retry; the completion tool returned
793
- // db_unavailable so the artifact was never written. Retrying would
794
- // produce an infinite re-dispatch loop (#2517).
813
+ // #4973: Deterministic policy rejections (e.g. context_write_blocked from the
814
+ // write-gate) are checked FIRST before the DB-availability check — because
815
+ // they are structural gates that will fire on every retry regardless of DB or
816
+ // model tier. Short-circuit immediately by writing a blocker placeholder.
817
+ if (!triggerArtifactVerified && s.lastToolInvocationError && isDeterministicPolicyError(s.lastToolInvocationError)) {
818
+ const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
819
+ debugLog("postUnit", { phase: "deterministic-policy-error-placeholder", unitType: s.currentUnit.type, unitId: s.currentUnit.id, error: s.lastToolInvocationError });
820
+ const reason = `Deterministic policy rejection for ${s.currentUnit.type} "${s.currentUnit.id}": ${s.lastToolInvocationError}. Retrying cannot resolve this gate — writing blocker placeholder to advance pipeline.`;
821
+ s.lastToolInvocationError = null;
822
+ s.pendingVerificationRetry = null;
823
+ s.verificationRetryCount.delete(retryKey);
824
+ writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
825
+ ctx.ui.notify(`${s.currentUnit.type} ${s.currentUnit.id} — deterministic policy rejection, wrote blocker placeholder (no retries) (#4973)`, "warning");
826
+ // Fall through to "continue" — do NOT enter the retry or db-unavailable paths.
827
+ }
828
+ else if (!triggerArtifactVerified && !isDbAvailable()) {
795
829
  debugLog("postUnit", { phase: "artifact-verify-skip-db-unavailable", unitType: s.currentUnit.type, unitId: s.currentUnit.id });
796
830
  const dbSkipDiag = diagnoseExpectedArtifact(s.currentUnit.type, s.currentUnit.id, s.basePath);
797
831
  ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — DB unavailable, skipping retry.${dbSkipDiag ? ` Expected: ${dbSkipDiag}` : ""}`, "error");
798
832
  }
799
833
  else if (!triggerArtifactVerified) {
800
- // #2883/#3595: If the artifact is missing because the tool invocation
801
- // failed (malformed JSON) or was skipped (queued user message), retrying
802
- // will produce the same failure. Pause auto-mode instead of looping.
803
834
  if (s.lastToolInvocationError) {
804
835
  const isUserSkip = /queued user message/i.test(s.lastToolInvocationError);
805
836
  const errMsg = isUserSkip
@@ -815,58 +846,29 @@ export async function postUnitPreVerification(pctx, opts) {
815
846
  if (hasExpectedArtifact) {
816
847
  const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
817
848
  const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
818
- s.verificationRetryCount.set(retryKey, attempt);
819
- if (attempt > MAX_VERIFICATION_RETRIES) {
820
- // #4175: For complete-milestone, a blocker placeholder is harmful —
821
- // the stub SUMMARY has no recovery value (milestone is terminal),
822
- // it does not update DB status (so deriveState never advances),
823
- // and it fools stopAuto's presence check into merging a milestone
824
- // that was never legitimately completed. Pause auto-mode with a
825
- // clear single failure signal and preserve the worktree branch.
826
- if (s.currentUnit.type === "complete-milestone") {
827
- debugLog("postUnit", {
828
- phase: "artifact-verify-pause-complete-milestone",
829
- unitType: s.currentUnit.type,
830
- unitId: s.currentUnit.id,
831
- attempt,
832
- maxRetries: MAX_VERIFICATION_RETRIES,
833
- });
834
- s.verificationRetryCount.delete(retryKey);
835
- s.pendingVerificationRetry = null;
836
- ctx.ui.notify(`Milestone ${s.currentUnit.id} verification failed after ${MAX_VERIFICATION_RETRIES} retries — worktree branch preserved. Re-run /gsd auto once blockers are resolved.`, "error");
837
- await pauseAuto(ctx, pi);
838
- return "dispatched";
839
- }
840
- // Retries exhausted — write a blocker placeholder so the pipeline
841
- // can advance past this stuck unit (#2653).
842
- debugLog("postUnit", {
843
- phase: "artifact-verify-escalate",
844
- unitType: s.currentUnit.type,
845
- unitId: s.currentUnit.id,
846
- attempt,
847
- maxRetries: MAX_VERIFICATION_RETRIES,
848
- });
849
- const reason = `Artifact verification failed after ${MAX_VERIFICATION_RETRIES} retries for ${s.currentUnit.type} "${s.currentUnit.id}".`;
850
- writeBlockerPlaceholder(s.currentUnit.type, s.currentUnit.id, s.basePath, reason);
851
- ctx.ui.notify(`${s.currentUnit.type} ${s.currentUnit.id} — verification retries exhausted (${MAX_VERIFICATION_RETRIES}), wrote blocker placeholder to advance pipeline`, "warning");
852
- // Reset retry count and fall through to "continue" so the loop
853
- // re-derives state with the placeholder in place.
849
+ if (attempt > MAX_ARTIFACT_VERIFICATION_RETRIES) {
854
850
  s.verificationRetryCount.delete(retryKey);
855
- s.pendingVerificationRetry = null;
856
- // Do NOT return "retry"fall through to "continue" below.
857
- }
858
- else {
859
- s.pendingVerificationRetry = {
860
- unitId: s.currentUnit.id,
861
- failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).`,
862
- attempt,
863
- };
864
- debugLog("postUnit", { phase: "artifact-verify-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
865
- ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — retrying (attempt ${attempt})`, "warning");
866
- return "retry";
851
+ debugLog("postUnit", { phase: "artifact-verify-exhausted", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
852
+ ctx.ui.notify(`Artifact still missing for ${s.currentUnit.type} ${s.currentUnit.id} after ${MAX_ARTIFACT_VERIFICATION_RETRIES} retries pausing auto-mode`, "error");
853
+ await pauseAuto(ctx, pi);
854
+ return "dispatched";
867
855
  }
856
+ s.verificationRetryCount.set(retryKey, attempt);
857
+ s.pendingVerificationRetry = {
858
+ unitId: s.currentUnit.id,
859
+ failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}/${MAX_ARTIFACT_VERIFICATION_RETRIES}).`,
860
+ attempt,
861
+ };
862
+ debugLog("postUnit", { phase: "artifact-verify-retry", unitType: s.currentUnit.type, unitId: s.currentUnit.id, attempt });
863
+ ctx.ui.notify(`Artifact missing for ${s.currentUnit.type} ${s.currentUnit.id} — retrying (attempt ${attempt}/${MAX_ARTIFACT_VERIFICATION_RETRIES})`, "warning");
864
+ return "retry";
868
865
  }
869
866
  }
867
+ // Verification succeeded — clear the retry counter so a future failure
868
+ // of the same unit gets a full retry budget instead of the stale count.
869
+ if (triggerArtifactVerified) {
870
+ s.verificationRetryCount.delete(`${s.currentUnit.type}:${s.currentUnit.id}`);
871
+ }
870
872
  }
871
873
  else {
872
874
  // Hook unit completed — no additional processing needed
@@ -1064,8 +1066,11 @@ export async function postUnitPostVerification(pctx) {
1064
1066
  }
1065
1067
  // Write evidence JSON to slice artifacts directory
1066
1068
  const slicePath = resolveSlicePath(s.basePath, mid, sid);
1069
+ const evidenceFileName = `${sid}-PRE-EXEC-VERIFY.json`;
1070
+ let evidencePath = join(".gsd", "milestones", mid, "slices", sid, evidenceFileName);
1067
1071
  if (slicePath) {
1068
1072
  writePreExecutionEvidence(result, slicePath, mid, sid);
1073
+ evidencePath = relative(s.basePath, join(slicePath, evidenceFileName)) || evidenceFileName;
1069
1074
  }
1070
1075
  if (uokFlags.gates) {
1071
1076
  const failedChecks = result.checks
@@ -1100,9 +1105,11 @@ export async function postUnitPostVerification(pctx) {
1100
1105
  if (result.status === "fail") {
1101
1106
  const blockingChecks = result.checks.filter(c => !c.passed && c.blocking);
1102
1107
  const blockingCount = blockingChecks.length;
1103
- const details = blockingChecks.slice(0, 3).map(c => ` \u2022 ${c.message}`).join("\n");
1104
- const suffix = blockingChecks.length > 3 ? `\n \u2022 ...and ${blockingChecks.length - 3} more` : "";
1105
- const evidenceNote = `\nSee ${sid}-PRE-EXEC-VERIFY.json for full details.`;
1108
+ const details = blockingChecks.slice(0, MAX_NOTIFICATION_DETAILS).map(formatPreExecutionCheckDetail).join("\n");
1109
+ const suffix = blockingChecks.length > MAX_NOTIFICATION_DETAILS
1110
+ ? `\n ${NOTIFICATION_BULLET} ...and ${blockingChecks.length - MAX_NOTIFICATION_DETAILS} more`
1111
+ : "";
1112
+ const evidenceNote = `\nSee ${evidencePath} for full details.`;
1106
1113
  ctx.ui.notify(`Pre-execution checks failed: ${blockingCount} blocking issue${blockingCount === 1 ? "" : "s"} found\n${details}${suffix}${evidenceNote}`, "error");
1107
1114
  // Persist failure context so the next plan-slice re-dispatch can inject
1108
1115
  // it into the prompt and break the infinite loop (#4551).