@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.72e3af2a

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 (357) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
  3. package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
  4. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
  6. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  7. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
  9. package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
  10. package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
  11. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  12. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  13. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
  14. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
  15. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
  16. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +10 -19
  18. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
  19. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
  20. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  21. package/dist/resources/extensions/gsd/commands-context.js +19 -1
  22. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
  23. package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
  24. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
  25. package/dist/resources/extensions/gsd/db/queries.js +60 -0
  26. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  27. package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
  28. package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
  29. package/dist/resources/extensions/gsd/forensics.js +2 -32
  30. package/dist/resources/extensions/gsd/git-service.js +4 -4
  31. package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
  32. package/dist/resources/extensions/gsd/health-widget.js +55 -29
  33. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  34. package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
  35. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
  36. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  37. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  38. package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
  39. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  40. package/dist/resources/extensions/gsd/quick.js +45 -2
  41. package/dist/resources/extensions/gsd/session-forensics.js +11 -1
  42. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  43. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  44. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  45. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  46. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  47. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  48. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
  49. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  50. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  51. package/dist/resources/extensions/gsd/state.js +12 -611
  52. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  53. package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
  54. package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
  55. package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
  56. package/dist/resources/extensions/gsd/unit-registry.js +32 -4
  57. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
  58. package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
  59. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  60. package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
  61. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  62. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  63. package/dist/web/standalone/.next/BUILD_ID +1 -1
  64. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  65. package/dist/web/standalone/.next/build-manifest.json +3 -3
  66. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  67. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  68. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  87. package/dist/web/standalone/.next/server/app/index.html +1 -1
  88. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  89. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  90. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  91. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  96. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  98. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  99. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  100. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  101. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  102. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  103. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  104. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  105. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  106. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  107. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  108. package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
  109. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  110. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  111. package/package.json +1 -1
  112. package/packages/cloud-mcp-gateway/package.json +2 -2
  113. package/packages/contracts/dist/workflow.d.ts +1 -0
  114. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  115. package/packages/contracts/dist/workflow.js +2 -0
  116. package/packages/contracts/dist/workflow.js.map +1 -1
  117. package/packages/contracts/package.json +1 -1
  118. package/packages/daemon/package.json +4 -4
  119. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  120. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  121. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  122. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  123. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  124. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  125. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  126. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  127. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  128. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  129. package/packages/gsd-agent-core/dist/index.js +2 -0
  130. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  131. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  132. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  133. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  134. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  135. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  136. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  137. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  138. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  139. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  140. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  141. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  142. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  143. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  144. package/packages/gsd-agent-core/package.json +5 -5
  145. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  146. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  147. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  148. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  149. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  150. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  151. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  152. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  153. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  154. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  155. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
  156. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  157. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  158. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  159. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  160. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  161. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  162. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  163. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  164. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  165. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  166. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  167. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  170. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  171. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  173. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  174. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  175. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  176. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  177. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  178. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  179. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  180. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  182. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  183. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  185. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  187. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  189. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  190. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  191. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  193. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  194. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  195. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  196. package/packages/gsd-agent-modes/package.json +7 -7
  197. package/packages/mcp-server/README.md +1 -1
  198. package/packages/mcp-server/dist/server.d.ts +1 -1
  199. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  200. package/packages/mcp-server/dist/server.js +3 -3
  201. package/packages/mcp-server/dist/server.js.map +1 -1
  202. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  203. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  204. package/packages/mcp-server/dist/workflow-tools.js +34 -20
  205. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  206. package/packages/mcp-server/package.json +4 -4
  207. package/packages/native/package.json +1 -1
  208. package/packages/pi-agent-core/package.json +1 -1
  209. package/packages/pi-ai/package.json +1 -1
  210. package/packages/pi-coding-agent/README.md +3 -2
  211. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  212. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  213. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  214. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  215. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  216. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  217. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  218. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  219. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  220. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  221. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  222. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  223. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  224. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  225. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  226. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  227. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  228. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  229. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  230. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  231. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  232. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  234. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  237. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  238. package/packages/pi-coding-agent/package.json +7 -7
  239. package/packages/pi-tui/package.json +2 -2
  240. package/packages/rpc-client/package.json +2 -2
  241. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  242. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  243. package/pkg/dist/theme/theme-schema.js +1 -1
  244. package/pkg/dist/theme/theme-schema.js.map +1 -1
  245. package/pkg/dist/theme/theme.d.ts.map +1 -1
  246. package/pkg/dist/theme/theme.js +11 -7
  247. package/pkg/dist/theme/theme.js.map +1 -1
  248. package/pkg/package.json +1 -1
  249. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
  250. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
  251. package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
  252. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  253. package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
  254. package/src/resources/extensions/gsd/auto/session.ts +5 -0
  255. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  256. package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
  257. package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
  258. package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
  259. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  260. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  261. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
  262. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
  263. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
  264. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
  265. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -19
  266. package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
  267. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
  268. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  269. package/src/resources/extensions/gsd/commands-context.ts +18 -1
  270. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
  271. package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
  272. package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
  273. package/src/resources/extensions/gsd/db/queries.ts +79 -0
  274. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  275. package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
  276. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  277. package/src/resources/extensions/gsd/forensics.ts +2 -33
  278. package/src/resources/extensions/gsd/git-service.ts +5 -5
  279. package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
  280. package/src/resources/extensions/gsd/health-widget.ts +69 -32
  281. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  282. package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
  283. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
  284. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  285. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  286. package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
  287. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  288. package/src/resources/extensions/gsd/quick.ts +43 -2
  289. package/src/resources/extensions/gsd/session-forensics.ts +11 -1
  290. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  291. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  292. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  293. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  294. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  295. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  296. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
  297. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  298. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  299. package/src/resources/extensions/gsd/state.ts +32 -732
  300. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  301. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
  302. package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
  303. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  304. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
  305. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
  306. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
  307. package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
  308. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
  309. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
  310. package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
  311. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
  312. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
  313. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
  314. package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
  315. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
  316. package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
  317. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +119 -1
  318. package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
  319. package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
  320. package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
  321. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
  322. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  323. package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
  324. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
  325. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
  326. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
  327. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
  328. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  329. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
  330. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
  331. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
  332. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
  333. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  334. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  335. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
  336. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
  337. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
  338. package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
  339. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
  340. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
  341. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
  342. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  343. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  344. package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
  345. package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
  346. package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
  347. package/src/resources/extensions/gsd/unit-registry.ts +32 -4
  348. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
  349. package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
  350. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  351. package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
  352. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  353. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  354. package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
  355. package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
  356. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_buildManifest.js +0 -0
  357. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_ssgManifest.js +0 -0
