gsd-pi 2.82.0-dev.c22380fc3 → 2.82.0-dev.e7a7f1ed5

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 (390) hide show
  1. package/README.md +5 -4
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +10 -1
  4. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  5. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +1 -1
  6. package/dist/resources/extensions/cmux/index.js +5 -0
  7. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  8. package/dist/resources/extensions/gsd/auto/loop.js +5 -5
  9. package/dist/resources/extensions/gsd/auto/orchestrator.js +11 -0
  10. package/dist/resources/extensions/gsd/auto/phases.js +81 -31
  11. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  12. package/dist/resources/extensions/gsd/auto-dashboard.js +66 -1
  13. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +1 -0
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +18 -17
  15. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  16. package/dist/resources/extensions/gsd/auto-post-unit.js +233 -127
  17. package/dist/resources/extensions/gsd/auto-prompts.js +2 -2
  18. package/dist/resources/extensions/gsd/auto-recovery.js +71 -14
  19. package/dist/resources/extensions/gsd/auto-start.js +87 -14
  20. package/dist/resources/extensions/gsd/auto-verification.js +45 -26
  21. package/dist/resources/extensions/gsd/auto-worktree.js +176 -10
  22. package/dist/resources/extensions/gsd/auto.js +37 -5
  23. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +31 -7
  24. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -2
  26. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
  27. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  28. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  29. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  30. package/dist/resources/extensions/gsd/commands/handlers/core.js +37 -0
  31. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  32. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +7 -2
  33. package/dist/resources/extensions/gsd/crash-recovery.js +43 -5
  34. package/dist/resources/extensions/gsd/db/milestone-leases.js +24 -0
  35. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  36. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  37. package/dist/resources/extensions/gsd/doctor-git-checks.js +46 -1
  38. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  39. package/dist/resources/extensions/gsd/doctor.js +2 -28
  40. package/dist/resources/extensions/gsd/export-html.js +27 -425
  41. package/dist/resources/extensions/gsd/git-service.js +45 -3
  42. package/dist/resources/extensions/gsd/gsd-db.js +21 -6
  43. package/dist/resources/extensions/gsd/guided-flow-queue.js +4 -3
  44. package/dist/resources/extensions/gsd/guided-flow.js +101 -116
  45. package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
  46. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  47. package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
  48. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  49. package/dist/resources/extensions/gsd/native-git-bridge.js +48 -12
  50. package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
  51. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  52. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  53. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  54. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  56. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  57. package/dist/resources/extensions/gsd/prompts/discuss.md +9 -9
  58. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  59. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  60. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  61. package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
  62. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  63. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  64. package/dist/resources/extensions/gsd/queue-reorder-ui.js +30 -13
  65. package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
  66. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  67. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +9 -14
  68. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +19 -24
  69. package/dist/resources/extensions/gsd/status-guards.js +11 -0
  70. package/dist/resources/extensions/gsd/templates/plan.md +8 -5
  71. package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
  72. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  73. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  74. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  75. package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
  76. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  77. package/dist/resources/extensions/gsd/validation.js +23 -1
  78. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  79. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  80. package/dist/resources/extensions/gsd/workflow-mcp.js +17 -1
  81. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  82. package/dist/resources/extensions/gsd/worktree-lifecycle.js +33 -8
  83. package/dist/resources/extensions/shared/html-shell.js +388 -0
  84. package/dist/resources/extensions/subagent/index.js +448 -78
  85. package/dist/resources/extensions/subagent/launch.js +77 -0
  86. package/dist/resources/extensions/subagent/run-store.js +148 -0
  87. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  88. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  89. package/dist/resources/extensions/visual-brief/index.js +5 -0
  90. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  91. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  92. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  93. package/dist/web/standalone/.next/BUILD_ID +1 -1
  94. package/dist/web/standalone/.next/app-path-routes-manifest.json +13 -13
  95. package/dist/web/standalone/.next/build-manifest.json +3 -3
  96. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  97. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  98. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  99. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  100. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  106. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  108. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  109. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  111. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  112. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  113. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  115. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  118. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/index.html +1 -1
  121. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  122. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  124. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  126. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  127. package/dist/web/standalone/.next/server/app/page.js +2 -2
  128. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  129. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app-paths-manifest.json +13 -13
  131. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  132. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  136. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  137. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  138. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  139. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  140. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  141. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  142. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  143. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  144. package/package.json +4 -4
  145. package/packages/mcp-server/src/workflow-tools.test.ts +1 -1
  146. package/packages/native/tsconfig.json +2 -1
  147. package/packages/native/tsconfig.tsbuildinfo +1 -1
  148. package/packages/pi-ai/dist/providers/google-gemini-cli.d.ts.map +1 -1
  149. package/packages/pi-ai/dist/providers/google-gemini-cli.js +5 -0
  150. package/packages/pi-ai/dist/providers/google-gemini-cli.js.map +1 -1
  151. package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts +2 -0
  152. package/packages/pi-ai/dist/providers/google-gemini-cli.test.d.ts.map +1 -0
  153. package/packages/pi-ai/dist/providers/google-gemini-cli.test.js +41 -0
  154. package/packages/pi-ai/dist/providers/google-gemini-cli.test.js.map +1 -0
  155. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  156. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  157. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  158. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  159. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  160. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  161. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  162. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  163. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  164. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  165. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  166. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  167. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  168. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  169. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  170. package/packages/pi-ai/src/providers/google-gemini-cli.test.ts +49 -0
  171. package/packages/pi-ai/src/providers/google-gemini-cli.ts +7 -0
  172. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  173. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  174. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  175. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  176. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  177. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  178. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  179. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  180. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  181. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  182. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +24 -6
  185. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  186. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  187. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  188. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +23 -7
  189. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  190. package/packages/pi-tui/dist/__tests__/terminal.test.d.ts +2 -0
  191. package/packages/pi-tui/dist/__tests__/terminal.test.d.ts.map +1 -0
  192. package/packages/pi-tui/dist/__tests__/terminal.test.js +103 -0
  193. package/packages/pi-tui/dist/__tests__/terminal.test.js.map +1 -0
  194. package/packages/pi-tui/dist/terminal.d.ts +2 -0
  195. package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
  196. package/packages/pi-tui/dist/terminal.js +12 -0
  197. package/packages/pi-tui/dist/terminal.js.map +1 -1
  198. package/packages/pi-tui/src/__tests__/terminal.test.ts +121 -0
  199. package/packages/pi-tui/src/terminal.ts +11 -0
  200. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  201. package/src/resources/GSD-WORKFLOW.md +10 -1
  202. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  203. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +1 -1
  204. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  205. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +9 -0
  206. package/src/resources/extensions/cmux/index.ts +6 -0
  207. package/src/resources/extensions/gsd/auto/contracts.ts +14 -6
  208. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  209. package/src/resources/extensions/gsd/auto/loop.ts +8 -5
  210. package/src/resources/extensions/gsd/auto/orchestrator.ts +11 -0
  211. package/src/resources/extensions/gsd/auto/phases.ts +90 -38
  212. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  213. package/src/resources/extensions/gsd/auto-dashboard.ts +72 -1
  214. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -0
  215. package/src/resources/extensions/gsd/auto-dispatch.ts +19 -17
  216. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  217. package/src/resources/extensions/gsd/auto-post-unit.ts +266 -139
  218. package/src/resources/extensions/gsd/auto-prompts.ts +2 -2
  219. package/src/resources/extensions/gsd/auto-recovery.ts +74 -11
  220. package/src/resources/extensions/gsd/auto-start.ts +94 -12
  221. package/src/resources/extensions/gsd/auto-verification.ts +58 -36
  222. package/src/resources/extensions/gsd/auto-worktree.ts +193 -10
  223. package/src/resources/extensions/gsd/auto.ts +40 -5
  224. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +42 -7
  225. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
  226. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -2
  227. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
  228. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  229. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  230. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  231. package/src/resources/extensions/gsd/commands/handlers/core.ts +40 -0
  232. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  233. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +8 -3
  234. package/src/resources/extensions/gsd/crash-recovery.ts +44 -4
  235. package/src/resources/extensions/gsd/db/milestone-leases.ts +26 -0
  236. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  237. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  238. package/src/resources/extensions/gsd/doctor-git-checks.ts +45 -1
  239. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  240. package/src/resources/extensions/gsd/doctor-types.ts +1 -0
  241. package/src/resources/extensions/gsd/doctor.ts +2 -27
  242. package/src/resources/extensions/gsd/export-html.ts +27 -427
  243. package/src/resources/extensions/gsd/git-service.ts +51 -4
  244. package/src/resources/extensions/gsd/gsd-db.ts +21 -6
  245. package/src/resources/extensions/gsd/guided-flow-queue.ts +4 -3
  246. package/src/resources/extensions/gsd/guided-flow.ts +134 -133
  247. package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
  248. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  249. package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
  250. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  251. package/src/resources/extensions/gsd/native-git-bridge.ts +54 -12
  252. package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
  253. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  254. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  255. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  256. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  257. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  258. package/src/resources/extensions/gsd/prompts/discuss-headless.md +8 -8
  259. package/src/resources/extensions/gsd/prompts/discuss.md +9 -9
  260. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  261. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  262. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  263. package/src/resources/extensions/gsd/prompts/queue.md +4 -4
  264. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  265. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  266. package/src/resources/extensions/gsd/queue-reorder-ui.ts +31 -13
  267. package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
  268. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  269. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +12 -15
  270. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +17 -25
  271. package/src/resources/extensions/gsd/status-guards.ts +13 -0
  272. package/src/resources/extensions/gsd/templates/plan.md +8 -5
  273. package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
  274. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +71 -0
  275. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  276. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +56 -0
  277. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +80 -1
  278. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +35 -7
  279. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
  280. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  281. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +91 -6
  282. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +1 -0
  283. package/src/resources/extensions/gsd/tests/auto-stop-notification.test.ts +20 -0
  284. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  285. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  286. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +87 -0
  287. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  288. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
  289. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  290. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  291. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
  292. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  293. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +86 -2
  294. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +2 -0
  295. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
  296. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +59 -2
  297. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +66 -0
  298. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  299. package/src/resources/extensions/gsd/tests/doctor-empty-worktree.test.ts +65 -0
  300. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  301. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  302. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +11 -0
  303. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
  304. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
  305. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
  306. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  307. package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
  308. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +7 -7
  309. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  310. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  311. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  312. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  313. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +112 -1
  314. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  315. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +46 -0
  316. package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +179 -0
  317. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  318. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
  319. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  320. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  321. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  322. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
  323. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  324. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  325. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
  326. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  327. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  328. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  329. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  330. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  331. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +59 -0
  332. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  333. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +37 -1
  334. package/src/resources/extensions/gsd/tests/queue-reorder-ui.test.ts +54 -0
  335. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +89 -2
  336. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +2 -3
  337. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  338. package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
  339. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +53 -2
  340. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +119 -23
  341. package/src/resources/extensions/gsd/tests/status-guards.test.ts +13 -1
  342. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  343. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  344. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  345. package/src/resources/extensions/gsd/tests/validate-milestone-stuck-guard.test.ts +29 -2
  346. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  347. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  348. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +19 -1
  349. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  350. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  351. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  352. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  353. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +54 -0
  354. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  355. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  356. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  357. package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
  358. package/src/resources/extensions/gsd/types.ts +1 -1
  359. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  360. package/src/resources/extensions/gsd/validation.ts +23 -1
  361. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  362. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  363. package/src/resources/extensions/gsd/workflow-mcp.ts +18 -1
  364. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  365. package/src/resources/extensions/gsd/worktree-lifecycle.ts +41 -8
  366. package/src/resources/extensions/shared/html-shell.ts +412 -0
  367. package/src/resources/extensions/subagent/index.ts +567 -103
  368. package/src/resources/extensions/subagent/launch.ts +131 -0
  369. package/src/resources/extensions/subagent/run-store.ts +218 -0
  370. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  371. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  372. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  373. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  374. package/src/resources/extensions/visual-brief/index.ts +8 -0
  375. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  376. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  377. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  378. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  379. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  380. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  381. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  382. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  383. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  384. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  385. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  386. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  387. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  388. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  389. /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → 4dSwdrs__8NwCZggxP9KF}/_buildManifest.js +0 -0
  390. /package/dist/web/standalone/.next/static/{Wop3A7KRGyR06H3rla_1- → 4dSwdrs__8NwCZggxP9KF}/_ssgManifest.js +0 -0
