@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
@@ -3,9 +3,10 @@
3
3
 
4
4
  import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
- import { mkdirSync, writeFileSync } from "node:fs";
7
- import { join } from "node:path";
6
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { delimiter, join } from "node:path";
8
8
 
9
+ import { GIT_NO_PROMPT_ENV } from "../git-constants.js";
9
10
  import { probeGitConflictState } from "../git-conflict-state.js";
10
11
  import { ensureWorkspaceGitReadyForPath } from "../workspace-git-preflight.js";
11
12
  import { isWorkspaceGitAllowedCommand } from "../workspace-git-guard.js";
@@ -50,6 +51,38 @@ function seedProductConflict(base: string): void {
50
51
  }
51
52
  }
52
53
 
54
+ function installCountingGitShim(binDir: string, logPath: string): void {
55
+ const posixShim = join(binDir, "git");
56
+ writeFileSync(
57
+ posixShim,
58
+ [
59
+ "#!/bin/sh",
60
+ 'printf "%s\\n" "$*" >> "$GSD_GIT_LOG"',
61
+ 'PATH="$GSD_REAL_PATH"',
62
+ "export PATH",
63
+ 'exec git "$@"',
64
+ "",
65
+ ].join("\n"),
66
+ );
67
+ chmodSync(posixShim, 0o755);
68
+
69
+ writeFileSync(
70
+ join(binDir, "git.cmd"),
71
+ [
72
+ "@echo off",
73
+ 'echo %*>>"%GSD_GIT_LOG%"',
74
+ 'set "PATH=%GSD_REAL_PATH%"',
75
+ "git %*",
76
+ "",
77
+ ].join("\r\n"),
78
+ );
79
+ }
80
+
81
+ function countGitShimInvocations(logPath: string): number {
82
+ if (!existsSync(logPath)) return 0;
83
+ return readFileSync(logPath, "utf-8").split(/\r?\n/).filter(Boolean).length;
84
+ }
85
+
53
86
  test("probeGitConflictState reports clean repo", () => {
54
87
  const base = makeTempRepo("gsd-ws-git-clean-");
55
88
  try {
@@ -60,6 +93,122 @@ test("probeGitConflictState reports clean repo", () => {
60
93
  }
61
94
  });
62
95
 
96
+ test("ensureWorkspaceGitReadyForPath caches clean target probes briefly", async () => {
97
+ const base = makeTempRepo("gsd-ws-git-clean-cache-");
98
+ const binDir = makeTempDir("gsd-ws-git-shim-");
99
+ const logPath = join(binDir, "git.log");
100
+ const originalProcessPath = process.env.PATH;
101
+ const originalEnvPath = GIT_NO_PROMPT_ENV.PATH;
102
+ const originalEnvGitLog = GIT_NO_PROMPT_ENV.GSD_GIT_LOG;
103
+ const originalEnvRealPath = GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
104
+
105
+ try {
106
+ installCountingGitShim(binDir, logPath);
107
+ const shimmedPath = `${binDir}${delimiter}${originalProcessPath ?? ""}`;
108
+ process.env.PATH = shimmedPath;
109
+ GIT_NO_PROMPT_ENV.PATH = shimmedPath;
110
+ GIT_NO_PROMPT_ENV.GSD_GIT_LOG = logPath;
111
+ GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalProcessPath ?? "";
112
+
113
+ const first = await ensureWorkspaceGitReadyForPath(base);
114
+ assert.equal(first.ok, true);
115
+ const firstCount = countGitShimInvocations(logPath);
116
+ assert.equal(firstCount, 3, "first clean probe should run the existing conflict checks");
117
+
118
+ const second = await ensureWorkspaceGitReadyForPath(base);
119
+ assert.equal(second.ok, true);
120
+ assert.equal(
121
+ countGitShimInvocations(logPath),
122
+ firstCount,
123
+ "second clean probe within the cache window must not spawn git again",
124
+ );
125
+ } finally {
126
+ if (originalProcessPath === undefined) delete process.env.PATH;
127
+ else process.env.PATH = originalProcessPath;
128
+ if (originalEnvPath === undefined) delete GIT_NO_PROMPT_ENV.PATH;
129
+ else GIT_NO_PROMPT_ENV.PATH = originalEnvPath;
130
+ if (originalEnvGitLog === undefined) delete GIT_NO_PROMPT_ENV.GSD_GIT_LOG;
131
+ else GIT_NO_PROMPT_ENV.GSD_GIT_LOG = originalEnvGitLog;
132
+ if (originalEnvRealPath === undefined) delete GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
133
+ else GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalEnvRealPath;
134
+ cleanup(binDir);
135
+ cleanup(base);
136
+ }
137
+ });
138
+
139
+ test("ensureWorkspaceGitReadyForPath detects merge state that appears after a clean probe", async () => {
140
+ const base = makeTempRepo("gsd-ws-git-cache-stale-");
141
+ const binDir = makeTempDir("gsd-ws-git-shim2-");
142
+ const logPath = join(binDir, "git2.log");
143
+ const originalProcessPath = process.env.PATH;
144
+ const originalEnvPath = GIT_NO_PROMPT_ENV.PATH;
145
+ const originalEnvGitLog = GIT_NO_PROMPT_ENV.GSD_GIT_LOG;
146
+ const originalEnvRealPath = GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
147
+
148
+ try {
149
+ installCountingGitShim(binDir, logPath);
150
+ const shimmedPath = `${binDir}${delimiter}${originalProcessPath ?? ""}`;
151
+ process.env.PATH = shimmedPath;
152
+ GIT_NO_PROMPT_ENV.PATH = shimmedPath;
153
+ GIT_NO_PROMPT_ENV.GSD_GIT_LOG = logPath;
154
+ GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalProcessPath ?? "";
155
+
156
+ // First call — repo is clean, cache is populated.
157
+ const first = await ensureWorkspaceGitReadyForPath(base);
158
+ assert.equal(first.ok, true);
159
+ const afterFirstCount = countGitShimInvocations(logPath);
160
+ assert.ok(afterFirstCount > 0, "first probe must have called git");
161
+
162
+ // Introduce MERGE_HEAD to simulate merge state appearing mid-TTL window.
163
+ writeFileSync(join(base, ".git", "MERGE_HEAD"), "0000000000000000000000000000000000000000\n");
164
+
165
+ // Second call — cache should be bypassed because merge state markers are present.
166
+ await ensureWorkspaceGitReadyForPath(base);
167
+ const afterSecondCount = countGitShimInvocations(logPath);
168
+ assert.ok(
169
+ afterSecondCount > afterFirstCount,
170
+ "cache must be invalidated when merge state appears, causing a fresh git probe",
171
+ );
172
+ } finally {
173
+ if (originalProcessPath === undefined) delete process.env.PATH;
174
+ else process.env.PATH = originalProcessPath;
175
+ if (originalEnvPath === undefined) delete GIT_NO_PROMPT_ENV.PATH;
176
+ else GIT_NO_PROMPT_ENV.PATH = originalEnvPath;
177
+ if (originalEnvGitLog === undefined) delete GIT_NO_PROMPT_ENV.GSD_GIT_LOG;
178
+ else GIT_NO_PROMPT_ENV.GSD_GIT_LOG = originalEnvGitLog;
179
+ if (originalEnvRealPath === undefined) delete GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
180
+ else GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalEnvRealPath;
181
+ cleanup(binDir);
182
+ cleanup(base);
183
+ }
184
+ });
185
+
186
+ test("ensureWorkspaceGitReadyForPath detects conflicts after a non-git folder becomes a repo", async () => {
187
+ const base = makeTempDir("gsd-ws-git-non-repo-cache-");
188
+ try {
189
+ const first = await ensureWorkspaceGitReadyForPath(base);
190
+ assert.equal(first.ok, true);
191
+
192
+ git(base, "init");
193
+ git(base, "config", "user.email", "test@test.com");
194
+ git(base, "config", "user.name", "Test");
195
+ git(base, "config", "core.autocrlf", "false");
196
+ writeFileSync(join(base, "README.md"), "# init\n");
197
+ git(base, "add", "-A");
198
+ git(base, "commit", "-m", "init");
199
+ git(base, "branch", "-M", "main");
200
+ seedProductConflict(base);
201
+
202
+ const second = await ensureWorkspaceGitReadyForPath(base);
203
+ assert.equal(second.ok, false);
204
+ if (second.ok) return;
205
+ assert.equal(second.severity, "product-conflicts");
206
+ assert.ok(second.conflictedPaths.includes("app.ts"));
207
+ } finally {
208
+ cleanup(base);
209
+ }
210
+ });
211
+
63
212
  test("ensureWorkspaceGitReadyForPath allows fresh non-git project setup folders", async () => {
64
213
  const base = makeTempDir("gsd-ws-git-non-repo-");
65
214
  try {
@@ -13,6 +13,7 @@ import {
13
13
  diffWorktreeNumstat,
14
14
  getWorktreeGSDDiff,
15
15
  getWorktreeLog,
16
+ mergeWorktreeToMain,
16
17
  worktreeBranchName,
17
18
  worktreePath,
18
19
  pruneEphemeralGhostWorktreeDirectories,
@@ -333,6 +334,44 @@ describe("getWorktreeLog", () => {
333
334
  });
334
335
  });
335
336
 
337
+ // ─── mergeWorktreeToMain ─────────────────────────────────────────────────────
338
+
339
+ describe("mergeWorktreeToMain", () => {
340
+ let base: string;
341
+ let wtPath: string;
342
+
343
+ beforeEach(() => {
344
+ base = makeBaseRepo();
345
+ wtPath = worktreePath(base, "M900");
346
+ mkdirSync(join(base, ".gsd-worktrees"), { recursive: true });
347
+ run(`git worktree add -b milestone/M900 "${wtPath}"`, base);
348
+ });
349
+
350
+ afterEach(() => { rmSync(base, { recursive: true, force: true }); });
351
+
352
+ test("cleans failed milestone squash state and deletes orphan branch", () => {
353
+ writeFileSync(join(wtPath, "README.md"), "# Milestone change\n", "utf-8");
354
+ run("git add README.md", wtPath);
355
+ run('git commit -m "feat: milestone change"', wtPath);
356
+
357
+ writeFileSync(join(base, "README.md"), "# Main change\n", "utf-8");
358
+ run("git add README.md", base);
359
+ run('git commit -m "feat: main change"', base);
360
+
361
+ assert.throws(
362
+ () => mergeWorktreeToMain(base, "M900", "feat: merge M900", "milestone/M900"),
363
+ /Merge conflicts detected/,
364
+ );
365
+
366
+ assert.equal(run("git status --porcelain", base), "", "failed squash cleanup should leave main clean");
367
+ assert.ok(!existsSync(wtPath), "failed milestone worktree should be removed");
368
+ assert.ok(
369
+ !run("git branch", base).includes("milestone/M900"),
370
+ "failed milestone branch should be deleted after worktree removal",
371
+ );
372
+ });
373
+ });
374
+
336
375
  // ─── removeWorktree ───────────────────────────────────────────────────────────
337
376
 
338
377
  describe("removeWorktree", () => {
@@ -23,7 +23,7 @@ import {
23
23
  } from "../gsd-db.js";
24
24
  import { gsdProjectionRoot, clearPathCache, relMilestoneFile } from "../paths.js";
25
25
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
26
- import { isClosedStatus } from "../status-guards.js";
26
+ import { isClosedStatus, isDeferredStatus } from "../status-guards.js";
27
27
  import { saveFile, clearParseCache } from "../files.js";
28
28
  import { invalidateStateCache } from "../state.js";
29
29
  import { stripIdPrefix } from "../workflow-projections.js";
@@ -180,7 +180,7 @@ export async function handleCompleteMilestone(
180
180
  return;
181
181
  }
182
182
 
183
- const incompleteSlices = slices.filter(s => !isClosedStatus(s.status));
183
+ const incompleteSlices = slices.filter(s => !isClosedStatus(s.status) && !isDeferredStatus(s.status));
184
184
  if (incompleteSlices.length > 0) {
185
185
  const incompleteIds = incompleteSlices.map(s => `${s.id} (status: ${s.status})`).join(", ");
186
186
  guardError = `incomplete slices: ${incompleteIds}`;
@@ -189,6 +189,7 @@ export async function handleCompleteMilestone(
189
189
 
190
190
  // Deep check: verify all tasks in all slices are complete
191
191
  for (const slice of slices) {
192
+ if (isDeferredStatus(slice.status)) continue;
192
193
  const tasks = getSliceTasks(params.milestoneId, slice.id);
193
194
  const incompleteTasks = tasks.filter(t => !isClosedStatus(t.status));
194
195
  if (incompleteTasks.length > 0) {
@@ -30,7 +30,7 @@ import { classifyUatContent, escalatesArtifactUatToBrowser } from "../uat-policy
30
30
  import { invalidateStateCache } from "../state.js";
31
31
  import { renderRoadmapFromDb, roadmapRenderMarksSliceDone } from "../markdown-renderer.js";
32
32
  import { isStaleWrite } from "../auto/turn-epoch.js";
33
- import { flushWorkflowProjections } from "../projection-flush.js";
33
+ import { renderMilestoneShellProjections } from "../workflow-projections.js";
34
34
  import { writeManifest } from "../workflow-manifest.js";
35
35
  import { appendEvent } from "../workflow-events.js";
36
36
  import { logWarning, logError } from "../workflow-logger.js";
@@ -540,7 +540,7 @@ export async function handleCompleteSlice(
540
540
  // Separate try/catch per step so a projection failure doesn't prevent
541
541
  // the event log entry (critical for worktree reconciliation).
542
542
  try {
543
- await flushWorkflowProjections(artifactBasePath, { milestoneId: params.milestoneId });
543
+ await renderMilestoneShellProjections(artifactBasePath, params.milestoneId);
544
544
  } catch (projErr) {
545
545
  logWarning("tool", `complete-slice projection warning for ${params.milestoneId}/${params.sliceId}: ${(projErr as Error).message}`);
546
546
  }
@@ -6,11 +6,12 @@
6
6
  *
7
7
  * Validates inputs, writes task row and rendered SUMMARY.md to DB in a
8
8
  * transaction, then renders projections to disk and invalidates caches.
9
- * Projection write failures are reported as stale projections and do not roll
10
- * back committed DB state.
9
+ * If the critical task summary / plan projection write fails, the DB
10
+ * completion is compensated back to pending so DB state does not drift ahead
11
+ * of PLAN.md.
11
12
  */
12
13
 
13
- import { existsSync } from "node:fs";
14
+ import { existsSync, unlinkSync } from "node:fs";
14
15
  import { join } from "node:path";
15
16
 
16
17
  import type { CompleteTaskParams, EscalationArtifact } from "../types.js";
@@ -29,6 +30,7 @@ import {
29
30
  saveGateResult,
30
31
  getPendingGatesForTurn,
31
32
  } from "../gsd-db.js";
33
+ import { getWorkflowDatabasePath, ensureWorkflowDbAtPath } from "../db-workspace.js";
32
34
  import { getGatesForTurn } from "../gate-registry.js";
33
35
  import { gsdProjectionRoot, clearPathCache, resolveMilestonePath, resolveSlicePath } from "../paths.js";
34
36
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
@@ -36,8 +38,10 @@ import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
36
38
  import { saveFile, clearParseCache } from "../files.js";
37
39
  import { invalidateStateCache } from "../state.js";
38
40
  import { renderPlanCheckboxes } from "../markdown-renderer.js";
39
- import { renderSummaryContent } from "../workflow-projections.js";
40
- import { flushWorkflowProjections } from "../projection-flush.js";
41
+ import {
42
+ renderMilestoneShellProjections,
43
+ renderSummaryContent,
44
+ } from "../workflow-projections.js";
41
45
  import { writeManifest } from "../workflow-manifest.js";
42
46
  import { appendEvent } from "../workflow-events.js";
43
47
  import { logWarning, logError } from "../workflow-logger.js";
@@ -131,7 +135,7 @@ async function repairMissingTaskSummaryProjection(
131
135
  clearParseCache();
132
136
 
133
137
  try {
134
- await flushWorkflowProjections(artifactBasePath, { milestoneId: taskRow.milestone_id });
138
+ await renderMilestoneShellProjections(artifactBasePath, taskRow.milestone_id);
135
139
  } catch (projErr) {
136
140
  logWarning("tool", `complete-task repair projection warning: ${(projErr as Error).message}`);
137
141
  }
@@ -267,6 +271,7 @@ export async function handleCompleteTask(
267
271
  let guardError: string | null = null;
268
272
  let summaryMd = "";
269
273
  let repairTaskSummaryRow: TaskRow | null = null;
274
+ const rollbackDbPath = getWorkflowDatabasePath();
270
275
 
271
276
  // ── ADR-011 Phase 2: validate escalation payload BEFORE any side effects ─
272
277
  // Building the artifact runs the full shape validation (2-4 options, unique
@@ -423,8 +428,6 @@ export async function handleCompleteTask(
423
428
  return { error: guardError };
424
429
  }
425
430
 
426
- let projectionStale = false;
427
-
428
431
  // Resolve and write summary to disk
429
432
  const summaryPath = taskSummaryPath(
430
433
  artifactBasePath,
@@ -438,12 +441,48 @@ export async function handleCompleteTask(
438
441
 
439
442
  // Toggle or regenerate the plan projection from DB. Missing projection
440
443
  // files are rebuilt by the renderer instead of being skipped.
441
- await renderPlanCheckboxes(artifactBasePath, params.milestoneId, params.sliceId);
444
+ if (!ensureWorkflowDbAtPath(rollbackDbPath)) {
445
+ throw new Error(`database unavailable before plan projection render for ${params.milestoneId}/${params.sliceId}`);
446
+ }
447
+ const wrotePlan = await renderPlanCheckboxes(artifactBasePath, params.milestoneId, params.sliceId);
448
+ if (!wrotePlan) {
449
+ throw new Error(`plan projection write returned false for ${params.milestoneId}/${params.sliceId}`);
450
+ }
442
451
  } catch (renderErr) {
443
- projectionStale = true;
444
- logWarning("projection", `complete_task projection write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}; DB completion remains committed`, {
445
- error: (renderErr as Error).message,
446
- });
452
+ logWarning(
453
+ "projection",
454
+ `complete_task projection write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}`,
455
+ { error: (renderErr as Error).message },
456
+ );
457
+ let rollbackSucceeded = false;
458
+ try {
459
+ ensureWorkflowDbAtPath(rollbackDbPath);
460
+ deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
461
+ updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, "pending");
462
+ invalidateStateCache();
463
+ rollbackSucceeded = true;
464
+ } catch (rollbackErr) {
465
+ logWarning(
466
+ "projection",
467
+ `complete_task rollback failed after projection write failure for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${(rollbackErr as Error).message}`,
468
+ );
469
+ }
470
+ try {
471
+ if (existsSync(summaryPath)) unlinkSync(summaryPath);
472
+ } catch (summaryErr) {
473
+ logWarning(
474
+ "projection",
475
+ `complete_task could not remove SUMMARY.md after projection write failure for ${params.milestoneId}/${params.sliceId}/${params.taskId}: ${(summaryErr as Error).message}`,
476
+ );
477
+ }
478
+ // Clear path/parse caches regardless of rollback outcome so stale
479
+ // entries from the failed write attempt don't leak into subsequent calls.
480
+ clearPathCache();
481
+ clearParseCache();
482
+ const returnMsg = rollbackSucceeded
483
+ ? `complete_task projection write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}; rolled completion back to pending`
484
+ : `complete_task projection write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}; rollback also failed — task may remain complete with stale plan`;
485
+ return { error: returnMsg };
447
486
  }
448
487
 
449
488
  // ── Close gates owned by execute-task (Q5/Q6/Q7) for this task ────────
@@ -569,7 +608,7 @@ export async function handleCompleteTask(
569
608
  // Separate try/catch per step so a projection failure doesn't prevent
570
609
  // the event log entry (critical for worktree reconciliation).
571
610
  try {
572
- await flushWorkflowProjections(artifactBasePath, { milestoneId: params.milestoneId });
611
+ await renderMilestoneShellProjections(artifactBasePath, params.milestoneId);
573
612
  } catch (projErr) {
574
613
  logWarning("tool", `complete-task projection warning: ${(projErr as Error).message}`);
575
614
  }
@@ -597,6 +636,5 @@ export async function handleCompleteTask(
597
636
  milestoneId: params.milestoneId,
598
637
  summaryPath,
599
638
  ...(escalationMetadata ? { escalation: escalationMetadata } : {}),
600
- ...(projectionStale ? { stale: true } : {}),
601
639
  };
602
640
  }
@@ -31,6 +31,7 @@ export interface ExecToolDeps {
31
31
  env?: NodeJS.ProcessEnv;
32
32
  now?: () => Date;
33
33
  generateId?: () => string;
34
+ signal?: AbortSignal;
34
35
  }
35
36
 
36
37
  export type UatExecIntent =
@@ -74,7 +75,7 @@ const UAT_EXEC_INTENT_ALIASES: Record<string, UatExecIntent> = {
74
75
  export function buildExecOptions(
75
76
  baseDir: string,
76
77
  cfg: ContextModeConfig | undefined,
77
- extras?: Pick<ExecSandboxOptions, "env" | "now" | "generateId">,
78
+ extras?: Pick<ExecSandboxOptions, "env" | "now" | "generateId" | "signal">,
78
79
  ): ExecSandboxOptions {
79
80
  const allowlist = Array.isArray(cfg?.exec_env_allowlist) ? cfg!.exec_env_allowlist! : EXEC_DEFAULTS.envAllowlist;
80
81
  const stdoutCap = clampNumber(
@@ -209,7 +210,7 @@ export async function executeGsdExec(
209
210
  const opts = buildExecOptions(
210
211
  deps.baseDir,
211
212
  deps.preferences?.context_mode,
212
- { env: deps.env, now: deps.now, generateId: deps.generateId },
213
+ { env: deps.env, now: deps.now, generateId: deps.generateId, signal: deps.signal },
213
214
  );
214
215
  const run = deps.run ?? runExecSandbox;
215
216
 
@@ -308,6 +309,7 @@ function formatResult(result: ExecSandboxResult): ToolExecutionResult {
308
309
  exit_code: result.exit_code,
309
310
  signal: result.signal,
310
311
  timed_out: result.timed_out,
312
+ aborted: result.aborted === true,
311
313
  force_resolved: result.force_resolved,
312
314
  duration_ms: result.duration_ms,
313
315
  stdout_bytes: result.stdout_bytes,
@@ -318,13 +320,15 @@ function formatResult(result: ExecSandboxResult): ToolExecutionResult {
318
320
  stderr_path: result.stderr_path,
319
321
  meta_path: result.meta_path,
320
322
  },
321
- isError: result.timed_out || result.signal !== null || result.exit_code !== 0,
323
+ isError: result.aborted === true || result.timed_out || result.signal !== null || result.exit_code !== 0,
322
324
  };
323
325
  }
324
326
 
325
327
  function formatExit(result: ExecSandboxResult): string {
326
328
  // force_resolved means a non-closing (D-state) child was force-resolved past its
327
329
  // hard deadline rather than observed exiting; distinguish it from a clean timeout.
330
+ if (result.aborted && result.force_resolved) return "aborted(force-killed)";
331
+ if (result.aborted) return "aborted";
328
332
  if (result.force_resolved) return "timeout(force-killed)";
329
333
  if (result.timed_out) return "timeout";
330
334
  if (result.signal) return `signal:${result.signal}`;
@@ -51,7 +51,7 @@ export interface PlanSliceParams {
51
51
  milestoneId: string;
52
52
  sliceId: string;
53
53
  goal: string;
54
- tasks: PlanSliceTaskInput[];
54
+ tasks?: PlanSliceTaskInput[];
55
55
  /** @optional — omitted fields render as conservative defaults */
56
56
  successCriteria?: string;
57
57
  /** @optional — omitted fields render as conservative defaults */
@@ -86,10 +86,12 @@ function validateRepositoryTargetIds(
86
86
  return deduped;
87
87
  }
88
88
 
89
- function validateTasks(value: unknown): PlanSliceTaskInput[] {
90
- if (!Array.isArray(value) || value.length === 0) {
91
- throw new Error("tasks must be a non-empty array");
89
+ function validateTasks(value: unknown): PlanSliceTaskInput[] | undefined {
90
+ if (value === undefined) return undefined;
91
+ if (!Array.isArray(value)) {
92
+ throw new Error("tasks must be an array");
92
93
  }
94
+ if (value.length === 0) return undefined;
93
95
 
94
96
  const seen = new Set<string>();
95
97
  return value.map((entry, index) => {
@@ -200,7 +202,7 @@ function validateReferencedRepositories(
200
202
  };
201
203
 
202
204
  for (const id of params.targetRepositories ?? defaultTargets) noteMissing(id);
203
- for (const task of params.tasks) {
205
+ for (const task of params.tasks ?? []) {
204
206
  for (const id of task.targetRepositories ?? params.targetRepositories ?? defaultTargets) noteMissing(id);
205
207
  }
206
208
 
@@ -211,7 +213,7 @@ function validateReferencedRepositories(
211
213
  function resolveAllowedRootsForPathScope(params: PlanSliceParams, registry: RepositoryRegistry, defaultTargets: string[]): string[] {
212
214
  const requested = new Set<string>();
213
215
  for (const id of params.targetRepositories ?? defaultTargets) requested.add(id);
214
- for (const task of params.tasks) {
216
+ for (const task of params.tasks ?? []) {
215
217
  for (const id of task.targetRepositories ?? params.targetRepositories ?? defaultTargets) requested.add(id);
216
218
  }
217
219
  if (requested.size === 0) return [registry.projectRoot];
@@ -222,7 +224,7 @@ function resolveAllowedRootsForPathScope(params: PlanSliceParams, registry: Repo
222
224
  }
223
225
 
224
226
  function toTaskRows(params: PlanSliceParams, defaultTargets: string[]): TaskRow[] {
225
- return params.tasks.map((task, index) => ({
227
+ return (params.tasks ?? []).map((task, index) => ({
226
228
  milestone_id: params.milestoneId,
227
229
  slice_id: params.sliceId,
228
230
  id: task.taskId,
@@ -309,6 +311,8 @@ export async function handlePlanSlice(
309
311
  return { error: `validation failed: ${message}` };
310
312
  }
311
313
  const defaultTargets = defaultRepositoryTargets(repositoryRegistry);
314
+ const taskPayload = params.tasks ?? [];
315
+ const hasTaskPayload = taskPayload.length > 0;
312
316
  const repoValidationError = validateReferencedRepositories(params, repositoryRegistry, defaultTargets);
313
317
  if (repoValidationError) {
314
318
  return { error: `validation failed: ${repoValidationError}` };
@@ -317,7 +321,7 @@ export async function handlePlanSlice(
317
321
  const allowedAbsoluteRoots = resolveAllowedRootsForPathScope(params, repositoryRegistry, defaultTargets);
318
322
 
319
323
  const pathOnlyError = validatePathOnlyPlanningFields(
320
- params.tasks.map((task, index) => ({
324
+ taskPayload.map((task, index) => ({
321
325
  field: `tasks[${index}].expectedOutput`,
322
326
  values: task.expectedOutput,
323
327
  })),
@@ -328,7 +332,7 @@ export async function handlePlanSlice(
328
332
 
329
333
  const pathScopeError = validatePlanningPathScope(
330
334
  basePath,
331
- params.tasks.flatMap((task, index) => [
335
+ taskPayload.flatMap((task, index) => [
332
336
  { field: `tasks[${index}].files`, values: task.files },
333
337
  { field: `tasks[${index}].inputs`, values: task.inputs },
334
338
  { field: `tasks[${index}].expectedOutput`, values: task.expectedOutput },
@@ -372,16 +376,18 @@ export async function handlePlanSlice(
372
376
  return;
373
377
  }
374
378
 
375
- const newTaskIds = new Set(params.tasks.map((task) => task.taskId));
379
+ const newTaskIds = new Set(taskPayload.map((task) => task.taskId));
376
380
  const existingTasks = getSliceTasks(params.milestoneId, params.sliceId);
377
- omittedTaskIds = existingTasks
378
- .filter((task) => !newTaskIds.has(task.id))
379
- .map((task) => task.id);
380
-
381
- for (const task of existingTasks) {
382
- if (!newTaskIds.has(task.id) && isClosedStatus(task.status)) {
383
- guardError = `cannot remove completed task ${task.id}`;
384
- return;
381
+ if (hasTaskPayload) {
382
+ omittedTaskIds = existingTasks
383
+ .filter((task) => !newTaskIds.has(task.id))
384
+ .map((task) => task.id);
385
+
386
+ for (const task of existingTasks) {
387
+ if (!newTaskIds.has(task.id) && isClosedStatus(task.status)) {
388
+ guardError = `cannot remove completed task ${task.id}`;
389
+ return;
390
+ }
385
391
  }
386
392
  }
387
393
 
@@ -398,35 +404,37 @@ export async function handlePlanSlice(
398
404
  targetRepositories: params.targetRepositories ?? defaultTargets,
399
405
  });
400
406
 
401
- for (const taskId of omittedTaskIds) {
402
- deleteTask(params.milestoneId, params.sliceId, taskId);
403
- }
407
+ if (hasTaskPayload) {
408
+ for (const taskId of omittedTaskIds) {
409
+ deleteTask(params.milestoneId, params.sliceId, taskId);
410
+ }
404
411
 
405
- const existingTaskById = new Map(existingTasks.map((task) => [task.id, task]));
406
- for (const task of params.tasks) {
407
- const planning = {
408
- title: task.title,
409
- description: task.description,
410
- estimate: task.estimate,
411
- files: task.files,
412
- verify: task.verify,
413
- inputs: task.inputs,
414
- expectedOutput: task.expectedOutput,
415
- observabilityImpact: task.observabilityImpact ?? "",
416
- fullPlanMd: task.fullPlanMd,
417
- targetRepositories: task.targetRepositories ?? params.targetRepositories ?? defaultTargets,
418
- };
419
- const existingTask = existingTaskById.get(task.taskId);
420
- if (!existingTask || !isClosedStatus(existingTask.status)) {
421
- insertTask({
422
- id: task.taskId,
423
- sliceId: params.sliceId,
424
- milestoneId: params.milestoneId,
412
+ const existingTaskById = new Map(existingTasks.map((task) => [task.id, task]));
413
+ for (const task of taskPayload) {
414
+ const planning = {
425
415
  title: task.title,
426
- status: "pending",
427
- });
416
+ description: task.description,
417
+ estimate: task.estimate,
418
+ files: task.files,
419
+ verify: task.verify,
420
+ inputs: task.inputs,
421
+ expectedOutput: task.expectedOutput,
422
+ observabilityImpact: task.observabilityImpact ?? "",
423
+ fullPlanMd: task.fullPlanMd,
424
+ targetRepositories: task.targetRepositories ?? params.targetRepositories ?? defaultTargets,
425
+ };
426
+ const existingTask = existingTaskById.get(task.taskId);
427
+ if (!existingTask || !isClosedStatus(existingTask.status)) {
428
+ insertTask({
429
+ id: task.taskId,
430
+ sliceId: params.sliceId,
431
+ milestoneId: params.milestoneId,
432
+ title: task.title,
433
+ status: "pending",
434
+ });
435
+ }
436
+ upsertTaskPlanning(params.milestoneId, params.sliceId, task.taskId, planning);
428
437
  }
429
- upsertTaskPlanning(params.milestoneId, params.sliceId, task.taskId, planning);
430
438
  }
431
439
 
432
440
  // Seed quality gate rows inside the transaction — all-or-nothing with
@@ -436,7 +444,7 @@ export async function handlePlanSlice(
436
444
  insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "slice" });
437
445
  }
438
446
  const taskGates = resolveTaskGates(gateEvaluation);
439
- for (const task of params.tasks) {
447
+ for (const task of taskPayload) {
440
448
  for (const gid of taskGates) {
441
449
  insertGateRow({ milestoneId: params.milestoneId, sliceId: params.sliceId, gateId: gid, scope: "task", taskId: task.taskId });
442
450
  }
@@ -460,8 +468,13 @@ export async function handlePlanSlice(
460
468
  deleteArtifactByPath(artifactPath);
461
469
  }
462
470
 
463
- const renderResult = await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
464
- setSliceSketchFlag(params.milestoneId, params.sliceId, false);
471
+ const sliceTasks = getSliceTasks(params.milestoneId, params.sliceId);
472
+ const renderResult = sliceTasks.length === 0
473
+ ? { planPath: "", taskPlanPaths: [] as string[] }
474
+ : await renderPlanFromDb(basePath, params.milestoneId, params.sliceId);
475
+ if (sliceTasks.length > 0) {
476
+ setSliceSketchFlag(params.milestoneId, params.sliceId, false);
477
+ }
465
478
  invalidateStateCache();
466
479
  clearParseCache();
467
480