@@ -7,7 +7,7 @@
7
7
 
8
8
  import test from "node:test";
9
9
  import assert from "node:assert/strict";
10
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
10
+ import { mkdtempSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
11
11
  import { tmpdir } from "node:os";
12
12
  import { join } from "node:path";
13
13
  import { classifyError, isTransient, isTransientNetworkError } from "../error-classifier.ts";
@@ -26,6 +26,10 @@ import { _buildCancelledUnitStopReason } from "../auto/phase-helpers.ts";
26
26
  import { _classifyZeroToolProviderMessageForTest } from "../auto/unit-phase.ts";
27
27
  import { autoSession } from "../auto-runtime-state.ts";
28
28
  import { getNextFallbackModel } from "../preferences.ts";
29
+ import { clearGuidedUnitContext, getGuidedUnitContext, setGuidedUnitContext } from "../guided-unit-context.ts";
30
+ import { initNotificationStore, readNotifications, _resetNotificationStore } from "../notification-store.ts";
31
+ import { installNotifyInterceptor } from "../bootstrap/notify-interceptor.ts";
32
+ import { extractTrace } from "../session-forensics.ts";
29
33
  // Zero-import module — imported by path rather than through the package
30
34
  // barrel to avoid pulling the full AgentSession / @gsd/pi-ai dep graph into
31
35
  // this unit test (see #4837).
@@ -488,6 +492,63 @@ test("pauseAutoForProviderError falls back to indefinite pause when not rate lim
488
492
  ]);
489
493
  });
490
494
 
