@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.738160b9

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 (488) hide show
  1. package/README.md +1 -1
  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/claude-code-cli/stream-adapter.js +11 -2
  10. package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
  11. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  12. package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
  13. package/dist/resources/extensions/gsd/auto/session.js +11 -0
  14. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
  15. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  16. package/dist/resources/extensions/gsd/auto-budget.js +5 -1
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +34 -20
  18. package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
  19. package/dist/resources/extensions/gsd/auto-prompts.js +29 -14
  20. package/dist/resources/extensions/gsd/auto-recovery.js +9 -510
  21. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  22. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  23. package/dist/resources/extensions/gsd/auto-verification.js +5 -2
  24. package/dist/resources/extensions/gsd/auto.js +19 -10
  25. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
  26. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +39 -0
  27. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +11 -5
  28. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
  29. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +2 -0
  30. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +41 -21
  31. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
  32. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +96 -10
  33. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  34. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
  35. package/dist/resources/extensions/gsd/commands/handlers/auto.js +1 -1
  36. package/dist/resources/extensions/gsd/commands-context.js +19 -1
  37. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +17 -11
  38. package/dist/resources/extensions/gsd/commands-usage.js +1 -1
  39. package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
  40. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
  41. package/dist/resources/extensions/gsd/db/engine.js +4 -1
  42. package/dist/resources/extensions/gsd/db/queries.js +60 -0
  43. package/dist/resources/extensions/gsd/db-memory-fts-schema.js +34 -0
  44. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  45. package/dist/resources/extensions/gsd/delegation-policy.js +1 -1
  46. package/dist/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  47. package/dist/resources/extensions/gsd/doctor-engine-checks.js +192 -4
  48. package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
  49. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
  50. package/dist/resources/extensions/gsd/exec-sandbox.js +53 -11
  51. package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
  52. package/dist/resources/extensions/gsd/forensics.js +2 -32
  53. package/dist/resources/extensions/gsd/git-service.js +61 -4
  54. package/dist/resources/extensions/gsd/gitignore.js +4 -1
  55. package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
  56. package/dist/resources/extensions/gsd/health-widget.js +55 -29
  57. package/dist/resources/extensions/gsd/json-persistence.js +20 -0
  58. package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
  59. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  60. package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
  61. package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
  62. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
  63. package/dist/resources/extensions/gsd/metrics.js +4 -4
  64. package/dist/resources/extensions/gsd/migrate-external.js +11 -2
  65. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  66. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  67. package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
  68. package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
  69. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  70. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  71. package/dist/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  72. package/dist/resources/extensions/gsd/quick.js +45 -2
  73. package/dist/resources/extensions/gsd/root-write-leak-guard.js +7 -1
  74. package/dist/resources/extensions/gsd/safety/destructive-guard.js +8 -1
  75. package/dist/resources/extensions/gsd/safety/evidence-collector.js +16 -1
  76. package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
  77. package/dist/resources/extensions/gsd/session-forensics.js +11 -1
  78. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  79. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  80. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  81. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  82. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  83. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  84. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
  85. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  86. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  87. package/dist/resources/extensions/gsd/state.js +12 -611
  88. package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
  89. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  90. package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
  91. package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
  92. package/dist/resources/extensions/gsd/tools/plan-slice.js +59 -44
  93. package/dist/resources/extensions/gsd/tools/plan-task.js +28 -5
  94. package/dist/resources/extensions/gsd/undo.js +43 -6
  95. package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
  96. package/dist/resources/extensions/gsd/unit-registry.js +36 -6
  97. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
  98. package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
  99. package/dist/resources/extensions/gsd/verification-gate.js +7 -0
  100. package/dist/resources/extensions/gsd/workflow-manifest.js +126 -13
  101. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  102. package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
  103. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  104. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  105. package/dist/web/standalone/.next/BUILD_ID +1 -1
  106. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  107. package/dist/web/standalone/.next/build-manifest.json +3 -3
  108. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  109. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  110. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  112. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  114. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  115. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  121. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  126. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  127. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  128. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/index.html +1 -1
  130. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  131. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  132. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  133. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  134. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  135. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  136. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  138. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  139. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  142. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  143. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  144. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  145. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  146. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  147. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  148. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  149. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  150. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  151. package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
  152. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  153. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  154. package/package.json +1 -1
  155. package/packages/cloud-mcp-gateway/package.json +2 -2
  156. package/packages/contracts/dist/workflow.d.ts +1 -0
  157. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  158. package/packages/contracts/dist/workflow.js +2 -0
  159. package/packages/contracts/dist/workflow.js.map +1 -1
  160. package/packages/contracts/package.json +1 -1
  161. package/packages/daemon/package.json +4 -4
  162. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  163. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  164. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  165. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  166. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  167. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  168. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  169. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  170. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  171. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  172. package/packages/gsd-agent-core/dist/index.js +2 -0
  173. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  174. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  175. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  176. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  177. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  178. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  179. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  180. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  181. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  182. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  183. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  184. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  185. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  186. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  187. package/packages/gsd-agent-core/package.json +5 -5
  188. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  189. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  190. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  191. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  192. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  193. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  194. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  195. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  196. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  197. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  198. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
  199. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  200. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  201. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  202. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  203. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  204. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  205. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  206. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  207. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  208. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  209. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  210. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  211. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  212. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  213. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  214. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  215. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  216. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  217. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  218. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  219. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  220. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  221. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  222. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  223. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  224. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  225. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  226. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  227. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  228. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  229. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  230. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  231. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  232. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  233. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  234. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  235. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  236. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts +2 -0
  237. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  238. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js +23 -5
  239. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-client.js.map +1 -1
  240. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  241. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  242. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  243. package/packages/gsd-agent-modes/package.json +7 -7
  244. package/packages/mcp-server/README.md +1 -1
  245. package/packages/mcp-server/dist/server.d.ts +1 -1
  246. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  247. package/packages/mcp-server/dist/server.js +3 -3
  248. package/packages/mcp-server/dist/server.js.map +1 -1
  249. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  250. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  251. package/packages/mcp-server/dist/workflow-tools.js +35 -21
  252. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  253. package/packages/mcp-server/package.json +4 -4
  254. package/packages/native/package.json +1 -1
  255. package/packages/pi-agent-core/dist/agent-loop.js +3 -2
  256. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  257. package/packages/pi-agent-core/package.json +1 -1
  258. package/packages/pi-ai/dist/models.generated.d.ts +46 -0
  259. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  260. package/packages/pi-ai/dist/models.generated.js +38 -0
  261. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  262. package/packages/pi-ai/package.json +1 -1
  263. package/packages/pi-coding-agent/README.md +3 -2
  264. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  265. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  266. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  267. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  268. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  269. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  270. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  271. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  272. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  273. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  274. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  275. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  276. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  277. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  278. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  279. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  280. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  281. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  282. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  283. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  284. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  285. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  286. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  287. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  288. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  289. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  290. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  291. package/packages/pi-coding-agent/package.json +7 -7
  292. package/packages/pi-tui/package.json +2 -2
  293. package/packages/rpc-client/package.json +2 -2
  294. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  295. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  296. package/pkg/dist/theme/theme-schema.js +1 -1
  297. package/pkg/dist/theme/theme-schema.js.map +1 -1
  298. package/pkg/dist/theme/theme.d.ts.map +1 -1
  299. package/pkg/dist/theme/theme.js +11 -7
  300. package/pkg/dist/theme/theme.js.map +1 -1
  301. package/pkg/package.json +1 -1
  302. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  303. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
  304. package/src/resources/extensions/claude-code-cli/tests/index.test.ts +14 -0
  305. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
  306. package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
  307. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  308. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
  309. package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
  310. package/src/resources/extensions/gsd/auto/session.ts +14 -0
  311. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
  312. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  313. package/src/resources/extensions/gsd/auto-budget.ts +12 -1
  314. package/src/resources/extensions/gsd/auto-dispatch.ts +35 -24
  315. package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
  316. package/src/resources/extensions/gsd/auto-prompts.ts +41 -15
  317. package/src/resources/extensions/gsd/auto-recovery.ts +13 -511
  318. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  319. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  320. package/src/resources/extensions/gsd/auto-verification.ts +5 -1
  321. package/src/resources/extensions/gsd/auto.ts +23 -8
  322. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
  323. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +44 -0
  324. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +11 -5
  325. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
  326. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +2 -0
  327. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +52 -21
  328. package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
  329. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +104 -10
  330. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  331. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
  332. package/src/resources/extensions/gsd/commands/handlers/auto.ts +1 -1
  333. package/src/resources/extensions/gsd/commands-context.ts +18 -1
  334. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -10
  335. package/src/resources/extensions/gsd/commands-usage.ts +1 -1
  336. package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
  337. package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
  338. package/src/resources/extensions/gsd/db/engine.ts +8 -1
  339. package/src/resources/extensions/gsd/db/queries.ts +79 -0
  340. package/src/resources/extensions/gsd/db-memory-fts-schema.ts +45 -0
  341. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  342. package/src/resources/extensions/gsd/delegation-policy.ts +1 -1
  343. package/src/resources/extensions/gsd/docs/preferences-reference.md +2 -2
  344. package/src/resources/extensions/gsd/doctor-engine-checks.ts +233 -5
  345. package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
  346. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
  347. package/src/resources/extensions/gsd/doctor-types.ts +3 -0
  348. package/src/resources/extensions/gsd/exec-sandbox.ts +56 -11
  349. package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
  350. package/src/resources/extensions/gsd/forensics.ts +2 -33
  351. package/src/resources/extensions/gsd/git-service.ts +69 -5
  352. package/src/resources/extensions/gsd/gitignore.ts +4 -1
  353. package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
  354. package/src/resources/extensions/gsd/health-widget.ts +69 -32
  355. package/src/resources/extensions/gsd/json-persistence.ts +20 -0
  356. package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
  357. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  358. package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
  359. package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
  360. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
  361. package/src/resources/extensions/gsd/metrics.ts +4 -4
  362. package/src/resources/extensions/gsd/migrate-external.ts +8 -2
  363. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  364. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  365. package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
  366. package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
  367. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  368. package/src/resources/extensions/gsd/prompts/plan-milestone.md +2 -2
  369. package/src/resources/extensions/gsd/prompts/plan-slice.md +3 -3
  370. package/src/resources/extensions/gsd/quick.ts +43 -2
  371. package/src/resources/extensions/gsd/root-write-leak-guard.ts +13 -1
  372. package/src/resources/extensions/gsd/safety/destructive-guard.ts +9 -1
  373. package/src/resources/extensions/gsd/safety/evidence-collector.ts +16 -1
  374. package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
  375. package/src/resources/extensions/gsd/session-forensics.ts +11 -1
  376. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  377. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  378. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  379. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  380. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  381. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  382. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
  383. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  384. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  385. package/src/resources/extensions/gsd/state.ts +32 -732
  386. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
  387. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  388. package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +14 -5
  389. package/src/resources/extensions/gsd/tests/auto-pause-double-entry-guard.test.ts +36 -0
  390. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +182 -4
  391. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +146 -1
  392. package/src/resources/extensions/gsd/tests/auto-retry-mcp-churn-fixes.test.ts +30 -0
  393. package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
  394. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  395. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
  396. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
  397. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
  398. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
  399. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
  400. package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
  401. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
  402. package/src/resources/extensions/gsd/tests/db-memory-fts-schema.test.ts +58 -3
  403. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
  404. package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +44 -0
  405. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +143 -0
  406. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
  407. package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
  408. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
  409. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +239 -2
  410. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
  411. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +16 -0
  412. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
  413. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
  414. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
  415. package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
  416. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
  417. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
  418. package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
  419. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +130 -3
  420. package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
  421. package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
  422. package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
  423. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
  424. package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
  425. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
  426. package/src/resources/extensions/gsd/tests/memory-store.test.ts +48 -0
  427. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
  428. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
  429. package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
  430. package/src/resources/extensions/gsd/tests/migrate-external-worktree.test.ts +57 -0
  431. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  432. package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
  433. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
  434. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
  435. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -1
  436. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
  437. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
  438. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +103 -4
  439. package/src/resources/extensions/gsd/tests/plan-task.test.ts +44 -1
  440. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +4 -2
  441. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
  442. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  443. package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +58 -0
  444. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +8 -6
  445. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
  446. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
  447. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
  448. package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +103 -0
  449. package/src/resources/extensions/gsd/tests/root-write-leak-guard.test.ts +20 -1
  450. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +15 -3
  451. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  452. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  453. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +208 -0
  454. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +32 -3
  455. package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
  456. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
  457. package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
  458. package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +38 -0
  459. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
  460. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
  461. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
  462. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +96 -15
  463. package/src/resources/extensions/gsd/tests/workflow-mcp-preflight.test.ts +1 -0
  464. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +8 -4
  465. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +6 -5
  466. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
  467. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  468. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  469. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  470. package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
  471. package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
  472. package/src/resources/extensions/gsd/tools/plan-slice.ts +60 -47
  473. package/src/resources/extensions/gsd/tools/plan-task.ts +30 -5
  474. package/src/resources/extensions/gsd/undo.ts +47 -6
  475. package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
  476. package/src/resources/extensions/gsd/unit-registry.ts +36 -6
  477. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
  478. package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
  479. package/src/resources/extensions/gsd/verification-gate.ts +8 -0
  480. package/src/resources/extensions/gsd/workflow-manifest.ts +141 -7
  481. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  482. package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
  483. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  484. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  485. package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
  486. package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
  487. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → 2A_WqqCS-EoYZxN9_v3d2}/_buildManifest.js +0 -0
  488. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → 2A_WqqCS-EoYZxN9_v3d2}/_ssgManifest.js +0 -0
