@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
@@ -1,15 +1,18 @@
1
1
  import { clearParseCache } from "../files.js";
2
2
  import { isClosedStatus } from "../status-guards.js";
3
3
  import { isNonEmptyString, validateStringArray } from "../validation.js";
4
- import { transaction, getSlice, getTask, insertTask, upsertTaskPlanning } from "../gsd-db.js";
4
+ import { getGateIdsForTurn } from "../gate-registry.js";
5
+ import { transaction, getSlice, getTask, insertTask, upsertTaskPlanning, insertGateRow, setSliceSketchFlag } from "../gsd-db.js";
5
6
  import { invalidateStateCache } from "../state.js";
6
7
  import { renderTaskPlanFromDb, renderPlanFromDb } from "../markdown-renderer.js";
7
- import { resolveSliceFile, resolveTasksDir } from "../paths.js";
8
+ import { resolveTasksDir } from "../paths.js";
8
9
  import { flushWorkflowProjections } from "../projection-flush.js";
9
10
  import { writeManifest } from "../workflow-manifest.js";
10
11
  import { appendEvent } from "../workflow-events.js";
11
12
  import { logWarning } from "../workflow-logger.js";
13
+ import { loadEffectiveGSDPreferences } from "../preferences.js";
12
14
  import { validatePathOnlyPlanningFields, validatePlanningPathScope } from "../planning-path-scope.js";
15
+ import type { GateId } from "../types.js";
13
16
 
14
17
  export interface PlanTaskParams {
15
18
  milestoneId: string;
@@ -57,6 +60,12 @@ function validateParams(params: PlanTaskParams): PlanTaskParams {
57
60
  };
58
61
  }
59
62
 
63
+ function resolveTaskGates(basePath: string): GateId[] {
64
+ const loaded = loadEffectiveGSDPreferences(basePath);
65
+ if (loaded?.preferences?.gate_evaluation?.task_gates === false) return [];
66
+ return [...getGateIdsForTurn("execute-task")];
67
+ }
68
+
60
69
  export async function handlePlanTask(
61
70
  rawParams: PlanTaskParams,
62
71
  basePath: string,
@@ -84,6 +93,14 @@ export async function handlePlanTask(
84
93
  return { error: `validation failed: ${pathScopeError}` };
85
94
  }
86
95
 
96
+ let taskGates: GateId[];
97
+ try {
98
+ taskGates = resolveTaskGates(basePath);
99
+ } catch (err) {
100
+ const message = err instanceof Error ? err.message : String(err);
101
+ return { error: `validation failed: ${message}` };
102
+ }
103
+
87
104
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
88
105
  // Guards must be inside the transaction so the state they check cannot
89
106
  // change between the read and the write (#2723).
@@ -127,6 +144,9 @@ export async function handlePlanTask(
127
144
  observabilityImpact: params.observabilityImpact ?? "",
128
145
  fullPlanMd: params.fullPlanMd,
129
146
  });
147
+ for (const gid of taskGates) {
148
+ insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "task", taskId: params.taskId });
149
+ }
130
150
  });
