@opengsd/gsd-pi 1.7.0 → 1.8.1-dev.897b3c90

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 (465) hide show
  1. package/README.md +8 -6
  2. package/dist/cli.js +5 -1
  3. package/dist/headless-events.d.ts +2 -1
  4. package/dist/headless-events.js +11 -3
  5. package/dist/headless-milestone-readiness.d.ts +29 -0
  6. package/dist/headless-milestone-readiness.js +161 -0
  7. package/dist/headless.js +25 -2
  8. package/dist/print-mode-exit.d.ts +12 -0
  9. package/dist/print-mode-exit.js +14 -0
  10. package/dist/resources/.managed-resources-content-hash +1 -1
  11. package/dist/resources/GSD-WORKFLOW.md +16 -47
  12. package/dist/resources/extensions/browser-tools/capture.js +2 -77
  13. package/dist/resources/extensions/browser-tools/screenshot-constraints.js +83 -0
  14. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -2
  15. package/dist/resources/extensions/claude-code-cli/turn-assembler.js +30 -2
  16. package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +74 -19
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -0
  19. package/dist/resources/extensions/gsd/auto-prompts.js +35 -1
  20. package/dist/resources/extensions/gsd/auto.js +5 -1
  21. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +78 -4
  22. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +84 -26
  23. package/dist/resources/extensions/gsd/command-feedback.js +16 -12
  24. package/dist/resources/extensions/gsd/compat/compat-marker.js +19 -2
  25. package/dist/resources/extensions/gsd/db/command-queue.js +7 -2
  26. package/dist/resources/extensions/gsd/db/engine.js +51 -16
  27. package/dist/resources/extensions/gsd/db/writers/cascades.js +5 -1
  28. package/dist/resources/extensions/gsd/db/writers/status.js +13 -3
  29. package/dist/resources/extensions/gsd/db-base-schema.js +28 -0
  30. package/dist/resources/extensions/gsd/db-migration-steps.js +30 -0
  31. package/dist/resources/extensions/gsd/db-schema-metadata.js +4 -1
  32. package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -4
  33. package/dist/resources/extensions/gsd/doctor-history.js +65 -0
  34. package/dist/resources/extensions/gsd/doctor-state-checks.js +509 -0
  35. package/dist/resources/extensions/gsd/doctor-workspace-checks.js +91 -0
  36. package/dist/resources/extensions/gsd/doctor.js +14 -653
  37. package/dist/resources/extensions/gsd/flat-phase-migration.js +156 -21
  38. package/dist/resources/extensions/gsd/gsd-db.js +187 -45
  39. package/dist/resources/extensions/gsd/guided-flow.js +29 -10
  40. package/dist/resources/extensions/gsd/markdown-renderer.js +16 -2
  41. package/dist/resources/extensions/gsd/md-importer.js +109 -16
  42. package/dist/resources/extensions/gsd/migrate/command.js +19 -162
  43. package/dist/resources/extensions/gsd/migrate/execution.js +98 -0
  44. package/dist/resources/extensions/gsd/migrate/plan.js +49 -0
  45. package/dist/resources/extensions/gsd/migrate/planning-writer.js +20 -19
  46. package/dist/resources/extensions/gsd/migrate/presentation.js +64 -0
  47. package/dist/resources/extensions/gsd/migration-auto-check.js +4 -0
  48. package/dist/resources/extensions/gsd/parallel-eligibility.js +5 -4
  49. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +28 -43
  50. package/dist/resources/extensions/gsd/paths.js +4 -1
  51. package/dist/resources/extensions/gsd/phase-anchor.js +7 -2
  52. package/dist/resources/extensions/gsd/post-execution-checks.js +4 -4
  53. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  54. package/dist/resources/extensions/gsd/safety/evidence-collector.js +26 -1
  55. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +30 -5
  56. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +27 -13
  57. package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +13 -3
  58. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +54 -14
  59. package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +48 -5
  60. package/dist/resources/extensions/gsd/stop-notice.js +15 -1
  61. package/dist/resources/extensions/gsd/sync-lock.js +13 -7
  62. package/dist/resources/extensions/gsd/tools/complete-task.js +82 -1
  63. package/dist/resources/extensions/gsd/tools/plan-task.js +13 -15
  64. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +18 -15
  65. package/dist/resources/extensions/gsd/tools/replan-task.js +128 -0
  66. package/dist/resources/extensions/gsd/tools/rework-brief.js +74 -0
  67. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +81 -0
  68. package/dist/resources/extensions/gsd/unit-registry.js +4 -0
  69. package/dist/resources/extensions/gsd/uok/plan-v2.js +19 -3
  70. package/dist/resources/extensions/gsd/worktree-state-projection.js +6 -0
  71. package/dist/resources/extensions/mac-tools/index.js +31 -9
  72. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  73. package/dist/web/standalone/.next/BUILD_ID +1 -1
  74. package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
  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/required-server-files.json +3 -3
  78. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  79. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  81. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  89. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  98. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  117. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  139. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  149. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  155. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  171. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  175. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/index.html +1 -1
  185. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  186. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  187. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  188. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  190. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  191. package/dist/web/standalone/.next/server/app/page.js +2 -2
  192. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
  194. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  195. package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
  196. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  197. package/dist/web/standalone/.next/server/middleware.js +3 -3
  198. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  199. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  200. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +1 -0
  202. package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
  203. package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
  205. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
  206. package/dist/web/standalone/.next/static/css/3b1ceb7abfb9a230.css +1 -0
  207. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  208. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  209. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  210. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  211. package/dist/web/standalone/package.json +0 -1
  212. package/dist/web/standalone/server.js +1 -1
  213. package/dist/web-mode.js +31 -3
  214. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  215. package/integrations/hermes/pyproject.toml +1 -1
  216. package/package.json +12 -10
  217. package/packages/cloud-mcp-gateway/package.json +2 -2
  218. package/packages/contracts/dist/workflow.d.ts +14 -0
  219. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  220. package/packages/contracts/dist/workflow.js +16 -0
  221. package/packages/contracts/dist/workflow.js.map +1 -1
  222. package/packages/contracts/package.json +1 -1
  223. package/packages/daemon/dist/cloud-config.d.ts +1 -1
  224. package/packages/daemon/dist/cloud-config.d.ts.map +1 -1
  225. package/packages/daemon/dist/cloud-config.js +30 -8
  226. package/packages/daemon/dist/cloud-config.js.map +1 -1
  227. package/packages/daemon/dist/cloud-config.test.js +69 -1
  228. package/packages/daemon/dist/cloud-config.test.js.map +1 -1
  229. package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -1
  230. package/packages/daemon/dist/local-tool-executor.js +2 -0
  231. package/packages/daemon/dist/local-tool-executor.js.map +1 -1
  232. package/packages/daemon/package.json +6 -6
  233. package/packages/gsd-agent-core/package.json +5 -5
  234. package/packages/gsd-agent-modes/package.json +7 -7
  235. package/packages/gsd-cloud/README.md +18 -4
  236. package/packages/gsd-cloud/bin/gsd-cloud.js +6 -5
  237. package/packages/gsd-cloud/dist/cli.d.ts +5 -0
  238. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -0
  239. package/packages/gsd-cloud/dist/cli.js +160 -0
  240. package/packages/gsd-cloud/dist/cli.js.map +1 -0
  241. package/packages/gsd-cloud/dist/cloud-config.d.ts +19 -0
  242. package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -0
  243. package/packages/gsd-cloud/dist/cloud-config.js +231 -0
  244. package/packages/gsd-cloud/dist/cloud-config.js.map +1 -0
  245. package/packages/gsd-cloud/dist/cloud-config.test.d.ts +2 -0
  246. package/packages/gsd-cloud/dist/cloud-config.test.d.ts.map +1 -0
  247. package/packages/gsd-cloud/dist/cloud-config.test.js +118 -0
  248. package/packages/gsd-cloud/dist/cloud-config.test.js.map +1 -0
  249. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts +2 -0
  250. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts.map +1 -0
  251. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js +37 -0
  252. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js.map +1 -0
  253. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +35 -0
  254. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -0
  255. package/packages/gsd-cloud/dist/cloud-runtime.js +258 -0
  256. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -0
  257. package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts +2 -0
  258. package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts.map +1 -0
  259. package/packages/gsd-cloud/dist/cloud-runtime.test.js +88 -0
  260. package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -0
  261. package/packages/gsd-cloud/dist/cloud-token.d.ts +3 -0
  262. package/packages/gsd-cloud/dist/cloud-token.d.ts.map +1 -0
  263. package/packages/gsd-cloud/dist/cloud-token.js +37 -0
  264. package/packages/gsd-cloud/dist/cloud-token.js.map +1 -0
  265. package/packages/gsd-cloud/dist/config.d.ts +17 -0
  266. package/packages/gsd-cloud/dist/config.d.ts.map +1 -0
  267. package/packages/gsd-cloud/dist/config.js +146 -0
  268. package/packages/gsd-cloud/dist/config.js.map +1 -0
  269. package/packages/gsd-cloud/dist/device-flow.d.ts +24 -0
  270. package/packages/gsd-cloud/dist/device-flow.d.ts.map +1 -0
  271. package/packages/gsd-cloud/dist/device-flow.js +128 -0
  272. package/packages/gsd-cloud/dist/device-flow.js.map +1 -0
  273. package/packages/gsd-cloud/dist/device-flow.test.d.ts +2 -0
  274. package/packages/gsd-cloud/dist/device-flow.test.d.ts.map +1 -0
  275. package/packages/gsd-cloud/dist/device-flow.test.js +155 -0
  276. package/packages/gsd-cloud/dist/device-flow.test.js.map +1 -0
  277. package/packages/gsd-cloud/dist/executors/claude-executor.d.ts +6 -0
  278. package/packages/gsd-cloud/dist/executors/claude-executor.d.ts.map +1 -0
  279. package/packages/gsd-cloud/dist/executors/claude-executor.js +15 -0
  280. package/packages/gsd-cloud/dist/executors/claude-executor.js.map +1 -0
  281. package/packages/gsd-cloud/dist/executors/codex-executor.d.ts +6 -0
  282. package/packages/gsd-cloud/dist/executors/codex-executor.d.ts.map +1 -0
  283. package/packages/gsd-cloud/dist/executors/codex-executor.js +15 -0
  284. package/packages/gsd-cloud/dist/executors/codex-executor.js.map +1 -0
  285. package/packages/gsd-cloud/dist/executors/executor.d.ts +28 -0
  286. package/packages/gsd-cloud/dist/executors/executor.d.ts.map +1 -0
  287. package/packages/gsd-cloud/dist/executors/executor.js +11 -0
  288. package/packages/gsd-cloud/dist/executors/executor.js.map +1 -0
  289. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts +34 -0
  290. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts.map +1 -0
  291. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js +179 -0
  292. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js.map +1 -0
  293. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts +2 -0
  294. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts.map +1 -0
  295. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js +44 -0
  296. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js.map +1 -0
  297. package/packages/gsd-cloud/dist/executors/index.d.ts +17 -0
  298. package/packages/gsd-cloud/dist/executors/index.d.ts.map +1 -0
  299. package/packages/gsd-cloud/dist/executors/index.js +29 -0
  300. package/packages/gsd-cloud/dist/executors/index.js.map +1 -0
  301. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts +36 -0
  302. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts.map +1 -0
  303. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js +176 -0
  304. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js.map +1 -0
  305. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts +2 -0
  306. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts.map +1 -0
  307. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js +34 -0
  308. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js.map +1 -0
  309. package/packages/gsd-cloud/dist/index.d.ts +7 -0
  310. package/packages/gsd-cloud/dist/index.d.ts.map +1 -0
  311. package/packages/gsd-cloud/dist/index.js +8 -0
  312. package/packages/gsd-cloud/dist/index.js.map +1 -0
  313. package/packages/gsd-cloud/dist/inject-gateway.d.ts +16 -0
  314. package/packages/gsd-cloud/dist/inject-gateway.d.ts.map +1 -0
  315. package/packages/gsd-cloud/dist/inject-gateway.js +29 -0
  316. package/packages/gsd-cloud/dist/inject-gateway.js.map +1 -0
  317. package/packages/gsd-cloud/dist/inject-gateway.test.d.ts +2 -0
  318. package/packages/gsd-cloud/dist/inject-gateway.test.d.ts.map +1 -0
  319. package/packages/gsd-cloud/dist/inject-gateway.test.js +41 -0
  320. package/packages/gsd-cloud/dist/inject-gateway.test.js.map +1 -0
  321. package/packages/gsd-cloud/dist/logger.d.ts +25 -0
  322. package/packages/gsd-cloud/dist/logger.d.ts.map +1 -0
  323. package/packages/gsd-cloud/dist/logger.js +72 -0
  324. package/packages/gsd-cloud/dist/logger.js.map +1 -0
  325. package/packages/gsd-cloud/dist/types.d.ts +51 -0
  326. package/packages/gsd-cloud/dist/types.d.ts.map +1 -0
  327. package/packages/gsd-cloud/dist/types.js +9 -0
  328. package/packages/gsd-cloud/dist/types.js.map +1 -0
  329. package/packages/gsd-cloud/package.json +7 -5
  330. package/packages/mcp-server/README.md +8 -0
  331. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  332. package/packages/mcp-server/dist/workflow-tools.js +64 -2
  333. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  334. package/packages/mcp-server/package.json +4 -4
  335. package/packages/native/package.json +1 -1
  336. package/packages/pi-agent-core/package.json +1 -1
  337. package/packages/pi-ai/dist/models.generated.d.ts +60 -0
  338. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  339. package/packages/pi-ai/dist/models.generated.js +54 -0
  340. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  341. package/packages/pi-ai/dist/providers/amazon-bedrock.js +1 -0
  342. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  343. package/packages/pi-ai/dist/providers/anthropic.js +19 -0
  344. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  345. package/packages/pi-ai/package.json +1 -1
  346. package/packages/pi-coding-agent/package.json +7 -7
  347. package/packages/pi-tui/package.json +2 -2
  348. package/packages/rpc-client/package.json +2 -2
  349. package/pkg/package.json +1 -1
  350. package/src/resources/GSD-WORKFLOW.md +16 -47
  351. package/src/resources/extensions/browser-tools/capture.ts +2 -86
  352. package/src/resources/extensions/browser-tools/screenshot-constraints.ts +94 -0
  353. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -1
  354. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +108 -0
  355. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +30 -2
  356. package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
  357. package/src/resources/extensions/gsd/auto-dispatch.ts +86 -19
  358. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -0
  359. package/src/resources/extensions/gsd/auto-prompts.ts +38 -1
  360. package/src/resources/extensions/gsd/auto.ts +5 -1
  361. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +91 -4
  362. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -24
  363. package/src/resources/extensions/gsd/command-feedback.ts +19 -12
  364. package/src/resources/extensions/gsd/compat/compat-marker.ts +21 -4
  365. package/src/resources/extensions/gsd/db/command-queue.ts +7 -1
  366. package/src/resources/extensions/gsd/db/engine.ts +53 -17
  367. package/src/resources/extensions/gsd/db/writers/cascades.ts +5 -1
  368. package/src/resources/extensions/gsd/db/writers/status.ts +25 -8
  369. package/src/resources/extensions/gsd/db-base-schema.ts +30 -0
  370. package/src/resources/extensions/gsd/db-migration-steps.ts +32 -0
  371. package/src/resources/extensions/gsd/db-schema-metadata.ts +4 -1
  372. package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -4
  373. package/src/resources/extensions/gsd/doctor-history.ts +81 -0
  374. package/src/resources/extensions/gsd/doctor-state-checks.ts +527 -0
  375. package/src/resources/extensions/gsd/doctor-workspace-checks.ts +97 -0
  376. package/src/resources/extensions/gsd/doctor.ts +14 -673
  377. package/src/resources/extensions/gsd/flat-phase-migration.ts +160 -19
  378. package/src/resources/extensions/gsd/gsd-db.ts +255 -52
  379. package/src/resources/extensions/gsd/guided-flow.ts +39 -10
  380. package/src/resources/extensions/gsd/markdown-renderer.ts +23 -7
  381. package/src/resources/extensions/gsd/md-importer.ts +116 -13
  382. package/src/resources/extensions/gsd/migrate/command.ts +21 -227
  383. package/src/resources/extensions/gsd/migrate/execution.ts +136 -0
  384. package/src/resources/extensions/gsd/migrate/plan.ts +101 -0
  385. package/src/resources/extensions/gsd/migrate/planning-writer.ts +21 -23
  386. package/src/resources/extensions/gsd/migrate/presentation.ts +83 -0
  387. package/src/resources/extensions/gsd/migration-auto-check.ts +3 -0
  388. package/src/resources/extensions/gsd/parallel-eligibility.ts +5 -4
  389. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +37 -40
  390. package/src/resources/extensions/gsd/paths.ts +4 -1
  391. package/src/resources/extensions/gsd/phase-anchor.ts +8 -2
  392. package/src/resources/extensions/gsd/post-execution-checks.ts +4 -4
  393. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  394. package/src/resources/extensions/gsd/safety/evidence-collector.ts +28 -1
  395. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +31 -5
  396. package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +30 -13
  397. package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +16 -3
  398. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +67 -20
  399. package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +47 -5
  400. package/src/resources/extensions/gsd/state-reconciliation/types.ts +2 -2
  401. package/src/resources/extensions/gsd/stop-notice.ts +18 -1
  402. package/src/resources/extensions/gsd/sync-lock.ts +14 -6
  403. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +96 -1
  404. package/src/resources/extensions/gsd/tests/command-feedback.test.ts +12 -0
  405. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +104 -0
  406. package/src/resources/extensions/gsd/tests/complete-task.test.ts +286 -0
  407. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +76 -1
  408. package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +186 -0
  409. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +29 -0
  410. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +123 -0
  411. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +50 -0
  412. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +49 -0
  413. package/src/resources/extensions/gsd/tests/doctor-history-public-api.test.ts +25 -0
  414. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +81 -0
  415. package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +22 -0
  416. package/src/resources/extensions/gsd/tests/external-edit-scoped-import.test.ts +262 -0
  417. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +202 -0
  418. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +29 -0
  419. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +25 -0
  420. package/src/resources/extensions/gsd/tests/import-slice-milestone-completion-preserve.test.ts +116 -0
  421. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +53 -0
  422. package/src/resources/extensions/gsd/tests/migrate-plan.test.ts +68 -0
  423. package/src/resources/extensions/gsd/tests/migrate-presentation.test.ts +98 -0
  424. package/src/resources/extensions/gsd/tests/migrate-safety-audit.test.ts +1 -1
  425. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +53 -0
  426. package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +55 -0
  427. package/src/resources/extensions/gsd/tests/parallel-eligibility-file-overlap.test.ts +146 -0
  428. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +59 -0
  429. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -0
  430. package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +32 -4
  431. package/src/resources/extensions/gsd/tests/plan-task.test.ts +26 -23
  432. package/src/resources/extensions/gsd/tests/planning-writer.test.ts +47 -1
  433. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
  434. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +18 -3
  435. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +50 -0
  436. package/src/resources/extensions/gsd/tests/replan-task-handler.test.ts +103 -0
  437. package/src/resources/extensions/gsd/tests/rework-brief.test.ts +197 -0
  438. package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +21 -0
  439. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +177 -25
  440. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +17 -0
  441. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +19 -0
  442. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +34 -0
  443. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +93 -1
  444. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -0
  445. package/src/resources/extensions/gsd/tests/workflow-migration-gates.test.ts +139 -0
  446. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +36 -0
  447. package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +52 -0
  448. package/src/resources/extensions/gsd/tools/complete-task.ts +120 -0
  449. package/src/resources/extensions/gsd/tools/plan-task.ts +14 -16
  450. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +18 -17
  451. package/src/resources/extensions/gsd/tools/replan-task.ts +160 -0
  452. package/src/resources/extensions/gsd/tools/rework-brief.ts +91 -0
  453. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +92 -0
  454. package/src/resources/extensions/gsd/types.ts +11 -0
  455. package/src/resources/extensions/gsd/unit-registry.ts +4 -0
  456. package/src/resources/extensions/gsd/uok/plan-v2.ts +20 -3
  457. package/src/resources/extensions/gsd/worktree-state-projection.ts +12 -0
  458. package/src/resources/extensions/mac-tools/index.ts +52 -9
  459. package/src/resources/extensions/mac-tools/tests/mac-screenshot-payload.test.cjs +70 -0
  460. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
  461. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
  462. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
  463. package/dist/web/standalone/.next/static/css/18c2fdf261bfc1b7.css +0 -1
  464. /package/dist/web/standalone/.next/static/{enWQFRY-aLTUq3wmxG2Qz → TezjdjSHBVRRr54XjAvte}/_buildManifest.js +0 -0
  465. /package/dist/web/standalone/.next/static/{enWQFRY-aLTUq3wmxG2Qz → TezjdjSHBVRRr54XjAvte}/_ssgManifest.js +0 -0
