@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,12 +1,22 @@
1
1
  import { afterEach, test } from "node:test";
2
2
  import assert from "node:assert/strict";
3
- import { _getAdapter, closeDatabase, insertMilestone, openDatabase } from "../gsd-db.ts";
4
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import {
4
+ _getAdapter,
5
+ closeDatabase,
6
+ insertArtifact,
7
+ insertMilestone,
8
+ insertSlice,
9
+ insertTask,
10
+ openDatabase,
11
+ } from "../gsd-db.ts";
12
+ import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
5
13
  import { join } from "node:path";
6
14
  import { tmpdir } from "node:os";
7
15
  import { filterDoctorIssues } from "../doctor-format.ts";
8
16
  import { checkEngineHealth } from "../doctor-engine-checks.ts";
17
+ import { MEMORIES_FTS_REBUILT_KEY } from "../db-memory-fts-schema.ts";
9
18
  import { appendEvent } from "../workflow-events.ts";
19
+ import { renderPlanFromDb, renderRoadmapFromDb } from "../markdown-renderer.ts";
10
20
 
11
21
  afterEach(() => {
12
22
  closeDatabase();
@@ -61,6 +71,139 @@ test("checkEngineHealth reports db_unavailable when gsd.db exists but the DB is
61
71
  assert.equal(dbIssue.file, ".gsd/gsd.db");
62
72
  });
63
73
 
74
+ test("checkEngineHealth reports memories_fts without the rebuild marker", async (t) => {
75
+ const base = mkdtempSync(join(tmpdir(), "gsd-doctor-memory-fts-marker-"));
76
+ t.after(() => rmSync(base, { recursive: true, force: true }));
77
+
78
+ const gsdDir = join(base, ".gsd");
79
+ mkdirSync(gsdDir, { recursive: true });
80
+
81
+ openDatabase(join(gsdDir, "gsd.db"));
82
+ const adapter = _getAdapter()!;
83
+ const fts = adapter.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memories_fts'").get();
84
+ if (!fts) return;
85
+
86
+ adapter.prepare(
87
+ "DELETE FROM runtime_kv WHERE scope = 'global' AND scope_id = '' AND key = :key",
88
+ ).run({ ":key": MEMORIES_FTS_REBUILT_KEY });
89
+
90
+ const issues: any[] = [];
91
+ await checkEngineHealth(base, issues, []);
92
+
93
+ const ftsIssue = issues.find((issue) => issue.code === "memories_fts_rebuild_missing");
94
+ assert.ok(ftsIssue, "doctor should surface a potentially desynced memory FTS index");
95
+ assert.equal(ftsIssue.severity, "warning");
96
+ assert.equal(ftsIssue.unitId, "project");
97
+ assert.equal(ftsIssue.file, ".gsd/gsd.db");
98
+ assert.equal(ftsIssue.fixable, false);
99
+ });
100
+
101
+ test("checkEngineHealth reports checkbox divergence against DB status", async (t) => {
102
+ const base = mkdtempSync(join(tmpdir(), "gsd-doctor-checkbox-drift-"));
103
+ t.after(() => rmSync(base, { recursive: true, force: true }));
104
+
105
+ const gsdDir = join(base, ".gsd");
106
+ mkdirSync(gsdDir, { recursive: true });
107
+
108
+ openDatabase(join(gsdDir, "gsd.db"));
109
+ insertMilestone({ id: "M001", title: "Foundation", status: "active" });
110
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending", risk: "low", depends: [], sequence: 1 });
111
+ insertTask({ id: "T01", milestoneId: "M001", sliceId: "S01", title: "Task", status: "complete", sequence: 1 });
112
+
113
+ const roadmap = await renderRoadmapFromDb(base, "M001");
114
+ if ("skipped" in roadmap) assert.fail("planned milestone should render a roadmap");
115
+ const plan = await renderPlanFromDb(base, "M001", "S01");
116
+
117
+ writeFileSync(roadmap.roadmapPath, readFileSync(roadmap.roadmapPath, "utf-8").replace("- [ ] **S01:", "- [x] **S01:"), "utf-8");
118
+ writeFileSync(plan.planPath, readFileSync(plan.planPath, "utf-8").replace("- [x] **T01**:", "- [ ] **T01**:"), "utf-8");
119
+
120
+ const issues: any[] = [];
121
+ await checkEngineHealth(base, issues, []);
122
+
123
+ const divergences = issues.filter((issue) => issue.code === "checkbox_db_status_divergence");
124
+ assert.deepEqual(
125
+ divergences.map((issue) => issue.unitId).sort(),
126
+ ["M001/S01", "M001/S01/T01"],
127
+ );
128
+ });
129
+
130
+ test("checkEngineHealth keeps PLAN checkbox divergence after stale projection flush", async (t) => {
131
+ const base = mkdtempSync(join(tmpdir(), "gsd-doctor-checkbox-plan-drift-"));
132
+ t.after(() => rmSync(base, { recursive: true, force: true }));
133
+
134
+ const gsdDir = join(base, ".gsd");
135
+ mkdirSync(gsdDir, { recursive: true });
136
+
137
+ openDatabase(join(gsdDir, "gsd.db"));
138
+ insertMilestone({ id: "M001", title: "Foundation", status: "active" });
139
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending", risk: "low", depends: [], sequence: 1 });
140
+ insertTask({ id: "T01", milestoneId: "M001", sliceId: "S01", title: "Task", status: "complete", sequence: 1 });
141
+
142
+ const roadmap = await renderRoadmapFromDb(base, "M001");
143
+ if ("skipped" in roadmap) assert.fail("planned milestone should render a roadmap");
144
+ const plan = await renderPlanFromDb(base, "M001", "S01");
145
+
146
+ writeFileSync(roadmap.roadmapPath, readFileSync(roadmap.roadmapPath, "utf-8").replace("- [ ] **S01:", "- [x] **S01:"), "utf-8");
147
+ writeFileSync(plan.planPath, readFileSync(plan.planPath, "utf-8").replace("- [x] **T01**:", "- [ ] **T01**:"), "utf-8");
148
+ appendEvent(base, {
149
+ cmd: "complete-task",
150
+ params: { milestoneId: "M001", sliceId: "S01", taskId: "T01" },
151
+ ts: "2999-01-01T00:00:00.000Z",
152
+ actor: "agent",
153
+ });
154
+
155
+ const issues: any[] = [];
156
+ const fixes: string[] = [];
157
+ await checkEngineHealth(base, issues, fixes);
158
+
159
+ const divergences = issues.filter((issue) => issue.code === "checkbox_db_status_divergence");
160
+ assert.deepEqual(
161
+ divergences.map((issue) => issue.unitId),
162
+ ["M001/S01/T01"],
163
+ "stale ROADMAP divergence is cleared after re-render, but stale PLAN task divergence remains",
164
+ );
165
+ assert.ok(fixes.includes("re-rendered stale projections for M001"));
166
+ assert.match(readFileSync(plan.planPath, "utf-8"), /- \[ \] \*\*T01\*\*:/);
167
+ });
168
+
169
+ test("checkEngineHealth reads task checkboxes from the <tasks> block, not a stray line above it", async (t) => {
170
+ const base = mkdtempSync(join(tmpdir(), "gsd-doctor-task-section-"));
171
+ t.after(() => rmSync(base, { recursive: true, force: true }));
172
+
173
+ const gsdDir = join(base, ".gsd");
174
+ mkdirSync(gsdDir, { recursive: true });
175
+
176
+ openDatabase(join(gsdDir, "gsd.db"));
177
+ insertMilestone({ id: "M001", title: "Foundation", status: "active" });
178
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending", risk: "low", depends: [], sequence: 1 });
179
+ insertTask({ id: "T01", milestoneId: "M001", sliceId: "S01", title: "Task", status: "complete", sequence: 1 });
180
+
181
+ const roadmap = await renderRoadmapFromDb(base, "M001");
182
+ if ("skipped" in roadmap) assert.fail("planned milestone should render a roadmap");
183
+ const plan = await renderPlanFromDb(base, "M001", "S01");
184
+
185
+ // Flip the authoritative <tasks> checkbox so it disagrees with the DB
186
+ // (DB: complete, markdown: unchecked = real drift), then inject a *checked*
187
+ // decoy line for the same task above the <tasks> block. The old whole-file
188
+ // regex matched the decoy first and hid the real drift; parsePlan reads the
189
+ // authoritative <tasks> block and still reports it. No events are appended,
190
+ // so the projection-drift re-render never runs and the crafted file stands.
191
+ const rendered = readFileSync(plan.planPath, "utf-8")
192
+ .replace("- [x] **T01**:", "- [ ] **T01**:")
193
+ .replace("<tasks>\n", "- [x] **T01**: stale duplicate above the tasks block\n\n<tasks>\n");
194
+ writeFileSync(plan.planPath, rendered, "utf-8");
195
+
196
+ const issues: any[] = [];
197
+ await checkEngineHealth(base, issues, []);
198
+
199
+ const divergences = issues.filter((issue) => issue.code === "checkbox_db_status_divergence");
200
+ assert.deepEqual(
201
+ divergences.map((issue) => issue.unitId),
202
+ ["M001/S01/T01"],
203
+ "task drift must be read from the authoritative <tasks> block, not a stray checkbox above it",
204
+ );
205
+ });
206
+
64
207
  test("checkEngineHealth reads canonical reopen events from worktree bases", async (t) => {
65
208
  const base = mkdtempSync(join(tmpdir(), "gsd-doctor-reopen-worktree-"));
66
209
  t.after(() => rmSync(base, { recursive: true, force: true }));
@@ -149,3 +292,97 @@ test("checkEngineHealth treats explicit reopen as authoritative when dispatch ti
149
292
  "explicit reopen should exempt reopened milestone even when completion dispatch timestamps are absent",
150
293
  );
151
294
  });
295
+
296
+ test("checkEngineHealth reports artifact rows whose files are missing on disk", async (t) => {
297
+ const base = mkdtempSync(join(tmpdir(), "gsd-doctor-missing-artifact-"));
298
+ t.after(() => rmSync(base, { recursive: true, force: true }));
299
+
300
+ const gsdDir = join(base, ".gsd");
301
+ const worktree = join(gsdDir, "worktrees", "M001");
302
+ mkdirSync(join(worktree, ".gsd", "milestones", "M001"), { recursive: true });
303
+ writeFileSync(join(worktree, ".git"), "gitdir: ../../../../.git/worktrees/M001\n", "utf-8");
304
+ writeFileSync(join(worktree, ".gsd", "milestones", "M001", "M001-CONTEXT.md"), "# Context\n", "utf-8");
305
+
306
+ openDatabase(join(gsdDir, "gsd.db"));
307
+ insertArtifact({
308
+ path: "milestones/M001/M001-CONTEXT.md",
309
+ artifact_type: "CONTEXT",
310
+ milestone_id: "M001",
311
+ slice_id: null,
312
+ task_id: null,
313
+ full_content: "# Context\n",
314
+ });
315
+ insertArtifact({
316
+ path: "milestones/M001/M001-MISSING.md",
317
+ artifact_type: "CONTEXT",
318
+ milestone_id: "M001",
319
+ slice_id: null,
320
+ task_id: null,
321
+ full_content: "# Missing\n",
322
+ });
323
+
324
+ const issues: any[] = [];
325
+ await checkEngineHealth(worktree, issues, []);
326
+
327
+ assert.equal(
328
+ issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === "milestones/M001/M001-CONTEXT.md"),
329
+ false,
330
+ "worktree-local artifacts should resolve through the projection root",
331
+ );
332
+ const missing = issues.find((issue) => issue.code === "artifact_file_missing" && issue.file === "milestones/M001/M001-MISSING.md");
333
+ assert.ok(missing, "missing artifact rows should be reported");
334
+ assert.equal(missing.unitId, "M001");
335
+ assert.equal(missing.fixable, false);
336
+ });
337
+
338
+ test("checkEngineHealth clears artifact_file_missing after projection re-render recreates the file", async (t) => {
339
+ const base = mkdtempSync(join(tmpdir(), "gsd-doctor-stale-missing-artifact-"));
340
+ t.after(() => rmSync(base, { recursive: true, force: true }));
341
+
342
+ const gsdDir = join(base, ".gsd");
343
+ mkdirSync(gsdDir, { recursive: true });
344
+
345
+ openDatabase(join(gsdDir, "gsd.db"));
346
+ insertMilestone({ id: "M001", title: "Foundation", status: "active", planning: { vision: "Ship the foundation." } });
347
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending", risk: "low", depends: [], sequence: 1 });
348
+ insertArtifact({
349
+ path: "phases/01-foundation/01-ROADMAP.md",
350
+ artifact_type: "ROADMAP",
351
+ milestone_id: "M001",
352
+ slice_id: null,
353
+ task_id: null,
354
+ full_content: "# stale row only\n",
355
+ });
356
+ // CONTEXT is not re-rendered by flushWorkflowProjections, so its missing-file
357
+ // diagnostic must survive the post-re-render cleanup (guards against the
358
+ // clearing logic over-broadening to artifacts the same run did not recreate).
359
+ insertArtifact({
360
+ path: "phases/01-foundation/01-CONTEXT.md",
361
+ artifact_type: "CONTEXT",
362
+ milestone_id: "M001",
363
+ slice_id: null,
364
+ task_id: null,
365
+ full_content: "# stale context row only\n",
366
+ });
367
+ appendEvent(base, {
368
+ cmd: "plan-milestone",
369
+ params: { milestoneId: "M001" },
370
+ ts: "2999-01-01T00:00:00.000Z",
371
+ actor: "agent",
372
+ });
373
+
374
+ const issues: any[] = [];
375
+ const fixes: string[] = [];
376
+ await checkEngineHealth(base, issues, fixes);
377
+
378
+ assert.ok(fixes.includes("re-rendered missing projections for M001"));
379
+ assert.equal(
380
+ issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === "phases/01-foundation/01-ROADMAP.md"),
381
+ false,
382
+ "doctor should not report a missing artifact that projection repair recreated in the same run",
383
+ );
384
+ assert.ok(
385
+ issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === "phases/01-foundation/01-CONTEXT.md"),
386
+ "doctor should still report a missing artifact that projection repair did not recreate",
387
+ );
388
+ });
@@ -63,6 +63,44 @@ function wallClockGuard<T>(promise: Promise<T>, ms: number): Promise<T> {
63
63
  ]);
