@opengsd/gsd-pi 1.3.0-dev.eed73bea → 1.4.0

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 (505) hide show
  1. package/README.md +10 -10
  2. package/dist/cli.js +17 -4
  3. package/dist/headless-answers.js +1 -1
  4. package/dist/headless-ui.d.ts +1 -1
  5. package/dist/headless-ui.js +1 -1
  6. package/dist/headless.js +1 -1
  7. package/dist/resources/.managed-resources-content-hash +1 -1
  8. package/dist/resources/extensions/claude-code-cli/models.js +9 -0
  9. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  10. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  11. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
  12. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  13. package/dist/resources/extensions/gsd/auto-budget.js +5 -1
  14. package/dist/resources/extensions/gsd/auto-dispatch.js +17 -6
  15. package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
  16. package/dist/resources/extensions/gsd/auto-prompts.js +8 -24
  17. package/dist/resources/extensions/gsd/auto-recovery.js +9 -517
  18. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  19. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  20. package/dist/resources/extensions/gsd/auto-verification.js +5 -2
  21. package/dist/resources/extensions/gsd/auto.js +19 -10
  22. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +39 -0
  23. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +5 -4
  24. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -18
  26. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +48 -36
  27. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +15 -4
  28. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
  29. package/dist/resources/extensions/gsd/commands/handlers/auto.js +1 -1
  30. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +1 -1
  31. package/dist/resources/extensions/gsd/commands-usage.js +1 -1
  32. package/dist/resources/extensions/gsd/db/engine.js +4 -1
  33. package/dist/resources/extensions/gsd/db-memory-fts-schema.js +34 -0
  34. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  35. package/dist/resources/extensions/gsd/delegation-policy.js +1 -1
  36. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  37. package/dist/resources/extensions/gsd/doctor-engine-checks.js +192 -4
  38. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
  39. package/dist/resources/extensions/gsd/exec-sandbox.js +8 -2
  40. package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
  41. package/dist/resources/extensions/gsd/git-service.js +57 -0
  42. package/dist/resources/extensions/gsd/gitignore.js +4 -1
  43. package/dist/resources/extensions/gsd/guided-flow-queue.js +20 -13
  44. package/dist/resources/extensions/gsd/json-persistence.js +20 -0
  45. package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
  46. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  47. package/dist/resources/extensions/gsd/markdown-renderer.js +2 -7
  48. package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
  49. package/dist/resources/extensions/gsd/metrics.js +4 -4
  50. package/dist/resources/extensions/gsd/migrate-external.js +11 -2
  51. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  52. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  53. package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
  54. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  55. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  56. package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  57. package/dist/resources/extensions/gsd/root-write-leak-guard.js +7 -1
  58. package/dist/resources/extensions/gsd/safety/destructive-guard.js +8 -1
  59. package/dist/resources/extensions/gsd/safety/evidence-collector.js +16 -1
  60. package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
  61. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  62. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  63. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  64. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  65. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  66. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  67. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +27 -33
  68. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  69. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  70. package/dist/resources/extensions/gsd/state.js +12 -611
  71. package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
  72. package/dist/resources/extensions/gsd/tools/complete-slice.js +3 -34
  73. package/dist/resources/extensions/gsd/tools/complete-task.js +8 -41
  74. package/dist/resources/extensions/gsd/tools/plan-slice.js +59 -44
  75. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -5
  76. package/dist/resources/extensions/gsd/undo.js +43 -6
  77. package/dist/resources/extensions/gsd/unit-registry.js +11 -3
  78. package/dist/resources/extensions/gsd/verification-gate.js +7 -0
  79. package/dist/resources/extensions/gsd/workflow-manifest.js +126 -13
  80. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  81. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  82. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  83. package/dist/web/standalone/.next/BUILD_ID +1 -1
  84. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  85. package/dist/web/standalone/.next/build-manifest.json +4 -4
  86. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  87. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  88. package/dist/web/standalone/.next/required-server-files.json +3 -3
  89. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  90. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  100. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  106. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  107. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  108. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  109. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  116. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  128. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  150. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  160. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  166. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  182. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  185. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  186. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  195. package/dist/web/standalone/.next/server/app/index.html +1 -1
  196. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  197. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  198. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  199. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  200. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  201. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  202. package/dist/web/standalone/.next/server/app/page.js +2 -2
  203. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  205. package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
  206. package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
  207. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/middleware.js +3 -3
  210. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  211. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  212. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  213. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  214. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  215. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  216. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  217. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  218. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  219. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  220. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/app/{layout-4ae2d68984392bbf.js → layout-b35cbfff38aaf4cf.js} +1 -1
  222. package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/{main-app-90d1d8d5e5d2dc6b.js → main-app-590a74400e35f685.js} +1 -1
  224. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  226. package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
  227. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  228. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  229. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  230. package/dist/web/standalone/server.js +1 -1
  231. package/package.json +6 -6
  232. package/packages/cloud-mcp-gateway/package.json +2 -2
  233. package/packages/contracts/package.json +1 -1
  234. package/packages/daemon/package.json +4 -4
  235. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  236. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  237. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  238. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  239. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  240. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  241. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  242. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  243. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  244. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  245. package/packages/gsd-agent-core/dist/index.js +2 -0
  246. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  247. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  248. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  249. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  250. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  251. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  252. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  253. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  254. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  255. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  256. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  257. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  258. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  259. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  260. package/packages/gsd-agent-core/package.json +5 -5
  261. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  262. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  263. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  264. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  265. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  266. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  267. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  268. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  269. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  270. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  271. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -841
  272. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  273. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  274. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  275. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  276. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  277. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  278. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  279. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  280. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  281. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  282. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  283. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  284. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  285. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  286. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  287. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  288. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  289. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  290. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  291. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  292. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  293. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  294. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  295. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  296. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  297. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  298. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  299. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  300. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  301. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  302. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  303. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  304. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  305. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  306. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  307. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  308. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  309. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts +2 -0
  310. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  311. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +23 -5
  312. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  313. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  314. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  315. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  316. package/packages/gsd-agent-modes/package.json +7 -7
  317. package/packages/mcp-server/dist/workflow-tools.js +1 -1
  318. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  319. package/packages/mcp-server/package.json +4 -4
  320. package/packages/native/package.json +1 -1
  321. package/packages/pi-agent-core/dist/agent-loop.js +3 -2
  322. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  323. package/packages/pi-agent-core/package.json +1 -1
  324. package/packages/pi-ai/dist/models.generated.d.ts +46 -0
  325. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  326. package/packages/pi-ai/dist/models.generated.js +38 -0
  327. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  328. package/packages/pi-ai/package.json +1 -1
  329. package/packages/pi-coding-agent/README.md +3 -2
  330. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  331. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  332. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  333. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  334. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  335. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  336. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  337. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  338. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  339. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  341. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  342. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  343. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  344. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  345. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  346. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  347. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  348. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  349. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  350. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  351. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  352. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  353. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  354. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  355. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  356. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  357. package/packages/pi-coding-agent/package.json +7 -7
  358. package/packages/pi-tui/package.json +2 -2
  359. package/packages/rpc-client/package.json +2 -2
  360. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  361. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  362. package/pkg/dist/theme/theme-schema.js +1 -1
  363. package/pkg/dist/theme/theme-schema.js.map +1 -1
  364. package/pkg/dist/theme/theme.d.ts.map +1 -1
  365. package/pkg/dist/theme/theme.js +11 -7
  366. package/pkg/dist/theme/theme.js.map +1 -1
  367. package/pkg/package.json +1 -1
  368. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  369. package/src/resources/extensions/claude-code-cli/tests/index.test.ts +14 -0
  370. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  371. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  372. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  373. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
  374. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  375. package/src/resources/extensions/gsd/auto-budget.ts +12 -1
  376. package/src/resources/extensions/gsd/auto-dispatch.ts +17 -7
  377. package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
  378. package/src/resources/extensions/gsd/auto-prompts.ts +7 -23
  379. package/src/resources/extensions/gsd/auto-recovery.ts +13 -518
  380. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  381. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  382. package/src/resources/extensions/gsd/auto-verification.ts +5 -1
  383. package/src/resources/extensions/gsd/auto.ts +23 -8
  384. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +44 -0
  385. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +5 -4
  386. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  387. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +43 -18
  388. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +49 -1
  389. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +50 -37
  390. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +16 -4
  391. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
  392. package/src/resources/extensions/gsd/commands/handlers/auto.ts +1 -1
  393. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +1 -1
  394. package/src/resources/extensions/gsd/commands-usage.ts +1 -1
  395. package/src/resources/extensions/gsd/db/engine.ts +8 -1
  396. package/src/resources/extensions/gsd/db-memory-fts-schema.ts +45 -0
  397. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  398. package/src/resources/extensions/gsd/delegation-policy.ts +1 -1
  399. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  400. package/src/resources/extensions/gsd/doctor-engine-checks.ts +233 -5
  401. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
  402. package/src/resources/extensions/gsd/doctor-types.ts +3 -0
  403. package/src/resources/extensions/gsd/exec-sandbox.ts +7 -2
  404. package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
  405. package/src/resources/extensions/gsd/git-service.ts +64 -0
  406. package/src/resources/extensions/gsd/gitignore.ts +4 -1
  407. package/src/resources/extensions/gsd/guided-flow-queue.ts +20 -13
  408. package/src/resources/extensions/gsd/json-persistence.ts +20 -0
  409. package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
  410. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  411. package/src/resources/extensions/gsd/markdown-renderer.ts +2 -10
  412. package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
  413. package/src/resources/extensions/gsd/metrics.ts +4 -4
  414. package/src/resources/extensions/gsd/migrate-external.ts +8 -2
  415. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  416. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  417. package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
  418. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  419. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  420. package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  421. package/src/resources/extensions/gsd/root-write-leak-guard.ts +13 -1
  422. package/src/resources/extensions/gsd/safety/destructive-guard.ts +9 -1
  423. package/src/resources/extensions/gsd/safety/evidence-collector.ts +16 -1
  424. package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
  425. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  426. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  427. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  428. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  429. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  430. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  431. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +36 -30
  432. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  433. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  434. package/src/resources/extensions/gsd/state.ts +32 -732
  435. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
  436. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  437. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +14 -5
  438. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +36 -0
  439. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +182 -4
  440. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +35 -0
  441. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +30 -0
  442. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  443. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
  444. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
  445. package/src/resources/extensions/gsd/tests/db-memory-fts-schema.test.ts +58 -3
  446. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
  447. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +44 -0
  448. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +143 -0
  449. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +239 -2
  450. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +16 -0
  451. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
  452. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
  453. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
  454. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +11 -2
  455. package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
  456. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
  457. package/src/resources/extensions/gsd/tests/memory-store.test.ts +48 -0
  458. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
  459. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
  460. package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
  461. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +57 -0
  462. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  463. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
  464. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -1
  465. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
  466. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +103 -4
  467. package/src/resources/extensions/gsd/tests/plan-task.test.ts +44 -1
  468. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +4 -2
  469. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  470. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +58 -0
  471. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -6
  472. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +103 -0
  473. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +20 -1
  474. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +15 -3
  475. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  476. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +115 -0
  477. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +140 -0
  478. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +6 -3
  479. package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
  480. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +38 -0
  481. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
  482. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +96 -15
  483. package/src/resources/extensions/gsd/tests/workflow-mcp-preflight.test.ts +1 -0
  484. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +8 -4
  485. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +6 -5
  486. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  487. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  488. package/src/resources/extensions/gsd/tools/complete-slice.ts +3 -30
  489. package/src/resources/extensions/gsd/tools/complete-task.ts +7 -39
  490. package/src/resources/extensions/gsd/tools/plan-slice.ts +60 -47
  491. package/src/resources/extensions/gsd/tools/plan-task.ts +30 -5
  492. package/src/resources/extensions/gsd/undo.ts +47 -6
  493. package/src/resources/extensions/gsd/unit-registry.ts +11 -3
  494. package/src/resources/extensions/gsd/verification-gate.ts +8 -0
  495. package/src/resources/extensions/gsd/workflow-manifest.ts +141 -7
  496. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  497. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  498. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  499. package/dist/web/standalone/.next/static/chunks/796.3976108148518f7d.js +0 -10
  500. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-a6fb1847f67f167c.js +0 -1
  501. package/dist/web/standalone/.next/static/chunks/app/page-6644fc6ee8ca1247.js +0 -1
  502. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +0 -1
  503. package/dist/web/standalone/.next/static/chunks/webpack-7c1d97e39be2da11.js +0 -1
  504. /package/dist/web/standalone/.next/static/{SzEuqWX37DR9MEpEuQjP1 → zQLcXm0Za8lexoXtgDpMG}/_buildManifest.js +0 -0
  505. /package/dist/web/standalone/.next/static/{SzEuqWX37DR9MEpEuQjP1 → zQLcXm0Za8lexoXtgDpMG}/_ssgManifest.js +0 -0
