@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,8 +3,10 @@
3
3
 
4
4
  import test from "node:test";
5
5
  import assert from "node:assert/strict";
6
- import { mkdirSync, rmSync, writeFileSync } from "node:fs";
7
- import { join } from "node:path";
6
+ import { execFileSync } from "node:child_process";
7
+ import { chmodSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
8
+ import { performance } from "node:perf_hooks";
9
+ import { delimiter, join } from "node:path";
8
10
  import { tmpdir } from "node:os";
9
11
  import {
10
12
  buildHealthLines,
@@ -12,8 +14,9 @@ import {
12
14
  formatRelativeTime,
13
15
  type HealthWidgetData,
14
16
  } from "../health-widget-core.ts";
15
- import { HEALTH_WIDGET_ACTIVE_HINTS } from "../health-widget.ts";
17
+ import { HEALTH_WIDGET_ACTIVE_HINTS, getCachedProjectState, initHealthWidget } from "../health-widget.ts";
16
18
  import { registerHooks } from "../bootstrap/register-hooks.ts";
19
+ import { GIT_NO_PROMPT_ENV } from "../git-constants.ts";
17
20
 
18
21
  function makeTempDir(prefix: string): string {
19
22
  const dir = join(
@@ -32,6 +35,58 @@ function cleanup(dir: string): void {
32
35
  }
33
36
  }
34
37
 
38
+ function runGit(cwd: string, ...args: string[]): string {
39
+ return execFileSync("git", args, {
40
+ cwd,
41
+ encoding: "utf-8",
42
+ stdio: ["ignore", "pipe", "pipe"],
43
+ }).trim();
44
+ }
45
+
46
+ function makeTempRepo(prefix: string): string {
47
+ const dir = makeTempDir(prefix);
48
+ runGit(dir, "init");
49
+ runGit(dir, "config", "user.email", "test@test.com");
50
+ runGit(dir, "config", "user.name", "Test");
51
+ writeFileSync(join(dir, "README.md"), "# test\n", "utf-8");
52
+ runGit(dir, "add", "README.md");
53
+ runGit(dir, "commit", "-m", "initial commit");
54
+ return dir;
55
+ }
56
+
57
+ function installSlowGitLogShim(binDir: string): void {
58
+ writeFileSync(
59
+ join(binDir, "git"),
60
+ [
61
+ "#!/bin/sh",
62
+ 'if [ "$1" = "log" ]; then sleep 1; fi',
63
+ 'PATH="$GSD_REAL_PATH"',
64
+ "export PATH",
65
+ 'exec git "$@"',
66
+ "",
67
+ ].join("\n"),
68
+ "utf-8",
69
+ );
70
+ chmodSync(join(binDir, "git"), 0o755);
71
+
72
+ writeFileSync(
73
+ join(binDir, "git.cmd"),
74
+ [
75
+ "@echo off",
76
+ 'if "%1"=="log" powershell -NoProfile -Command "Start-Sleep -Seconds 1"',
77
+ 'set "PATH=%GSD_REAL_PATH%"',
78
+ "git %*",
79
+ "",
80
+ ].join("\r\n"),
81
+ "utf-8",
82
+ );
83
+ }
84
+
85
+ type HealthWidgetFactory = (
86
+ tui: { requestRender(): void },
87
+ theme: { fg(style: string, text: string): string },
88
+ ) => { dispose(): void };
89
+
35
90
  function activeData(overrides: Partial<HealthWidgetData> = {}): HealthWidgetData {
36
91
  return {
37
92
  projectState: "active",
@@ -70,6 +125,86 @@ test("detectHealthWidgetProjectState: milestone without metrics returns active",
70
125
  assert.equal(detectHealthWidgetProjectState(dir), "active");
71
126
  });
72
127
 
128
+ test("getCachedProjectState: reuses project state until the refresh TTL expires", (t) => {
129
+ const dir = makeTempDir("cached-state");
130
+ t.after(() => { cleanup(dir); });
131
+
132
+ let now = 1_000_000;
133
+ const dateNow = t.mock.method(Date, "now", () => now);
134
+ t.after(() => { dateNow.mock.restore(); });
135
+
136
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
137
+ assert.equal(getCachedProjectState(dir), "initialized");
138
+
139
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
140
+ assert.equal(getCachedProjectState(dir), "initialized");
141
+
142
+ now += 60_000;
143
+ assert.equal(getCachedProjectState(dir), "initialized");
144
+
145
+ now += 1;
146
+ assert.equal(getCachedProjectState(dir), "active");
147
+ });
148
+
149
+ test("getCachedProjectState: force=true bypasses TTL and returns fresh state within TTL window", (t) => {
150
+ const dir = makeTempDir("forced-state");
151
+ t.after(() => { cleanup(dir); });
152
+
153
+ let now = 2_000_000;
154
+ const dateNow = t.mock.method(Date, "now", () => now);
155
+ t.after(() => { dateNow.mock.restore(); });
156
+
157
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
158
+ // Prime the cache with "initialized".
159
+ assert.equal(getCachedProjectState(dir), "initialized");
160
+
161
+ // Disk changes within the TTL window.
162
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
163
+ now += 1_000; // well within 60s TTL
164
+
165
+ // Normal call still returns stale cached value.
166
+ assert.equal(getCachedProjectState(dir), "initialized");
167
+
168
+ // force=true bypasses TTL and returns the fresh disk state.
169
+ assert.equal(getCachedProjectState(dir, true), "active");
170
+
171
+ // Subsequent non-forced call also reflects the freshened cache.
172
+ assert.equal(getCachedProjectState(dir), "active");
173
+ });
174
+
175
+ test("initHealthWidget: re-init paints fresh project state within cache TTL", (t) => {
176
+ const dir = makeTempDir("reinit-state");
177
+ t.after(() => { cleanup(dir); });
178
+
179
+ let now = 3_000_000;
180
+ const dateNow = t.mock.method(Date, "now", () => now);
181
+ t.after(() => { dateNow.mock.restore(); });
182
+
183
+ const originalCwd = process.cwd();
184
+ process.chdir(dir);
185
+ t.after(() => { process.chdir(originalCwd); });
186
+
187
+ const initialLineSets: string[][] = [];
188
+ const ctx = {
189
+ hasUI: true,
190
+ ui: {
191
+ setWidget: (_key: string, value: unknown) => {
192
+ if (Array.isArray(value)) initialLineSets.push(value as string[]);
193
+ },
194
+ },
195
+ } as any;
196
+
197
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
198
+ initHealthWidget(ctx);
199
+ assert.equal(initialLineSets.at(-1)?.[0], " GSD Project Initialized");
200
+
201
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
202
+ now += 1_000;
203
+
204
+ initHealthWidget(ctx);
205
+ assert.match(initialLineSets.at(-1)?.[0] ?? "", /System OK/);
206
+ });
207
+
73
208
  test("buildHealthLines: none state shows single onboarding line pointing at /gsd", (t) => {
74
209
  const lines = buildHealthLines(activeData({ projectState: "none" }));
75
210
  assert.equal(lines.length, 1, "renders exactly one line");
@@ -101,6 +236,136 @@ test("health widget active hints include visualization and notifications", () =>
101
236
  assert.match(HEALTH_WIDGET_ACTIVE_HINTS, /\/gsd help/);
102
237
  });
103
238
 
239
+ test("health widget async refresh does not block timers while git log is slow", async (t) => {
240
+ const dir = makeTempRepo("slow-git-log");
241
+ const binDir = makeTempDir("slow-git-log-bin");
242
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
243
+ installSlowGitLogShim(binDir);
244
+
245
+ const originalCwd = process.cwd();
246
+ const originalProcessPath = process.env.PATH;
247
+ const originalEnvPath = GIT_NO_PROMPT_ENV.PATH;
248
+ const originalEnvRealPath = GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
249
+ const shimmedPath = `${binDir}${delimiter}${originalProcessPath ?? ""}`;
250
+
251
+ process.chdir(dir);
252
+ process.env.PATH = shimmedPath;
253
+ GIT_NO_PROMPT_ENV.PATH = shimmedPath;
254
+ GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalProcessPath ?? "";
255
+
256
+ let factory: HealthWidgetFactory | null = null;
257
+ let resolveRefresh: (() => void) | undefined;
258
+ const refreshed = new Promise<void>((resolve) => { resolveRefresh = resolve; });
259
+ const gaps: number[] = [];
260
+ let lastTick = performance.now();
261
+ let heartbeat: NodeJS.Timeout | undefined;
262
+ let refreshTimeout: NodeJS.Timeout | undefined;
263
+ let widget: { dispose(): void } | undefined;
264
+
265
+ t.after(() => {
266
+ if (widget) widget.dispose();
267
+ if (heartbeat) clearInterval(heartbeat);
268
+ if (refreshTimeout) clearTimeout(refreshTimeout);
269
+ process.chdir(originalCwd);
270
+ if (originalProcessPath === undefined) delete process.env.PATH;
271
+ else process.env.PATH = originalProcessPath;
272
+ if (originalEnvPath === undefined) delete GIT_NO_PROMPT_ENV.PATH;
273
+ else GIT_NO_PROMPT_ENV.PATH = originalEnvPath;
274
+ if (originalEnvRealPath === undefined) delete GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
275
+ else GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalEnvRealPath;
276
+ cleanup(binDir);
277
+ cleanup(dir);
278
+ });
279
+
280
+ initHealthWidget({
281
+ hasUI: true,
282
+ ui: {
283
+ setWidget: (_key: string, value: unknown) => {
284
+ if (typeof value === "function") factory = value as HealthWidgetFactory;
285
+ },
286
+ },
287
+ } as any);
288
+
289
+ assert.ok(factory, "health widget factory is registered");
290
+
291
+ heartbeat = setInterval(() => {
292
+ const now = performance.now();
293
+ gaps.push(now - lastTick);
294
+ lastTick = now;
295
+ }, 25);
296
+
297
+ // assert.ok above guards at runtime; double-cast is needed because TypeScript
298
+ // cannot track the factory assignment through the `as any` closure call.
299
+ widget = (factory as unknown as HealthWidgetFactory)(
300
+ { requestRender: () => { resolveRefresh?.(); } },
301
+ { fg: (_style: string, text: string) => text },
302
+ );
303
+
304
+ await Promise.race([
305
+ refreshed,
306
+ new Promise<never>((_, reject) => {
307
+ refreshTimeout = setTimeout(() => reject(new Error("health widget refresh did not complete")), 4_000);
308
+ }),
309
+ ]);
310
+ if (refreshTimeout) clearTimeout(refreshTimeout);
311
+
312
+ assert.ok(gaps.length > 0, "heartbeat ran while refresh was in flight");
313
+ const maxGap = Math.max(...gaps);
314
+ assert.ok(maxGap < 750, `slow git log must not starve timers; max gap was ${Math.round(maxGap)}ms`);
315
+ });
316
+
317
+ test("initHealthWidget: synchronous first-paint render never contains last-commit info (regression #964)", (t) => {
318
+ // Before the fix, loadHealthWidgetData with includeChecks:true called
319
+ // loadLastCommitInfo — synchronous native-git-bridge ops (nativeIsRepo,
320
+ // nativeGetCurrentBranch, nativeLastCommitEpoch, nativeCommitSubject) — which
321
+ // froze the TUI on slow repos. The fix removes that synchronous git path
322
+ // entirely: lastCommitEpoch/lastCommitMessage are now always null from the
323
+ // synchronous loader; only the async refresh (loadLastCommitInfoAsync) fills
324
+ // them in. This test guards that contract by verifying that the initial
325
+ // string-array setWidget call never contains "Last commit:" even on a real
326
+ // git repo where native git queries would succeed.
327
+ const dir = makeTempRepo("sync-last-commit-regression");
328
+ mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
329
+
330
+ const originalCwd = process.cwd();
331
+ process.chdir(dir);
332
+
333
+ let widget: { dispose(): void } | undefined;
334
+ t.after(() => {
335
+ if (widget) widget.dispose();
336
+ process.chdir(originalCwd);
337
+ cleanup(dir);
338
+ });
339
+
340
+ const initialRenders: string[][] = [];
341
+
342
+ initHealthWidget({
343
+ hasUI: true,
344
+ ui: {
345
+ setWidget: (_key: string, value: unknown) => {
346
+ if (Array.isArray(value)) {
347
+ initialRenders.push(value as string[]);
348
+ } else if (typeof value === "function") {
349
+ // Instantiate the factory to satisfy dispose(), but do not await the
350
+ // async refresh — we are only inspecting the synchronous first-paint.
351
+ widget = (value as unknown as HealthWidgetFactory)(
352
+ { requestRender: () => {} },
353
+ { fg: (_style: string, text: string) => text },
354
+ );
355
+ }
356
+ },
357
+ },
358
+ } as any);
359
+
360
+ assert.ok(initialRenders.length > 0, "at least one synchronous setWidget call");
361
+
362
+ const combined = initialRenders.flat().join("\n");
363
+ assert.ok(
364
+ !combined.includes("Last commit:"),
365
+ "synchronous first-paint render must not contain 'Last commit:' — sync git path removed (regression #964)",
366
+ );
367
+ });
368
+
104
369
  test("buildHealthLines: active state with budget ceiling shows percent summary", (t) => {
105
370
  const lines = buildHealthLines(activeData({ budgetSpent: 2.5, budgetCeiling: 10 }));
106
371
  assert.equal(lines.length, 1);
@@ -3,7 +3,7 @@
3
3
  import { describe, test } from 'node:test';
4
4
  import assert from 'node:assert/strict';
5
5
  import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, symlinkSync, readFileSync, chmodSync } from "node:fs";
6
- import { join, dirname } from "node:path";
6
+ import { join, dirname, delimiter } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
  import { execFileSync, execSync } from "node:child_process";
9
9
 
@@ -23,6 +23,7 @@ import {
23
23
  type PreMergeCheckResult,
24
24
  type TaskCommitContext,
25
25
  } from "../../git-service.ts";
26
+ import { GIT_NO_PROMPT_ENV } from "../../git-constants.ts";
26
27
  import { nativeAddAllWithExclusions, nativeHasChanges, _resetHasChangesCache } from "../../native-git-bridge.ts";
27
28
  function run(command: string, cwd: string): string {
28
29
  return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
@@ -293,12 +294,13 @@ describe('git-service', async () => {
293
294
 
294
295
  assert.deepStrictEqual(
295
296
  RUNTIME_EXCLUSION_PATHS.length,
296
- 17,
297
- "exactly 17 runtime exclusion paths"
297
+ 18,
298
+ "exactly 18 runtime exclusion paths"
298
299
  );
299
300
 
300
301
  const expectedPaths = [
301
302
  ".gsd-worktrees/",
303
+ ".gsd-backups/**",
302
304
  ".gsd/activity/",
303
305
  ".gsd/audit/",
304
306
  ".gsd/forensics/",
@@ -405,6 +407,49 @@ describe('git-service', async () => {
405
407
  }).trim();
406
408
  }
407
409
 
410
+ function findGitExecutable(): string {
411
+ const command = process.platform === "win32" ? "where git" : "command -v git";
412
+ const output = execSync(command, { encoding: "utf-8" });
413
+ const found = output.split(/\r?\n/).map(line => line.trim()).find(Boolean);
414
+ assert.ok(found, "git executable is available");
415
+ return found;
416
+ }
417
+
418
+ function installGitRmCachedCounterShim(shimDir: string): void {
419
+ const shimScript = join(shimDir, "git-shim.cjs");
420
+ writeFileSync(shimScript, `
421
+ const { appendFileSync } = require("node:fs");
422
+ const { spawnSync } = require("node:child_process");
423
+
424
+ const args = process.argv.slice(2);
425
+ if (args[0] === "rm" && args[1] === "--cached") {
426
+ appendFileSync(process.env.GSD_GIT_SHIM_LOG, args.join("\\0") + "\\n");
427
+ }
428
+
429
+ const result = spawnSync(process.env.GSD_REAL_GIT || "git", args, {
430
+ stdio: "inherit",
431
+ env: process.env,
432
+ });
433
+ if (result.error) {
434
+ console.error(result.error.message);
435
+ process.exit(1);
436
+ }
437
+ process.exit(result.status ?? 0);
438
+ `.trimStart(), "utf-8");
439
+
440
+ const posixShim = join(shimDir, "git");
441
+ const quotedScript = `'${shimScript.replace(/'/g, "'\\''")}'`;
442
+ writeFileSync(posixShim, `#!/bin/sh\nexec node ${quotedScript} "$@"\n`, "utf-8");
443
+ chmodSync(posixShim, 0o755);
444
+
445
+ writeFileSync(join(shimDir, "git.cmd"), "@echo off\r\nnode \"%~dp0git-shim.cjs\" %*\r\n", "utf-8");
446
+ }
447
+
448
+ function countRmCachedInvocations(logFile: string): number {
449
+ if (!existsSync(logFile)) return 0;
450
+ return readFileSync(logFile, "utf-8").split(/\r?\n/).filter(Boolean).length;
451
+ }
452
+
408
453
  // ─── GitServiceImpl: smart staging ─────────────────────────────────────
409
454
 
410
455
  test('GitServiceImpl: smart staging', () => {
@@ -447,6 +492,80 @@ describe('git-service', async () => {
447
492
  rmSync(repo, { recursive: true, force: true });
448
493
  });
449
494
 
495
+ test('GitServiceImpl: runtime cleanup runs once per repo across service instances', () => {
496
+ const repo = initTempRepo();
497
+ const shimDir = mkdtempSync(join(tmpdir(), "gsd-git-rm-shim-"));
498
+ const logFile = join(shimDir, "git-rm-cached.log");
499
+ const originalPath = process.env.PATH;
500
+ const originalRealGit = process.env.GSD_REAL_GIT;
501
+ const originalShimLog = process.env.GSD_GIT_SHIM_LOG;
502
+ const originalSafePath = GIT_NO_PROMPT_ENV.PATH;
503
+ const originalSafeRealGit = GIT_NO_PROMPT_ENV.GSD_REAL_GIT;
504
+ const originalSafeShimLog = GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG;
505
+
506
+ try {
507
+ installGitRmCachedCounterShim(shimDir);
508
+ const realGit = findGitExecutable();
509
+ process.env.GSD_REAL_GIT = realGit;
510
+ process.env.GSD_GIT_SHIM_LOG = logFile;
511
+ process.env.PATH = `${shimDir}${delimiter}${originalPath ?? ""}`;
512
+ GIT_NO_PROMPT_ENV.GSD_REAL_GIT = realGit;
513
+ GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG = logFile;
514
+ GIT_NO_PROMPT_ENV.PATH = process.env.PATH;
515
+
516
+ createFile(repo, "src/first.ts", "first");
517
+ const first = new GitServiceImpl(repo).commit({ message: "test: first cleanup pass" });
518
+ assert.deepStrictEqual(first, "test: first cleanup pass", "first commit succeeds");
519
+ assert.deepStrictEqual(
520
+ countRmCachedInvocations(logFile),
521
+ RUNTIME_EXCLUSION_PATHS.length,
522
+ "first service instance checks each runtime exclusion once",
523
+ );
524
+
525
+ createFile(repo, "src/second.ts", "second");
526
+ const second = new GitServiceImpl(repo).commit({ message: "test: second cleanup pass" });
527
+ assert.deepStrictEqual(second, "test: second cleanup pass", "second commit succeeds");
528
+ assert.deepStrictEqual(
529
+ countRmCachedInvocations(logFile),
530
+ RUNTIME_EXCLUSION_PATHS.length,
531
+ "fresh service instance for the same repo does not rerun runtime cleanup",
532
+ );
533
+ } finally {
534
+ if (originalPath === undefined) {
535
+ delete process.env.PATH;
536
+ } else {
537
+ process.env.PATH = originalPath;
538
+ }
539
+ if (originalRealGit === undefined) {
540
+ delete process.env.GSD_REAL_GIT;
541
+ } else {
542
+ process.env.GSD_REAL_GIT = originalRealGit;
543
+ }
544
+ if (originalShimLog === undefined) {
545
+ delete process.env.GSD_GIT_SHIM_LOG;
546
+ } else {
547
+ process.env.GSD_GIT_SHIM_LOG = originalShimLog;
548
+ }
549
+ if (originalSafePath === undefined) {
550
+ delete GIT_NO_PROMPT_ENV.PATH;
551
+ } else {
552
+ GIT_NO_PROMPT_ENV.PATH = originalSafePath;
553
+ }
554
+ if (originalSafeRealGit === undefined) {
555
+ delete GIT_NO_PROMPT_ENV.GSD_REAL_GIT;
556
+ } else {
557
+ GIT_NO_PROMPT_ENV.GSD_REAL_GIT = originalSafeRealGit;
558
+ }
559
+ if (originalSafeShimLog === undefined) {
560
+ delete GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG;
561
+ } else {
562
+ GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG = originalSafeShimLog;
563
+ }
564
+ rmSync(repo, { recursive: true, force: true });
565
+ rmSync(shimDir, { recursive: true, force: true });
566
+ }
567
+ });
568
+
450
569
  test('GitServiceImpl: task autoCommit skips keyFiles inside submodules', () => {
451
570
  const repo = initTempRepo();
452
571
  const subSrc = mkdtempSync(join(tmpdir(), "gsd-git-submodule-src-"));
@@ -1505,11 +1624,13 @@ describe('git-service', async () => {
1505
1624
  // Create and track runtime files (simulates pre-.gitignore state)
1506
1625
  mkdirSync(join(repo, ".gsd", "activity"), { recursive: true });
1507
1626
  mkdirSync(join(repo, ".gsd", "runtime"), { recursive: true });
1627
+ mkdirSync(join(repo, ".gsd-backups", "migrate-old"), { recursive: true });
1508
1628
  writeFileSync(join(repo, ".gsd", "completed-units.json"), '["u1"]');
1509
1629
  writeFileSync(join(repo, ".gsd", "metrics.json"), '{}');
1510
1630
  writeFileSync(join(repo, ".gsd", "STATE.md"), "# State");
1511
1631
  writeFileSync(join(repo, ".gsd", "activity", "log.jsonl"), "{}");
1512
1632
  writeFileSync(join(repo, ".gsd", "runtime", "data.json"), "{}");
1633
+ writeFileSync(join(repo, ".gsd-backups", "migrate-old", "marker.txt"), "backup");
1513
1634
  writeFileSync(join(repo, "src.ts"), "code");
1514
1635
  runGit(repo, ["add", "-A"]);
1515
1636
  runGit(repo, ["commit", "-m", "init"]);
@@ -1518,6 +1639,8 @@ describe('git-service', async () => {
1518
1639
  const trackedBefore = run("git ls-files .gsd/", repo);
1519
1640
  assert.ok(trackedBefore.includes("completed-units.json"), "untrack: precondition — completed-units tracked");
1520
1641
  assert.ok(trackedBefore.includes("metrics.json"), "untrack: precondition — metrics tracked");
1642
+ const backupTrackedBefore = run("git ls-files .gsd-backups/", repo);
1643
+ assert.ok(backupTrackedBefore.includes("marker.txt"), "untrack: precondition — migration backup tracked");
1521
1644
 
1522
1645
  // Run untrackRuntimeFiles
1523
1646
  untrackRuntimeFiles(repo);
@@ -1525,6 +1648,8 @@ describe('git-service', async () => {
1525
1648
  // Runtime files should be removed from the index
1526
1649
  const trackedAfter = run("git ls-files .gsd/", repo);
1527
1650
  assert.deepStrictEqual(trackedAfter, "", "untrack: all runtime files removed from index");
1651
+ const backupTrackedAfter = run("git ls-files .gsd-backups/", repo);
1652
+ assert.deepStrictEqual(backupTrackedAfter, "", "untrack: migration backups removed from index");
1528
1653
 
1529
1654
  // Non-runtime files remain tracked
1530
1655
  const srcTracked = run("git ls-files src.ts", repo);
@@ -1535,6 +1660,8 @@ describe('git-service', async () => {
1535
1660
  "untrack: completed-units.json still on disk");
1536
1661
  assert.ok(existsSync(join(repo, ".gsd", "metrics.json")),
1537
1662
  "untrack: metrics.json still on disk");
1663
+ assert.ok(existsSync(join(repo, ".gsd-backups", "migrate-old", "marker.txt")),
1664
+ "untrack: migration backup still on disk");
1538
1665
 
1539
1666
  // Idempotent — running again doesn't error
1540
1667
  untrackRuntimeFiles(repo);
@@ -0,0 +1,93 @@
1
+ import { describe, test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+
7
+ import { buildExistingMilestonesContext } from "../../guided-flow-queue.ts";
8
+ import type { GSDState, MilestoneRegistryEntry } from "../../types.ts";
9
+
10
+ const LARGE_BODY = "A".repeat(150_000);
11
+ const LARGE_DRAFT = "D".repeat(150_000);
12
+ const LARGE_ROADMAP = "R".repeat(150_000);
13
+
14
+ function makeState(registry: MilestoneRegistryEntry[]): GSDState {
15
+ return {
16
+ activeMilestone: registry.find(m => m.status === "active") ?? null,
17
+ activeSlice: null,
18
+ activeTask: null,
19
+ phase: "executing",
20
+ recentDecisions: [],
21
+ blockers: [],
22
+ nextAction: "",
23
+ registry,
24
+ };
25
+ }
26
+
27
+ function writeMilestoneArtifact(base: string, mid: string, suffix: string, content: string): void {
28
+ mkdirSync(join(base, ".gsd", "milestones", mid), { recursive: true });
29
+ writeFileSync(join(base, ".gsd", "milestones", mid, `${mid}-${suffix}.md`), content);
30
+ }
31
+
32
+ describe("queue active/pending milestone context budget", () => {
33
+ test("summarizes active and pending artifacts with source paths and bounded excerpts", async () => {
34
+ const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-active-budget-"));
35
+ try {
36
+ writeMilestoneArtifact(tmpBase, "M001", "CONTEXT", `# Active context\n\n${LARGE_BODY}\nEND_ACTIVE_CONTEXT`);
37
+ writeMilestoneArtifact(tmpBase, "M001", "ROADMAP", `# Active roadmap\n\n${LARGE_ROADMAP}\nEND_ACTIVE_ROADMAP`);
38
+ writeMilestoneArtifact(tmpBase, "M002", "CONTEXT-DRAFT", `# Pending draft\n\n${LARGE_DRAFT}\nEND_PENDING_DRAFT`);
39
+ writeMilestoneArtifact(tmpBase, "M002", "ROADMAP", `# Pending roadmap\n\n${LARGE_ROADMAP}\nEND_PENDING_ROADMAP`);
40
+
41
+ const registry: MilestoneRegistryEntry[] = [
42
+ { id: "M001", title: "Active milestone", status: "active" },
43
+ { id: "M002", title: "Pending milestone", status: "pending" },
44
+ ];
45
+
46
+ const context = await buildExistingMilestonesContext(tmpBase, ["M001", "M002"], makeState(registry));
47
+
48
+ assert.match(context, /Source: `.gsd\/milestones\/M001\/M001-CONTEXT.md`/);
49
+ assert.match(context, /Source: `.gsd\/milestones\/M001\/M001-ROADMAP.md`/);
50
+ assert.match(context, /Source: `.gsd\/milestones\/M002\/M002-CONTEXT-DRAFT.md`/);
51
+ assert.match(context, /Source: `.gsd\/milestones\/M002\/M002-ROADMAP.md`/);
52
+ assert.match(context, /Read `.gsd\/milestones\/M001\/M001-CONTEXT.md` for full content/);
53
+ assert.equal(context.includes("END_ACTIVE_CONTEXT"), false);
54
+ assert.equal(context.includes("END_ACTIVE_ROADMAP"), false);
55
+ assert.equal(context.includes("END_PENDING_DRAFT"), false);
56
+ assert.equal(context.includes("END_PENDING_ROADMAP"), false);
57
+ } finally {
58
+ rmSync(tmpBase, { recursive: true, force: true });
59
+ }
60
+ });
61
+
62
+ test("caps the total existing milestones context", async () => {
63
+ const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-total-budget-"));
64
+ try {
65
+ const registry: MilestoneRegistryEntry[] = [];
66
+ const milestoneIds: string[] = [];
67
+ for (let i = 1; i <= 5; i++) {
68
+ const mid = `M${String(i).padStart(3, "0")}`;
69
+ milestoneIds.push(mid);
70
+ registry.push({ id: mid, title: `Pending milestone ${i}`, status: "pending" });
71
+ writeMilestoneArtifact(tmpBase, mid, "CONTEXT", `# ${mid} context\n\n${LARGE_BODY}\nEND_${mid}_CONTEXT`);
72
+ writeMilestoneArtifact(tmpBase, mid, "ROADMAP", `# ${mid} roadmap\n\n${LARGE_ROADMAP}\nEND_${mid}_ROADMAP`);
73
+ }
74
+
75
+ const context = await buildExistingMilestonesContext(tmpBase, milestoneIds, makeState(registry));
76
+
77
+ assert.ok(
78
+ context.length <= 120_000,
79
+ `expected total context to stay within budget, got ${context.length} chars`,
80
+ );
81
+ assert.match(context, /Existing milestones context truncated/);
82
+ for (let i = 1; i <= 5; i++) {
83
+ const mid = `M${String(i).padStart(3, "0")}`;
84
+ assert.match(context, new RegExp(`### ${mid}: Pending milestone ${i}`));
85
+ assert.match(context, new RegExp(`Source: \`.gsd/milestones/${mid}/${mid}-CONTEXT.md\``));
86
+ assert.match(context, new RegExp(`Source: \`.gsd/milestones/${mid}/${mid}-ROADMAP.md\``));
87
+ }
88
+ assert.equal(context.includes("END_M005_ROADMAP"), false);
89
+ } finally {
90
+ rmSync(tmpBase, { recursive: true, force: true });
91
+ }
92
+ });
93
+ });