gsd-pi 2.65.0-dev.5c8557b → 2.65.0-dev.6cc5110

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 (305) hide show
  1. package/dist/mcp-server.js +6 -2
  2. package/dist/resources/extensions/browser-tools/capture.js +20 -1
  3. package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  4. package/dist/resources/extensions/gsd/auto/run-unit.js +13 -2
  5. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  6. package/dist/resources/extensions/gsd/auto-dispatch.js +99 -9
  7. package/dist/resources/extensions/gsd/auto-model-selection.js +7 -5
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -6
  9. package/dist/resources/extensions/gsd/auto-prompts.js +24 -0
  10. package/dist/resources/extensions/gsd/auto-recovery.js +40 -22
  11. package/dist/resources/extensions/gsd/auto-start.js +42 -11
  12. package/dist/resources/extensions/gsd/auto-tool-tracking.js +10 -0
  13. package/dist/resources/extensions/gsd/auto-worktree.js +29 -7
  14. package/dist/resources/extensions/gsd/auto.js +21 -15
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +17 -4
  16. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +10 -0
  17. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +6 -4
  18. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -1
  19. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +4 -1
  20. package/dist/resources/extensions/gsd/commands/context.js +8 -1
  21. package/dist/resources/extensions/gsd/commands/handlers/core.js +20 -0
  22. package/dist/resources/extensions/gsd/commands-extensions.js +1 -1
  23. package/dist/resources/extensions/gsd/config-overlay.js +312 -0
  24. package/dist/resources/extensions/gsd/db-writer.js +13 -3
  25. package/dist/resources/extensions/gsd/detection.js +1 -1
  26. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -1
  27. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  28. package/dist/resources/extensions/gsd/doctor.js +2 -1
  29. package/dist/resources/extensions/gsd/gitignore.js +1 -0
  30. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  31. package/dist/resources/extensions/gsd/guided-flow.js +220 -29
  32. package/dist/resources/extensions/gsd/json-persistence.js +5 -2
  33. package/dist/resources/extensions/gsd/md-importer.js +14 -7
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.js +17 -11
  35. package/dist/resources/extensions/gsd/pre-execution-checks.js +12 -5
  36. package/dist/resources/extensions/gsd/preferences-types.js +3 -0
  37. package/dist/resources/extensions/gsd/preferences-validation.js +45 -1
  38. package/dist/resources/extensions/gsd/preferences.js +9 -2
  39. package/dist/resources/extensions/gsd/preparation.js +1092 -0
  40. package/dist/resources/extensions/gsd/prompt-validation.js +67 -0
  41. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  42. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  43. package/dist/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  44. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +4 -1
  45. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  46. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  47. package/dist/resources/extensions/gsd/prompts/rethink.md +2 -1
  48. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  49. package/dist/resources/extensions/gsd/quick.js +19 -15
  50. package/dist/resources/extensions/gsd/reactive-graph.js +12 -0
  51. package/dist/resources/extensions/gsd/roadmap-slices.js +24 -5
  52. package/dist/resources/extensions/gsd/safety/content-validator.js +3 -3
  53. package/dist/resources/extensions/gsd/session-lock.js +23 -1
  54. package/dist/resources/extensions/gsd/state.js +112 -22
  55. package/dist/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  56. package/dist/resources/extensions/gsd/tools/complete-milestone.js +15 -3
  57. package/dist/resources/extensions/gsd/tools/complete-slice.js +27 -6
  58. package/dist/resources/extensions/gsd/tools/complete-task.js +31 -7
  59. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -5
  60. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +5 -2
  61. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +119 -0
  62. package/dist/resources/extensions/gsd/tools/reopen-slice.js +30 -0
  63. package/dist/resources/extensions/gsd/tools/reopen-task.js +18 -0
  64. package/dist/resources/extensions/gsd/triage-resolution.js +33 -16
  65. package/dist/resources/extensions/gsd/undo.js +3 -2
  66. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  67. package/dist/resources/extensions/gsd/workflow-projections.js +4 -7
  68. package/dist/resources/extensions/gsd/workflow-reconcile.js +100 -9
  69. package/dist/resources/extensions/gsd/workflow-templates.js +11 -2
  70. package/dist/resources/extensions/gsd/worktree-manager.js +5 -2
  71. package/dist/resources/extensions/gsd/worktree.js +9 -0
  72. package/dist/resources/extensions/shared/interview-ui.js +1 -1
  73. package/dist/web/standalone/.next/BUILD_ID +1 -1
  74. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
  75. package/dist/web/standalone/.next/build-manifest.json +3 -3
  76. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  77. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  79. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.html +1 -1
  95. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
  102. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  103. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  105. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  106. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  107. package/dist/web/standalone/.next/static/chunks/6502.8874bcae249c02e1.js +9 -0
  108. package/dist/web/standalone/.next/static/chunks/{webpack-a1c1e452c6b32d04.js → webpack-9fed74684e1c5bb1.js} +1 -1
  109. package/package.json +1 -1
  110. package/packages/pi-coding-agent/dist/core/retry-handler.d.ts.map +1 -1
  111. package/packages/pi-coding-agent/dist/core/retry-handler.js +30 -19
  112. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  113. package/packages/pi-coding-agent/dist/core/retry-handler.test.js +51 -0
  114. package/packages/pi-coding-agent/dist/core/retry-handler.test.js.map +1 -1
  115. package/packages/pi-coding-agent/dist/core/sdk.js +9 -9
  116. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  117. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +2 -1
  118. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
  119. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +10 -1
  120. package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
  121. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  122. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  123. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +20 -5
  124. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  125. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +15 -1
  126. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  127. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +18 -0
  128. package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
  129. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  130. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +4 -0
  131. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  132. package/packages/pi-coding-agent/src/core/retry-handler.test.ts +80 -0
  133. package/packages/pi-coding-agent/src/core/retry-handler.ts +37 -25
  134. package/packages/pi-coding-agent/src/core/sdk.ts +9 -9
  135. package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +10 -0
  136. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +20 -4
  137. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +27 -0
  138. package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +16 -1
  139. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +5 -0
  140. package/packages/pi-tui/dist/components/image.d.ts +2 -0
  141. package/packages/pi-tui/dist/components/image.d.ts.map +1 -1
  142. package/packages/pi-tui/dist/components/image.js +4 -0
  143. package/packages/pi-tui/dist/components/image.js.map +1 -1
  144. package/packages/pi-tui/dist/components/image.test.d.ts +6 -0
  145. package/packages/pi-tui/dist/components/image.test.d.ts.map +1 -0
  146. package/packages/pi-tui/dist/components/image.test.js +32 -0
  147. package/packages/pi-tui/dist/components/image.test.js.map +1 -0
  148. package/packages/pi-tui/src/components/image.test.ts +36 -0
  149. package/packages/pi-tui/src/components/image.ts +5 -0
  150. package/src/resources/extensions/browser-tools/capture.ts +19 -1
  151. package/src/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +93 -0
  152. package/src/resources/extensions/gsd/auto/run-unit.ts +12 -2
  153. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  154. package/src/resources/extensions/gsd/auto-dispatch.ts +110 -9
  155. package/src/resources/extensions/gsd/auto-model-selection.ts +7 -5
  156. package/src/resources/extensions/gsd/auto-post-unit.ts +16 -6
  157. package/src/resources/extensions/gsd/auto-prompts.ts +31 -0
  158. package/src/resources/extensions/gsd/auto-recovery.ts +29 -23
  159. package/src/resources/extensions/gsd/auto-start.ts +45 -10
  160. package/src/resources/extensions/gsd/auto-tool-tracking.ts +10 -0
  161. package/src/resources/extensions/gsd/auto-worktree.ts +28 -7
  162. package/src/resources/extensions/gsd/auto.ts +19 -8
  163. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -4
  164. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +10 -0
  165. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +5 -4
  166. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +4 -1
  167. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +4 -1
  168. package/src/resources/extensions/gsd/commands/context.ts +7 -1
  169. package/src/resources/extensions/gsd/commands/handlers/core.ts +23 -0
  170. package/src/resources/extensions/gsd/commands-extensions.ts +1 -1
  171. package/src/resources/extensions/gsd/config-overlay.ts +331 -0
  172. package/src/resources/extensions/gsd/db-writer.ts +11 -3
  173. package/src/resources/extensions/gsd/detection.ts +1 -1
  174. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  175. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -0
  176. package/src/resources/extensions/gsd/doctor.ts +2 -1
  177. package/src/resources/extensions/gsd/gitignore.ts +1 -0
  178. package/src/resources/extensions/gsd/gsd-db.ts +13 -2
  179. package/src/resources/extensions/gsd/guided-flow.ts +254 -30
  180. package/src/resources/extensions/gsd/json-persistence.ts +6 -3
  181. package/src/resources/extensions/gsd/md-importer.ts +13 -6
  182. package/src/resources/extensions/gsd/parallel-orchestrator.ts +19 -11
  183. package/src/resources/extensions/gsd/pre-execution-checks.ts +15 -7
  184. package/src/resources/extensions/gsd/preferences-types.ts +25 -0
  185. package/src/resources/extensions/gsd/preferences-validation.ts +45 -1
  186. package/src/resources/extensions/gsd/preferences.ts +9 -2
  187. package/src/resources/extensions/gsd/preparation.ts +1419 -0
  188. package/src/resources/extensions/gsd/prompt-validation.ts +88 -0
  189. package/src/resources/extensions/gsd/prompts/complete-milestone.md +3 -3
  190. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  191. package/src/resources/extensions/gsd/prompts/discuss-prepared.md +424 -0
  192. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +4 -1
  193. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +5 -4
  194. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +23 -0
  195. package/src/resources/extensions/gsd/prompts/rethink.md +2 -1
  196. package/src/resources/extensions/gsd/prompts/validate-milestone.md +56 -23
  197. package/src/resources/extensions/gsd/quick.ts +20 -15
  198. package/src/resources/extensions/gsd/reactive-graph.ts +18 -0
  199. package/src/resources/extensions/gsd/roadmap-slices.ts +21 -5
  200. package/src/resources/extensions/gsd/safety/content-validator.ts +3 -3
  201. package/src/resources/extensions/gsd/session-lock.ts +17 -1
  202. package/src/resources/extensions/gsd/state.ts +112 -20
  203. package/src/resources/extensions/gsd/templates/context-enhanced.md +138 -0
  204. package/src/resources/extensions/gsd/tests/adversarial-review-fixes.test.ts +223 -0
  205. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +33 -2
  206. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +56 -0
  207. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +41 -0
  208. package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +72 -0
  209. package/src/resources/extensions/gsd/tests/complete-task-normalize-lists.test.ts +54 -0
  210. package/src/resources/extensions/gsd/tests/defer-milestone-stamp.test.ts +30 -0
  211. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -3
  212. package/src/resources/extensions/gsd/tests/discuss-incremental-persistence.test.ts +36 -0
  213. package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +46 -0
  214. package/src/resources/extensions/gsd/tests/dispatch-guard-closed-status.test.ts +33 -0
  215. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +37 -0
  216. package/src/resources/extensions/gsd/tests/error-success-mask.test.ts +37 -0
  217. package/src/resources/extensions/gsd/tests/find-missing-summaries-closed.test.ts +48 -0
  218. package/src/resources/extensions/gsd/tests/frontmatter-parse-noise.test.ts +42 -0
  219. package/src/resources/extensions/gsd/tests/gitignore-bg-shell.test.ts +38 -0
  220. package/src/resources/extensions/gsd/tests/guided-flow-state-rebuild.test.ts +103 -0
  221. package/src/resources/extensions/gsd/tests/import-done-milestones.test.ts +42 -0
  222. package/src/resources/extensions/gsd/tests/integration/auto-recovery.test.ts +11 -9
  223. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +4 -2
  224. package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +28 -30
  225. package/src/resources/extensions/gsd/tests/integration/test-isolation.ts +53 -0
  226. package/src/resources/extensions/gsd/tests/integration-prepared-discussion.test.ts +525 -0
  227. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +62 -0
  228. package/src/resources/extensions/gsd/tests/needs-remediation-revalidation.test.ts +48 -0
  229. package/src/resources/extensions/gsd/tests/note-captures-executed.test.ts +46 -0
  230. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +77 -0
  231. package/src/resources/extensions/gsd/tests/phantom-ghost-detection.test.ts +55 -0
  232. package/src/resources/extensions/gsd/tests/phantom-milestone-default-queued.test.ts +39 -0
  233. package/src/resources/extensions/gsd/tests/pre-exec-backtick-strip.test.ts +68 -0
  234. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +218 -20
  235. package/src/resources/extensions/gsd/tests/pre-execution-fail-closed.test.ts +2 -2
  236. package/src/resources/extensions/gsd/tests/pre-execution-pause-wiring.test.ts +2 -2
  237. package/src/resources/extensions/gsd/tests/preparation.test.ts +1211 -0
  238. package/src/resources/extensions/gsd/tests/project-root-cwd-crash.test.ts +53 -0
  239. package/src/resources/extensions/gsd/tests/projection-no-plan-overwrite.test.ts +83 -0
  240. package/src/resources/extensions/gsd/tests/prompt-builder.test.ts +669 -0
  241. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +7 -4
  242. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +85 -0
  243. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +2 -1
  244. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +47 -0
  245. package/src/resources/extensions/gsd/tests/queued-discuss-fast-path.test.ts +107 -0
  246. package/src/resources/extensions/gsd/tests/reactive-graph.test.ts +45 -0
  247. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +63 -0
  248. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +4 -5
  249. package/src/resources/extensions/gsd/tests/run-uat-replay-cap.test.ts +51 -0
  250. package/src/resources/extensions/gsd/tests/show-config-command.test.ts +56 -0
  251. package/src/resources/extensions/gsd/tests/skip-slice-state-rebuild.test.ts +31 -0
  252. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +39 -0
  253. package/src/resources/extensions/gsd/tests/slice-sequence-insert.test.ts +51 -0
  254. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +1 -1
  255. package/src/resources/extensions/gsd/tests/stale-lockfile-recovery.test.ts +36 -0
  256. package/src/resources/extensions/gsd/tests/stale-queued-milestone.test.ts +147 -0
  257. package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +13 -0
  258. package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +21 -0
  259. package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +21 -0
  260. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +6 -7
  261. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +47 -0
  262. package/src/resources/extensions/gsd/tests/stuck-detection-coverage.test.ts +1 -0
  263. package/src/resources/extensions/gsd/tests/symlink-extension-discovery.test.ts +125 -0
  264. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +65 -0
  265. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +29 -1
  266. package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +2 -1
  267. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +3 -4
  268. package/src/resources/extensions/gsd/tests/verification-operational-gate.test.ts +15 -0
  269. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +89 -0
  270. package/src/resources/extensions/gsd/tests/wave1-critical-regressions.test.ts +49 -0
  271. package/src/resources/extensions/gsd/tests/wave2-events-regressions.test.ts +48 -0
  272. package/src/resources/extensions/gsd/tests/wave3-session-regressions.test.ts +47 -0
  273. package/src/resources/extensions/gsd/tests/wave4-write-safety-regressions.test.ts +70 -0
  274. package/src/resources/extensions/gsd/tests/worker-model-override.test.ts +48 -0
  275. package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +6 -3
  276. package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +38 -0
  277. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +16 -0
  278. package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +20 -0
  279. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +16 -17
  280. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +13 -9
  281. package/src/resources/extensions/gsd/tests/worktree.test.ts +26 -9
  282. package/src/resources/extensions/gsd/tests/zero-slice-roadmap-guided.test.ts +19 -0
  283. package/src/resources/extensions/gsd/tools/complete-milestone.ts +13 -3
  284. package/src/resources/extensions/gsd/tools/complete-slice.ts +26 -6
  285. package/src/resources/extensions/gsd/tools/complete-task.ts +29 -7
  286. package/src/resources/extensions/gsd/tools/plan-milestone.ts +11 -9
  287. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +5 -2
  288. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +152 -0
  289. package/src/resources/extensions/gsd/tools/reopen-slice.ts +27 -0
  290. package/src/resources/extensions/gsd/tools/reopen-task.ts +17 -0
  291. package/src/resources/extensions/gsd/triage-resolution.ts +37 -17
  292. package/src/resources/extensions/gsd/types.ts +4 -0
  293. package/src/resources/extensions/gsd/undo.ts +3 -2
  294. package/src/resources/extensions/gsd/workflow-events.ts +1 -1
  295. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  296. package/src/resources/extensions/gsd/workflow-projections.ts +4 -6
  297. package/src/resources/extensions/gsd/workflow-reconcile.ts +109 -8
  298. package/src/resources/extensions/gsd/workflow-templates.ts +11 -2
  299. package/src/resources/extensions/gsd/worktree-manager.ts +4 -2
  300. package/src/resources/extensions/gsd/worktree.ts +10 -0
  301. package/src/resources/extensions/shared/interview-ui.ts +1 -1
  302. package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +8 -10
  303. package/dist/web/standalone/.next/static/chunks/6502.7593d7797a4b3999.js +0 -9
  304. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → iueakR5x5bQbax2sGz8Yr}/_buildManifest.js +0 -0
  305. /package/dist/web/standalone/.next/static/{qq3YfHPfyqvh3DIMVmsRH → iueakR5x5bQbax2sGz8Yr}/_ssgManifest.js +0 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * GSD Configuration Overlay