64
64
  }
65
65
 
66
+ test(
67
+ 'runExecSandbox: aborted signal terminates promptly without waiting for timeout',
68
+ { timeout: 10_000 },
69
+ async (t) => {
70
+ const base = freshBase();
71
+ t.after(() => cleanup(base));
72
+
73
+ const controller = new AbortController();
74
+ controller.abort();
75
+ const TIMEOUT_MS = 10_000;
76
+
77
+ const start = Date.now();
78
+ const result = await wallClockGuard(
79
+ runExecSandbox(
80
+ { runtime: 'node', script: 'setTimeout(() => {}, 30_000);', timeout_ms: TIMEOUT_MS },
81
+ baseOpts(base, {
82
+ signal: controller.signal,
83
+ kill_grace_ms: 500,
84
+ force_resolve_delay_ms: 2_000,
85
+ }),
86
+ ),
87
+ 5_000,
88
+ );
89
+ const elapsed = Date.now() - start;
90
+
91
+ assert.equal(result.aborted, true, 'aborted signal must be recorded on the sandbox result');
92
+ assert.equal(result.timed_out, false, 'client abort must not be reported as a sandbox timeout');
93
+ assert.ok(elapsed < TIMEOUT_MS, `expected abort before ${TIMEOUT_MS}ms timeout, took ${elapsed}ms`);
94
+ assert.ok(
95
+ result.duration_ms < TIMEOUT_MS,
96
+ `expected result duration before timeout, got ${result.duration_ms}ms`,
97
+ );
98
+ const meta = JSON.parse(readFileSync(result.meta_path, 'utf-8'));
99
+ assert.equal(meta.aborted, true, 'meta.json must persist the aborted marker');
100
+ assert.equal(meta.timed_out, false, 'meta.json must not report timed_out when abort caused the kill');
101
+ },
102
+ );
103
+
66
104
  // Clean up a detached child (and its process group) that a large kill_grace_ms
