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
@@ -0,0 +1,341 @@
1
+ // GSD-2 + Regression tests for deterministic policy error classification (#4973)
2
+ //
3
+ // When gsd_summary_save returns context_write_blocked (a deterministic write-gate
4
+ // rejection), the retry controller must NOT re-dispatch with escalating model tiers.
5
+ // Instead it must write a blocker placeholder and advance the pipeline immediately.
6
+ //
7
+ // Test 5 — deterministic error short-circuits retry:
8
+ // - isDeterministicPolicyError correctly classifies context_write_blocked errors
9
+ // - recordToolInvocationError captures deterministic errors in lastToolInvocationError
10
+ // - postUnitPreVerification returns "continue" (not "retry"), writes placeholder,
11
+ // leaves pendingVerificationRetry null — zero additional model calls dispatched
12
+ //
13
+ // Test 6 — model-quality failures still use standard retry path:
14
+ // - non-deterministic failures set pendingVerificationRetry and return "retry"
15
+ // - tier escalates on retry 1 (previousTier "standard" → "heavy")
16
+ // - tier is RETAINED at "heavy" on subsequent retries (no downgrade back to fresh
17
+ // classification when already at max tier) — "escalate once" semantics
18
+
19
+ import { describe, test, beforeEach, afterEach } from "node:test";
20
+ import assert from "node:assert/strict";
21
+ import { mkdtempSync, mkdirSync, existsSync, rmSync } from "node:fs";
22
+ import { join } from "node:path";
23
+ import { tmpdir } from "node:os";
24
+ import { randomUUID } from "node:crypto";
25
+
26
+ import {
27
+ isDeterministicPolicyError,
28
+ DETERMINISTIC_POLICY_ERROR_STRINGS,
29
+ } from "../auto-tool-tracking.ts";
30
+ import { AutoSession } from "../auto/session.ts";
31
+ import { _setAutoActiveForTest } from "../auto.ts";
32
+ import { escalateTier } from "../model-router.ts";
33
+
34
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
35
+
36
+ const tmpDirs: string[] = [];
37
+
38
+ function makeTmpBase(): string {
39
+ const base = mkdtempSync(join(tmpdir(), `gsd-test-4973-${randomUUID().slice(0, 8)}-`));
40
+ tmpDirs.push(base);
41
+ mkdirSync(join(base, ".gsd", "milestones", "M001"), { recursive: true });
42
+ return base;
43
+ }
44
+
45
+ function resetAutoState(): void {
46
+ _setAutoActiveForTest(false);
47
+ }
48
+
49
+ // ─── Test 5: Deterministic error short-circuits retry ─────────────────────
50
+
51
+ describe("Test 5 — isDeterministicPolicyError classifier (#4973)", () => {
52
+ // ── Classifier unit tests ──────────────────────────────────────────────
53
+
54
+ test("classifies context_write_blocked fallback text as deterministic", () => {
55
+ // This is the text emitted by workflow-tool-executors.ts when contextGuard.reason
56
+ // is undefined: `Error saving artifact: ${contextGuard.reason ?? "context write blocked"}`
57
+ const errorText = "gsd_summary_save: Error saving artifact: context write blocked";
58
+ assert.strictEqual(
59
+ isDeterministicPolicyError(errorText),
60
+ true,
61
+ "fallback context_write_blocked text must be classified as deterministic",
62
+ );
63
+ });
64
+
65
+ test("classifies write-gate verbose reason as deterministic", () => {
66
+ // This is the text when shouldBlockContextArtifactSaveInSnapshot returns its reason:
67
+ // "HARD BLOCK: Cannot save milestone CONTEXT without depth verification for M001. ..."
68
+ const verboseError = [
69
+ "gsd_summary_save: Error saving artifact:",
70
+ "HARD BLOCK: Cannot save milestone CONTEXT without depth verification for M001.",
71
+ "This is a mechanical gate — you MUST NOT proceed, retry, or rationalize past this block.",
72
+ ].join(" ");
73
+ assert.strictEqual(
74
+ isDeterministicPolicyError(verboseError),
75
+ true,
76
+ "verbose write-gate reason containing 'CONTEXT without depth verification' must be classified as deterministic",
77
+ );
78
+ });
79
+
80
+ test("returns false for malformed-JSON errors (separate classification path)", () => {
81
+ assert.strictEqual(
82
+ isDeterministicPolicyError("Unexpected end of JSON input"),
83
+ false,
84
+ "malformed-JSON errors are not deterministic policy errors",
85
+ );
86
+ assert.strictEqual(
87
+ isDeterministicPolicyError("Validation failed for tool gsd_complete_slice"),
88
+ false,
89
+ );
90
+ });
91
+
92
+ test("returns false for normal business-logic tool errors", () => {
93
+ assert.strictEqual(
94
+ isDeterministicPolicyError("Slice S01 is already complete"),
95
+ false,
96
+ );
97
+ assert.strictEqual(
98
+ isDeterministicPolicyError("Error saving artifact: db_unavailable"),
99
+ false,
100
+ );
101
+ });
102
+
103
+ test("returns false for empty string", () => {
104
+ assert.strictEqual(isDeterministicPolicyError(""), false);
105
+ });
106
+
107
+ test("DETERMINISTIC_POLICY_ERROR_STRINGS list is non-empty and contains context_write_blocked entry", () => {
108
+ assert.ok(
109
+ DETERMINISTIC_POLICY_ERROR_STRINGS.length > 0,
110
+ "must have at least one known deterministic error string",
111
+ );
112
+ const hasContextWriteBlocked = DETERMINISTIC_POLICY_ERROR_STRINGS.some(
113
+ (s) => s.includes("context write blocked") || s.includes("CONTEXT without depth verification"),
114
+ );
115
+ assert.ok(hasContextWriteBlocked, "must include context_write_blocked family entries");
116
+ });
117
+ });
118
+
119
+ describe("Test 5 — recordToolInvocationError captures deterministic errors (#4973)", () => {
120
+ beforeEach(resetAutoState);
121
+ afterEach(resetAutoState);
122
+
123
+ test("lastToolInvocationError is NOT set for deterministic errors on current main (pre-fix baseline)", () => {
124
+ // This test documents the FIXED behavior: deterministic errors ARE captured.
125
+ // On current main (before this fix), recordToolInvocationError would NOT store
126
+ // context_write_blocked because it only checked isToolInvocationError and
127
+ // isQueuedUserMessageSkip. After the fix, it also checks isDeterministicPolicyError.
128
+ //
129
+ // We test the fixed behavior here: the error IS captured.
130
+ _setAutoActiveForTest(true);
131
+
132
+ // Import recordToolInvocationError from auto.ts (it delegates to auto-tool-tracking.ts)
133
+ // We test indirectly via the session state: after calling recordToolInvocationError,
134
+ // lastToolInvocationError should be set for deterministic errors.
135
+ //
136
+ // Since recordToolInvocationError is not exported directly, we verify the fix
137
+ // through the AutoSession field behavior documented in the classifier tests above.
138
+ // The recordToolInvocationError integration is exercised in the postUnitPreVerification
139
+ // integration test below.
140
+ const s = new AutoSession();
141
+ assert.strictEqual(s.lastToolInvocationError, null, "starts null");
142
+
143
+ // Simulate what postUnitPreVerification checks: if isDeterministicPolicyError
144
+ // matches on lastToolInvocationError, the short-circuit fires.
145
+ // The value is set by recordToolInvocationError (tested via auto.ts integration).
146
+ s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
147
+ assert.ok(
148
+ isDeterministicPolicyError(s.lastToolInvocationError),
149
+ "classifier recognises the stored error — short-circuit will fire",
150
+ );
151
+ assert.strictEqual(s.pendingVerificationRetry, null, "pendingVerificationRetry starts null");
152
+ });
153
+
154
+ test("AutoSession.lastToolInvocationError can hold a deterministic policy error string", () => {
155
+ const s = new AutoSession();
156
+ s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
157
+ assert.ok(s.lastToolInvocationError);
158
+ assert.ok(isDeterministicPolicyError(s.lastToolInvocationError));
159
+ });
160
+
161
+ test("AutoSession.lastToolInvocationError is cleared on reset()", () => {
162
+ const s = new AutoSession();
163
+ s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
164
+ s.reset();
165
+ assert.strictEqual(s.lastToolInvocationError, null);
166
+ });
167
+ });
168
+
169
+ describe("Test 5 — postUnitPreVerification short-circuits on deterministic error (#4973)", () => {
170
+ // This integration test calls postUnitPreVerification with a deterministic error
171
+ // in lastToolInvocationError and asserts that:
172
+ // 1. pendingVerificationRetry is NOT set (no retry dispatched)
173
+ // 2. the blocker placeholder is written to disk
174
+ // 3. the function returns "continue" (not "retry" or "dispatched")
175
+
176
+ let base = "";
177
+ beforeEach(() => {
178
+ base = makeTmpBase();
179
+ _setAutoActiveForTest(true);
180
+ });
181
+ afterEach(() => {
182
+ _setAutoActiveForTest(false);
183
+ // Cleanup is handled by tmpDirs at process exit; individual cleanup here
184
+ // is best-effort only so as not to mask assertion failures.
185
+ });
186
+
187
+ test("returns 'continue' and writes placeholder for context_write_blocked — no pendingVerificationRetry set", async () => {
188
+ const { postUnitPreVerification } = await import("../auto-post-unit.ts");
189
+
190
+ const s = new AutoSession();
191
+ s.active = true;
192
+ s.basePath = base;
193
+ s.currentUnit = { type: "discuss-milestone", id: "M001", startedAt: Date.now() };
194
+ // Set the deterministic error that would be recorded by recordToolInvocationError
195
+ s.lastToolInvocationError = "gsd_summary_save: Error saving artifact: context write blocked";
196
+ s.verificationRetryCount.set("discuss-milestone:M001", 2);
197
+
198
+ let pauseCalled = false;
199
+ const ctx = {
200
+ ui: { notify: () => {} },
201
+ } as any;
202
+ const pi = {} as any;
203
+
204
+ const pctx = {
205
+ s,
206
+ ctx,
207
+ pi,
208
+ buildSnapshotOpts: () => ({}) as any,
209
+ lockBase: () => base,
210
+ stopAuto: async () => {},
211
+ pauseAuto: async () => { pauseCalled = true; },
212
+ updateProgressWidget: () => {},
213
+ } as any;
214
+
215
+ const result = await postUnitPreVerification(pctx, { skipSettleDelay: true });
216
+
217
+ // Core assertion: deterministic error short-circuits — returns "continue",
218
+ // no retry, and the placeholder is written so the pipeline can advance.
219
+ assert.strictEqual(result, "continue", "must return 'continue', not 'retry' or 'dispatched'");
220
+ assert.strictEqual(s.pendingVerificationRetry, null, "pendingVerificationRetry must NOT be set");
221
+ assert.strictEqual(s.verificationRetryCount.has("discuss-milestone:M001"), false, "deterministic short-circuit clears stale retry count");
222
+ assert.strictEqual(s.lastToolInvocationError, null, "lastToolInvocationError cleared after handling");
223
+ assert.strictEqual(pauseCalled, false, "pauseAuto must NOT be called for deterministic errors");
224
+
225
+ // The blocker placeholder must exist on disk so the pipeline can advance.
226
+ const placeholderPath = join(base, ".gsd", "milestones", "M001", "M001-CONTEXT.md");
227
+ assert.ok(
228
+ existsSync(placeholderPath),
229
+ `blocker placeholder must be written at ${placeholderPath}`,
230
+ );
231
+ });
232
+ });
233
+
234
+ // ─── Test 6: Model-quality failures use standard retry path ──────────────────
235
+
236
+ describe("Test 6 — non-deterministic failures use standard retry; tier escalates once (#4973)", () => {
237
+ // ── escalateTier behavior (existing, unchanged) ───────────────────────────
238
+
239
+ test("escalateTier: light → standard → heavy → null (max)", () => {
240
+ assert.strictEqual(escalateTier("light"), "standard");
241
+ assert.strictEqual(escalateTier("standard"), "heavy");
242
+ assert.strictEqual(escalateTier("heavy"), null, "heavy is the max tier — no further escalation");
243
+ });
244
+
245
+ test("standard-start retry: escalates to heavy on retry 1, stays at heavy on retry 2 (escalateTier returns null)", () => {
246
+ // Simulate what selectAndApplyModel does across two retries for a standard-start unit.
247
+ // Retry 1: previousTier = "standard", escalateTier → "heavy". Applied tier = "heavy".
248
+ const tier1 = escalateTier("standard");
249
+ assert.strictEqual(tier1, "heavy", "retry 1: standard escalates to heavy");
250
+
251
+ // Retry 2: previousTier = "heavy" (from retry 1 result), escalateTier → null.
252
+ // The "retain escalated tier" fix kicks in: prevOrder(heavy=2) > freshOrder(standard=1),
253
+ // so the tier stays at "heavy" rather than reverting to fresh classification.
254
+ const tier2 = escalateTier("heavy");
255
+ assert.strictEqual(tier2, null, "retry 2: heavy cannot escalate further");
256
+
257
+ // Verify the tier-order comparison used in selectAndApplyModel (#4973 fix):
258
+ const tierOrder: Record<string, number> = { light: 0, standard: 1, heavy: 2 };
259
+ const prevOrder = tierOrder["heavy"] ?? 0; // 2 (from retry 1 result)
260
+ const freshOrder = tierOrder["standard"] ?? 0; // 1 (fresh classifyUnitComplexity for a standard unit)
261
+ assert.ok(
262
+ prevOrder > freshOrder,
263
+ "prevOrder(heavy=2) > freshOrder(standard=1) — the fix retains 'heavy' and prevents revert",
264
+ );
265
+ });
266
+
267
+ test("light-start retry 3: escalated tier is retained, not reverted to 'light'", () => {
268
+ // Without the fix: retry 3 would see previousTier="heavy" (from retry 2),
269
+ // escalateTier returns null, and fresh classification is "light" — the model
270
+ // reverts to a cheap light-tier model. With the fix, we retain "heavy".
271
+
272
+ // Retry 1: light → standard
273
+ assert.strictEqual(escalateTier("light"), "standard");
274
+ // Retry 2: standard → heavy
275
+ assert.strictEqual(escalateTier("standard"), "heavy");
276
+ // Retry 3: heavy → null (can't escalate), fix retains "heavy" instead of reverting to "light"
277
+ assert.strictEqual(escalateTier("heavy"), null);
278
+
279
+ // The fix logic: when escalateTier returns null, compare prevOrder vs freshOrder.
280
+ const tierOrder: Record<string, number> = { light: 0, standard: 1, heavy: 2 };
281
+ const prevOrderRetry3 = tierOrder["heavy"] ?? 0; // 2
282
+ const freshOrderLight = tierOrder["light"] ?? 0; // 0
283
+ assert.ok(
284
+ prevOrderRetry3 > freshOrderLight,
285
+ "on retry 3, prevOrder(heavy=2) > freshOrder(light=0) — 'heavy' must be retained, not reverted",
286
+ );
287
+ });
288
+
289
+ test("non-deterministic error: session sets pendingVerificationRetry (standard retry path)", () => {
290
+ // Simulate what postUnitPreVerification does for a non-deterministic failure:
291
+ // no lastToolInvocationError → falls into the standard retry path → sets pendingVerificationRetry.
292
+ const s = new AutoSession();
293
+ s.currentUnit = { type: "plan-slice", id: "M001:S01", startedAt: Date.now() };
294
+
295
+ // Simulate the retry count increment (as postUnitPreVerification does internally)
296
+ const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
297
+ const attempt = (s.verificationRetryCount.get(retryKey) ?? 0) + 1;
298
+ s.verificationRetryCount.set(retryKey, attempt);
299
+
300
+ // Simulate setting pendingVerificationRetry (what the "else" branch does)
301
+ s.pendingVerificationRetry = {
302
+ unitId: s.currentUnit.id,
303
+ failureContext: `Artifact verification failed: expected artifact for ${s.currentUnit.type} "${s.currentUnit.id}" was not found on disk after unit execution (attempt ${attempt}).`,
304
+ attempt,
305
+ };
306
+
307
+ assert.ok(s.pendingVerificationRetry !== null, "standard retry path sets pendingVerificationRetry");
308
+ assert.strictEqual(s.pendingVerificationRetry.attempt, 1, "attempt is 1");
309
+ assert.ok(
310
+ s.pendingVerificationRetry.failureContext.includes("plan-slice"),
311
+ "failureContext references the unit type",
312
+ );
313
+ });
314
+
315
+ test("isDeterministicPolicyError returns false for non-deterministic verification failure", () => {
316
+ // A plain 'artifact not found' is NOT a deterministic policy error.
317
+ // The standard retry path must still fire for these.
318
+ assert.strictEqual(
319
+ isDeterministicPolicyError(""),
320
+ false,
321
+ "empty error (no tool error) is not deterministic",
322
+ );
323
+ assert.strictEqual(
324
+ isDeterministicPolicyError("Artifact not found on disk"),
325
+ false,
326
+ "plain artifact-missing message is not a deterministic policy error",
327
+ );
328
+ assert.strictEqual(
329
+ isDeterministicPolicyError("existsSync returned false"),
330
+ false,
331
+ );
332
+ });
333
+
334
+ });
335
+
336
+ // Cleanup all temp dirs after the test suite completes
337
+ process.on("exit", () => {
338
+ for (const dir of tmpDirs) {
339
+ try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
340
+ }
341
+ });
@@ -0,0 +1,264 @@
1
+ // GSD-2 + Regression tests for auto-mode discuss-milestone write-gate deadlock (#4973)
2
+ //
3
+ // The depth-verification write-gate in write-gate.ts:415-443 blocks
4
+ // gsd_summary_save({artifact_type:"CONTEXT"}) until markDepthVerified() is
5
+ // called. In interactive mode this happens when the user picks the confirmation
6
+ // option in ask_user_questions. In auto-mode there is no human — the gate
7
+ // deadlocked every discuss-milestone unit, wasting 200K-360K tokens per run.
8
+ //
9
+ // Fix: each dispatch rule that fires a discuss-milestone unit now calls
10
+ // markDepthVerified(mid) when isAutoActive() is true, before returning the
11
+ // dispatch action. These tests verify:
12
+ // Test 1 — CONTEXT artifact save unblocks after markDepthVerified
13
+ // Test 2 — raw write to *-CONTEXT.md unblocks after markDepthVerified
14
+ // Test 3 — session_switch ordering: clearDiscussionFlowState clears the mark
15
+ // Test 4 — interactive sessions (isAutoActive===false) are unaffected
16
+
17
+ import { describe, test, afterEach, beforeEach } from 'node:test';
18
+ import assert from 'node:assert/strict';
19
+ import { mkdtempSync, rmSync, existsSync, unlinkSync } from 'node:fs';
20
+ import { tmpdir } from 'node:os';
21
+ import { join } from 'node:path';
22
+
23
+ import {
24
+ markDepthVerified,
25
+ clearDiscussionFlowState,
26
+ shouldBlockContextArtifactSaveInSnapshot,
27
+ shouldBlockContextWrite,
28
+ loadWriteGateSnapshot,
29
+ isMilestoneDepthVerifiedInSnapshot,
30
+ } from '../bootstrap/write-gate.ts';
31
+
32
+ import { DISPATCH_RULES, type DispatchContext } from '../auto-dispatch.ts';
33
+ import { _setAutoActiveForTest } from '../auto.ts';
34
+
35
+ // Reset all relevant state before and after each test.
36
+ function resetState(): void {
37
+ _setAutoActiveForTest(false);
38
+ clearDiscussionFlowState();
39
+ }
40
+
41
+ describe('auto-discuss-milestone-deadlock-4973', () => {
42
+ beforeEach(resetState);
43
+ afterEach(resetState);
44
+
45
+ // ── Test 1 ──────────────────────────────────────────────────────────────
46
+ // CONTEXT artifact save via gsd_summary_save is blocked before the mark
47
+ // and unblocked after it. This is the exact path that deadlocked in #4973:
48
+ // workflow-tool-executors.ts calls shouldBlockContextArtifactSaveInSnapshot
49
+ // against a snapshot that had no verified milestones.
50
+ test('Test 1: CONTEXT artifact save unblocks after markDepthVerified (auto-mode)', () => {
51
+ _setAutoActiveForTest(true);
52
+
53
+ // Before mark: blocked
54
+ const snapshotBefore = loadWriteGateSnapshot();
55
+ const beforeResult = shouldBlockContextArtifactSaveInSnapshot(
56
+ snapshotBefore,
57
+ 'CONTEXT',
58
+ 'M001',
59
+ null,
60
+ );
61
+ assert.strictEqual(beforeResult.block, true, 'should block before markDepthVerified');
62
+
63
+ // Simulate what the dispatch rule now does in auto-mode
64
+ markDepthVerified('M001');
65
+
66
+ // After mark: unblocked
67
+ const snapshotAfter = loadWriteGateSnapshot();
68
+ const afterResult = shouldBlockContextArtifactSaveInSnapshot(
69
+ snapshotAfter,
70
+ 'CONTEXT',
71
+ 'M001',
72
+ null,
73
+ );
74
+ assert.strictEqual(afterResult.block, false, 'should not block after markDepthVerified');
75
+ });
76
+
77
+ // ── Test 2 ──────────────────────────────────────────────────────────────
78
+ // Raw write tool to a *-CONTEXT.md path is also gated. The register-hooks
79
+ // tool_call handler calls shouldBlockContextWrite for write events.
80
+ test('Test 2: raw write to M001-CONTEXT.md unblocks after markDepthVerified (auto-mode)', () => {
81
+ _setAutoActiveForTest(true);
82
+
83
+ const contextPath = '.gsd/milestones/M001/M001-CONTEXT.md';
84
+
85
+ // Before mark: blocked
86
+ const beforeResult = shouldBlockContextWrite('write', contextPath, 'M001');
87
+ assert.strictEqual(beforeResult.block, true, 'write should be blocked before markDepthVerified');
88
+
89
+ // Simulate dispatch rule auto-mark
90
+ markDepthVerified('M001');
91
+
92
+ // After mark: unblocked
93
+ const afterResult = shouldBlockContextWrite('write', contextPath, 'M001');
94
+ assert.strictEqual(afterResult.block, false, 'write should not be blocked after markDepthVerified');
95
+ });
96
+
97
+ // ── Test 3 ──────────────────────────────────────────────────────────────
98
+ // Documents the session_switch ordering contract.
99
+ //
100
+ // When auto-mode dispatches a new session, the event sequence is:
101
+ // session_switch → clearDiscussionFlowState() (register-hooks.ts:106)
102
+ // before_agent_start fires
103
+ // resolveDispatch is called → discuss-milestone rule match fn runs
104
+ // markDepthVerified(mid) is called HERE (after the clear)
105
+ //
106
+ // This test demonstrates that clearDiscussionFlowState() (the session_switch
107
+ // side effect) clears any previously set mark, and that calling
108
+ // markDepthVerified after the clear correctly re-establishes it — proving
109
+ // the dispatch-site call site is safe regardless of prior session state.
110
+ test('Test 3: session_switch ordering — clearDiscussionFlowState clears mark; dispatch-site call re-establishes it', () => {
111
+ // Simulate a mark from a prior session
112
+ markDepthVerified('M001');
113
+ let snapshot = loadWriteGateSnapshot();
114
+ assert.strictEqual(
115
+ isMilestoneDepthVerifiedInSnapshot(snapshot, 'M001'),
116
+ true,
117
+ 'precondition: mark set from prior session',
118
+ );
119
+
120
+ // session_switch fires clearDiscussionFlowState() — this is exactly what
121
+ // register-hooks.ts:106 does
122
+ clearDiscussionFlowState();
123
+ snapshot = loadWriteGateSnapshot();
124
+ assert.strictEqual(
125
+ isMilestoneDepthVerifiedInSnapshot(snapshot, 'M001'),
126
+ false,
127
+ 'session_switch (clearDiscussionFlowState) must clear the mark',
128
+ );
129
+
130
+ // Now the dispatch rule fires (after session_switch cleared state)
131
+ // and re-establishes the mark for the new session
132
+ _setAutoActiveForTest(true);
133
+ markDepthVerified('M001'); // this is what the dispatch rule does
134
+
135
+ snapshot = loadWriteGateSnapshot();
136
+ assert.strictEqual(
137
+ isMilestoneDepthVerifiedInSnapshot(snapshot, 'M001'),
138
+ true,
139
+ 'dispatch-site markDepthVerified re-establishes the mark after session_switch cleared it',
140
+ );
141
+
142
+ // And the artifact save is now unblocked for this session
143
+ const result = shouldBlockContextArtifactSaveInSnapshot(snapshot, 'CONTEXT', 'M001', null);
144
+ assert.strictEqual(result.block, false, 'CONTEXT save unblocked in the new session');
145
+ });
146
+
147
+ // ── Test 4 ──────────────────────────────────────────────────────────────
148
+ // Interactive sessions (isAutoActive()===false) must NOT be auto-marked.
149
+ // The dispatch rules guard the markDepthVerified call with isAutoActive(),
150
+ // so in a non-auto session the gate still requires the human to confirm.
151
+ // This test passes on both current main AND with the fix applied.
152
+ test('Test 4: interactive sessions unaffected — gate still blocks unverified milestones when auto is off', () => {
153
+ _setAutoActiveForTest(false);
154
+
155
+ // Do NOT call markDepthVerified — simulating that dispatch rule's
156
+ // isAutoActive() guard prevented the auto-mark (as it should for
157
+ // interactive sessions)
158
+
159
+ // CONTEXT artifact save is still blocked
160
+ const snapshotResult = shouldBlockContextArtifactSaveInSnapshot(
161
+ loadWriteGateSnapshot(),
162
+ 'CONTEXT',
163
+ 'M002',
164
+ null,
165
+ );
166
+ assert.strictEqual(
167
+ snapshotResult.block,
168
+ true,
169
+ 'CONTEXT save must still be blocked in interactive mode without depth verification',
170
+ );
171
+
172
+ // Raw write to CONTEXT.md is still blocked
173
+ const writeResult = shouldBlockContextWrite(
174
+ 'write',
175
+ '.gsd/milestones/M002/M002-CONTEXT.md',
176
+ 'M002',
177
+ );
178
+ assert.strictEqual(
179
+ writeResult.block,
180
+ true,
181
+ 'write to CONTEXT.md must still be blocked in interactive mode',
182
+ );
183
+ });
184
+
185
+ // ── Test 5 ──────────────────────────────────────────────────────────────
186
+ // The actual fix lives inside the discuss-milestone dispatch rules at
187
+ // auto-dispatch.ts:280-291, :423-432, :449-458. This test invokes the
188
+ // "needs-discussion → discuss-milestone" rule directly and asserts that
189
+ // (a) the rule auto-marks depth-verified when isAutoActive() is true, and
190
+ // (b) it does NOT mark when isAutoActive() is false.
191
+ //
192
+ // This is the test codex flagged as missing: Tests 1-4 above only exercise
193
+ // the markDepthVerified primitive — they pass on origin/main. This Test 5
194
+ // FAILS on origin/main (the rule does nothing for the gate) and PASSES with
195
+ // the fix (the rule calls markDepthVerified inside isAutoActive()).
196
+ test('Test 5: needs-discussion dispatch rule auto-marks depth-verified in auto-mode', async () => {
197
+ const rule = DISPATCH_RULES.find(r => r.name === 'needs-discussion → discuss-milestone');
198
+ assert.ok(rule, 'dispatch rule must exist');
199
+
200
+ // Use a real temp directory so the snapshot file the rule writes is
201
+ // readable by the same loadWriteGateSnapshot(basePath) the test reads
202
+ // from. The rule passes basePath through to markDepthVerified (since
203
+ // commit 73bb7e085) — without this, the rule writes the snapshot under
204
+ // basePath but the test would read process.cwd() and never see it.
205
+ const tempBase = mkdtempSync(join(tmpdir(), '4973-rule-test-'));
206
+ const snapshotFile = join(tempBase, '.gsd', 'runtime', 'write-gate-state.json');
207
+ try {
208
+ const baseCtx = {
209
+ basePath: tempBase,
210
+ mid: 'M005',
211
+ midTitle: 'Test Milestone',
212
+ state: { phase: 'needs-discussion' },
213
+ prefs: undefined,
214
+ structuredQuestionsAvailable: 'false',
215
+ } as unknown as DispatchContext;
216
+
217
+ // ── Auto-mode case: the rule must call markDepthVerified ──
218
+ _setAutoActiveForTest(true);
219
+ let snap = loadWriteGateSnapshot(tempBase);
220
+ assert.strictEqual(
221
+ isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
222
+ false,
223
+ 'precondition: M005 not yet marked',
224
+ );
225
+
226
+ // The rule's match fn calls markDepthVerified(mid, basePath) BEFORE
227
+ // awaiting buildDiscussMilestonePrompt — so even if the prompt build
228
+ // fails (e.g. because basePath does not contain a valid milestone),
229
+ // the side effect (snapshot write) has already happened.
230
+ try { await rule!.match(baseCtx); } catch { /* prompt build may fail; we only care about the mark */ }
231
+
232
+ snap = loadWriteGateSnapshot(tempBase);
233
+ assert.strictEqual(
234
+ isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
235
+ true,
236
+ 'auto-mode: dispatch rule must call markDepthVerified(mid) — this fails on origin/main without the H6 fix',
237
+ );
238
+
239
+ // ── Interactive case: the rule must NOT call markDepthVerified ──
240
+ // clearDiscussionFlowState() only deletes the snapshot at process.cwd(),
241
+ // so we must explicitly remove the snapshot under our tempBase too.
242
+ clearDiscussionFlowState();
243
+ if (existsSync(snapshotFile)) unlinkSync(snapshotFile);
244
+ _setAutoActiveForTest(false);
245
+ snap = loadWriteGateSnapshot(tempBase);
246
+ assert.strictEqual(
247
+ isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
248
+ false,
249
+ 'precondition: state cleared',
250
+ );
251
+
252
+ try { await rule!.match(baseCtx); } catch { /* prompt build may fail */ }
253
+
254
+ snap = loadWriteGateSnapshot(tempBase);
255
+ assert.strictEqual(
256
+ isMilestoneDepthVerifiedInSnapshot(snap, 'M005'),
257
+ false,
258
+ 'interactive mode: dispatch rule must NOT call markDepthVerified — humans still confirm',
259
+ );
260
+ } finally {
261
+ rmSync(tempBase, { recursive: true, force: true });
262
+ }
263
+ });
264
+ });