3
+ *
4
+ * Read-only TUI overlay showing the effective GSD configuration:
5
+ * token profile, model assignments, dynamic routing, git settings,
6
+ * budget, workflow toggles, and preference file sources.
7
+ * Opened via `/gsd show-config` or `/gsd config`.
8
+ */
9
+
10
+ import type { Theme } from "@gsd/pi-coding-agent";
11
+ import { matchesKey, Key, truncateToWidth } from "@gsd/pi-tui";
12
+
13
+ import {
14
+ loadEffectiveGSDPreferences,
15
+ loadGlobalGSDPreferences,
16
+ loadProjectGSDPreferences,
17
+ getGlobalGSDPreferencesPath,
18
+ getProjectGSDPreferencesPath,
19
+ resolveDynamicRoutingConfig,
20
+ resolveEffectiveProfile,
21
+ resolveModelWithFallbacksForUnit,
22
+ resolveAutoSupervisorConfig,
23
+ } from "./preferences.js";
24
+
25
+ // ─── Data Collection ──────────────────────────────────────────────────────
26
+
27
+ interface ConfigSection {
28
+ title: string;
29
+ rows: Array<{ label: string; value: string; accent?: boolean }>;
30
+ }
31
+
32
+ function collectConfigSections(): ConfigSection[] {
33
+ const sections: ConfigSection[] = [];
34
+
35
+ const globalPrefs = loadGlobalGSDPreferences();
36
+ const projectPrefs = loadProjectGSDPreferences();
37
+ const effective = loadEffectiveGSDPreferences();
38
+ const prefs = effective?.preferences;
39
+
40
+ // ─── Sources ─────────────────────────────────────────────────────────
41
+ sections.push({
42
+ title: "Sources",
43
+ rows: [
44
+ { label: "Global", value: globalPrefs ? globalPrefs.path : `(none) ${getGlobalGSDPreferencesPath()}` },
45
+ { label: "Project", value: projectPrefs ? projectPrefs.path : `(none) ${getProjectGSDPreferencesPath()}` },
46
+ ],
47
+ });
48
+
49
+ // ─── Profile ─────────────────────────────────────────────────────────
50
+ const profile = resolveEffectiveProfile();
51
+ const profileRows: ConfigSection["rows"] = [
52
+ { label: "Token profile", value: `${profile}${!prefs?.token_profile ? " (default)" : ""}`, accent: true },
53
+ ];
54
+ if (prefs?.mode) profileRows.push({ label: "Workflow mode", value: prefs.mode });
55
+ sections.push({ title: "Profile", rows: profileRows });
56
+
57
+ // ─── Models ──────────────────────────────────────────────────────────
58
+ const unitTypes: Array<[string, string]> = [
59
+ ["research", "research-milestone"],
60
+ ["planning", "plan-milestone"],
61
+ ["discuss", "discuss-milestone"],
62
+ ["execution", "execute-task"],
63
+ ["completion", "complete-slice"],
64
+ ["validation", "run-uat"],
65
+ ];
66
+
67
+ const modelRows: ConfigSection["rows"] = [];
68
+ for (const [label, unitType] of unitTypes) {
69
+ const resolved = resolveModelWithFallbacksForUnit(unitType);
70
+ if (resolved) {
71
+ let val = resolved.primary;
72
+ if (resolved.fallbacks.length > 0) {
73
+ val += ` \u2192 ${resolved.fallbacks.join(" \u2192 ")}`;
74
+ }
75
+ modelRows.push({ label, value: val });
76
+ } else {
77
+ modelRows.push({ label, value: "(inherit)" });
78
+ }
79
+ }
80
+
81
+ // subagent is a direct config key
82
+ const models = prefs?.models as Record<string, unknown> | undefined;
83
+ const subVal = models?.subagent;
84
+ if (subVal) {
85
+ const model = typeof subVal === "string" ? subVal : (subVal as { model?: string })?.model ?? "?";
86
+ modelRows.push({ label: "subagent", value: model });
87
+ } else {
88
+ modelRows.push({ label: "subagent", value: "(inherit)" });
89
+ }
90
+
91
+ sections.push({ title: "Models", rows: modelRows });
92
+
93
+ // ─── Dynamic Routing ─────────────────────────────────────────────────
94
+ const routing = resolveDynamicRoutingConfig();
95
+ const routingRows: ConfigSection["rows"] = [
96
+ { label: "Enabled", value: routing.enabled ? "yes" : "no", accent: routing.enabled },
97
+ ];
98
+ if (routing.enabled) {
99
+ routingRows.push({ label: "Escalate on fail", value: routing.escalate_on_failure !== false ? "yes" : "no" });
100
+ routingRows.push({ label: "Budget pressure", value: routing.budget_pressure !== false ? "yes" : "no" });
101
+ routingRows.push({ label: "Cross-provider", value: routing.cross_provider !== false ? "yes" : "no" });
102
+ if (routing.tier_models) {
103
+ const tm = routing.tier_models;
104
+ if (tm.light) routingRows.push({ label: "[L] light", value: tm.light });
105
+ if (tm.standard) routingRows.push({ label: "[S] standard", value: tm.standard });
106
+ if (tm.heavy) routingRows.push({ label: "[H] heavy", value: tm.heavy });
107
+ }
108
+ }
109
+ sections.push({ title: "Dynamic Routing", rows: routingRows });
110
+
111
+ // ─── Git ─────────────────────────────────────────────────────────────
112
+ if (prefs?.git) {
113
+ const g = prefs.git;
114
+ const gitRows: ConfigSection["rows"] = [];
115
+ if (g.isolation !== undefined) gitRows.push({ label: "Isolation", value: String(g.isolation) });
116
+ if (g.auto_push !== undefined) gitRows.push({ label: "Auto push", value: String(g.auto_push) });
117
+ if (g.push_branches !== undefined) gitRows.push({ label: "Push branches", value: String(g.push_branches) });
118
+ if (g.merge_strategy) gitRows.push({ label: "Merge strategy", value: g.merge_strategy });
119
+ if (g.main_branch) gitRows.push({ label: "Main branch", value: g.main_branch });
120
+ if (g.remote) gitRows.push({ label: "Remote", value: g.remote });
121
+ if (gitRows.length > 0) sections.push({ title: "Git", rows: gitRows });
122
+ }
123
+
124
+ // ─── Budget ──────────────────────────────────────────────────────────
125
+ if (prefs?.budget_ceiling !== undefined || prefs?.budget_enforcement) {
126
+ const budgetRows: ConfigSection["rows"] = [];
127
+ if (prefs.budget_ceiling !== undefined) budgetRows.push({ label: "Ceiling", value: `$${prefs.budget_ceiling}` });
128
+ if (prefs.budget_enforcement) budgetRows.push({ label: "Enforcement", value: String(prefs.budget_enforcement) });
129
+ sections.push({ title: "Budget", rows: budgetRows });
130
+ }
131
+
132
+ // ─── Auto Supervisor ─────────────────────────────────────────────────
133
+ if (prefs?.auto_supervisor) {
134
+ const sup = resolveAutoSupervisorConfig();
135
+ const supRows: ConfigSection["rows"] = [];
136
+ if (sup.model) supRows.push({ label: "Model", value: sup.model });
137
+ supRows.push({ label: "Soft timeout", value: `${sup.soft_timeout_minutes}m` });
138
+ supRows.push({ label: "Idle timeout", value: `${sup.idle_timeout_minutes}m` });
139
+ supRows.push({ label: "Hard timeout", value: `${sup.hard_timeout_minutes}m` });
140
+ sections.push({ title: "Auto Supervisor", rows: supRows });
141
+ }
142
+
143
+ // ─── Toggles ─────────────────────────────────────────────────────────
144
+ const toggleRows: ConfigSection["rows"] = [];
145
+ if (prefs?.phases) {
146
+ const p = prefs.phases;
147
+ if (p.skip_research) toggleRows.push({ label: "skip_research", value: "on" });
148
+ if (p.skip_reassess) toggleRows.push({ label: "skip_reassess", value: "on" });
149
+ if (p.skip_slice_research) toggleRows.push({ label: "skip_slice_research", value: "on" });
150
+ if (p.skip_milestone_validation) toggleRows.push({ label: "skip_milestone_validation", value: "on" });
151
+ if (p.require_slice_discussion) toggleRows.push({ label: "require_slice_discussion", value: "on" });
152
+ }
153
+ if (prefs?.uat_dispatch) toggleRows.push({ label: "uat_dispatch", value: "on" });
154
+ if (prefs?.auto_visualize) toggleRows.push({ label: "auto_visualize", value: "on" });
155
+ if (prefs?.auto_report === false) toggleRows.push({ label: "auto_report", value: "off" });
156
+ if (prefs?.show_token_cost) toggleRows.push({ label: "show_token_cost", value: "on" });
157
+ if (prefs?.forensics_dedup) toggleRows.push({ label: "forensics_dedup", value: "on" });
158
+ if (prefs?.unique_milestone_ids) toggleRows.push({ label: "unique_milestone_ids", value: "on" });
159
+ if (prefs?.service_tier) toggleRows.push({ label: "service_tier", value: prefs.service_tier });
160
+ if (prefs?.search_provider && prefs.search_provider !== "auto") toggleRows.push({ label: "search_provider", value: prefs.search_provider });
161
+ if (prefs?.context_selection) toggleRows.push({ label: "context_selection", value: prefs.context_selection });
162
+ if (prefs?.widget_mode && prefs.widget_mode !== "full") toggleRows.push({ label: "widget_mode", value: prefs.widget_mode });
163
+ if (prefs?.experimental?.rtk) toggleRows.push({ label: "experimental.rtk", value: "on" });
164
+ if (toggleRows.length > 0) sections.push({ title: "Toggles", rows: toggleRows });
165
+
166
+ // ─── Parallel ────────────────────────────────────────────────────────
167
+ if (prefs?.parallel) {
168
+ const pc = prefs.parallel;
169
+ const parallelRows: ConfigSection["rows"] = [];
170
+ if (pc.max_workers !== undefined) parallelRows.push({ label: "Max workers", value: String(pc.max_workers) });
171
+ if (pc.merge_strategy) parallelRows.push({ label: "Merge strategy", value: pc.merge_strategy });
172
+ if (pc.auto_merge) parallelRows.push({ label: "Auto merge", value: pc.auto_merge });
173
+ if (parallelRows.length > 0) sections.push({ title: "Parallel", rows: parallelRows });
174
+ }
175
+
176
+ // ─── Hooks ───────────────────────────────────────────────────────────
177
+ const postHooks = prefs?.post_unit_hooks?.filter(h => h.enabled !== false) ?? [];
178
+ const preHooks = prefs?.pre_dispatch_hooks?.filter(h => h.enabled !== false) ?? [];
179
+ if (postHooks.length > 0 || preHooks.length > 0) {
180
+ const hookRows: ConfigSection["rows"] = [];
181
+ if (preHooks.length > 0) hookRows.push({ label: "Pre-dispatch", value: `${preHooks.length} active` });
182
+ if (postHooks.length > 0) hookRows.push({ label: "Post-unit", value: `${postHooks.length} active` });
183
+ sections.push({ title: "Hooks", rows: hookRows });
184
+ }
185
+
186
+ // ─── Warnings ────────────────────────────────────────────────────────
187
+ const warnings = [
188
+ ...(globalPrefs?.warnings ?? []),
189
+ ...(projectPrefs?.warnings ?? []),
190
+ ];
191
+ if (warnings.length > 0) {
192
+ sections.push({
193
+ title: "Warnings",
194
+ rows: warnings.map(w => ({ label: "\u26a0", value: w })),
195
+ });
196
+ }
197
+
198
+ return sections;
199
+ }
200
+
201
+ // ─── Plain Text Formatter (headless/RPC fallback) ─────────────────────────
202
+
203
+ export function formatConfigText(): string {
204
+ const sections = collectConfigSections();
205
+ const lines: string[] = ["GSD Configuration\n"];
206
+
207
+ let maxLabel = 0;
208
+ for (const section of sections) {
209
+ for (const row of section.rows) {
210
+ if (row.label.length > maxLabel) maxLabel = row.label.length;
211
+ }
212
+ }
213
+ const pad = Math.min(maxLabel + 2, 24);
214
+
215
+ for (const section of sections) {
216
+ lines.push("");
217
+ lines.push(section.title.toUpperCase());
218
+ for (const row of section.rows) {
219
+ lines.push(` ${row.label.padEnd(pad)}${row.value}`);
220
+ }
221
+ }
222
+
223
+ return lines.join("\n");
224
+ }
225
+
226
+ // ─── Overlay Class ────────────────────────────────────────────────────────
227
+
228
+ export class GSDConfigOverlay {
229
+ private tui: { requestRender: () => void };
230
+ private theme: Theme;
231
+ private onClose: () => void;
232
+ private sections: ConfigSection[];
233
+ private cachedLines?: string[];
234
+ private scrollOffset = 0;
235
+ private disposed = false;
236
+
237
+ constructor(
238
+ tui: { requestRender: () => void },
239
+ theme: Theme,
240
+ onClose: () => void,
241
+ ) {
242
+ this.tui = tui;
243
+ this.theme = theme;
244
+ this.onClose = onClose;
245
+ this.sections = collectConfigSections();
246
+ }
247
+
248
+ invalidate(): void {
249
+ this.cachedLines = undefined;
250
+ }
251
+
252
+ dispose(): void {
253
+ this.disposed = true;
254
+ }
255
+
256
+ handleInput(data: string): void {
257
+ if (matchesKey(data, Key.escape) || data === "q") {
258
+ this.dispose();
259
+ this.onClose();
260
+ return;
261
+ }
262
+ if (matchesKey(data, Key.down) || data === "j") {
263
+ this.scrollOffset++;
264
+ this.cachedLines = undefined;
265
+ this.tui.requestRender();
266
+ return;
267
+ }
268
+ if (matchesKey(data, Key.up) || data === "k") {
269
+ this.scrollOffset = Math.max(0, this.scrollOffset - 1);
270
+ this.cachedLines = undefined;
271
+ this.tui.requestRender();
272
+ return;
273
+ }
274
+ if (matchesKey(data, Key.pageDown)) {
275
+ this.scrollOffset += 10;
276
+ this.cachedLines = undefined;
277
+ this.tui.requestRender();
278
+ return;
279
+ }
280
+ if (matchesKey(data, Key.pageUp)) {
281
+ this.scrollOffset = Math.max(0, this.scrollOffset - 10);
282
+ this.cachedLines = undefined;
283
+ this.tui.requestRender();
284
+ return;
285
+ }
286
+ }
287
+
288
+ render(width: number): string[] {
289
+ if (this.cachedLines) return this.cachedLines;
290
+
291
+ const t = this.theme;
292
+ const w = Math.max(width, 50);
293
+ const allLines: string[] = [];
294
+
295
+ // Header
296
+ allLines.push(t.bold(t.fg("accent", " GSD Configuration ")));
297
+ allLines.push(t.fg("muted", "\u2500".repeat(w)));
298
+
299
+ // Find max label width for alignment
300
+ let maxLabel = 0;
301
+ for (const section of this.sections) {
302
+ for (const row of section.rows) {
303
+ if (row.label.length > maxLabel) maxLabel = row.label.length;
304
+ }
305
+ }
306
+ const labelPad = Math.min(maxLabel + 2, 24);
307
+
308
+ for (const section of this.sections) {
309
+ allLines.push("");
310
+ allLines.push(t.bold(t.fg("accent", ` ${section.title}`)));
311
+
312
+ for (const row of section.rows) {
313
+ const label = t.fg("muted", ` ${row.label.padEnd(labelPad)}`);
314
+ const value = row.accent ? t.bold(row.value) : row.value;
315
+ allLines.push(truncateToWidth(`${label}${value}`, w));
316
+ }
317
+ }
318
+
319
+ allLines.push("");
320
+ allLines.push(t.fg("muted", ` ${"\u2500".repeat(w - 4)}`));
321
+ allLines.push(t.fg("muted", " esc/q close \u2502 \u2191\u2193/jk scroll \u2502 /gsd prefs to edit"));
322
+
323
+ // Apply scroll
324
+ const maxScroll = Math.max(0, allLines.length - 20);
325
+ this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
326
+ const visible = allLines.slice(this.scrollOffset);
327
+
328
+ this.cachedLines = visible;
329
+ return visible;
330
+ }
331
+ }
@@ -345,8 +345,12 @@ export async function saveRequirementToDb(
345
345
  await saveFile(filePath, md);
346
346
  } catch (diskErr) {
347
347
  logError('manifest', 'disk write failed, rolling back DB row', { fn: 'saveRequirementToDb', error: String((diskErr as Error).message) });
348
- const rollbackAdapter = db._getAdapter();
349
- rollbackAdapter?.prepare('DELETE FROM requirements WHERE id = :id').run({ ':id': id });
348
+ try {
349
+ const rollbackAdapter = db._getAdapter();
350
+ rollbackAdapter?.prepare('DELETE FROM requirements WHERE id = :id').run({ ':id': id });
351
+ } catch (rollbackErr) {
352
+ logError('manifest', 'SPLIT BRAIN: disk write failed AND DB rollback failed — DB has orphaned row', { fn: 'saveRequirementToDb', id, error: String((rollbackErr as Error).message) });
353
+ }
350
354
  throw diskErr;
351
355
  }