67
105
  // intentionally leaves alive past the force-resolve deadline.
68
106
  function cleanupByPidFile(pidFile: string): void {
@@ -7,6 +7,7 @@ import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync } from 'node:f
7
7
  import { tmpdir } from 'node:os';
8
8
  import { join } from 'node:path';
9
9
 
10
+ import { getShellConfig } from '@gsd/pi-coding-agent';
10
11
  import { EXEC_DEFAULTS, runExecSandbox, type ExecSandboxOptions } from '../exec-sandbox.ts';
11
12
  import { buildExecOptions, executeGsdExec } from '../tools/exec-tool.ts';
12
13
  import { isContextModeEnabled } from '../preferences-types.ts';
@@ -53,6 +54,21 @@ test('runExecSandbox: captures stdout, persists artifacts, returns digest', asyn
53
54
  }
54
55
  });
55
56
 
57
+ test('runExecSandbox: bash runtime uses resolved shell config', async () => {
58
+ const base = freshBase();
59
+ try {
60
+ const { shell } = getShellConfig();
61
+ const result = await runExecSandbox(
62
+ { runtime: 'bash', script: 'printf "%s" "$0"' },
63
+ baseOpts(base),
64
+ );
65
+ assert.equal(result.exit_code, 0);
66
+ assert.equal(readFileSync(result.stdout_path, 'utf-8'), shell);
67
+ } finally {
68
+ cleanup(base);
69
+ }
70
+ });
71
+
56
72
  test('runExecSandbox: persists optional request metadata', async () => {
57
73
  const base = freshBase();
58
74
  try {
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
3
3
 
4
4
  import { registerExecTools } from "../bootstrap/exec-tools.ts";
5
5
  import { executeGsdExec, executeUatExec } from "../tools/exec-tool.ts";
6
- import type { ExecSandboxRequest, ExecSandboxResult } from "../exec-sandbox.ts";
6
+ import type { ExecSandboxOptions, ExecSandboxRequest, ExecSandboxResult } from "../exec-sandbox.ts";
7
7
 
8
8
  function makeExecResult(request: ExecSandboxRequest): ExecSandboxResult {
9
9
  return {
@@ -12,6 +12,7 @@ function makeExecResult(request: ExecSandboxRequest): ExecSandboxResult {
12
12
  exit_code: 0,
13
13
  signal: null,
14
14
  timed_out: false,
15
+ aborted: false,
15
16
  force_resolved: false,
16
17
  duration_ms: 1,
17
18
  stdout_bytes: 12,
@@ -52,6 +53,49 @@ test("executeUatExec accepts evidence-mode aliases for intent", async () => {
52
53
  assert.equal(requests[0]?.metadata?.intent, "uat-artifact-check");
53
54
  });
54
55
 
56
+ test("executeGsdExec passes AbortSignal into sandbox options", async () => {
57
+ const controller = new AbortController();
58
+ let capturedSignal: AbortSignal | undefined;
59
+
60
+ const result = await executeGsdExec(
61
+ { runtime: "bash", script: "sleep 60" },
62
+ {
63
+ baseDir: "/tmp/gsd-exec-abort-signal-test",
64
+ preferences: null,
65
+ signal: controller.signal,
66
+ run: async (request, opts: ExecSandboxOptions) => {
67
+ capturedSignal = opts.signal;
68
+ return makeExecResult(request);
69
+ },
70
+ },
71
+ );
72
+
73
+ assert.equal(result.isError, false);
74
+ assert.equal(capturedSignal, controller.signal);
75
+ });
76
+
77
+ test("gsd_exec surfaces aborted child termination distinctly from a clean exit", async () => {
78
+ const result = await executeGsdExec(
79
+ { runtime: "bash", script: "trap 'exit 0' TERM; sleep 60" },
80
+ {
81
+ baseDir: "/tmp/gsd-exec-aborted-test",
82
+ preferences: null,
83
+ run: async (request) => ({
84
+ ...makeExecResult(request),
85
+ aborted: true,
86
+ exit_code: 0,
87
+ signal: null,
88
+ digest: "[no stdout — aborted]",
89
+ }),
90
+ },
91
+ );
92
+
93
+ assert.equal(result.isError, true, "an aborted run must be an error even if the child exits 0");
94
+ assert.equal(result.details?.aborted, true, "details must expose aborted");
95
+ const text = result.content.map((c) => c.text ?? "").join("\n");
96
+ assert.match(text, /exit=aborted/, "summary must distinguish an aborted result");
97
+ });
98
+
55
99
  test("gsd_exec surfaces a force-resolved (D-state) kill distinctly from a clean exit", async () => {
56
100
  // A hard-deadline force-resolve sets force_resolved=true with a synthetic SIGKILL
57
101
  // signal and null exit code. The tool result must carry that flag in details and
@@ -116,6 +116,46 @@ test("effectiveFileChangeAllowlist keeps .gitignore auditable when management is
116
116
  assert.deepEqual(effectiveFileChangeAllowlist(["docs/**"], false), ["docs/**"]);
117
117
  });
118
118
 
119
+ test("validateFileChanges excludes .gsd-backups/ migration snapshots from unexpected-change warnings", (t) => {
120
+ const base = mkdtempSync(join(tmpdir(), "gsd-file-change-validator-"));
121
+ t.after(() => rmSync(base, { recursive: true, force: true }));
122
+
123
+ const backupFile = join(
124
+ base,
125
+ ".gsd-backups",
126
+ "migrate-1782703701330",
127
+ "M010",
128
+ "slices",
129
+ "S01",
130
+ "S01-ASSESSMENT.md",
131
+ );
132
+ mkdirSync(join(base, "src"), { recursive: true });
133
+ mkdirSync(join(backupFile, ".."), { recursive: true });
134
+
135
+ git(base, "init");
136
+ git(base, "config", "user.email", "test@example.com");
137
+ git(base, "config", "user.name", "Test User");
138
+
139
+ writeFileSync(join(base, "src", "app.ts"), "initial\n");
140
+ writeFileSync(backupFile, "legacy assessment\n");
141
+ git(base, "add", ".");
142
+ git(base, "commit", "-m", "initial");
143
+
144
+ writeFileSync(join(base, "src", "app.ts"), "updated\n");
145
+ writeFileSync(backupFile, "legacy assessment touched\n");
146
+ git(base, "add", ".");
147
+ git(base, "commit", "-m", "task commit");
148
+
149
+ const audit = validateFileChanges(base, ["src/app.ts"], []);
150
+ assert.ok(audit, "audit should be produced");
151
+ assert.deepEqual(audit!.unexpectedFiles, [], ".gsd-backups/ must not trigger warnings");
152
+ assert.equal(
153
+ audit!.violations.filter(v => v.severity === "warning").length,
154
+ 0,
155
+ "no warnings when only source and migration backup files changed",
156
+ );
157
+ });
158
+
119
159
  test("GSD-managed .gitignore edit swept into a task commit is not flagged", (t) => {
120
160
  const base = mkdtempSync(join(tmpdir(), "gsd-file-change-validator-"));
121
161
  t.after(() => rmSync(base, { recursive: true, force: true }));
@@ -2,12 +2,16 @@
2
2
  // File Purpose: Tests the one-time migration from nested to flat-phase layout.
3
3
  import test, { afterEach } from "node:test";
4
4
  import assert from "node:assert/strict";
5
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readdirSync, readFileSync } from "node:fs";
5
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, existsSync, readdirSync, readFileSync, utimesSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
  import { randomUUID } from "node:crypto";
9
9
 
10
- import { migrateToFlatPhase, needsFlatPhaseMigration } from "../flat-phase-migration.ts";
10
+ import {
11
+ migrateToFlatPhase,
12
+ needsFlatPhaseMigration,
13
+ pruneStaleFlatPhaseBackups,
14
+ } from "../flat-phase-migration.ts";
11
15
  import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask, getAllMilestones, getMilestoneSlices, getSliceTasks } from "../gsd-db.ts";
12
16
 
13
17
  const tmpDirs: string[] = [];
@@ -112,3 +116,34 @@ test("migrateToFlatPhase preserves slice sidecar artifacts and skips recovery pl
112
116
  "recovery placeholder PLAN should not be promoted when no DB tasks can render a real plan",
113
117
  );
114
118
  });
119
+
120
+ test("pruneStaleFlatPhaseBackups removes migrate-* dirs older than retention window", async () => {
121
+ const base = makeTmp();
122
+ await migrateToFlatPhase(base);
123
+
124
+ const backupRoot = join(base, ".gsd-backups");
125
+ assert.ok(existsSync(backupRoot), "backup should exist immediately after migration");
126
+
127
+ const staleDir = join(backupRoot, "migrate-stale");
128
+ mkdirSync(staleDir, { recursive: true });
129
+ writeFileSync(join(staleDir, "marker.txt"), "old backup\n", "utf-8");
130
+ const staleDate = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000);
131
+ utimesSync(staleDir, staleDate, staleDate);
132
+
133
+ const removed = pruneStaleFlatPhaseBackups(base);
134
+ assert.equal(removed, 1, "stale migrate-* dir should be pruned");
135
+ assert.equal(existsSync(staleDir), false, "stale backup dir should be gone");
136
+ assert.ok(existsSync(backupRoot), "fresh migration backup should remain");
137
+ });
138
+
139
+ test("pruneStaleFlatPhaseBackups is a no-op while flat-phase migration is still needed", () => {
140
+ const base = makeTmp();
141
+ const backupRoot = join(base, ".gsd-backups", "migrate-stale");
142
+ mkdirSync(backupRoot, { recursive: true });
143
+ writeFileSync(join(backupRoot, "marker.txt"), "old backup\n", "utf-8");
144
+ const staleDate = new Date(Date.now() - 31 * 24 * 60 * 60 * 1000);
145
+ utimesSync(backupRoot, staleDate, staleDate);
146
+
147
+ assert.equal(pruneStaleFlatPhaseBackups(base), 0, "must not prune while migration is pending");
148
+ assert.ok(existsSync(backupRoot), "backup must remain until migration completes");
149
+ });
@@ -119,3 +119,91 @@ describe("extractTrace error filtering (#2539)", () => {
119
119
  assert.equal(trace.errors.length, 1, "lint error with output should be counted");
120
120
  });
121
121
  });
122
+
123
+ describe("extractTrace pending tool calls (#945)", () => {
124
+ test("tool call without a toolResult is counted as an error", () => {
125
+ const entries = [
126
+ {
127
+ type: "message",
128
+ message: {
129
+ role: "assistant",
130
+ content: [
131
+ {
132
+ type: "toolCall",
133
+ id: "toolu_missing",
134
+ name: "gsd_summary_save",
135
+ arguments: { content: "unfinished summary" },
136
+ },
137
+ ],
138
+ },
139
+ },
140
+ ];
141
+
142
+ const trace = extractTrace(entries);
143
+
144
+ assert.equal(trace.toolCalls.length, 1);
145
+ assert.equal(trace.toolCalls[0]?.name, "gsd_summary_save");
146
+ assert.equal(trace.toolCalls[0]?.isError, true);
147
+ assert.equal(trace.toolCalls[0]?.result, "missing tool result (stream/tool-call abort before execution)");
148
+ assert.equal(trace.errors.length, 1);
149
+ assert.equal(trace.errors[0], "Tool call gsd_summary_save started but no toolResult was recorded");
150
+ });
151
+
152
+ test("pending bash call with no toolResult marks commandsRun entry as failed", () => {
153
+ // A bash tool call that was initiated but never got a toolResult (stream abort).
154
+ const entries = [
155
+ {
156
+ type: "message",
157
+ message: {
158
+ role: "assistant",
159
+ content: [
160
+ {
161
+ type: "toolCall",
162
+ id: "toolu_bash_missing",
163
+ name: "bash",
164
+ arguments: { command: "npm run build" },
165
+ },
166
+ ],
167
+ },
168
+ },
169
+ ];
170
+
171
+ const trace = extractTrace(entries);
172
+
173
+ // The tool call should be recorded as an error.
174
+ assert.equal(trace.toolCalls.length, 1);
175
+ assert.equal(trace.toolCalls[0]?.name, "bash");
176
+ assert.equal(trace.toolCalls[0]?.isError, true);
177
+
178
+ // The commandsRun entry must be consistent: failed: true, not failed: false.
179
+ assert.equal(trace.commandsRun.length, 1);
180
+ assert.equal(trace.commandsRun[0]?.command, "npm run build");
181
+ assert.equal(trace.commandsRun[0]?.failed, true,
182
+ "commandsRun entry should be failed when its bash call never returned a result");
183
+ });
184
+
185
+ test("pending bg_shell call with no toolResult marks commandsRun entry as failed", () => {
186
+ const entries = [
187
+ {
188
+ type: "message",
189
+ message: {
190
+ role: "assistant",
191
+ content: [
192
+ {
193
+ type: "toolCall",
194
+ id: "toolu_bg_missing",
195
+ name: "bg_shell",
196
+ arguments: { command: "sleep 30" },
197
+ },
198
+ ],
199
+ },
200
+ },
201
+ ];
202
+
203
+ const trace = extractTrace(entries);
204
+
205
+ assert.equal(trace.commandsRun.length, 1);
206
+ assert.equal(trace.commandsRun[0]?.failed, true,
207
+ "bg_shell commandsRun entry should be failed when no toolResult was recorded");
208
+ });
209
+ });
@@ -40,6 +40,10 @@ describe('ensureGitignore writes .bg-shell/ baseline (#4902)', () => {
40
40
  lines.has('.bg-shell/'),
41
41
  `.gitignore should include .bg-shell/. Got:\n${ignore}`,
42
42
  );
43
+ assert.ok(
44
+ lines.has('.gsd-backups/'),
45
+ `.gitignore should include .gsd-backups/. Got:\n${ignore}`,
46
+ );
43
47
  } finally {
44
48
  cleanup(dir);
45
49
  }
@@ -86,3 +86,45 @@ test("guided milestone prompt builder preloads milestone planning context", asyn
86
86
  rmSync(base, { recursive: true, force: true });
87
87
  }
88
88
  });