495
+ test("agent_end retries when empty errorMessage has stream failure in content (#956)", async () => {
496
+ const originalSetTimeout = globalThis.setTimeout;
497
+ const notifications: Array<{ message: string; level?: string }> = [];
498
+ const sendMessageCalls: unknown[][] = [];
499
+ const timers: Array<{ fn: () => void; delay: number }> = [];
500
+
501
+ resetTransientRetryState();
502
+ autoSession.reset();
503
+ // handleAgentEnd returns at the isAutoActive() guard unless auto-mode is
504
+ // active. Set the minimum fields needed to reach the stopReason === "error"
505
+ // branch without requiring a real DB or worktree.
506
+ autoSession.active = true;
507
+ autoSession.currentUnit = { type: "execute-task", id: "M001/S01/T01", startedAt: Date.now() };
508
+
509
+ globalThis.setTimeout = ((fn: () => void, delay?: number) => {
510
+ timers.push({ fn, delay: delay ?? 0 });
511
+ return 0 as unknown as ReturnType<typeof setTimeout>;
512
+ }) as typeof setTimeout;
513
+
514
+ try {
515
+ await handleAgentEnd({
516
+ sendMessage: (...args: unknown[]) => {
517
+ sendMessageCalls.push(args);
518
+ },
519
+ } as any, {
520
+ messages: [{
521
+ role: "assistant",
522
+ stopReason: "error",
523
+ errorMessage: "",
524
+ content: [{ type: "text", text: "API Error: stream idle timeout - partial response received" }],
525
+ }],
526
+ } as any, {
527
+ model: { provider: "openai-codex", id: "gpt-5.5" },
528
+ ui: {
529
+ notify(message: string, level?: "info" | "warning" | "error" | "success") {
530
+ notifications.push({ message, level });
531
+ },
532
+ },
533
+ } as any);
534
+
535
+ assert.equal(timers.length, 1, "empty errorMessage stream failures should use the network retry path");
536
+ assert.equal(timers[0].delay, 3_000);
537
+ assert.deepEqual(notifications[0], {
538
+ message: "Network error on gpt-5.5: API Error: stream idle timeout - partial response received. Retry 1/2 in 3s...",
539
+ level: "warning",
540
+ });
541
+
542
+ timers[0].fn();
543
+ assert.equal(sendMessageCalls.length, 1);
544
+ assert.deepEqual(sendMessageCalls[0][1], { triggerTurn: true });
545
+ } finally {
546
+ globalThis.setTimeout = originalSetTimeout;
547
+ resetTransientRetryState();
548
+ autoSession.reset();
549
+ }
550
+ });
551
+
491
552
  test("rate-limit agent_end walks past unavailable fallback models before pausing (#716 follow-up)", async () => {
492
553
  const originalCwd = process.cwd();
493
554
  const originalSetTimeout = globalThis.setTimeout;
@@ -648,6 +709,139 @@ test("does not suppress deleted-worktree provider errors outside terminal comple
648
709
  assert.equal(event.message.stopReason, "error");
649
710
  });
650
711
 
712
+ test("manual guided discuss provider error records warning and activity marker (#944)", async () => {
713
+ const originalCwd = process.cwd();
714
+ const base = mkdtempSync(join(tmpdir(), "gsd-manual-discuss-error-"));
715
+ const notifications: Array<{ message: string; level?: string }> = [];
716
+ const sendMessageCalls: unknown[][] = [];
717
+
718
+ try {
719
+ autoSession.reset();
720
+ mkdirSync(join(base, ".git"), { recursive: true });
721
+ mkdirSync(join(base, ".gsd"), { recursive: true });
722
+ process.chdir(base);
723
+ initNotificationStore(base);
724
+
725
+ // Use base as the guided context path so gsdRoot(base) hits the fast path
726
+ // (.gsd exists at base directly) and doesn't need git to walk up from a
727
+ // subdirectory — the empty .git folder is not a real repo and git resolution
728
+ // from a child directory would return the wrong .gsd path.
729
+ setGuidedUnitContext(base, "discuss-slice");
730
+
731
+ const ctx = {
732
+ model: { provider: "openai-codex", id: "gpt-5.1-codex" },
733
+ modelRegistry: { getAvailable: () => [] },
734
+ ui: {
735
+ notify(message: string, level?: "info" | "warning" | "error" | "success") {
736
+ notifications.push({ message, level });
737
+ },
738
+ },
739
+ } as any;
740
+ installNotifyInterceptor(ctx);
741
+
742
+ const pi = {
743
+ sendMessage: (...args: unknown[]) => {
744
+ sendMessageCalls.push(args);
745
+ },
746
+ } as any;
747
+
748
+ await handleAgentEnd(pi, {
749
+ messages: [{
750
+ role: "assistant",
751
+ stopReason: "error",
752
+ errorMessage: "",
753
+ content: [{ type: "text", text: "stream idle timeout while saving summary" }],
754
+ }],
755
+ } as any, ctx);
756
+
757
+ assert.deepEqual(sendMessageCalls, [], "manual discuss terminal errors must not auto-retry or redispatch");
758
+ assert.equal(getGuidedUnitContext(base), null, "guided unit context must still be cleared after the turn");
759
+ assert.equal(notifications.length, 1);
760
+ assert.equal(notifications[0]?.level, "warning");
761
+ assert.match(notifications[0]?.message ?? "", /Manual \/gsd discuss discuss-slice ended with a provider error/);
762
+ assert.match(notifications[0]?.message ?? "", /openai-codex\/gpt-5\.1-codex/);
763
+ assert.match(notifications[0]?.message ?? "", /stream idle timeout/);
764
+
765
+ const persisted = readNotifications(base);
766
+ assert.equal(persisted.length, 1, "wrapped notify should persist exactly one warning notification");
767
+ assert.equal(persisted[0]?.severity, "warning");
768
+ assert.equal(persisted[0]?.source, "notify");
769
+
770
+ const activityDir = join(base, ".gsd", "activity");
771
+ const files = readdirSync(activityDir).filter((file) => file.endsWith(".jsonl"));
772
+ assert.equal(files.length, 1, "manual guided provider error should write one activity marker");
773
+ const entries = readFileSync(join(activityDir, files[0]!), "utf-8")
774
+ .split("\n")
775
+ .filter(Boolean)
776
+ .map((line) => JSON.parse(line));
777
+ const trace = extractTrace(entries);
778
+ assert.equal(trace.errors.length, 1);
779
+ assert.match(trace.errors[0] ?? "", /discuss-slice/);
780
+ assert.match(trace.errors[0] ?? "", /openai-codex\/gpt-5\.1-codex/);
781
+ assert.match(trace.errors[0] ?? "", /stream idle timeout/);
782
+ } finally {
783
+ clearGuidedUnitContext();
784
+ _resetNotificationStore();
785
+ autoSession.reset();
786
+ process.chdir(originalCwd);
787
+ rmSync(base, { recursive: true, force: true });
788
+ }
789
+ });
790
+
791
+ test("manual guided discuss user-cancel is not treated as a provider error (#944)", async () => {
792
+ const originalCwd = process.cwd();
793
+ const base = mkdtempSync(join(tmpdir(), "gsd-manual-discuss-cancel-"));
794
+ const guidedBase = join(base, "slice-work");
795
+ const notifications: Array<{ message: string; level?: string }> = [];
796
+
797
+ try {
798
+ autoSession.reset();
799
+ mkdirSync(join(base, ".git"), { recursive: true });
800
+ mkdirSync(join(base, ".gsd"), { recursive: true });
801
+ mkdirSync(guidedBase, { recursive: true });
802
+ process.chdir(base);
803
+ initNotificationStore(base);
804
+
805
+ setGuidedUnitContext(guidedBase, "discuss-slice");
806
+
807
+ const ctx = {
808
+ model: { provider: "anthropic", id: "claude-opus-4" },
809
+ modelRegistry: { getAvailable: () => [] },
810
+ ui: {
811
+ notify(message: string, level?: "info" | "warning" | "error" | "success") {
812
+ notifications.push({ message, level });
813
+ },
814
+ },
815
+ } as any;
816
+ installNotifyInterceptor(ctx);
817
+
818
+ const pi = { sendMessage: () => {} } as any;
819
+
820
+ await handleAgentEnd(pi, {
821
+ messages: [{
822
+ role: "assistant",
823
+ stopReason: "error",
824
+ errorMessage: "Request aborted by user",
825
+ content: [],
826
+ }],
827
+ } as any, ctx);
828
+
829
+ assert.deepEqual(notifications, [], "user-cancel stopReason=error must not emit a provider-error warning");
830
+ assert.equal(getGuidedUnitContext(guidedBase), null, "context must be cleared even for user-cancel");
831
+
832
+ // No activity directory should have been created
833
+ let activityExists = false;
834
+ try { readdirSync(join(base, ".gsd", "activity")); activityExists = true; } catch { /* expected */ }
835
+ assert.equal(activityExists, false, "user-cancel must not write an activity error marker");
836
+ } finally {
837
+ clearGuidedUnitContext();
838
+ _resetNotificationStore();
839
+ autoSession.reset();
840
+ process.chdir(originalCwd);
841
+ rmSync(base, { recursive: true, force: true });
842
+ }
843
+ });
844
+
651
845
  // ── resumeAutoAfterProviderDelay ────────────────────────────────────────────
652
846
 
653
847
  test("resumeAutoAfterProviderDelay restarts paused auto-mode from the recorded base path", async () => {
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Behavioural regression test for the milestone-closeout UAT gate —
3
+ * `readUatGateVerdict`.
4
+ *
5
+ * The gate (ADR-017: DB-authoritative UAT sign-off) reads a slice's UAT
6
+ * verdict from its ASSESSMENT artifact via the *canonical* expected path
7
+ * (`resolveSliceFile` + a path-keyed `getAssessment`). When a milestone
8
+ * artifact-layout migration orphans the ASSESSMENT markdown from that canonical
9
+ * path (e.g. `phases/…` → `milestones/…`), the gate used to return `null` and
10
+ * block milestone closure with "missing UAT PASS verdict" — even though the
11
+ * verdict was correctly recorded in the `assessments` table by
12
+ * `gsd_uat_result_save`.
13
+ *
14
+ * The DB fallback added to `readUatGateVerdict` consults the authoritative
15
+ * `assessments` table by (milestoneId, sliceId, scope='run-uat') identity,
16
+ * independent of path. These tests pin that behaviour.
17
+ */
18
+
19
+ import { describe, test, beforeEach, afterEach } from 'node:test';
20
+ import assert from 'node:assert/strict';
21
+ import * as fs from 'node:fs';
22
+ import * as path from 'node:path';
23
+ import * as os from 'node:os';
24
+
25
+ import {
26
+ openDatabase,
27
+ closeDatabase,
28
+ insertMilestone,
29
+ insertSlice,
30
+ insertAssessment,
31
+ } from '../gsd-db.ts';
32
+ import { readUatGateVerdict } from '../auto-dispatch.ts';
33
+
34
+ function tempDbPath(): string {
35
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-uat-gate-'));
36
+ return path.join(dir, 'test.db');
37
+ }
38
+
39
+ function cleanupDb(dbPath: string): void {
40
+ closeDatabase();
41
+ try { fs.rmSync(path.dirname(dbPath), { recursive: true, force: true }); } catch { /* */ }
42
+ }
43
+
44
+ const MID = 'M001';
45
+ const SLICE = 'S01';
46
+
47
+ /** Canonical on-disk ASSESSMENT path produced by `resolveSliceFile`. */
48
+ function canonicalAssessmentPath(basePath: string): string {
49
+ return path.join(basePath, '.gsd', 'milestones', MID, 'slices', SLICE, `${SLICE}-ASSESSMENT.md`);
50
+ }
51
+
52
+ /** An ASSESSMENT body that declares a runtime-executable UAT type and a PASS verdict. */
53
+ const RUNTIME_PASS_BODY = [
54
+ '---',
55
+ 'verdict: pass',
56
+ '---',
57
+ '',
58
+ '# S01 UAT Assessment',
59
+ '',
60
+ '## UAT Type',
61
+ '- UAT mode: runtime-executable',
62
+ '',
63
+ '## Result',
64
+ 'All checks passed.',
65
+ ].join('\n');
66
+
67
+ describe('readUatGateVerdict — DB fallback for orphaned ASSESSMENT', () => {
68
+ let dbPath: string;
69
+ let basePath: string;
70
+
71
+ beforeEach(() => {
72
+ dbPath = tempDbPath();
73
+ openDatabase(dbPath);
74
+ basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-uat-gate-proj-'));
75
+ insertMilestone({ id: MID });
76
+ insertSlice({ id: SLICE, milestoneId: MID });
77
+ });
78
+
79
+ afterEach(() => {
80
+ cleanupDb(dbPath);
81
+ try { fs.rmSync(basePath, { recursive: true, force: true }); } catch { /* */ }
82
+ });
83
+
84
+ test('returns pass when the ASSESSMENT is keyed by a legacy/orphaned path (the bug)', async () => {
85
+ // Reproduces milestone 15: `gsd_uat_result_save` wrote S01's assessment row
86
+ // under a now-migrated path; the canonical file never existed on disk and
87
+ // the `assessments.path` is not what `resolveSliceFile` computes.
88
+ insertAssessment({
89
+ // Deliberately non-canonical — a legacy `phases/…` path.
90
+ path: `.gsd/phases/01-some-feature/01-01-ASSESSMENT.md`,
91
+ milestoneId: MID,
92
+ sliceId: SLICE,
93
+ status: 'pass',
94
+ scope: 'run-uat',
95
+ fullContent: RUNTIME_PASS_BODY,
96
+ });
97
+
98
+ const result = await readUatGateVerdict(basePath, MID, SLICE);
99
+
100
+ assert.ok(result, 'expected the DB fallback to resolve a verdict, got null');
101
+ assert.equal(result!.verdict, 'pass');
102
+ });
103
+
104
+ test('the DB fallback derives uatType from the assessment body when no file exists', async () => {
105
+ insertAssessment({
106
+ path: `.gsd/phases/01-some-feature/01-01-ASSESSMENT.md`,
107
+ milestoneId: MID,
108
+ sliceId: SLICE,
109
+ status: 'pass',
110
+ scope: 'run-uat',
111
+ fullContent: RUNTIME_PASS_BODY,
112
+ });
113
+
114
+ const result = await readUatGateVerdict(basePath, MID, SLICE);
115
+
116
+ assert.ok(result);
117
+ assert.equal(result!.verdict, 'pass');
118
+ assert.equal(result!.uatType, 'runtime-executable');
119
+ });
120
+
121
+ test('canonical ASSESSMENT file on disk still resolves (regression guard)', async () => {
122
+ // When the file is present at the canonical path, the existing path-keyed
123
+ // lookup must resolve it without needing the fallback.
124
+ const file = canonicalAssessmentPath(basePath);
125
+ fs.mkdirSync(path.dirname(file), { recursive: true });
126
+ fs.writeFileSync(file, RUNTIME_PASS_BODY);
127
+ // Also seed the path-keyed assessments row, mirroring a normal save.
128
+ insertAssessment({
129
+ path: `.gsd/milestones/${MID}/slices/${SLICE}/${SLICE}-ASSESSMENT.md`,
130
+ milestoneId: MID,
131
+ sliceId: SLICE,
132
+ status: 'pass',
133
+ scope: 'run-uat',
134
+ fullContent: RUNTIME_PASS_BODY,
135
+ });
136
+
137
+ const result = await readUatGateVerdict(basePath, MID, SLICE);
138
+
139
+ assert.ok(result);
140
+ assert.equal(result!.verdict, 'pass');
141
+ assert.equal(result!.uatType, 'runtime-executable');
142
+ });
143
+
144
+ test('a roadmap-scoped assessment does NOT satisfy the UAT gate', async () => {
145
+ // `reassess-roadmap` writes roadmap-scoped assessments to the same
146
+ // S##-ASSESSMENT path; those must never be treated as a UAT verdict. The
147
+ // legacy-path fallback queries scope='run-uat', so a roadmap-only row is
148
+ // invisible and the gate returns null.
149
+ insertAssessment({
150
+ path: `.gsd/milestones/${MID}/slices/${SLICE}/${SLICE}-ASSESSMENT.md`,
151
+ milestoneId: MID,
152
+ sliceId: SLICE,
153
+ status: 'pass',
154
+ scope: 'roadmap',
155
+ fullContent: RUNTIME_PASS_BODY,
156
+ });
157
+
158
+ const result = await readUatGateVerdict(basePath, MID, SLICE);
159
+
160
+ assert.equal(result, null, 'roadmap-scoped assessments must not satisfy the UAT gate');
161
+ });
162
+
163
+ test('returns null when no assessment and no file exist (fallback does not hallucinate)', async () => {
164
+ const result = await readUatGateVerdict(basePath, MID, SLICE);
165
+ assert.equal(result, null);
166
+ });
167
+
168
+ test('surfaces a recorded non-pass verdict via the DB fallback', async () => {
169
+ // A failing verdict stored under a legacy path must surface (not be masked
170
+ // as "missing") so the gate's non-PASS branch can act on it.
171
+ insertAssessment({
172
+ path: `.gsd/phases/01-some-feature/01-01-ASSESSMENT.md`,
173
+ milestoneId: MID,
174
+ sliceId: SLICE,
175
+ status: 'fail',
176
+ scope: 'run-uat',
177
+ fullContent: RUNTIME_PASS_BODY.replace('verdict: pass', 'verdict: fail'),
178
+ });
179
+
180
+ const result = await readUatGateVerdict(basePath, MID, SLICE);
181
+
182
+ assert.ok(result);
183
+ assert.equal(result!.verdict, 'fail');
184
+ });
185
+ });
@@ -970,6 +970,93 @@ test("register-hooks agent_end does not re-arm deferred gate after workflow MCP
970
970
  });
971
971
  });
