gsd-pi 2.77.0-dev.eaa4973bc → 2.78.0-dev.aeeb2ca00

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 (545) hide show
  1. package/README.md +53 -17
  2. package/dist/claude-cli-check.js +46 -10
  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 +72 -16
  13. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +481 -17
  14. package/dist/resources/extensions/github-sync/templates.js +103 -0
  15. package/dist/resources/extensions/google-search/index.js +3 -2
  16. package/dist/resources/extensions/gsd/auto/loop.js +124 -2
  17. package/dist/resources/extensions/gsd/auto/phases.js +57 -39
  18. package/dist/resources/extensions/gsd/auto/session.js +6 -2
  19. package/dist/resources/extensions/gsd/auto-dispatch.js +142 -29
  20. package/dist/resources/extensions/gsd/auto-model-selection.js +124 -4
  21. package/dist/resources/extensions/gsd/auto-post-unit.js +150 -64
  22. package/dist/resources/extensions/gsd/auto-prompts.js +372 -104
  23. package/dist/resources/extensions/gsd/auto-recovery.js +197 -48
  24. package/dist/resources/extensions/gsd/auto-start.js +107 -29
  25. package/dist/resources/extensions/gsd/auto-tool-tracking.js +47 -7
  26. package/dist/resources/extensions/gsd/auto-worktree.js +122 -26
  27. package/dist/resources/extensions/gsd/auto.js +76 -21
  28. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +19 -1
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +209 -0
  30. package/dist/resources/extensions/gsd/bootstrap/provider-error-resume.js +3 -6
  31. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -3
  32. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +127 -9
  33. package/dist/resources/extensions/gsd/component-loader.js +447 -0
  34. package/dist/resources/extensions/gsd/component-types.js +69 -0
  35. package/dist/resources/extensions/gsd/context-store.js +23 -7
  36. package/dist/resources/extensions/gsd/detection.js +49 -1
  37. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -17
  38. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  39. package/dist/resources/extensions/gsd/forensics.js +106 -0
  40. package/dist/resources/extensions/gsd/gate-registry.js +2 -2
  41. package/dist/resources/extensions/gsd/git-constants.js +28 -1
  42. package/dist/resources/extensions/gsd/git-self-heal.js +27 -0
  43. package/dist/resources/extensions/gsd/git-service.js +126 -2
  44. package/dist/resources/extensions/gsd/gsd-db.js +6 -3
  45. package/dist/resources/extensions/gsd/guided-flow.js +39 -13
  46. package/dist/resources/extensions/gsd/memory-extractor.js +7 -1
  47. package/dist/resources/extensions/gsd/milestone-scope-classifier.js +299 -0
  48. package/dist/resources/extensions/gsd/milestone-summary-classifier.js +37 -0
  49. package/dist/resources/extensions/gsd/model-cost-table.js +3 -0
  50. package/dist/resources/extensions/gsd/model-router.js +6 -0
  51. package/dist/resources/extensions/gsd/native-git-bridge.js +34 -4
  52. package/dist/resources/extensions/gsd/preferences-validation.js +23 -0
  53. package/dist/resources/extensions/gsd/prompt-cache-optimizer.js +4 -0
  54. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  55. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  56. package/dist/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  58. package/dist/resources/extensions/gsd/safety/git-checkpoint.js +11 -0
  59. package/dist/resources/extensions/gsd/service-tier.js +5 -2
  60. package/dist/resources/extensions/gsd/session-lock.js +19 -10
  61. package/dist/resources/extensions/gsd/skill-manifest.js +168 -0
  62. package/dist/resources/extensions/gsd/slice-cadence.js +238 -0
  63. package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +278 -8
  64. package/dist/resources/extensions/gsd/state-transition-matrix.js +118 -0
  65. package/dist/resources/extensions/gsd/state.js +69 -58
  66. package/dist/resources/extensions/gsd/sync-lock.js +98 -42
  67. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -2
  68. package/dist/resources/extensions/gsd/unit-context-composer.js +147 -0
  69. package/dist/resources/extensions/gsd/unit-context-manifest.js +370 -0
  70. package/dist/resources/extensions/gsd/uok/dispatch-envelope.js +33 -0
  71. package/dist/resources/extensions/gsd/uok/execution-graph.js +10 -0
  72. package/dist/resources/extensions/gsd/uok/gate-runner.js +53 -5
  73. package/dist/resources/extensions/gsd/uok/gitops.js +2 -1
  74. package/dist/resources/extensions/gsd/uok/loop-adapter.js +37 -10
  75. package/dist/resources/extensions/gsd/uok/parity-report.js +58 -0
  76. package/dist/resources/extensions/gsd/uok/plan-v2.js +10 -4
  77. package/dist/resources/extensions/gsd/uok/writer.js +82 -0
  78. package/dist/resources/extensions/gsd/workflow-mcp.js +6 -0
  79. package/dist/resources/extensions/gsd/worktree-manager.js +85 -8
  80. package/dist/resources/extensions/gsd/worktree-resolver.js +86 -7
  81. package/dist/resources/extensions/gsd/worktree-telemetry.js +198 -0
  82. package/dist/resources/extensions/mcp-client/index.js +3 -1
  83. package/dist/resources/extensions/ollama/index.js +5 -1
  84. package/dist/resources/extensions/remote-questions/manager.js +11 -5
  85. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  86. package/dist/web/standalone/.next/BUILD_ID +1 -1
  87. package/dist/web/standalone/.next/app-path-routes-manifest.json +11 -11
  88. package/dist/web/standalone/.next/build-manifest.json +2 -2
  89. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  90. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/index.html +1 -1
  108. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  110. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
  115. package/dist/web/standalone/.next/server/chunks/1926.js +1 -1
  116. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  117. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  119. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  120. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  121. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  122. package/package.json +2 -3
  123. package/packages/daemon/package.json +2 -2
  124. package/packages/daemon/src/logger.ts +4 -3
  125. package/packages/mcp-server/dist/server.d.ts +24 -0
  126. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  127. package/packages/mcp-server/dist/server.js +88 -87
  128. package/packages/mcp-server/dist/server.js.map +1 -1
  129. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  130. package/packages/mcp-server/dist/workflow-tools.js +15 -6
  131. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  132. package/packages/mcp-server/package.json +2 -2
  133. package/packages/mcp-server/src/mcp-server.test.ts +25 -3
  134. package/packages/mcp-server/src/readers/graph.test.ts +87 -15
  135. package/packages/mcp-server/src/secure-env-collect.test.ts +232 -237
  136. package/packages/mcp-server/src/server.ts +131 -105
  137. package/packages/mcp-server/src/workflow-tools.test.ts +85 -0
  138. package/packages/mcp-server/src/workflow-tools.ts +19 -6
  139. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  140. package/packages/native/package.json +2 -2
  141. package/packages/native/src/__tests__/_test-coverage-guard.test.mjs +98 -0
  142. package/packages/native/src/__tests__/module-compat.test.mjs +59 -27
  143. package/packages/native/src/__tests__/ps.test.mjs +14 -8
  144. package/packages/native/src/__tests__/stream-process.test.mjs +23 -2
  145. package/packages/native/src/__tests__/truncate.test.mjs +17 -2
  146. package/packages/pi-agent-core/package.json +1 -1
  147. package/packages/pi-agent-core/src/agent-loop.test.ts +5 -15
  148. package/packages/pi-agent-core/src/agent.test.ts +96 -102
  149. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  150. package/packages/pi-ai/dist/models/capability-patches.d.ts.map +1 -1
  151. package/packages/pi-ai/dist/models/capability-patches.js +9 -2
  152. package/packages/pi-ai/dist/models/capability-patches.js.map +1 -1
  153. package/packages/pi-ai/dist/models/generated/index.d.ts +34 -0
  154. package/packages/pi-ai/dist/models/generated/index.d.ts.map +1 -1
  155. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts +17 -0
  156. package/packages/pi-ai/dist/models/generated/openai-codex.d.ts.map +1 -1
  157. package/packages/pi-ai/dist/models/generated/openai-codex.js +17 -0
  158. package/packages/pi-ai/dist/models/generated/openai-codex.js.map +1 -1
  159. package/packages/pi-ai/dist/models/generated/openai.d.ts +17 -0
  160. package/packages/pi-ai/dist/models/generated/openai.d.ts.map +1 -1
  161. package/packages/pi-ai/dist/models/generated/openai.js +17 -0
  162. package/packages/pi-ai/dist/models/generated/openai.js.map +1 -1
  163. package/packages/pi-ai/dist/models.generated.test.js +43 -70
  164. package/packages/pi-ai/dist/models.generated.test.js.map +1 -1
  165. package/packages/pi-ai/dist/models.test.js +36 -11
  166. package/packages/pi-ai/dist/models.test.js.map +1 -1
  167. package/packages/pi-ai/package.json +1 -1
  168. package/packages/pi-ai/scripts/generate-models.ts +44 -0
  169. package/packages/pi-ai/src/models/capability-patches.ts +10 -2
  170. package/packages/pi-ai/src/models/generated/openai-codex.ts +17 -0
  171. package/packages/pi-ai/src/models/generated/openai.ts +17 -0
  172. package/packages/pi-ai/src/models.generated.test.ts +46 -73
  173. package/packages/pi-ai/src/models.test.ts +48 -11
  174. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  175. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +96 -32
  176. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  177. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js +75 -12
  178. package/packages/pi-coding-agent/dist/core/agent-session-model-switch.test.js.map +1 -1
  179. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +99 -31
  180. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +5 -0
  182. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/extensions/loader.js +61 -0
  184. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js +30 -4
  186. package/packages/pi-coding-agent/dist/core/lsp/lsp-integration.test.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +17 -0
  188. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js +76 -18
  190. package/packages/pi-coding-agent/dist/core/resource-loader-cache-reset.test.js.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  192. package/packages/pi-coding-agent/dist/core/retry-handler.js +2 -6
  193. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  194. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +5 -1
  195. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  196. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts +18 -0
  197. package/packages/pi-coding-agent/dist/core/retryable-error-regex.d.ts.map +1 -0
  198. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js +18 -0
  199. package/packages/pi-coding-agent/dist/core/retryable-error-regex.js.map +1 -0
  200. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +20 -0
  201. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  202. package/packages/pi-coding-agent/dist/core/system-prompt.js +16 -2
  203. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  204. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  205. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  206. package/packages/pi-coding-agent/dist/index.js +1 -0
  207. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  208. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +36 -5
  209. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  210. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js +20 -13
  211. package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.test.js.map +1 -1
  212. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  213. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +30 -12
  214. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  215. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  216. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +18 -3
  217. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  218. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +125 -0
  219. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  220. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +2 -0
  221. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  223. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
  224. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  225. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +105 -13
  226. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  227. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts +2 -0
  228. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.d.ts.map +1 -0
  229. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js +130 -0
  230. package/packages/pi-coding-agent/dist/tests/system-prompt-skill-filter.test.js.map +1 -0
  231. package/packages/pi-coding-agent/package.json +1 -1
  232. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +113 -37
  233. package/packages/pi-coding-agent/src/core/agent-session-model-switch.test.ts +89 -17
  234. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +112 -43
  235. package/packages/pi-coding-agent/src/core/extensions/loader.ts +58 -0
  236. package/packages/pi-coding-agent/src/core/lsp/lsp-integration.test.ts +35 -4
  237. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +20 -0
  238. package/packages/pi-coding-agent/src/core/resource-loader-cache-reset.test.ts +93 -28
  239. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +5 -1
  240. package/packages/pi-coding-agent/src/core/retry-handler.ts +2 -8
  241. package/packages/pi-coding-agent/src/core/retryable-error-regex.ts +18 -0
  242. package/packages/pi-coding-agent/src/core/system-prompt.ts +35 -1
  243. package/packages/pi-coding-agent/src/index.ts +1 -0
  244. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +49 -3
  245. package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.test.ts +26 -20
  246. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +48 -9
  247. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +146 -1
  248. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +20 -3
  249. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +2 -0
  250. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +119 -13
  251. package/packages/pi-coding-agent/src/tests/system-prompt-skill-filter.test.ts +157 -0
  252. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  253. package/packages/pi-tui/dist/__tests__/autocomplete.test.js +18 -8
  254. package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
  255. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +128 -17
  256. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  257. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js +37 -11
  258. package/packages/pi-tui/dist/__tests__/stdin-buffer.test.js.map +1 -1
  259. package/packages/pi-tui/dist/__tests__/tui.test.js +18 -30
  260. package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
  261. package/packages/pi-tui/dist/components/__tests__/input.test.js +10 -3
  262. package/packages/pi-tui/dist/components/__tests__/input.test.js.map +1 -1
  263. package/packages/pi-tui/dist/components/__tests__/loader.test.js +53 -9
  264. package/packages/pi-tui/dist/components/__tests__/loader.test.js.map +1 -1
  265. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js +6 -2
  266. package/packages/pi-tui/dist/components/__tests__/markdown-maxlines.test.js.map +1 -1
  267. package/packages/pi-tui/dist/components/editor.d.ts +14 -0
  268. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  269. package/packages/pi-tui/dist/components/editor.js +19 -0
  270. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  271. package/packages/pi-tui/dist/components/image.test.js +6 -5
  272. package/packages/pi-tui/dist/components/image.test.js.map +1 -1
  273. package/packages/pi-tui/dist/editor-component.d.ts +2 -0
  274. package/packages/pi-tui/dist/editor-component.d.ts.map +1 -1
  275. package/packages/pi-tui/dist/editor-component.js.map +1 -1
  276. package/packages/pi-tui/package.json +1 -1
  277. package/packages/pi-tui/src/__tests__/autocomplete.test.ts +24 -8
  278. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +140 -17
  279. package/packages/pi-tui/src/__tests__/stdin-buffer.test.ts +42 -11
  280. package/packages/pi-tui/src/__tests__/tui.test.ts +18 -37
  281. package/packages/pi-tui/src/components/__tests__/input.test.ts +19 -3
  282. package/packages/pi-tui/src/components/__tests__/loader.test.ts +112 -35
  283. package/packages/pi-tui/src/components/__tests__/markdown-maxlines.test.ts +9 -2
  284. package/packages/pi-tui/src/components/editor.ts +22 -0
  285. package/packages/pi-tui/src/components/image.test.ts +10 -5
  286. package/packages/pi-tui/src/editor-component.ts +3 -0
  287. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  288. package/packages/rpc-client/dist/rpc-client.test.js +101 -51
  289. package/packages/rpc-client/dist/rpc-client.test.js.map +1 -1
  290. package/packages/rpc-client/package.json +1 -1
  291. package/packages/rpc-client/src/rpc-client.test.ts +109 -52
  292. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  293. package/pkg/package.json +1 -1
  294. package/scripts/install.js +15 -1
  295. package/src/resources/extensions/browser-tools/capture.ts +12 -0
  296. package/src/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +8 -59
  297. package/src/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +36 -24
  298. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +69 -71
  299. package/src/resources/extensions/browser-tools/tools/forms.ts +5 -1
  300. package/src/resources/extensions/browser-tools/tools/intent.ts +5 -1
  301. package/src/resources/extensions/claude-code-cli/readiness.ts +75 -16
  302. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +518 -19
  303. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +919 -75
  304. package/src/resources/extensions/github-sync/templates.ts +151 -0
  305. package/src/resources/extensions/github-sync/tests/cli.test.ts +76 -7
  306. package/src/resources/extensions/github-sync/tests/templates.test.ts +92 -1
  307. package/src/resources/extensions/google-search/index.ts +3 -2
  308. package/src/resources/extensions/gsd/auto/loop.ts +142 -2
  309. package/src/resources/extensions/gsd/auto/phases.ts +62 -38
  310. package/src/resources/extensions/gsd/auto/session.ts +7 -2
  311. package/src/resources/extensions/gsd/auto-dispatch.ts +156 -29
  312. package/src/resources/extensions/gsd/auto-model-selection.ts +131 -4
  313. package/src/resources/extensions/gsd/auto-post-unit.ts +163 -73
  314. package/src/resources/extensions/gsd/auto-prompts.ts +385 -93
  315. package/src/resources/extensions/gsd/auto-recovery.ts +230 -51
  316. package/src/resources/extensions/gsd/auto-start.ts +127 -9
  317. package/src/resources/extensions/gsd/auto-tool-tracking.ts +51 -7
  318. package/src/resources/extensions/gsd/auto-worktree.ts +130 -26
  319. package/src/resources/extensions/gsd/auto.ts +90 -23
  320. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +20 -1
  321. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +221 -0
  322. package/src/resources/extensions/gsd/bootstrap/provider-error-resume.ts +3 -7
  323. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -3
  324. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +158 -9
  325. package/src/resources/extensions/gsd/component-loader.ts +598 -0
  326. package/src/resources/extensions/gsd/component-types.ts +362 -0
  327. package/src/resources/extensions/gsd/context-store.ts +25 -8
  328. package/src/resources/extensions/gsd/detection.ts +58 -1
  329. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -20
  330. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  331. package/src/resources/extensions/gsd/forensics.ts +118 -1
  332. package/src/resources/extensions/gsd/gate-registry.ts +2 -2
  333. package/src/resources/extensions/gsd/git-constants.ts +30 -1
  334. package/src/resources/extensions/gsd/git-self-heal.ts +31 -0
  335. package/src/resources/extensions/gsd/git-service.ts +149 -2
  336. package/src/resources/extensions/gsd/gsd-db.ts +6 -3
  337. package/src/resources/extensions/gsd/guided-flow.ts +57 -14
  338. package/src/resources/extensions/gsd/journal.ts +11 -1
  339. package/src/resources/extensions/gsd/memory-extractor.ts +11 -3
  340. package/src/resources/extensions/gsd/milestone-scope-classifier.ts +366 -0
  341. package/src/resources/extensions/gsd/milestone-summary-classifier.ts +42 -0
  342. package/src/resources/extensions/gsd/model-cost-table.ts +3 -0
  343. package/src/resources/extensions/gsd/model-router.ts +6 -0
  344. package/src/resources/extensions/gsd/native-git-bridge.ts +34 -4
  345. package/src/resources/extensions/gsd/preferences-validation.ts +21 -0
  346. package/src/resources/extensions/gsd/prompt-cache-optimizer.ts +4 -0
  347. package/src/resources/extensions/gsd/prompts/complete-milestone.md +6 -2
  348. package/src/resources/extensions/gsd/prompts/discuss-headless.md +23 -4
  349. package/src/resources/extensions/gsd/prompts/doctor-heal.md +5 -4
  350. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -2
  351. package/src/resources/extensions/gsd/safety/git-checkpoint.ts +15 -0
  352. package/src/resources/extensions/gsd/service-tier.ts +5 -2
  353. package/src/resources/extensions/gsd/session-lock.ts +20 -10
  354. package/src/resources/extensions/gsd/skill-manifest.ts +175 -0
  355. package/src/resources/extensions/gsd/slice-cadence.ts +299 -0
  356. package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +309 -8
  357. package/src/resources/extensions/gsd/state-transition-matrix.ts +152 -0
  358. package/src/resources/extensions/gsd/state.ts +76 -66
  359. package/src/resources/extensions/gsd/sync-lock.ts +97 -39
  360. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +270 -0
  361. package/src/resources/extensions/gsd/tests/artifacts-table-preserved-on-cache-invalidate.test.ts +2 -1
  362. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +341 -0
  363. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +264 -0
  364. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +133 -292
  365. package/src/resources/extensions/gsd/tests/auto-model-selection-tool-poisoning.test.ts +742 -0
  366. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +78 -0
  367. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +61 -0
  368. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +93 -0
  369. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +4 -1
  370. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +8 -194
  371. package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +3 -2
  372. package/src/resources/extensions/gsd/tests/auto-start-cold-db-bootstrap.test.ts +2 -2
  373. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +15 -58
  374. package/src/resources/extensions/gsd/tests/auto-start-worktree-db-path.test.ts +2 -2
  375. package/src/resources/extensions/gsd/tests/auto-thinking-restore.test.ts +3 -2
  376. package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +3 -2
  377. package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +2 -1
  378. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +17 -21
  379. package/src/resources/extensions/gsd/tests/canonical-milestone-root.test.ts +108 -0
  380. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +263 -0
  381. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +25 -0
  382. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +192 -0
  383. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +2 -1
  384. package/src/resources/extensions/gsd/tests/complete-task.test.ts +16 -8
  385. package/src/resources/extensions/gsd/tests/component-loader.test.ts +589 -0
  386. package/src/resources/extensions/gsd/tests/component-types.test.ts +127 -0
  387. package/src/resources/extensions/gsd/tests/context-store.test.ts +79 -0
  388. package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +2 -1
  389. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +50 -1
  390. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +159 -0
  391. package/src/resources/extensions/gsd/tests/db-access-guardrails.test.ts +1 -0
  392. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +3 -3
  393. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +40 -0
  394. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +91 -3
  395. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -4
  396. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +2 -1
  397. package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +2 -1
  398. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +5 -0
  399. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +25 -0
  400. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +14 -0
  401. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +3 -2
  402. package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +4 -3
  403. package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +4 -3
  404. package/src/resources/extensions/gsd/tests/execution-entry-missing-context-4671.test.ts +173 -0
  405. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +139 -129
  406. package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +8 -104
  407. package/src/resources/extensions/gsd/tests/gate-state-canonicalization.test.ts +102 -0
  408. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +1 -1
  409. package/src/resources/extensions/gsd/tests/google-search-stub.test.ts +14 -4
  410. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +117 -0
  411. package/src/resources/extensions/gsd/tests/hook-key-parsing.test.ts +4 -55
  412. package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +7 -56
  413. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +20 -0
  414. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +18 -2
  415. package/src/resources/extensions/gsd/tests/integration/queue-completed-milestone-perf.test.ts +10 -4
  416. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +144 -7
  417. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +4 -0
  418. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +2 -16
  419. package/src/resources/extensions/gsd/tests/interactive-routing-bypass.test.ts +9 -3
  420. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +6 -9
  421. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +64 -0
  422. package/src/resources/extensions/gsd/tests/knowledge.test.ts +93 -1
  423. package/src/resources/extensions/gsd/tests/mcp-client-security.test.ts +8 -37
  424. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +5 -15
  425. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +227 -55
  426. package/src/resources/extensions/gsd/tests/milestone-scope-classifier.test.ts +187 -0
  427. package/src/resources/extensions/gsd/tests/milestone-summary-classifier.test.ts +30 -0
  428. package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
  429. package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
  430. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +6 -48
  431. package/src/resources/extensions/gsd/tests/notification-widget.test.ts +6 -3
  432. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +59 -2
  433. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +273 -130
  434. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +301 -0
  435. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +32 -1
  436. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +2 -1
  437. package/src/resources/extensions/gsd/tests/prompt-cache-optimizer.test.ts +12 -0
  438. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +15 -4
  439. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +23 -24
  440. package/src/resources/extensions/gsd/tests/queue-auto-guard.test.ts +32 -0
  441. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +3 -2
  442. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +4 -5
  443. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +75 -2
  444. package/src/resources/extensions/gsd/tests/reassess-default-optin.test.ts +132 -0
  445. package/src/resources/extensions/gsd/tests/recovery-attempts-reset.test.ts +8 -40
  446. package/src/resources/extensions/gsd/tests/regex-hardening.test.ts +136 -256
  447. package/src/resources/extensions/gsd/tests/research-milestone-composer.test.ts +114 -0
  448. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +6 -3
  449. package/src/resources/extensions/gsd/tests/run-uat-composer.test.ts +148 -0
  450. package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
  451. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +29 -0
  452. package/src/resources/extensions/gsd/tests/sidecar-queue.test.ts +3 -2
  453. package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +55 -95
  454. package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +158 -0
  455. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +120 -1
  456. package/src/resources/extensions/gsd/tests/skill-manifest.test.ts +112 -0
  457. package/src/resources/extensions/gsd/tests/slice-cadence.test.ts +242 -0
  458. package/src/resources/extensions/gsd/tests/slice-context-injection.test.ts +3 -2
  459. package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +164 -1
  460. package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +2 -1
  461. package/src/resources/extensions/gsd/tests/stale-dirlistcache-4648.test.ts +112 -0
  462. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +29 -5
  463. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +44 -0
  464. package/src/resources/extensions/gsd/tests/stop-auto-race-null-unit.test.ts +3 -3
  465. package/src/resources/extensions/gsd/tests/structured-data-formatter.test.ts +11 -92
  466. package/src/resources/extensions/gsd/tests/subagent-model-dispatch.test.ts +7 -6
  467. package/src/resources/extensions/gsd/tests/survivor-branch-complete.test.ts +102 -101
  468. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +31 -0
  469. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +4 -3
  470. package/src/resources/extensions/gsd/tests/test-helpers.test.ts +98 -0
  471. package/src/resources/extensions/gsd/tests/test-helpers.ts +153 -0
  472. package/src/resources/extensions/gsd/tests/token-profile.test.ts +8 -1
  473. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +61 -1
  474. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -1
  475. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +355 -0
  476. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +258 -0
  477. package/src/resources/extensions/gsd/tests/uok-contracts.test.ts +51 -0
  478. package/src/resources/extensions/gsd/tests/uok-execution-graph.test.ts +16 -0
  479. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +75 -0
  480. package/src/resources/extensions/gsd/tests/uok-gitops-wiring.test.ts +49 -26
  481. package/src/resources/extensions/gsd/tests/uok-loop-adapter-writer.test.ts +65 -0
  482. package/src/resources/extensions/gsd/tests/uok-parity-report.test.ts +42 -0
  483. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +19 -2
  484. package/src/resources/extensions/gsd/tests/uok-writer.test.ts +75 -0
  485. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +12 -0
  486. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +144 -80
  487. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -54
  488. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +342 -277
  489. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +37 -29
  490. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +226 -266
  491. package/src/resources/extensions/gsd/tests/worktree-health-monorepo.test.ts +103 -67
  492. package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +92 -90
  493. package/src/resources/extensions/gsd/tests/worktree-submodule-safety.test.ts +238 -59
  494. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +113 -161
  495. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +210 -0
  496. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +262 -0
  497. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +186 -0
  498. package/src/resources/extensions/gsd/tests/write-gate.test.ts +7 -5
  499. package/src/resources/extensions/gsd/tests/zombie-gsd-state.test.ts +80 -96
  500. package/src/resources/extensions/gsd/tools/validate-milestone.ts +8 -2
  501. package/src/resources/extensions/gsd/types.ts +3 -3
  502. package/src/resources/extensions/gsd/unit-context-composer.ts +218 -0
  503. package/src/resources/extensions/gsd/unit-context-manifest.ts +574 -0
  504. package/src/resources/extensions/gsd/uok/contracts.ts +65 -0
  505. package/src/resources/extensions/gsd/uok/dispatch-envelope.ts +56 -0
  506. package/src/resources/extensions/gsd/uok/execution-graph.ts +22 -0
  507. package/src/resources/extensions/gsd/uok/gate-runner.ts +65 -5
  508. package/src/resources/extensions/gsd/uok/gitops.ts +6 -1
  509. package/src/resources/extensions/gsd/uok/loop-adapter.ts +45 -10
  510. package/src/resources/extensions/gsd/uok/parity-report.ts +84 -0
  511. package/src/resources/extensions/gsd/uok/plan-v2.ts +13 -5
  512. package/src/resources/extensions/gsd/uok/writer.ts +113 -0
  513. package/src/resources/extensions/gsd/workflow-mcp.ts +6 -0
  514. package/src/resources/extensions/gsd/worktree-manager.ts +108 -7
  515. package/src/resources/extensions/gsd/worktree-resolver.ts +96 -9
  516. package/src/resources/extensions/gsd/worktree-telemetry.ts +322 -0
  517. package/src/resources/extensions/mcp-client/index.ts +3 -1
  518. package/src/resources/extensions/mcp-client/tests/server-name-spaces.test.ts +70 -36
  519. package/src/resources/extensions/ollama/index.ts +5 -1
  520. package/src/resources/extensions/ollama/ollama-auth-mode.test.ts +123 -15
  521. package/src/resources/extensions/ollama/ollama-status-indicator.test.ts +206 -19
  522. package/src/resources/extensions/remote-questions/manager.ts +36 -4
  523. package/src/resources/extensions/remote-questions/tests/command-polling.test.ts +200 -190
  524. package/src/resources/extensions/shared/tests/interview-preview.test.ts +11 -3
  525. package/src/resources/extensions/voice/tests/linux-ready.test.ts +129 -113
  526. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts +0 -2
  527. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.d.ts.map +0 -1
  528. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js +0 -289
  529. package/packages/pi-ai/dist/utils/oauth/oauth-providers.test.js.map +0 -1
  530. package/packages/pi-ai/src/utils/oauth/oauth-providers.test.ts +0 -363
  531. package/src/resources/extensions/gsd/tests/auto-start-model-capture.test.ts +0 -143
  532. package/src/resources/extensions/gsd/tests/complete-milestone-false-merge.test.ts +0 -157
  533. package/src/resources/extensions/gsd/tests/dashboard-model-label-ordering.test.ts +0 -107
  534. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +0 -48
  535. package/src/resources/extensions/gsd/tests/forensics-context-persist.test.ts +0 -159
  536. package/src/resources/extensions/gsd/tests/forensics-db-completion.test.ts +0 -96
  537. package/src/resources/extensions/gsd/tests/forensics-dedup.test.ts +0 -79
  538. package/src/resources/extensions/gsd/tests/forensics-hook-key-parse.test.ts +0 -74
  539. package/src/resources/extensions/gsd/tests/forensics-journal.test.ts +0 -162
  540. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +0 -38
  541. package/src/resources/extensions/gsd/tests/gsd-no-project-error.test.ts +0 -73
  542. package/src/resources/extensions/gsd/tests/idle-watchdog-stall-override.test.ts +0 -125
  543. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +0 -42
  544. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_buildManifest.js +0 -0
  545. /package/dist/web/standalone/.next/static/{5wbu35_C2_MQ3Jj1lEVDx → cAJH99yNS1UPbeSEiNRrV}/_ssgManifest.js +0 -0
