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
@@ -10,6 +10,7 @@ import { isAbsolute, join, resolve } from "node:path";
10
10
  import { getErrorMessage } from "../../error-utils.js";
11
11
  import { nativeAddPaths, nativeCheckoutTheirs, nativeCommit, nativeConflictFiles, nativeMergeAbort, nativeRebaseAbort, nativeResetHard, } from "../../native-git-bridge.js";
12
12
  import { logError, logWarning } from "../../workflow-logger.js";
13
+ import { isGsdWorktreePath } from "../../worktree-root.js";
13
14
  const SILENT_NOTIFY = () => { };
14
15
  function resolveGitDir(basePath) {
15
16
  try {
@@ -23,7 +24,11 @@ function resolveGitDir(basePath) {
23
24
  }
24
25
  }
25
26
  catch (err) {
26
- logWarning("recovery", `gitdir resolution failed: ${getErrorMessage(err)}`);
27
+ const message = getErrorMessage(err);
28
+ logWarning("recovery", `gitdir resolution failed: ${message}`);
29
+ if (isGsdWorktreePath(basePath)) {
30
+ throw new Error(`Worktree integrity failure: ${basePath} is not a valid git worktree (git rev-parse failed: ${message.split("\n")[0]}). Repair or recreate the worktree before retrying.`);
31
+ }
27
32
  }
28
33
  return join(basePath, ".git");
29
34
  }
@@ -1,12 +1,11 @@
1
1
  // Project/App: GSD-2
2
2
  // File Purpose: ADR-017 unregistered-milestone drift handler. Detects
3
3
  // milestones whose on-disk directory has meaningful content (ROADMAP/
4
- // CONTEXT/SUMMARY) but no DB row, then runs the markdown importer to
5
- // reconcile. PROJECT.md is the human-facing index the importer's source
6
- // of truth is the .gsd/milestones/ directory tree.
4
+ // CONTEXT/SUMMARY) but no DB row, then fails closed with an explicit recovery
5
+ // instruction. Markdown hierarchy import is reserved for operator-controlled
6
+ // migration/recovery commands, not automatic runtime reconciliation.
7
7
  import { existsSync } from "node:fs";
8
8
  import { getMilestone, isDbAvailable } from "../../gsd-db.js";
9
- import { migrateHierarchyToDb } from "../../md-importer.js";
10
9
  import { findMilestoneIds } from "../../milestone-ids.js";
11
10
  import { resolveMilestoneFile } from "../../paths.js";