972
972
 
973
+ test("register-hooks message_update uses in-memory write-gate snapshot instead of disk reconcile", async (t) => {
974
+ const dir = makeTempDir("message-update-memory-snapshot");
975
+ const originalCwd = process.cwd();
976
+ const originalEnv = process.env.GSD_PERSIST_WRITE_GATE_STATE;
977
+ process.chdir(dir);
978
+ process.env.GSD_PERSIST_WRITE_GATE_STATE = "1";
979
+ resetWriteGateState(dir);
980
+ clearPendingAutoStart(dir);
981
+
982
+ const gateId = "depth_verification_M012_confirm";
983
+ const statePath = join(dir, ".gsd", "runtime", "write-gate-state.json");
984
+
985
+ t.after(() => {
986
+ try {
987
+ resetWriteGateState(dir);
988
+ clearPendingAutoStart(dir);
989
+ } finally {
990
+ if (originalEnv === undefined) {
991
+ delete process.env.GSD_PERSIST_WRITE_GATE_STATE;
992
+ } else {
993
+ process.env.GSD_PERSIST_WRITE_GATE_STATE = originalEnv;
994
+ }
995
+ process.chdir(originalCwd);
996
+ rmSync(dir, { recursive: true, force: true });
997
+ }
998
+ });
999
+
1000
+ const handlers = new Map<string, Array<(event: any, ctx?: any) => Promise<any> | any>>();
1001
+ const pi = {
1002
+ on(event: string, handler: (event: any, ctx?: any) => Promise<any> | any) {
1003
+ const existing = handlers.get(event) ?? [];
1004
+ existing.push(handler);
1005
+ handlers.set(event, existing);
1006
+ },
1007
+ } as any;
1008
+
1009
+ const notices: Array<{ text: string; level: string }> = [];
1010
+ const ctx = {
1011
+ cwd: dir,
1012
+ ui: { notify: (text: string, level: string) => notices.push({ text, level }) },
1013
+ } as any;
1014
+
1015
+ registerHooks(pi, []);
1016
+ setPendingAutoStart(dir, {
1017
+ basePath: dir,
1018
+ milestoneId: "M012",
1019
+ ctx,
1020
+ pi: { sendMessage: () => undefined } as any,
1021
+ });
1022
+
1023
+ mkdirSync(join(dir, ".gsd", "runtime"), { recursive: true });
1024
+ writeFileSync(statePath, JSON.stringify({
1025
+ verifiedDepthMilestones: ["M012"],
1026
+ verifiedApprovalGates: [gateId],
1027
+ activeQueuePhase: false,
1028
+ pendingGateId: null,
1029
+ }, null, 2), "utf-8");
1030
+
1031
+ const approvalMessage = {
1032
+ role: "assistant",
1033
+ content: [
1034
+ { type: "text", text: "Here is the milestone plan.\n\nDid I capture the project correctly?" },
1035
+ ],
1036
+ };
1037
+
1038
+ for (const handler of handlers.get("message_update") ?? []) {
1039
+ await handler({ message: approvalMessage }, ctx);
1040
+ }
1041
+
1042
+ assert.equal(
1043
+ notices.some((n) => /discuss-milestone M012 is waiting for your approval - pausing/.test(n.text)),
1044
+ true,
1045
+ "streaming hook must not suppress the pause from a disk-only verification",
1046
+ );
1047
+ assert.equal(
1048
+ shouldBlockContextArtifactSave("CONTEXT", "M012", null, dir).block,
1049
+ true,
1050
+ "streaming hook must not reconcile disk-only verification into the in-memory snapshot",
1051
+ );
1052
+
1053
+ for (const handler of handlers.get("agent_end") ?? []) {
1054
+ await handler({ messages: [] }, ctx);
1055
+ }
1056
+
1057
+ assert.equal(getPendingGate(dir), null, "agent_end still reconciles disk and suppresses durable re-arm");
1058
+ });
1059
+
973
1060
  // ── External-engine post-hoc gate replay (write-gate two-process sync) ──────
