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
@@ -44,6 +44,7 @@ import {
44
44
  nativeDetectMainBranch,
45
45
  nativeCheckoutBranch,
46
46
  nativeBranchList,
47
+ nativeBranchExists,
47
48
  nativeBranchListMerged,
48
49
  nativeBranchDelete,
49
50
  nativeWorktreeRemove,
@@ -64,7 +65,7 @@ import { initRoutingHistory } from "./routing-history.js";
64
65
  import { restoreHookState, resetHookState } from "./post-unit-hooks.js";
65
66
  import { resetProactiveHealing, setLevelChangeCallback } from "./doctor-proactive.js";
66
67
  import { snapshotSkills } from "./skill-discovery.js";
67
- import { isDbAvailable, getMilestone, openDatabase, getDbStatus } from "./gsd-db.js";
68
+ import { isDbAvailable, getMilestone, getAllMilestones, openDatabase, getDbStatus } from "./gsd-db.js";
68
69
  import { isClosedStatus } from "./status-guards.js";
69
70
  import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.js";
70
71
  import { auditOrphanedPreflightStashes } from "./orphan-stash-audit.js";
@@ -101,6 +102,7 @@ import { getSessionModelOverride } from "./session-model-override.js";
101
102
  export interface BootstrapDeps {
102
103
  shouldUseWorktreeIsolation: (basePath?: string) => boolean;
103
104
  registerSigtermHandler: (basePath: string) => void;
105
+ registerAutoWorkerForSession: (basePath: string) => void;
104
106
  lockBase: () => string;
105
107
  buildLifecycle: () => WorktreeLifecycle;
106
108
  }
@@ -202,9 +204,15 @@ export function decideSurvivorAction(
202
204
  export function auditOrphanedMilestoneBranches(
203
205
  basePath: string,
204
206
  isolationMode: "worktree" | "branch" | "none",
207
+ gitDeps: {
208
+ branchList?: typeof nativeBranchList;
209
+ branchExists?: typeof nativeBranchExists;
210
+ } = {},
205
211
  ): { recovered: string[]; warnings: string[] } {
206
212
  const recovered: string[] = [];
207
213
  const warnings: string[] = [];
214
+ const branchList = gitDeps.branchList ?? nativeBranchList;
215
+ const branchExists = gitDeps.branchExists ?? nativeBranchExists;
208
216
 
209
217
  // Skip in none mode — no milestone branches are created
210
218
  if (isolationMode === "none") return { recovered, warnings };
@@ -213,15 +221,16 @@ export function auditOrphanedMilestoneBranches(
213
221
  if (!isDbAvailable()) return { recovered, warnings };
214
222
 
215
223
  let milestoneBranches: string[];
224
+ let milestoneBranchListAvailable = true;
216
225
  try {
217
- milestoneBranches = nativeBranchList(basePath, "milestone/*");
226
+ milestoneBranches = branchList(basePath, "milestone/*");
218
227
  } catch {
219
- // git branch list failed — skip audit
220
- return { recovered, warnings };
228
+ milestoneBranchListAvailable = false;
229
+ // git branch list failed — fall through with an empty branch set so the
230
+ // branch-less orphan pass can still run after per-milestone verification.
231
+ milestoneBranches = [];
221
232
  }
222
233
 
223
- if (milestoneBranches.length === 0) return { recovered, warnings };
224
-
225
234
  // Detect main branch for merge-check
226
235
  let mainBranch: string;
227
236
  try {
@@ -354,6 +363,74 @@ export function auditOrphanedMilestoneBranches(
354
363
  }
355
364
  }
356
365
 
366
+ // Second pass (#5879): catch worktree directories stranded by a previous
367
+ // audit that deleted the milestone/* branch but failed to remove the
368
+ // directory (or the dir was orphaned by a separate path entirely, e.g.
369
+ // postflight-stash-restore-failed during closeout). The branch-keyed loop
370
+ // above is invisible to these cases — `nativeBranchList` returns nothing
371
+ // for the milestone, so the dir-cleanup block at line ~310 is never
372
+ // reached.
373
+ //
374
+ // Keyed on milestones whose DB status is `complete`. We do not iterate
375
+ // over arbitrary directories under .gsd/worktrees/ to avoid touching
376
+ // dirs that belong to an in-progress milestone whose branch was deleted
377
+ // separately — those are handled by the in-progress orphan path above
378
+ // when the branch is present, and by `/gsd doctor` when it is not.
379
+ const seenMilestoneIds = new Set(
380
+ milestoneBranches.map((branch) => branch.replace(/^milestone\//, "")),
381
+ );
382
+ let completedMilestones: readonly { id: string; status: string }[] = [];
383
+ try {
384
+ completedMilestones = getAllMilestones();
385
+ } catch {
386
+ // DB read failure — skip the second pass; the first pass is still useful.
387
+ completedMilestones = [];
388
+ }
389
+ for (const m of completedMilestones) {
390
+ if (m.status !== "complete") continue;
391
+ if (seenMilestoneIds.has(m.id)) continue; // already processed in the branch loop
392
+ if (!milestoneBranchListAvailable) {
393
+ try {
394
+ if (branchExists(basePath, `milestone/${m.id}`)) continue;
395
+ } catch (err) {
396
+ warnings.push(
397
+ `Could not verify whether milestone/${m.id} still exists; skipping branch-less worktree cleanup for safety: ${err instanceof Error ? err.message : String(err)}`,
398
+ );
399
+ continue;
400
+ }
401
+ }
402
+ const wtDir = getWorktreeDir(basePath, m.id);
403
+ if (!existsSync(wtDir)) continue;
404
+ if (!isInsideWorktreesDir(basePath, wtDir)) {
405
+ warnings.push(
406
+ `Orphaned worktree directory for ${m.id} is outside .gsd/worktrees/ — skipping removal for safety.`,
407
+ );
408
+ continue;
409
+ }
410
+ // Try `git worktree remove` first in case the dir is still registered
411
+ // (defensive — usually it is not when we reach this branch-less pass).
412
+ try {
413
+ nativeWorktreeRemove(basePath, wtDir, true);
414
+ } catch (e) {
415
+ logWarning(
416
+ "engine",
417
+ `worktree remove failed (expected for branch-less orphans): ${e instanceof Error ? e.message : String(e)}`,
418
+ );
419
+ }
420
+ if (existsSync(wtDir)) {
421
+ try {
422
+ rmSync(wtDir, { recursive: true, force: true });
423
+ recovered.push(`Removed orphaned worktree directory for ${m.id} (branch already deleted).`);
424
+ } catch (err) {
425
+ warnings.push(
426
+ `Failed to remove orphaned worktree directory for ${m.id}: ${err instanceof Error ? err.message : String(err)}`,
427
+ );
428
+ }
429
+ } else {
430
+ recovered.push(`Removed orphaned worktree directory for ${m.id} (branch already deleted).`);
431
+ }
432
+ }
433
+
357
434
  return { recovered, warnings };
358
435
  }
359
436
 
@@ -533,6 +610,7 @@ export async function bootstrapAutoSession(
533
610
  const {
534
611
  shouldUseWorktreeIsolation,
535
612
  registerSigtermHandler,
613
+ registerAutoWorkerForSession,
536
614
  lockBase,
537
615
  buildLifecycle,
538
616
  } = deps;
@@ -722,6 +800,7 @@ export async function bootstrapAutoSession(
722
800
  // consult DB status and avoid clearing runtime units for milestones that
723
801
  // only have a failure-path SUMMARY on disk (#4663).
724
802
  await openProjectDbIfPresent(base);
803
+ registerAutoWorkerForSession(base);
725
804
 
726
805
  // Clean stale runtime unit files for completed milestones (#887).
727
806
  // DB-authoritative: when DB is available, require DB status to be closed
@@ -818,14 +897,18 @@ export async function bootstrapAutoSession(
818
897
  // worktree cleanup) was never run — the survivor branch must be merged.
819
898
  // Applies to both worktree and branch isolation modes.
820
899
  let hasSurvivorBranch = false;
900
+ let survivorMilestoneId = state.activeMilestone?.id ?? null;
901
+ if (!survivorMilestoneId && state.phase === "complete") {
902
+ survivorMilestoneId = findUnmergedCompletedMilestone(base, getIsolationMode(base));
903
+ }
821
904
  if (
822
- state.activeMilestone &&
905
+ survivorMilestoneId &&
823
906
  (state.phase === "pre-planning" || state.phase === "complete") &&
824
907
  getIsolationMode(base) !== "none" &&
825
908
  !detectWorktreeName(base) &&
826
909
  !base.includes(`${pathSep}.gsd${pathSep}worktrees${pathSep}`)
827
910
  ) {
828
- const milestoneBranch = `milestone/${state.activeMilestone.id}`;
911
+ const milestoneBranch = `milestone/${survivorMilestoneId}`;
829
912
  const { nativeBranchExists } = await import("./native-git-bridge.js");
830
913
  hasSurvivorBranch = nativeBranchExists(base, milestoneBranch);
831
914
  if (hasSurvivorBranch) {
@@ -869,7 +952,7 @@ export async function bootstrapAutoSession(
869
952
  // Re-evaluate via the helper — the discuss branch above may have cleared
870
953
  // hasSurvivorBranch after a successful promotion.
871
954
  if (decideSurvivorAction(hasSurvivorBranch, state.phase) === "finalize") {
872
- const mid = state.activeMilestone!.id;
955
+ const mid = survivorMilestoneId!;
873
956
  // Commit 68ef58a3c made `_mergeBranchMode` throw on wrong-branch
874
957
  // instead of returning false silently. Wrap the call so the throw is
875
958
  // converted into an error notify + clean bootstrap abort, not an
@@ -252,7 +252,17 @@ function gitPathspecForWorktreePath(basePath: string, targetPath: string): strin
252
252
  let base = basePath;
253
253
  let target = targetPath;
254
254
  try {
255
- base = realpathSync.native(basePath);
255
+ base = execFileSync("git", ["rev-parse", "--show-toplevel"], {
256
+ cwd: basePath,
257
+ stdio: ["ignore", "pipe", "ignore"],
258
+ encoding: "utf-8",
259
+ }).trim() || basePath;
260
+ } catch {
261
+ /* keep original */
262
+ void base;
263
+ }
264
+ try {
265
+ base = realpathSync.native(base);
256
266
  } catch {
257
267
  /* keep original */
258
268
  void base;
@@ -269,6 +279,10 @@ function gitPathspecForWorktreePath(basePath: string, targetPath: string): strin
269
279
  return rel.replaceAll("\\", "/");
270
280
  }
271
281
 
282
+ export function _gitPathspecForWorktreePath(basePath: string, targetPath: string): string | null {
283
+ return gitPathspecForWorktreePath(basePath, targetPath);
284
+ }
285
+
272
286
  function gitRemoteExists(basePath: string, remote: string): boolean {
273
287
  try {
274
288
  execFileSync("git", ["remote", "get-url", remote], {
@@ -282,6 +296,50 @@ function gitRemoteExists(basePath: string, remote: string): boolean {
282
296
  }
283
297
  }
284
298
 
299
+ function findRegularMergeChangedPaths(basePath: string, milestoneBranch: string, mainBranch: string): Set<string> {
300
+ const changedPaths = new Set<string>();
301
+ let mergeLog = "";
302
+ try {
303
+ mergeLog = execFileSync("git", ["rev-list", "--merges", "--parents", mainBranch], {
304
+ cwd: basePath,
305
+ stdio: ["ignore", "pipe", "pipe"],
306
+ encoding: "utf-8",
307
+ }).trim();
308
+ } catch (err) {
309
+ logWarning("worktree", `regular merge lookup failed: ${err instanceof Error ? err.message : String(err)}`);
310
+ return changedPaths;
311
+ }
312
+
313
+ for (const line of mergeLog.split("\n").filter(Boolean)) {
314
+ const [mergeCommit, firstParent, ...otherParents] = line.split(" ");
315
+ if (!mergeCommit || !firstParent || otherParents.length === 0) continue;
316
+ const mergedMilestone = otherParents.some((parent) => {
317
+ try {
318
+ return nativeIsAncestor(basePath, milestoneBranch, parent);
319
+ } catch {
320
+ return false;
321
+ }
322
+ });
323
+ if (!mergedMilestone) continue;
324
+
325
+ try {
326
+ const output = execFileSync("git", ["diff", "--name-only", firstParent, mergeCommit], {
327
+ cwd: basePath,
328
+ stdio: ["ignore", "pipe", "pipe"],
329
+ encoding: "utf-8",
330
+ }).trim();
331
+ for (const path of output.split("\n").filter(Boolean)) {
332
+ if (!path.startsWith(".gsd/")) changedPaths.add(path);
333
+ }
334
+ } catch (err) {
335
+ logWarning("worktree", `regular merge diff lookup failed: ${err instanceof Error ? err.message : String(err)}`);
336
+ }
337
+ return changedPaths;
338
+ }
339
+
340
+ return changedPaths;
341
+ }
342
+
285
343
  function clearProjectRootStateFiles(basePath: string, milestoneId: string): void {
286
344
  const gsdDir = gsdRoot(basePath);
287
345
  // Phase C pt 2: auto.lock removed from this list — the file is gone
@@ -1739,6 +1797,66 @@ export function mergeMilestoneToMain(
1739
1797
  }
1740
1798
  }
1741
1799
 
1800
+ // Already regular-merged milestones can skip the squash path and proceed to cleanup (#5831).
1801
+ if (nativeIsAncestor(originalBasePath_, milestoneBranch, mainBranch)) {
1802
+ const codeChanges = nativeDiffNumstat(
1803
+ originalBasePath_,
1804
+ mainBranch,
1805
+ milestoneBranch,
1806
+ ).filter((entry) => !entry.path.startsWith(".gsd/"));
1807
+ if (codeChanges.length > 0) {
1808
+ const regularMergeChangedPaths = findRegularMergeChangedPaths(
1809
+ originalBasePath_,
1810
+ milestoneBranch,
1811
+ mainBranch,
1812
+ );
1813
+ const unanchoredCodeChanges = codeChanges.filter((entry) =>
1814
+ regularMergeChangedPaths.has(entry.path)
1815
+ );
1816
+ if (unanchoredCodeChanges.length > 0) {
1817
+ process.chdir(previousCwd);
1818
+ throw new GSDError(
1819
+ GSD_GIT_ERROR,
1820
+ `Milestone branch "${milestoneBranch}" is reachable from "${mainBranch}" ` +
1821
+ `but has ${unanchoredCodeChanges.length} milestone-touched code file(s) not on current "${mainBranch}". ` +
1822
+ `Aborting worktree teardown to prevent data loss.`,
1823
+ );
1824
+ }
1825
+ }
1826
+ debugLog("mergeMilestoneToMain", {
1827
+ action: "skip-squash-already-merged",
1828
+ milestoneId,
1829
+ milestoneBranch,
1830
+ mainBranch,
1831
+ });
1832
+ try {
1833
+ clearProjectRootStateFiles(originalBasePath_, milestoneId);
1834
+ } catch (err) {
1835
+ logWarning("worktree", `clearProjectRootStateFiles failed during already-merged cleanup: ${err instanceof Error ? err.message : String(err)}`);
1836
+ }
1837
+ try {
1838
+ removeWorktree(originalBasePath_, milestoneId, {
1839
+ branch: milestoneBranch,
1840
+ deleteBranch: false,
1841
+ });
1842
+ } catch (err) {
1843
+ logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
1844
+ }
1845
+ try {
1846
+ nativeBranchDelete(originalBasePath_, milestoneBranch);
1847
+ } catch (err) {
1848
+ logWarning("worktree", `git branch-delete failed: ${err instanceof Error ? err.message : String(err)}`);
1849
+ }
1850
+ setActiveWorkspace(null);
1851
+ nudgeGitBranchCache(previousCwd);
1852
+ try {
1853
+ process.chdir(originalBasePath_);
1854
+ } catch (err) {
1855
+ logWarning("worktree", `chdir to project root after already-merged cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1856
+ }
1857
+ return { commitMessage, pushed: false, prCreated: false, codeFilesChanged: true };
1858
+ }
1859
+
1742
1860
  // 7. Shelter queued milestone directories before the squash merge (#2505).
1743
1861
  // The milestone branch may contain copies of queued milestone dirs (via
1744
1862
  // copyPlanningArtifacts), so `git merge --squash` rejects when those same
@@ -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";
@@ -1044,6 +1045,7 @@ export async function cleanupAfterLoopExit(ctx: ExtensionContext): Promise<void>
1044
1045
  // visible so the user still has a resumable auto-mode signal on screen.
1045
1046
  if (!s.paused) {
1046
1047
  ctx.ui.setStatus("gsd-auto", undefined);
1048
+ ctx.ui.setWidget("gsd-progress", undefined);
1047
1049
  if (s.completionStopInProgress) {
1048
1050
  s.completionStopInProgress = false;
1049
1051
  }
@@ -1839,6 +1841,13 @@ export function createWiredDispatchAdapter(
1839
1841
  modelRegistry,
1840
1842
  });
1841
1843
 
1844
+ if (action.action === "stop") {
1845
+ return {
1846
+ kind: "blocked",
1847
+ reason: action.reason,
1848
+ action: action.level === "warning" ? "pause" : "stop",
1849
+ };
1850
+ }
1842
1851
  if (action.action !== "dispatch") return null;
1843
1852
  return {
1844
1853
  unitType: action.unitType,
@@ -2065,6 +2074,18 @@ export function createWiredAutoOrchestrationModule(
2065
2074
  return createAutoOrchestrator(deps);
2066
2075
  }
2067
2076
 
2077
+ function notifyResumeBlocked(ctx: ExtensionContext, result: Extract<AutoAdvanceResult, { kind: "blocked" }>): void {
2078
+ const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
2079
+ ctx.ui.notify(`Auto-mode blocked: ${result.reason}. Fix and run ${resumeCmd} to resume.`, "warning");
2080
+ setLifecycleOutcome(ctx, {
2081
+ status: "blocked",
2082
+ title: "Auto-mode blocked",
2083
+ detail: result.reason,
2084
+ nextAction: `Fix the blocker, then run ${resumeCmd} to resume.`,
2085
+ commands: ["/gsd status for overview", `${resumeCmd} to resume`, "/gsd doctor to diagnose"],
2086
+ });
2087
+ }
2088
+
2068
2089
  function ensureOrchestrationModule(ctx: ExtensionContext, pi: ExtensionAPI, basePath: string): void {
2069
2090
  s.orchestration = createWiredAutoOrchestrationModule(ctx, pi, basePath, lockBase());
2070
2091
  }
@@ -2402,7 +2423,7 @@ export async function startAuto(
2402
2423
  // This closes the journal gap reported in #3348 where the worker wrote side
2403
2424
  // effects (SUMMARY.md, DB updates) but died before emitting unit-end.
2404
2425
  emitCrashRecoveredUnitEnd(base, freshStartAssessment.lock);
2405
- clearLock(base);
2426
+ clearStaleWorkerLock(base);
2406
2427
  }
2407
2428
 
2408
2429
  if (!s.paused) {
@@ -2473,6 +2494,8 @@ export async function startAuto(
2473
2494
  s.unitLifetimeDispatches.clear();
2474
2495
  if (!getLedger()) initMetrics(base);
2475
2496
  if (s.currentMilestoneId) setActiveMilestoneId(base, s.currentMilestoneId);
2497
+ await openProjectDbIfPresent(base);
2498
+ registerAutoWorkerForSession(s, base);
2476
2499
 
2477
2500
  // Re-register health level notification callback lost across process restart
2478
2501
  setLevelChangeCallback((_from, to, summary) => {
@@ -2580,7 +2603,12 @@ export async function startAuto(
2580
2603
  pi.events.emit(CMUX_CHANNELS.LOG, { preferences: loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, message: s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", level: "progress" });
2581
2604
 
2582
2605
  try {
2583
- await s.orchestration?.resume();
2606
+ const resumeResult = await s.orchestration?.resume();
2607
+ if (resumeResult?.kind === "blocked") {
2608
+ notifyResumeBlocked(ctx, resumeResult);
2609
+ await cleanupAfterLoopExit(ctx);
2610
+ return;
2611
+ }
2584
2612
  } catch (err) {
2585
2613
  debugLog("resume-orchestration-resume", { error: err instanceof Error ? err.message : String(err) });
2586
2614
  }
@@ -2601,6 +2629,7 @@ export async function startAuto(
2601
2629
  const bootstrapDeps: BootstrapDeps = {
2602
2630
  shouldUseWorktreeIsolation,
2603
2631
  registerSigtermHandler,
2632
+ registerAutoWorkerForSession: (projectRoot) => registerAutoWorkerForSession(s, projectRoot),
2604
2633
  lockBase,
2605
2634
  buildLifecycle,
2606
2635
  };
@@ -48,6 +48,11 @@ const MAX_NETWORK_RETRIES = 2;
48
48
  function isObjectRecord(value: unknown): value is Record<string, unknown> {
49
49
  return !!value && typeof value === "object";
50
50
  }
51
+
52
+ export function _hasEmptyAgentEndContent(content: unknown): boolean {
53
+ return content == null || (Array.isArray(content) && content.length === 0);
54
+ }
55
+
51
56
  /**
52
57
  * Cap on auto-resume attempts for sustained transient-provider errors.
53
58
  *
@@ -310,7 +315,7 @@ export async function handleAgentEnd(
310
315
  // that carry error context — e.g. errorMessage field or non-empty content
311
316
  // indicating a mid-stream failure. (#2695)
312
317
  const content = "content" in lastMsg ? lastMsg.content : undefined;
313
- const hasEmptyContent = Array.isArray(content) && content.length === 0;
318
+ const hasEmptyContent = _hasEmptyAgentEndContent(content);
314
319
  const hasErrorMessage = "errorMessage" in lastMsg && !!lastMsg.errorMessage;
315
320
 
316
321
  if (hasEmptyContent && !hasErrorMessage) {
@@ -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')" })),
@@ -4,7 +4,9 @@ export function extractSubagentAgentClasses(input: unknown): string[] {
4
4
  const agentClasses: string[] = [];
5
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
11
 
10
12
  const visitItems = (value: unknown): void => {
@@ -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,7 +808,7 @@ 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
 
@@ -862,6 +866,17 @@ export function shouldBlockPlanningUnit(
862
866
  }
863
867
 
864
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
+ }
865
880
  if (BASH_READ_ONLY_RE.test(pathOrCommand)) return { block: false };
866
881
  return {
867
882
  block: true,
@@ -1,6 +1,7 @@
1
1
  import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from "@gsd/pi-coding-agent";
2
2
  import type { Model } from "@gsd/pi-ai";
3
3
  import type { GSDState } from "../../types.js";
4
+ import { createRequire } from "node:module";
4
5
 
5
6
  import { computeProgressScore, formatProgressLine } from "../../progress-score.js";
6
7
  import { loadEffectiveGSDPreferences, getGlobalGSDPreferencesPath, getProjectGSDPreferencesPath } from "../../preferences.js";
@@ -220,7 +221,22 @@ export async function handleBrief(args: string, ctx: ExtensionCommandContext, pi
220
221
  }
221
222
 
222
223
  const outputDir = getVisualBriefOutputDir();
223
- pi.sendUserMessage(buildVisualBriefPrompt(request, { outputDir }));
224
+ const version = resolveGsdVersion();
225
+ pi.sendUserMessage(buildVisualBriefPrompt(request, { outputDir, version }));
226
+ }
227
+
228
+ const briefRequire = createRequire(import.meta.url);
229
+
230
+ function resolveGsdVersion(): string | undefined {
231
+ const envVersion = process.env.GSD_VERSION?.trim();
232
+ if (envVersion) return envVersion;
233
+ try {
234
+ const pkg = briefRequire("../../../../../../package.json") as { version?: unknown };
235
+ const fromPkg = typeof pkg.version === "string" ? pkg.version.trim() : "";
236
+ return fromPkg || undefined;
237
+ } catch {
238
+ return undefined;
239
+ }
224
240
  }
225
241
 
226
242
  export async function handleSetup(args: string, ctx: ExtensionCommandContext, pi?: ExtensionAPI): Promise<void> {
@@ -30,9 +30,10 @@ import { join } from "node:path";
30
30
  import {
31
31
  findStaleWorkerForProject,
32
32
  getAllAutoWorkers,
33
+ markWorkerCrashed,
33
34
  type AutoWorkerRow,
34
35
  } from "./db/auto-workers.js";
35
- import { getLatestForUnit, type DispatchStatus } from "./db/unit-dispatches.js";
36
+ import { markLatestActiveForWorkerCanceled, type DispatchStatus } from "./db/unit-dispatches.js";
36
37
  import { getRuntimeKv, setRuntimeKv, deleteRuntimeKv } from "./db/runtime-kv.js";
37
38
  import { _getAdapter, isDbAvailable } from "./gsd-db.js";
38
39
  import { gsdRoot, normalizeRealPath } from "./paths.js";
@@ -56,6 +57,15 @@ function lockPath(basePath: string): string {
56
57
  return join(gsdRoot(basePath), effectiveLockFile());
57
58
  }
58
59
 
60
+ function clearLegacyLockFile(basePath: string): void {
61
+ try {
62
+ const p = lockPath(basePath);
63
+ if (existsSync(p)) unlinkSync(p);
64
+ } catch {
65
+ // Best-effort.
66
+ }
67
+ }
68
+
59
69
  function readLegacyLock(basePath: string): LockData | null {
60
70
  try {
61
71
  const p = lockPath(basePath);
@@ -204,18 +214,34 @@ export function writeLock(
204
214
  * stale session-file pointer.
205
215
  */
206
216
  export function clearLock(basePath: string): void {
217
+ clearLegacyLockFile(basePath);
218
+
219
+ if (!isDbAvailable()) return;
207
220
  try {
208
- const p = lockPath(basePath);
209
- if (existsSync(p)) unlinkSync(p);
221
+ const projectRoot = normalizeRealPath(basePath);
222
+ const worker = findActiveWorkerForCurrentProcess(projectRoot);
223
+ if (!worker) return;
224
+ deleteRuntimeKv("worker", worker.worker_id, SESSION_FILE_KV_KEY);
210
225
  } catch {
211
226
  // Best-effort.
212
227
  }
228
+ }
229
+
230
+ /**
231
+ * Clear a stale DB-backed worker lock after readCrashLock/findStaleWorkerForProject
232
+ * has identified a dead worker. Unlike clearLock(), this targets the stale
233
+ * worker row instead of the current process's active worker.
234
+ */
235
+ export function clearStaleWorkerLock(basePath: string): void {
236
+ clearLegacyLockFile(basePath);
213
237
 
214
238
  if (!isDbAvailable()) return;
215
239
  try {
216
240
  const projectRoot = normalizeRealPath(basePath);
217
- const worker = findActiveWorkerForCurrentProcess(projectRoot);
241
+ const worker = findStaleWorkerForProject(projectRoot);
218
242
  if (!worker) return;
243
+ markLatestActiveForWorkerCanceled(worker.worker_id, "crash-recovered");
244
+ markWorkerCrashed(worker.worker_id);
219
245
  deleteRuntimeKv("worker", worker.worker_id, SESSION_FILE_KV_KEY);
220
246
  } catch {
221
247
  // Best-effort.