@@ -116,6 +116,46 @@ test("effectiveFileChangeAllowlist keeps .gitignore auditable when management is
116
116
  assert.deepEqual(effectiveFileChangeAllowlist(["docs/**"], false), ["docs/**"]);
117
117
  });
118
118
 
119
+ test("validateFileChanges excludes .gsd-backups/ migration snapshots from unexpected-change warnings", (t) => {
120
+ const base = mkdtempSync(join(tmpdir(), "gsd-file-change-validator-"));
121
+ t.after(() => rmSync(base, { recursive: true, force: true }));
122
+
123
+ const backupFile = join(
124
+ base,
125
+ ".gsd-backups",
126
+ "migrate-1782703701330",
127
+ "M010",
128
+ "slices",
129
+ "S01",
130
+ "S01-ASSESSMENT.md",
131
+ );
132
+ mkdirSync(join(base, "src"), { recursive: true });
133
+ mkdirSync(join(backupFile, ".."), { recursive: true });
134
+
135
+ git(base, "init");
136
+ git(base, "config", "user.email", "test@example.com");
137
+ git(base, "config", "user.name", "Test User");
138
+
139
+ writeFileSync(join(base, "src", "app.ts"), "initial\n");
140
+ writeFileSync(backupFile, "legacy assessment\n");
141
+ git(base, "add", ".");
142
+ git(base, "commit", "-m", "initial");
143
+
144
+ writeFileSync(join(base, "src", "app.ts"), "updated\n");
145
+ writeFileSync(backupFile, "legacy assessment touched\n");
146
+ git(base, "add", ".");
147
+ git(base, "commit", "-m", "task commit");
148
+
149
+ const audit = validateFileChanges(base, ["src/app.ts"], []);
150
+ assert.ok(audit, "audit should be produced");
151
+ assert.deepEqual(audit!.unexpectedFiles, [], ".gsd-backups/ must not trigger warnings");
152
+ assert.equal(
153
+ audit!.violations.filter(v => v.severity === "warning").length,
154
+ 0,
155
+ "no warnings when only source and migration backup files changed",
156
+ );
157
+ });
158
+
119
159
  test("GSD-managed .gitignore edit swept into a task commit is not flagged", (t) => {
120
160
  const base = mkdtempSync(join(tmpdir(), "gsd-file-change-validator-"));
121
161
  t.after(() => rmSync(base, { recursive: true, force: true }));
@@ -2,12 +2,16 @@
2
2
  // File Purpose: Tests the one-time migration from nested to flat-phase layout.
3
3
  import test, { afterEach } from "node:test";
4
4
  import assert from "node:assert/strict";
5
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readdirSync, readFileSync } from "node:fs";
5
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readdirSync, readFileSync, utimesSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
  import { randomUUID } from "node:crypto";
9
9
 
10
- import { migrateToFlatPhase, needsFlatPhaseMigration } from "../flat-phase-migration.ts";
10
+ import {
11
+ migrateToFlatPhase,
12
+ needsFlatPhaseMigration,
13
+ pruneStaleFlatPhaseBackups,
14
+ } from "../flat-phase-migration.ts";
11
15
  import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask, getAllMilestones, getMilestoneSlices, getSliceTasks } from "../gsd-db.ts";