@@ -0,0 +1,270 @@
1
+ /**
2
+ * artifact-retry-cap.test.ts — Regression tests for #2007.
3
+ *
4
+ * Three interacting bugs caused unbounded artifact-verification retry loops
5
+ * that burned unlimited budget (202 dispatches observed in production):
6
+ *
7
+ * Bug 1: postUnitPreVerification in auto-post-unit.ts had no MAX check before
8
+ * returning "retry" when an expected artifact was missing. The attempt
9
+ * counter incremented forever.
10
+ *
11
+ * Bug 2: runDispatch in auto/phases.ts only pushed to loopState.recentUnits
12
+ * when pendingVerificationRetry was falsy, so the sliding-window stuck
13
+ * detector never saw artifact-retry dispatches and could not fire.
14
+ *
15
+ * Bug 3: MAX_UNIT_DISPATCHES and MAX_LIFETIME_DISPATCHES were exported from
16
+ * auto/session.ts but never compared against unitDispatchCount anywhere
17
+ * in the codebase — dead constants that provided false confidence.
18
+ */
19
+
20
+ import test from "node:test";
21
+ import assert from "node:assert/strict";
22
+ import { readFileSync } from "node:fs";
23
+ import { join } from "node:path";
24
+ import ts from "typescript";
25
+
26
+ const dir = join(import.meta.dirname, "..");
27
+
28
+ const postUnitSrc = readFileSync(join(dir, "auto-post-unit.ts"), "utf-8");
29
+ const phasesSrc = readFileSync(join(dir, "auto", "phases.ts"), "utf-8");
30
+ const sessionSrc = readFileSync(join(dir, "auto", "session.ts"), "utf-8");
31
+ const autoSrc = readFileSync(join(dir, "auto.ts"), "utf-8");
32
+
33
+ function extractFunctionBody(source: string, functionName: string): string {
34
+ const sourceFile = ts.createSourceFile(
35
+ "auto-post-unit.ts",
36
+ source,
37
+ ts.ScriptTarget.Latest,
38
+ true,
39
+ ts.ScriptKind.TS,
40
+ );
41
+ let body: ts.Block | undefined;
42
+
43
+ function visit(node: ts.Node): void {
44
+ if (
45
+ ts.isFunctionDeclaration(node) &&
46
+ node.name?.text === functionName
47
+ ) {
48
+ body = node.body;
49
+ return;
50
+ }
51
+ ts.forEachChild(node, visit);
52
+ }
53
+
54
+ visit(sourceFile);
55
+
56
+ assert.ok(body, `${functionName} must have a function body`);
57
+ return source.slice(body.getStart(sourceFile) + 1, body.end - 1);
58
+ }
59
+
60
+ function extractStuckDetectionSection(source: string): string {
61
+ const stuckSectionIdx = source.indexOf("Sliding-window stuck detection");
62
+ assert.ok(stuckSectionIdx !== -1, "stuck-detection section must exist");
63
+
64
+ const preDispatchIdx = source.indexOf("// Pre-dispatch hooks", stuckSectionIdx);
65
+ assert.ok(preDispatchIdx !== -1, "pre-dispatch hooks section must follow stuck detection");
66
+
67
+ return source.slice(stuckSectionIdx, preDispatchIdx);
68
+ }
69
+
70
+ const postUnitPreVerificationBody = extractFunctionBody(
71
+ postUnitSrc,
72
+ "postUnitPreVerification",
73
+ );
74
+
75
+ // ─── Bug 1: artifact retry must be bounded ───────────────────────────────────
76
+
77
+ test("#2007 bug 1: MAX_ARTIFACT_VERIFICATION_RETRIES constant is defined", () => {
78
+ assert.ok(
79
+ postUnitSrc.includes("MAX_ARTIFACT_VERIFICATION_RETRIES"),
80
+ "auto-post-unit.ts must define MAX_ARTIFACT_VERIFICATION_RETRIES",
81
+ );
82
+ });
83
+
84
+ test("#2007 bug 1: attempt is compared against MAX_ARTIFACT_VERIFICATION_RETRIES before returning retry", () => {
85
+ const retryBlockIdx = postUnitPreVerificationBody.indexOf("const retryKey =");
86
+ assert.ok(retryBlockIdx !== -1, "retry block must exist in postUnitPreVerification");
87
+
88
+ const retryIdx = postUnitPreVerificationBody.indexOf("return \"retry\"", retryBlockIdx);
89
+ assert.ok(retryIdx !== -1, "return \"retry\" must exist in postUnitPreVerification");
90
+
91
+ const maxIdx = postUnitPreVerificationBody.indexOf(
92
+ "if (attempt > MAX_ARTIFACT_VERIFICATION_RETRIES)",
93
+ retryBlockIdx,
94
+ );
95
+ assert.ok(maxIdx !== -1, "retry block must compare attempt against MAX");
96
+ assert.ok(
97
+ maxIdx < retryIdx,
98
+ "MAX_ARTIFACT_VERIFICATION_RETRIES check must appear before return \"retry\"",
99
+ );
100
+ });
101
+
102
+ test("#2007 bug 1: exhaustion path pauses auto-mode instead of silently continuing", () => {
103
+ const exhaustionIdx = postUnitPreVerificationBody.indexOf("phase: \"artifact-verify-exhausted\"");
104
+ assert.ok(exhaustionIdx !== -1, "exhaustion branch must log artifact-verify-exhausted");
105
+
106
+ const pauseIdx = postUnitPreVerificationBody.indexOf("await pauseAuto", exhaustionIdx);
107
+ const dispatchedIdx = postUnitPreVerificationBody.indexOf("return \"dispatched\"", exhaustionIdx);
108
+
109
+ assert.ok(
110
+ pauseIdx !== -1 && dispatchedIdx !== -1 && pauseIdx < dispatchedIdx,
111
+ "exhaustion branch must pause auto-mode before returning \"dispatched\"",
112
+ );
113
+ });
114
+
115
+ test("#2007 bug 1: failure context message includes attempt count and max", () => {
116
+ const failureContextIdx = postUnitPreVerificationBody.indexOf("failureContext:");
117
+ assert.ok(failureContextIdx !== -1, "failureContext assignment must exist");
118
+ assert.ok(
119
+ postUnitPreVerificationBody.includes(
120
+ "attempt ${attempt}/${MAX_ARTIFACT_VERIFICATION_RETRIES}",
121
+ failureContextIdx,
122
+ ),
123
+ "failure context should include attempt progress (attempt/current-max)",
124
+ );
125
+ });
126
+
127
+ // ─── Bug 2: stuck detection must see all dispatches ──────────────────────────
128
+
129
+ test("#2007 bug 2: recentUnits.push is unconditional — not gated on pendingVerificationRetry", () => {
130
+ const stuckSection = extractStuckDetectionSection(phasesSrc);
131
+ const pushIdx = stuckSection.indexOf("recentUnits.push");
132
+ assert.ok(pushIdx !== -1, "recentUnits.push must exist in phases.ts");
133
+
134
+ const pendingCheckIdx = stuckSection.indexOf("!s.pendingVerificationRetry");
135
+ assert.ok(pendingCheckIdx !== -1, "pendingVerificationRetry guard must exist");
136
+
137
+ // The push must come BEFORE the pendingVerificationRetry guard
138
+ assert.ok(
139
+ pushIdx < pendingCheckIdx,
140
+ "recentUnits.push must be unconditional — it must appear before the !pendingVerificationRetry check",
141
+ );
142
+ });
143
+
144
+ test("#2007 bug 2: detectStuck is still inside the pendingVerificationRetry guard", () => {
145
+ // detectStuck should only run when NOT in a retry — to avoid false positives
146
+ // during legitimate retries, but now the window is always populated.
147
+ const stuckSection = extractStuckDetectionSection(phasesSrc);
148
+ const pendingCheckIdx = stuckSection.indexOf("!s.pendingVerificationRetry");
149
+ assert.ok(
150
+ pendingCheckIdx !== -1,
151
+ "pendingVerificationRetry guard must exist in the stuck-detection section",
152
+ );
153
+ const detectStuckIdx = stuckSection.indexOf("detectStuck(", pendingCheckIdx);
154
+
155
+ assert.ok(
156
+ detectStuckIdx !== -1 && detectStuckIdx > pendingCheckIdx,
157
+ "detectStuck call must remain inside the !pendingVerificationRetry block",
158
+ );
159
+ });
160
+
161
+ // ─── Bug 3: dead dispatch-limit constants removed ────────────────────────────
162
+
163
+ test("#2007 bug 3: MAX_UNIT_DISPATCHES is removed from session.ts", () => {
164
+ assert.ok(
165
+ !sessionSrc.includes("MAX_UNIT_DISPATCHES"),
166
+ "MAX_UNIT_DISPATCHES was never enforced and must be removed to prevent false confidence",
167
+ );
168
+ });
169
+
170
+ test("#2007 bug 3: MAX_LIFETIME_DISPATCHES is removed from session.ts", () => {
171
+ assert.ok(
172
+ !sessionSrc.includes("MAX_LIFETIME_DISPATCHES"),
173
+ "MAX_LIFETIME_DISPATCHES was never enforced and must be removed to prevent false confidence",
174
+ );
175
+ });
176
+
177
+ test("#2007 bug 3: dead constants are not re-exported from auto.ts", () => {
178
+ assert.ok(
179
+ !autoSrc.includes("MAX_UNIT_DISPATCHES"),
180
+ "MAX_UNIT_DISPATCHES must not be re-exported from auto.ts",
181
+ );
182
+ assert.ok(
183
+ !autoSrc.includes("MAX_LIFETIME_DISPATCHES"),
184
+ "MAX_LIFETIME_DISPATCHES must not be re-exported from auto.ts",
185
+ );
186
+ });
187
+
188
+ // ─── No stray dead constants left behind by the fix ──────────────────────────
189
+
190
+ test("#2007 fix does not introduce a new dead constant (STATE_REBUILD_MIN_INTERVAL_MS)", () => {
191
+ // Bug 3 was about removing dead constants. A draft of this PR added
192
+ // STATE_REBUILD_MIN_INTERVAL_MS without referencing it — the same anti-pattern
193
+ // it set out to remove. Lock that down so it cannot regress.
194
+ assert.ok(
195
+ !postUnitSrc.includes("STATE_REBUILD_MIN_INTERVAL_MS"),
196
+ "STATE_REBUILD_MIN_INTERVAL_MS was added but never referenced — must be removed",
197
+ );
198
+ });
199
+
200
+ // ─── Behavioral: retry counter is cleared on success ─────────────────────────
201
+
202
+ test("#2007 verificationRetryCount is cleared on artifact verification success", () => {
203
+ // Find the success-clear we just added: when triggerArtifactVerified is
204
+ // true, the retry counter for the current unit must be deleted so a future
205
+ // failure of the same unit type+id gets the full retry budget instead of
206
+ // a stale leftover count.
207
+ //
208
+ // We assert on the structural shape because a behavioral test would need
209
+ // to mock 30+ imports of postUnitPreVerification. The AutoSession-level
210
+ // test below covers the Map contract.
211
+ const successClearIdx = postUnitPreVerificationBody.indexOf(
212
+ "if (triggerArtifactVerified)",
213
+ );
214
+ assert.ok(
215
+ successClearIdx !== -1,
216
+ "Must guard the retry-count clear behind a triggerArtifactVerified check",
217
+ );
218
+ const deleteIdx = postUnitPreVerificationBody.indexOf(
219
+ "verificationRetryCount.delete",
220
+ successClearIdx,
221
+ );
222
+ assert.ok(
223
+ deleteIdx !== -1,
224
+ "verificationRetryCount.delete must be called on the verification-success path",
225
+ );
226
+ });
227
+
228
+ // ─── AutoSession.verificationRetryCount Map behavior ─────────────────────────
229
+
230
+ import { AutoSession } from "../auto/session.ts";
231
+
232
+ test("AutoSession.verificationRetryCount tracks attempts per retry key", () => {
233
+ const s = new AutoSession();
234
+ const key = "execute-task:M01/S01/T01";
235
+
236
+ assert.equal(s.verificationRetryCount.get(key), undefined);
237
+
238
+ s.verificationRetryCount.set(key, 1);
239
+ assert.equal(s.verificationRetryCount.get(key), 1);
240
+
241
+ s.verificationRetryCount.set(key, 2);
242
+ assert.equal(s.verificationRetryCount.get(key), 2);
243
+
244
+ // Simulate the success-clear path
245
+ s.verificationRetryCount.delete(key);
246
+ assert.equal(s.verificationRetryCount.get(key), undefined);
247
+ });
248
+
249
+ test("AutoSession.verificationRetryCount is cleared on session reset", () => {
250
+ const s = new AutoSession();
251
+ s.verificationRetryCount.set("execute-task:M01/S01/T01", 2);
252
+ s.verificationRetryCount.set("plan-slice:M01/S02", 1);
253
+
254
+ s.reset();
255
+
256
+ assert.equal(s.verificationRetryCount.size, 0);
257
+ });
258
+
259
+ test("AutoSession.verificationRetryCount independence across retry keys", () => {
260
+ // Critical: if retries for unit A fail twice and unit A then succeeds, the
261
+ // counter for A should be cleared but B's counter must remain untouched.
262
+ const s = new AutoSession();
263
+ s.verificationRetryCount.set("execute-task:M01/S01/T01", 2);
264
+ s.verificationRetryCount.set("execute-task:M01/S01/T02", 1);
265
+
266
+ s.verificationRetryCount.delete("execute-task:M01/S01/T01");
267
+
268
+ assert.equal(s.verificationRetryCount.get("execute-task:M01/S01/T01"), undefined);
269
+ assert.equal(s.verificationRetryCount.get("execute-task:M01/S01/T02"), 1);
270
+ });
@@ -22,6 +22,7 @@ import { readFileSync } from "node:fs";
22
22
  import { resolve } from "node:path";
23
23
 
24
24
  import { invalidateAllCaches } from "../cache.ts";
25
+ import { extractSourceRegion } from "./test-helpers.ts";
25
26
  import {
26
27
  openDatabase,
27
28
  closeDatabase,
@@ -160,7 +161,7 @@ describe("cache.ts must not re-import clearArtifacts into invalidateAllCaches",
160
161
  test("invalidateAllCaches does not call clearArtifacts", () => {
161
162
  const fnIdx = src.indexOf("function invalidateAllCaches");
162
163
  assert.ok(fnIdx !== -1);
163
- const body = src.slice(fnIdx, fnIdx + 1000);
164
+ const body = extractSourceRegion(src, "function invalidateAllCaches", { fromIdx: fnIdx });
164
165
  assert.ok(
165
166
  !/\bclearArtifacts\s*\(/.test(body),
166
167
  "invalidateAllCaches must not call clearArtifacts() — it wipes the write-through store",
@@ -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
+ });