131
151
  } catch (err) {
132
152
  return { error: `db write failed: ${(err as Error).message}` };
@@ -142,17 +162,22 @@ export async function handlePlanTask(
142
162
  // Flat-phase: tasks live as checkboxes in the slice plan's <tasks> block,
143
163
  // not as standalone TID-PLAN.md files. Re-render the slice plan so the
144
164
  // new/updated task appears in the plan file that gsd-core reads.
145
- // Guard: resolveTasksDir is null in flat-phase (no tasks/ subdir exists);
146
- // only sync if a slice plan already exists (guards pre-plan-slice calls).
165
+ // Guard: resolveTasksDir is null in flat-phase (no tasks/ subdir exists).
166
+ let slicePlanSynced = false;
147
167
  try {
148
168
  const tDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
149
- if (!tDir && resolveSliceFile(basePath, params.milestoneId, params.sliceId, "PLAN")) {
169
+ if (!tDir) {
150
170
  await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
171
+ slicePlanSynced = true;
151
172
  }
152
173
  } catch (syncErr) {
153
174
  logWarning("tool", `plan-task: slice-plan sync failed: ${(syncErr as Error).message}`);
154
175
  }
155
176
 
177
+ if (slicePlanSynced) {
178
+ setSliceSketchFlag(params.milestoneId, params.sliceId, false);
179
+ }
180
+
156
181
  invalidateStateCache();
157
182
  clearParseCache();
158
183
 
@@ -15,6 +15,7 @@ import { gsdRoot, resolveTasksDir, resolveSlicePath, resolveSliceFile, resolveTa
15
15
  import { sendDesktopNotification } from "./notifications.js";
16
16
  import { getTask, getSlice, getSliceTasks, updateTaskStatus, resetSliceCascade } from "./gsd-db.js";
17
17
  import { renderPlanCheckboxes, renderRoadmapCheckboxes } from "./markdown-renderer.js";
18
+ import { UNIT_REGISTRY } from "./unit-registry.js";
18
19
 
19
20
  /**
20
21
  * Undo the last completed unit: revert git commits,
@@ -42,16 +43,20 @@ export async function handleUndo(args: string, ctx: ExtensionCommandContext, _pi
42
43
  return;
43
44
  }
44
45
 
45
- // Extract unit type and ID from the most recent activity log filename
46
- // Format: <seq>-<unitType>-<unitId>.jsonl
47
- const match = files[0].match(/^\d+-(.+?)-(.+)\.jsonl$/);
48
- if (!match) {
46
+ // Extract unit type and ID from the most recent activity log filename.
47
+ // Both the unit type and the unit ID may contain hyphens, so anchor on the
48
+ // known unit-type vocabulary instead of guessing the unit-ID shape: a regex
49
+ // tuned to milestone-shaped IDs rejects project-level units whose IDs are
50
+ // symbolic (e.g. discuss-project uses PROJECT, workflow-preferences uses
51
+ // WORKFLOW-PREFS).
52
+ const parsed = parseActivityLogFilename(files[0]);
53
+ if (!parsed) {
49
54
  ctx.ui.notify("Nothing to undo — could not parse latest activity log.", "warning");
50
55
  return;
51
56
  }
52
57
 
53
- const unitType = match[1];
54
- const unitId = match[2].replace(/-/g, "/");
58
+ const unitType = parsed.unitType;
59
+ const unitId = parsed.unitId.replace(/-/g, "/");
55
60
 
56
61
  if (!force) {
57
62
  ctx.ui.notify(
@@ -100,6 +105,11 @@ export async function handleUndo(args: string, ctx: ExtensionCommandContext, _pi
100
105
  if (unitType === "execute-task" && task !== undefined && slice !== undefined) {
101
106
  const [mid, sid, tid] = [milestone, slice, task];
102
107
  planUpdated = uncheckTaskInPlan(basePath, mid, sid, tid);
108
+ if (getTask(mid, sid, tid)) {
109
+ updateTaskStatus(mid, sid, tid, "pending");
110
+ await renderPlanCheckboxes(basePath, mid, sid);
111
+ planUpdated = true;
112
+ }
103
113
  }
104
114
 
105
115
  // 3. Try to revert git commits from activity log
@@ -379,6 +389,37 @@ export async function handleResetSlice(
379
389
 
380
390
  // ─── Helpers ──────────────────────────────────────────────────────────────────
381
391
 
392
+ // Known unit types sorted longest-first so a more specific type (e.g.
393
+ // "execute-task-simple") matches before a prefix of it ("execute-task") when
394
+ // splitting "<seq>-<unitType>-<unitId>.jsonl".
395
+ const UNIT_TYPES_BY_LENGTH_DESC: readonly string[] = Object.keys(UNIT_REGISTRY).sort(
396
+ (a, b) => b.length - a.length,
397
+ );
398
+
399
+ /**
400
+ * Parse an activity-log filename of the form `<seq>-<unitType>-<unitId>.jsonl`
401
+ * (the format written by activity-log.ts). Both the unit type and the unit ID
402
+ * may contain hyphens, so we anchor on the known unit-type vocabulary rather
403
+ * than guessing the ID shape. This keeps non-milestone IDs (e.g. PROJECT,
404
+ * WORKFLOW-PREFS) parseable. Returns null when the name has no sequence prefix
405
+ * or does not start with a recognised unit type.
406
+ */
407
+ export function parseActivityLogFilename(
408
+ filename: string,
409
+ ): { unitType: string; unitId: string } | null {
410
+ const seqMatch = filename.match(/^\d+-(.+)\.jsonl$/);
411
+ if (!seqMatch) return null;
412
+ const rest = seqMatch[1];
413
+ for (const unitType of UNIT_TYPES_BY_LENGTH_DESC) {
414
+ const prefix = `${unitType}-`;
415
+ if (rest.startsWith(prefix)) {
416
+ const unitId = rest.slice(prefix.length);
417
+ if (unitId.length > 0) return { unitType, unitId };
418
+ }
419
+ }
420
+ return null;
421
+ }
422
+
382
423
  export function uncheckTaskInPlan(basePath: string, mid: string, sid: string, tid: string): boolean {
383
424
  const slicePath = resolveSlicePath(basePath, mid, sid);
384
425
  if (!slicePath) return false;
@@ -135,14 +135,38 @@ const CONTEXT_MODE_GUIDANCE_BY_LANE: Record<Exclude<ContextModePolicy, "none">,
135
135
  "Use `gsd_resume` for prior context, `gsd_exec_search` for saved evidence, and `gsd_exec` for noisy doc validation commands.",
136
136
  };
137
137
 
138
- // Per-unit overrides win over the lane default. run-uat's tool contract
139
- // forbids `gsd_exec`/`gsd_exec_search` (acceptance evidence must flow through
140
- // `gsd_uat_exec`) and Claude Code dispatch strips the tools entirely, so the
141
- // shared verification-lane guidance would steer the agent into calling an
142
- // unavailable tool.
143
- const CONTEXT_MODE_GUIDANCE_BY_UNIT: Record<string, string> = {
138
+ // Per-unit overrides win over the lane default. Some units intentionally run
139
+ // with narrower tool contracts than their shared Context Mode lane, so their
140
+ // guidance must name only tools the unit can actually call.
141
+ export const CONTEXT_MODE_GUIDANCE_BY_UNIT: Readonly<Record<string, string>> = {
142
+ "discuss-milestone":
143
+ "Use `ask_user_questions` to continue the milestone interview, then persist outcomes with `gsd_summary_save`, `gsd_decision_save`, `gsd_requirement_save`, `gsd_requirement_update`, `gsd_plan_milestone`, or `gsd_milestone_generate_id` as appropriate.",
144
+ "discuss-slice":
145
+ "Use `ask_user_questions` to continue the slice interview, then persist outcomes with `gsd_summary_save` or `gsd_decision_save` as appropriate.",
146
+ "discuss-project":
147
+ "Use `ask_user_questions` to continue the project interview, then persist outcomes with `gsd_summary_save`, `gsd_decision_save`, or `gsd_requirement_save` as appropriate.",
148
+ "discuss-requirements":
149
+ "Use `ask_user_questions` to continue the requirements interview, then persist outcomes with `gsd_requirement_save` or `gsd_summary_save` as appropriate.",
150
+ "replan-slice":
151
+ "Use `gsd_replan_slice` to persist the revised slice plan, and `gsd_decision_save` for planning decisions that need durable rationale.",
152
+ "reassess-roadmap":
153
+ "Use `gsd_milestone_status` to inspect current milestone state, then `gsd_reassess_roadmap` to persist the roadmap reassessment.",
144
154
  "run-uat":
145
155
  "Use `gsd_uat_exec` for acceptance checks so evidence is typed as UAT-owned, and `gsd_resume` after compaction or resume.",
156
+ "research-project":
157
+ "Dispatch parallel scout subagents for stack, features, architecture, and pitfalls research; each writes one file under `.gsd/research/` (`STACK.md`, `FEATURES.md`, `ARCHITECTURE.md`, `PITFALLS.md`).",
158
+ "gate-evaluate":
159
+ "Use `subagent` to dispatch tester agents, then persist each gate with `gsd_save_gate_result`; rely on testers for verification evidence.",
160
+ };
161
+
162
+ // Per-unit guidance for the nested render mode (renderMode: "nested"), used when this
163
+ // unit's Context Mode line is embedded into a subagent prompt — e.g. the tester prompts
164
+ // dispatched by gate-evaluate. Must instruct the subagent on what IT should do, not
165
+ // re-state the parent coordinator's dispatch instructions. Falls back to
166
+ // CONTEXT_MODE_GUIDANCE_BY_UNIT then the lane default when no nested entry exists.
167
+ export const CONTEXT_MODE_NESTED_GUIDANCE_BY_UNIT: Readonly<Record<string, string>> = {
168
+ "gate-evaluate":
169
+ "Run verification checks to answer the gate question, then persist the verdict with `gsd_save_gate_result`.",
146
170
  };
147
171
 
148
172
  /**
@@ -160,7 +184,9 @@ export function composeContextModeInstructions(
160
184
 
161
185
  const lane = CONTEXT_MODE_LANE_LABELS[manifest.contextMode];
162
186
  const guidance =
163
- CONTEXT_MODE_GUIDANCE_BY_UNIT[unitType] ?? CONTEXT_MODE_GUIDANCE_BY_LANE[manifest.contextMode];
187
+ (opts.renderMode === "nested" ? CONTEXT_MODE_NESTED_GUIDANCE_BY_UNIT[unitType] : undefined)
188
+ ?? CONTEXT_MODE_GUIDANCE_BY_UNIT[unitType]
189
+ ?? CONTEXT_MODE_GUIDANCE_BY_LANE[manifest.contextMode];
164
190
  if (opts.renderMode === "nested") {
165
191
  return `Context Mode (${lane} lane): ${guidance}`;
166
192
  }
@@ -98,7 +98,13 @@ export const UNIT_REGISTRY = {
98
98
  scopeClass: "standard",
99
99
  phaseChain: ["research"],
100
100
  toolContract: {
101
- allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
101
+ allowedGsdTools: [
102
+ "gsd_summary_save",
103
+ "gsd_decision_save",
104
+ "gsd_exec",
105
+ "gsd_exec_search",
106
+ "gsd_resume",
107
+ ],
102
108
  requiredWorkflowTools: ["gsd_summary_save"],
103
109
  },
104
110
  },
@@ -111,10 +117,11 @@ export const UNIT_REGISTRY = {
111
117
  "gsd_milestone_status",
112
118
  "gsd_plan_milestone",
113
119
  "gsd_plan_slice",
120
+ "gsd_plan_task",
114
121
  "gsd_decision_save",
115
122
  "gsd_requirement_update",
116
123
  ],
117
- requiredWorkflowTools: ["gsd_milestone_status", "gsd_plan_milestone", "gsd_plan_slice"],
124
+ requiredWorkflowTools: ["gsd_milestone_status", "gsd_plan_milestone", "gsd_plan_slice", "gsd_plan_task"],
118
125
  },
119
126
  },
120
127
  "discuss-milestone": {
@@ -154,7 +161,15 @@ export const UNIT_REGISTRY = {
154
161
  scopeClass: "section-close",
155
162
  phaseChain: ["validation", "planning"],
156
163
  toolContract: {
157
- allowedGsdTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap", "subagent"],
164
+ allowedGsdTools: [
165
+ "gsd_milestone_status",
166
+ "gsd_exec",
167
+ "gsd_exec_search",
168
+ "gsd_resume",
169
+ "gsd_validate_milestone",
170
+ "gsd_reassess_roadmap",
171
+ "subagent",
172
+ ],
158
173
  requiredWorkflowTools: ["gsd_milestone_status", "gsd_validate_milestone", "gsd_reassess_roadmap"],
159
174
  },
160
175
  },
@@ -165,6 +180,9 @@ export const UNIT_REGISTRY = {
165
180
  toolContract: {
166
181
  allowedGsdTools: [
167
182
  "gsd_milestone_status",
183
+ "gsd_exec",
184
+ "gsd_exec_search",
185
+ "gsd_resume",
168
186
  "gsd_requirement_update",
169
187
  "gsd_summary_save",
170
188
  "gsd_complete_milestone",
@@ -202,13 +220,14 @@ export const UNIT_REGISTRY = {
202
220
  allowedGsdTools: [
203
221
  "gsd_milestone_status",
204
222
  "gsd_plan_slice",
223
+ "gsd_plan_task",
205
224
  "gsd_reassess_roadmap",
206
225
  "gsd_decision_save",
207
226
  "gsd_exec",
208
227
  "gsd_exec_search",
209
228
  "gsd_resume",
210
229
  ],
211
- requiredWorkflowTools: ["gsd_plan_slice", "gsd_reassess_roadmap"],
230
+ requiredWorkflowTools: ["gsd_plan_slice", "gsd_plan_task", "gsd_reassess_roadmap"],
212
231
  },
213
232
  },
214
233
  "refine-slice": {
@@ -244,6 +263,8 @@ export const UNIT_REGISTRY = {
244
263
  allowedGsdTools: [
245
264
  "gsd_milestone_status",
246
265
  "gsd_exec",
266
+ "gsd_exec_search",
267
+ "gsd_resume",
247
268
  "gsd_slice_complete",
248
269
  "gsd_task_reopen",
249
270
  "gsd_replan_slice",
@@ -326,7 +347,13 @@ export const UNIT_REGISTRY = {
326
347
  scopeClass: "execute-task",
327
348
  phaseChain: ["execution"],
328
349
  toolContract: {
329
- allowedGsdTools: ["gsd_task_complete", "gsd_summary_save", "gsd_decision_save"],
350
+ allowedGsdTools: [
351
+ "gsd_milestone_status",
352
+ "gsd_task_complete",
353
+ "gsd_exec",
354
+ "gsd_summary_save",
355
+ "gsd_decision_save",
356
+ ],
330
357
  requiredWorkflowTools: ["gsd_task_complete", "gsd_summary_save"],
331
358
  },
332
359
  },
@@ -413,12 +440,15 @@ export const UNIT_REGISTRY = {
413
440
  requiredWorkflowTools: ["ask_user_questions"],
414
441
  },
415
442
  },
443
+ // research-project dispatches 4 parallel scout subagents (Task calls); each scout
444
+ // writes one file under .gsd/research/ directly. The parent coordinator does not
445
+ // call gsd_summary_save or gsd_decision_save — the scouts own their own output.
416
446
  "research-project": {
417
447
  kind: "primary",
418
448
  scopeClass: "standard",
419
449
  phaseChain: ["research"],
420
450
  toolContract: {
421
- allowedGsdTools: ["gsd_summary_save", "gsd_decision_save"],
451
+ allowedGsdTools: [],
422
452
  requiredWorkflowTools: [],
423
453
  },
424
454
  },
@@ -1,6 +1,8 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Block new workflow entry when completed milestone branches are still unmerged.
3
3
 
4
+ import { execFileSync } from "node:child_process";
5
+
4
6
  import {
5
7
  nativeBranchExists,
6
8
  nativeDetectMainBranch,
@@ -12,17 +14,23 @@ import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
12
14
  import { getAllMilestones } from "./gsd-db.js";
13
15
  import { resolveMilestoneIntegrationBranch } from "./git-service.js";
14
16
  import { loadEffectiveGSDPreferences } from "./preferences.js";
15
- import { captureRootDirtySnapshot, type RootDirtyEntry } from "./root-write-leak-guard.js";
16
17
  import { isClosedStatus } from "./status-guards.js";
17
18
 
19
+ export interface UnmergedMilestoneDirtyEntry {
20
+ path: string;
21
+ status: string;
22
+ }
23
+
18
24
  export interface UnmergedMilestoneBlocker {
19
25
  milestoneId: string;
20
26
  branch: string;
21
27
  integrationBranch: string;
22
28
  files: string[];
23
- dirtyOverlap: RootDirtyEntry[];
29
+ dirtyOverlap: UnmergedMilestoneDirtyEntry[];
24
30
  }
25
31
 
32
+ type UnmergedMilestoneDirtySnapshot = Map<string, UnmergedMilestoneDirtyEntry>;
33
+
26
34
  const BLOCKED_COMMANDS = new Set([
27
35
  "auto",
28
36
  "next",
@@ -67,6 +75,32 @@ function isRuntimePath(path: string): boolean {
67
75
  return path === ".gsd" || path.startsWith(".gsd/");
68
76
  }
69
77
 
78
+ function captureDirtyPathStatusSnapshot(rootPath: string): UnmergedMilestoneDirtySnapshot {
79
+ const snapshot: UnmergedMilestoneDirtySnapshot = new Map();
80
+ let status = "";
81
+ try {
82
+ status = execFileSync("git", ["status", "--porcelain", "--untracked-files=all"], {
83
+ cwd: rootPath,
84
+ stdio: ["ignore", "pipe", "pipe"],
85
+ encoding: "utf-8",
86
+ });
87
+ } catch {
88
+ return snapshot;
89
+ }
90
+
91
+ for (const line of status.split("\n")) {
92
+ if (!line.trim()) continue;
93
+ const code = line.slice(0, 2);
94
+ const path = line.slice(3).replace(/^"|"$/g, "");
95
+ if (!path || isRuntimePath(path)) continue;
96
+ snapshot.set(path, {
97
+ path,
98
+ status: code.trim() || code,
99
+ });
100
+ }
101
+ return snapshot;
102
+ }
103
+
70
104
  function formatCommandLabel(attemptedCommand: string): string {
71
105
  const trimmed = attemptedCommand.trim();
72
106
  return trimmed ? `/gsd ${trimmed}` : "/gsd";
@@ -132,7 +166,7 @@ export async function findUnmergedCompletedMilestones(base: string): Promise<Unm
132
166
  await ensureDbOpen(base);
133
167
 
134
168
  const blockers: UnmergedMilestoneBlocker[] = [];
135
- const dirtyByPath = captureRootDirtySnapshot(base);
169
+ let dirtyByPath: UnmergedMilestoneDirtySnapshot | null = null;
136
170
 
137
171
  for (const milestone of getAllMilestones()) {
138
172
  if (!isClosedStatus(milestone.status)) continue;
@@ -158,14 +192,16 @@ export async function findUnmergedCompletedMilestones(base: string): Promise<Unm
158
192
  const uniqueFiles = [...new Set(files)].sort();
159
193
  if (uniqueFiles.length === 0) continue;
160
194
 
195
+ if (!dirtyByPath) dirtyByPath = captureDirtyPathStatusSnapshot(base);
196
+ const dirtySnapshot = dirtyByPath;
161
197
  blockers.push({
162
198
  milestoneId: milestone.id,
163
199
  branch,
164
200
  integrationBranch,
165
201
  files: uniqueFiles,
166
202
  dirtyOverlap: uniqueFiles
167
- .map((path) => dirtyByPath.get(path))
168
- .filter((entry): entry is RootDirtyEntry => Boolean(entry)),
203
+ .map((path) => dirtySnapshot.get(path))
204
+ .filter((entry): entry is UnmergedMilestoneDirtyEntry => Boolean(entry)),
169
205
  });
170
206
  }
171
207
 
@@ -7,7 +7,7 @@ import { getAutoWorktreePath, isInAutoWorktree } from "./auto-worktree.js";
7
7
  import { ensureDbOpen } from "./bootstrap/dynamic-tools.js";
8
8
  import { refreshWorkflowDatabaseFromDisk } from "./db-workspace.js";
9
9
  import { getIsolationMode } from "./preferences.js";
10
- import { deriveState } from "./state.js";
10
+ import { deriveState, invalidateStateCache } from "./state.js";
11
11
  import type { GSDState } from "./types.js";
12
12
  import { detectWorktreeName } from "./worktree.js";
13
13
 
@@ -133,12 +133,7 @@ export function formatValidationBlockedMessage(
133
133
  ].join("\n\n");
134
134
  }
135
135
 
136
- export async function getValidationBlockMessageForBase(
137
- base: string,
138
- attemptedCommand = "",
139
- ): Promise<string | null> {
140
- await ensureDbOpen(base);
141
- refreshWorkflowDatabaseFromDisk();
136
+ async function deriveValidationBlockState(base: string): Promise<GSDState> {
142
137
  let state = await deriveState(base);
143
138
 
144
139
  if (
@@ -153,5 +148,16 @@ export async function getValidationBlockMessageForBase(
153
148
  }
154
149
  }
155
150
 
151
+ return state;
152
+ }
153
+
154
+ export async function getValidationBlockMessageForBase(
155
+ base: string,
156
+ attemptedCommand = "",
157
+ ): Promise<string | null> {
158
+ await ensureDbOpen(base);
159
+ refreshWorkflowDatabaseFromDisk();
160
+ invalidateStateCache();
161
+ const state = await deriveValidationBlockState(base);
156
162
  return formatValidationBlockedMessage(state, attemptedCommand);
157
163
  }
@@ -244,6 +244,14 @@ export function formatFailureContext(result: VerificationResult): string {
244
244
  return header + body;
245
245
  }
246
246
 
247
+ export function formatFailureSignature(result: VerificationResult): string {
248
+ return result.checks
249
+ .filter((check) => check.exitCode !== 0)
250
+ .map((check) => `${check.command.trim()}#${check.exitCode}`)
251
+ .sort()
252
+ .join("\n");
253
+ }
254
+
247
255
  // ─── Gate Execution ─────────────────────────────────────────────────────────
248
256
 
249
257
  /** Characters that indicate shell control syntax when unquoted in a command string. */
@@ -10,11 +10,12 @@ import type { ArtifactRow, MilestoneRow } from "./db-milestone-artifact-rows.js"
10
10
  import type { SliceRow, TaskRow } from "./db-task-slice-rows.js";
11
11
  import type { VerificationEvidenceRow } from "./db-verification-evidence-rows.js";
12
12
  import type { Decision, GateRow, Requirement } from "./types.js";
13
- import { atomicWriteSync } from "./atomic-write.js";
13
+ import { atomicWriteAsync, atomicWriteSync } from "./atomic-write.js";
14
14
  import { getAllDecisionsFromMemories } from "./context-store.js";
15
15
  import { backfillDecisionsToMemories } from "./memory-backfill.js";
16
16
  import { invalidateAllCaches } from "./cache.js";
17
- import { readFileSync, existsSync, mkdirSync } from "node:fs";
17
+ import { logWarning } from "./workflow-logger.js";
18
+ import { readFileSync, existsSync } from "node:fs";
18
19
  import { isAbsolute, join, relative, resolve } from "node:path";
19
20
 
20
21
  // ─── Manifest Types ──────────────────────────────────────────────────────
@@ -349,16 +350,146 @@ export function snapshotState(): StateManifest {
349
350
 
350
351
  // ─── writeManifest ───────────────────────────────────────────────────────
351
352
 
353
+ interface ManifestWriteState {
354
+ filePath: string;
355
+ latestJson: string | null;
356
+ inFlightJson: string | null;
357
+ /** Set by sync exit flush so an in-flight async write cannot leave stale data on disk. */
358
+ syncFlushJson: string | null;
359
+ active: Promise<void> | null;
360
+ }
361
+
362
+ const manifestWrites = new Map<string, ManifestWriteState>();
363
+ let processTeardownFlushInstalled = false;
364
+ let beforeExitFlushStarted = false;
365
+
366
+ function manifestWriteKey(basePath: string): string {
367
+ return resolve(basePath);
368
+ }
369
+
370
+ function manifestFilePath(basePath: string): string {
371
+ return join(resolve(basePath), ".gsd", "state-manifest.json");
372
+ }
373
+
374
+ function describeError(err: unknown): string {
375
+ return err instanceof Error ? err.message : String(err);
376
+ }
377
+
378
+ function logManifestWriteFailure(filePath: string, err: unknown): void {
379
+ logWarning("manifest", `state manifest write failed: ${describeError(err)}`, { file: filePath });
380
+ }
381
+
382
+ async function drainManifestWrites(key: string, state: ManifestWriteState): Promise<void> {
383
+ let lastWriteError: unknown = null;
384
+ try {
385
+ while (state.latestJson !== null && state.syncFlushJson === null) {
386
+ const json = state.latestJson;
387
+ state.latestJson = null;
388
+ state.inFlightJson = json;
389
+ try {
390
+ await atomicWriteAsync(state.filePath, json);
391
+ if (state.syncFlushJson !== null) {
392
+ atomicWriteSync(state.filePath, state.syncFlushJson);
393
+ break;
394
+ }
395
+ lastWriteError = null;
396
+ } catch (err) {
397
+ lastWriteError = err;
398
+ logManifestWriteFailure(state.filePath, err);
399
+ } finally {
400
+ state.inFlightJson = null;
401
+ }
402
+ }
403
+ } finally {
404
+ state.active = null;
405
+ if (state.latestJson === null && state.inFlightJson === null) {
406
+ manifestWrites.delete(key);
407
+ }
408
+ }
409
+ if (lastWriteError !== null && state.syncFlushJson === null) {
410
+ throw lastWriteError;
411
+ }
412
+ }
413
+
414
+ function enqueueManifestWrite(basePath: string, json: string): void {
415
+ const key = manifestWriteKey(basePath);
416
+ let state = manifestWrites.get(key);
417
+ if (!state) {
418
+ state = {
419
+ filePath: manifestFilePath(basePath),
420
+ latestJson: null,
421
+ inFlightJson: null,
422
+ syncFlushJson: null,
423
+ active: null,
424
+ };
425
+ manifestWrites.set(key, state);
426
+ }
427
+
428
+ state.latestJson = json;
429
+ if (!state.active) {
430
+ const active = drainManifestWrites(key, state);
431
+ void active.catch(() => {});
432
+ state.active = active;
433
+ }
434
+ }
435
+
352
436
  /**
353
- * Write current DB state to .gsd/state-manifest.json via atomicWriteSync.
437
+ * Queue current DB state for an async atomic write to .gsd/state-manifest.json.
354
438
  * Uses JSON.stringify with 2-space indent for git three-way merge friendliness.
355
439
  */
356
440
  export function writeManifest(basePath: string): void {
357
441
  const manifest = snapshotState();
358
442
  const json = JSON.stringify(manifest, null, 2);
359
- const dir = join(basePath, ".gsd");
360
- mkdirSync(dir, { recursive: true });
361
- atomicWriteSync(join(dir, "state-manifest.json"), json);
443
+ enqueueManifestWrite(basePath, json);
444
+ }
445
+
446
+ async function flushManifestByKey(key: string): Promise<void> {
447
+ for (;;) {
448
+ const active = manifestWrites.get(key)?.active;
449
+ if (!active) return;
450
+ await active;
451
+ }
452
+ }
453
+
454
+ /**
455
+ * Wait for any queued manifest write for this base path to become durable.
456
+ */
457
+ export async function flushManifest(basePath: string): Promise<void> {
458
+ await flushManifestByKey(manifestWriteKey(basePath));
459
+ }
460
+
461
+ export async function flushAllManifests(): Promise<void> {
462
+ await Promise.all(Array.from(manifestWrites.keys(), key => flushManifestByKey(key)));
463
+ }
464
+
465
+ function flushAllManifestsSync(): void {
466
+ for (const [key, state] of manifestWrites) {
467
+ const json = state.latestJson ?? state.inFlightJson;
468
+ if (json === null) continue;
469
+ try {
470
+ atomicWriteSync(state.filePath, json);
471
+ state.latestJson = null;
472
+ state.inFlightJson = null;
473
+ state.syncFlushJson = json;
474
+ manifestWrites.delete(key);
475
+ } catch (err) {
476
+ logManifestWriteFailure(state.filePath, err);
477
+ }
478
+ }
479
+ }
480
+
481
+ export function installManifestFlushOnProcessTeardown(): void {
482
+ if (processTeardownFlushInstalled) return;
483
+ processTeardownFlushInstalled = true;
484
+
485
+ process.once("beforeExit", () => {
486
+ if (beforeExitFlushStarted) return;
487
+ beforeExitFlushStarted = true;
488
+ void flushAllManifests();
489
+ });
490
+ process.once("exit", () => {
491
+ flushAllManifestsSync();
492
+ });
362
493
  }
363
494
 
364
495
  // ─── readManifest ────────────────────────────────────────────────────────
@@ -414,9 +545,12 @@ function restoreArtifactProjections(basePath: string, manifest: StateManifest):
414
545
  if (manifest.artifacts === undefined) return;
415
546
 
416
547
  for (const artifact of manifest.artifacts) {
548
+ const content = artifact.full_content ?? "";
549
+ if (content === "") continue;
550
+
417
551
  atomicWriteSync(
418
552
  artifactProjectionPath(basePath, artifact.path),
419
- artifact.full_content ?? "",
553
+ content,
420
554
  );
421
555
  }
422
556
  }