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
@@ -1,20 +1,89 @@
1
- import test, { describe, it, beforeEach } from "node:test";
1
+ import { describe, it, beforeEach, afterEach } from "node:test";
2
2
  import assert from "node:assert/strict";
3
3
  import { ghIsAvailable, _resetGhCache } from "../cli.ts";
4
4
 
5
- describe("cli", () => {
5
+ describe("github-sync/cli.ghIsAvailable", () => {
6
+ let originalPath: string | undefined;
7
+
6
8
  beforeEach(() => {
7
9
  _resetGhCache();
10
+ originalPath = process.env.PATH;
11
+ });
12
+
13
+ afterEach(() => {
14
+ if (originalPath !== undefined) {
15
+ process.env.PATH = originalPath;
16
+ } else {
17
+ delete process.env.PATH;
18
+ }
19
+ _resetGhCache();
8
20
  });
9
21
 
10
- it("ghIsAvailable returns boolean", () => {
11
- const result = ghIsAvailable();
12
- assert.equal(typeof result, "boolean");
22
+ it("returns true when gh is on PATH, false otherwise", () => {
23
+ // Force gh to be unavailable by setting PATH to an empty-ish string
24
+ // that contains no gh. This is more robust than asserting a raw
25
+ // `typeof === 'boolean'` (which the previous test did — a tautology,
26
+ // since the function's TypeScript signature already guarantees it).
27
+ process.env.PATH = "/nonexistent-path-for-test";
28
+ assert.equal(
29
+ ghIsAvailable(),
30
+ false,
31
+ "with gh not on PATH, ghIsAvailable must return false",
32
+ );
13
33
  });
14
34
 
15
- it("ghIsAvailable caches result", () => {
35
+ it("caches the availability result — PATH changes after first call are ignored", () => {
36
+ // With the original PATH, gh may or may not be present (depends on
37
+ // the dev machine / CI runner). Either way, capture the first
38
+ // result, then mutate PATH so a fresh subprocess spawn would yield
39
+ // a different result. If the function is genuinely caching, the
40
+ // second call returns the same value despite the PATH change.
41
+
42
+ // Prime the cache with whatever the current PATH says.
16
43
  const first = ghIsAvailable();
44
+
45
+ // Change PATH so the `gh` binary is no longer findable — any
46
+ // subsequent subprocess spawn would yield false.
47
+ process.env.PATH = "/nonexistent-path-for-test";
48
+
17
49
  const second = ghIsAvailable();
18
- assert.equal(first, second);
50
+
51
+ assert.equal(
52
+ second,
53
+ first,
54
+ "cached result must not change when PATH changes after the first call. " +
55
+ "Without caching, mutating PATH away from gh would flip the result.",
56
+ );
57
+ });
58
+
59
+ it("re-evaluates after _resetGhCache — cache is the thing being tested", () => {
60
+ // This locks in that `_resetGhCache` actually clears the cache:
61
+ // with it absent, the second assertion wouldn't observe the PATH change.
62
+ process.env.PATH = "/nonexistent-path-for-test";
63
+ const beforeReset = ghIsAvailable(); // false — gh not on PATH
64
+ assert.equal(beforeReset, false);
65
+
66
+ _resetGhCache();
67
+ // Restore PATH so that if a real gh is available, the cached
68
+ // "false" from before-reset must not persist.
69
+ if (originalPath !== undefined) {
70
+ process.env.PATH = originalPath;
71
+ }
72
+
73
+ // After reset, another call re-probes. We don't know whether the
74
+ // real machine has gh, but we know the re-probe happened because
75
+ // before-reset with an empty PATH was false AND if the machine
76
+ // has gh, the post-reset result would be true (i.e. different).
77
+ const afterReset = ghIsAvailable();
78
+ // Invariant: either the dev machine has gh (afterReset=true, differs
79
+ // from beforeReset) or it doesn't (afterReset=false, unchanged).
80
+ // Both are fine — what matters is that _resetGhCache cleared the
81
+ // memoized value so the re-probe ran. That's observable by the
82
+ // fact that with gh present, afterReset=true even though the
83
+ // cached pre-reset value was false.
84
+ assert.ok(
85
+ typeof afterReset === "boolean",
86
+ "re-probe must return a boolean",
87
+ );
19
88
  });
20
89
  });
@@ -105,9 +105,41 @@ describe("templates", () => {
105
105
  assert.ok(comment.includes("duration:"));
106
106
  });
107
107
 
108
- it("handles empty data gracefully", () => {
108
+ it("handles empty data gracefully — no debug-artifact output", () => {
109
+ // Previous version only asserted `typeof === 'string'`, which the
110
+ // function signature already guarantees (tautology).
111
+ //
112
+ // The real invariant is: empty input must produce a string that
113
+ // is safe to post (or skip) without leaking a debug-stringified
114
+ // object. An empty string IS allowed here — callers are expected
115
+ // to gate on truthiness before posting ("skip if empty"). What
116
+ // must NOT happen is leaking 'undefined', '[object Object]',
117
+ // 'null', or a template-placeholder tell like '{{' / '}}'.
109
118
  const comment = formatSummaryComment({});
110
119
  assert.equal(typeof comment, "string");
120
+ assert.doesNotMatch(
121
+ comment,
122
+ /^undefined$|^\[object Object\]$|^null$/,
123
+ "empty-data comment must not be a debug-style stringified artifact",
124
+ );
125
+ assert.doesNotMatch(
126
+ comment,
127
+ /\{\{\s*\w+\s*\}\}/,
128
+ "empty-data comment must not leak unsubstituted {{placeholders}}",
129
+ );
130
+ });
131
+
132
+ it("empty input produces empty comment — callers gate on truthiness", () => {
133
+ // Sister to the previous test: this locks in the current behaviour
134
+ // that empty input returns empty string, so a regression that
135
+ // unexpectedly starts emitting a non-empty default (which would
136
+ // then post spam comments for every bare-data milestone) fails.
137
+ const comment = formatSummaryComment({});
138
+ assert.equal(
139
+ comment,
140
+ "",
141
+ "empty data must return exactly '' so callers can `if (comment)` gate",
142
+ );
111
143
  });
112
144
  });
113
145
 
@@ -29,6 +29,7 @@ import {
29
29
  } from "./phases.js";
30
30
  import { debugLog } from "../debug-logger.js";
31
31
  import { isInfrastructureError, isTransientCooldownError, getCooldownRetryAfterMs, COOLDOWN_FALLBACK_WAIT_MS, MAX_COOLDOWN_RETRIES } from "./infra-errors.js";
32
+ import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
32
33
  import { resolveEngine } from "../engine-resolver.js";
33
34
  import { logWarning } from "../workflow-logger.js";
34
35
  import { gsdRoot } from "../paths.js";
@@ -703,6 +704,52 @@ export async function autoLoop(
703
704
  // runFinalize leave the journal incomplete, making diagnosis harder.
704
705
  deps.emitJournalEvent({ ts: new Date().toISOString(), flowId, seq: nextSeq(), eventType: "iteration-end", data: { iteration, error: msg } });
705
706
 
707
+ // ── Pre-send model-policy block: not a retryable error (#4959 / #4850) ──
708
+ // The model-policy gate runs before the prompt is sent. When every
709
+ // candidate model is denied (cross-provider disabled + flat-rate
710
+ // baseline + tool-policy denial), retrying the same unit produces the
711
+ // same denial — burning the consecutive-error budget toward a 3-strike
712
+ // hard stop and corrupting auto-mode state. Pause for user attention
713
+ // instead, with the per-model deny reasons surfaced from the typed
714
+ // error.
715
+ if (loopErr instanceof ModelPolicyDispatchBlockedError) {
716
+ debugLog("autoLoop", {
717
+ phase: "model-policy-blocked",
718
+ iteration,
719
+ unitType: loopErr.unitType,
720
+ unitId: loopErr.unitId,
721
+ reasons: loopErr.reasons,
722
+ });
723
+ ctx.ui.notify(
724
+ `Auto-mode paused: model-policy denied dispatch for ${loopErr.unitType}/${loopErr.unitId}. ${msg}`,
725
+ "error",
726
+ );
727
+ deps.emitJournalEvent({
728
+ ts: new Date().toISOString(),
729
+ flowId,
730
+ seq: nextSeq(),
731
+ eventType: "unit-end",
732
+ data: {
733
+ unitType: loopErr.unitType,
734
+ unitId: loopErr.unitId,
735
+ status: "blocked",
736
+ reason: "model-policy-dispatch-blocked",
737
+ reasons: loopErr.reasons,
738
+ },
739
+ });
740
+ // Carry the blocked unit identity into the turn-result observer:
741
+ // the throw originated inside dispatch, so observedUnitType/Id were
742
+ // not assigned by the success path at lines 453/631/647 — but the
743
+ // typed error already names the unit (#4959 / CodeRabbit).
744
+ observedUnitType = loopErr.unitType;
745
+ observedUnitId = loopErr.unitId;
746
+ await deps.pauseAuto(ctx, pi);
747
+ finishTurn("paused", "manual-attention", msg);
748
+ // Do NOT increment consecutiveErrors — the failure is configuration,
749
+ // not a transient runtime fault.
750
+ break;
751
+ }
752
+
706
753
  // ── Infrastructure errors: immediate stop, no retry ──
707
754
  // These are unrecoverable (disk full, OOM, etc.). Retrying just burns
708
755
  // LLM budget on guaranteed failures.
@@ -26,12 +26,12 @@ import {
26
26
  import { detectStuck } from "./detect-stuck.js";
27
27
  import { runUnit } from "./run-unit.js";
28
28
  import { debugLog } from "../debug-logger.js";
29
- import { PROJECT_FILES } from "../detection.js";
29
+ import { PROJECT_FILES, hasProjectFileInAncestor } from "../detection.js";
30
30
  import { MergeConflictError } from "../git-service.js";
31
31
  import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
32
32
  import { pauseAutoForProviderError } from "../provider-error-pause.js";
33
33
  import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
34
- import { join, basename, dirname, parse as parsePath } from "node:path";
34
+ import { join, basename } from "node:path";
35
35
  import { existsSync, cpSync, readdirSync } from "node:fs";
36
36
  import {
37
37
  logWarning,
@@ -201,6 +201,7 @@ async function closeoutAndStop(
201
201
  s.currentUnit.startedAt,
202
202
  deps.buildSnapshotOpts(s.currentUnit.type, s.currentUnit.id),
203
203
  );
204
+ s.currentUnit = null;
204
205
  }
205
206
  await deps.stopAuto(ctx, pi, reason);
206
207
  }
@@ -961,10 +962,14 @@ export async function runDispatch(
961
962
  // ── Sliding-window stuck detection with graduated recovery ──
962
963
  const derivedKey = `${unitType}/${unitId}`;
963
964
 
964
- if (!s.pendingVerificationRetry) {
965
- loopState.recentUnits.push({ key: derivedKey });
966
- if (loopState.recentUnits.length > STUCK_WINDOW_SIZE) loopState.recentUnits.shift();
965
+ // Always record this dispatch in the sliding window so detectStuck() has
966
+ // accurate history. Skipping the push when pendingVerificationRetry is set
967
+ // caused infinite artifact-retry loops to be invisible to stuck detection
968
+ // (#2007). Only the *response* to a stuck signal is suppressed during retries.
969
+ loopState.recentUnits.push({ key: derivedKey });
970
+ if (loopState.recentUnits.length > STUCK_WINDOW_SIZE) loopState.recentUnits.shift();
967
971
 
972
+ if (!s.pendingVerificationRetry) {
968
973
  const stuckSignal = detectStuck(loopState.recentUnits);
969
974
  if (stuckSignal) {
970
975
  debugLog("autoLoop", {
@@ -1378,21 +1383,10 @@ export async function runUnitPhase(
1378
1383
  // Monorepo support (#2347): if no project files in the worktree directory,
1379
1384
  // walk parent directories up to the filesystem root. In monorepos,
1380
1385
  // package.json / Cargo.toml etc. live in a parent directory.
1381
- let hasProjectFileInParent = false;
1382
- if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle) {
1383
- let checkDir = dirname(s.basePath);
1384
- const { root } = parsePath(checkDir);
1385
- while (checkDir !== root) {
1386
- // Stop at git repository boundary — ancestors above the repo root
1387
- // (e.g. ~ or /usr/local) may contain unrelated project files.
1388
- if (deps.existsSync(join(checkDir, ".git"))) break;
1389
- if (PROJECT_FILES.some((f) => deps.existsSync(join(checkDir, f)))) {
1390
- hasProjectFileInParent = true;
1391
- break;
1392
- }
1393
- checkDir = dirname(checkDir);
1394
- }
1395
- }
1386
+ const hasProjectFileInParent =
1387
+ !hasProjectFile && !hasSrcDir && !hasXcodeBundle
1388
+ ? hasProjectFileInAncestor(s.basePath, deps.existsSync)
1389
+ : false;
1396
1390
  if (!hasProjectFile && !hasSrcDir && !hasXcodeBundle && !hasProjectFileInParent) {
1397
1391
  // Greenfield projects won't have project files yet — the first task creates them.
1398
1392
  // Log a warning but allow execution to proceed. The .git check above is sufficient
@@ -2112,6 +2106,8 @@ export async function runFinalize(
2112
2106
 
2113
2107
  // Both pre and post verification completed without timeout — reset counter
2114
2108
  loopState.consecutiveFinalizeTimeouts = 0;
2109
+ s.currentUnit = null;
2110
+ clearCurrentPhase();
2115
2111
 
2116
2112
  // Surface accumulated workflow-logger issues for this unit to the user.
2117
2113
  // Warnings/errors logged during the unit are buffered in the logger and
@@ -75,9 +75,7 @@ export interface PreExecFailure {
75
75
 
76
76
  // ─── Constants ───────────────────────────────────────────────────────────────
77
77
 
78
- export const MAX_UNIT_DISPATCHES = 3;
79
78
  export const STUB_RECOVERY_THRESHOLD = 2;
80
- export const MAX_LIFETIME_DISPATCHES = 6;
81
79
  export const NEW_SESSION_TIMEOUT_MS = 120_000;
82
80
 
83
81
  // ─── AutoSession ─────────────────────────────────────────────────────────────
@@ -59,7 +59,10 @@ import {
59
59
  import { resolveModelWithFallbacksForUnit } from "./preferences-models.js";
60
60
  import { resolveUokFlags } from "./uok/flags.js";
61
61
  import { selectReactiveDispatchBatch } from "./uok/execution-graph.js";
62
+ import { getMilestonePipelineVariant } from "./milestone-scope-classifier.js";
62
63
  import { EXECUTION_ENTRY_PHASES, hasFinalizedMilestoneContext } from "./uok/plan-v2.js";
64
+ import { isAutoActive } from "./auto.js";
65
+ import { markDepthVerified } from "./bootstrap/write-gate.js";
63
66
 
64
67
  // ─── Types ────────────────────────────────────────────────────────────────
65
68
 
@@ -90,6 +93,18 @@ export interface DispatchContext {
90
93
  modelRegistry?: MinimalModelRegistry;
91
94
  }
92
95
 
96
+ type ReassessmentChecker = typeof checkNeedsReassessment;
97
+
98
+ let reassessmentChecker: ReassessmentChecker = checkNeedsReassessment;
99
+
100
+ export function setReassessmentCheckerForTest(checker: ReassessmentChecker): () => void {
101
+ const previous = reassessmentChecker;
102
+ reassessmentChecker = checker;
103
+ return () => {
104
+ reassessmentChecker = previous;
105
+ };
106
+ }
107
+
93
108
  export interface DispatchRule {
94
109
  /** Human-readable name for debugging and test identification */
95
110
  name: string;
@@ -97,6 +112,38 @@ export interface DispatchRule {
97
112
  match: (ctx: DispatchContext) => Promise<DispatchAction | null>;
98
113
  }
99
114
 
115
+ async function readUatGateVerdict(
116
+ basePath: string,
117
+ mid: string,
118
+ sliceId: string,
119
+ ): Promise<{ verdict: string; uatType: UatType | undefined } | null> {
120
+ const uatFile = resolveSliceFile(basePath, mid, sliceId, "UAT");
121
+ const assessmentFile = resolveSliceFile(basePath, mid, sliceId, "ASSESSMENT");
122
+
123
+ const uatContent = uatFile ? await loadFile(uatFile) : null;
124
+ const uatType = uatContent ? extractUatType(uatContent) : undefined;
125
+
126
+ const assessmentContent = assessmentFile ? await loadFile(assessmentFile) : null;
127
+ if (assessmentContent) {
128
+ const assessmentVerdict = extractVerdict(assessmentContent);
129
+ if (assessmentVerdict) {
130
+ return {
131
+ verdict: assessmentVerdict,
132
+ uatType: uatType ?? extractUatType(assessmentContent),
133
+ };
134
+ }
135
+ }
136
+
137
+ if (uatContent) {
138
+ const legacyUatVerdict = extractVerdict(uatContent);
139
+ if (legacyUatVerdict) {
140
+ return { verdict: legacyUatVerdict, uatType };
141
+ }
142
+ }
143
+
144
+ return null;
145
+ }
146
+
100
147
  function missingSliceStop(mid: string, phase: string): DispatchAction {
101
148
  return {
102
149
  action: "stop",
@@ -263,6 +310,16 @@ export const DISPATCH_RULES: DispatchRule[] = [
263
310
  // Align with the plan-v2 gate's lookup semantics: whitespace-only counts
264
311
  // as missing, and an auto worktree may fall back to GSD_PROJECT_ROOT.
265
312
  if (hasFinalizedMilestoneContext(basePath, mid)) return null;
313
+ // H6 fix (#4973): In auto-mode there is no human to answer the
314
+ // depth-verification ask_user_questions, so the write-gate deadlocks.
315
+ // Pre-mark the milestone as depth-verified so gsd_summary_save({artifact_type:"CONTEXT"})
316
+ // is not blocked. Safe ordering: session_switch fires clearDiscussionFlowState()
317
+ // (register-hooks.ts:106) before before_agent_start, which fires before resolveDispatch
318
+ // reaches this match fn — so this call always happens after any session-switch reset.
319
+ // Interactive sessions (isAutoActive()===false) are unaffected.
320
+ if (isAutoActive()) {
321
+ markDepthVerified(mid, basePath);
322
+ }
266
323
  return {
267
324
  action: "dispatch",
268
325
  unitType: "discuss-milestone",
@@ -336,27 +393,28 @@ export const DISPATCH_RULES: DispatchRule[] = [
336
393
  // Only applies when UAT dispatch is enabled
337
394
  if (!prefs?.uat_dispatch) return null;
338
395
 
339
- const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
340
-
341
- // DB-first: get completed slices from DB
342
- let completedSliceIds: string[];
396
+ // DB-first: prefer closed slices from DB; fall back to ROADMAP on disk.
397
+ let closedSliceIds: string[];
343
398
  if (isDbAvailable()) {
344
- completedSliceIds = getMilestoneSlices(mid)
345
- .filter(s => s.status === "complete")
399
+ closedSliceIds = getMilestoneSlices(mid)
400
+ .filter(s => isClosedStatus(s.status))
346
401
  .map(s => s.id);
347
402
  } else {
348
- return null;
403
+ // Filesystem fallback for degraded / unmigrated projects.
404
+ // `slice.done` in the parsed ROADMAP is the disk-level closed signal.
405
+ const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
406
+ const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
407
+ if (!roadmapContent) return null;
408
+ const roadmap = parseRoadmap(roadmapContent);
409
+ closedSliceIds = roadmap.slices.filter(s => s.done).map(s => s.id);
349
410
  }
350
411
 
351
- for (const sliceId of completedSliceIds) {
352
- const resultFile = resolveSliceFile(basePath, mid, sliceId, "UAT");
353
- if (!resultFile) continue;
354
- const content = await loadFile(resultFile);
355
- if (!content) continue;
356
- const verdict = extractVerdict(content);
357
- const uatType = extractUatType(content);
412
+ for (const sliceId of closedSliceIds) {
413
+ const result = await readUatGateVerdict(basePath, mid, sliceId);
414
+ if (!result) continue;
415
+ const { verdict, uatType } = result;
358
416
 
359
- if (verdict && !isAcceptableUatVerdict(verdict, uatType)) {
417
+ if (!isAcceptableUatVerdict(verdict, uatType)) {
360
418
  return {
361
419
  action: "stop" as const,
362
420
  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.`,
@@ -371,11 +429,15 @@ export const DISPATCH_RULES: DispatchRule[] = [
371
429
  name: "reassess-roadmap (post-completion)",
372
430
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
373
431
  if (prefs?.phases?.skip_reassess) return null;
374
- // Default reassess_after_slice to true reassessment after slice completion
375
- // is essential for roadmap integrity. Opt-out via explicit `false`.
376
- const reassessEnabled = prefs?.phases?.reassess_after_slice ?? true;
432
+ // Default reassess_after_slice to false per ADR-003 §4 most reassess
433
+ // units conclude "roadmap is fine" and burn a session for no change.
434
+ // The plan-slice prompt now carries a reassessment preamble so the
435
+ // next slice's planner does JIT roadmap verification at zero extra
436
+ // cost. Opt-in via explicit `reassess_after_slice: true` (e.g.
437
+ // burn-max profile) when you want the dedicated reassess session.
438
+ const reassessEnabled = prefs?.phases?.reassess_after_slice ?? false;
377
439
  if (!reassessEnabled) return null;
378
- const needsReassess = await checkNeedsReassessment(basePath, mid, state);
440
+ const needsReassess = await reassessmentChecker(basePath, mid, state);
379
441
  if (!needsReassess) return null;
380
442
  return {
381
443
  action: "dispatch",
@@ -394,6 +456,12 @@ export const DISPATCH_RULES: DispatchRule[] = [
394
456
  name: "needs-discussion → discuss-milestone",
395
457
  match: async ({ state, mid, midTitle, basePath, structuredQuestionsAvailable }) => {
396
458
  if (state.phase !== "needs-discussion") return null;
459
+ // H6 fix (#4973): auto-mark depth-verified so the write-gate does not
460
+ // deadlock in non-interactive (auto-mode) runs. See ordering note at
461
+ // "execution-entry phase (no context) → discuss-milestone" above.
462
+ if (isAutoActive()) {
463
+ markDepthVerified(mid, basePath);
464
+ }
397
465
  return {
398
466
  action: "dispatch",
399
467
  unitType: "discuss-milestone",
@@ -414,6 +482,12 @@ export const DISPATCH_RULES: DispatchRule[] = [
414
482
  const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
415
483
  const hasContext = !!(contextFile && (await loadFile(contextFile)));
416
484
  if (hasContext) return null; // fall through to next rule
485
+ // H6 fix (#4973): auto-mark depth-verified so the write-gate does not
486
+ // deadlock in non-interactive (auto-mode) runs. See ordering note at
487
+ // "execution-entry phase (no context) → discuss-milestone" above.
488
+ if (isAutoActive()) {
489
+ markDepthVerified(mid, basePath);
490
+ }
417
491
  return {
418
492
  action: "dispatch",
419
493
  unitType: "discuss-milestone",
@@ -482,6 +556,11 @@ export const DISPATCH_RULES: DispatchRule[] = [
482
556
  match: async ({ state, mid, midTitle, basePath, prefs }) => {
483
557
  if (state.phase !== "planning") return null;
484
558
  if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research) return null;
559
+ // #4781 phase 2: trivial-scope milestones skip dedicated slice research.
560
+ // plan-slice absorbs the lightweight discovery a trivial deliverable
561
+ // needs. Null result (DB unavailable / unknown) falls through to today's
562
+ // behavior.
563
+ if (await getMilestonePipelineVariant(mid) === "trivial") return null;
485
564
 
486
565
  // Load roadmap to find all slices
487
566
  const roadmapFile = resolveMilestoneFile(basePath, mid, "ROADMAP");
@@ -538,6 +617,8 @@ export const DISPATCH_RULES: DispatchRule[] = [
538
617
  // Phase skip: skip research when preference or profile says so
539
618
  if (prefs?.phases?.skip_research || prefs?.phases?.skip_slice_research)
540
619
  return null;
620
+ // #4781 phase 2: trivial-scope milestones skip dedicated slice research.
621
+ if (await getMilestonePipelineVariant(mid) === "trivial") return null;
541
622
  if (!state.activeSlice) return missingSliceStop(mid, state.phase);
542
623
  const sid = state.activeSlice!.id;
543
624
  const sTitle = state.activeSlice!.title;
@@ -890,8 +971,13 @@ export const DISPATCH_RULES: DispatchRule[] = [
890
971
  };
891
972
  }
892
973
 
893
- // Skip preference: write a minimal pass-through VALIDATION file
894
- if (prefs?.phases?.skip_milestone_validation) {
974
+ // #4781 phase 2: trivial-scope milestones skip the dedicated validate
975
+ // unit — complete-milestone's own verification steps (3/4/5 in the
976
+ // closer prompt) are sufficient proof for contained deliverables.
977
+ const trivialVariant = await getMilestonePipelineVariant(mid) === "trivial";
978
+
979
+ // Skip preference OR trivial scope: write a minimal pass-through VALIDATION file.
980
+ if (prefs?.phases?.skip_milestone_validation || trivialVariant) {
895
981
  const mDir = resolveMilestonePath(basePath, mid);
896
982
  if (mDir) {
897
983
  if (!existsSync(mDir)) mkdirSync(mDir, { recursive: true });
@@ -899,15 +985,18 @@ export const DISPATCH_RULES: DispatchRule[] = [
899
985
  mDir,
900
986
  buildMilestoneFileName(mid, "VALIDATION"),
901
987
  );
988
+ const skipSource = trivialVariant
989
+ ? "trivial-scope pipeline variant (#4781)"
990
+ : "`skip_milestone_validation` preference";
902
991
  const content = [
903
992
  "---",
904
993
  "verdict: pass",
905
994
  "remediation_round: 0",
906
995
  "---",
907
996
  "",
908
- "# Milestone Validation (skipped by preference)",
997
+ "# Milestone Validation (skipped)",
909
998
  "",
910
- "Milestone validation was skipped via `skip_milestone_validation` preference.",
999
+ `Milestone validation was skipped via ${skipSource}.`,
911
1000
  ].join("\n");
912
1001
  writeFileSync(validationPath, content, "utf-8");
913
1002
  }
@@ -978,7 +1067,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
978
1067
  // Safety guard (#1703): verify the milestone produced implementation
979
1068
  // artifacts (non-.gsd/ files). A milestone with only plan files and
980
1069
  // zero implementation code should not be marked complete.
981
- const artifactCheck = hasImplementationArtifacts(basePath);
1070
+ const artifactCheck = hasImplementationArtifacts(basePath, mid);
982
1071
  if (artifactCheck === "absent") {
983
1072
  return {
984
1073
  action: "stop",