@@ -1698,6 +1698,10 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
1698
1698
  `Safety: task ${sTid} claimed passing verification that failed in recorded execution`,
1699
1699
  "error",
1700
1700
  );
1701
+ const gitActionResult = await runCloseoutGitAction(pctx, s.currentUnit);
1702
+ if (gitActionResult === "dispatched") {
1703
+ return "dispatched";
1704
+ }
1701
1705
  await pauseAuto(ctx, pi);
1702
1706
  return "dispatched";
1703
1707
  }
@@ -28,7 +28,7 @@ import type { GSDPreferences } from "./preferences.js";
28
28
  import { join, basename, relative, sep } from "node:path";
29
29
  import { existsSync } from "node:fs";
30
30
  import { computeBudgets, resolveExecutorContextWindow, truncateAtSectionBoundary, type MinimalModelRegistry } from "./context-budget.js";
31
- import { getGateResults, getPendingGates, getPendingGatesForTurn } from "./gsd-db.js";
31
+ import { getBlockingReworkFindingsForTask, getGateResults, getPendingGates, getPendingGatesForTurn } from "./gsd-db.js";
32
32
  import {
33
33
  GATE_REGISTRY,
34
34
  assertGateCoverage,
@@ -106,6 +106,34 @@ function resolveSummaryBudgetChars(): number {
106
106
  return resolvePromptBudgets().summaryBudgetChars;
107
107
  }
108
108
 
109
+
110
+ function renderBlockingReworkFindingsBlock(mid: string, sid: string, tid: string): string {
111
+ const findings = getBlockingReworkFindingsForTask(mid, sid, tid)
112
+ .filter((finding) => finding.status === "pending");
113
+ if (findings.length === 0) return "";
114
+
115
+ const lines = [
116
+ "## Blocking Rework Findings (non-optional)",
117
+ "",
118
+ "This task has structured blocking rework findings. Address every finding before calling `gsd_task_complete`. Include `reworkResolution` entries with each `findingId`, `status: \"resolved\"`, and concrete evidence. If deferring a finding, use `status: \"deferred-with-override\"` with concrete evidence and `decisionRef`.",
119
+ "",
120
+ ];
121
+ for (const finding of findings) {
122
+ lines.push(`### ${finding.finding_id} (${finding.severity})`);
123
+ lines.push(`- Status: ${finding.status}`);
124
+ lines.push(`- Finding: ${finding.description}`);
125
+ lines.push(`- Required fix: ${finding.required_fix}`);
126
+ if (finding.verification_commands.length > 0) {
127
+ lines.push("- Required verification:");
128
+ for (const command of finding.verification_commands) {
129
+ lines.push(` - \`${command}\``);
130
+ }
131
+ }
132
+ lines.push("");
133
+ }
134
+ return lines.join("\n").trim();
135
+ }
136
+
109
137
  function formatProjectClassificationForPlanning(classification: ProjectClassification): string {
110
138
  const sampleFiles = classification.contentFiles.slice(0, 8);
111
139
  const sample = sampleFiles.length > 0 ? sampleFiles.map((file) => `\`${file}\``).join(", ") : "(none)";
@@ -2745,6 +2773,15 @@ export async function buildExecuteTaskPrompt(
2745
2773
  logWarning("prompt", `reopen reason injection failed: ${(reopenErr as Error).message}`);
2746
2774
  }
2747
2775
 
2776
+ const reworkFindingsBlock = renderBlockingReworkFindingsBlock(mid, sid, tid);
2777
+ if (reworkFindingsBlock) {
2778
+ const block = reworkFindingsBlock + "\n\n---\n\n";
2779
+ phaseAnchorSection = phaseAnchorSection
2780
+ ? `${block}${phaseAnchorSection}`
2781
+ : block;
2782
+ trackPromptContext(contextTelemetry, "rework-findings", "inline", reworkFindingsBlock);
2783
+ }
2784
+
2748
2785
  // ADR-011 Phase 2: inject any resolved-but-unapplied escalation override
2749
2786
  // into this task's prompt. Claim is atomic via DB UPDATE WHERE IS NULL, so
2750
2787
  // if a parallel build already injected it, we skip. Feature-gated by
@@ -19,7 +19,7 @@ import type {
19
19
  SessionMessageEntry,
20
20
  } from "@gsd/pi-coding-agent";
21
21
 
22
- import { deriveState } from "./state.js";
22
+ import { deriveState, invalidateStateCache } from "./state.js";
23
23
  import {
24
24
  buildRequirementsBacklogSummaryLines,
25
25
  countUnmappedActiveRequirements,
@@ -443,6 +443,9 @@ function captureMilestoneLockEnv(milestoneId: string | null): void {
443
443
  } else {
444
444
  delete process.env.GSD_MILESTONE_LOCK;
445
445
  }
446
+ // The derive cache is keyed on basePath only; GSD_MILESTONE_LOCK changes
447
+ // what deriveState computes, so a lock flip must not serve a cached result.
448
+ invalidateStateCache();
446
449
  }
447
450
 
448
451
  function restoreMilestoneLockEnv(): void {
@@ -457,6 +460,7 @@ function restoreMilestoneLockEnv(): void {
457
460
  s.previousMilestoneLockEnv = null;
458
461
  s.hadMilestoneLockEnv = false;
459
462
  s.milestoneLockEnvCaptured = false;
463
+ invalidateStateCache();
460
464
  }
461
465
 
462
466
  /**
@@ -771,12 +771,12 @@ export function registerDbTools(pi: ExtensionAPI): void {
771
771
  name: "gsd_plan_task",
772
772
  label: "Plan Task",
773
773
  description:
774
- "Write task planning state to the GSD database, render tasks/T##-PLAN.md from DB, and clear caches after a successful render.",
775
- promptSnippet: "Plan a task via DB write + task PLAN render + cache invalidation",
774
+ "Write task planning state to the GSD database, render the slice PLAN from DB, and clear caches after a successful render.",
775
+ promptSnippet: "Plan a task via DB write + slice PLAN render + cache invalidation",
776
776
  promptGuidelines: [
777
- "Use gsd_plan_task for task planning instead of writing tasks/T##-PLAN.md directly.",
777
+ "Use gsd_plan_task for task planning instead of writing task PLAN files directly.",
778
778
  "Keep parameters flat and provide the full task planning payload.",
779
- "The tool validates input, requires an existing parent slice, writes task planning data, renders the task PLAN file from DB, and clears both state and parse caches after success.",
779
+ "The tool validates input, requires an existing parent slice, writes task planning data, renders the slice PLAN file from DB, and clears both state and parse caches after success.",
780
780
  "Use the canonical name gsd_plan_task; gsd_task_plan is only an alias.",
781
781
  ],
782
782
  parameters: Type.Object({
@@ -860,6 +860,15 @@ export function registerDbTools(pi: ExtensionAPI): void {
860
860
  }),
861
861
  { description: "Array of verification evidence entries (structured objects only)" },
862
862
  )),
863
+ reworkResolution: Type.Optional(Type.Array(
864
+ Type.Object({
865
+ findingId: Type.String({ description: "Rework finding ID being resolved (e.g. F1)" }),
866
+ status: StringEnum(["resolved", "deferred-with-override"], { description: "Resolution status for this finding" }),
867
+ evidence: Type.String({ description: "Concrete evidence that the required fix was completed" }),
868
+ decisionRef: Type.Optional(Type.String({ description: "Decision reference required for deferred-with-override findings" })),
869
+ }),
870
+ { description: "Resolution evidence for structured rework findings linked to this task" },
871
+ )),
863
872
  // Single-writer v3 audit trail (Stream 2): caller-provided actor identity + causation.
864
873
  actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail (e.g. 'executor-01', 'gsd-orchestrator')" })),
865
874
  triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered (e.g. 'task verified after retry')" })),
@@ -1180,6 +1189,84 @@ export function registerDbTools(pi: ExtensionAPI): void {
1180
1189
 
1181
1190
  registerWorkflowTool(pi, replanSliceTool);
1182
1191
 
1192
+ // ─── gsd_replan_task ───────────────────────────────────────────────────
1193
+
1194
+ const replanTaskExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1195
+ const { executeReplanTask } = await loadWorkflowExecutors();
1196
+ return executeReplanTask(params, resolveWorkflowToolBasePath(_ctx, params));
1197
+ };
1198
+
1199
+ const replanTaskTool = {
1200
+ name: "gsd_replan_task",
1201
+ label: "Replan Task",
1202
+ description:
1203
+ "Update one pending task's planning contract after rework without touching sibling tasks. " +
1204
+ "Completed tasks are structurally protected. Re-renders the task/slice PLAN projection and records replan history.",
1205
+ promptSnippet: "Replan one pending GSD task after rework",
1206
+ promptGuidelines: [
1207
+ "Use gsd_replan_task when a reopened pending task needs its title, description, files, verify, inputs, or expectedOutput updated for rework.",
1208
+ "The task must already exist and be pending; completed tasks are rejected.",
1209
+ "Set reworkBriefRef when a structured rework brief triggered the task replan.",
1210
+ ],
1211
+ parameters: Type.Object({
1212
+ milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
1213
+ sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
1214
+ taskId: Type.String({ description: "Task ID (e.g. T01)" }),
1215
+ title: Type.String({ description: "Updated task title" }),
1216
+ description: Type.String({ description: "Updated task description incorporating rework scope" }),
1217
+ estimate: Type.String({ description: "Updated task estimate string" }),
1218
+ files: Type.Array(Type.String(), { description: "Updated files likely touched" }),
1219
+ verify: Type.String({ description: "Updated verification command or block" }),
1220
+ inputs: Type.Array(Type.String(), { description: "Updated input files or references" }),
1221
+ expectedOutput: Type.Array(Type.String(), { description: "Updated files this task creates or overwrites" }),
1222
+ reworkBriefRef: Type.Optional(Type.String({ description: "Rework brief ID/reference that triggered this task replan" })),
1223
+ actorName: Type.Optional(Type.String({ description: "Caller-provided actor identity for the audit trail" })),
1224
+ triggerReason: Type.Optional(Type.String({ description: "Caller-provided reason this action was triggered" })),
1225
+ }),
1226
+ execute: replanTaskExecute,
1227
+ };
1228
+
1229
+ registerWorkflowTool(pi, replanTaskTool);
1230
+
1231
+ // ─── gsd_rework_brief_save ─────────────────────────────────────────────
1232
+
1233
+ const reworkBriefSaveExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
1234
+ const { executeReworkBriefSave } = await loadWorkflowExecutors();
1235
+ return executeReworkBriefSave(params, resolveWorkflowToolBasePath(_ctx, params));
1236
+ };
1237
+
1238
+ const reworkBriefSaveTool = {
1239
+ name: "gsd_rework_brief_save",
1240
+ label: "Save Rework Brief",
1241
+ description:
1242
+ "Persist a structured rework brief for a task. Blocking findings are enforced by gsd_task_complete until resolved with evidence.",
1243
+ promptSnippet: "Persist structured task rework findings",
1244
+ promptGuidelines: [
1245
+ "Use gsd_rework_brief_save when post-unit checks produce task rework findings.",
1246
+ "Use severity 'blocking' for findings that must be resolved before gsd_task_complete succeeds.",
1247
+ "Each finding needs a stable findingId, requiredFix, and verificationCommands.",
1248
+ ],
1249
+ parameters: Type.Object({
1250
+ briefId: Type.Optional(Type.String({ description: "Stable brief ID; omitted defaults to RB-<milestone>-<slice>-<task>" })),
1251
+ milestoneId: Type.String({ description: "Milestone ID (e.g. M001)" }),
1252
+ sliceId: Type.String({ description: "Slice ID (e.g. S01)" }),
1253
+ taskId: Type.String({ description: "Task ID (e.g. T01)" }),
1254
+ findings: Type.Array(Type.Object({
1255
+ findingId: Type.String({ description: "Stable finding ID (e.g. F1)" }),
1256
+ severity: StringEnum(["blocking", "advisory"], { description: "Whether this finding blocks task completion" }),
1257
+ description: Type.String({ description: "What was found" }),
1258
+ requiredFix: Type.String({ description: "What must change" }),
1259
+ verificationCommands: Type.Array(Type.String(), { description: "Commands that must pass after the fix" }),
1260
+ status: Type.Optional(StringEnum(["pending", "resolved", "deferred-with-override"], { description: "Finding status; defaults to pending" })),
1261
+ evidence: Type.Optional(Type.String({ description: "Resolution evidence when already resolved" })),
1262
+ decisionRef: Type.Optional(Type.String({ description: "Decision reference for deferred-with-override" })),
1263
+ }), { description: "Structured rework findings for this task" }),
1264
+ }),
1265
+ execute: reworkBriefSaveExecute,
1266
+ };
1267
+
1268
+ registerWorkflowTool(pi, reworkBriefSaveTool);
1269
+
1183
1270
  // ─── gsd_reassess_roadmap (gsd_roadmap_reassess alias) ─────────────────
1184
1271
 
1185
1272
  const reassessRoadmapExecute = async (_toolCallId: string, params: any, _signal: AbortSignal | undefined, _onUpdate: unknown, _ctx: unknown) => {
@@ -10,6 +10,7 @@ import { loadJsonFileOrNull } from "../json-persistence.js";
10
10
  import { getIsolationMode } from "../preferences.js";
11
11
  import { compileSubagentPermissionContract, type ToolsPolicy } from "../unit-context-manifest.js";
12
12
  import { logWarning } from "../workflow-logger.js";
13
+ import { acquireSyncLock, releaseSyncLock } from "../sync-lock.js";
13
14
  import { isGsdWorktreePath, resolveWorktreeProjectRoot } from "../worktree-root.js";
14
15
  import { worktreesDirs } from "../worktree-placement.js";
15
16
  import { bashReferencesProjectRootOutsideWorktree } from "../worktree-shell-guard.js";
@@ -364,12 +365,63 @@ export function refreshWriteGateStateFromDisk(basePath: string): WriteGateSnapsh
364
365
  return currentWriteGateSnapshot(basePath);
365
366
  }
366
367
 
368
+ const WRITE_GATE_LOCK_NAME = "write-gate.lock";
369
+
370
+ /**
371
+ * Test-only seam: fires once inside the held cross-process lock (after acquire,
372
+ * before the read-merge-write body) with the resolved lock root. Lets a
373
+ * regression test observe that a concurrent process is genuinely locked out of
374
+ * the critical section. Production leaves it null. Follows the repo's
375
+ * `_setGhAvailableForTest` naming convention.
376
+ */
377
+ let interleaveHookForTest: ((lockRoot: string) => void) | null = null;
378
+ export function _setWriteGateInterleaveHookForTest(fn: ((lockRoot: string) => void) | null): void {
379
+ interleaveHookForTest = fn;
380
+ }
381
+
382
+ /**
383
+ * Serialize the read-merge-write critical section across the two processes
384
+ * that share the snapshot file (extension host + workflow MCP child). Atomic
385
+ * rename prevents torn files but not lost updates: without this lock the host
386
+ * can read the snapshot, the child can arm a gate and persist, then the host's
387
+ * later persist overwrites `pendingGateId`/`activeQueuePhase` (last-writer-wins
388
+ * fields) and silently drops the armed HARD-BLOCK gate.
389
+ *
390
+ * Fail-OPEN by contract: if a live peer holds the lock we proceed WITHOUT it —
391
+ * exactly the racy-but-functional behavior that shipped before this lock — and
392
+ * log once. The lock shrinks the race window to ~zero; it must never block,
393
+ * throw, or refuse a gate mutation. The lock is skipped entirely when snapshot
394
+ * persistence is off (no disk file, no cross-process seam).
395
+ */
396
+ function withWriteGateLock<T>(basePath: string, fn: () => T): T {
397
+ if (!shouldPersistWriteGateSnapshot()) return fn();
398
+ const lockRoot = resolveWriteGateSnapshotRoot(basePath);
399
+ // Materialize `.gsd/` (including a dangling external-state symlink target)
400
+ // before locking. acquireSyncLock's own mkdir does NOT follow a symlink to
401
+ // create its target, so without this the O_EXCL open lands on a missing
402
+ // parent and the lock silently fails open — leaving the seam unserialized
403
+ // for exactly the external-state projects that need it. This is the same
404
+ // directory the persist path ensures anyway.
405
+ try { ensureWriteGateSnapshotDirectory(basePath); } catch { /* best-effort; acquire fails-open below */ }
406
+ const { acquired } = acquireSyncLock(lockRoot, 0, WRITE_GATE_LOCK_NAME);
407
+ if (!acquired) {
408
+ logWarning("intercept", "write-gate: proceeding without cross-process lock (held by live peer)", { lockRoot });
409
+ return fn();
410
+ }
411
+ try {
412
+ interleaveHookForTest?.(lockRoot);
413
+ return fn();
414
+ } finally {
415
+ releaseSyncLock(lockRoot, WRITE_GATE_LOCK_NAME);
416
+ }
417
+ }
418
+
367
419
  /**
368
420
  * Read-modify-write primitive for gate mutations. Reconciles the disk
369
421
  * snapshot into memory (union merge), applies the mutation on top, then
370
- * persists. The whole sequence is synchronous, so the reconcile read
371
- * doubles as the pre-persist merge of concurrent writes there is no
372
- * version field; the read-merge-write is simply unconditional.
422
+ * persists all under the cross-process lock (withWriteGateLock). The whole
423
+ * sequence is synchronous, so the reconcile read doubles as the pre-persist
424
+ * merge of concurrent writes; there is no version field.
373
425
  *
374
426
  * The mutate callback sees the already-reconciled state, so policy checks
375
427
  * (e.g. host setPending's verified-on-disk-wins guard) can live inside it
@@ -385,21 +437,23 @@ function mutateWriteGateState(
385
437
  opts?: { writer?: WriteGateWriter; reconcile?: boolean },
386
438
  ): boolean {
387
439
  const state = getWriteGateState(basePath);
388
- if (shouldPersistWriteGateSnapshot() && (opts?.reconcile ?? true)) {
389
- const disk = readDiskSnapshot(basePath);
390
- if (disk) {
391
- mergeSnapshotIntoState(state, disk);
392
- } else {
393
- // Missing OR unparseable on disk: treat as a full reset. Keeping
394
- // stale in-memory state across a corrupt snapshot would persist it
395
- // back on this very mutation and defeat the documented
396
- // "delete the file to clear the HARD BLOCK gate" escape hatch.
397
- replaceStateFromSnapshot(state, EMPTY_SNAPSHOT);
440
+ return withWriteGateLock(basePath, () => {
441
+ if (shouldPersistWriteGateSnapshot() && (opts?.reconcile ?? true)) {
442
+ const disk = readDiskSnapshot(basePath);
443
+ if (disk) {
444
+ mergeSnapshotIntoState(state, disk);
445
+ } else {
446
+ // Missing OR unparseable on disk: treat as a full reset. Keeping
447
+ // stale in-memory state across a corrupt snapshot would persist it
448
+ // back on this very mutation and defeat the documented
449
+ // "delete the file to clear the HARD BLOCK gate" escape hatch.
450
+ replaceStateFromSnapshot(state, EMPTY_SNAPSHOT);
451
+ }
398
452
  }
399
- }
400
- if (mutate(state) === false) return false;
401
- persistWriteGateSnapshot(basePath, opts?.writer ?? defaultWriteGateWriter());
402
- return true;
453
+ if (mutate(state) === false) return false;
454
+ persistWriteGateSnapshot(basePath, opts?.writer ?? defaultWriteGateWriter());
455
+ return true;
456
+ });
403
457
  }
404
458
 
405
459
  export function isDepthVerified(basePath: string = process.cwd()): boolean {
@@ -643,13 +697,15 @@ export const childWriteGateAdapter: WriteGateStateAdapter = {
643
697
 
644
698
  function childMutate(basePath: string, mutate: (state: InMemoryWriteGateState) => void): void {
645
699
  const state = getWriteGateState(basePath);
646
- if (shouldPersistWriteGateSnapshot()) {
647
- // Always-fresh: disk is the only truth for the child; discard any
648
- // cross-turn in-memory residue before mutating.
649
- replaceStateFromSnapshot(state, readDiskSnapshot(basePath) ?? EMPTY_SNAPSHOT);
650
- }
651
- mutate(state);
652
- persistWriteGateSnapshot(basePath, "child");
700
+ withWriteGateLock(basePath, () => {
701
+ if (shouldPersistWriteGateSnapshot()) {
702
+ // Always-fresh: disk is the only truth for the child; discard any
703
+ // cross-turn in-memory residue before mutating.
704
+ replaceStateFromSnapshot(state, readDiskSnapshot(basePath) ?? EMPTY_SNAPSHOT);
705
+ }
706
+ mutate(state);
707
+ persistWriteGateSnapshot(basePath, "child");
708
+ });
653
709
  }
654
710
 
655
711
  /**
@@ -96,22 +96,29 @@ export function notifyDiscussNeedsInteractiveMenu(
96
96
  });
97
97
  }
98
98
 
99
- /** Hub picker for /gsd queue (reorder vs add) is unavailable. */
99
+ /**
100
+ * Hub picker for /gsd queue (reorder vs add) is unavailable.
101
+ *
102
+ * Unlike the other picker-guidance notices, /gsd queue does NOT dead-end here:
103
+ * the caller falls through to the add-work flow, which runs headlessly. So this
104
+ * notice must avoid the "did not start:" picker-failure phrasing — the headless
105
+ * host classifies that as a blocked dead-end (see stop-notice.ts / #1294) and
106
+ * would exit 10 before the add-work run starts.
107
+ */
100
108
  export function notifyQueueHubNeedsInteractiveMenu(
101
109
  ctx: ExtensionCommandContext,
102
110
  reason: string,
103
111
  ): void {
104
- notifyPickerCommandNeedsInteractiveMenu(ctx, {
105
- command: "/gsd queue",
106
- reason,
107
- alternatives: [
108
- "Add-work flow can run headless — continuing with queue discussion.",
109
- ],
110
- hints: [
111
- "/gsd queue — run from the GSD TUI to reorder pending milestones",
112
- "/gsd status — see milestone order and phase",
113
- ],
114
- });
112
+ ctx.ui.notify(
113
+ [
114
+ `/gsd queue reorder menu needs an interactive session (${reason}).`,
115
+ "Continuing headless with the add-work flow.",
116
+ "",
117
+ " • /gsd queue — run from the GSD TUI to reorder pending milestones",
118
+ " • /gsd status — see milestone order and phase",
119
+ ].join("\n"),
120
+ "warning",
121
+ );
115
122
  }
116
123
 
117
124
  /** /gsd, /gsd new-milestone, /gsd new-project wizard menus. */
@@ -7,7 +7,7 @@
7
7
 
8
8
  import { createHash } from "node:crypto";
9
9
  import { existsSync, readFileSync, renameSync, unlinkSync, writeFileSync, mkdirSync } from "node:fs";
10
- import { dirname, join } from "node:path";
10
+ import { dirname, join, isAbsolute, normalize } from "node:path";
11
11
 
12
12
  /** Current marker schema version. Bump on breaking format changes + migrate. */
13
13
  export const COMPAT_MARKER_SCHEMA = 2;
@@ -34,8 +34,10 @@ export interface PlanningMarker {
34
34
 
35
35
  /**
36
36
  * Per-file projection entry. `sha` is a normalized-content SHA-256; `entities`
37
- * is the list of DB entity ids (milestone/slice/task) that the file projects,
38
- * so repair can scope re-import rather than re-importing the whole tree.
37
+ * is the list of DB entity ids (milestone/slice/task) that the file projects.
38
+ * External-edit repair derives milestone ids from this list so markdown status
39
+ * authority is limited to the drifted projection's milestone scope while the
40
+ * importer still walks the whole tree.
39
41
  */
40
42
  export interface ProjectionEntry {
41
43
  sha: string;
@@ -220,9 +222,24 @@ function isValidProjectionEntry(x: unknown): x is ProjectionEntry {
220
222
  return true;
221
223
  }
222
224
 
225
+ /**
226
+ * Projection-map keys are repo-relative paths under .gsd/ or .planning/.
227
+ * The marker is repo-controlled content, so a key must never escape its
228
+ * root: reject absolute paths, drive letters, backslashes, and any
229
+ * normalized form that starts with "..". Mirrors the write-side guard in
230
+ * db-writer.ts.
231
+ */
232
+ function isSafeProjectionKey(key: string): boolean {
233
+ if (key.length === 0 || key.includes("\\") || key.includes("\0")) return false;
234
+ if (isAbsolute(key) || /^[A-Za-z]:/.test(key)) return false;
235
+ const norm = normalize(key);
236
+ return norm !== ".." && !norm.startsWith("../") && !isAbsolute(norm);
237
+ }
238
+
223
239
  function isValidProjectionMap(x: unknown): boolean {
224
240
  if (typeof x !== "object" || x === null) return false;
225
- for (const v of Object.values(x as Record<string, unknown>)) {
241
+ for (const [k, v] of Object.entries(x as Record<string, unknown>)) {
242
+ if (!isSafeProjectionKey(k)) return false;
226
243
  if (!isValidProjectionEntry(v)) return false;
227
244
  }
228
245
  return true;
@@ -15,6 +15,7 @@
15
15
 
16
16
  import {
17
17
  _getAdapter,
18
+ immediateTransaction,
18
19
  isDbAvailable,
19
20
  transaction,
20
21
  } from "../gsd-db.js";
@@ -75,7 +76,12 @@ export function claimNextCommand(workerId: string): CommandQueueRow | null {
75
76
  const now = new Date().toISOString();
76
77
  const db = _getAdapter()!;
77
78
 
78
- return transaction((): CommandQueueRow | null => {
79
+ // BEGIN IMMEDIATE: this is a read-then-write claim. Under WAL, a deferred
80
+ // transaction that reads first and then upgrades to a writer can fail with
81
+ // SQLITE_BUSY_SNAPSHOT (not retried by busy_timeout) when two workers race;
82
+ // taking the write lock up front serializes claimants so the loser waits
83
+ // and then sees the row already claimed (changes !== 1 → null).
84
+ return immediateTransaction((): CommandQueueRow | null => {
79
85
  // Find the oldest unclaimed command targeted at this worker OR
80
86
  // broadcast. The partial index covers both via NULL-in-B-tree.
81
87
  const row = db.prepare(
@@ -46,6 +46,7 @@ import {
46
46
  applyMigrationV27ArtifactHash,
47
47
  applyMigrationV28MemoryLastHitAt,
48
48
  applyMigrationV29RepositoryTargets,
49
+ applyMigrationV30ReworkBriefs,
49
50
  } from "../db-migration-steps.js";
50
51
  import {
51
52
  isMemoriesFtsAvailableSchema,
@@ -93,7 +94,7 @@ const providerLoader = createSqliteProviderLoader({
93
94
  nodeVersion: process.versions.node,
94
95
  writeStderr: (message: string) => process.stderr.write(message),
95
96
  });
96
- export const SCHEMA_VERSION = 29;
97
+ export const SCHEMA_VERSION = 30;
97
98
  function initSchema(db: DbAdapter, fileBacked: boolean, dbPath: string | null): void {
98
99
  const conservativeFilePragmas = fileBacked && _isLikelyWslDrvFsPathForTest(dbPath);
99
100
  if (fileBacked) db.exec(conservativeFilePragmas ? "PRAGMA journal_mode=DELETE" : "PRAGMA journal_mode=WAL");
@@ -114,21 +115,39 @@ function initSchema(db: DbAdapter, fileBacked: boolean, dbPath: string | null):
114
115
 
115
116
  const existing = db.prepare("SELECT count(*) as cnt FROM schema_version").get();
116
117
  if (existing && (existing["cnt"] as number) === 0) {
117
- createCoordinationTablesV24(db);
118
- createRuntimeKvTableV25(db);
119
-
120
- // Fresh install all tables are created above with the full current schema,
121
- // so it is safe to create all migration-specific indexes here. For existing
122
- // databases these indexes are created inside the individual migration guards
123
- // in migrateSchema() after the corresponding columns have been added.
124
- db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_escalation_pending ON tasks(milestone_id, slice_id, escalation_pending)");
125
- db.exec("CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope)");
126
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_kind ON memory_sources(kind)");
127
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_scope ON memory_sources(scope)");
128
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_from ON memory_relations(from_id)");
129
- db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_to ON memory_relations(to_id)");
130
-
131
- recordSchemaVersion(db, SCHEMA_VERSION);
118
+ // An empty schema_version table usually means a fresh install, but it can
119
+ // also be a legacy/truncated DB that already holds user data. Stamping
120
+ // that DB SCHEMA_VERSION without running migrations would mis-mark it as
121
+ // fully migrated and break at first query. Probe before stamping.
122
+ const hasData = ["milestones", "decisions", "memories"].some((t) => {
123
+ try {
124
+ const r = db.prepare(`SELECT count(*) as cnt FROM ${t}`).get();
125
+ return ((r?.["cnt"] as number) ?? 0) > 0;
126
+ } catch { /* table absent on a truly fresh DB — treat as no data */ return false; }
127
+ });
128
+ if (hasData) {
129
+ // Legacy DB with data but no version row: record the baseline so
130
+ // migrateSchema runs the full chain instead of stamping the current version.
131
+ recordSchemaVersion(db, 1);
132
+ } else {
133
+ createCoordinationTablesV24(db);
134
+ createRuntimeKvTableV25(db);
135
+
136
+ // Fresh install — all tables are created above with the full current schema,
137
+ // so it is safe to create all migration-specific indexes here. For existing
138
+ // databases these indexes are created inside the individual migration guards
139
+ // in migrateSchema() after the corresponding columns have been added.
140
+ db.exec("CREATE INDEX IF NOT EXISTS idx_tasks_escalation_pending ON tasks(milestone_id, slice_id, escalation_pending)");
141
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope)");
142
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_kind ON memory_sources(kind)");
143
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_sources_scope ON memory_sources(scope)");
144
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_from ON memory_relations(from_id)");
145
+ db.exec("CREATE INDEX IF NOT EXISTS idx_memory_relations_to ON memory_relations(to_id)");
146
+ db.exec("CREATE INDEX IF NOT EXISTS idx_rework_briefs_task ON rework_briefs(milestone_id, slice_id, task_id)");
147
+ db.exec("CREATE INDEX IF NOT EXISTS idx_rework_findings_status ON rework_brief_findings(brief_id, severity, status)");
148
+
149
+ recordSchemaVersion(db, SCHEMA_VERSION);
150
+ }
132
151
  }
133
152
 
134
153
  db.exec("COMMIT");
@@ -183,9 +202,19 @@ function copyQualityGateRowsToRepairedTable(db: DbAdapter): void {
183
202
  `);
184
203
  }
185
204
 
205
+ let _migrationFaultForTest = false;
206
+ /** Test-only: force migrateSchema to throw after applying its steps but before COMMIT. */
207
+ export function _setMigrationFaultForTest(v: boolean): void { _migrationFaultForTest = v; }
208
+
186
209
  function migrateSchema(db: DbAdapter, dbPath: string | null): void {
187
210
  const currentVersion = getCurrentSchemaVersion(db);
188
- if (currentVersion >= SCHEMA_VERSION) return;
211
+ if (currentVersion > SCHEMA_VERSION) {
212
+ throw new Error(
213
+ `gsd.db schema is v${currentVersion}, newer than the v${SCHEMA_VERSION} this gsd-pi supports. ` +
214
+ `Update gsd-pi (npm i -g @opengsd/gsd-pi) before opening this project.`,
215
+ );
216
+ }
217
+ if (currentVersion === SCHEMA_VERSION) return;
189
218
 
190
219
  backupDatabaseBeforeMigration(db, dbPath, currentVersion, {
191
220
  existsSync,
@@ -351,6 +380,13 @@ function migrateSchema(db: DbAdapter, dbPath: string | null): void {
351
380
  recordSchemaVersion(db, 29);
352
381
  }
353
382
 
383
+ if (currentVersion < 30) {
384
+ applyMigrationV30ReworkBriefs(db);
385
+ recordSchemaVersion(db, 30);
386
+ }
387
+
388
+ if (_migrationFaultForTest) throw new Error("migration fault injected for test");
389
+
354
390
  db.exec("COMMIT");
355
391
  } catch (err) {
356
392
  db.exec("ROLLBACK");
@@ -211,7 +211,11 @@ export function skipSliceCascade(milestoneId: string, sliceId: string): SkipSlic
211
211
  transaction(() => {
212
212
  const slice = getSlice(milestoneId, sliceId);
213
213
  if (!slice) { outcome = { ok: false, reason: "slice-not-found" }; return; }
214
- if (slice.status === "complete" || slice.status === "done") {
214
+ // Reject any closed slice except "skipped" (which falls through to the
215
+ // wasAlreadySkipped no-op). isClosedStatus covers the "closed"/"done"
216
+ // aliases too — hand-picking literals here nulls a completed slice's
217
+ // completed_at on re-skip.
218
+ if (isClosedStatus(slice.status) && slice.status !== "skipped") {
215
219
  outcome = { ok: false, reason: "slice-already-complete" };
216
220
  return;
217
221
  }