gsd-pi 2.82.0-dev.2841a1e44 → 2.82.0-dev.98ea09b1e

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 (277) hide show
  1. package/README.md +2 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +7 -0
  4. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  5. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  6. package/dist/resources/extensions/gsd/auto/loop.js +5 -5
  7. package/dist/resources/extensions/gsd/auto/orchestrator.js +11 -0
  8. package/dist/resources/extensions/gsd/auto/phases.js +8 -1
  9. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
  11. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  12. package/dist/resources/extensions/gsd/auto-post-unit.js +70 -9
  13. package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
  14. package/dist/resources/extensions/gsd/auto-start.js +85 -12
  15. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  16. package/dist/resources/extensions/gsd/auto.js +30 -3
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -1
  18. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
  19. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +5 -2
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +13 -1
  21. package/dist/resources/extensions/gsd/commands/handlers/core.js +17 -1
  22. package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
  23. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  24. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  25. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  26. package/dist/resources/extensions/gsd/doctor.js +2 -28
  27. package/dist/resources/extensions/gsd/export-html.js +27 -425
  28. package/dist/resources/extensions/gsd/git-service.js +39 -1
  29. package/dist/resources/extensions/gsd/gsd-db.js +1 -0
  30. package/dist/resources/extensions/gsd/guided-flow.js +13 -6
  31. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  32. package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
  33. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  34. package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
  35. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  36. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  37. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  38. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  39. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  40. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  41. package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
  42. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  43. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  44. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  45. package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
  46. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  47. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  48. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  49. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
  50. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
  51. package/dist/resources/extensions/gsd/status-guards.js +4 -0
  52. package/dist/resources/extensions/gsd/templates/plan.md +8 -5
  53. package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
  54. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  55. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  56. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  57. package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
  58. package/dist/resources/extensions/gsd/unit-context-manifest.js +7 -8
  59. package/dist/resources/extensions/gsd/validation.js +23 -1
  60. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  61. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  62. package/dist/resources/extensions/gsd/worktree-lifecycle.js +33 -8
  63. package/dist/resources/extensions/shared/html-shell.js +388 -0
  64. package/dist/resources/extensions/visual-brief/page-contract.js +2 -0
  65. package/dist/resources/extensions/visual-brief/prompts.js +29 -0
  66. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  67. package/dist/web/standalone/.next/BUILD_ID +1 -1
  68. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  69. package/dist/web/standalone/.next/build-manifest.json +3 -3
  70. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  71. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  81. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  91. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  92. package/dist/web/standalone/.next/server/app/index.html +1 -1
  93. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  94. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  96. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  98. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  99. package/dist/web/standalone/.next/server/app/page.js +2 -2
  100. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  101. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  103. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  104. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  107. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  108. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  109. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  110. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  111. package/dist/web/standalone/.next/static/chunks/{webpack-6a95bc41e0f7ec89.js → webpack-9a4db269f9ed63ad.js} +1 -1
  112. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  113. package/package.json +2 -2
  114. package/packages/mcp-server/src/workflow-tools.test.ts +1 -1
  115. package/packages/native/tsconfig.json +2 -1
  116. package/packages/native/tsconfig.tsbuildinfo +1 -1
  117. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  118. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  119. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  120. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  121. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  122. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  123. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  124. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  125. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  126. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  127. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  128. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  129. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  130. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  131. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  132. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  133. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  134. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  135. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  136. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  137. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  138. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  139. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  140. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  141. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  142. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  143. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  144. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  145. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  146. package/src/resources/GSD-WORKFLOW.md +7 -0
  147. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  148. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  149. package/src/resources/extensions/gsd/auto/contracts.ts +14 -6
  150. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  151. package/src/resources/extensions/gsd/auto/loop.ts +8 -5
  152. package/src/resources/extensions/gsd/auto/orchestrator.ts +11 -0
  153. package/src/resources/extensions/gsd/auto/phases.ts +7 -1
  154. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  155. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
  156. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  157. package/src/resources/extensions/gsd/auto-post-unit.ts +77 -7
  158. package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
  159. package/src/resources/extensions/gsd/auto-start.ts +92 -9
  160. package/src/resources/extensions/gsd/auto-worktree.ts +119 -1
  161. package/src/resources/extensions/gsd/auto.ts +32 -3
  162. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +6 -1
  163. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
  164. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +3 -1
  165. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +16 -1
  166. package/src/resources/extensions/gsd/commands/handlers/core.ts +17 -1
  167. package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
  168. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  169. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  170. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  171. package/src/resources/extensions/gsd/doctor.ts +2 -27
  172. package/src/resources/extensions/gsd/export-html.ts +27 -427
  173. package/src/resources/extensions/gsd/git-service.ts +45 -1
  174. package/src/resources/extensions/gsd/gsd-db.ts +3 -0
  175. package/src/resources/extensions/gsd/guided-flow.ts +14 -7
  176. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  177. package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
  178. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  179. package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
  180. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  181. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  182. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  183. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  184. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  185. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  186. package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
  187. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  188. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  189. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  190. package/src/resources/extensions/gsd/prompts/queue.md +4 -4
  191. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  192. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  193. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  194. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
  195. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
  196. package/src/resources/extensions/gsd/status-guards.ts +5 -0
  197. package/src/resources/extensions/gsd/templates/plan.md +8 -5
  198. package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
  199. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  200. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +54 -0
  201. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +80 -1
  202. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -6
  203. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  204. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  205. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
  206. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  207. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  208. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
  209. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  210. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
  211. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
  212. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
  213. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
  214. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  215. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  216. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
  217. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  218. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +6 -6
  219. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  220. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  221. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  222. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  223. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +103 -1
  224. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  225. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  226. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
  227. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  228. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  229. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  230. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  231. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  232. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
  233. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  234. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  235. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  236. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  237. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  238. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  239. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  240. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +31 -1
  241. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
  242. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  243. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  244. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +65 -7
  245. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  246. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +1 -1
  247. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  248. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  249. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  250. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  251. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +38 -0
  252. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  253. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  254. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  255. package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
  256. package/src/resources/extensions/gsd/types.ts +1 -1
  257. package/src/resources/extensions/gsd/unit-context-manifest.ts +12 -9
  258. package/src/resources/extensions/gsd/validation.ts +23 -1
  259. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  260. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  261. package/src/resources/extensions/gsd/worktree-lifecycle.ts +41 -8
  262. package/src/resources/extensions/shared/html-shell.ts +412 -0
  263. package/src/resources/extensions/visual-brief/page-contract.ts +2 -0
  264. package/src/resources/extensions/visual-brief/prompts.ts +37 -1
  265. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +40 -0
  266. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  267. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  268. package/dist/web/standalone/.next/static/css/0262768ec1b89d34.css +0 -1
  269. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  270. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  271. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  272. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  273. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  274. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  275. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  276. /package/dist/web/standalone/.next/static/{Qgr2B_MRhPxC0z8fwv4vT → euQ0CLP_v8V4e76Tu3odJ}/_buildManifest.js +0 -0
  277. /package/dist/web/standalone/.next/static/{Qgr2B_MRhPxC0z8fwv4vT → euQ0CLP_v8V4e76Tu3odJ}/_ssgManifest.js +0 -0