12
11
  function milestoneHasContent(basePath, milestoneId) {
@@ -31,17 +30,13 @@ export function detectUnregisteredMilestoneDrift(_state, ctx) {
31
30
  return drifts;
32
31
  }
33
32
  /**
34
- * Repair: invoke the markdown importer. migrateHierarchyToDb walks the same
35
- * findMilestoneIds list the detector uses and INSERTs OR IGNOREs every
36
- * missing milestone (and its slices/tasks) idempotent under cap=2 retry.
37
- *
38
- * Note: even though we receive one record at a time, the importer is a
39
- * project-wide operation. Repeated invocation across multiple drift records
40
- * in the same pass is wasteful but safe; a future optimization could
41
- * coalesce by checking whether the importer has already run this pass.
33
+ * Repair intentionally fails closed. The project-root DB is authoritative at
34
+ * runtime; markdown-only milestones must be imported through an explicit
35
+ * migration/recovery command so operators opt into changing canonical state.
42
36
  */
43
- export function repairUnregisteredMilestone(_record, ctx) {
44
- migrateHierarchyToDb(ctx.basePath);
37
+ export function repairUnregisteredMilestone(record, _ctx) {
38
+ throw new Error(`Milestone ${record.milestoneId} exists only as markdown projection. ` +
39
+ "Runtime reconciliation will not import markdown into the authoritative DB; run explicit GSD recovery/migration if this markdown should repopulate the database.");
45
40
  }
46
41
  export const unregisteredMilestoneHandler = {
47
42
  kind: "unregistered-milestone",
@@ -1,14 +1,15 @@
1
1
  // Project/App: GSD-2
2
2
  // File Purpose: ADR-017 roadmap-divergence drift handler. Detects mismatches
3
- // between ROADMAP.md (parsed slice sequence + depends declarations) and the
4
- // DB slice rows for that milestone, then reconciles via the markdown
5
- // importer plus an explicit junction-table sync.
3
+ // between ROADMAP.md (parsed slice sequence, depends declarations, and
4
+ // checkboxes) and the DB slice rows for that milestone, then re-renders the
5
+ // ROADMAP projection from the authoritative DB rows.
6
6
  import { existsSync, readFileSync } from "node:fs";
7
- import { getMilestone, getMilestoneSlices, isDbAvailable, syncSliceDependencies, } from "../../gsd-db.js";
8
- import { migrateHierarchyToDb } from "../../md-importer.js";
7
+ import { getMilestone, getMilestoneSlices, isDbAvailable, } from "../../gsd-db.js";
8
+ import { renderRoadmapFromDb } from "../../markdown-renderer.js";
9
9
  import { findMilestoneIds } from "../../milestone-ids.js";
10
10
  import { parseRoadmap } from "../../parsers-legacy.js";
11
11
  import { resolveMilestoneFile } from "../../paths.js";
12
+ import { isClosedStatus } from "../../status-guards.js";
12
13
  function arraysEqual(a, b) {
13
14
  if (a.length !== b.length)
14
15
  return false;
@@ -30,8 +31,10 @@ function milestoneHasDivergence(basePath, milestoneId) {
30
31
  }
31
32
  const dbSlices = getMilestoneSlices(milestoneId);
32
33
  const dbSliceMap = new Map(dbSlices.map((s) => [s.id, s]));
34
+ const roadmapSliceIds = new Set();
33
35
  for (let i = 0; i < roadmap.slices.length; i++) {
34
36
  const roadmapSlice = roadmap.slices[i];
37
+ roadmapSliceIds.add(roadmapSlice.id);
35
38
  const expectedSequence = i + 1;
36
39
  const dbSlice = dbSliceMap.get(roadmapSlice.id);
37
40
  if (!dbSlice)
@@ -40,6 +43,12 @@ function milestoneHasDivergence(basePath, milestoneId) {
40
43
  return true;
41
44
  if (!arraysEqual(dbSlice.depends, roadmapSlice.depends))
42
45
  return true;
46
+ if (isClosedStatus(dbSlice.status) !== roadmapSlice.done)
47
+ return true;
48
+ }
49
+ for (const dbSlice of dbSlices) {
50
+ if (!roadmapSliceIds.has(dbSlice.id))
51
+ return true;
43
52
  }
44
53
  return false;
45
54
  }
@@ -59,26 +68,12 @@ export function detectRoadmapDivergenceDrift(_state, ctx) {
59
68
  return drifts;
60
69
  }
61
70
  /**
62
- * Repair a milestone's roadmap divergence:
63
- * 1. migrateHierarchyToDb upserts slice rows (sequence + depends JSON
64
- * update via ON CONFLICT DO UPDATE).
65
- * 2. syncSliceDependencies updates the junction table per slice — the
66
- * importer only writes the JSON column, not the relational view.
71
+ * Repair a milestone's roadmap divergence by regenerating the projection from
72
+ * DB rows. ROADMAP.md is a projection; runtime reconciliation must not import
73
+ * slice presence, sequence, dependencies, or checkbox state from markdown.
67
74
  */
68
- export function repairRoadmapDivergence(record, ctx) {
69
- migrateHierarchyToDb(ctx.basePath);
70
- const roadmapPath = resolveMilestoneFile(ctx.basePath, record.milestoneId, "ROADMAP");
71
- if (!roadmapPath || !existsSync(roadmapPath))
72
- return;
73
- try {
74
- const roadmap = parseRoadmap(readFileSync(roadmapPath, "utf-8"));
75
- for (const slice of roadmap.slices) {
76
- syncSliceDependencies(record.milestoneId, slice.id, slice.depends);
77
- }
78
- }
79
- catch {
80
- /* parse failure: detector will fire again next pass */
81
- }
75
+ export async function repairRoadmapDivergence(record, ctx) {
76
+ await renderRoadmapFromDb(ctx.basePath, record.milestoneId);
82
77
  }
83
78
  export const roadmapDivergenceHandler = {
84
79
  kind: "roadmap-divergence",
@@ -22,3 +22,7 @@ export function isDeferredStatus(status) {
22
22
  export function isInactiveStatus(status) {
23
23
  return isClosedStatus(status) || isDeferredStatus(status);
24
24
  }
25
+ /** Returns true when a prior milestone should not block dispatch ordering. */
26
+ export function isSkippedForDispatch(status) {
27
+ return isClosedStatus(status) || status === "parked" || isDeferredStatus(status);
28
+ }
@@ -99,11 +99,11 @@
99
99
  - "Improve UI"
100
100
 
101
101
  Each task should usually include:
102
- - Why: why this task exists / what part of the slice it closes
103
- - Files: the main files likely touched
104
- - Do: concrete implementation steps and important constraints
105
- - Verify: the command, test, or runtime check that proves it worked
106
- - Done when: a measurable acceptance condition
102
+ - description: why this task exists, concrete steps, and done-when acceptance
103
+ - files: JSON array of likely touched paths
104
+ - verify: the command, test, or runtime check that proves it worked
105
+ - inputs: JSON array of existing paths or prior task outputs this task consumes
106
+ - expectedOutput: JSON array of paths this task creates or overwrites
107
107
 
108
108
  Keep the checkbox line format exactly:
109
109
  - [ ] **T01: Title** `est:30m`
@@ -131,10 +131,13 @@
131
131
 
132
132
  Verify field rules:
133
133
  - MUST be a mechanically executable command: `npm test`, `grep -q "pattern" file`, `test -f path`
134
+ - MUST NOT use shell pipes, redirects, semicolons, backticks, command substitution, or output trimming
134
135
  - For content/document tasks: verify file existence, section count, YAML validity, or word count
135
136
  NOT exact phrasing, specific formulas, or "zero TBD" aspirational criteria
136
137
  - If no command can verify the output, write: "Manual review — file exists and is non-empty"
138
+ - BAD: `python3 -m pytest tests/ -q --tb=short 2>&1 | tail -5`
137
139
  - BAD: "Sections 3.1 and 3.2 exist with exact formulas. Zero TBD/TODO."
140
+ - GOOD: `python3 -m pytest tests/ -q --tb=short`
138
141
  - GOOD: `grep -c "^## " doc.md` returns >= 4 (4+ sections), `! grep -q "TBD\|TODO" doc.md`
139
142
 
140
143
  Integration closure rule:
@@ -72,7 +72,8 @@ skills_used:
72
72
  <!-- Every input MUST be a backtick-wrapped file path. These paths are machine-parsed to
73
73
  derive task dependencies — vague descriptions without paths break dependency detection.
74
74
  For the first task in a slice with no prior task outputs, list the existing source files
75
- this task reads or modifies. -->
75
+ this task reads or modifies.
76
+ Tool field: inputs must be an array of strings, e.g. ["src/index.ts"], never a single string. -->
76
77
 
77
78
  - `{{filePath}}` — {{whatThisTaskNeedsFromPriorWork}}
78
79
 
@@ -82,6 +83,7 @@ skills_used:
82
83
  or modifies. These paths are machine-parsed to derive task dependencies.
83
84
  This task should produce a real increment toward making the slice goal/demo true. A full
84
85
  slice plan should not be able to mark every task complete while the claimed slice behavior
85
- still does not work at the stated proof level. -->
86
+ still does not work at the stated proof level.
87
+ Tool field: expectedOutput must be an array of strings, e.g. ["src/index.ts"], never a single string. -->
86
88
 
87
89
  - `{{filePath}}` — {{whatThisTaskCreatesOrModifies}}
@@ -24,11 +24,11 @@ function renderMilestoneSummaryMarkdown(params, completedAt) {
24
24
  const keyFiles = params.keyFiles ?? [];
25
25
  const lessonsLearned = params.lessonsLearned ?? [];
26
26
  const keyDecisionsYaml = keyDecisions.length > 0
27
- ? keyDecisions.map(d => ` - ${d}`).join("\n")
28
- : " - (none)";
27
+ ? `\n${keyDecisions.map(d => ` - ${d}`).join("\n")}`
28
+ : " []";
29
29
  const keyFilesYaml = keyFiles.length > 0
30
- ? keyFiles.map(f => ` - ${f}`).join("\n")
31
- : " - (none)";
30
+ ? `\n${keyFiles.map(f => ` - ${f}`).join("\n")}`
31
+ : " []";
32
32
  const lessonsYaml = lessonsLearned.length > 0
33
33
  ? lessonsLearned.map(l => ` - ${l}`).join("\n")
34
34
  : " - (none)";
@@ -37,10 +37,8 @@ id: ${params.milestoneId}
37
37
  title: "${displayTitle}"
38
38
  status: complete
39
39
  completed_at: ${completedAt}
40
- key_decisions:
41
- ${keyDecisionsYaml}
42
- key_files:
43
- ${keyFilesYaml}
40
+ key_decisions:${keyDecisionsYaml}
41
+ key_files:${keyFilesYaml}
44
42
  lessons_learned:
45
43
  ${lessonsYaml}
46
44
  ---
@@ -65,11 +65,11 @@ function renderSliceSummaryMarkdown(params) {
65
65
  ? affects.map(a => ` - ${a}`).join("\n")
66
66
  : " []";
67
67
  const keyFilesYaml = keyFiles.length > 0
68
- ? keyFiles.map(f => ` - ${f}`).join("\n")
69
- : " - (none)";
68
+ ? `\n${keyFiles.map(f => ` - ${f}`).join("\n")}`
69
+ : " []";
70
70
  const keyDecisionsYaml = keyDecisions.length > 0
71
- ? keyDecisions.map(d => ` - ${d}`).join("\n")
72
- : " - (none)";
71
+ ? `\n${keyDecisions.map(d => ` - ${d}`).join("\n")}`
72
+ : " []";
73
73
  const patternsYaml = patternsEstablished.length > 0
74
74
  ? patternsEstablished.map(p => ` - ${p}`).join("\n")
75
75
  : " - (none)";
@@ -106,10 +106,8 @@ requires:
106
106
  ${requiresYaml}
107
107
  affects:
108
108
  ${affectsYaml}
109
- key_files:
110
- ${keyFilesYaml}
111
- key_decisions:
112
- ${keyDecisionsYaml}
109
+ key_files:${keyFilesYaml}
110
+ key_decisions:${keyDecisionsYaml}
113
111
  patterns_established:
114
112
  ${patternsYaml}
115
113
  observability_surfaces:
@@ -1,6 +1,6 @@
1
1
  import { clearParseCache } from "../files.js";
2
2
  import { isClosedStatus } from "../status-guards.js";
3
- import { isNonEmptyString, validateStringArray } from "../validation.js";
3
+ import { isNonEmptyString, validateStringArray, validateTitle } from "../validation.js";
4
4
  import { transaction, getMilestone, getMilestoneSlices, getSlice, insertMilestone, insertSlice, upsertMilestonePlanning, upsertSlicePlanning, } from "../gsd-db.js";
5
5
  import { invalidateStateCache } from "../state.js";
6
6
  import { renderRoadmapFromDb } from "../markdown-renderer.js";
@@ -77,6 +77,9 @@ function validateSlices(value) {
77
77
  seen.add(sliceId);
78
78
  if (!isNonEmptyString(title))
79
79
  throw new Error(`slices[${index}].title must be a non-empty string`);
80
+ const titleIssue = validateTitle(title);
81
+ if (titleIssue)
82
+ throw new Error(`slices[${index}].title is invalid: ${titleIssue}`);
80
83
  if (!isNonEmptyString(risk))
81
84
  throw new Error(`slices[${index}].risk must be a non-empty string`);
82
85
  if (!Array.isArray(depends) || depends.some((item) => !isNonEmptyString(item))) {
@@ -127,6 +130,9 @@ function validateParams(params) {
127
130
  throw new Error("title is required");
128
131
  if (!isNonEmptyString(params?.vision))
129
132
  throw new Error("vision is required");
133
+ const milestoneTitleIssue = validateTitle(params.title);
134
+ if (milestoneTitleIssue)
135
+ throw new Error(`title is invalid: ${milestoneTitleIssue}`);
130
136
  return {
131
137
  ...params,
132
138
  dependsOn: params.dependsOn ? validateStringArray(params.dependsOn, "dependsOn") : [],
@@ -1,7 +1,9 @@
1
+ import { existsSync, rmSync } from "node:fs";
2
+ import { join, relative } from "node:path";
1
3
  import { clearParseCache } from "../files.js";
2
4
  import { isClosedStatus, isDeferredStatus } from "../status-guards.js";
3
- import { isNonEmptyString } from "../validation.js";
4
- import { transaction, getMilestone, getSlice, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, updateSliceStatus, } from "../gsd-db.js";
5
+ import { isNonEmptyString, validateStringArray } from "../validation.js";
6
+ import { transaction, getMilestone, getSlice, getSliceTasks, insertTask, upsertSlicePlanning, upsertTaskPlanning, insertGateRow, updateSliceStatus, setSliceSketchFlag, deleteTask, deleteArtifactByPath, } from "../gsd-db.js";
5
7
  import { invalidateStateCache } from "../state.js";
6
8
  import { renderPlanFromDb } from "../markdown-renderer.js";
7
9
  import { renderAllProjections } from "../workflow-projections.js";
@@ -9,6 +11,8 @@ import { writeManifest } from "../workflow-manifest.js";
9
11
  import { appendEvent } from "../workflow-events.js";
10
12
  import { logWarning } from "../workflow-logger.js";
11
13
  import { validatePlanningPathScope } from "../planning-path-scope.js";
14
+ import { checkFilePathConsistency, checkTaskOrdering } from "../pre-execution-checks.js";
15
+ import { buildTaskFileName, gsdRoot, resolveTasksDir } from "../paths.js";
12
16
  function validateTasks(value) {
13
17
  if (!Array.isArray(value) || value.length === 0) {
14
18
  throw new Error("tasks must be a non-empty array");
@@ -39,17 +43,11 @@ function validateTasks(value) {
39
43
  throw new Error(`tasks[${index}].description must be a non-empty string`);
40
44
  if (!isNonEmptyString(estimate))
41
45
  throw new Error(`tasks[${index}].estimate must be a non-empty string`);
42
- if (!Array.isArray(files) || files.some((item) => !isNonEmptyString(item))) {
43
- throw new Error(`tasks[${index}].files must be an array of non-empty strings`);
44
- }
46
+ const validatedFiles = validateStringArray(files, `tasks[${index}].files`);
45
47
  if (!isNonEmptyString(verify))
46
48
  throw new Error(`tasks[${index}].verify must be a non-empty string`);
47
- if (!Array.isArray(inputs) || inputs.some((item) => !isNonEmptyString(item))) {
48
- throw new Error(`tasks[${index}].inputs must be an array of non-empty strings`);
49
- }
50
- if (!Array.isArray(expectedOutput) || expectedOutput.some((item) => !isNonEmptyString(item))) {
51
- throw new Error(`tasks[${index}].expectedOutput must be an array of non-empty strings`);
52
- }
49
+ const validatedInputs = validateStringArray(inputs, `tasks[${index}].inputs`);
50
+ const validatedExpectedOutput = validateStringArray(expectedOutput, `tasks[${index}].expectedOutput`);
53
51
  if (observabilityImpact !== undefined && !isNonEmptyString(observabilityImpact)) {
54
52
  throw new Error(`tasks[${index}].observabilityImpact must be a non-empty string when provided`);
55
53
  }
@@ -58,10 +56,10 @@ function validateTasks(value) {
58
56
  title,
59
57
  description,
60
58
  estimate,
61
- files,
59
+ files: validatedFiles,
62
60
  verify,
63
- inputs,
64
- expectedOutput,
61
+ inputs: validatedInputs,
62
+ expectedOutput: validatedExpectedOutput,
65
63
  observabilityImpact: typeof observabilityImpact === "string" ? observabilityImpact : "",
66
64
  };
67
65
  });
@@ -84,6 +82,53 @@ function validateParams(params) {
84
82
  tasks: validateTasks(params.tasks),
85
83
  };
86
84
  }
85
+ function toTaskRows(params) {
86
+ return params.tasks.map((task, index) => ({
87
+ milestone_id: params.milestoneId,
88
+ slice_id: params.sliceId,
89
+ id: task.taskId,
90
+ title: task.title,
91
+ status: "pending",
92
+ one_liner: "",
93
+ narrative: "",
94
+ verification_result: "",
95
+ duration: "",
96
+ completed_at: null,
97
+ blocker_discovered: false,
98
+ deviations: "",
99
+ known_issues: "",
100
+ key_files: [],
101
+ key_decisions: [],
102
+ full_summary_md: "",
103
+ description: task.description,
104
+ estimate: task.estimate,
105
+ files: task.files,
106
+ verify: task.verify,
107
+ inputs: task.inputs,
108
+ expected_output: task.expectedOutput,
109
+ observability_impact: task.observabilityImpact ?? "",
110
+ full_plan_md: task.fullPlanMd ?? "",
111
+ sequence: index + 1,
112
+ blocker_source: "",
113
+ escalation_pending: 0,
114
+ escalation_awaiting_review: 0,
115
+ escalation_artifact_path: null,
116
+ escalation_override_applied_at: null,
117
+ }));
118
+ }
119
+ function validateTaskPathsBeforePersist(params, basePath) {
120
+ const taskRows = toTaskRows(params);
121
+ const checks = [
122
+ ...checkFilePathConsistency(taskRows, basePath),
123
+ ...checkTaskOrdering(taskRows, basePath),
124
+ ];
125
+ const blocking = checks.filter((check) => !check.passed && check.blocking);
126
+ if (blocking.length === 0)
127
+ return null;
128
+ return blocking
129
+ .map((check) => `[${check.category}] ${check.target}: ${check.message}`)
130
+ .join("\n");
131
+ }
87
132
  export async function handlePlanSlice(rawParams, basePath) {
88
133
  let params;
89
134
  try {
@@ -100,10 +145,15 @@ export async function handlePlanSlice(rawParams, basePath) {
100
145
  if (pathScopeError) {
101
146
  return { error: `validation failed: ${pathScopeError}` };
102
147
  }
148
+ const pathError = validateTaskPathsBeforePersist(params, basePath);
149
+ if (pathError) {
150
+ return { error: `pre-execution validation failed:\n${pathError}` };
151
+ }
103
152
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
104
153
  // Guards must be inside the transaction so the state they check cannot
105
154
  // change between the read and the write (#2723).
106
155
  let guardError = null;
156
+ let omittedTaskIds = [];
107
157
  try {
108
158
  transaction(() => {
109
159
  const parentMilestone = getMilestone(params.milestoneId);
@@ -124,9 +174,21 @@ export async function handlePlanSlice(rawParams, basePath) {
124
174
  guardError = `cannot re-plan slice ${params.sliceId}: it is already complete — use gsd_slice_reopen first`;
125
175
  return;
126
176
  }
177
+ const newTaskIds = new Set(params.tasks.map((task) => task.taskId));
178
+ const existingTasks = getSliceTasks(params.milestoneId, params.sliceId);
179
+ omittedTaskIds = existingTasks
180
+ .filter((task) => !newTaskIds.has(task.id))
181
+ .map((task) => task.id);
182
+ for (const task of existingTasks) {
183
+ if (!newTaskIds.has(task.id) && isClosedStatus(task.status)) {
184
+ guardError = `cannot remove completed task ${task.id}`;
185
+ return;
186
+ }
187
+ }
127
188
  if (isDeferredStatus(parentSlice.status)) {
128
189
  updateSliceStatus(params.milestoneId, params.sliceId, "pending");
129
190
  }
191
+ setSliceSketchFlag(params.milestoneId, params.sliceId, false);
130
192
  upsertSlicePlanning(params.milestoneId, params.sliceId, {
131
193
  goal: params.goal,
132
194
  successCriteria: params.successCriteria,
@@ -134,6 +196,9 @@ export async function handlePlanSlice(rawParams, basePath) {
134
196
  integrationClosure: params.integrationClosure,
135
197
  observabilityImpact: params.observabilityImpact,
136
198
  });
199
+ for (const taskId of omittedTaskIds) {
200
+ deleteTask(params.milestoneId, params.sliceId, taskId);
201
+ }
137
202
  for (const task of params.tasks) {
138
203
  insertTask({
139
204
  id: task.taskId,
@@ -176,6 +241,16 @@ export async function handlePlanSlice(rawParams, basePath) {
176
241
  return { error: guardError };
177
242
  }
178
243
  try {
244
+ const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
245
+ for (const taskId of omittedTaskIds) {
246
+ if (!tasksDir)
247
+ continue;
248
+ const taskPlanPath = join(tasksDir, buildTaskFileName(taskId, "PLAN"));
249
+ if (existsSync(taskPlanPath))
250
+ rmSync(taskPlanPath, { force: true });
251
+ const artifactPath = relative(gsdRoot(basePath), taskPlanPath).replace(/\\/g, "/");
252
+ deleteArtifactByPath(artifactPath);
253
+ }
179
254
  const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
180
255
  invalidateStateCache();
181
256
  clearParseCache();
@@ -73,6 +73,7 @@ const COMMON_BUDGET_SMALL = 250_000; // ~65K tokens
73
73
  // allowed-path set for the docs policy lives in one reviewable place.
74
74
  const TOOLS_ALL = { mode: "all" };
75
75
  const TOOLS_PLANNING = { mode: "planning" };
76
+ const TOOLS_VERIFICATION = { mode: "verification" };
76
77
  // Like TOOLS_PLANNING but permits dispatch to read-only recon/planning
77
78
  // specialists. Runtime-enforced by write-gate.ts before the subagent tool runs.
78
79
  const TOOLS_PLANNING_DISPATCH_RECON = {
@@ -187,8 +188,8 @@ export const UNIT_MANIFESTS = {
187
188
  contextMode: "verification",
188
189
  // planning-dispatch: validation is a verification-fan-out unit. It reads
189
190
  // the milestone surface and dispatches reviewer/security/tester subagents
190
- // to report findings without touching user source. Mirrors
191
- // complete-milestone's policy. Write isolation to .gsd/ is preserved.
191
+ // to report findings without touching user source. Write isolation to
192
+ // .gsd/ is preserved.
192
193
  tools: TOOLS_PLANNING_DISPATCH_REVIEW,
193
194
  artifacts: {
194
195
  inline: ["roadmap", "slice-summary", "slice-uat", "requirements", "decisions", "templates"],
@@ -204,11 +205,9 @@ export const UNIT_MANIFESTS = {
204
205
  codebaseMap: false,
205
206
  preferences: "active-only",
206
207
  contextMode: "verification",
207
- // planning-dispatch: completion is a high-leverage place to fan out to
208
- // reviewer / security / tester subagents. They read the diff and report
209
- // findings; they do not write user source. Write isolation to .gsd/ is
210
- // preserved.
211
- tools: TOOLS_PLANNING_DISPATCH_REVIEW,
208
+ // Milestone closeout must run unrestricted shell verification commands
209
+ // against the final diff before recording completion.
210
+ tools: TOOLS_ALL,
212
211
  artifacts: {
213
212
  // #4780 landed slice-summary as excerpt for this unit; phase 2 of
214
213
  // the architecture will read this manifest as the source of truth
@@ -367,7 +366,7 @@ export const UNIT_MANIFESTS = {
367
366
  codebaseMap: false,
368
367
  preferences: "active-only",
369
368
  contextMode: "verification",
370
- tools: TOOLS_PLANNING,
369
+ tools: TOOLS_VERIFICATION,
371
370
  artifacts: {
372
371
  // Phase 3 migration (#4782): manifest matches today's actual
373
372
  // buildRunUatPrompt inlining. Prior phase-1 entry listed
@@ -5,6 +5,27 @@
5
5
  export function isNonEmptyString(value) {
6
6
  return typeof value === "string" && value.trim().length > 0;
7
7
  }
8
+ /**
9
+ * Characters that are used as delimiters in GSD state management documents
10
+ * and should not appear in milestone or slice titles.
11
+ */
12
+ const TITLE_DELIMITER_RE = /[\u2014\u2013\/]/; // em dash, en dash, forward slash
13
+ /**
14
+ * Check whether a milestone or slice title contains characters that conflict
15
+ * with GSD's state document delimiter conventions.
16
+ * Returns a human-readable description of the problem, or null if the title is safe.
17
+ */
18
+ export function validateTitle(title) {
19
+ if (TITLE_DELIMITER_RE.test(title)) {
20
+ const found = [];
21
+ if (/[\u2014\u2013]/.test(title))
22
+ found.push("em/en dash (\u2014 or \u2013)");
23
+ if (/\//.test(title))
24
+ found.push("forward slash (/)");
25
+ return `title contains ${found.join(" and ")}, which conflict with GSD state document delimiters`;
26
+ }
27
+ return null;
28
+ }
8
29
  /**
9
30
  * Validate that `value` is an array of non-empty strings.
10
31
  * Throws with a message referencing `field` on failure.
@@ -12,7 +33,8 @@ export function isNonEmptyString(value) {
12
33
  */
13
34
  export function validateStringArray(value, field) {
14
35
  if (!Array.isArray(value)) {
15
- throw new Error(`${field} must be an array`);
36
+ const received = value === null ? "null" : typeof value;
37
+ throw new Error(`${field} must be an array of strings, not ${received}`);
16
38
  }
17
39
  if (value.some((item) => !isNonEmptyString(item))) {
18
40
  throw new Error(`${field} must contain only non-empty strings`);