@opengsd/gsd-pi 1.8.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 (463) hide show
  1. package/README.md +8 -3
  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 +5 -5
  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 +5 -5
  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 +5 -1
  226. package/packages/daemon/dist/cloud-config.js.map +1 -1
  227. package/packages/daemon/dist/cloud-config.test.js +51 -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/dist/cli.d.ts +5 -0
  236. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -0
  237. package/packages/gsd-cloud/dist/cli.js +160 -0
  238. package/packages/gsd-cloud/dist/cli.js.map +1 -0
  239. package/packages/gsd-cloud/dist/cloud-config.d.ts +19 -0
  240. package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -0
  241. package/packages/gsd-cloud/dist/cloud-config.js +231 -0
  242. package/packages/gsd-cloud/dist/cloud-config.js.map +1 -0
  243. package/packages/gsd-cloud/dist/cloud-config.test.d.ts +2 -0
  244. package/packages/gsd-cloud/dist/cloud-config.test.d.ts.map +1 -0
  245. package/packages/gsd-cloud/dist/cloud-config.test.js +118 -0
  246. package/packages/gsd-cloud/dist/cloud-config.test.js.map +1 -0
  247. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts +2 -0
  248. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts.map +1 -0
  249. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js +37 -0
  250. package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js.map +1 -0
  251. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +35 -0
  252. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -0
  253. package/packages/gsd-cloud/dist/cloud-runtime.js +258 -0
  254. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -0
  255. package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts +2 -0
  256. package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts.map +1 -0
  257. package/packages/gsd-cloud/dist/cloud-runtime.test.js +88 -0
  258. package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -0
  259. package/packages/gsd-cloud/dist/cloud-token.d.ts +3 -0
  260. package/packages/gsd-cloud/dist/cloud-token.d.ts.map +1 -0
  261. package/packages/gsd-cloud/dist/cloud-token.js +37 -0
  262. package/packages/gsd-cloud/dist/cloud-token.js.map +1 -0
  263. package/packages/gsd-cloud/dist/config.d.ts +17 -0
  264. package/packages/gsd-cloud/dist/config.d.ts.map +1 -0
  265. package/packages/gsd-cloud/dist/config.js +146 -0
  266. package/packages/gsd-cloud/dist/config.js.map +1 -0
  267. package/packages/gsd-cloud/dist/device-flow.d.ts +24 -0
  268. package/packages/gsd-cloud/dist/device-flow.d.ts.map +1 -0
  269. package/packages/gsd-cloud/dist/device-flow.js +128 -0
  270. package/packages/gsd-cloud/dist/device-flow.js.map +1 -0
  271. package/packages/gsd-cloud/dist/device-flow.test.d.ts +2 -0
  272. package/packages/gsd-cloud/dist/device-flow.test.d.ts.map +1 -0
  273. package/packages/gsd-cloud/dist/device-flow.test.js +155 -0
  274. package/packages/gsd-cloud/dist/device-flow.test.js.map +1 -0
  275. package/packages/gsd-cloud/dist/executors/claude-executor.d.ts +6 -0
  276. package/packages/gsd-cloud/dist/executors/claude-executor.d.ts.map +1 -0
  277. package/packages/gsd-cloud/dist/executors/claude-executor.js +15 -0
  278. package/packages/gsd-cloud/dist/executors/claude-executor.js.map +1 -0
  279. package/packages/gsd-cloud/dist/executors/codex-executor.d.ts +6 -0
  280. package/packages/gsd-cloud/dist/executors/codex-executor.d.ts.map +1 -0
  281. package/packages/gsd-cloud/dist/executors/codex-executor.js +15 -0
  282. package/packages/gsd-cloud/dist/executors/codex-executor.js.map +1 -0
  283. package/packages/gsd-cloud/dist/executors/executor.d.ts +28 -0
  284. package/packages/gsd-cloud/dist/executors/executor.d.ts.map +1 -0
  285. package/packages/gsd-cloud/dist/executors/executor.js +11 -0
  286. package/packages/gsd-cloud/dist/executors/executor.js.map +1 -0
  287. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts +34 -0
  288. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts.map +1 -0
  289. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js +179 -0
  290. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js.map +1 -0
  291. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts +2 -0
  292. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts.map +1 -0
  293. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js +44 -0
  294. package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js.map +1 -0
  295. package/packages/gsd-cloud/dist/executors/index.d.ts +17 -0
  296. package/packages/gsd-cloud/dist/executors/index.d.ts.map +1 -0
  297. package/packages/gsd-cloud/dist/executors/index.js +29 -0
  298. package/packages/gsd-cloud/dist/executors/index.js.map +1 -0
  299. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts +36 -0
  300. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts.map +1 -0
  301. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js +176 -0
  302. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js.map +1 -0
  303. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts +2 -0
  304. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts.map +1 -0
  305. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js +34 -0
  306. package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js.map +1 -0
  307. package/packages/gsd-cloud/dist/index.d.ts +7 -0
  308. package/packages/gsd-cloud/dist/index.d.ts.map +1 -0
  309. package/packages/gsd-cloud/dist/index.js +8 -0
  310. package/packages/gsd-cloud/dist/index.js.map +1 -0
  311. package/packages/gsd-cloud/dist/inject-gateway.d.ts +16 -0
  312. package/packages/gsd-cloud/dist/inject-gateway.d.ts.map +1 -0
  313. package/packages/gsd-cloud/dist/inject-gateway.js +29 -0
  314. package/packages/gsd-cloud/dist/inject-gateway.js.map +1 -0
  315. package/packages/gsd-cloud/dist/inject-gateway.test.d.ts +2 -0
  316. package/packages/gsd-cloud/dist/inject-gateway.test.d.ts.map +1 -0
  317. package/packages/gsd-cloud/dist/inject-gateway.test.js +41 -0
  318. package/packages/gsd-cloud/dist/inject-gateway.test.js.map +1 -0
  319. package/packages/gsd-cloud/dist/logger.d.ts +25 -0
  320. package/packages/gsd-cloud/dist/logger.d.ts.map +1 -0
  321. package/packages/gsd-cloud/dist/logger.js +72 -0
  322. package/packages/gsd-cloud/dist/logger.js.map +1 -0
  323. package/packages/gsd-cloud/dist/types.d.ts +51 -0
  324. package/packages/gsd-cloud/dist/types.d.ts.map +1 -0
  325. package/packages/gsd-cloud/dist/types.js +9 -0
  326. package/packages/gsd-cloud/dist/types.js.map +1 -0
  327. package/packages/gsd-cloud/package.json +3 -3
  328. package/packages/mcp-server/README.md +8 -0
  329. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  330. package/packages/mcp-server/dist/workflow-tools.js +64 -2
  331. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  332. package/packages/mcp-server/package.json +4 -4
  333. package/packages/native/package.json +1 -1
  334. package/packages/pi-agent-core/package.json +1 -1
  335. package/packages/pi-ai/dist/models.generated.d.ts +60 -0
  336. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  337. package/packages/pi-ai/dist/models.generated.js +54 -0
  338. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  339. package/packages/pi-ai/dist/providers/amazon-bedrock.js +1 -0
  340. package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
  341. package/packages/pi-ai/dist/providers/anthropic.js +19 -0
  342. package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
  343. package/packages/pi-ai/package.json +1 -1
  344. package/packages/pi-coding-agent/package.json +7 -7
  345. package/packages/pi-tui/package.json +2 -2
  346. package/packages/rpc-client/package.json +2 -2
  347. package/pkg/package.json +1 -1
  348. package/src/resources/GSD-WORKFLOW.md +16 -47
  349. package/src/resources/extensions/browser-tools/capture.ts +2 -86
  350. package/src/resources/extensions/browser-tools/screenshot-constraints.ts +94 -0
  351. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -1
  352. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +108 -0
  353. package/src/resources/extensions/claude-code-cli/turn-assembler.ts +30 -2
  354. package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
  355. package/src/resources/extensions/gsd/auto-dispatch.ts +86 -19
  356. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -0
  357. package/src/resources/extensions/gsd/auto-prompts.ts +38 -1
  358. package/src/resources/extensions/gsd/auto.ts +5 -1
  359. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +91 -4
  360. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -24
  361. package/src/resources/extensions/gsd/command-feedback.ts +19 -12
  362. package/src/resources/extensions/gsd/compat/compat-marker.ts +21 -4
  363. package/src/resources/extensions/gsd/db/command-queue.ts +7 -1
  364. package/src/resources/extensions/gsd/db/engine.ts +53 -17
  365. package/src/resources/extensions/gsd/db/writers/cascades.ts +5 -1
  366. package/src/resources/extensions/gsd/db/writers/status.ts +25 -8
  367. package/src/resources/extensions/gsd/db-base-schema.ts +30 -0
  368. package/src/resources/extensions/gsd/db-migration-steps.ts +32 -0
  369. package/src/resources/extensions/gsd/db-schema-metadata.ts +4 -1
  370. package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -4
  371. package/src/resources/extensions/gsd/doctor-history.ts +81 -0
  372. package/src/resources/extensions/gsd/doctor-state-checks.ts +527 -0
  373. package/src/resources/extensions/gsd/doctor-workspace-checks.ts +97 -0
  374. package/src/resources/extensions/gsd/doctor.ts +14 -673
  375. package/src/resources/extensions/gsd/flat-phase-migration.ts +160 -19
  376. package/src/resources/extensions/gsd/gsd-db.ts +255 -52
  377. package/src/resources/extensions/gsd/guided-flow.ts +39 -10
  378. package/src/resources/extensions/gsd/markdown-renderer.ts +23 -7
  379. package/src/resources/extensions/gsd/md-importer.ts +116 -13
  380. package/src/resources/extensions/gsd/migrate/command.ts +21 -227
  381. package/src/resources/extensions/gsd/migrate/execution.ts +136 -0
  382. package/src/resources/extensions/gsd/migrate/plan.ts +101 -0
  383. package/src/resources/extensions/gsd/migrate/planning-writer.ts +21 -23
  384. package/src/resources/extensions/gsd/migrate/presentation.ts +83 -0
  385. package/src/resources/extensions/gsd/migration-auto-check.ts +3 -0
  386. package/src/resources/extensions/gsd/parallel-eligibility.ts +5 -4
  387. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +37 -40
  388. package/src/resources/extensions/gsd/paths.ts +4 -1
  389. package/src/resources/extensions/gsd/phase-anchor.ts +8 -2
  390. package/src/resources/extensions/gsd/post-execution-checks.ts +4 -4
  391. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  392. package/src/resources/extensions/gsd/safety/evidence-collector.ts +28 -1
  393. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +31 -5
  394. package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +30 -13
  395. package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +16 -3
  396. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +67 -20
  397. package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +47 -5
  398. package/src/resources/extensions/gsd/state-reconciliation/types.ts +2 -2
  399. package/src/resources/extensions/gsd/stop-notice.ts +18 -1
  400. package/src/resources/extensions/gsd/sync-lock.ts +14 -6
  401. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +96 -1
  402. package/src/resources/extensions/gsd/tests/command-feedback.test.ts +12 -0
  403. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +104 -0
  404. package/src/resources/extensions/gsd/tests/complete-task.test.ts +286 -0
  405. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +76 -1
  406. package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +186 -0
  407. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +29 -0
  408. package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +123 -0
  409. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +50 -0
  410. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +49 -0
  411. package/src/resources/extensions/gsd/tests/doctor-history-public-api.test.ts +25 -0
  412. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +81 -0
  413. package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +22 -0
  414. package/src/resources/extensions/gsd/tests/external-edit-scoped-import.test.ts +262 -0
  415. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +202 -0
  416. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +29 -0
  417. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +25 -0
  418. package/src/resources/extensions/gsd/tests/import-slice-milestone-completion-preserve.test.ts +116 -0
  419. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +53 -0
  420. package/src/resources/extensions/gsd/tests/migrate-plan.test.ts +68 -0
  421. package/src/resources/extensions/gsd/tests/migrate-presentation.test.ts +98 -0
  422. package/src/resources/extensions/gsd/tests/migrate-safety-audit.test.ts +1 -1
  423. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +53 -0
  424. package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +55 -0
  425. package/src/resources/extensions/gsd/tests/parallel-eligibility-file-overlap.test.ts +146 -0
  426. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +59 -0
  427. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -0
  428. package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +32 -4
  429. package/src/resources/extensions/gsd/tests/plan-task.test.ts +26 -23
  430. package/src/resources/extensions/gsd/tests/planning-writer.test.ts +47 -1
  431. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
  432. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +18 -3
  433. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +50 -0
  434. package/src/resources/extensions/gsd/tests/replan-task-handler.test.ts +103 -0
  435. package/src/resources/extensions/gsd/tests/rework-brief.test.ts +197 -0
  436. package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +21 -0
  437. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +177 -25
  438. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +17 -0
  439. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +19 -0
  440. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +34 -0
  441. package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +93 -1
  442. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -0
  443. package/src/resources/extensions/gsd/tests/workflow-migration-gates.test.ts +139 -0
  444. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +36 -0
  445. package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +52 -0
  446. package/src/resources/extensions/gsd/tools/complete-task.ts +120 -0
  447. package/src/resources/extensions/gsd/tools/plan-task.ts +14 -16
  448. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +18 -17
  449. package/src/resources/extensions/gsd/tools/replan-task.ts +160 -0
  450. package/src/resources/extensions/gsd/tools/rework-brief.ts +91 -0
  451. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +92 -0
  452. package/src/resources/extensions/gsd/types.ts +11 -0
  453. package/src/resources/extensions/gsd/unit-registry.ts +4 -0
  454. package/src/resources/extensions/gsd/uok/plan-v2.ts +20 -3
  455. package/src/resources/extensions/gsd/worktree-state-projection.ts +12 -0
  456. package/src/resources/extensions/mac-tools/index.ts +52 -9
  457. package/src/resources/extensions/mac-tools/tests/mac-screenshot-payload.test.cjs +70 -0
  458. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
  459. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
  460. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
  461. package/dist/web/standalone/.next/static/css/18c2fdf261bfc1b7.css +0 -1
  462. /package/dist/web/standalone/.next/static/{seHIUVbR1mm7LfG83nWJO → TezjdjSHBVRRr54XjAvte}/_buildManifest.js +0 -0
  463. /package/dist/web/standalone/.next/static/{seHIUVbR1mm7LfG83nWJO → TezjdjSHBVRRr54XjAvte}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, statSync, utimesSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
 