@@ -448,6 +448,13 @@ What differed from the plan and why (or "None").
448
448
 
449
449
  The one-liner must be substantive: "JWT auth with refresh rotation using jose" not "Authentication implemented."
450
450
 
451
+ When `key_files` or `key_decisions` are empty, render them as empty YAML lists:
452
+
453
+ ```yaml
454
+ key_files: []
455
+ key_decisions: []
456
+ ```
457
+
451
458
  **Slice summary:** Written when all tasks in a slice complete. Compresses all task summaries. Includes `drill_down_paths` to each task summary. During slice completion, review task summaries for `key_decisions` and ensure any significant ones are captured in `.gsd/DECISIONS.md`.
452
459
 
453
460
  **Milestone summary:** Updated each time a slice completes. Compresses all slice summaries. This is what gets injected into later slice planning instead of loading many individual summaries.
@@ -134,10 +134,11 @@ export function mapUsage(sdkUsage: NonNullableUsage, totalCostUsd: number): Usag
134
134
  output: sdkUsage.output_tokens,
135
135
  cacheRead: sdkUsage.cache_read_input_tokens,
136
136
  cacheWrite: sdkUsage.cache_creation_input_tokens,
137
+ // Claude Agent SDK result usage is cumulative across its internal loop;
138
+ // repeated cache reads do not represent additional live context.
137
139
  totalTokens:
138
140
  sdkUsage.input_tokens +
139
141
  sdkUsage.output_tokens +
140
- sdkUsage.cache_read_input_tokens +
141
142
  sdkUsage.cache_creation_input_tokens,
142
143
  cost: {
143
144
  input: 0,
@@ -1,7 +1,24 @@
1
1
  import { describe, test } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mapContentBlock, parseMcpToolName, PartialMessageBuilder } from "../partial-builder.ts";
4
- import type { BetaContentBlock, BetaRawMessageStreamEvent } from "../sdk-types.ts";
3
+ import { mapContentBlock, mapUsage, parseMcpToolName, PartialMessageBuilder } from "../partial-builder.ts";
4
+ import type { BetaContentBlock, BetaRawMessageStreamEvent, NonNullableUsage } from "../sdk-types.ts";
5
+
6
+ describe("mapUsage", () => {
7
+ test("excludes cumulative cache reads from context-sized totalTokens (#5243)", () => {
8
+ const usage: NonNullableUsage = {
9
+ input_tokens: 150_000,
10
+ output_tokens: 2_000,
11
+ cache_read_input_tokens: 900_000,
12
+ cache_creation_input_tokens: 3_000,
13
+ };
14
+
15
+ const mapped = mapUsage(usage, 1.23);
16
+
17
+ assert.equal(mapped.cacheRead, 900_000);
18
+ assert.equal(mapped.totalTokens, 155_000);
19
+ assert.equal(mapped.cost.total, 1.23);
20
+ });
21
+ });
5
22
 
6
23
  describe("PartialMessageBuilder — malformed tool arguments (#2574)", () => {
7
24
  /**
@@ -47,12 +47,20 @@ export interface DispatchAdapter {
47
47
  sessionProvider?: string;
48
48
  /** Model registry for executor-model lookups inside the budget engine. */
49
49
  modelRegistry?: MinimalModelRegistry;
50
- }): Promise<{
51
- unitType: string;
52
- unitId: string;
53
- reason: string;
54
- preconditions: string[];
55
- } | null>;
50
+ }): Promise<
51
+ | {
52
+ kind: "blocked";
53
+ reason: string;
54
+ action: "pause" | "stop";
55
+ }
56
+ | {
57
+ unitType: string;
58
+ unitId: string;
59
+ reason: string;
60
+ preconditions: string[];
61
+ }
62
+ | null
63
+ >;
56
64
  }
57
65
 