@@ -101,6 +101,32 @@ test("analyzeSessionContext buckets injections, tool results, and loaded skills"
101
101
  assert.equal(result.subagentSpawns, 1);
102
102
  });
103
103
 
104
+ test("analyzeSessionContext bounds large assistant tool-call arguments", () => {
105
+ const result = analyzeSessionContext(sessionEntries({
106
+ role: "assistant",
107
+ content: [
108
+ {
109
+ type: "toolCall",
110
+ id: "tc-save",
111
+ name: "gsd_summary_save",
112
+ arguments: {
113
+ path: ".gsd/milestones/M001/M001-SUMMARY.md",
114
+ artifact_type: "SUMMARY",
115
+ content: "x".repeat(20_000),
116
+ },
117
+ },
118
+ ],
119
+ timestamp: TS,
120
+ }), PROVIDER);
121
+
122
+ const assistant = result.conversationSections.find((section) => section.label === "Assistant responses");
123
+ assert.ok(assistant);
124
+ assert.ok(
125
+ assistant.tokens < 200,
126
+ `assistant tool-call arguments should be redacted before token counting, got ${assistant.tokens} tokens`,
127
+ );
128
+ });
129
+
104
130
  test("formatContextReport lists skills and subagents", () => {
105
131
  const report = buildContextBreakdown({
106
132
  modelLabel: "claude-code/claude-sonnet-4-6",
@@ -460,6 +460,7 @@ describe("Batch 3 prompt templates resolve", () => {
460
460
  const out = loadPrompt("code-review", { scope: "s", depth: "deep", fixMode: "off", reviewId: "001" });
461
461
  assert.match(out, /deep/);
462
462
  assert.match(out, /001/);
463
+ assert.match(out, /diff-first/i, "code-review prompt should require diff-first context gathering");
463
464
  });
464
465
  test("review.md loads with target + reviewers", () => {
465
466
  const out = loadPrompt("review", { target: "t", reviewers: "claude, codex" });
@@ -1,10 +1,22 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
+ import { execFileSync } from "node:child_process";
4
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
3
7
 
4
8
  import {
5
9
  formatCleanKeepReason,
10
+ handleWorktree,
6
11
  type WorktreeStatus,
7
12
  } from "../commands-worktree.ts";
13
+ import { withCommandCwd } from "../commands/context.ts";
14
+ import { createWorktree } from "../worktree-manager.ts";
15
+ import {
16
+ disableDebug,
17
+ enableDebug,
18
+ getDebugCounters,
19
+ } from "../debug-logger.ts";
8
20
 
9
21
  function mkStatus(over: Partial<WorktreeStatus>): WorktreeStatus {
10
22
  const name = over.name ?? "feat-x";
@@ -22,6 +34,45 @@ function mkStatus(over: Partial<WorktreeStatus>): WorktreeStatus {
22
34
  };
23
35
  }
24
36
 
37
+ function git(cwd: string, args: string[]): string {
38
+ return execFileSync("git", args, {
39
+ cwd,
40
+ stdio: ["ignore", "pipe", "pipe"],
41
+ encoding: "utf-8",
42
+ }).trim();
43
+ }
44
+
45
+ function makeRepo(): string {
46
+ const base = mkdtempSync(join(tmpdir(), "gsd-worktree-command-"));
47
+ git(base, ["init", "-b", "main"]);
48
+ git(base, ["config", "user.name", "Test User"]);
49
+ git(base, ["config", "user.email", "test@example.com"]);
50
+ mkdirSync(join(base, ".gsd"), { recursive: true });
51
+ writeFileSync(join(base, "README.md"), "# Test\n", "utf-8");
52
+ git(base, ["add", "."]);
53
+ git(base, ["commit", "-m", "chore: init"]);
54
+ return base;
55
+ }
56
+
57
+ function createCommittedWorktree(base: string, name: string): void {
58
+ const wt = createWorktree(base, name);
59
+ writeFileSync(join(wt.path, `${name}.txt`), `${name}\n`, "utf-8");
60
+ git(wt.path, ["add", "."]);
61
+ git(wt.path, ["commit", "-m", `feat: ${name}`]);
62
+ }
63
+
64
+ function createMockCtx() {
65
+ const notifications: { message: string; level: string }[] = [];
66
+ return {
67
+ notifications,
68
+ ui: {
69
+ notify(message: string, level: string) {
70
+ notifications.push({ message, level });
71
+ },
72
+ },
73
+ };
74
+ }
75
+
25
76
  test("clean keep reason shows uncommitted-only worktrees clearly", () => {
26
77
  const reason = formatCleanKeepReason(mkStatus({ uncommitted: true }));
27
78
  assert.equal(reason, "uncommitted changes");
@@ -46,3 +97,32 @@ test("clean keep reason uses singular form for a single changed file", () => {
46
97
  const reason = formatCleanKeepReason(mkStatus({ filesChanged: 1, uncommitted: false }));
47
98
  assert.equal(reason, "1 changed file");
48
99
  });
100
+
101
+ test("worktree list detects main branch once for the command", async (t) => {
102
+ if (process.env.GSD_ENABLE_NATIVE_GSD_GIT === "1") {
103
+ t.skip("git invocation regression is specific to the CLI fallback path");
104
+ return;
105
+ }
106
+
107
+ const base = makeRepo();
108
+ try {
109
+ createCommittedWorktree(base, "feature-a");
110
+ createCommittedWorktree(base, "feature-b");
111
+
112
+ const ctx = createMockCtx();
113
+ enableDebug(base);
114
+ try {
115
+ await withCommandCwd(base, async () => {
116
+ await handleWorktree("list", ctx as any);
117
+ });
118
+
119
+ assert.equal(ctx.notifications.length, 1);
120
+ assert.match(ctx.notifications[0].message, /Worktrees — 2/);
121
+ assert.equal(getDebugCounters().gitInvocations, 12);
122
+ } finally {
123
+ disableDebug();
124
+ }
125
+ } finally {
126
+ rmSync(base, { recursive: true, force: true });
127
+ }
128
+ });
@@ -312,6 +312,34 @@ console.log('\n=== complete-milestone: incomplete slices block closeout ===');
312
312
  cleanup(dbPath);
313
313
  }
314
314
 
315
+ // ═══════════════════════════════════════════════════════════════════════════
316
+ // complete-milestone: deferred slices are inactive for closeout
317
+ // ═══════════════════════════════════════════════════════════════════════════
318
+
319
+ console.log('\n=== complete-milestone: deferred slices do not block closeout ===');
320
+ {
321
+ const dbPath = tempDbPath();
322
+ openDatabase(dbPath);
323
+ const { basePath } = createTempProject();
324
+ seedCompletedMilestone({ basePath, validationVerdict: 'pass' });
325
+ insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Deferred Slice', status: 'deferred' });
326
+ insertTask({
327
+ id: 'T02',
328
+ sliceId: 'S02',
329
+ milestoneId: 'M001',
330
+ status: 'pending',
331
+ title: 'Deferred Slice Task',
332
+ });
333
+
334
+ const result = await handleCompleteMilestone(makeValidParams(), basePath);
335
+
336
+ assertTrue(!('error' in result), 'deferred slice should not block milestone completion');
337
+ assertEq(getMilestone('M001')!.status, 'complete', 'milestone should complete with deferred inactive slice');
338
+
339
+ cleanupDir(basePath);
340
+ cleanup(dbPath);
341
+ }
342
+
315
343
  // ═══════════════════════════════════════════════════════════════════════════
316
344
  // complete-milestone: deep task check (slice closed, task not)
317
345
  // ═══════════════════════════════════════════════════════════════════════════
@@ -7,6 +7,10 @@ import { join } from "node:path";
7
7
 
8
8
  import { postUnitPreVerification } from "../auto-post-unit.ts";
9
9
  import { AutoSession } from "../auto/session.ts";
10
+ import {
11
+ decideVerificationRetry,
12
+ hashVerificationFailureContext,
13
+ } from "../auto/verification-retry-policy.ts";
10
14
  import { cleanup, makeTempRepo } from "./test-utils.ts";
11
15
 
12
16
  function makePostUnitContext(base: string, s: AutoSession, notifications: string[]) {
@@ -227,3 +231,68 @@ test("complete-slice with gsd_replan_slice but no REPLAN artifact retries", asyn
227
231
  cleanup(base);
228
232
  }
229
233
  });
234
+
235
+ test("artifact retry context stays stable across attempts while notifications show attempt count", async () => {
236
+ const base = makeTempRepo("gsd-artifact-retry-stable-context-");
237
+ try {
238
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S01"), { recursive: true });
239
+
240
+ const s = new AutoSession();
241
+ s.active = true;
242
+ s.basePath = base;
243
+ s.currentUnit = { type: "complete-slice", id: "M001/S01", startedAt: Date.now() };
244
+
245
+ const notifications: string[] = [];
246
+ const pctx = makePostUnitContext(base, s, notifications);
247
+ const opts = {
248
+ skipSettleDelay: true,
249
+ skipWorktreeSync: true,
250
+ agentEndMessages: [
251
+ {
252
+ role: "toolResult",
253
+ toolName: "gsd_replan_slice",
254
+ isError: false,
255
+ content: "Slice replanned with reopened task T02.",
256
+ },
257
+ ],
258
+ };
259
+
260
+ assert.equal(await postUnitPreVerification(pctx, opts), "retry");
261
+ const firstRetry = s.pendingVerificationRetry;
262
+ assert.ok(firstRetry);
263
+ assert.equal(firstRetry.attempt, 1);
264
+ assert.doesNotMatch(firstRetry.failureContext, /\(attempt \d\/3\)\.$/);
265
+ const firstFailureHash = hashVerificationFailureContext(firstRetry.failureContext);
266
+
267
+ assert.equal(await postUnitPreVerification(pctx, opts), "retry");
268
+ const secondRetry = s.pendingVerificationRetry;
269
+ assert.ok(secondRetry);
270
+ assert.equal(secondRetry.attempt, 2);
271
+ assert.equal(secondRetry.failureContext, firstRetry.failureContext);
272
+ assert.equal(hashVerificationFailureContext(secondRetry.failureContext), firstFailureHash);
273
+ assert.deepEqual(
274
+ decideVerificationRetry({
275
+ unitType: s.currentUnit.type,
276
+ retryInfo: secondRetry,
277
+ previousFailureHash: firstFailureHash,
278
+ random: () => 0.5,
279
+ }),
280
+ {
281
+ action: "pause",
282
+ reason: "duplicate-failure-context",
283
+ key: "complete-slice:M001/S01",
284
+ failureHash: firstFailureHash,
285
+ },
286
+ );
287
+ assert.ok(
288
+ notifications.some((message) => message.includes("Retrying (attempt 1/3).")),
289
+ `expected first attempt notification, got: ${notifications.join("\n")}`,
290
+ );
291
+ assert.ok(
292
+ notifications.some((message) => message.includes("Retrying (attempt 2/3).")),
293
+ `expected second attempt notification, got: ${notifications.join("\n")}`,
294
+ );
295
+ } finally {
296
+ cleanup(base);
297
+ }
298
+ });
@@ -203,6 +203,10 @@ console.log('\n=== complete-slice: handler happy path ===');
203
203
  insertSlice({ id: 'S02', milestoneId: 'M001', title: 'Second Slice', risk: 'low', depends: ['S01'], demo: 'advanced stuff', sequence: 2 });
204
204
  insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', status: 'complete', title: 'Task 1' });
205
205
  insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', status: 'complete', title: 'Task 2' });
206
+ insertTask({ id: 'T99', sliceId: 'S02', milestoneId: 'M001', status: 'complete', title: 'Sibling Task' });
207
+ const siblingSummaryPath = path.join(path.dirname(roadmapPath), 'T99-SUMMARY.md');
208
+ const siblingSummaryBefore = '# Existing sibling task summary\n\nDo not rewrite this file.\n';
209
+ fs.writeFileSync(siblingSummaryPath, siblingSummaryBefore);
206
210
 
207
211
  const params = makeValidSliceParams();
208
212
  const result = await handleCompleteSlice(params, basePath);
@@ -267,6 +271,13 @@ console.log('\n=== complete-slice: handler happy path ===');
267
271
  // (e) Verify slice status is complete in DB
268
272
  assertEq(sliceAfter!.status, 'complete', 'slice status should be complete in DB');
269
273
  assertTrue(sliceAfter!.completed_at !== null, 'completed_at should be set in DB');
274
+
275
+ // (f) Verify unrelated completed task summaries are not re-rendered during slice completion
276
+ assertEq(
277
+ fs.readFileSync(siblingSummaryPath, 'utf-8'),
278
+ siblingSummaryBefore,
279
+ 'complete-slice should not re-render sibling completed task summaries',
280
+ );
270
281
  }
271
282
 
272
283
  cleanupDir(basePath);
@@ -1,6 +1,6 @@
1
1
  import { describe, it, afterEach } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { mkdirSync, readFileSync, rmSync, writeFileSync, promises as fsPromises } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import { randomUUID } from "node:crypto";
@@ -41,7 +41,7 @@ const VALID_PARAMS = {
41
41
  ],
42
42
  };
43
43
 
44
- describe("complete-task projection failures keep DB completion committed", () => {
44
+ describe("complete-task projection failures roll back DB completion", () => {
45
45
  let base: string;
46
46
 
47
47
  afterEach(() => {
@@ -75,7 +75,44 @@ describe("complete-task projection failures keep DB completion committed", () =>
75
75
  assert.equal(rows.length, 2, "should have 2 evidence rows after success");
76
76
  });
77
77
 
78
- it("keeps task completion and verification_evidence when disk projection write fails", async () => {
78
+ it("reopens the workflow DB when it disappears between SUMMARY.md write and plan render", async (t) => {
79
+ base = makeTmpBase();
80
+ openDatabase(join(base, ".gsd", "gsd.db"));
81
+ insertMilestone({ id: "M001" });
82
+ insertSlice({ id: "S01", milestoneId: "M001" });
83
+
84
+ const planPath = join(base, ".gsd", "milestones", "M001", "slices", "S01", "S01-PLAN.md");
85
+ writeFileSync(
86
+ planPath,
87
+ "# S01 Plan\n\n## Tasks\n\n- [ ] **T01: Test task**\n",
88
+ );
89
+
90
+ const originalRename = fsPromises.rename.bind(fsPromises);
91
+ let closedAfterSummaryWrite = false;
92
+ t.mock.method(fsPromises, "rename", async (...args: Parameters<typeof fsPromises.rename>) => {
93
+ await originalRename(...args);
94
+ const target = String(args[1]);
95
+ if (!closedAfterSummaryWrite && target.endsWith("T01-SUMMARY.md")) {
96
+ closedAfterSummaryWrite = true;
97
+ closeDatabase();
98
+ }
99
+ });
100
+
101
+ const result = await handleCompleteTask(VALID_PARAMS, base);
102
+ assert.ok(closedAfterSummaryWrite, "test fixture should close DB after SUMMARY.md atomic rename");
103
+ assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`);
104
+
105
+ const content = readFileSync(planPath, "utf-8");
106
+ assert.match(content, /\[x\][^\n]*\*\*T01\*\*/, "PLAN.md checkbox should be rendered after DB reopen");
107
+
108
+ const adapter = _getAdapter()!;
109
+ const task = adapter.prepare(
110
+ `SELECT status FROM tasks WHERE milestone_id = 'M001' AND slice_id = 'S01' AND id = 'T01'`,
111
+ ).get() as { status: string } | undefined;
112
+ assert.equal(task?.status, "complete", "task should remain complete after successful projection");
113
+ });
114
+
115
+ it("rolls back DB completion and clears verification_evidence when disk projection write fails", async () => {
79
116
  base = makeTmpBase();
80
117
  openDatabase(join(base, ".gsd", "gsd.db"));
81
118
  insertMilestone({ id: "M001" });
@@ -87,19 +124,22 @@ describe("complete-task projection failures keep DB completion committed", () =>
87
124
  writeFileSync(tasksDir, "not-a-directory");
88
125
 
89
126
  const result = await handleCompleteTask(VALID_PARAMS, base);
90
- assert.ok(!("error" in result), `unexpected error: ${"error" in result ? result.error : ""}`);
91
- assert.equal(result.stale, true, "result should report stale projection");
127
+ assert.ok("error" in result, "expected rollback error when projection write fails");
128
+ assert.ok(
129
+ (result as { error: string }).error.includes("rolled completion back to pending"),
130
+ `error should mention rollback; got: ${"error" in result ? result.error : ""}`,
131
+ );
92
132
 
93
133
  const adapter = _getAdapter()!;
94
134
  const task = adapter.prepare(
95
135
  `SELECT status FROM tasks WHERE milestone_id = 'M001' AND slice_id = 'S01' AND id = 'T01'`,
96
136
  ).get() as { status: string } | undefined;
97
- assert.ok(task, "task row should still exist");
98
- assert.equal(task!.status, "complete", "task status should remain complete");
137
+ assert.ok(task, "task row should still exist after rollback");
138
+ assert.equal(task!.status, "pending", "task status should be rolled back to pending");
99
139
 
100
140
  const evidenceRows = adapter.prepare(
101
141
  `SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'`,
102
142
  ).all();
103
- assert.equal(evidenceRows.length, 2, "verification_evidence should remain committed");
143
+ assert.equal(evidenceRows.length, 0, "verification_evidence should be deleted after rollback");
104
144
  });
105
145
  });
@@ -380,6 +380,81 @@ console.log('\n=== complete-task: handler happy path ===');
380
380
  cleanup(dbPath);
381
381
  }
382
382
 
383
+ // ═══════════════════════════════════════════════════════════════════════════
384
+ // complete-task: Projection failure rolls DB completion back
385
+ // ═══════════════════════════════════════════════════════════════════════════
386
+
387
+ console.log('\n=== complete-task: projection failure rolls DB completion back ===');
388
+ {
389
+ const dbPath = tempDbPath();
390
+ openDatabase(dbPath);
391
+
392
+ const { basePath, planPath } = createTempProject();
393
+
394
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
395
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice' });
396
+
397
+ fs.unlinkSync(planPath);
398
+ fs.mkdirSync(planPath, { recursive: true });
399
+
400
+ const result = await handleCompleteTask(makeValidParams(), basePath);
401
+
402
+ assertTrue('error' in result, 'projection failure should return an error');
403
+ if ('error' in result) {
404
+ assertMatch(result.error, /projection write failed/, 'error should mention projection write failure');
405
+ }
406
+
407
+ const task = getTask('M001', 'S01', 'T01');
408
+ assertTrue(task !== null, 'task row should remain for retry');
409
+ assertEq(task!.status, 'pending', 'task status should be rolled back to pending');
410
+ assertEq(task!.completed_at, null, 'rolled back task should not keep completed_at');
411
+
412
+ const adapter = _getAdapter()!;
413
+ const evRows = adapter.prepare(
414
+ "SELECT * FROM verification_evidence WHERE task_id = 'T01' AND slice_id = 'S01' AND milestone_id = 'M001'"
415
+ ).all();
416
+ assertEq(evRows.length, 0, 'verification evidence should be deleted when projection rollback runs');
417
+
418
+ const summaryPath = path.join(path.dirname(planPath), 'T01-SUMMARY.md');
419
+ assertTrue(!fs.existsSync(summaryPath), 'SUMMARY.md should be removed so disk state stays pending');
420
+
421
+ cleanupDir(basePath);
422
+ cleanup(dbPath);
423
+ }
424
+
425
+ // ═══════════════════════════════════════════════════════════════════════════
426
+ // complete-task: Handler does not re-render completed sibling summaries
427
+ // ═══════════════════════════════════════════════════════════════════════════
428
+
429
+ console.log('\n=== complete-task: handler leaves completed sibling summaries untouched ===');
430
+ {
431
+ const dbPath = tempDbPath();
432
+ openDatabase(dbPath);
433
+
434
+ const { basePath, planPath } = createTempProject();
435
+
436
+ insertMilestone({ id: 'M001', title: 'Test Milestone' });
437
+ insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Test Slice', risk: 'high', depends: [], demo: 'basic functionality works', sequence: 1 });
438
+ insertTask({ id: 'T00', sliceId: 'S01', milestoneId: 'M001', status: 'complete', title: 'Already complete task', oneLiner: 'Previously completed' });
439
+ insertTask({ id: 'T02', sliceId: 'S01', milestoneId: 'M001', status: 'pending', title: 'Second task' });
440
+
441
+ const siblingSummaryPath = path.join(path.dirname(planPath), 'T00-SUMMARY.md');
442
+ const siblingSummaryContent = 'existing sibling summary marker\n';
443
+ fs.writeFileSync(siblingSummaryPath, siblingSummaryContent);
444
+
445
+ const result = await handleCompleteTask(makeValidParams(), basePath);
446
+
447
+ assertTrue(!('error' in result), 'handler should succeed without error');
448
+ assertEq(
449
+ fs.readFileSync(siblingSummaryPath, 'utf-8'),
450
+ siblingSummaryContent,
451
+ 'complete-task should not re-render summaries for already-completed sibling tasks',
452
+ );
453
+
454
+ cleanupDir(basePath);
455
+ cleanup(dbPath);
456
+ }
457
+
383
458
  // ═══════════════════════════════════════════════════════════════════════════
384
459
  // complete-task: hard-blocker escalation with mid-execution escalation disabled
385
460
  // ═══════════════════════════════════════════════════════════════════════════
@@ -4,9 +4,10 @@
4
4
 
5
5
  import test from "node:test";
6
6
  import assert from "node:assert/strict";
7
- import { mkdirSync, rmSync } from "node:fs";
8
- import { join } from "node:path";
7
+ import { chmodSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
8
+ import { delimiter, join } from "node:path";
9
9
  import { tmpdir } from "node:os";
10
+ import { performance } from "node:perf_hooks";
10
11
 
11
12
  import { GSDDashboardOverlay } from "../dashboard-overlay.ts";
12
13
  import type { UnitMetrics } from "../metrics.ts";
@@ -85,6 +86,58 @@ test("GSDDashboardOverlay non-identity refresh avoids reparsing preferences", as
85
86
  );
86
87
  });
87
88
 
89
+ test("GSDDashboardOverlay render and scroll do not run environment doctor subprocesses", (t) => {
90
+ const basePath = join(
91
+ tmpdir(),
92
+ `gsd-dashboard-overlay-env-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
93
+ );
94
+ const shimDir = join(
95
+ tmpdir(),
96
+ `gsd-dashboard-overlay-shim-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
97
+ );
98
+ mkdirSync(basePath, { recursive: true });
99
+ mkdirSync(shimDir, { recursive: true });
100
+ writeFileSync(join(basePath, "package.json"), JSON.stringify({ engines: { node: ">=22.0.0" } }));
101
+
102
+ const posixNodeShim = join(shimDir, "node");
103
+ writeFileSync(posixNodeShim, "#!/bin/sh\nsleep 1\nexit 1\n");
104
+ chmodSync(posixNodeShim, 0o755);
105
+ writeFileSync(join(shimDir, "node.cmd"), "@echo off\r\nping -n 2 127.0.0.1 > nul\r\nexit /b 1\r\n");
106
+
107
+ const originalPath = process.env.PATH;
108
+ const overlay = new GSDDashboardOverlay({ requestRender() {} }, fakeTheme as any, () => {});
109
+ overlay.dispose();
110
+
111
+ t.after(() => {
112
+ if (originalPath === undefined) {
113
+ delete process.env.PATH;
114
+ } else {
115
+ process.env.PATH = originalPath;
116
+ }
117
+ rmSync(basePath, { recursive: true, force: true });
118
+ rmSync(shimDir, { recursive: true, force: true });
119
+ });
120
+
121
+ (overlay as any).loading = false;
122
+ (overlay as any).milestoneData = null;
123
+ (overlay as any).dashData = {
124
+ ...(overlay as any).dashData,
125
+ basePath,
126
+ };
127
+
128
+ process.env.PATH = `${shimDir}${delimiter}${originalPath ?? ""}`;
129
+ const start = performance.now();
130
+ overlay.render(100);
131
+ overlay.handleInput("j");
132
+ overlay.render(100);
133
+ const elapsed = performance.now() - start;
134
+
135
+ assert.ok(
136
+ elapsed < 500,
137
+ `rendering and scrolling should not wait for environment subprocesses, took ${Math.round(elapsed)}ms`,
138
+ );
139
+ });
140
+
88
141
  function makeUnit(id: string, cost: number): UnitMetrics {
89
142
  return {
90
143
  type: "execute-task",
@@ -3,11 +3,16 @@
3
3
 
4
4
  import { describe, test } from "node:test";
5
5
  import assert from "node:assert/strict";
6
+ import { createRequire } from "node:module";
6
7
  import {
8
+ MEMORIES_FTS_REBUILT_KEY,
7
9
  isMemoriesFtsAvailableSchema,
10
+ rebuildMemoriesFtsSchemaOnce,
8
11
  tryCreateMemoriesFtsSchema,
9
12
  } from "../db-memory-fts-schema.ts";
10
- import type { DbAdapter, DbStatement } from "../db-adapter.ts";
13
+ import { createDbAdapter, type DbAdapter, type DbStatement } from "../db-adapter.ts";
14
+
15
+ const _require = createRequire(import.meta.url);
11
16
 
12
17
  class FakeStatement implements DbStatement {
13
18
  private readonly row: Record<string, unknown> | undefined;
@@ -32,6 +37,7 @@ class FakeStatement implements DbStatement {
32
37
  class FakeAdapter implements DbAdapter {
33
38
  readonly execCalls: string[] = [];
34
39
  hasFts = false;
40
+ hasRebuildMarker = false;
35
41
  failExec = false;
36
42
 
37
43
  exec(sql: string): void {
@@ -39,13 +45,26 @@ class FakeAdapter implements DbAdapter {
39
45
  if (this.failExec) throw new Error("fts unavailable");
40
46
  }
41
47
 
42
- prepare(): DbStatement {
43
- return new FakeStatement(this.hasFts ? { name: "memories_fts" } : undefined);
48
+ prepare(sql: string): DbStatement {
49
+ if (sql.includes("sqlite_master")) {
50
+ return new FakeStatement(this.hasFts ? { name: "memories_fts" } : undefined);
51
+ }
52
+ if (sql.includes("runtime_kv")) {
53
+ return new FakeStatement(this.hasRebuildMarker ? { present: 1 } : undefined);
54
+ }
55
+ return new FakeStatement(undefined);
44
56
  }
45
57
 
46
58
  close(): void {}
47
59
  }
48
60
 
61
+ function openMemoryAdapter(): { db: DbAdapter; close: () => void } {
62
+ const sqlite = _require("node:sqlite") as { DatabaseSync: new (path: string) => unknown };
63
+ const raw = new sqlite.DatabaseSync(":memory:");
64
+ const db = createDbAdapter(raw);
65
+ return { db, close: () => db.close() };
66
+ }
67
+
49
68
  describe("db-memory-fts-schema", () => {
50
69
  test("creates memories_fts and insert/delete/update triggers", () => {
51
70
  const db = new FakeAdapter();
@@ -83,4 +102,40 @@ describe("db-memory-fts-schema", () => {
83
102
  db.hasFts = true;
84
103
  assert.equal(isMemoriesFtsAvailableSchema(db), true);
85
104
  });
105
+
106
+ test("rebuilds the external-content index once and records a marker", () => {
107
+ const { db, close } = openMemoryAdapter();
108
+ try {
109
+ db.exec("CREATE TABLE memories (seq INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL)");
110
+ db.exec("INSERT INTO memories (content) VALUES ('pre FTS auth memory must be searchable')");
111
+
112
+ const ok = tryCreateMemoriesFtsSchema(db);
113
+ if (!ok) return;
114
+
115
+ const before = db.prepare("SELECT rowid FROM memories_fts WHERE memories_fts MATCH 'auth'").all();
116
+ assert.equal(before.length, 0, "external-content FTS starts empty for existing rows");
117
+
118
+ rebuildMemoriesFtsSchemaOnce(db);
119
+
120
+ const after = db.prepare("SELECT rowid FROM memories_fts WHERE memories_fts MATCH 'auth'").all();
121
+ assert.deepEqual(after.map((row) => row["rowid"]), [1]);
122
+ const marker = db.prepare(
123
+ "SELECT value_json FROM runtime_kv WHERE scope = 'global' AND scope_id = '' AND key = :key",
124
+ ).get({ ":key": MEMORIES_FTS_REBUILT_KEY });
125
+ assert.ok(marker, "rebuild marker should be stored");
126
+ } finally {
127
+ close();
128
+ }
129
+ });
130
+
131
+ test("skips rebuild when marker already exists", () => {
132
+ const db = new FakeAdapter();
133
+ db.hasFts = true;
134
+ db.hasRebuildMarker = true;
135
+
136
+ rebuildMemoriesFtsSchemaOnce(db);
137
+
138
+ assert.ok(db.execCalls.some((sql) => sql.includes("CREATE TABLE IF NOT EXISTS runtime_kv")));
139
+ assert.ok(!db.execCalls.some((sql) => sql.includes("VALUES('rebuild')")));
140
+ });
86
141
  });