352
356
  invalidateStateCache();
@@ -466,7 +470,11 @@ export async function saveDecisionToDb(
466
470
  await saveFile(filePath, md);
467
471
  } catch (diskErr) {
468
472
  logError('manifest', 'disk write failed, rolling back DB row', { fn: 'saveDecisionToDb', error: String((diskErr as Error).message) });
469
- adapter?.prepare('DELETE FROM decisions WHERE id = :id').run({ ':id': id });
473
+ try {
474
+ adapter?.prepare('DELETE FROM decisions WHERE id = :id').run({ ':id': id });
475
+ } catch (rollbackErr) {
476
+ logError('manifest', 'SPLIT BRAIN: disk write failed AND DB rollback failed — DB has orphaned row', { fn: 'saveDecisionToDb', id, error: String((rollbackErr as Error).message) });
477
+ }
470
478
  throw diskErr;
471
479
  }
472
480
  // #2661: When a decision defers a slice, update the slice status in the DB
@@ -1114,7 +1114,7 @@ function resolveVersionCatalogAccessors(
1114
1114
  return accessors;
1115
1115
  }
1116
1116
 
1117
- function scanProjectFiles(basePath: string): string[] {
1117
+ export function scanProjectFiles(basePath: string): string[] {
1118
1118
  const files: string[] = [];
1119
1119
  const queue: Array<{ path: string; depth: number }> = [{ path: basePath, depth: 0 }];
1120
1120
 
@@ -5,6 +5,7 @@ import { findMilestoneIds } from "./guided-flow.js";
5
5
  import { parseUnitId } from "./unit-id.js";
6
6
  import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js";
7
7
  import { parseRoadmap } from "./parsers-legacy.js";
8
+ import { isClosedStatus } from "./status-guards.js";
8
9
  import { readFileSync } from "node:fs";
9
10
 
10
11
  const SLICE_DISPATCH_TYPES = new Set([
@@ -57,7 +58,7 @@ export function getPriorSliceCompletionBlocker(
57
58
  if (rows.length > 0) {
58
59
  slices = rows.map((r) => ({
59
60
  id: r.id,
60
- done: r.status === "complete",
61
+ done: isClosedStatus(r.status),
61
62
  depends: r.depends ?? [],
62
63
  }));
63
64
  }
@@ -211,6 +211,7 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
211
211
  - `budget_ceiling`: number — optional per-parallel-run budget ceiling.
212
212
  - `merge_strategy`: `"per-slice"` or `"per-milestone"` — when to merge worktree results back. Default: `"per-milestone"`.
213
213
  - `auto_merge`: `"auto"`, `"confirm"`, or `"manual"` — merge behavior after completion. `"auto"` merges immediately; `"confirm"` asks first; `"manual"` leaves branches for you. Default: `"confirm"`.
214
+ - `worker_model`: string — optional model override for parallel milestone workers. When set, workers use this model (e.g. `"claude-haiku-4-5"`) instead of inheriting the coordinator's model. Useful for cost savings on execution-heavy milestones.
214
215
 
215
216
  - `verification_commands`: string[] — shell commands to run as verification after task execution (e.g., `["npm test", "npm run lint"]`). Commands run in order; if any fails, the task is marked as needing fixes.
216
217
 
@@ -87,7 +87,8 @@ function validatePreferenceShape(preferences: GSDPreferences): string[] {
87
87
  return issues;
88
88
  }
89
89
 
90
- function buildStateMarkdown(state: Awaited<ReturnType<typeof deriveState>>): string {
90
+ /** Build STATE.md content from derived state. Exported for guided-flow pre-dispatch rebuild (#3475). */
91
+ export function buildStateMarkdown(state: Awaited<ReturnType<typeof deriveState>>): string {
91
92
  const lines: string[] = [];
92
93
  lines.push("# GSD State", "");
93
94
 
@@ -42,6 +42,7 @@ const BASELINE_PATTERNS = [
42
42
  // ── GSD state directory (symlink to external storage) ──
43
43
  ".gsd",
44
44
  ".gsd-id",
45
+ ".bg-shell/",
45
46
 
46
47
  // ── OS junk ──
47
48
  ".DS_Store",
@@ -409,6 +409,7 @@ function initSchema(db: DbAdapter, fileBacked: boolean): void {
409
409
  db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
410
410
  db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
411
411
  db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
412
+ db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
412
413
 
413
414
  // v14 index — slice dependency lookups
414
415
  db.exec("CREATE INDEX IF NOT EXISTS idx_slice_deps_target ON slice_dependencies(milestone_id, depends_on_slice_id)");
@@ -722,6 +723,7 @@ function migrateSchema(db: DbAdapter): void {
722
723
  db.exec("CREATE INDEX IF NOT EXISTS idx_milestones_status ON milestones(status)");
723
724
  db.exec("CREATE INDEX IF NOT EXISTS idx_quality_gates_pending ON quality_gates(milestone_id, slice_id, status)");
724
725
  db.exec("CREATE INDEX IF NOT EXISTS idx_verification_evidence_task ON verification_evidence(milestone_id, slice_id, task_id)");
726
+ db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_verification_evidence_dedup ON verification_evidence(task_id, slice_id, milestone_id, command, verdict)");
725
727
  db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
726
728
  ":version": 13,
727
729
  ":applied_at": new Date().toISOString(),
@@ -1119,7 +1121,9 @@ export function insertMilestone(m: {
1119
1121
  ).run({
1120
1122
  ":id": m.id,
1121
1123
  ":title": m.title ?? "",
1122
- ":status": m.status ?? "active",
1124
+ // Default to "queued" never auto-create milestones as "active" (#3380).
1125
+ // Callers that need "active" must pass it explicitly.
1126
+ ":status": m.status ?? "queued",
1123
1127
  ":depends_on": JSON.stringify(m.depends_on ?? []),
1124
1128
  ":created_at": new Date().toISOString(),
1125
1129
  ":vision": m.planning?.vision ?? "",
@@ -1349,6 +1353,13 @@ export function updateTaskStatus(milestoneId: string, sliceId: string, taskId: s
1349
1353
  });
1350
1354
  }
1351
1355
 
1356
+ export function setTaskBlockerDiscovered(milestoneId: string, sliceId: string, taskId: string, discovered: boolean): void {
1357
+ if (!currentDb) return;
1358
+ currentDb.prepare(
1359
+ `UPDATE tasks SET blocker_discovered = :discovered WHERE milestone_id = :mid AND slice_id = :sid AND id = :tid`,
1360
+ ).run({ ":discovered": discovered ? 1 : 0, ":mid": milestoneId, ":sid": sliceId, ":tid": taskId });
1361
+ }
1362
+
1352
1363
  export function upsertTaskPlanning(milestoneId: string, sliceId: string, taskId: string, planning: Partial<TaskPlanningRecord>): void {
1353
1364
  if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1354
1365
  currentDb.prepare(
@@ -1543,7 +1554,7 @@ export function insertVerificationEvidence(e: {
1543
1554
  }): void {
1544
1555
  if (!currentDb) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1545
1556
  currentDb.prepare(
1546
- `INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
1557
+ `INSERT OR IGNORE INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
1547
1558
  VALUES (:task_id, :slice_id, :milestone_id, :command, :exit_code, :verdict, :duration_ms, :created_at)`,
1548
1559
  ).run({
1549
1560
  ":task_id": e.taskId,