58
66
  export interface RecoveryAdapter {
@@ -7,9 +7,14 @@
7
7
  */
8
8
 
9
9
  /**
10
- * Error codes indicating infrastructure failures that cannot be recovered by
11
- * retrying. Each retry re-dispatches the unit at full LLM cost, so we bail
12
- * immediately rather than burning budget on guaranteed failures.
10
+ * Error codes indicating infrastructure-level failures from the OS,
11
+ * filesystem, or network. This set includes permanent resource failures
12
+ * (ENOSPC, ENOMEM, EROFS), transient resource exhaustion (EAGAIN, ENOBUFS),
13
+ * and network/offline errors (ECONNREFUSED, ENOTFOUND, ENETUNREACH).
14
+ *
15
+ * Transient git failures are retried separately through
16
+ * TRANSIENT_GIT_RETRY_CODES in native-git-bridge.ts before escalating to the
17
+ * auto-loop.
13
18
  */
14
19
  export const INFRA_ERROR_CODES: ReadonlySet<string> = new Set([
15
20
  "ENOSPC", // disk full
@@ -19,6 +24,7 @@ export const INFRA_ERROR_CODES: ReadonlySet<string> = new Set([
19
24
  "EMFILE", // too many open files (process)
20
25
  "ENFILE", // too many open files (system)
21
26
  "EAGAIN", // resource temporarily unavailable (resource exhaustion)
27
+ "ENOBUFS", // no buffer space available (transient pipe exhaustion)
22
28
  "ECONNREFUSED", // connection refused (offline / local server down)
23
29
  "ENOTFOUND", // DNS lookup failed (offline / no network)
24
30
  "ENETUNREACH", // network unreachable (offline / no route)
@@ -78,7 +78,10 @@ import { createWorkflowTurnReporter } from "./workflow-turn-reporter.js";
78
78
  import { validateWorkflowSessionLock } from "./workflow-session-lock.js";
79
79
  import { dequeueSidecarItem } from "./workflow-sidecar-queue.js";
80
80
  import { maintainWorkerHeartbeat } from "./workflow-worker-heartbeat.js";
81
- import { measureMemoryPressure } from "./workflow-memory-pressure.js";
81
+ import {
82
+ measureMemoryPressure,
83
+ shouldCheckMemoryPressure,
84
+ } from "./workflow-memory-pressure.js";
82
85
  import { buildSidecarIterationData } from "./workflow-sidecar-iteration.js";
83
86
  import {
84
87
  createExecutionGraphUnitDispatchDeps,
@@ -203,9 +206,9 @@ function logCustomVerifyRetrySaveFailure(err: unknown): void {
203
206
  }
204
207
 
205
208
  // ── Memory pressure monitoring (#3331) ──────────────────────────────────
206
- // Check heap usage every N iterations and trigger graceful shutdown before
207
- // the OS OOM killer sends SIGKILL. The threshold is 90% of the V8 heap
208
- // limit (--max-old-space-size or default ~1.5-4GB depending on platform).
209
+ // Check heap usage on session startup, then every N iterations, and trigger
210
+ // graceful shutdown before the OS OOM killer sends SIGKILL. The threshold is
211
+ // 90% of the V8 heap limit (--max-old-space-size or default ~1.5-4GB depending on platform).
209
212
  const MEMORY_CHECK_INTERVAL = 5; // check every 5 iterations
210
213
  const MAX_CUSTOM_ENGINE_VERIFY_RETRIES = 3;
211
214
 
@@ -372,7 +375,7 @@ export async function autoLoop(
372
375
 
373
376
  // ── Memory pressure check (#3331) ──
374
377
  // Graceful shutdown before OOM killer sends SIGKILL.
375
- if (iteration % MEMORY_CHECK_INTERVAL === 0) {
378
+ if (shouldCheckMemoryPressure(iteration, MEMORY_CHECK_INTERVAL)) {
376
379
  const mem = measureMemoryPressure();
377
380
  debugLog("autoLoop", { phase: "memory-check", ...mem });
378
381
  const memoryDecision = decideMemoryPressure({ ...mem, iteration });
@@ -128,6 +128,17 @@ export class AutoOrchestrator implements AutoOrchestrationModule {
128
128
  await this.deps.health.postAdvanceRecord(stopped);
129
129
  return stopped;
130
130
  }
131
+ if (!("unitType" in decision)) {
132
+ const blocked: AutoAdvanceResult = {
133
+ kind: "blocked",
134
+ reason: decision.reason,
135
+ action: decision.action,
136
+ stateSnapshot: reconciliation.stateSnapshot,
137
+ };
138
+ await this.deps.runtime.journalTransition({ name: "advance-blocked", reason: blocked.reason });
139
+ await this.deps.health.postAdvanceRecord(blocked);
140
+ return blocked;
141
+ }
131
142
 
132
143
  const nextKey = `${decision.unitType}:${decision.unitId}`;
133
144
 
@@ -1327,9 +1327,15 @@ export async function runDispatch(
1327
1327
  }
1328
1328
 
1329
1329
  const guardBasePath = _resolveDispatchGuardBasePath(s);
1330
+ let mainBranch = "main";
1331
+ try {
1332
+ mainBranch = deps.getMainBranch(guardBasePath);
1333
+ } catch (err) {
1334
+ debugLog("autoLoop", { phase: "getMainBranch-failed", error: String(err) });
1335
+ }
1330
1336
  const priorSliceBlocker = deps.getPriorSliceCompletionBlocker(
1331
1337
  guardBasePath,
1332
- deps.getMainBranch(guardBasePath),
1338
+ mainBranch,
1333
1339
  unitType,
1334
1340
  unitId,
1335
1341
  );
@@ -19,6 +19,19 @@ export interface MeasureMemoryPressureDeps {
19
19
  heapLimitBytes: () => number;
20
20
  }
21
21
 
22
+ /**
23
+ * Returns true on auto-mode startup, then every configured interval.
24
+ *
25
+ * Iteration 1 is checked explicitly so early session memory pressure cannot
26
+ * bypass the periodic interval guard.
27
+ */
28
+ export function shouldCheckMemoryPressure(iteration: number, interval: number): boolean {
29
+ if (!Number.isInteger(interval) || interval <= 0) {
30
+ throw new Error("Memory pressure check interval must be a positive integer");
31
+ }
32
+ return iteration === 1 || iteration % interval === 0;
33
+ }
34
+
22
35
  function defaultHeapLimitBytes(): number {
23
36
  const v8 = require("node:v8") as {
24
37
  getHeapStatistics?: () => { heap_size_limit?: number };
@@ -239,6 +239,12 @@ function missingSliceStop(mid: string, phase: string): DispatchAction {
239
239
  };
240
240
  }
241
241
 
242
+ function isRegistryMilestoneComplete(state: GSDState, mid: string): boolean {
243
+ return state.registry.some((milestone) =>
244
+ milestone.id === mid && milestone.status === "complete"
245
+ );
246
+ }
247
+
242
248
  /**
243
249
  * Check for milestone slices missing SUMMARY files.
244
250
  * Returns array of missing slice IDs, or empty array if all present or DB unavailable.
@@ -395,6 +401,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
395
401
  match: async ({ state, mid, midTitle, basePath, prefs, structuredQuestionsAvailable }) => {
396
402
  if (!EXECUTION_ENTRY_PHASES.has(state.phase)) return null;
397
403
  if (!MILESTONE_ID_RE.test(mid)) return null;
404
+ if (isRegistryMilestoneComplete(state, mid)) return null;
398
405
  // Align with the plan-v2 gate's lookup semantics: whitespace-only counts
399
406
  // as missing, and an auto worktree may fall back to GSD_PROJECT_ROOT.
400
407
  if (hasFinalizedMilestoneContext(basePath, mid)) return null;
@@ -709,6 +716,7 @@ export const DISPATCH_RULES: DispatchRule[] = [
709
716
  name: "pre-planning (no context) → discuss-milestone",
710
717
  match: async ({ state, mid, midTitle, basePath, prefs, structuredQuestionsAvailable }) => {
711
718
  if (state.phase !== "pre-planning") return null;
719
+ if (isRegistryMilestoneComplete(state, mid)) return null;
712
720
  const contextFile = resolveMilestoneFile(basePath, mid, "CONTEXT");
713
721
  const hasContext = !!(contextFile && (await loadFile(contextFile)));
714
722
  if (hasContext) return null; // fall through to next rule
@@ -1322,19 +1330,19 @@ export const DISPATCH_RULES: DispatchRule[] = [
1322
1330
  }
1323
1331
  }
1324
1332
 
1325
- // Safety guard (#2675): block completion when VALIDATION verdict is
1326
- // needs-remediation. The state machine treats needs-remediation as
1327
- // terminal (to prevent validate-milestone loops per #832), but
1328
- // completing-milestone should NOT proceed — remediation work is needed.
1333
+ // Safety guard (#2675, #5747): block completion when VALIDATION
1334
+ // verdict is non-passing. The state machine treats these verdicts as
1335
+ // terminal, but completing-milestone should NOT proceed — remediation
1336
+ // or human attention is needed.
1329
1337
  const validationFile = resolveMilestoneFile(basePath, mid, "VALIDATION");
1330
1338
  if (validationFile) {
1331
1339
  const validationContent = await loadFile(validationFile);
1332
1340
  if (validationContent) {
1333
1341
  const verdict = extractVerdict(validationContent);
1334
- if (verdict === "needs-remediation") {
1342
+ if (verdict === "needs-remediation" || verdict === "needs-attention") {
1335
1343
  return {
1336
1344
  action: "stop",
1337
- reason: `Cannot complete milestone ${mid}: VALIDATION verdict is "needs-remediation". Address the remediation findings and re-run validation, or update the verdict manually.`,
1345
+ reason: `Cannot complete milestone ${mid}: VALIDATION verdict is "${verdict}". Address the validation findings and re-run validation, or update the verdict manually.`,
1338
1346
  level: "warning",
1339
1347
  };
1340
1348
  }
@@ -615,10 +615,11 @@ export async function selectAndApplyModel(
615
615
  * Handles formats: "provider/model", "bare-id", "org/model-name" (OpenRouter).
616
616
  */
617
617
  export function resolveModelId<T extends { id: string; provider: string }>(
618
- modelId: string,
618
+ modelId: string | undefined,
619
619
  availableModels: T[],
620
620
  currentProvider: string | undefined,
621
621
  ): T | undefined {
622
+ if (!modelId) return undefined;
622
623
  const slashIdx = modelId.indexOf("/");
623
624
 
624
625
  if (slashIdx !== -1) {
@@ -41,6 +41,7 @@ import {
41
41
  resolveExpectedArtifactPath,
42
42
  writeBlockerPlaceholder,
43
43
  diagnoseExpectedArtifact,
44
+ diagnoseWorktreeIntegrityFailure,
44
45
  } from "./auto-recovery.js";
45
46
  import { regenerateIfMissing } from "./workflow-projections.js";
46
47
  import { WorktreeStateProjection } from "./worktree-state-projection.js";
@@ -163,7 +164,7 @@ async function buildTaskCommitContextForUnit(
163
164
  sliceTitle: stripKnownIdPrefix(slice?.title, sid),
164
165
  oneLiner: summary?.oneLiner || task?.one_liner || undefined,
165
166
  keyFiles:
166
- summary?.frontmatter.key_files?.filter(f => !f.includes("{{")) ??
167
+ summary?.frontmatter.key_files?.filter(f => !f.includes("{{") && f.trim() !== "(none)") ??
167
168
  task?.key_files ??
168
169
  undefined,
169
170
  issueNumber: ghIssueNumber,
@@ -381,6 +382,23 @@ export function buildStepCompleteMessage(nextState: import("./types.js").GSDStat
381
382
  + `Run /clear, then /gsd to continue (or /gsd auto to run continuously).`;
382
383
  }
383
384
 
385
+ /**
386
+ * Decide whether step mode should stop at the step wizard after a unit finishes.
387
+ *
388
+ * @param currentUnitType The just-finished unit type, such as "execute-task" or
389
+ * "complete-milestone"; may be null/undefined when no current unit is known.
390
+ * @param phaseAfterUnit The freshly derived next phase, such as "executing" or
391
+ * "complete"; may be null/undefined if state derivation failed.
392
+ * @returns true to show the step wizard; false to keep the loop running so
393
+ * terminal milestone completion can reach the merge/finalization path.
394
+ */
395
+ export function shouldReturnStepWizardAfterUnit(
396
+ currentUnitType: string | null | undefined,
397
+ phaseAfterUnit: string | null | undefined,
398
+ ): boolean {
399
+ return currentUnitType !== "complete-milestone" && phaseAfterUnit !== "complete";
400
+ }
401
+
384
402
  export interface PreVerificationOpts {
385
403
  skipSettleDelay?: boolean;
386
404
  skipWorktreeSync?: boolean;
@@ -413,6 +431,11 @@ function artifactValidationKind(unitType: string): "project" | "requirements" |
413
431
  }
414
432
 
415
433
  function describeArtifactVerificationFailure(unitType: string, unitId: string, basePath: string): string {
434
+ const worktreeFailure = diagnoseWorktreeIntegrityFailure(basePath);
435
+ if (worktreeFailure) {
436
+ return `${worktreeFailure} Unit: ${unitType} ${unitId}.`;
437
+ }
438
+
416
439
  const artifactPath = resolveExpectedArtifactPath(unitType, unitId, basePath);
417
440
  if (!artifactPath) {
418
441
  return `Artifact verification failed: ${unitType} "${unitId}" has no resolvable artifact path.`;
@@ -469,9 +492,17 @@ export async function autoCommitUnit(
469
492
  }
470
493
  }
471
494
 
495
+ /**
496
+ * Execute the turn-level git action (commit, snapshot, or status-only).
497
+ *
498
+ * @param opts.softFailure - Defaults to false. When true, retry git failures,
499
+ * warn, and continue without pausing auto-mode; use for best-effort deferred
500
+ * closeout work where a git failure should not block the run.
501
+ */
472
502
  async function runCloseoutGitAction(
473
503
  pctx: PostUnitContext,
474
504
  unit: NonNullable<AutoSession["currentUnit"]>,
505
+ opts?: { softFailure?: boolean },
475
506
  ): Promise<"continue" | "dispatched"> {
476
507
  const { s, ctx, pi, pauseAuto } = pctx;
477
508
  const prefs = loadEffectiveGSDPreferences()?.preferences;
@@ -506,13 +537,24 @@ async function runCloseoutGitAction(
506
537
  unitId: unit.id,
507
538
  });
508
539
  } else {
509
- const gitResult = runTurnGitAction({
540
+ const maxAttempts = opts?.softFailure ? 3 : 1;
541
+ let gitResult = runTurnGitAction({
510
542
  basePath: s.basePath,
511
543
  action: turnAction,
512
544
  unitType: unit.type,
513
545
  unitId: unit.id,
514
546
  taskContext,
515
547
  });
548
+ for (let attempt = 1; gitResult.status === "failed" && attempt < maxAttempts; attempt++) {
549
+ await new Promise((resolve) => setTimeout(resolve, 250 * attempt));
550
+ gitResult = runTurnGitAction({
551
+ basePath: s.basePath,
552
+ action: turnAction,
553
+ unitType: unit.type,
554
+ unitId: unit.id,
555
+ taskContext,
556
+ });
557
+ }
516
558
 
517
559
  if (uokFlags.gitops) {
518
560
  writeTurnGitTransaction({
@@ -563,12 +605,15 @@ async function runCloseoutGitAction(
563
605
  }
564
606
 
565
607
  const failureMsg = `Git ${turnAction} failed: ${(gitResult.error ?? "unknown error").split("\n")[0]}`;
566
- ctx.ui.notify(failureMsg, "error");
608
+ ctx.ui.notify(failureMsg, opts?.softFailure ? "warning" : "error");
567
609
  debugLog("postUnit", {
568
- phase: "git-action-failed-blocking",
610
+ phase: opts?.softFailure ? "git-action-failed-soft" : "git-action-failed-blocking",
569
611
  action: turnAction,
570
612
  error: gitResult.error ?? "unknown error",
571
613
  });
614
+ if (opts?.softFailure) {
615
+ return "continue";
616
+ }
572
617
  await pauseAuto(ctx, pi);
573
618
  return "dispatched";
574
619
  }
@@ -586,7 +631,10 @@ async function runCloseoutGitAction(
586
631
  s.lastGitActionFailure = message;
587
632
  s.lastGitActionStatus = "failed";
588
633
  debugLog("postUnit", { phase: "git-action", error: message, action: turnAction });
589
- ctx.ui.notify(`Git ${turnAction} failed: ${message.split("\n")[0]}`, uokFlags.gitops ? "error" : "warning");
634
+ ctx.ui.notify(`Git ${turnAction} failed: ${message.split("\n")[0]}`, opts?.softFailure ? "warning" : "error");
635
+ if (opts?.softFailure) {
636
+ return "continue";
637
+ }
590
638
  if (uokFlags.gitops) {
591
639
  await pauseAuto(ctx, pi);
592
640
  return "dispatched";
@@ -1133,6 +1181,24 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1133
1181
  "warning",
1134
1182
  );
1135
1183
  // Fall through to "continue" — do NOT enter the retry or db-unavailable paths.
1184
+ } else if (!triggerArtifactVerified && diagnoseWorktreeIntegrityFailure(s.basePath)) {
1185
+ const retryKey = `${s.currentUnit.type}:${s.currentUnit.id}`;
1186
+ const worktreeFailure = diagnoseWorktreeIntegrityFailure(s.basePath)!;
1187
+ s.pendingVerificationRetry = null;
1188
+ s.verificationRetryCount.delete(retryKey);
1189
+ s.verificationRetryFailureHashes.delete(retryKey);
1190
+ debugLog("postUnit", {
1191
+ phase: "worktree-integrity-failure",
1192
+ unitType: s.currentUnit.type,
1193
+ unitId: s.currentUnit.id,
1194
+ basePath: s.basePath,
1195
+ });
1196
+ ctx.ui.notify(
1197
+ `${worktreeFailure} Retry ${s.currentUnit.id} after repair.`,
1198
+ "error",
1199
+ );
1200
+ await pauseAuto(ctx, pi);
1201
+ return "dispatched";
1136
1202
  } else if (!triggerArtifactVerified && !isDbAvailable()) {
1137
1203
  debugLog("postUnit", { phase: "artifact-verify-skip-db-unavailable", unitType: s.currentUnit.type, unitId: s.currentUnit.id });
1138
1204
  const dbSkipDiag = diagnoseExpectedArtifact(s.currentUnit.type, s.currentUnit.id, s.basePath);
@@ -1220,7 +1286,7 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1220
1286
 
1221
1287
  if (s.currentUnit) {
1222
1288
  if (shouldDeferCloseoutGitAction(s.currentUnit.type)) {
1223
- const gitActionResult = await runCloseoutGitAction(pctx, s.currentUnit);
1289
+ const gitActionResult = await runCloseoutGitAction(pctx, s.currentUnit, { softFailure: true });
1224
1290
  if (gitActionResult === "dispatched") {
1225
1291
  return "stopped";
1226
1292
  }
@@ -1668,14 +1734,18 @@ export async function postUnitPostVerification(pctx: PostUnitContext): Promise<"
1668
1734
  // Without this notify(), /gsd in step mode finishes a unit and silently
1669
1735
  // exits the loop, leaving the user with no hint to /clear and /gsd again.
1670
1736
  if (s.stepMode) {
1737
+ let phaseAfterUnit: string | null = null;
1671
1738
  try {
1672
1739
  const nextState = await deriveState(s.canonicalProjectRoot);
1740
+ phaseAfterUnit = nextState.phase;
1673
1741
  ctx.ui.notify(buildStepCompleteMessage(nextState), "info");
1674
1742
  } catch (e) {
1675
1743
  debugLog("postUnit", { phase: "step-wizard-notify", error: String(e) });
1676
1744
  ctx.ui.notify(STEP_COMPLETE_FALLBACK_MESSAGE, "info");
1677
1745
  }
1678
- return "step-wizard";
1746
+ return shouldReturnStepWizardAfterUnit(s.currentUnit?.type, phaseAfterUnit)
1747
+ ? "step-wizard"
1748
+ : "continue";
1679
1749
  }
1680
1750
 
1681
1751
  return "continue";
@@ -46,6 +46,7 @@ import {
46
46
  import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.js";
47
47
  import { validateArtifact } from "./schemas/validate.js";
48
48
  import { getProjectResearchStatus } from "./project-research-policy.js";
49
+ import { isGsdWorktreePath } from "./worktree-root.js";
49
50
 
50
51
  // Re-export so existing consumers of auto-recovery.ts keep working.
51
52
  export { resolveExpectedArtifactPath, diagnoseExpectedArtifact };
@@ -56,6 +57,29 @@ export {
56
57
 
57
58
  // ─── Artifact Resolution & Verification ───────────────────────────────────────
58
59
 
60
+ export function diagnoseWorktreeIntegrityFailure(basePath: string): string | null {
61
+ if (!isGsdWorktreePath(basePath)) return null;
62
+ if (!existsSync(basePath)) {
63
+ return `Worktree integrity failure: ${basePath} does not exist. Repair or recreate the worktree before retrying.`;
64
+ }
65
+
66
+ const gitPath = join(basePath, ".git");
67
+ if (!existsSync(gitPath)) {
68
+ return `Worktree integrity failure: ${basePath} is not a valid git worktree (.git missing). Repair or recreate the worktree before retrying.`;
69
+ }
70
+
71
+ try {
72
+ execFileSync("git", ["rev-parse", "--git-dir"], {
73
+ cwd: basePath,
74
+ stdio: ["ignore", "pipe", "pipe"],
75
+ encoding: "utf-8",
76
+ });
77
+ return null;
78
+ } catch (err) {
79
+ return `Worktree integrity failure: ${basePath} is not a valid git worktree (git rev-parse failed: ${getErrorMessage(err).split("\n")[0]}). Repair or recreate the worktree before retrying.`;
80
+ }
81
+ }
82
+
59
83
  export type ArtifactRecoveryDbRefreshResult =
60
84
  | { ok: true }
61
85
  | { ok: false; fatal: boolean; message: string; reason: string };
@@ -752,6 +776,11 @@ export function verifyExpectedArtifact(
752
776
  return false;
753
777
  }
754
778
  if (!existsSync(absPath)) {
779
+ const worktreeFailure = diagnoseWorktreeIntegrityFailure(base);
780
+ if (worktreeFailure) {
781
+ logError("recovery", `${worktreeFailure} Unit: ${unitType} ${unitId}.`);
782
+ return false;
783
+ }
755
784
  logWarning("recovery", `verify-fail ${unitType} ${unitId}: existsSync false for ${absPath}`);
756
785
  return false;
757
786
  }