12
16
 
13
17
  const tmpDirs: string[] = [];
@@ -112,3 +116,34 @@ test("migrateToFlatPhase preserves slice sidecar artifacts and skips recovery pl
112
116
  "recovery placeholder PLAN should not be promoted when no DB tasks can render a real plan",
113
117
  );
114
118
  });
119
+
120
+ test("pruneStaleFlatPhaseBackups removes migrate-* dirs older than retention window", async () => {
121
+ const base = makeTmp();
122
+ await migrateToFlatPhase(base);
123
+
124
+ const backupRoot = join(base, ".gsd-backups");
125
+ assert.ok(existsSync(backupRoot), "backup should exist immediately after migration");
126
+
127
+ const staleDir = join(backupRoot, "migrate-stale");
128
+ mkdirSync(staleDir, { recursive: true });
129
+ writeFileSync(join(staleDir, "marker.txt"), "old backup\n", "utf-8");
130
+ const staleDate = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000);
131
+ utimesSync(staleDir, staleDate, staleDate);
132
+
133
+ const removed = pruneStaleFlatPhaseBackups(base);
134
+ assert.equal(removed, 1, "stale migrate-* dir should be pruned");
135
+ assert.equal(existsSync(staleDir), false, "stale backup dir should be gone");
136
+ assert.ok(existsSync(backupRoot), "fresh migration backup should remain");
137
+ });
138
+
139
+ test("pruneStaleFlatPhaseBackups is a no-op while flat-phase migration is still needed", () => {
140
+ const base = makeTmp();
141
+ const backupRoot = join(base, ".gsd-backups", "migrate-stale");
142
+ mkdirSync(backupRoot, { recursive: true });
143
+ writeFileSync(join(backupRoot, "marker.txt"), "old backup\n", "utf-8");
144
+ const staleDate = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000);
145
+ utimesSync(backupRoot, staleDate, staleDate);
146
+
147
+ assert.equal(pruneStaleFlatPhaseBackups(base), 0, "must not prune while migration is pending");
148
+ assert.ok(existsSync(backupRoot), "backup must remain until migration completes");
149
+ });
@@ -40,6 +40,10 @@ describe('ensureGitignore writes .bg-shell/ baseline (#4902)', () => {
40
40
  lines.has('.bg-shell/'),
41
41
  `.gitignore should include .bg-shell/. Got:\n${ignore}`,
42
42
  );
43
+ assert.ok(
44
+ lines.has('.gsd-backups/'),
45
+ `.gitignore should include .gsd-backups/. Got:\n${ignore}`,
46
+ );
43
47
  } finally {
44
48
  cleanup(dir);
45
49
  }
