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
@@ -173,7 +173,18 @@ function gitPathspecForWorktreePath(basePath, targetPath) {
173
173
  let base = basePath;
174
174
  let target = targetPath;
175
175
  try {
176
- base = realpathSync.native(basePath);
176
+ base = execFileSync("git", ["rev-parse", "--show-toplevel"], {
177
+ cwd: basePath,
178
+ stdio: ["ignore", "pipe", "ignore"],
179
+ encoding: "utf-8",
180
+ }).trim() || basePath;
181
+ }
182
+ catch {
183
+ /* keep original */
184
+ void base;
185
+ }
186
+ try {
187
+ base = realpathSync.native(base);
177
188
  }
178
189
  catch {
179
190
  /* keep original */
@@ -191,6 +202,9 @@ function gitPathspecForWorktreePath(basePath, targetPath) {
191
202
  return null;
192
203
  return rel.replaceAll("\\", "/");
193
204
  }
205
+ export function _gitPathspecForWorktreePath(basePath, targetPath) {
206
+ return gitPathspecForWorktreePath(basePath, targetPath);
207
+ }
194
208
  function gitRemoteExists(basePath, remote) {
195
209
  try {
196
210
  execFileSync("git", ["remote", "get-url", remote], {
@@ -204,6 +218,52 @@ function gitRemoteExists(basePath, remote) {
204
218
  return false;
205
219
  }
206
220
  }
221
+ function findRegularMergeChangedPaths(basePath, milestoneBranch, mainBranch) {
222
+ const changedPaths = new Set();
223
+ let mergeLog = "";
224
+ try {
225
+ mergeLog = execFileSync("git", ["rev-list", "--merges", "--parents", mainBranch], {
226
+ cwd: basePath,
227
+ stdio: ["ignore", "pipe", "pipe"],
228
+ encoding: "utf-8",
229
+ }).trim();
230
+ }
231
+ catch (err) {
232
+ logWarning("worktree", `regular merge lookup failed: ${err instanceof Error ? err.message : String(err)}`);
233
+ return changedPaths;
234
+ }
235
+ for (const line of mergeLog.split("\n").filter(Boolean)) {
236
+ const [mergeCommit, firstParent, ...otherParents] = line.split(" ");
237
+ if (!mergeCommit || !firstParent || otherParents.length === 0)
238
+ continue;
239
+ const mergedMilestone = otherParents.some((parent) => {
240
+ try {
241
+ return nativeIsAncestor(basePath, milestoneBranch, parent);
242
+ }
243
+ catch {
244
+ return false;
245
+ }
246
+ });
247
+ if (!mergedMilestone)
248
+ continue;
249
+ try {
250
+ const output = execFileSync("git", ["diff", "--name-only", firstParent, mergeCommit], {
251
+ cwd: basePath,
252
+ stdio: ["ignore", "pipe", "pipe"],
253
+ encoding: "utf-8",
254
+ }).trim();
255
+ for (const path of output.split("\n").filter(Boolean)) {
256
+ if (!path.startsWith(".gsd/"))
257
+ changedPaths.add(path);
258
+ }
259
+ }
260
+ catch (err) {
261
+ logWarning("worktree", `regular merge diff lookup failed: ${err instanceof Error ? err.message : String(err)}`);
262
+ }
263
+ return changedPaths;
264
+ }
265
+ return changedPaths;
266
+ }
207
267
  function clearProjectRootStateFiles(basePath, milestoneId) {
208
268
  const gsdDir = gsdRoot(basePath);
209
269
  // Phase C pt 2: auto.lock removed from this list — the file is gone
@@ -1482,6 +1542,56 @@ export function mergeMilestoneToMain(originalBasePath_, milestoneId, roadmapCont
1482
1542
  });
1483
1543
  }
1484
1544
  }
1545
+ // Already regular-merged milestones can skip the squash path and proceed to cleanup (#5831).
1546
+ if (nativeIsAncestor(originalBasePath_, milestoneBranch, mainBranch)) {
1547
+ const codeChanges = nativeDiffNumstat(originalBasePath_, mainBranch, milestoneBranch).filter((entry) => !entry.path.startsWith(".gsd/"));
1548
+ if (codeChanges.length > 0) {
1549
+ const regularMergeChangedPaths = findRegularMergeChangedPaths(originalBasePath_, milestoneBranch, mainBranch);
1550
+ const unanchoredCodeChanges = codeChanges.filter((entry) => regularMergeChangedPaths.has(entry.path));
1551
+ if (unanchoredCodeChanges.length > 0) {
1552
+ process.chdir(previousCwd);
1553
+ throw new GSDError(GSD_GIT_ERROR, `Milestone branch "${milestoneBranch}" is reachable from "${mainBranch}" ` +
1554
+ `but has ${unanchoredCodeChanges.length} milestone-touched code file(s) not on current "${mainBranch}". ` +
1555
+ `Aborting worktree teardown to prevent data loss.`);
1556
+ }
1557
+ }
1558
+ debugLog("mergeMilestoneToMain", {
1559
+ action: "skip-squash-already-merged",
1560
+ milestoneId,
1561
+ milestoneBranch,
1562
+ mainBranch,
1563
+ });
1564
+ try {
1565
+ clearProjectRootStateFiles(originalBasePath_, milestoneId);
1566
+ }
1567
+ catch (err) {
1568
+ logWarning("worktree", `clearProjectRootStateFiles failed during already-merged cleanup: ${err instanceof Error ? err.message : String(err)}`);
1569
+ }
1570
+ try {
1571
+ removeWorktree(originalBasePath_, milestoneId, {
1572
+ branch: milestoneBranch,
1573
+ deleteBranch: false,
1574
+ });
1575
+ }
1576
+ catch (err) {
1577
+ logWarning("worktree", `worktree removal failed: ${err instanceof Error ? err.message : String(err)}`);
1578
+ }
1579
+ try {
1580
+ nativeBranchDelete(originalBasePath_, milestoneBranch);
1581
+ }
1582
+ catch (err) {
1583
+ logWarning("worktree", `git branch-delete failed: ${err instanceof Error ? err.message : String(err)}`);
1584
+ }
1585
+ setActiveWorkspace(null);
1586
+ nudgeGitBranchCache(previousCwd);
1587
+ try {
1588
+ process.chdir(originalBasePath_);
1589
+ }
1590
+ catch (err) {
1591
+ logWarning("worktree", `chdir to project root after already-merged cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
1592
+ }
1593
+ return { commitMessage, pushed: false, prCreated: false, codeFilesChanged: true };
1594
+ }
1485
1595
  // 7. Shelter queued milestone directories before the squash merge (#2505).
1486
1596
  // The milestone branch may contain copies of queued milestone dirs (via
1487
1597
  // copyPlanningArtifacts), so `git merge --squash` rejects when those same
@@ -22,7 +22,7 @@ import { gsdRoot, resolveMilestoneFile, resolveMilestonePath, resolveDir, milest
22
22
  import { invalidateAllCaches } from "./cache.js";
23
23
  import { clearActivityLogState } from "./activity-log.js";
24
24
  import { synthesizeCrashRecovery, getDeepDiagnostic, readActiveMilestoneId, } from "./session-forensics.js";
25
- import { writeLock, clearLock, readCrashLock, isLockProcessAlive, formatCrashInfo, emitCrashRecoveredUnitEnd, emitOpenUnitEndForUnit, } from "./crash-recovery.js";
25
+ import { writeLock, clearLock, clearStaleWorkerLock, readCrashLock, isLockProcessAlive, formatCrashInfo, emitCrashRecoveredUnitEnd, emitOpenUnitEndForUnit, } from "./crash-recovery.js";
26
26
  import { acquireSessionLock, getSessionLockStatus, releaseSessionLock, updateSessionLock, } from "./session-lock.js";
27
27
  import { resolveAutoSupervisorConfig, loadEffectiveGSDPreferences, getIsolationMode, } from "./preferences.js";
28
28
  import { sendDesktopNotification } from "./notifications.js";
@@ -707,6 +707,7 @@ export async function cleanupAfterLoopExit(ctx) {
707
707
  // visible so the user still has a resumable auto-mode signal on screen.
708
708
  if (!s.paused) {
709
709
  ctx.ui.setStatus("gsd-auto", undefined);
710
+ ctx.ui.setWidget("gsd-progress", undefined);
710
711
  if (s.completionStopInProgress) {
711
712
  s.completionStopInProgress = false;
712
713
  }
@@ -1420,6 +1421,13 @@ export function createWiredDispatchAdapter(ctx, pi, dispatchBasePath) {
1420
1421
  sessionProvider,
1421
1422
  modelRegistry,
1422
1423
  });
1424
+ if (action.action === "stop") {
1425
+ return {
1426
+ kind: "blocked",
1427
+ reason: action.reason,
1428
+ action: action.level === "warning" ? "pause" : "stop",
1429
+ };
1430
+ }
1423
1431
  if (action.action !== "dispatch")
1424
1432
  return null;
1425
1433
  return {
@@ -1640,6 +1648,17 @@ export function createWiredAutoOrchestrationModule(ctx, pi, dispatchBasePath, ru
1640
1648
  };
1641
1649
  return createAutoOrchestrator(deps);
1642
1650
  }
1651
+ function notifyResumeBlocked(ctx, result) {
1652
+ const resumeCmd = s.stepMode ? "/gsd next" : "/gsd auto";
1653
+ ctx.ui.notify(`Auto-mode blocked: ${result.reason}. Fix and run ${resumeCmd} to resume.`, "warning");
1654
+ setLifecycleOutcome(ctx, {
1655
+ status: "blocked",
1656
+ title: "Auto-mode blocked",
1657
+ detail: result.reason,
1658
+ nextAction: `Fix the blocker, then run ${resumeCmd} to resume.`,
1659
+ commands: ["/gsd status for overview", `${resumeCmd} to resume`, "/gsd doctor to diagnose"],
1660
+ });
1661
+ }
1643
1662
  function ensureOrchestrationModule(ctx, pi, basePath) {
1644
1663
  s.orchestration = createWiredAutoOrchestrationModule(ctx, pi, basePath, lockBase());
1645
1664
  }
@@ -1924,7 +1943,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1924
1943
  // This closes the journal gap reported in #3348 where the worker wrote side
1925
1944
  // effects (SUMMARY.md, DB updates) but died before emitting unit-end.
1926
1945
  emitCrashRecoveredUnitEnd(base, freshStartAssessment.lock);
1927
- clearLock(base);
1946
+ clearStaleWorkerLock(base);
1928
1947
  }
1929
1948
  if (!s.paused) {
1930
1949
  s.pendingCrashRecovery =
@@ -1987,6 +2006,8 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
1987
2006
  initMetrics(base);
1988
2007
  if (s.currentMilestoneId)
1989
2008
  setActiveMilestoneId(base, s.currentMilestoneId);
2009
+ await openProjectDbIfPresent(base);
2010
+ registerAutoWorkerForSession(s, base);
1990
2011
  // Re-register health level notification callback lost across process restart
1991
2012
  setLevelChangeCallback((_from, to, summary) => {
1992
2013
  const level = to === "red" ? "error" : to === "yellow" ? "warning" : "info";
@@ -2062,7 +2083,12 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
2062
2083
  }
2063
2084
  pi.events.emit(CMUX_CHANNELS.LOG, { preferences: loadEffectiveGSDPreferences(s.basePath || undefined)?.preferences, message: s.stepMode ? "Step-mode resumed." : "Auto-mode resumed.", level: "progress" });
2064
2085
  try {
2065
- await s.orchestration?.resume();
2086
+ const resumeResult = await s.orchestration?.resume();
2087
+ if (resumeResult?.kind === "blocked") {
2088
+ notifyResumeBlocked(ctx, resumeResult);
2089
+ await cleanupAfterLoopExit(ctx);
2090
+ return;
2091
+ }
2066
2092
  }
2067
2093
  catch (err) {
2068
2094
  debugLog("resume-orchestration-resume", { error: err instanceof Error ? err.message : String(err) });
@@ -2083,6 +2109,7 @@ export async function startAuto(ctx, pi, base, verboseMode, options) {
2083
2109
  const bootstrapDeps = {
2084
2110
  shouldUseWorktreeIsolation,
2085
2111
  registerSigtermHandler,
2112
+ registerAutoWorkerForSession: (projectRoot) => registerAutoWorkerForSession(s, projectRoot),
2086
2113
  lockBase,
2087
2114
  buildLifecycle,
2088
2115
  };
@@ -18,6 +18,9 @@ const MAX_NETWORK_RETRIES = 2;
18
18
  function isObjectRecord(value) {
19
19
  return !!value && typeof value === "object";
20
20
  }
21
+ export function _hasEmptyAgentEndContent(content) {
22
+ return content == null || (Array.isArray(content) && content.length === 0);
23
+ }
21
24
  /**
22
25
  * Cap on auto-resume attempts for sustained transient-provider errors.
23
26
  *
@@ -246,7 +249,7 @@ export async function handleAgentEnd(pi, event, ctx) {
246
249
  // that carry error context — e.g. errorMessage field or non-empty content
247
250
  // indicating a mid-stream failure. (#2695)
248
251
  const content = "content" in lastMsg ? lastMsg.content : undefined;
249
- const hasEmptyContent = Array.isArray(content) && content.length === 0;
252
+ const hasEmptyContent = _hasEmptyAgentEndContent(content);
250
253
  const hasErrorMessage = "errorMessage" in lastMsg && !!lastMsg.errorMessage;
251
254
  if (hasEmptyContent && !hasErrorMessage) {
252
255
  // Non-fatal: treat as a normal agent end so the loop can continue
@@ -468,17 +468,18 @@ export function registerDbTools(pi) {
468
468
  promptGuidelines: [
469
469
  "Use gsd_plan_milestone for milestone planning instead of writing ROADMAP.md directly.",
470
470
  "Keep parameters flat and provide the full milestone planning payload, including slices.",
471
+ "Milestone and slice titles must not contain forward slash (/), en dash, or em dash characters.",
471
472
  "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.",
472
473
  "Use the canonical name gsd_plan_milestone; gsd_milestone_plan is only an alias.",
473
474
  ],
474
475
  parameters: Type.Object({
475
476
  // ── Core identification + content (required) ──────────────────────
476
477
  milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
477
- title: Type.String({ description: "Milestone title" }),
478
+ title: Type.String({ description: "Milestone title; must not contain forward slash (/), en dash, or em dash characters" }),
478
479
  vision: Type.String({ description: "Milestone vision" }),
479
480
  slices: Type.Array(Type.Object({
480
481
  sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
481
- title: Type.String({ description: "Slice title" }),
482
+ title: Type.String({ description: "Slice title; must not contain forward slash (/), en dash, or em dash characters" }),
482
483
  risk: Type.String({ description: "Slice risk" }),
483
484
  depends: Type.Array(Type.String(), { description: "Slice dependency IDs" }),
484
485
  demo: Type.String({ description: "Roadmap demo text / After this" }),
@@ -546,10 +547,10 @@ export function registerDbTools(pi) {
546
547
  title: Type.String({ description: "Task title" }),
547
548
  description: Type.String({ description: "Task description / steps block" }),
548
549
  estimate: Type.String({ description: "Task estimate string" }),
549
- files: Type.Array(Type.String(), { description: "Files likely touched" }),
550
+ files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
550
551
  verify: Type.String({ description: "Verification command or block" }),
551
- inputs: Type.Array(Type.String(), { description: "Input files or references" }),
552
- expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
552
+ inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
553
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
553
554
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
554
555
  }), { description: "Planned tasks for the slice" }),
555
556
  // ── Enrichment metadata (optional — defaults to empty) ────────────
@@ -622,10 +623,10 @@ export function registerDbTools(pi) {
622
623
  title: Type.String({ description: "Task title" }),
623
624
  description: Type.String({ description: "Task description / steps block" }),
624
625
  estimate: Type.String({ description: "Task estimate string" }),
625
- files: Type.Array(Type.String(), { description: "Files likely touched" }),
626
+ files: Type.Array(Type.String(), { description: "Array<string> of files likely touched; pass [\"path\"] or [], never a single string" }),
626
627
  verify: Type.String({ description: "Verification command or block" }),
627
- inputs: Type.Array(Type.String(), { description: "Input files or references" }),
628
- expectedOutput: Type.Array(Type.String(), { description: "Expected output files or artifacts" }),
628
+ inputs: Type.Array(Type.String(), { description: "Array<string> of input files or references; pass [\"path\"] or [], never a single string" }),
629
+ expectedOutput: Type.Array(Type.String(), { description: "Array<string> of expected output files or artifacts; pass [\"path\"] or [], never a single string" }),
629
630
  observabilityImpact: Type.Optional(Type.String({ description: "Task observability impact" })),
630
631
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
631
632
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
@@ -4,8 +4,11 @@ export function extractSubagentAgentClasses(input) {
4
4
  const agentClasses = [];
5
5
  const visited = new WeakSet();
6
6
  const addAgentClass = (value) => {
7
- if (typeof value === "string" && value.trim().length > 0)
8
- agentClasses.push(value.trim());
7
+ if (typeof value !== "string")
8
+ return;
9
+ const normalized = value.trim().replace(/\.md$/i, "");
10
+ if (normalized.length > 0)
11
+ agentClasses.push(normalized);
9
12
  };
10
13
  const visitItems = (value) => {
11
14
  if (!Array.isArray(value))
@@ -59,6 +59,7 @@ const QUEUE_SAFE_TOOLS = new Set([
59
59
  * true / false — shell no-ops / test exit codes
60
60
  */
61
61
  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)/;
62
+ 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)/;
62
63
  function createEmptyWriteGateState() {
63
64
  return {
64
65
  verifiedDepthMilestones: new Set(),
@@ -643,6 +644,9 @@ function blockReason(unitType, mode, what) {
643
644
  * and listed in the policy's allowedSubagents.
644
645
  * - "docs" → like "planning" but also allows writes to paths
645
646
  * matching `allowedPathGlobs` relative to basePath.
647
+ * - "verification"
648
+ * → allows Bash for project verification commands, but keeps
649
+ * writes restricted to .gsd/ and blocks subagent dispatch.
646
650
  *
647
651
  * `pathOrCommand` is the file path for write/edit-shaped tools and the
648
652
  * shell command for bash. Other tools ignore this argument.
@@ -674,7 +678,7 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
674
678
  // Unknown tool in read-only mode — block by default.
675
679
  return { block: true, reason: blockReason(unitType, policy.mode, `tool "${tool}" is not on the read-only allowlist`) };
676
680
  }
677
- // planning / planning-dispatch / docs modes share the same surface for safe tools, bash, and subagent.
681
+ // planning / planning-dispatch / docs / verification modes share the same surface for safe tools, bash, and subagent.
678
682
  if (PLANNING_SAFE_TOOLS.has(tool))
679
683
  return { block: false };
680
684
  if (tool.startsWith("gsd_"))
@@ -720,6 +724,14 @@ export function shouldBlockPlanningUnit(toolName, pathOrCommand, basePath, unitT
720
724
  return { block: true, reason: blockReason(unitType, policy.mode, `subagent dispatch is not permitted in planning units`) };
721
725
  }
722
726
  if (tool === "bash") {
727
+ if (policy.mode === "verification") {
728
+ if (BASH_VERIFICATION_RE.test(pathOrCommand) || BASH_READ_ONLY_RE.test(pathOrCommand))
729
+ return { block: false };
730
+ return {
731
+ block: true,
732
+ reason: blockReason(unitType, policy.mode, `bash is restricted to build/test verification commands (npm run build, npm test, etc.); cannot run "${pathOrCommand.slice(0, 80)}${pathOrCommand.length > 80 ? "…" : ""}"`),
733
+ };
734
+ }
723
735
  if (BASH_READ_ONLY_RE.test(pathOrCommand))
724
736
  return { block: false };
725
737
  return {
@@ -1,3 +1,4 @@
1
+ import { createRequire } from "node:module";
1
2
  import { computeProgressScore, formatProgressLine } from "../../progress-score.js";
2
3
  import { getGlobalGSDPreferencesPath, getProjectGSDPreferencesPath } from "../../preferences.js";
3
4
  import { ensurePreferencesFile, handlePrefs, handlePrefsMode, handlePrefsWizard, handleLanguage } from "../../commands-prefs-wizard.js";
@@ -197,7 +198,22 @@ export async function handleBrief(args, ctx, pi) {
197
198
  return;
198
199
  }
199
200
  const outputDir = getVisualBriefOutputDir();
200
- pi.sendUserMessage(buildVisualBriefPrompt(request, { outputDir }));
201
+ const version = resolveGsdVersion();
202
+ pi.sendUserMessage(buildVisualBriefPrompt(request, { outputDir, version }));
203
+ }
204
+ const briefRequire = createRequire(import.meta.url);
205
+ function resolveGsdVersion() {
206
+ const envVersion = process.env.GSD_VERSION?.trim();
207
+ if (envVersion)
208
+ return envVersion;
209
+ try {
210
+ const pkg = briefRequire("../../../../../../package.json");
211
+ const fromPkg = typeof pkg.version === "string" ? pkg.version.trim() : "";
212
+ return fromPkg || undefined;
213
+ }
214
+ catch {
215
+ return undefined;
216
+ }
201
217
  }
202
218
  export async function handleSetup(args, ctx, pi) {
203
219
  const { detectProjectState, hasGlobalSetup } = await import("../../detection.js");
@@ -23,7 +23,8 @@
23
23
  import { emitJournalEvent, queryJournal, } from "./journal.js";
24
24
  import { readFileSync, unlinkSync, existsSync } from "node:fs";
25
25
  import { join } from "node:path";
26
- import { findStaleWorkerForProject, getAllAutoWorkers, } from "./db/auto-workers.js";
26
+ import { findStaleWorkerForProject, getAllAutoWorkers, markWorkerCrashed, } from "./db/auto-workers.js";
27
+ import { markLatestActiveForWorkerCanceled } from "./db/unit-dispatches.js";
27
28
  import { getRuntimeKv, setRuntimeKv, deleteRuntimeKv } from "./db/runtime-kv.js";
28
29
  import { _getAdapter, isDbAvailable } from "./gsd-db.js";
29
30
  import { gsdRoot, normalizeRealPath } from "./paths.js";
@@ -34,6 +35,16 @@ const SESSION_FILE_KV_KEY = "session_file";
34
35
  function lockPath(basePath) {
35
36
  return join(gsdRoot(basePath), effectiveLockFile());
36
37
  }
38
+ function clearLegacyLockFile(basePath) {
39
+ try {
40
+ const p = lockPath(basePath);
41
+ if (existsSync(p))
42
+ unlinkSync(p);
43
+ }
44
+ catch {
45
+ // Best-effort.
46
+ }
47
+ }
37
48
  function readLegacyLock(basePath) {
38
49
  try {
39
50
  const p = lockPath(basePath);
@@ -166,21 +177,36 @@ export function writeLock(basePath, unitType, unitId, sessionFile) {
166
177
  * stale session-file pointer.
167
178
  */
168
179
  export function clearLock(basePath) {
180
+ clearLegacyLockFile(basePath);
181
+ if (!isDbAvailable())
182
+ return;
169
183
  try {
170
- const p = lockPath(basePath);
171
- if (existsSync(p))
172
- unlinkSync(p);
184
+ const projectRoot = normalizeRealPath(basePath);
185
+ const worker = findActiveWorkerForCurrentProcess(projectRoot);
186
+ if (!worker)
187
+ return;
188
+ deleteRuntimeKv("worker", worker.worker_id, SESSION_FILE_KV_KEY);
173
189
  }
174
190
  catch {
175
191
  // Best-effort.
176
192
  }
193
+ }
194
+ /**
195
+ * Clear a stale DB-backed worker lock after readCrashLock/findStaleWorkerForProject
196
+ * has identified a dead worker. Unlike clearLock(), this targets the stale
197
+ * worker row instead of the current process's active worker.
198
+ */
199
+ export function clearStaleWorkerLock(basePath) {
200
+ clearLegacyLockFile(basePath);
177
201
  if (!isDbAvailable())
178
202
  return;
179
203
  try {
180
204
  const projectRoot = normalizeRealPath(basePath);
181
- const worker = findActiveWorkerForCurrentProcess(projectRoot);
205
+ const worker = findStaleWorkerForProject(projectRoot);
182
206
  if (!worker)
183
207
  return;
208
+ markLatestActiveForWorkerCanceled(worker.worker_id, "crash-recovered");
209
+ markWorkerCrashed(worker.worker_id);
184
210
  deleteRuntimeKv("worker", worker.worker_id, SESSION_FILE_KV_KEY);
185
211
  }
186
212
  catch {
@@ -391,16 +391,17 @@ export function getRecentUnitKeysForProjectRoot(projectRootRealpath, limit = 20)
391
391
  if (!isDbAvailable())
392
392
  return [];
393
393
  const db = _getAdapter();
394
- const rows = db.prepare(`SELECT ud.unit_id
394
+ const rows = db.prepare(`SELECT ud.unit_type, ud.unit_id
395
395
  FROM unit_dispatches ud
396
396
  INNER JOIN workers w ON w.worker_id = ud.worker_id
397
397
  WHERE w.project_root_realpath = :project_root_realpath
398
+ AND w.status != 'crashed'
398
399
  ORDER BY ud.started_at DESC, ud.id DESC
399
400
  LIMIT :limit`).all({
400
401
  ":project_root_realpath": projectRootRealpath,
401
402
  ":limit": limit,
402
403
  });
403
- return rows.reverse().map((r) => ({ key: r.unit_id }));
404
+ return rows.reverse().map((r) => ({ key: `${r.unit_type}/${r.unit_id}` }));
404
405
  }
405
406
  /**
406
407
  * Fetch dispatches for a milestone filtered by status. Useful for janitors
@@ -4,7 +4,7 @@ import { findMilestoneIds } from "./guided-flow.js";
4
4
  import { parseUnitId } from "./unit-id.js";
5
5
  import { isDbAvailable, getMilestoneSlices, getMilestone } from "./gsd-db.js";
6
6
  import { parseRoadmap } from "./parsers-legacy.js";
7
- import { isClosedStatus } from "./status-guards.js";
7
+ import { isClosedStatus, isSkippedForDispatch } from "./status-guards.js";
8
8
  import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.js";
9
9
  import { readFileSync } from "node:fs";
10
10
  const SLICE_DISPATCH_TYPES = new Set([
@@ -49,7 +49,7 @@ export function getPriorSliceCompletionBlocker(base, _mainBranch, unitType, unit
49
49
  // DB-backed projects must not treat SUMMARY.md as authoritative.
50
50
  if (isDbAvailable()) {
51
51
  const milestoneRow = getMilestone(mid);
52
- if (milestoneRow && isClosedStatus(milestoneRow.status))
52
+ if (milestoneRow && isSkippedForDispatch(milestoneRow.status))
53
53
  continue;
54
54
  }
55
55
  else {
@@ -5,7 +5,7 @@ import { milestonesDir, gsdRoot, resolveGsdRootFile } from "./paths.js";
5
5
  import { deriveState, isGhostMilestone, isReusableGhostMilestone } from "./state.js";
6
6
  import { saveFile } from "./files.js";
7
7
  import { nativeIsRepo, nativeForEachRef, nativeUpdateRef } from "./native-git-bridge.js";
8
- import { readCrashLock, isLockProcessAlive, clearLock } from "./crash-recovery.js";
8
+ import { readCrashLock, isLockProcessAlive, clearStaleWorkerLock } from "./crash-recovery.js";
9
9
  import { getActiveAutoWorkers } from "./db/auto-workers.js";
10
10
  import { normalizeRealPath } from "./paths.js";
11
11
  import { ensureGitignore, isGsdGitignored } from "./gitignore.js";
@@ -46,7 +46,7 @@ export async function checkRuntimeHealth(basePath, issues, fixesApplied, shouldF
46
46
  fixable: true,
47
47
  });
48
48
  if (shouldFix("stale_crash_lock")) {
49
- clearLock(basePath);
49
+ clearStaleWorkerLock(basePath);
50
50
  fixesApplied.push("cleared stale auto-mode worker state");
51
51
  }
52
52
  }
@@ -70,15 +70,32 @@ export async function checkRuntimeHealth(basePath, issues, fixesApplied, shouldF
70
70
  // heartbeat for this project?" — readCrashLock returns null for
71
71
  // healthy live workers (it surfaces stale ones only), so we must
72
72
  // consult getActiveAutoWorkers directly.
73
- const projectRoot = normalizeRealPath(basePath);
74
- const activeWorkers = getActiveAutoWorkers().filter((w) => w.project_root_realpath === projectRoot && isLockProcessAlive({
75
- pid: w.pid,
76
- startedAt: w.started_at,
77
- unitType: "starting",
78
- unitId: "bootstrap",
79
- unitStartedAt: w.started_at,
80
- }));
81
- const lockHolderAlive = activeWorkers.length > 0;
73
+ let lockHolderAlive = false;
74
+ try {
75
+ const projectRoot = normalizeRealPath(basePath);
76
+ for (const worker of getActiveAutoWorkers()) {
77
+ if (worker.project_root_realpath !== projectRoot)
78
+ continue;
79
+ try {
80
+ if (isLockProcessAlive({
81
+ pid: worker.pid,
82
+ startedAt: worker.started_at,
83
+ unitType: "starting",
84
+ unitId: "bootstrap",
85
+ unitStartedAt: worker.started_at,
86
+ })) {
87
+ lockHolderAlive = true;
88
+ break;
89
+ }
90
+ }
91
+ catch {
92
+ // Ignore malformed worker rows or transient PID probe failures.
93
+ }
94
+ }
95
+ }
96
+ catch {
97
+ // If worker lookup fails, continue with the stranded lock diagnosis.
98
+ }
82
99
  if (!lockHolderAlive) {
83
100
  issues.push({
84
101
  severity: "error",
@@ -12,37 +12,11 @@ import { GLOBAL_STATE_CODES } from "./doctor-types.js";
12
12
  import { checkGitHealth, checkRuntimeHealth, checkGlobalHealth, checkEngineHealth } from "./doctor-checks.js";
13
13
  import { checkEnvironmentHealth } from "./doctor-environment.js";
14
14
  import { runProviderChecks } from "./doctor-providers.js";
15
+ import { validateTitle } from "./validation.js";
15
16
  export { summarizeDoctorIssues, filterDoctorIssues, formatDoctorReport, formatDoctorIssuesForPrompt, formatDoctorReportJson } from "./doctor-format.js";
16
17
  export { runEnvironmentChecks, runFullEnvironmentChecks, formatEnvironmentReport } from "./doctor-environment.js";
17
18
  export { computeProgressScore, computeProgressScoreWithContext, formatProgressLine, formatProgressReport } from "./progress-score.js";
18
- /**
19
- * Characters that are used as delimiters in GSD state management documents
20
- * and should not appear in milestone or slice titles.
21
- *
22
- * - "\u2014" (em dash, U+2014): used as a display separator in STATE.md and other docs.
23
- * A title containing "\u2014" makes the separator ambiguous, corrupting state display
24
- * and confusing the LLM agent that reads and writes these files.
25
- * - "\u2013" (en dash, U+2013): visually similar to em dash; same ambiguity risk.
26
- * - "/" (forward slash, U+002F): used as the path separator in unit IDs (M001/S01)
27
- * and git branch names (gsd/M001/S01). A slash in a title can break path resolution.
28
- */
29
- const TITLE_DELIMITER_RE = /[\u2014\u2013\/]/; // em dash, en dash, forward slash
30
- /**
31
- * Check whether a milestone or slice title contains characters that conflict
32
- * with GSD's state document delimiter conventions.
33
- * Returns a human-readable description of the problem, or null if the title is safe.
34
- */
35
- export function validateTitle(title) {
36
- if (TITLE_DELIMITER_RE.test(title)) {
37
- const found = [];
38
- if (/[\u2014\u2013]/.test(title))
39
- found.push("em/en dash (\u2014 or \u2013)");
40
- if (/\//.test(title))
41
- found.push("forward slash (/)");
42
- return `title contains ${found.join(" and ")}, which conflict with GSD state document delimiters`;
43
- }
44
- return null;
45
- }
19
+ export { validateTitle } from "./validation.js";
46
20
  function validatePreferenceShape(preferences) {
47
21
  const issues = [];
48
22
  const listFields = ["always_use_skills", "prefer_skills", "avoid_skills", "custom_instructions"];