974
1061
  // On claude-code-cli, pi ingests the SDK turn's tool blocks after the workflow
975
1062
  // MCP child already executed them. The depth gate can therefore arrive at
@@ -177,6 +177,13 @@ test("auto Unit tool scope allows status/read helpers named by closeout prompts"
177
177
  }
178
178
  });
179
179
 
180
+ test("auto Unit tool scope allows reactive-execute status and diagnostic tools", () => {
181
+ for (const toolName of ["gsd_milestone_status", "gsd_exec"]) {
182
+ const result = shouldBlockAutoUnitToolCall("reactive-execute", toolName);
183
+ assert.equal(result.block, false, `reactive-execute should be able to call ${toolName}`);
184
+ }
185
+ });
186
+
180
187
  test("auto Unit tool scope blocks stale per-task planner in slice planning phases", () => {
181
188
  for (const unitType of ["plan-slice", "refine-slice", "replan-slice"]) {
182
189
  const result = shouldBlockAutoUnitToolCall(unitType, "gsd_plan_task");
@@ -48,6 +48,16 @@ function makeRecordingPi() {
48
48
  } as any;
49
49
  }
50
50
 
51
+ function makeRecordingCtx() {
52
+ const notifications: Array<{ message: string; level: string }> = [];
53
+ return {
54
+ notifications,
55
+ ui: {
56
+ notify: (message: string, level: string) => { notifications.push({ message, level }); },
57
+ },
58
+ } as any;
59
+ }
60
+
51
61
  // ═══ #1855: empty RecoveryContext (basePath undefined) crashes ════════════════
52
62
 
53
63
  {
@@ -111,6 +121,52 @@ function makeRecordingPi() {
111
121
  }
112
122
  }
113
123
 
124
+ // ═══ plan-slice timeout recovery verifies stale PLAN before advancing ═══════
125
+
126
+ {
127
+ console.log("\n=== plan-slice timeout recovery rejects stale placeholder PLAN ===");
128
+ const base = mkdtempSync(join(tmpdir(), "gsd-timeout-stale-plan-"));
129
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
130
+ mkdirSync(sliceDir, { recursive: true });
131
+
132
+ try {
133
+ openDatabase(join(base, ".gsd", "gsd.db"));
134
+ insertMilestone({ id: "M001", title: "Milestone", status: "active" });
135
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
136
+ writeFileSync(
137
+ join(sliceDir, "S01-PLAN.md"),
138
+ "# S01: Slice\n\n## Tasks\n\nPlanning was interrupted before task rows were persisted.\n",
139
+ "utf-8",
140
+ );
141
+
142
+ const ctx = makeRecordingCtx();
143
+ const pi = makeRecordingPi();
144
+ const result = await recoverTimedOutUnit(ctx, pi, "plan-slice", "M001/S01", "idle", {
145
+ basePath: base,
146
+ verbose: false,
147
+ currentUnitStartedAt: Date.now(),
148
+ unitRecoveryCount: new Map(),
149
+ });
150
+
151
+ assert.equal(result, "recovered", "invalid existing plan should enter steering recovery");
152
+ assert.equal(pi.messages.length, 1, "invalid existing plan should trigger steering instead of advancing");
153
+ assert.ok(
154
+ ctx.notifications.some((entry: { message: string }) => entry.message.includes("steering plan-slice M001/S01")),
155
+ "recovery notification should describe steering, not artifact advance",
156
+ );
157
+ assert.ok(
158
+ !ctx.notifications.some((entry: { message: string }) => entry.message.includes("artifact already exists on disk. Advancing.")),
159
+ "stale placeholder plan should not use the advance-on-existence path",
160
+ );
161
+ const runtime = JSON.parse(readFileSync(join(base, ".gsd", "runtime", "units", "plan-slice-M001-S01.json"), "utf-8"));
162
+ assert.equal(runtime.phase, "recovered", "stale placeholder plan must not be finalized");
163
+ assert.equal(runtime.recoveryAttempts, 1, "steering recovery attempt should be recorded");
164
+ } finally {
165
+ closeDatabase();
166
+ rmSync(base, { recursive: true, force: true });
167
+ }
168
+ }
169
+
114
170
  // ═══ #1855: valid RecoveryContext does not crash ═════════════════════════════
115
171
 
116
172
  {