@@ -239,6 +239,98 @@ test("plan-v2 ensure rejects empty executable graph", () => {
239
239
  assert.equal(isEmptyPlanV2GraphResult(compiled), true);
240
240
  });
241
241
 
242
+ function seedMultiSliceRows(): void {
243
+ insertMilestone({ id: MILESTONE_ID, title: "Milestone", status: "active" });
244
+ const slices: Array<[string, number, Array<[string, number]>]> = [
245
+ ["S01", 1, [["T01", 1], ["T02", 2]]],
246
+ ["S02", 2, [["T03", 1], ["T04", 2]]],
247
+ ];
248
+ for (const [sid, sseq, tasks] of slices) {
249
+ insertSlice({ id: sid, milestoneId: MILESTONE_ID, title: `Slice ${sid}`, status: "in_progress", sequence: sseq });
250
+ for (const [tid, tseq] of tasks) {
251
+ insertTask({
252
+ id: tid,
253
+ milestoneId: MILESTONE_ID,
254
+ sliceId: sid,
255
+ title: `Task ${tid}`,
256
+ status: "pending",
257
+ keyFiles: [`src/${tid}.ts`],
258
+ sequence: tseq,
259
+ });
260
+ }
261
+ }
262
+ }
263
+
264
+ test("plan-v2 batched compile preserves node ids, order, and count", () => {
265
+ const basePath = createBasePath();
266
+ seedMultiSliceRows();
267
+ writeMilestoneFile(basePath, "CONTEXT", "Finalized context.");
268
+
269
+ const compiled = ensurePlanV2Graph(basePath, buildState("executing"));
270
+ assert.equal(compiled.ok, true);
271
+
272
+ const graph = JSON.parse(readFileSync(compiled.graphPath ?? "", "utf-8")) as {
273
+ nodes: Array<{ id: string }>;
274
+ };
275
+ assert.deepEqual(
276
+ graph.nodes.map((n) => n.id),
277
+ [
278
+ "execute-task:M001:S01:T01",
279
+ "execute-task:M001:S01:T02",
280
+ "complete-slice:M001:S01",
281
+ "execute-task:M001:S02:T03",
282
+ "execute-task:M001:S02:T04",
283
+ "complete-slice:M001:S02",
284
+ ],
285
+ );
286
+ });
287
+
288
+ test("plan-v2 skips the disk write when the graph is unchanged", () => {
289
+ const basePath = createBasePath();
290
+ seedMultiSliceRows();
291
+ writeMilestoneFile(basePath, "CONTEXT", "Finalized context.");
292
+
293
+ const first = ensurePlanV2Graph(basePath, buildState("executing"));
294
+ assert.equal(first.ok, true);
295
+ const graphPath = first.graphPath ?? "";
296
+ const before = readFileSync(graphPath, "utf-8");
297
+
298
+ // Backdate mtime so a rewrite is detectable even within the same millisecond.
299
+ utimesSync(graphPath, 1000, 1000);
300
+ const backdatedMtime = statSync(graphPath).mtimeMs;
301
+
302
+ const second = ensurePlanV2Graph(basePath, buildState("executing"));
303
+ assert.equal(second.ok, true);
304
+ assert.equal(statSync(graphPath).mtimeMs, backdatedMtime, "identical state must not rewrite the graph");
305
+ assert.equal(readFileSync(graphPath, "utf-8"), before);
306
+ });
307
+
308
+ test("plan-v2 rewrites the graph when it changes", () => {
309
+ const basePath = createBasePath();
310
+ seedMultiSliceRows();
311
+ writeMilestoneFile(basePath, "CONTEXT", "Finalized context.");
312
+
313
+ const first = ensurePlanV2Graph(basePath, buildState("executing"));
314
+ const graphPath = first.graphPath ?? "";
315
+ const before = readFileSync(graphPath, "utf-8");
316
+ utimesSync(graphPath, 1000, 1000);
317
+
318
+ insertTask({
319
+ id: "T05",
320
+ milestoneId: MILESTONE_ID,
321
+ sliceId: "S02",
322
+ title: "Added task",
323
+ status: "pending",
324
+ keyFiles: ["src/T05.ts"],
325
+ sequence: 3,
326
+ });
327
+
328
+ const second = ensurePlanV2Graph(basePath, buildState("executing"));
329
+ assert.equal(second.ok, true);
330
+ assert.notEqual(statSync(graphPath).mtimeMs, 1000000, "changed graph must rewrite");
331
+ assert.notEqual(readFileSync(graphPath, "utf-8"), before);
332
+ });
333
+
242
334
  test("plan-v2 allows empty graph for milestone terminal phases", () => {
243
335
  const basePath = createBasePath();
244
336
  writeMilestoneFile(basePath, "CONTEXT", "Finalized context.");
@@ -94,6 +94,8 @@ test("complete-slice requires status, closeout, and execution handoff tools", ()
94
94
  "gsd_slice_complete",
95
95
  "gsd_task_reopen",
96
96
  "gsd_replan_slice",
97
+ "gsd_replan_task",
98
+ "gsd_rework_brief_save",
97
99
  "gsd_requirement_update",
98
100
  "gsd_summary_save",
99
101
  ];
@@ -0,0 +1,139 @@
1
+ // GSD Extension - Auto-migration gate tests (plan 026).
2
+ // Covers needsAutoMigration's trigger (empty milestones table + .gsd/milestones
3
+ // present) and validateMigration's engine-vs-markdown count comparison.
4
+
5
+ import { describe, test } from 'node:test';
6
+ import assert from 'node:assert/strict';
7
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { tmpdir } from 'node:os';
10
+
11
+ import { openDatabase, closeDatabase, insertMilestone, insertSlice } from '../gsd-db.ts';
12
+ import { needsAutoMigration, validateMigration } from '../workflow-migration.ts';
13
+
14
+ function makeProject(): string {
15
+ return mkdtempSync(join(tmpdir(), 'gsd-migration-gates-'));
16
+ }
17
+
18
+ function openProjectDb(base: string): string {
19
+ const gsdDir = join(base, '.gsd');
20
+ mkdirSync(gsdDir, { recursive: true });
21
+ const dbPath = join(gsdDir, 'gsd.db');
22
+ assert.ok(openDatabase(dbPath), 'openDatabase should succeed');
23
+ return dbPath;
24
+ }
25
+
26
+ function cleanup(base: string): void {
27
+ closeDatabase();
28
+ rmSync(base, { recursive: true, force: true });
29
+ }
30
+
31
+ function writeRoadmap(base: string, mId: string, sliceLines: string[]): void {
32
+ const mDir = join(base, '.gsd', 'milestones', mId);
33
+ mkdirSync(mDir, { recursive: true });
34
+ writeFileSync(join(mDir, 'ROADMAP.md'), [
35
+ `# ${mId}: Test`,
36
+ '',
37
+ '## Slices',
38
+ '',
39
+ ...sliceLines,
40
+ '',
41
+ ].join('\n'));
42
+ }
43
+
44
+ describe('needsAutoMigration', () => {
45
+ test('false for a fresh project with no legacy markdown', () => {
46
+ const base = makeProject();
47
+ try {
48
+ openProjectDb(base);
49
+ assert.equal(needsAutoMigration(base), false, 'no .gsd/milestones dir → no migration');
50
+ } finally {
51
+ cleanup(base);
52
+ }
53
+ });
54
+
55
+ test('true when engine tables are empty and .gsd/milestones exists', () => {
56
+ const base = makeProject();
57
+ try {
58
+ openProjectDb(base);
59
+ mkdirSync(join(base, '.gsd', 'milestones', 'M001'), { recursive: true });
60
+ assert.equal(needsAutoMigration(base), true, 'legacy markdown + empty engine → migrate');
61
+ } finally {
62
+ cleanup(base);
63
+ }
64
+ });
65
+
66
+ test('false once the milestones table has rows (migration already done)', () => {
67
+ const base = makeProject();
68
+ try {
69
+ openProjectDb(base);
70
+ mkdirSync(join(base, '.gsd', 'milestones', 'M001'), { recursive: true });
71
+ insertMilestone({ id: 'M001', title: 'Migrated' });
72
+ assert.equal(needsAutoMigration(base), false, 'engine rows present → migration already done');
73
+ } finally {
74
+ cleanup(base);
75
+ }
76
+ });
77
+
78
+ test('false when no database connection is open', () => {
79
+ const base = makeProject();
80
+ try {
81
+ mkdirSync(join(base, '.gsd', 'milestones', 'M001'), { recursive: true });
82
+ closeDatabase();
83
+ assert.equal(needsAutoMigration(base), false, 'no DB → cannot migrate');
84
+ } finally {
85
+ cleanup(base);
86
+ }
87
+ });
88
+ });
89
+
90
+ describe('validateMigration', () => {
91
+ test('reports no discrepancies when engine counts match markdown', () => {
92
+ const base = makeProject();
93
+ try {
94
+ openProjectDb(base);
95
+ writeRoadmap(base, 'M001', ['- [ ] **S01: Test Slice** `risk:low` `depends:[]`']);
96
+ insertMilestone({ id: 'M001', title: 'Test' });
97
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice' });
98
+
99
+ const result = validateMigration(base);
100
+ assert.deepEqual(result.discrepancies, [], 'matching engine/markdown counts → clean');
101
+ } finally {
102
+ cleanup(base);
103
+ }
104
+ });
105
+
106
+ test('reports discrepancies when engine counts diverge from markdown', () => {
107
+ const base = makeProject();
108
+ try {
109
+ openProjectDb(base);
110
+ writeRoadmap(base, 'M001', [
111
+ '- [ ] **S01: Test Slice** `risk:low` `depends:[]`',
112
+ '- [ ] **S02: Second Slice** `risk:low` `depends:[]`',
113
+ ]);
114
+ // Engine has no rows at all → milestone and slice counts both diverge.
115
+ const result = validateMigration(base);
116
+ assert.ok(
117
+ result.discrepancies.some((d) => d.includes('Milestone count mismatch')),
118
+ `expected milestone mismatch, got: ${result.discrepancies.join('; ')}`,
119
+ );
120
+ assert.ok(
121
+ result.discrepancies.some((d) => d.includes('Slice count mismatch')),
122
+ `expected slice mismatch, got: ${result.discrepancies.join('; ')}`,
123
+ );
124
+ } finally {
125
+ cleanup(base);
126
+ }
127
+ });
128
+
129
+ test('reports a validation failure when no database connection is open', () => {
130
+ const base = makeProject();
131
+ try {
132
+ closeDatabase();
133
+ const result = validateMigration(base);
134
+ assert.deepEqual(result.discrepancies, ['No database connection for validation']);
135
+ } finally {
136
+ cleanup(base);
137
+ }
138
+ });
139
+ });
@@ -97,6 +97,42 @@ test("projectRootToWorktree forwards root PROJECT.md into isolated worktrees", (
97
97
  }
98
98
  });
99
99
 
100
+ test("projectRootToWorktree projects flat-phase discuss artifacts before dispatch", () => {
101
+ const { dir, cleanup } = makeProjectRoot();
102
+ try {
103
+ const worktree = join(dir, ".gsd", "worktrees", "M001");
104
+ mkdirSync(join(worktree, ".gsd", "phases", "01-foundation"), { recursive: true });
105
+
106
+ const projectPhaseDir = join(dir, ".gsd", "phases", "01-foundation");
107
+ mkdirSync(projectPhaseDir, { recursive: true });
108
+ writeFileSync(join(projectPhaseDir, "01-CONTEXT.md"), "# M001 Context\n");
109
+ writeFileSync(join(projectPhaseDir, "01-DISCUSSION.md"), "# M001 Discussion\n");
110
+
111
+ const metaDir = join(dir, ".gsd", "milestones", "M001");
112
+ mkdirSync(metaDir, { recursive: true });
113
+ writeFileSync(join(metaDir, "M001-META.json"), '{"branch":"milestone/M001"}');
114
+
115
+ const workspace = createWorkspace(worktree);
116
+ const scope = scopeMilestone(workspace, "M001");
117
+ new WorktreeStateProjection().projectRootToWorktree(scope);
118
+
119
+ assert.ok(
120
+ existsSync(join(worktree, ".gsd", "milestones", "M001", "M001-META.json")),
121
+ "milestone metadata is projected into the worktree",
122
+ );
123
+ assert.ok(
124
+ existsSync(join(worktree, ".gsd", "phases", "01-foundation", "01-CONTEXT.md")),
125
+ "flat-phase CONTEXT.md is projected into the worktree",
126
+ );
127
+ assert.ok(
128
+ existsSync(join(worktree, ".gsd", "phases", "01-foundation", "01-DISCUSSION.md")),
129
+ "flat-phase DISCUSSION.md is projected into the worktree",
130
+ );
131
+ } finally {
132
+ cleanup();
133
+ }
134
+ });
135
+
100
136
  test("projectRootToWorktree projects prior-milestone slice/task SUMMARY.md, not just top-level files", () => {
101
137
  // Regression: a worktree opened for the CURRENT milestone (M002) must also
102
138
  // receive the full subtree of OTHER, already-completed milestones (M001) so
@@ -26,6 +26,7 @@ import { tmpdir } from "node:os";
26
26
 
27
27
  import { registerHooks } from "../bootstrap/register-hooks.ts";
28
28
  import {
29
+ _setWriteGateInterleaveHookForTest,
29
30
  childWriteGateAdapter,
30
31
  clearDiscussionFlowState,
31
32
  getPendingGate,
@@ -36,6 +37,9 @@ import {
36
37
  setPendingGate,
37
38
  type WriteGateSnapshot,
38
39
  } from "../bootstrap/write-gate.ts";
40
+ import { acquireSyncLock, releaseSyncLock } from "../sync-lock.ts";
41
+
42
+ const WRITE_GATE_LOCK_NAME = "write-gate.lock";
39
43
 
40
44
  function makeTempDir(prefix: string): string {
41
45
  const dir = join(
@@ -356,3 +360,51 @@ test("seam: mutateWriteGateState reset path drops stale pendingGateId on a corru
356
360
  assert.equal(persisted.pendingGateId, null, "stale pending id must not be persisted after a corrupt-snapshot reconcile");
357
361
  assert.deepEqual(persisted.verifiedDepthMilestones, ["M099"]);
358
362
  });
363
+
364
+ // ── (f) cross-process lock closes the read-merge-write lost-update window ────
365
+
366
+ test("seam: read-merge-write holds the write-gate lock across the whole critical section", (t) => {
367
+ const dir = makeTempDir("lock-held");
368
+ t.after(() => {
369
+ _setWriteGateInterleaveHookForTest(null);
370
+ cleanup(dir);
371
+ });
372
+
373
+ // Fire inside the host's critical section, standing in for the OTHER process
374
+ // attempting to enter its own read-merge-write. The lock must lock it out.
375
+ let peerAcquiredMidSection: boolean | null = null;
376
+ _setWriteGateInterleaveHookForTest((lockRoot) => {
377
+ const res = acquireSyncLock(lockRoot, 0, WRITE_GATE_LOCK_NAME);
378
+ peerAcquiredMidSection = res.acquired;
379
+ if (res.acquired) releaseSyncLock(lockRoot, WRITE_GATE_LOCK_NAME);
380
+ });
381
+
382
+ markDepthVerified("M001", dir);
383
+
384
+ assert.equal(
385
+ peerAcquiredMidSection,
386
+ false,
387
+ "a concurrent process must be locked out of the read-merge-write section — this is what prevents the lost-update",
388
+ );
389
+ // The lock is released after the mutation, and the mutation still took effect.
390
+ assert.ok(loadWriteGateSnapshot(dir).verifiedDepthMilestones.includes("M001"));
391
+ assert.equal(acquireSyncLock(dir, 0, WRITE_GATE_LOCK_NAME).acquired, true, "lock released after mutation");
392
+ releaseSyncLock(dir, WRITE_GATE_LOCK_NAME);
393
+ });
394
+
395
+ test("seam: gate mutation fails OPEN when a live peer holds the lock (never blocks/refuses)", (t) => {
396
+ const dir = makeTempDir("lock-fail-open");
397
+ t.after(() => cleanup(dir));
398
+
399
+ // A live peer holds the lock (its own PID → never stolen as stale).
400
+ assert.equal(acquireSyncLock(dir, 0, WRITE_GATE_LOCK_NAME).acquired, true);
401
+ try {
402
+ // Host arms a gate while contended: fail-open means it proceeds anyway.
403
+ const armed = setPendingGate(GATE, dir);
404
+ assert.equal(armed, true, "arm must succeed (fail-open) even though the lock is held");
405
+ assert.equal(getPendingGate(dir), GATE);
406
+ assert.equal(readDiskRaw(dir).pendingGateId, GATE, "fail-open still persists the mutation");
407
+ } finally {
408
+ releaseSyncLock(dir, WRITE_GATE_LOCK_NAME);
409
+ }
410
+ });
@@ -29,6 +29,10 @@ import {
29
29
  deleteVerificationEvidence,
30
30
  saveGateResult,
31
31
  getPendingGatesForTurn,
32
+ getUnresolvedBlockingReworkFindingsForTask,
33
+ getBlockingReworkFindingsForTask,
34
+ applyReworkResolutions,
35
+ type ReworkFindingStatus,
32
36
  } from "../gsd-db.js";
33
37
  import { getWorkflowDatabasePath, ensureWorkflowDbAtPath } from "../db-workspace.js";
34
38
  import { getGatesForTurn } from "../gate-registry.js";
@@ -185,6 +189,70 @@ export function normalizeListParam(value: unknown): string[] {
185
189
  * Build a TaskRow-shaped object from CompleteTaskParams so the unified
186
190
  * renderSummaryContent() can be used at completion time (#2720).
187
191
  */
192
+
193
+ function normalizeReworkResolution(params: CompleteTaskParams): Array<{
194
+ milestoneId: string;
195
+ sliceId: string;
196
+ taskId: string;
197
+ findingId: string;
198
+ status: "resolved" | "deferred-with-override";
199
+ evidence: string;
200
+ decisionRef?: string;
201
+ }> {
202
+ return (params.reworkResolution ?? []).map((resolution) => ({
203
+ milestoneId: params.milestoneId,
204
+ sliceId: params.sliceId,
205
+ taskId: params.taskId,
206
+ findingId: resolution.findingId,
207
+ status: resolution.status,
208
+ evidence: resolution.evidence,
209
+ decisionRef: resolution.decisionRef,
210
+ }));
211
+ }
212
+
213
+ function unresolvedReworkError(missingFindingIds: string[]): string {
214
+ const plural = missingFindingIds.length === 1 ? "finding" : "findings";
215
+ return `unresolved blocking rework ${plural}: ${missingFindingIds.join(", ")} — provide reworkResolution entries with status resolved and evidence, or status deferred-with-override with evidence and decisionRef, before completing the task`;
216
+ }
217
+
218
+ function satisfiesBlockingReworkFinding(resolution: ReturnType<typeof normalizeReworkResolution>[number]): boolean {
219
+ if (resolution.evidence.trim().length === 0) return false;
220
+ if (resolution.status === "resolved") return true;
221
+ return (resolution.decisionRef ?? "").trim().length > 0;
222
+ }
223
+
224
+ /**
225
+ * Snapshot of a rework finding's status/evidence as it existed *before* this
226
+ * completion applied its reworkResolution. Captured inside the completion
227
+ * transaction so the compensating rollback paths can restore the exact prior
228
+ * state.
229
+ */
230
+ type ReworkFindingSnapshot = {
231
+ milestoneId: string;
232
+ sliceId: string;
233
+ taskId: string;
234
+ findingId: string;
235
+ status: ReworkFindingStatus;
236
+ evidence: string;
237
+ decisionRef?: string;
238
+ };
239
+
240
+ /**
241
+ * Restore rework findings to their pre-completion state.
242
+ *
243
+ * The completion transaction flips blocking findings to resolved/
244
+ * deferred-with-override via applyReworkResolutions. When a later projection
245
+ * or escalation write fails and we compensate by reverting the task to
246
+ * `pending`, we must also revert those findings. Otherwise a retry sees no
247
+ * unresolved blocking findings (getUnresolvedBlockingReworkFindingsForTask
248
+ * only returns `pending` rows) and completes the task with the blocking gate
249
+ * silently skipped.
250
+ */
251
+ function revertAppliedReworkResolutions(snapshot: ReworkFindingSnapshot[]): void {
252
+ if (snapshot.length === 0) return;
253
+ applyReworkResolutions(snapshot);
254
+ }
255
+
188
256
  function paramsToTaskRow(params: CompleteTaskParams, completedAt: string): TaskRow {
189
257
  return {
190
258
  milestone_id: params.milestoneId,
@@ -271,6 +339,7 @@ export async function handleCompleteTask(
271
339
  let guardError: string | null = null;
272
340
  let summaryMd = "";
273
341
  let repairTaskSummaryRow: TaskRow | null = null;
342
+ let appliedReworkSnapshot: ReworkFindingSnapshot[] = [];
274
343
  const rollbackDbPath = getWorkflowDatabasePath();
275
344
 
276
345
  // ── ADR-011 Phase 2: validate escalation payload BEFORE any side effects ─
@@ -282,6 +351,8 @@ export async function handleCompleteTask(
282
351
  // no escalation recorded, and the loop would silently advance past it.
283
352
  // The filesystem write happens later (after side effects) because that's
284
353
  // the cheapest ordering and validation is where 99% of failures live.
354
+ const reworkResolutions = normalizeReworkResolution(params);
355
+
285
356
  let validatedEscalationArtifact: ReturnType<typeof buildEscalationArtifact> | null = null;
286
357
  let escalationWriteEnabled = false;
287
358
  if (params.escalation) {
@@ -323,6 +394,20 @@ export async function handleCompleteTask(
323
394
  }
324
395
 
325
396
  const existingTask = getTask(params.milestoneId, params.sliceId, params.taskId);
397
+ const unresolvedRework = getUnresolvedBlockingReworkFindingsForTask(params.milestoneId, params.sliceId, params.taskId);
398
+ const resolvedFindingIds = new Set(
399
+ reworkResolutions
400
+ .filter(satisfiesBlockingReworkFinding)
401
+ .map((resolution) => resolution.findingId),
402
+ );
403
+ const missingFindingIds = unresolvedRework
404
+ .filter((finding) => !resolvedFindingIds.has(finding.finding_id))
405
+ .map((finding) => finding.finding_id);
406
+ if (missingFindingIds.length > 0) {
407
+ guardError = unresolvedReworkError(missingFindingIds);
408
+ return;
409
+ }
410
+
326
411
  if (existingTask && isClosedStatus(existingTask.status)) {
327
412
  // Stale-turn path: a timed-out turn that was superseded by recovery
328
413
  // can still reach this code when its LLM call eventually returns and
@@ -378,6 +463,38 @@ export async function handleCompleteTask(
378
463
  fullSummaryMd: summaryMd,
379
464
  });
380
465
 
466
+ // Only persist resolutions that actually satisfy the evidence
467
+ // requirement. The guard above admits a finding as long as ONE satisfying
468
+ // entry exists, but applyReworkResolutions writes by findingId and lets the
469
+ // last entry win. Persisting every entry would let a later non-satisfying
470
+ // duplicate (empty evidence, or deferred-with-override without decisionRef)
471
+ // overwrite a valid resolution and leave the finding non-pending without
472
+ // acceptable evidence. Filtering with the same predicate the guard uses
473
+ // keeps the applied set and the gate consistent.
474
+ const resolutionsToApply = reworkResolutions.filter(satisfiesBlockingReworkFinding);
475
+ if (resolutionsToApply.length > 0) {
476
+ // Snapshot the pre-resolution state of the findings we're about to
477
+ // change so a compensating rollback can restore them exactly (see
478
+ // revertAppliedReworkResolutions).
479
+ const resolvedFindingIdsForSnapshot = new Set(resolutionsToApply.map((r) => r.findingId));
480
+ appliedReworkSnapshot = getBlockingReworkFindingsForTask(
481
+ params.milestoneId,
482
+ params.sliceId,
483
+ params.taskId,
484
+ )
485
+ .filter((finding) => resolvedFindingIdsForSnapshot.has(finding.finding_id))
486
+ .map((finding) => ({
487
+ milestoneId: params.milestoneId,
488
+ sliceId: params.sliceId,
489
+ taskId: params.taskId,
490
+ findingId: finding.finding_id,
491
+ status: finding.status,
492
+ evidence: finding.evidence,
493
+ decisionRef: finding.decision_ref,
494
+ }));
495
+ applyReworkResolutions(resolutionsToApply);
496
+ }
497
+
381
498
  for (const evidence of (params.verificationEvidence ?? [])) {
382
499
  insertVerificationEvidence({
383
500
  taskId: params.taskId,
@@ -459,6 +576,7 @@ export async function handleCompleteTask(
459
576
  ensureWorkflowDbAtPath(rollbackDbPath);
460
577
  deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
461
578
  updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, "pending");
579
+ revertAppliedReworkResolutions(appliedReworkSnapshot);
462
580
  invalidateStateCache();
463
581
  rollbackSucceeded = true;
464
582
  } catch (rollbackErr) {
@@ -559,6 +677,7 @@ export async function handleCompleteTask(
559
677
  try {
560
678
  deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
561
679
  updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
680
+ revertAppliedReworkResolutions(appliedReworkSnapshot);
562
681
  invalidateStateCache();
563
682
  logWarning(
564
683
  "tool",
@@ -580,6 +699,7 @@ export async function handleCompleteTask(
580
699
  try {
581
700
  deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
582
701
  updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
702
+ revertAppliedReworkResolutions(appliedReworkSnapshot);
583
703
  invalidateStateCache();
584
704
  logWarning(
585
705
  "tool",
@@ -5,7 +5,7 @@ import { getGateIdsForTurn } from "../gate-registry.js";
5
5
  import { transaction, getSlice, getTask, insertTask, upsertTaskPlanning, insertGateRow, setSliceSketchFlag } from "../gsd-db.js";
6
6
  import { invalidateStateCache } from "../state.js";
7
7
  import { renderTaskPlanFromDb, renderPlanFromDb } from "../markdown-renderer.js";
8
- import { resolveTasksDir } from "../paths.js";
8
+ import { resolveMilestonePath, resolveSlicePath } from "../paths.js";
9
9
  import { flushWorkflowProjections } from "../projection-flush.js";
10
10
  import { writeManifest } from "../workflow-manifest.js";
11
11
  import { appendEvent } from "../workflow-events.js";
@@ -255,21 +255,19 @@ export async function handlePlanTask(
255
255
  }
256
256
 
257
257
  try {
258
- const renderResult = await renderTaskPlanFromDb(basePath, params.milestoneId, params.sliceId, params.taskId);
259
-
260
- // Flat-phase: tasks live as checkboxes in the slice plan's <tasks> block,
261
- // not as standalone TID-PLAN.md files. Re-render the slice plan so the
262
- // new/updated task appears in the plan file that gsd-core reads.
263
- // Guard: resolveTasksDir is null in flat-phase (no tasks/ subdir exists).
258
+ const milestonePath = resolveMilestonePath(basePath, params.milestoneId);
259
+ const slicePath = resolveSlicePath(basePath, params.milestoneId, params.sliceId);
260
+ const isLegacySliceLayout = Boolean(milestonePath && slicePath && slicePath !== milestonePath);
261
+ let renderedPath: string;
264
262
  let slicePlanSynced = false;
265
- try {
266
- const tDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
267
- if (!tDir) {
268
- await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
269
- slicePlanSynced = true;
270
- }
271
- } catch (syncErr) {
272
- logWarning("tool", `plan-task: slice-plan sync failed: ${(syncErr as Error).message}`);
263
+
264
+ if (isLegacySliceLayout) {
265
+ const renderResult = await renderTaskPlanFromDb(basePath, params.milestoneId, params.sliceId, params.taskId);
266
+ renderedPath = renderResult.taskPlanPath;
267
+ } else {
268
+ const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
269
+ renderedPath = renderResult.planPath;
270
+ slicePlanSynced = true;
273
271
  }
274
272
 
275
273
  if (slicePlanSynced) {
@@ -299,7 +297,7 @@ export async function handlePlanTask(
299
297
  milestoneId: params.milestoneId,
300
298
  sliceId: params.sliceId,
301
299
  taskId: params.taskId,
302
- taskPlanPath: renderResult.taskPlanPath,
300
+ taskPlanPath: renderedPath,
303
301
  };
304
302
  } catch (err) {
305
303
  return { error: `render failed: ${(err as Error).message}` };
@@ -1,6 +1,6 @@
1
1
  import { existsSync, unlinkSync } from "node:fs";
2
- import { join } from "node:path";
3
- import { relSliceFile, resolveMilestoneFile, resolveMilestonePath, targetMilestoneFile } from "../paths.js";
2
+ import { join, relative } from "node:path";
3
+ import { gsdProjectionRoot, gsdRoot, resolveMilestoneFile, resolveMilestonePath, targetMilestoneFile } from "../paths.js";
4
4
  import { clearParseCache } from "../files.js";
5
5
  import { isClosedStatus } from "../status-guards.js";
6
6
  import { isNonEmptyString } from "../validation.js";
@@ -53,6 +53,14 @@ export interface ReassessRoadmapResult {
53
53
  roadmapPath: string;
54
54
  }
55
55
 
56
+ function assessmentDbPathForRenderedFile(basePath: string, absPath: string): string {
57
+ const projectionRoot = gsdProjectionRoot(basePath);
58
+ const projectionRel = relative(projectionRoot, absPath);
59
+ const root = projectionRel && !projectionRel.startsWith("..") && !projectionRel.startsWith("/")
60
+ ? projectionRoot
61
+ : gsdRoot(basePath);
62
+ return `.gsd/${relative(root, absPath).replace(/\\/g, "/")}`;
63
+ }
56
64
 
57
65
  function validateParams(params: ReassessRoadmapParams): ReassessRoadmapParams {
58
66
  if (!isNonEmptyString(params?.milestoneId)) throw new Error("milestoneId is required");
@@ -119,11 +127,6 @@ export async function handleReassessRoadmap(
119
127
  return { error: `validation failed: ${(err as Error).message}` };
120
128
  }
121
129
 
122
- // ── Compute assessment artifact path ──────────────────────────────
123
- // Assessment lives in the completed slice's directory.
124
- // Use relSliceFile so the path is layout-aware (flat-phase or legacy).
125
- const assessmentRelPath = relSliceFile(basePath, params.milestoneId, params.completedSliceId, "ASSESSMENT");
126
-
127
130
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
128
131
  // Guards must be inside the transaction so the state they check cannot
129
132
  // change between the read and the write (#2723).
@@ -207,16 +210,6 @@ export async function handleReassessRoadmap(
207
210
  }
208
211
  }
209
212
 
210
- // Record assessment
211
- insertAssessment({
212
- path: assessmentRelPath,
213
- milestoneId: params.milestoneId,
214
- sliceId: params.completedSliceId,
215
- status: params.verdict,
216
- scope: "roadmap",
217
- fullContent: params.assessment,
218
- });
219
-
220
213
  // Apply slice modifications
221
214
  for (const mod of params.sliceChanges.modified) {
222
215
  updateSliceFields(params.milestoneId, mod.sliceId, {
@@ -282,6 +275,14 @@ export async function handleReassessRoadmap(
282
275
  assessment: params.assessment,
283
276
  completedSliceId: params.completedSliceId,
284
277
  });
278
+ insertAssessment({
279
+ path: assessmentDbPathForRenderedFile(basePath, assessmentResult.assessmentPath),
280
+ milestoneId: params.milestoneId,
281
+ sliceId: params.completedSliceId,
282
+ status: params.verdict,
283
+ scope: "roadmap",
284
+ fullContent: params.assessment,
285
+ });
285
286
 
286
287
  // ── Remove stale VALIDATION file from disk (#2957) ────────────
287
288
  const hasStructuralChanges =