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
@@ -14,9 +14,11 @@ import {
14
14
  resolveProjectRoot,
15
15
  setActiveMilestoneId,
16
16
  SLICE_BRANCH_RE,
17
+ _resetServiceCache,
17
18
  } from "../worktree.ts";
18
19
  import { readIntegrationBranch } from "../git-service.ts";
19
20
  import { _resetHasChangesCache } from "../native-git-bridge.ts";
21
+ import { _clearGsdRootCache } from "../paths.ts";
20
22
  import { describe, test } from 'node:test';
21
23
  import assert from 'node:assert/strict';
22
24
 
@@ -165,15 +167,30 @@ describe('worktree', async () => {
165
167
  run("git checkout -b my-feature", repo);
166
168
  captureIntegrationBranch(repo, "M001");
167
169
 
168
- // Without milestone set, getMainBranch returns "main"
169
- setActiveMilestoneId(repo, null);
170
- assert.deepStrictEqual(getMainBranch(repo), "main",
171
- "getMainBranch returns main without milestone set");
172
-
173
- // With milestone set, getMainBranch returns feature branch
174
- setActiveMilestoneId(repo, "M001");
175
- assert.deepStrictEqual(getMainBranch(repo), "my-feature",
176
- "getMainBranch returns integration branch with milestone set");
170
+ // Isolate from user's global preferences (which may have git.main_branch set).
171
+ // Reset caches so getService() creates a fresh instance with empty preferences.
172
+ const originalHome = process.env.HOME;
173
+ const fakeHome = mkdtempSync(join(tmpdir(), "gsd-fake-home-"));
174
+ process.env.HOME = fakeHome;
175
+ _clearGsdRootCache();
176
+ _resetServiceCache();
177
+
178
+ try {
179
+ // Without milestone set, getMainBranch returns "main"
180
+ setActiveMilestoneId(repo, null);
181
+ assert.deepStrictEqual(getMainBranch(repo), "main",
182
+ "getMainBranch returns main without milestone set");
183
+
184
+ // With milestone set, getMainBranch returns feature branch
185
+ setActiveMilestoneId(repo, "M001");
186
+ assert.deepStrictEqual(getMainBranch(repo), "my-feature",
187
+ "getMainBranch returns integration branch with milestone set");
188
+ } finally {
189
+ process.env.HOME = originalHome;
190
+ _clearGsdRootCache();
191
+ _resetServiceCache();
192
+ rmSync(fakeHome, { recursive: true, force: true });
193
+ }
177
194
 
178
195
  rmSync(repo, { recursive: true, force: true });
179
196
  }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Regression test for #3441: guided flow must treat a roadmap with zero
3
+ * parseable slices the same as no roadmap — offer "Create roadmap" not "Go auto".
4
+ */
5
+ import { test } from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import { readFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+
10
+ test("guided-flow checks roadmap slice count before offering auto (#3441)", () => {
11
+ const src = readFileSync(
12
+ join(import.meta.dirname, "..", "guided-flow.ts"),
13
+ "utf-8",
14
+ );
15
+ assert.ok(
16
+ src.includes("roadmapHasSlices") || src.includes("parseRoadmapSlices"),
17
+ "Guided flow must parse roadmap for slices before deciding which options to show",
18
+ );
19
+ });
@@ -23,7 +23,7 @@ import { invalidateStateCache } from "../state.js";
23
23
  import { renderAllProjections, stripIdPrefix } from "../workflow-projections.js";
24
24
  import { writeManifest } from "../workflow-manifest.js";
25
25
  import { appendEvent } from "../workflow-events.js";
26
- import { logWarning } from "../workflow-logger.js";
26
+ import { logWarning, logError } from "../workflow-logger.js";
27
27
 
28
28
  export interface CompleteMilestoneParams {
29
29
  milestoneId: string;
@@ -218,9 +218,19 @@ export async function handleCompleteMilestone(
218
218
  clearParseCache();
219
219
 
220
220
  // ── Post-mutation hook: projections, manifest, event log ───────────────
221
+ // Separate try/catch per step so a projection failure doesn't prevent
222
+ // the event log entry (critical for worktree reconciliation).
221
223
  try {
222
224
  await renderAllProjections(basePath, params.milestoneId);
225
+ } catch (projErr) {
226
+ logWarning("tool", `complete-milestone projection warning: ${(projErr as Error).message}`);
227
+ }
228
+ try {
223
229
  writeManifest(basePath);
230
+ } catch (mfErr) {
231
+ logWarning("tool", `complete-milestone manifest warning: ${(mfErr as Error).message}`);
232
+ }
233
+ try {
224
234
  appendEvent(basePath, {
225
235
  cmd: "complete-milestone",
226
236
  params: { milestoneId: params.milestoneId },
@@ -229,8 +239,8 @@ export async function handleCompleteMilestone(
229
239
  actor_name: params.actorName,
230
240
  trigger_reason: params.triggerReason,
231
241
  });
232
- } catch (hookErr) {
233
- logWarning("tool", `complete-milestone post-mutation hook warning: ${(hookErr as Error).message}`);
242
+ } catch (eventErr) {
243
+ logError("tool", `complete-milestone event log FAILED — completion invisible to reconciliation`, { error: (eventErr as Error).message });
234
244
  }
235
245
 
236
246
  return {
@@ -30,7 +30,7 @@ import { renderRoadmapCheckboxes } from "../markdown-renderer.js";
30
30
  import { renderAllProjections } from "../workflow-projections.js";
31
31
  import { writeManifest } from "../workflow-manifest.js";
32
32
  import { appendEvent } from "../workflow-events.js";
33
- import { logWarning } from "../workflow-logger.js";
33
+ import { logWarning, logError } from "../workflow-logger.js";
34
34
 
35
35
  export interface CompleteSliceResult {
36
36
  sliceId: string;
@@ -233,8 +233,18 @@ export async function handleCompleteSlice(
233
233
  return { error: ownershipErr };
234
234
  }
235
235
 
236
+ // ── Verification content gate (#3580) ──────────────────────────────────
237
+ // Reject completion when the provided verification/UAT clearly indicates
238
+ // the slice is blocked or failed. Prevents prompt regressions from
239
+ // silently advancing blocked slices.
240
+ const BLOCKED_SIGNALS = /\b(status:\s*blocked|verification_result:\s*failed|slice is blocked|cannot complete|verification failed)\b/i;
241
+ if (BLOCKED_SIGNALS.test(params.verification || "") || BLOCKED_SIGNALS.test(params.uatContent || "")) {
242
+ return { error: `slice verification indicates blocked/failed state — do not complete a slice that has not passed verification. Address the blockers and re-verify first.` };
243
+ }
244
+
236
245
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
237
246
  const completedAt = new Date().toISOString();
247
+ const originalSliceStatus = getSlice(params.milestoneId, params.sliceId)?.status ?? "pending";
238
248
  let guardError: string | null = null;
239
249
 
240
250
  transaction(() => {
@@ -268,8 +278,8 @@ export async function handleCompleteSlice(
268
278
  }
269
279
 
270
280
  // All guards passed — perform writes
271
- insertMilestone({ id: params.milestoneId });
272
- insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
281
+ insertMilestone({ id: params.milestoneId, title: params.milestoneId });
282
+ insertSlice({ id: params.sliceId, milestoneId: params.milestoneId, title: params.sliceId });
273
283
  updateSliceStatus(params.milestoneId, params.sliceId, "complete", completedAt);
274
284
  });
275
285
 
@@ -312,7 +322,7 @@ export async function handleCompleteSlice(
312
322
  } catch (renderErr) {
313
323
  // Disk render failed — roll back DB status so state stays consistent
314
324
  logWarning("tool", `complete_slice — disk render failed for ${params.milestoneId}/${params.sliceId}, rolling back DB status`, { error: (renderErr as Error).message });
315
- updateSliceStatus(params.milestoneId, params.sliceId, 'pending');
325
+ updateSliceStatus(params.milestoneId, params.sliceId, originalSliceStatus);
316
326
  invalidateStateCache();
317
327
  return { error: `disk render failed: ${(renderErr as Error).message}` };
318
328
  }
@@ -326,9 +336,19 @@ export async function handleCompleteSlice(
326
336
  clearParseCache();
327
337
 
328
338
  // ── Post-mutation hook: projections, manifest, event log ───────────────
339
+ // Separate try/catch per step so a projection failure doesn't prevent
340
+ // the event log entry (critical for worktree reconciliation).
329
341
  try {
330
342
  await renderAllProjections(basePath, params.milestoneId);
343
+ } catch (projErr) {
344
+ logWarning("tool", `complete-slice projection warning for ${params.milestoneId}/${params.sliceId}: ${(projErr as Error).message}`);
345
+ }
346
+ try {
331
347
  writeManifest(basePath);
348
+ } catch (mfErr) {
349
+ logWarning("tool", `complete-slice manifest warning: ${(mfErr as Error).message}`);
350
+ }
351
+ try {
332
352
  appendEvent(basePath, {
333
353
  cmd: "complete-slice",
334
354
  params: { milestoneId: params.milestoneId, sliceId: params.sliceId },
@@ -337,8 +357,8 @@ export async function handleCompleteSlice(
337
357
  actor_name: params.actorName,
338
358
  trigger_reason: params.triggerReason,
339
359
  });
340
- } catch (hookErr) {
341
- logWarning("tool", `complete-slice post-mutation hook failed for ${params.milestoneId}/${params.sliceId}`, { error: (hookErr as Error).message });
360
+ } catch (eventErr) {
361
+ logError("tool", `complete-slice event log FAILED completion invisible to reconciliation`, { error: (eventErr as Error).message });
342
362
  }
343
363
 
344
364
  return {
@@ -33,7 +33,7 @@ import { renderPlanCheckboxes } from "../markdown-renderer.js";
33
33
  import { renderAllProjections, renderSummaryContent } from "../workflow-projections.js";
34
34
  import { writeManifest } from "../workflow-manifest.js";
35
35
  import { appendEvent } from "../workflow-events.js";
36
- import { logWarning } from "../workflow-logger.js";
36
+ import { logWarning, logError } from "../workflow-logger.js";
37
37
 
38
38
  export interface CompleteTaskResult {
39
39
  taskId: string;
@@ -44,6 +44,18 @@ export interface CompleteTaskResult {
44
44
 
45
45
  import type { TaskRow } from "../gsd-db.js";
46
46
 
47
+ /**
48
+ * Normalize a list parameter that may arrive as a string (newline-delimited
49
+ * bullet list from the LLM) into a string array (#3361).
50
+ */
51
+ function normalizeListParam(value: unknown): string[] {
52
+ if (Array.isArray(value)) return value.map(String);
53
+ if (typeof value === "string" && value.trim()) {
54
+ return value.split(/\n/).map(s => s.replace(/^[\s\-*•]+/, "").trim()).filter(Boolean);
55
+ }
56
+ return [];
57
+ }
58
+
47
59
  /**
48
60
  * Build a TaskRow-shaped object from CompleteTaskParams so the unified
49
61
  * renderSummaryContent() can be used at completion time (#2720).
@@ -63,8 +75,8 @@ function paramsToTaskRow(params: CompleteTaskParams, completedAt: string): TaskR
63
75
  blocker_discovered: params.blockerDiscovered ?? false,
64
76
  deviations: params.deviations ?? "",
65
77
  known_issues: params.knownIssues ?? "",
66
- key_files: params.keyFiles ?? [],
67
- key_decisions: params.keyDecisions ?? [],
78
+ key_files: normalizeListParam(params.keyFiles),
79
+ key_decisions: normalizeListParam(params.keyDecisions),
68
80
  full_summary_md: "",
69
81
  description: "",
70
82
  estimate: "",
@@ -140,8 +152,8 @@ export async function handleCompleteTask(
140
152
  }
141
153
 
142
154
  // All guards passed — perform writes
143
- insertMilestone({ id: params.milestoneId });
144
- insertSlice({ id: params.sliceId, milestoneId: params.milestoneId });
155
+ insertMilestone({ id: params.milestoneId, title: params.milestoneId });
156
+ insertSlice({ id: params.sliceId, milestoneId: params.milestoneId, title: params.sliceId });
145
157
  insertTask({
146
158
  id: params.taskId,
147
159
  sliceId: params.sliceId,
@@ -230,9 +242,19 @@ export async function handleCompleteTask(
230
242
  clearParseCache();
231
243
 
232
244
  // ── Post-mutation hook: projections, manifest, event log ───────────────
245
+ // Separate try/catch per step so a projection failure doesn't prevent
246
+ // the event log entry (critical for worktree reconciliation).
233
247
  try {
234
248
  await renderAllProjections(basePath, params.milestoneId);
249
+ } catch (projErr) {
250
+ logWarning("tool", `complete-task projection warning: ${(projErr as Error).message}`);
251
+ }
252
+ try {
235
253
  writeManifest(basePath);
254
+ } catch (mfErr) {
255
+ logWarning("tool", `complete-task manifest warning: ${(mfErr as Error).message}`);
256
+ }
257
+ try {
236
258
  appendEvent(basePath, {
237
259
  cmd: "complete-task",
238
260
  params: { milestoneId: params.milestoneId, sliceId: params.sliceId, taskId: params.taskId },
@@ -241,8 +263,8 @@ export async function handleCompleteTask(
241
263
  actor_name: params.actorName,
242
264
  trigger_reason: params.triggerReason,
243
265
  });
244
- } catch (hookErr) {
245
- logWarning("tool", `complete-task post-mutation hook warning: ${(hookErr as Error).message}`);
266
+ } catch (eventErr) {
267
+ logError("tool", `complete-task event log FAILED — completion invisible to reconciliation`, { error: (eventErr as Error).message });
246
268
  }
247
269
 
248
270
  return {
@@ -48,13 +48,13 @@ export interface PlanMilestoneParams {
48
48
  keyRisks?: Array<{ risk: string; whyItMatters: string }>;
49
49
  /** @optional — defaults to [] when omitted */
50
50
  proofStrategy?: Array<{ riskOrUnknown: string; retireIn: string; whatWillBeProven: string }>;
51
- /** @optional — defaults to "Not provided." when omitted */
51
+ /** @optional — defaults to "" when omitted */
52
52
  verificationContract?: string;
53
- /** @optional — defaults to "Not provided." when omitted */
53
+ /** @optional — defaults to "" when omitted */
54
54
  verificationIntegration?: string;
55
- /** @optional — defaults to "Not provided." when omitted */
55
+ /** @optional — defaults to "" when omitted */
56
56
  verificationOperational?: string;
57
- /** @optional — defaults to "Not provided." when omitted */
57
+ /** @optional — defaults to "" when omitted */
58
58
  verificationUat?: string;
59
59
  /** @optional — defaults to [] when omitted */
60
60
  definitionOfDone?: string[];
@@ -168,10 +168,10 @@ function validateParams(params: PlanMilestoneParams): PlanMilestoneParams {
168
168
  successCriteria: params.successCriteria ? validateStringArray(params.successCriteria, "successCriteria") : [],
169
169
  keyRisks: params.keyRisks ? validateRiskEntries(params.keyRisks) : [],
170
170
  proofStrategy: params.proofStrategy ? validateProofStrategy(params.proofStrategy) : [],
171
- verificationContract: params.verificationContract ?? "Not provided.",
172
- verificationIntegration: params.verificationIntegration ?? "Not provided.",
173
- verificationOperational: params.verificationOperational ?? "Not provided.",
174
- verificationUat: params.verificationUat ?? "Not provided.",
171
+ verificationContract: params.verificationContract ?? "",
172
+ verificationIntegration: params.verificationIntegration ?? "",
173
+ verificationOperational: params.verificationOperational ?? "",
174
+ verificationUat: params.verificationUat ?? "",
175
175
  definitionOfDone: params.definitionOfDone ? validateStringArray(params.definitionOfDone, "definitionOfDone") : [],
176
176
  requirementCoverage: params.requirementCoverage ?? "Not provided.",
177
177
  boundaryMapMarkdown: params.boundaryMapMarkdown ?? "Not provided.",
@@ -256,7 +256,8 @@ export async function handlePlanMilestone(
256
256
  boundaryMapMarkdown: params.boundaryMapMarkdown,
257
257
  });
258
258
 
259
- for (const slice of params.slices) {
259
+ for (let i = 0; i < params.slices.length; i++) {
260
+ const slice = params.slices[i]!;
260
261
  // Preserve completed/done status on re-plan (#2558).
261
262
  // Without this, a re-plan after milestone transition would reset
262
263
  // already-completed slices back to "pending".
@@ -272,6 +273,7 @@ export async function handlePlanMilestone(
272
273
  risk: slice.risk,
273
274
  depends: slice.depends,
274
275
  demo: slice.demo,
276
+ sequence: i + 1, // Preserve agent-ordered sequence (#3356)
275
277
  });
276
278
  upsertSlicePlanning(params.milestoneId, slice.sliceId, {
277
279
  goal: slice.goal,
@@ -186,8 +186,10 @@ export async function handleReassessRoadmap(
186
186
  });
187
187
  }
188
188
 
189
- // Insert new slices
190
- for (const added of params.sliceChanges.added) {
189
+ // Insert new slices — assign sequence after existing slices (#3356)
190
+ const existingCount = getMilestoneSlices(params.milestoneId).length;
191
+ for (let i = 0; i < params.sliceChanges.added.length; i++) {
192
+ const added = params.sliceChanges.added[i]!;
191
193
  insertSlice({
192
194
  id: added.sliceId,
193
195
  milestoneId: params.milestoneId,
@@ -196,6 +198,7 @@ export async function handleReassessRoadmap(
196
198
  risk: added.risk,
197
199
  depends: added.depends,
198
200
  demo: added.demo ?? "",
201
+ sequence: existingCount + i + 1,
199
202
  });
200
203
  }
201
204
 
@@ -0,0 +1,152 @@
1
+ // GSD — reopen-milestone tool handler
2
+
3
+ /**
4
+ * reopen-milestone handler — the core operation behind gsd_milestone_reopen.
5
+ *
6
+ * Resets a closed milestone back to "active", all of its slices to
7
+ * "in_progress", and all tasks to "pending". Cleans up stale filesystem
8
+ * artifacts so the DB-filesystem reconciler does not auto-correct
9
+ * entities back to "complete".
10
+ */
11
+
12
+ import {
13
+ getMilestone,
14
+ getMilestoneSlices,
15
+ getSliceTasks,
16
+ updateMilestoneStatus,
17
+ updateSliceStatus,
18
+ updateTaskStatus,
19
+ transaction,
20
+ } from "../gsd-db.js";
21
+ import { invalidateStateCache } from "../state.js";
22
+ import { isClosedStatus } from "../status-guards.js";
23
+ import { renderAllProjections } from "../workflow-projections.js";
24
+ import { writeManifest } from "../workflow-manifest.js";
25
+ import { appendEvent } from "../workflow-events.js";
26
+ import { logWarning } from "../workflow-logger.js";
27
+ import { debugLog } from "../debug-logger.js";
28
+ import { existsSync, unlinkSync } from "node:fs";
29
+ import { join } from "node:path";
30
+ import { resolveMilestonePath, resolveSlicePath, resolveTasksDir, clearPathCache } from "../paths.js";
31
+
32
+ export interface ReopenMilestoneParams {
33
+ milestoneId: string;
34
+ reason?: string;
35
+ /** Optional caller-provided identity for audit trail */
36
+ actorName?: string;
37
+ /** Optional caller-provided reason this action was triggered */
38
+ triggerReason?: string;
39
+ }
40
+
41
+ export interface ReopenMilestoneResult {
42
+ milestoneId: string;
43
+ slicesReset: number;
44
+ tasksReset: number;
45
+ }
46
+
47
+ export async function handleReopenMilestone(
48
+ params: ReopenMilestoneParams,
49
+ basePath: string,
50
+ ): Promise<ReopenMilestoneResult | { error: string }> {
51
+ // ── Validate required fields ────────────────────────────────────────────
52
+ if (!params.milestoneId || typeof params.milestoneId !== "string" || params.milestoneId.trim() === "") {
53
+ return { error: "milestoneId is required and must be a non-empty string" };
54
+ }
55
+
56
+ // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
57
+ let guardError: string | null = null;
58
+ let slicesResetCount = 0;
59
+ let tasksResetCount = 0;
60
+
61
+ transaction(() => {
62
+ const milestone = getMilestone(params.milestoneId);
63
+ if (!milestone) {
64
+ guardError = `milestone not found: ${params.milestoneId}`;
65
+ return;
66
+ }
67
+ if (!isClosedStatus(milestone.status)) {
68
+ guardError = `milestone ${params.milestoneId} is not closed (status: ${milestone.status}) — nothing to reopen`;
69
+ return;
70
+ }
71
+
72
+ updateMilestoneStatus(params.milestoneId, "active", null);
73
+
74
+ const slices = getMilestoneSlices(params.milestoneId);
75
+ slicesResetCount = slices.length;
76
+
77
+ for (const slice of slices) {
78
+ updateSliceStatus(params.milestoneId, slice.id, "in_progress");
79
+ const tasks = getSliceTasks(params.milestoneId, slice.id);
80
+ tasksResetCount += tasks.length;
81
+ for (const task of tasks) {
82
+ updateTaskStatus(params.milestoneId, slice.id, task.id, "pending");
83
+ }
84
+ }
85
+ });
86
+
87
+ if (guardError) {
88
+ return { error: guardError };
89
+ }
90
+
91
+ // ── Invalidate caches ────────────────────────────────────────────────────
92
+ invalidateStateCache();
93
+
94
+ // ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
95
+ // Without this, the DB-filesystem reconciler sees SUMMARY.md files and
96
+ // auto-corrects entities back to "complete", making reopen a no-op (#3161).
97
+ try {
98
+ const milestoneDir = resolveMilestonePath(basePath, params.milestoneId);
99
+ if (milestoneDir) {
100
+ const milestoneSummary = join(milestoneDir, `${params.milestoneId}-SUMMARY.md`);
101
+ if (existsSync(milestoneSummary)) unlinkSync(milestoneSummary);
102
+ }
103
+
104
+ const slices = getMilestoneSlices(params.milestoneId);
105
+ for (const slice of slices) {
106
+ const sliceDir = resolveSlicePath(basePath, params.milestoneId, slice.id);
107
+ if (sliceDir) {
108
+ const sliceSummary = join(sliceDir, `${slice.id}-SUMMARY.md`);
109
+ if (existsSync(sliceSummary)) unlinkSync(sliceSummary);
110
+ const sliceUat = join(sliceDir, `${slice.id}-UAT.md`);
111
+ if (existsSync(sliceUat)) unlinkSync(sliceUat);
112
+ }
113
+
114
+ const tasksDir = resolveTasksDir(basePath, params.milestoneId, slice.id);
115
+ if (tasksDir) {
116
+ const tasks = getSliceTasks(params.milestoneId, slice.id);
117
+ for (const task of tasks) {
118
+ const taskSummary = join(tasksDir, `${task.id}-SUMMARY.md`);
119
+ if (existsSync(taskSummary)) unlinkSync(taskSummary);
120
+ }
121
+ }
122
+ }
123
+ } catch (err) { debugLog("reopen-milestone-cleanup-failed", { milestoneId: params.milestoneId, error: String(err) }); }
124
+ clearPathCache();
125
+
126
+ // ── Post-mutation hook ───────────────────────────────────────────────────
127
+ try {
128
+ await renderAllProjections(basePath, params.milestoneId);
129
+ writeManifest(basePath);
130
+ appendEvent(basePath, {
131
+ cmd: "reopen-milestone",
132
+ params: {
133
+ milestoneId: params.milestoneId,
134
+ reason: params.reason ?? null,
135
+ slicesReset: slicesResetCount,
136
+ tasksReset: tasksResetCount,
137
+ },
138
+ ts: new Date().toISOString(),
139
+ actor: "agent",
140
+ actor_name: params.actorName,
141
+ trigger_reason: params.triggerReason,
142
+ });
143
+ } catch (hookErr) {
144
+ logWarning("tool", `reopen-milestone post-mutation hook warning: ${(hookErr as Error).message}`);
145
+ }
146
+
147
+ return {
148
+ milestoneId: params.milestoneId,
149
+ slicesReset: slicesResetCount,
150
+ tasksReset: tasksResetCount,
151
+ };
152
+ }
@@ -25,6 +25,9 @@ import { renderAllProjections } from "../workflow-projections.js";
25
25
  import { writeManifest } from "../workflow-manifest.js";
26
26
  import { appendEvent } from "../workflow-events.js";
27
27
  import { logWarning } from "../workflow-logger.js";
28
+ import { existsSync, unlinkSync } from "node:fs";
29
+ import { join } from "node:path";
30
+ import { resolveTasksDir, resolveSlicePath, clearPathCache } from "../paths.js";
28
31
 
29
32
  export interface ReopenSliceParams {
30
33
  milestoneId: string;
@@ -96,6 +99,30 @@ export async function handleReopenSlice(
96
99
  // ── Invalidate caches ────────────────────────────────────────────────────
97
100
  invalidateStateCache();
98
101
 
102
+ // ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
103
+ // Without this, the DB-filesystem reconciler sees SUMMARY.md files and
104
+ // auto-corrects tasks back to "complete", making reopen a no-op (#3161).
105
+ try {
106
+ const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
107
+ if (tasksDir) {
108
+ const tasks = getSliceTasks(params.milestoneId, params.sliceId);
109
+ for (const task of tasks) {
110
+ const summaryPath = join(tasksDir, `${task.id}-SUMMARY.md`);
111
+ if (existsSync(summaryPath)) unlinkSync(summaryPath);
112
+ }
113
+ }
114
+ const sliceDir = resolveSlicePath(basePath, params.milestoneId, params.sliceId);
115
+ if (sliceDir) {
116
+ const sliceSummary = join(sliceDir, `${params.sliceId}-SUMMARY.md`);
117
+ if (existsSync(sliceSummary)) unlinkSync(sliceSummary);
118
+ const sliceUat = join(sliceDir, `${params.sliceId}-UAT.md`);
119
+ if (existsSync(sliceUat)) unlinkSync(sliceUat);
120
+ }
121
+ } catch (cleanupErr) {
122
+ logWarning("tool", `reopen-slice artifact cleanup warning: ${(cleanupErr as Error).message}`);
123
+ }
124
+ clearPathCache();
125
+
99
126
  // ── Post-mutation hook ───────────────────────────────────────────────────
100
127
  try {
101
128
  await renderAllProjections(basePath, params.milestoneId);
@@ -23,6 +23,9 @@ import { renderAllProjections } from "../workflow-projections.js";
23
23
  import { writeManifest } from "../workflow-manifest.js";
24
24
  import { appendEvent } from "../workflow-events.js";
25
25
  import { logWarning } from "../workflow-logger.js";
26
+ import { existsSync, unlinkSync } from "node:fs";
27
+ import { join } from "node:path";
28
+ import { resolveTasksDir, clearPathCache } from "../paths.js";
26
29
 
27
30
  export interface ReopenTaskParams {
28
31
  milestoneId: string;
@@ -100,6 +103,20 @@ export async function handleReopenTask(
100
103
  // ── Invalidate caches ────────────────────────────────────────────────────
101
104
  invalidateStateCache();
102
105
 
106
+ // ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
107
+ // Without this, the DB-filesystem reconciler sees the SUMMARY.md and
108
+ // auto-corrects the task back to "complete", making reopen a no-op (#3161).
109
+ try {
110
+ const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
111
+ if (tasksDir) {
112
+ const summaryPath = join(tasksDir, `${params.taskId}-SUMMARY.md`);
113
+ if (existsSync(summaryPath)) unlinkSync(summaryPath);
114
+ }
115
+ } catch (cleanupErr) {
116
+ logWarning("tool", `reopen-task artifact cleanup warning: ${(cleanupErr as Error).message}`);
117
+ }
118
+ clearPathCache();
119
+
103
120
  // ── Post-mutation hook ───────────────────────────────────────────────────
104
121
  try {
105
122
  await renderAllProjections(basePath, params.milestoneId);