@@ -58,6 +58,7 @@ import {
58
58
  import {
59
59
  writeLock,
60
60
  clearLock,
61
+ clearStaleWorkerLock,
61
62
  readCrashLock,
62
63
  isLockProcessAlive,
63
64
  formatCrashInfo,
@@ -240,7 +241,7 @@ import { runAutoLoopWithUok } from "./uok/kernel.js";
240
241
  import { resolveUokFlags } from "./uok/flags.js";
241
242
  import { validateDirectory } from "./validate-directory.js";
242
243
  import { createAutoOrchestrator } from "./auto/orchestrator.js";
243
- import type { AutoOrchestrationModule, AutoOrchestratorDeps, DispatchAdapter } from "./auto/contracts.js";
244
+ import type { AutoAdvanceResult, AutoOrchestrationModule, AutoOrchestratorDeps, DispatchAdapter } from "./auto/contracts.js";
244
245
  import { reconcileBeforeDispatch } from "./state-reconciliation.js";
245
246
  import { compileUnitToolContract } from "./tool-contract.js";
246
247
  import { createWorktreeSafetyModule } from "./worktree-safety.js";
@@ -316,6 +317,13 @@ import { normalizeRealPath } from "./paths.js";
316
317
  /** Throttle STATE.md rebuilds — at most once per 30 seconds */
317
318
  const STATE_REBUILD_MIN_INTERVAL_MS = 30_000;
318
319
 
320
+ export function formatAutoStopNotification(prefix: string, totals: { cost: number; tokens: { total: number } }, unitCount: number): string {
321
+ return [
322
+ `${prefix}.`,
323
+ `Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${unitCount} units`,
324
+ ].join("\n");
325
+ }
326
+
319
327
  /**
320
328
  * Phase B — register this auto-mode process in the workers table so other
321
329
  * workers and janitors can detect liveness via heartbeat. Best-effort: if
@@ -1044,6 +1052,7 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
1044
1052
  // visible so the user still has a resumable auto-mode signal on screen.
1045
1053
  if (!s.paused) {
1046
1054
  ctx.ui.setStatus("gsd-auto", undefined);
1055
+ ctx.ui.setWidget("gsd-progress", undefined);
1047
1056
  if (s.completionStopInProgress) {
1048
1057
  s.completionStopInProgress = false;
1049
1058
  }
@@ -1415,7 +1424,7 @@ export async function stopAuto(
1415
1424
  if (ledger && ledger.units.length > 0) {
1416
1425
  const totals = getProjectTotals(ledger.units);
1417
1426
  ctx?.ui.notify(
1418
- `${notificationPrefix}. Session: ${formatCost(totals.cost)} · ${formatTokenCount(totals.tokens.total)} tokens · ${ledger.units.length} units`,
1427
+ formatAutoStopNotification(notificationPrefix, totals, ledger.units.length),
1419
1428
  "info",
1420
1429
  );
1421
1430
  } else {
@@ -1722,7 +1731,6 @@ export async function pauseAuto(
1722
1731
  restoreProjectRootEnv();
1723
1732
  restoreMilestoneLockEnv();
1724
1733
  s.pendingVerificationRetry = null;
1725
- s.verificationRetryCount.clear();
1726
1734
  ctx?.ui.setStatus("gsd-auto", "paused");
1727
1735
  ctx?.ui.setWidget("gsd-progress", undefined);
1728
1736
  const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
@@ -1839,6 +1847,13 @@ export function createWiredDispatchAdapter(
1839
1847
  modelRegistry,
1840
1848
  });
1841
1849
 
1850
+ if (action.action === "stop") {
1851
+ return {
1852
+ kind: "blocked",
1853
+ reason: action.reason,
1854
+ action: action.level === "warning" ? "pause" : "stop",
1855
+ };
1856
+ }
1842
1857
  if (action.action !== "dispatch") return null;
1843
1858
  return {
1844
1859
  unitType: action.unitType,
@@ -2065,6 +2080,18 @@ export function createWiredAutoOrchestrationModule(
2065
2080
  return createAutoOrchestrator(deps);
2066
2081
  }
2067
2082
 
2083
+ function notifyResumeBlocked(ctx: ExtensionContext, result: Extract<AutoAdvanceResult, { kind: "blocked" }>): void {
2084
+ const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
2085
+ ctx.ui.notify(`Auto-mode blocked: ${result.reason}. Fix and run ${resumeCmd} to resume.`, "warning");
2086
+ setLifecycleOutcome(ctx, {
2087
+ status: "blocked",
2088
+ title: "Auto-mode blocked",
2089
+ detail: result.reason,
2090
+ nextAction: `Fix the blocker, then run ${resumeCmd} to resume.`,
2091
+ commands: ["/gsd status for overview", `${resumeCmd} to resume`, "/gsd doctor to diagnose"],
2092
+ });
2093
+ }
2094
+
2068
2095
  function ensureOrchestrationModule(ctx: ExtensionContext, pi: ExtensionAPI, basePath: string): void {
2069
2096
  s.orchestration = createWiredAutoOrchestrationModule(ctx, pi, basePath, lockBase());
2070
2097
  }
@@ -2402,7 +2429,7 @@ export async function startAuto(
2402
2429
  // This closes the journal gap reported in #3348 where the worker wrote side
2403
2430
  // effects (SUMMARY.md, DB updates) but died before emitting unit-end.
2404
2431
  emitCrashRecoveredUnitEnd(base, freshStartAssessment.lock);
2405
- clearLock(base);
2432
+ clearStaleWorkerLock(base);
2406
2433
  }
2407
2434
 
2408
2435
  if (!s.paused) {
@@ -2473,6 +2500,8 @@ export async function startAuto(
2473
2500
  s.unitLifetimeDispatches.clear();
2474
2501
  if (!getLedger()) initMetrics(base);
2475
2502
  if (s.currentMilestoneId) setActiveMilestoneId(base, s.currentMilestoneId);
2503
+ await openProjectDbIfPresent(base);
2504
+ registerAutoWorkerForSession(s, base);
2476
2505
 
2477
2506
  // Re-register health level notification callback lost across process restart
2478
2507
  setLevelChangeCallback((_from, to, summary) => {
@@ -2580,7 +2609,12 @@ export async function startAuto(
2580
2609
  pi.events.emit(CMUX_CHANNELS.LOG, { preferences: loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, message: s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", level: "progress" });
2581
2610
 
2582
2611
  try {
2583
- await s.orchestration?.resume();
2612
+ const resumeResult = await s.orchestration?.resume();
2613
+ if (resumeResult?.kind === "blocked") {
2614
+ notifyResumeBlocked(ctx, resumeResult);
2615
+ await cleanupAfterLoopExit(ctx);
2616
+ return;
2617
+ }
2584
2618
  } catch (err) {
2585
2619
  debugLog("resume-orchestration-resume", { error: err instanceof Error ? err.message : String(err) });
2586
2620
  }
@@ -2601,6 +2635,7 @@ export async function startAuto(
2601
2635
  const bootstrapDeps: BootstrapDeps = {
2602
2636
  shouldUseWorktreeIsolation,
2603
2637
  registerSigtermHandler,
2638
+ registerAutoWorkerForSession: (projectRoot) => registerAutoWorkerForSession(s, projectRoot),
2604
2639
  lockBase,
2605
2640
  buildLifecycle,
2606
2641
  };
@@ -32,6 +32,7 @@ import { shouldIgnoreAgentEndForActiveUnit } from "../auto/unit-runner-events.js
32
32
  import { resolveModelId } from "../auto-model-selection.js";
33
33
  import { resolveProjectRoot } from "../worktree.js";
34
34
  import { clearDiscussionFlowState } from "./write-gate.js";
35
+ import { clearGuidedUnitContext } from "../guided-unit-context.js";
35
36
  import { resumeAutoAfterProviderDelay } from "./provider-error-resume.js";
36
37
  import {
37
38
  classifyError,
@@ -48,6 +49,11 @@ const MAX_NETWORK_RETRIES = 2;
48
49
  function isObjectRecord(value: unknown): value is Record<string, unknown> {
49
50
  return !!value && typeof value === "object";
50
51
  }
52
+
53
+ export function _hasEmptyAgentEndContent(content: unknown): boolean {
54
+ return content == null || (Array.isArray(content) && content.length === 0);
55
+ }
56
+
51
57
  /**
52
58
  * Cap on auto-resume attempts for sustained transient-provider errors.
53
59
  *
@@ -94,6 +100,14 @@ export function isUserInitiatedAbortMessage(message: string | undefined | null):
94
100
  return /\b(?:claude code process aborted by user|request aborted by user|process aborted by user)\b/i.test(message);
95
101
  }
96
102
 
103
+ export function shouldDeferTransientErrorToCoreRetry(
104
+ cls: ErrorClass,
105
+ rawErrorMsg: string,
106
+ ): boolean {
107
+ if (!isTransient(cls) || cls.kind === "rate-limit") return false;
108
+ return !/retry failed after \d+ attempts:/i.test(rawErrorMsg);
109
+ }
110
+
97
111
  function isBareClaudeCodeSessionSwitchAbortMarker(message: string | undefined | null): boolean {
98
112
  if (!message) return false;
99
113
  const normalized = message.trim().replace(/\s+/g, " ").toLowerCase();
@@ -196,6 +210,14 @@ export function resolveAgentEndErrorDisplay(
196
210
  return rawErrorMsg;
197
211
  }
198
212
 
213
+ export function isTerminalDeletedWorktreeProviderError(
214
+ message: string | undefined | null,
215
+ ): boolean {
216
+ if (!message) return false;
217
+ if (!/\bdoes not exist\b/i.test(message)) return false;
218
+ return /[/\\]\.gsd[/\\](?:projects[/\\][^/\\]+[/\\])?worktrees[/\\][^/\\\s"']+/i.test(message);
219
+ }
220
+
199
221
  async function pauseTransientWithBackoff(
200
222
  cls: ErrorClass,
201
223
  pi: ExtensionAPI,
@@ -244,9 +266,11 @@ export async function handleAgentEnd(
244
266
  // falsely report files as missing — producing a spurious "ready signal
245
267
  // rejected" loop even though the files are on disk.
246
268
  clearPathCache();
269
+ const basePath = resolveAgentEndBasePath();
270
+ clearGuidedUnitContext(basePath);
247
271
 
248
272
  try {
249
- if (await checkDeepProjectSetupAfterTurn(event, ctx, resolveAgentEndBasePath())) {
273
+ if (await checkDeepProjectSetupAfterTurn(event, ctx, basePath)) {
250
274
  return;
251
275
  }
252
276
  } catch (err) {
@@ -254,8 +278,8 @@ export async function handleAgentEnd(
254
278
  logWarning("bootstrap", `checkDeepProjectSetupAfterTurn failed: ${message}`);
255
279
  }
256
280
 
257
- if (checkAutoStartAfterDiscuss()) {
258
- clearDiscussionFlowState(resolveAgentEndBasePath() ?? process.cwd());
281
+ if (checkAutoStartAfterDiscuss(basePath)) {
282
+ clearDiscussionFlowState(basePath ?? process.cwd());
259
283
  return;
260
284
  }
261
285
 
@@ -263,14 +287,14 @@ export async function handleAgentEnd(
263
287
  // are missing, `checkAutoStartAfterDiscuss` returns false silently. Surface
264
288
  // that and nudge the LLM to complete the writes before the user hits the
265
289
  // downstream "All milestones complete" warning loop.
266
- if (maybeHandleReadyPhraseWithoutFiles(event)) return;
290
+ if (maybeHandleReadyPhraseWithoutFiles(event, basePath)) return;
267
291
 
268
292
  // #4573 — Empty-turn recovery: if the LLM announced intent in prose but
269
293
  // emitted no tool calls, nudge it to execute. Fires only when auto-mode is
270
294
  // active or a discussion autostart is pending (non-auto interactive discuss
271
295
  // is user-driven). Runs before `isAutoActive` early return so pending
272
296
  // discussions (where isAutoActive may be false) still get recovered.
273
- if (maybeHandleEmptyIntentTurn(event, isAutoActive())) return;
297
+ if (maybeHandleEmptyIntentTurn(event, isAutoActive(), basePath)) return;
274
298
 
275
299
  if (!isAutoActive()) return;
276
300
 
@@ -310,7 +334,7 @@ export async function handleAgentEnd(
310
334
  // that carry error context — e.g. errorMessage field or non-empty content
311
335
  // indicating a mid-stream failure. (#2695)
312
336
  const content = "content" in lastMsg ? lastMsg.content : undefined;
313
- const hasEmptyContent = Array.isArray(content) && content.length === 0;
337
+ const hasEmptyContent = _hasEmptyAgentEndContent(content);
314
338
  const hasErrorMessage = "errorMessage" in lastMsg && !!lastMsg.errorMessage;
315
339
 
316
340
  if (hasEmptyContent && !hasErrorMessage) {
@@ -355,6 +379,17 @@ export async function handleAgentEnd(
355
379
  rawErrorMsg,
356
380
  "content" in lastMsg ? lastMsg.content : undefined,
357
381
  );
382
+ if (
383
+ isAutoCompletionStopInProgress() &&
384
+ isTerminalDeletedWorktreeProviderError(`${rawErrorMsg}\n${displayMsg}`)
385
+ ) {
386
+ resetRetryState(retryState);
387
+ logWarning(
388
+ "bootstrap",
389
+ `Ignoring stale deleted-worktree provider error during terminal completion reroot: ${displayMsg || rawErrorMsg}`,
390
+ );
391
+ return;
392
+ }
358
393
  const errorDetail = displayMsg ? `: ${displayMsg}` : "";
359
394
  const explicitRetryAfterMs = ("retryAfterMs" in lastMsg && typeof lastMsg.retryAfterMs === "number") ? lastMsg.retryAfterMs : undefined;
360
395
 
@@ -461,7 +496,7 @@ export async function handleAgentEnd(
461
496
  // Core retries transient failures in-session after this handler.
462
497
  // Keep that behavior for non-rate-limit classes to avoid pause/retry races,
463
498
  // but let rate-limit continue into model fallback logic below (#4373).
464
- if (isTransient(cls) && cls.kind !== "rate-limit") {
499
+ if (shouldDeferTransientErrorToCoreRetry(cls, rawErrorMsg)) {
465
500
  return;
466
501
  }
467
502
 
@@ -487,17 +487,18 @@ export function registerDbTools(pi: ExtensionAPI): void {
487
487
  promptGuidelines: [
488
488
  "Use gsd_plan_milestone for milestone planning instead of writing ROADMAP.md directly.",
489
489
  "Keep parameters flat and provide the full milestone planning payload, including slices.",
490
+ "Milestone and slice titles must not contain forward slash (/), en dash, or em dash characters.",
490
491
  "The tool validates input, writes milestone and slice planning data transactionally, renders ROADMAP.md from DB, and clears both state and parse caches after success.",
491
492
  "Use the canonical name gsd_plan_milestone; gsd_milestone_plan is only an alias.",
492
493
  ],
493
494
  parameters: Type.Object({
494
495
  // ── Core identification + content (required) ──────────────────────
495
496
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
496
- title: Type.String({ description: "Milestone title" }),
497
+ title: Type.String({ description: "Milestone title; must not contain forward slash (/), en dash, or em dash characters" }),
497
498
  vision: Type.String({ description: "Milestone vision" }),
498
499
  slices: Type.Array(Type.Object({
499
500
  sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
500
- title: Type.String({ description: "Slice title" }),
501
+ title: Type.String({ description: "Slice title; must not contain forward slash (/), en dash, or em dash characters" }),
501
502
  risk: Type.String({ description: "Slice risk" }),
502
503
  depends: Type.Array(Type.String(), { description: "Slice dependency IDs" }),
503
504
  demo: Type.String({ description: "Roadmap demo text / After this" }),
@@ -570,10 +571,10 @@ export function registerDbTools(pi: ExtensionAPI): void {
570
571
  title: Type.String({ description: "Task title" }),
571
572
  description: Type.String({ description: "Task description / steps block" }),
572
573
  estimate: Type.String({ description: "Task estimate string" }),
573
- files: Type.Array(Type.String(), { description: "Files likely touched" }),
574
+ files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
574
575
  verify: Type.String({ description: "Verification command or block" }),
575
- inputs: Type.Array(Type.String(), { description: "Input files or references" }),
576
- expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
576
+ inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
577
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
577
578
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
578
579
  }), { description: "Planned tasks for the slice" }),
579
580
  // ── Enrichment metadata (optional — defaults to empty) ────────────
@@ -650,10 +651,10 @@ export function registerDbTools(pi: ExtensionAPI): void {
650
651
  title: Type.String({ description: "Task title" }),
651
652
  description: Type.String({ description: "Task description / steps block" }),
652
653
  estimate: Type.String({ description: "Task estimate string" }),
653
- files: Type.Array(Type.String(), { description: "Files likely touched" }),
654
+ files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
654
655
  verify: Type.String({ description: "Verification command or block" }),
655
- inputs: Type.Array(Type.String(), { description: "Input files or references" }),
656
- expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
656
+ inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
657
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
657
658
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
658
659
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
659
660
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
@@ -31,6 +31,7 @@ import { resolveWorktreeProjectRoot } from "../worktree-root.js";
31
31
  import { extractSubagentAgentClasses } from "./subagent-input.js";
32
32
  import { approvalGateIdForUnit, isExplicitApprovalResponse, shouldPauseForUserApprovalQuestion } from "../user-input-boundary.js";
33
33
  import { resolveSkillManifest } from "../skill-manifest.js";
34
+ import { getGuidedUnitContext } from "../guided-unit-context.js";
34
35
 
35
36
  let approvalQuestionAbortInFlight = false;
36
37
 
@@ -772,7 +773,8 @@ export function registerHooks(
772
773
  // subagent dispatch. Closes the b23 bug class where a discuss-milestone
773
774
  // turn used the host Edit tool to modify user source files.
774
775
  const dash = getAutoRuntimeSnapshot();
775
- const activeUnitType = dash.currentUnit?.type;
776
+ const guidedUnit = getGuidedUnitContext(discussionBasePath);
777
+ const activeUnitType = dash.currentUnit?.type ?? guidedUnit?.unitType;
776
778
  if (activeUnitType) {
777
779
  const manifest = resolveManifest(activeUnitType);
778
780
  if (manifest) {
@@ -791,7 +793,7 @@ export function registerHooks(
791
793
  const planningGuard = shouldBlockPlanningUnit(
792
794
  event.toolName,
793
795
  planningInput,
794
- dash.basePath || discussionBasePath,
796
+ dash.basePath || guidedUnit?.basePath || discussionBasePath,
795
797
  activeUnitType,
796
798
  manifest.tools,
797
799
  agentClasses,
@@ -1,20 +1,32 @@
1
1
  export function extractSubagentAgentClasses(input: unknown): string[] {
2
2
  if (!input || typeof input !== "object") return [];
3
3
 
4
- const record = input as Record<string, unknown>;
5
4
  const agentClasses: string[] = [];
5
+ const visited = new WeakSet<object>();
6
6
  const addAgentClass = (value: unknown): void => {
7
- if (typeof value === "string" && value.trim().length > 0) agentClasses.push(value.trim());
7
+ if (typeof value !== "string") return;
8
+ const normalized = value.trim().replace(/\.md$/i, "");
9
+ if (normalized.length > 0) agentClasses.push(normalized);
8
10
  };
9
- const addFromItems = (value: unknown): void => {
11
+
12
+ const visitItems = (value: unknown): void => {
10
13
  if (!Array.isArray(value)) return;
11
14
  for (const item of value) {
12
- if (item && typeof item === "object") addAgentClass((item as Record<string, unknown>).agent);
15
+ visit(item);
13
16
  }
14
17
  };
15
18
 
16
- addAgentClass(record.agent);
17
- addFromItems(record.tasks);
18
- addFromItems(record.chain);
19
+ const visit = (value: unknown): void => {
20
+ if (!value || typeof value !== "object") return;
21
+ if (visited.has(value)) return;
22
+ visited.add(value);
23
+ const record = value as Record<string, unknown>;
24
+ addAgentClass(record.agent);
25
+ visitItems(record.tasks);
26
+ visitItems(record.chain);
27
+ visitItems(record.parallel);
28
+ };
29
+
30
+ visit(input);
19
31
  return agentClasses;
20
32
  }
@@ -5,7 +5,7 @@ import { isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { minimatch } from "minimatch";
6
6
 
7
7
  import { getIsolationMode } from "../preferences.js";
8
- import type { ToolsPolicy } from "../unit-context-manifest.js";
8
+ import { compileSubagentPermissionContract, type ToolsPolicy } from "../unit-context-manifest.js";
9
9
  import { logWarning } from "../workflow-logger.js";
10
10
  import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
11
11
 
@@ -65,6 +65,7 @@ const QUEUE_SAFE_TOOLS = new Set([
65
65
  * true / false — shell no-ops / test exit codes
66
66
  */
67
67
  const BASH_READ_ONLY_RE = /^\s*(cat|head|tail|less|more|wc|file|stat|du|df|which|type|echo|printf|ls|find|grep|rg|awk|sed\b(?!.*-i)|sort|uniq|diff|comm|tr|cut|tee\s+-a\s+\/dev\/null|git\s+(log|show|diff|status|branch|tag|remote|rev-parse|ls-files|blame|shortlog|describe|stash\s+list|config\s+--get|cat-file)|gh\s+(issue|pr|api|repo|release)\s+(view|list|diff|status|checks)|mkdir\s+-p\s+\.gsd|rtk\s|npm\s+run\s+(test|test:\w+|lint|lint:\w+|typecheck|type-check|type-check:\w+|check|verify|audit|outdated|format:check|ci|validate)\b|npm\s+(ls|list|info|view|show|outdated|audit|explain|doctor|ping|--version|-v)\b|npx\s|tsx\s|node\s+(--print|--version|-v\b)|python[23]?\s+(-c\s+'[^']*'|--version|-V\b|-m\s+(pip\s+show|pip\s+list|site))|pip[23]?\s+(show|list|freeze|check|index\s+versions)\b|jq\s|yq\s|curl\s+(-s\b|--silent\b)(?!\s+[^|>]*\s-[oO]\b)(?!\s+[^|>]*\s--output\b)[^|>]*$|openssl\s+(version|x509|s_client)|env\b|printenv\b|true\b|false\b)/;
68
+ const BASH_VERIFICATION_RE = /^\s*(npm\s+(run\s+(build|test|test:\w+|lint|lint:\w+|typecheck|type-check|verify|ci|validate)\b|test\b)|pnpm\s+(build|test|lint|typecheck|verify)\b|yarn\s+(build|test|lint|typecheck|verify)\b|vitest\b|jest\b|go\s+test\b)/;
68
69
 
69
70
  interface InMemoryWriteGateState {
70
71
  verifiedDepthMilestones: Set<string>;
@@ -767,6 +768,9 @@ function blockReason(unitType: string, mode: string, what: string): string {
767
768
  * and listed in the policy's allowedSubagents.
768
769
  * - "docs" → like "planning" but also allows writes to paths
769
770
  * matching `allowedPathGlobs` relative to basePath.
771
+ * - "verification"
772
+ * → allows Bash for project verification commands, but keeps
773
+ * writes restricted to .gsd/ and blocks subagent dispatch.
770
774
  *
771
775
  * `pathOrCommand` is the file path for write/edit-shaped tools and the
772
776
  * shell command for bash. Other tools ignore this argument.
@@ -804,14 +808,15 @@ export function shouldBlockPlanningUnit(
804
808
  return { block: true, reason: blockReason(unitType, policy.mode, `tool "${tool}" is not on the read-only allowlist`) };
805
809
  }
806
810
 
807
- // planning / planning-dispatch / docs modes share the same surface for safe tools, bash, and subagent.
811
+ // planning / planning-dispatch / docs / verification modes share the same surface for safe tools, bash, and subagent.
808
812
  if (PLANNING_SAFE_TOOLS.has(tool)) return { block: false };
809
813
  if (tool.startsWith("gsd_")) return { block: false };
810
814
 
811
815
  if (PLANNING_SUBAGENT_TOOLS.has(tool)) {
812
816
  if (policy.mode === "planning-dispatch") {
813
817
  const requested = (agentClasses ?? []).map(a => a.trim()).filter(Boolean);
814
- const allowedSubagents = Array.isArray(policy.allowedSubagents) ? policy.allowedSubagents : [];
818
+ const dispatchContract = compileSubagentPermissionContract(policy);
819
+ const allowedSubagents = dispatchContract.allowedSubagents;
815
820
  const allowed = new Set(allowedSubagents);
816
821
  // When agentClasses is undefined, the caller has not been updated to extract
817
822
  // agent identities yet. Block and warn so stale callers surface in telemetry
@@ -861,6 +866,17 @@ export function shouldBlockPlanningUnit(
861
866
  }
862
867
 
863
868
  if (tool === "bash") {
869
+ if (policy.mode === "verification") {
870
+ if (BASH_VERIFICATION_RE.test(pathOrCommand) || BASH_READ_ONLY_RE.test(pathOrCommand)) return { block: false };
871
+ return {
872
+ block: true,
873
+ reason: blockReason(
874
+ unitType,
875
+ policy.mode,
876
+ `bash is restricted to build/test verification commands (npm run build, npm test, etc.); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`,
877
+ ),
878
+ };
879
+ }
864
880
  if (BASH_READ_ONLY_RE.test(pathOrCommand)) return { block: false };
865
881
  return {
866
882
  block: true,
@@ -8,12 +8,14 @@
8
8
  *
9
9
  * Design constraints (from Trek-e approval):
10
10
  * - Warn the user before stashing (no silent surprises)
11
- * - git stash push / git stash pop only no custom stash management layer
12
- * - Stash/pop errors are logged but MUST NOT block the merge itself
11
+ * - git stash push / git stash apply+drop for targeted restore
12
+ * - Stash/apply errors are logged but MUST NOT block the merge itself
13
13
  * - Fast-path status check — clean trees pay no extra cost
14
14
  */
15
15
 
16
16
  import { execFileSync } from "node:child_process";
17
+ import { existsSync, readFileSync } from "node:fs";
18
+ import { join } from "node:path";
17
19
  import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
18
20
  import { logWarning } from "./workflow-logger.js";
19
21
  import { nativeHasChanges } from "./native-git-bridge.js";
@@ -32,6 +34,148 @@ export interface PostflightResult {
32
34
  needsManualRecovery: boolean;
33
35
  message: string;
34
36
  stashRef?: string;
37
+ resolution?: "applied" | "already-present-dropped" | "already-present-preserved" | "manual-recovery";
38
+ collidedPaths?: string[];
39
+ }
40
+
41
+ function gitText(basePath: string, args: string[]): string {
42
+ return execFileSync("git", args, {
43
+ cwd: basePath,
44
+ stdio: ["ignore", "pipe", "pipe"],
45
+ encoding: "utf-8",
46
+ env: GIT_NO_PROMPT_ENV,
47
+ });
48
+ }
49
+
50
+ function gitBuffer(basePath: string, args: string[]): Buffer {
51
+ return execFileSync("git", args, {
52
+ cwd: basePath,
53
+ stdio: ["ignore", "pipe", "pipe"],
54
+ env: GIT_NO_PROMPT_ENV,
55
+ });
56
+ }
57
+
58
+ function errorText(err: unknown): string {
59
+ if (!err || typeof err !== "object") return String(err);
60
+ const parts: string[] = [];
61
+ const stderr = (err as { stderr?: unknown }).stderr;
62
+ const stdout = (err as { stdout?: unknown }).stdout;
63
+ for (const value of [stderr, stdout]) {
64
+ if (typeof value === "string") parts.push(value);
65
+ else if (value instanceof Uint8Array) parts.push(Buffer.from(value).toString("utf-8"));
66
+ }
67
+ parts.push(err instanceof Error ? err.message : String(err));
68
+ return parts.filter(Boolean).join("\n");
69
+ }
70
+
71
+ function parseAlreadyExistsNoCheckoutPaths(text: string): string[] {
72
+ const paths: string[] = [];
73
+ for (const line of text.split(/\r?\n/)) {
74
+ const match = /^(.+?) already exists, no checkout$/i.exec(line.trim());
75
+ if (match?.[1]) paths.push(match[1]);
76
+ }
77
+ return [...new Set(paths)];
78
+ }
79
+
80
+ function readZeroDelimitedPaths(output: string): string[] {
81
+ return output.split("\0").filter(Boolean);
82
+ }
83
+
84
+ function listStashUntrackedPaths(basePath: string, stashRef: string): string[] | null {
85
+ try {
86
+ const output = gitText(basePath, ["ls-tree", "-r", "-z", "--name-only", `${stashRef}^3`]);
87
+ return readZeroDelimitedPaths(output);
88
+ } catch {
89
+ return null;
90
+ }
91
+ }
92
+
93
+ function listStashTrackedPaths(basePath: string, stashRef: string): string[] | null {
94
+ try {
95
+ const output = gitText(basePath, ["diff", "--name-only", "-z", `${stashRef}^1`, stashRef]);
96
+ return readZeroDelimitedPaths(output);
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+
102
+ function isWorktreeClean(basePath: string): boolean | null {
103
+ try {
104
+ return gitText(basePath, ["status", "--porcelain"]).trim() === "";
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+
110
+ function stashBlobEqualsWorktreeFile(basePath: string, stashRef: string, path: string): boolean | null {
111
+ try {
112
+ const worktreePath = join(basePath, path);
113
+ if (!existsSync(worktreePath)) return false;
114
+ const worktreeContent = readFileSync(worktreePath);
115
+ const stashContent = gitBuffer(basePath, ["show", `${stashRef}^3:${path}`]);
116
+ return Buffer.compare(worktreeContent, stashContent) === 0;
117
+ } catch {
118
+ return null;
119
+ }
120
+ }
121
+
122
+ function reconcileAlreadyPresentUntrackedStash(
123
+ basePath: string,
124
+ milestoneId: string,
125
+ stashRef: string,
126
+ err: unknown,
127
+ ): PostflightResult | null {
128
+ const text = errorText(err);
129
+ const collidedPaths = parseAlreadyExistsNoCheckoutPaths(text);
130
+ if (collidedPaths.length === 0) return null;
131
+
132
+ const untrackedPaths = listStashUntrackedPaths(basePath, stashRef);
133
+ if (!untrackedPaths || untrackedPaths.length === 0) return null;
134
+
135
+ const trackedPaths = listStashTrackedPaths(basePath, stashRef);
136
+ if (trackedPaths === null || trackedPaths.length > 0) return null;
137
+
138
+ const untrackedPathSet = new Set(untrackedPaths);
139
+ if (!collidedPaths.every((path) => untrackedPathSet.has(path))) return null;
140
+ if (!untrackedPaths.every((path) => existsSync(join(basePath, path)))) return null;
141
+ if (isWorktreeClean(basePath) !== true) return null;
142
+
143
+ const blobComparisons = untrackedPaths.map((path) => stashBlobEqualsWorktreeFile(basePath, stashRef, path));
144
+ if (blobComparisons.some((result) => result === null)) return null;
145
+ const allIdentical = blobComparisons.every(Boolean);
146
+ if (allIdentical) {
147
+ let dropped = true;
148
+ try {
149
+ execFileSync("git", ["stash", "drop", stashRef], {
150
+ cwd: basePath,
151
+ stdio: ["ignore", "pipe", "pipe"],
152
+ encoding: "utf-8",
153
+ env: GIT_NO_PROMPT_ENV,
154
+ });
155
+ } catch (err) {
156
+ dropped = false;
157
+ logWarning("preflight", `git stash drop ${stashRef} failed after identical preflight stash reconciliation: ${err instanceof Error ? err.message : String(err)}`);
158
+ }
159
+ return {
160
+ restored: true,
161
+ needsManualRecovery: false,
162
+ message: dropped
163
+ ? `Preflight stash for milestone ${milestoneId} contained files already present after merge; identical stash dropped.`
164
+ : `Preflight stash for milestone ${milestoneId} contained files already present after merge, but ${stashRef} could not be dropped and remains as a backup.`,
165
+ stashRef,
166
+ resolution: dropped ? "already-present-dropped" : "already-present-preserved",
167
+ collidedPaths,
168
+ };
169
+ }
170
+
171
+ return {
172
+ restored: false,
173
+ needsManualRecovery: false,
174
+ message: `Preflight stash for milestone ${milestoneId} contained untracked files already present after merge. Keeping merged files and preserving ${stashRef} as a backup.`,
175
+ stashRef,
176
+ resolution: "already-present-preserved",
177
+ collidedPaths,
178
+ };
35
179
  }
36
180
 
37
181
  function findPreflightStashRef(basePath: string, milestoneId: string, stashMarker?: string): string | null {
@@ -141,27 +285,48 @@ export function postflightPopStash(
141
285
  message: msg,
142
286
  };
143
287
  }
144
- execFileSync("git", ["stash", "pop", stashRef], {
288
+ execFileSync("git", ["stash", "apply", stashRef], {
145
289
  cwd: basePath,
146
290
  stdio: ["ignore", "pipe", "pipe"],
147
291
  encoding: "utf-8",
148
292
  env: GIT_NO_PROMPT_ENV,
149
293
  });
294
+ let dropWarning: string | null = null;
295
+ try {
296
+ execFileSync("git", ["stash", "drop", stashRef], {
297
+ cwd: basePath,
298
+ stdio: ["ignore", "pipe", "pipe"],
299
+ encoding: "utf-8",
300
+ env: GIT_NO_PROMPT_ENV,
301
+ });
302
+ } catch (err) {
303
+ dropWarning = ` Stash was restored, but git stash drop ${stashRef} failed: ${err instanceof Error ? err.message : String(err)}.`;
304
+ logWarning("preflight", dropWarning.trim());
305
+ }
150
306
  const msg = `Restored stashed changes after milestone ${milestoneId} merge.`;
151
- notify(msg, "info");
307
+ notify(`${msg}${dropWarning ?? ""}`, dropWarning ? "warning" : "info");
152
308
  return {
153
309
  restored: true,
154
310
  needsManualRecovery: false,
155
- message: msg,
311
+ message: `${msg}${dropWarning ?? ""}`,
156
312
  stashRef,
313
+ resolution: "applied",
157
314
  };
158
315
  } catch (err) {
159
- // Pop conflicts mean the merged code collides with the stashed changes.
316
+ if (stashRef) {
317
+ const reconciled = reconcileAlreadyPresentUntrackedStash(basePath, milestoneId, stashRef, err);
318
+ if (reconciled) {
319
+ logWarning("preflight", reconciled.message);
320
+ notify(reconciled.message, reconciled.resolution === "already-present-preserved" ? "warning" : "info");
321
+ return reconciled;
322
+ }
323
+ }
324
+ // Apply conflicts mean the merged code collides with the stashed changes.
160
325
  // Log a warning — the user needs to resolve manually, but the merge succeeded.
161
326
  const restoreHint = stashRef
162
- ? `Run "git stash pop ${stashRef}" or "git stash apply ${stashRef}" manually to restore the correct stash.`
327
+ ? `Run "git stash apply ${stashRef}" manually to restore the correct stash, then "git stash drop ${stashRef}" after recovery.`
163
328
  : `Run "git stash list" to find the matching GSD preflight stash before restoring manually.`;
164
- const msg = `git stash pop ${stashRef ?? ""}`.trim() + ` failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. ${restoreHint}`;
329
+ const msg = `git stash apply ${stashRef ?? ""}`.trim() + ` failed after merge of milestone ${milestoneId}: ${err instanceof Error ? err.message : String(err)}. ${restoreHint}`;
165
330
  logWarning("preflight", msg);
166
331
  notify(msg, "warning");
167
332
  return {
@@ -169,6 +334,7 @@ export function postflightPopStash(
169
334
  needsManualRecovery: true,
170
335
  message: msg,
171
336
  ...(stashRef ? { stashRef } : {}),
337
+ resolution: "manual-recovery",
172
338
  };
173
339
  }
174
340
  }