89
+
90
+ test("guided milestone prompt builder caps prior draft seed before interpolation", async () => {
91
+ const base = mkdtempSync(join(tmpdir(), "gsd-guided-milestone-draft-cap-"));
92
+ const previousGsdHome = process.env.GSD_HOME;
93
+ process.env.GSD_HOME = join(base, ".gsd-home");
94
+
95
+ try {
96
+ const currentDir = join(base, ".gsd", "milestones", "M001");
97
+ mkdirSync(currentDir, { recursive: true });
98
+
99
+ const draftPath = join(currentDir, "M001-CONTEXT-DRAFT.md");
100
+ writeFileSync(draftPath, "# Draft\n\nSMALL-DRAFT-SIGNAL", "utf-8");
101
+ const smallPrompt = await buildDiscussMilestonePrompt("M001", "Draft Resume", base, "true", {
102
+ includeContextMode: false,
103
+ });
104
+
105
+ writeFileSync(
106
+ draftPath,
107
+ ["# Draft", "", "SMALL-DRAFT-SIGNAL", "", "A".repeat(200_000), "", "OVERSIZED-DRAFT-TAIL-SIGNAL"].join("\n"),
108
+ "utf-8",
109
+ );
110
+ const largePrompt = await buildDiscussMilestonePrompt("M001", "Draft Resume", base, "true", {
111
+ includeContextMode: false,
112
+ });
113
+
114
+ const addedChars = largePrompt.length - smallPrompt.length;
115
+
116
+ assert.match(largePrompt, /## Prior Discussion \(Draft Seed\)/);
117
+ assert.match(largePrompt, /### Prior Discussion Draft/);
118
+ assert.match(largePrompt, /SMALL-DRAFT-SIGNAL/);
119
+ assert.doesNotMatch(largePrompt, /OVERSIZED-DRAFT-TAIL-SIGNAL/);
120
+ assert.match(
121
+ largePrompt,
122
+ /Draft seed truncated; read the full draft at `\.gsd\/milestones\/M001\/M001-CONTEXT-DRAFT\.md` if needed\./,
123
+ );
124
+ assert.ok(addedChars < 25_000, `large draft should add bounded seed chars, added ${addedChars}`);
125
+ } finally {
126
+ if (previousGsdHome === undefined) delete process.env.GSD_HOME;
127
+ else process.env.GSD_HOME = previousGsdHome;
128
+ rmSync(base, { recursive: true, force: true });
129
+ }
130
+ });