@@ -294,12 +294,13 @@ describe('git-service', async () => {
294
294
 
295
295
  assert.deepStrictEqual(
296
296
  RUNTIME_EXCLUSION_PATHS.length,
297
- 17,
298
- "exactly 17 runtime exclusion paths"
297
+ 18,
298
+ "exactly 18 runtime exclusion paths"
299
299
  );
300
300
 
301
301
  const expectedPaths = [
302
302
  ".gsd-worktrees/",
303
+ ".gsd-backups/**",
303
304
  ".gsd/activity/",
304
305
  ".gsd/audit/",
305
306
  ".gsd/forensics/",
@@ -1623,11 +1624,13 @@ process.exit(result.status ?? 0);
1623
1624
  // Create and track runtime files (simulates pre-.gitignore state)
1624
1625
  mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
1625
1626
  mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true });
1627
+ mkdirSync(join(repo, ".gsd-backups", "migrate-old"), { recursive: true });
1626
1628
  writeFileSync(join(repo, ".gsd", "completed-units.json"), '["u1"]');
1627
1629
  writeFileSync(join(repo, ".gsd", "metrics.json"), '{}');
1628
1630
  writeFileSync(join(repo, ".gsd", "STATE.md"), "# State");
1629
1631
  writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
1630
1632
  writeFileSync(join(repo, ".gsd", "runtime", "data.json"), "{}");
1633
+ writeFileSync(join(repo, ".gsd-backups", "migrate-old", "marker.txt"), "backup");
1631
1634
  writeFileSync(join(repo, "src.ts"), "code");
1632
1635
  runGit(repo, ["add", "-A"]);
1633
1636
  runGit(repo, ["commit", "-m", "init"]);
@@ -1636,6 +1639,8 @@ process.exit(result.status ?? 0);
1636
1639
  const trackedBefore = run("git ls-files .gsd/", repo);
1637
1640
  assert.ok(trackedBefore.includes("completed-units.json"), "untrack: precondition — completed-units tracked");
1638
1641
  assert.ok(trackedBefore.includes("metrics.json"), "untrack: precondition — metrics tracked");
1642
+ const backupTrackedBefore = run("git ls-files .gsd-backups/", repo);
1643
+ assert.ok(backupTrackedBefore.includes("marker.txt"), "untrack: precondition — migration backup tracked");
1639
1644
 
1640
1645
  // Run untrackRuntimeFiles
1641
1646
  untrackRuntimeFiles(repo);
@@ -1643,6 +1648,8 @@ process.exit(result.status ?? 0);
1643
1648
  // Runtime files should be removed from the index
1644
1649
  const trackedAfter = run("git ls-files .gsd/", repo);
1645
1650
  assert.deepStrictEqual(trackedAfter, "", "untrack: all runtime files removed from index");
1651
+ const backupTrackedAfter = run("git ls-files .gsd-backups/", repo);
1652
+ assert.deepStrictEqual(backupTrackedAfter, "", "untrack: migration backups removed from index");
1646
1653
 
1647
1654
  // Non-runtime files remain tracked
1648
1655
  const srcTracked = run("git ls-files src.ts", repo);
@@ -1653,6 +1660,8 @@ process.exit(result.status ?? 0);
1653
1660
  "untrack: completed-units.json still on disk");
1654
1661
  assert.ok(existsSync(join(repo, ".gsd", "metrics.json")),
1655
1662
  "untrack: metrics.json still on disk");
1663
+ assert.ok(existsSync(join(repo, ".gsd-backups", "migrate-old", "marker.txt")),
1664
+ "untrack: migration backup still on disk");
1656
1665
 
1657
1666
  // Idempotent — running again doesn't error
1658
1667
  untrackRuntimeFiles(repo);
@@ -19,6 +19,7 @@ import { tmpdir } from "node:os";
19
19
 
20
20
  import {
21
21
  saveJsonFile,
22
+ saveJsonFileCompact,
22
23
  loadJsonFile,
23
24
  writeJsonFileAtomic,
24
25
  } from "../json-persistence.ts";
@@ -56,6 +57,37 @@ test("saveJsonFile creates file with valid JSON content", () => {
56
57
  }
57
58
  });
58
59
 
60
+ test("saveJsonFile keeps pretty-printed output for human-readable state", () => {
61
+ const dir = makeTempDir();
62
+ const filePath = join(dir, "pretty.json");
63
+
64
+ try {
65
+ const data = { foo: "bar", nested: { count: 42 } };
66
+ saveJsonFile(filePath, data);
67
+
68
+ const content = readFileSync(filePath, "utf-8");
69
+ assert.equal(content, JSON.stringify(data, null, 2) + "\n");
70
+ } finally {
71
+ cleanup(dir);
72
+ }
73
+ });
74
+
75
+ test("saveJsonFileCompact writes valid compact JSON content", () => {
76
+ const dir = makeTempDir();
77
+ const filePath = join(dir, "compact.json");
78
+
79
+ try {
80
+ const data = { foo: "bar", nested: { count: 42 } };
81
+ saveJsonFileCompact(filePath, data);
82
+
83
+ const content = readFileSync(filePath, "utf-8");
84
+ assert.equal(content, JSON.stringify(data) + "\n");
85
+ assert.deepEqual(JSON.parse(content), data);
86
+ } finally {
87
+ cleanup(dir);
88
+ }
89
+ });
90
+
59
91
  test("saveJsonFile does not leave .tmp files on success", () => {
60
92
  const dir = makeTempDir();
61
93
  const filePath = join(dir, "clean.json");
@@ -72,6 +104,21 @@ test("saveJsonFile does not leave .tmp files on success", () => {
72
104
  }
73
105
  });
74
106
 
107
+ test("saveJsonFileCompact does not leave .tmp files on success", () => {
108
+ const dir = makeTempDir();
109
+ const filePath = join(dir, "compact-clean.json");
110
+
111
+ try {
112
+ saveJsonFileCompact(filePath, { test: true });
113
+
114
+ const files = readdirSync(dir);
115
+ const tmpFiles = files.filter(f => f.includes(".tmp"));
116
+ assert.equal(tmpFiles.length, 0, `Unexpected .tmp files: ${tmpFiles.join(", ")}`);
117
+ } finally {
118
+ cleanup(dir);
119
+ }
120
+ });
121
+
75
122
  test("saveJsonFile creates parent directories", () => {
76
123
  const dir = makeTempDir();
77
124
  const filePath = join(dir, "deep", "nested", "data.json");
@@ -161,6 +161,41 @@ test("backfill is idempotent — second run on the same file is a no-op", () =>
161
161
  }
162
162
  });
163
163
 
164
+ test("backfill skips a knowledge row whose memory insert throws and continues later rows", () => {
165
+ const base = makeTmpBase();
166
+ try {
167
+ writeKnowledgeMd(base, FIXTURE);
168
+
169
+ const adapter = _getAdapter();
170
+ assert.ok(adapter);
171
+ adapter.exec(`
172
+ CREATE TRIGGER fail_p002_memory_insert
173
+ BEFORE INSERT ON memories
174
+ WHEN json_extract(NEW.structured_fields, '$.sourceKnowledgeId') = 'P002'
175
+ BEGIN
176
+ SELECT RAISE(ABORT, 'poison knowledge P002');
177
+ END
178
+ `);
179
+
180
+ const written = backfillKnowledgeToMemories(base);
181
+ assert.equal(written, 2, "one poisoned row should not abort the batch");
182
+
183
+ const rows = adapter
184
+ .prepare(
185
+ "SELECT structured_fields FROM memories WHERE structured_fields LIKE '%\"sourceKnowledgeId\":\"%' ORDER BY seq",
186
+ )
187
+ .all() as Array<{ structured_fields: string }>;
188
+
189
+ assert.deepEqual(
190
+ rows.map((row) => JSON.parse(row.structured_fields).sourceKnowledgeId),
191
+ ["P001", "L001"],
192
+ "rows after the poisoned knowledge entry should still be migrated",
193
+ );
194
+ } finally {
195
+ cleanup(base);
196
+ }
197
+ });
198
+
164
199
  test("backfill returns 0 when KNOWLEDGE.md is absent", () => {
165
200
  const base = makeTmpBase();
166
201
  try {
@@ -5,6 +5,9 @@ import {
5
5
  SCHEMA_VERSION,
6
6
  _getAdapter,
7
7
  } from '../gsd-db.ts';
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+ import * as os from 'node:os';
8
11
  import {
9
12
  _resetLogs,
10
13
  peekLogs,
@@ -22,6 +25,7 @@ import {
22
25
  markUnitProcessed,
23
26
  decayStaleMemories,
24
27
  enforceMemoryCap,
28
+ queryMemoriesRanked,
25
29
  applyMemoryActions,
26
30
  formatMemoriesForPrompt,
27
31
  } from '../memory-store.ts';
@@ -29,6 +33,16 @@ import type { MemoryAction } from '../memory-store.ts';
29
33
  import { describe, test, beforeEach, afterEach } from 'node:test';
30
34
  import assert from 'node:assert/strict';
31
35
 
36
+ function tempDbPath(): string {
37
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-memory-store-test-'));
38
+ return path.join(dir, 'test.db');
39
+ }
40
+
41
+ function cleanupDbPath(dbPath: string): void {
42
+ closeDatabase();
43
+ fs.rmSync(path.dirname(dbPath), { recursive: true, force: true });
44
+ }
45
+
32
46
  // ═══════════════════════════════════════════════════════════════════════════
33
47
  // memory-store: fallback when DB not open
34
48
  // ═══════════════════════════════════════════════════════════════════════════
@@ -366,6 +380,40 @@ test('memory-store: schema includes memories table', () => {
366
380
  closeDatabase();
367
381
  });
368
382
 
383
+ test('memory-store: reopening rebuilds FTS index for pre-FTS memories', () => {
384
+ const dbPath = tempDbPath();
385
+ try {
386
+ openDatabase(dbPath);
387
+ const id = createMemory({ category: 'gotcha', content: 'pre FTS auth memory must become searchable' });
388
+ assert.equal(id, 'MEM001');
389
+
390
+ const adapter = _getAdapter()!;
391
+ const fts = adapter.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memories_fts'").get();
392
+ if (!fts) return;
393
+
394
+ adapter.exec('DROP TRIGGER IF EXISTS memories_ai');
395
+ adapter.exec('DROP TRIGGER IF EXISTS memories_ad');
396
+ adapter.exec('DROP TRIGGER IF EXISTS memories_au');
397
+ adapter.exec('DROP TABLE IF EXISTS memories_fts');
398
+ adapter.prepare(
399
+ "DELETE FROM runtime_kv WHERE scope = 'global' AND scope_id = '' AND key = 'memories_fts_rebuilt_at'",
400
+ ).run();
401
+ closeDatabase();
402
+
403
+ openDatabase(dbPath);
404
+ const reopened = _getAdapter()!;
405
+ const marker = reopened.prepare(
406
+ "SELECT value_json FROM runtime_kv WHERE scope = 'global' AND scope_id = '' AND key = 'memories_fts_rebuilt_at'",
407
+ ).get();
408
+ assert.ok(marker, 'FTS rebuild marker should be set after reopening');
409
+
410
+ const hits = queryMemoriesRanked({ query: 'auth', k: 5 }).map((hit) => hit.memory.id);
411
+ assert.deepEqual(hits, ['MEM001']);
412
+ } finally {
413
+ cleanupDbPath(dbPath);
414
+ }
415
+ });
416
+
369
417
  // ═══════════════════════════════════════════════════════════════════════════
370
418
  // regression #4967 — createMemory must not silently swallow SQL errors
371
419
  // ═══════════════════════════════════════════════════════════════════════════
@@ -9,7 +9,15 @@ import { tmpdir } from "node:os";
9
9
  import { execFileSync } from "node:child_process";
10
10
 
11
11
  import { mergeMilestoneToMain } from "../auto-worktree.ts";
12
- import { closeDatabase, insertMilestone, openDatabase } from "../gsd-db.ts";
12
+ import { checkCloseoutConsistencyGate } from "../closeout-consistency-gate.ts";
13
+ import {
14
+ closeDatabase,
15
+ insertAssessment,
16
+ insertMilestone,
17
+ insertSlice,
18
+ insertTask,
19
+ openDatabase,
20
+ } from "../gsd-db.ts";
13
21
 
14
22
  function git(args: string[], cwd: string): string {
15
23
  return execFileSync("git", args, {
@@ -61,3 +69,25 @@ test("mergeMilestoneToMain blocks when project DB closeout is still open", () =>
61
69
  if (existsSync(repo)) rmSync(repo, { recursive: true, force: true });
62
70
  }
63
71
  });
72
+
73
+ test("closeout consistency treats deferred slices as inactive", () => {
74
+ try {
75
+ assert.equal(openDatabase(":memory:"), true);
76
+ insertMilestone({ id: "M001", title: "Milestone One", status: "complete" });
77
+ insertSlice({ milestoneId: "M001", id: "S01", title: "Done", status: "complete" });
78
+ insertTask({ milestoneId: "M001", sliceId: "S01", id: "T01", title: "Done", status: "complete" });
79
+ insertSlice({ milestoneId: "M001", id: "S02", title: "Deferred", status: "deferred" });
80
+ insertTask({ milestoneId: "M001", sliceId: "S02", id: "T02", title: "Deferred", status: "pending" });
81
+ insertAssessment({
82
+ path: "milestones/M001/M001-VALIDATION.md",
83
+ milestoneId: "M001",
84
+ status: "pass",
85
+ scope: "milestone-validation",
86
+ fullContent: "verdict: pass",
87
+ });
88
+
89
+ assert.deepEqual(checkCloseoutConsistencyGate("M001"), { ok: true });
90
+ } finally {
91
+ closeDatabase();
92
+ }
93
+ });
@@ -112,6 +112,12 @@ describe("pruneMetricsLedger: invalidates scoped ledger cache", () => {
112
112
  // 3. Prune to keepCount=2 — should evict 3 old units from disk AND clear scopedLedgers.
113
113
  const removed = pruneMetricsLedger(tmpDir, 2);
114
114
  assert.equal(removed, 3, "pruneMetricsLedger should report 3 removed units");
115
+ const prunedRaw = readFileSync(metricsPath(tmpDir), "utf-8");
116
+ assert.equal(
117
+ prunedRaw,
118
+ JSON.stringify(JSON.parse(prunedRaw)) + "\n",
119
+ "pruneMetricsLedger should persist compact metrics.json",
120
+ );
115
121
 
116
122
  // 4. Snapshot a new unit via scope. This exercises the lazy-reload path
117
123
  // (scopedLedgers was cleared by prune) and writes the result to disk.
@@ -259,6 +259,7 @@ test("initMetrics creates ledger, snapshotUnitMetrics persists across resets", (
259
259
  // Verify file content
260
260
  const raw = readFileSync(join(tmpBase, ".gsd", "metrics.json"), "utf-8");
261
261
  const parsed: MetricsLedger = JSON.parse(raw);
262
+ assert.equal(raw, JSON.stringify(parsed) + "\n", "metrics.json should be serialized compactly");
262
263
  assert.equal(parsed.version, 1);
263
264
  assert.equal(parsed.units.length, 1);
264
265
 
@@ -7,7 +7,10 @@ import {
7
7
  existsSync,
8
8
  mkdirSync,
9
9
  realpathSync,
10
+ readFileSync,
10
11
  } from "node:fs";
12
+ import fs from "node:fs";
13
+ import { syncBuiltinESMExports } from "node:module";
11
14
  import { join } from "node:path";
12
15
  import { tmpdir } from "node:os";
13
16
  import { execSync } from "node:child_process";
@@ -116,6 +119,60 @@ describe("migrate-external worktree guard (#2970)", () => {
116
119
  });
117
120
  });
118
121
 
122
+ describe("migrateToExternalState copy failure rollback (#1047)", () => {
123
+ test("restores .gsd when a source entry fails to copy", () => {
124
+ const base = realpathSync(mkdtempSync(join(tmpdir(), "gsd-migrate-copy-fail-")));
125
+ const stateDir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-state-copy-fail-")));
126
+ const previousStateDir = process.env.GSD_STATE_DIR;
127
+ const previousProjectId = process.env.GSD_PROJECT_ID;
128
+ const originalCpSync = fs.cpSync;
129
+ process.env.GSD_STATE_DIR = stateDir;
130
+ process.env.GSD_PROJECT_ID = "copy-fail";
131
+
132
+ try {
133
+ run("git init -b main", base);
134
+ mkdirSync(join(base, ".gsd"), { recursive: true });
135
+ writeFileSync(join(base, ".gsd", "PREFERENCES.md"), "# prefs\n", "utf-8");
136
+ writeFileSync(join(base, ".gsd", "gsd.db"), "state", "utf-8");
137
+ const failingSource = join(base, ".gsd.migrating", "gsd.db");
138
+ fs.cpSync = ((...args: Parameters<typeof fs.cpSync>) => {
139
+ const src = args[0];
140
+ if (String(src) === failingSource) {
141
+ const error = new Error("simulated copy failure") as NodeJS.ErrnoException;
142
+ error.code = "EACCES";
143
+ throw error;
144
+ }
145
+ return originalCpSync(...args);
146
+ }) as typeof fs.cpSync;
147
+ syncBuiltinESMExports();
148
+
149
+ const result = migrateToExternalState(base);
150
+
151
+ assert.equal(result.migrated, false, "copy failure must fail the migration");
152
+ assert.match(result.error ?? "", /Migration copy failed/);
153
+ assert.ok(existsSync(join(base, ".gsd", "gsd.db")), "source .gsd must be restored");
154
+ assert.equal(readFileSync(join(base, ".gsd", "PREFERENCES.md"), "utf-8"), "# prefs\n");
155
+ assert.ok(!existsSync(join(base, ".gsd.migrating")), "backup staging dir must not remain after restore");
156
+ assert.ok(!existsSync(join(stateDir, "projects", "copy-fail", "gsd.db")), "failed file must not be reported as migrated");
157
+ } finally {
158
+ fs.cpSync = originalCpSync;
159
+ syncBuiltinESMExports();
160
+ if (previousStateDir === undefined) {
161
+ delete process.env.GSD_STATE_DIR;
162
+ } else {
163
+ process.env.GSD_STATE_DIR = previousStateDir;
164
+ }
165
+ if (previousProjectId === undefined) {
166
+ delete process.env.GSD_PROJECT_ID;
167
+ } else {
168
+ process.env.GSD_PROJECT_ID = previousProjectId;
169
+ }
170
+ rmSync(base, { recursive: true, force: true });
171
+ rmSync(stateDir, { recursive: true, force: true });
172
+ }
173
+ });
174
+ });
175
+
119
176
  // Regression tests for #5571 — recoverFailedMigration handles orphaned .gsd.migrating
120
177
  describe("recoverFailedMigration (#5571)", () => {
121
178
  test("returns false when .gsd.migrating does not exist", () => {
@@ -429,3 +429,73 @@ test("rebuildMarkdownProjectionsFromDb realigns markdown when DB holds extra row
429
429
  cleanup(base);
430
430
  }
431
431
  });
432
+
433
+ test("rebuildMarkdownProjectionsFromDb realigns markdown for numeric milestone IDs", async () => {
434
+ const base = makeBase();
435
+ try {
436
+ await writeGSDDirectory({ projectContent: "# P\n", decisionsContent: "", requirements: [], milestones: [] }, base);
437
+ assert.equal(await ensureDbOpen(base), true);
438
+ insertMilestone({ id: "15", title: "Numeric milestone", status: "active" });
439
+ insertSlice({
440
+ id: "S01",
441
+ milestoneId: "15",
442
+ title: "Numeric slice",
443
+ status: "pending",
444
+ risk: "medium",
445
+ depends: [],
446
+ demo: "d",
447
+ sequence: 1,
448
+ });
449
+ insertTask({
450
+ id: "T01",
451
+ sliceId: "S01",
452
+ milestoneId: "15",
453
+ title: "Numeric task",
454
+ status: "pending",
455
+ });
456
+
457
+ const before = await checkMarkdownHierarchyAgainstDb(base);
458
+ assert.equal(before.recoveryCommand, "/gsd rebuild markdown");
459
+
460
+ const { rebuildMarkdownProjectionsFromDb } = await import("../commands-maintenance.ts");
461
+ const rebuild = await rebuildMarkdownProjectionsFromDb(base);
462
+ assert.ok(rebuild.rendered > 0, "expected markdown projections to render");
463
+
464
+ const after = await checkMarkdownHierarchyAgainstDb(base);
465
+ assert.equal(after.action, "none");
466
+ assert.equal(after.reason, "in-sync");
467
+ assert.deepEqual(after.markdown, { milestones: 1, slices: 1, tasks: 1 });
468
+ assert.deepEqual(after.beforeDb, { milestones: 1, slices: 1, tasks: 1 });
469
+ } finally {
470
+ cleanup(base);
471
+ }
472
+ });
473
+
474
+ test("rebuildMarkdownProjectionsFromDb realigns markdown for planned zero-slice milestones", async () => {
475
+ const base = makeBase();
476
+ try {
477
+ await writeGSDDirectory({ projectContent: "# P\n", decisionsContent: "", requirements: [], milestones: [] }, base);
478
+ assert.equal(await ensureDbOpen(base), true);
479
+ insertMilestone({
480
+ id: "M016",
481
+ title: "Queued milestone",
482
+ status: "queued",
483
+ planning: { vision: "Defined but not sliced yet" },
484
+ });
485
+
486
+ const before = await checkMarkdownHierarchyAgainstDb(base);
487
+ assert.equal(before.recoveryCommand, "/gsd rebuild markdown");
488
+
489
+ const { rebuildMarkdownProjectionsFromDb } = await import("../commands-maintenance.ts");
490
+ const rebuild = await rebuildMarkdownProjectionsFromDb(base);
491
+ assert.ok(rebuild.rendered > 0, "expected markdown projections to render");
492
+
493
+ const after = await checkMarkdownHierarchyAgainstDb(base);
494
+ assert.equal(after.action, "none");
495
+ assert.equal(after.reason, "in-sync");
496
+ assert.deepEqual(after.markdown, { milestones: 1, slices: 0, tasks: 0 });
497
+ assert.deepEqual(after.beforeDb, { milestones: 1, slices: 0, tasks: 0 });
498
+ } finally {
499
+ cleanup(base);
500
+ }
501
+ });