@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
@@ -12,7 +12,7 @@ import { loadFile } from "./files.js";
12
12
  import { loadPrompt, inlineTemplate } from "./prompt-loader.js";
13
13
  import { deriveState } from "./state.js";
14
14
  import { invalidateAllCaches } from "./cache.js";
15
- import { gsdRoot, resolveMilestoneFile, resolveGsdRootFile, relGsdRootFile, } from "./paths.js";
15
+ import { gsdRoot, resolveMilestoneFile, resolveGsdRootFile, relGsdRootFile, relMilestoneFile, } from "./paths.js";
16
16
  import { readFileSync, writeFileSync, existsSync } from "node:fs";
17
17
  import { atomicWriteSync } from "./atomic-write.js";
18
18
  import { nativeAddPaths, nativeCommit } from "./native-git-bridge.js";
@@ -20,6 +20,9 @@ import { loadEffectiveGSDPreferences } from "./preferences.js";
20
20
  import { saveQueueOrder } from "./queue-order.js";
21
21
  import { findMilestoneIds, nextMilestoneId } from "./milestone-ids.js";
22
22
  import { isFutureMilestoneStatus } from "./status-guards.js";
23
+ const QUEUE_ARTIFACT_EXCERPT_MAX_CHARS = 20_000;
24
+ const QUEUE_EXISTING_MILESTONES_CONTEXT_MAX_CHARS = 120_000;
25
+ const QUEUE_CONTEXT_SECTION_SEPARATOR = "\n\n---\n\n";
23
26
  // ─── Queue Entry Point ──────────────────────────────────────────────────────
24
27
  /**
25
28
  * Queue future milestones via conversational intake.
@@ -215,7 +218,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
215
218
  if (contextFile) {
216
219
  const content = await loadFile(contextFile);
217
220
  if (content) {
218
- parts.push(`\n**Context:**\n${content.trim()}`);
221
+ parts.push(`\n**Context:**\n${summarizeArtifactForQueue(content, relMilestoneFile(basePath, mid, "CONTEXT"))}`);
219
222
  }
220
223
  }
221
224
  else {
@@ -224,7 +227,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
224
227
  if (draftFile) {
225
228
  const draftContent = await loadFile(draftFile);
226
229
  if (draftContent) {
227
- parts.push(`\n**Draft context available:**\n${draftContent.trim()}`);
230
+ parts.push(`\n**Draft context available:**\n${summarizeArtifactForQueue(draftContent, relMilestoneFile(basePath, mid, "CONTEXT-DRAFT"))}`);
228
231
  }
229
232
  }
230
233
  }
@@ -235,7 +238,7 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
235
238
  if (roadmapFile) {
236
239
  const content = await loadFile(roadmapFile);
237
240
  if (content) {
238
- parts.push(`\n**Roadmap:**\n${content.trim()}`);
241
+ parts.push(`\n**Roadmap:**\n${summarizeArtifactForQueue(content, relMilestoneFile(basePath, mid, "ROADMAP"))}`);
239
242
  }
240
243
  }
241
244
  }
@@ -249,7 +252,65 @@ export async function buildExistingMilestonesContext(basePath, milestoneIds, sta
249
252
  sections.push(`### Previous Queue Entries\nSource: \`${relGsdRootFile("QUEUE")}\`\n\n${queueContent.trim()}`);
250
253
  }
251
254
  }
252
- return sections.join("\n\n---\n\n");
255
+ return capExistingMilestonesContext(sections);
256
+ }
257
+ function summarizeArtifactForQueue(content, sourcePath, cap = QUEUE_ARTIFACT_EXCERPT_MAX_CHARS) {
258
+ const trimmed = content.trim();
259
+ if (trimmed.length <= cap) {
260
+ return `Source: \`${sourcePath}\`\n\n${trimmed}`;
261
+ }
262
+ const excerpt = trimmed.slice(0, cap).trimEnd();
263
+ const omittedChars = trimmed.length - excerpt.length;
264
+ return [
265
+ `Source: \`${sourcePath}\``,
266
+ "",
267
+ excerpt,
268
+ "",
269
+ `[Truncated ${omittedChars} chars. Read \`${sourcePath}\` for full content.]`,
270
+ ].join("\n");
271
+ }
272
+ function capExistingMilestonesContext(sections, cap = QUEUE_EXISTING_MILESTONES_CONTEXT_MAX_CHARS) {
273
+ const fullContext = sections.join(QUEUE_CONTEXT_SECTION_SEPARATOR);
274
+ if (fullContext.length <= cap)
275
+ return fullContext;
276
+ const notice = `[Existing milestones context truncated to ${cap} chars. Read source paths in this prompt or the corresponding .gsd artifacts for full details.]`;
277
+ const noticeSuffix = `${QUEUE_CONTEXT_SECTION_SEPARATOR}${notice}`;
278
+ const selected = [];
279
+ for (const section of sections) {
280
+ const candidate = [...selected, section].join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
281
+ if (candidate.length <= cap) {
282
+ selected.push(section);
283
+ continue;
284
+ }
285
+ break;
286
+ }
287
+ if (selected.length === sections.length) {
288
+ return selected.join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
289
+ }
290
+ const compactTail = sections.slice(selected.length).map(compactSectionForQueueBudget);
291
+ const hybrid = [...selected, ...compactTail].join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
292
+ if (hybrid.length <= cap)
293
+ return hybrid;
294
+ const compact = sections.map(compactSectionForQueueBudget);
295
+ const compactContext = compact.join(QUEUE_CONTEXT_SECTION_SEPARATOR) + noticeSuffix;
296
+ if (compactContext.length <= cap)
297
+ return compactContext;
298
+ return `${compactContext.slice(0, Math.max(0, cap - notice.length - 2)).trimEnd()}\n\n${notice}`;
299
+ }
300
+ function compactSectionForQueueBudget(section) {
301
+ const lines = section.split("\n");
302
+ const compact = [];
303
+ if (lines[0])
304
+ compact.push(lines[0]);
305
+ const statusLine = lines.find(line => line.startsWith("**Status:**"));
306
+ if (statusLine)
307
+ compact.push(statusLine);
308
+ const sourceLines = lines.filter(line => line.startsWith("Source: `"));
309
+ if (sourceLines.length > 0) {
310
+ compact.push("", "**Sources:**", ...sourceLines);
311
+ compact.push("", "[Artifact excerpts omitted due to total queue/rethink context budget.]");
312
+ }
313
+ return compact.join("\n");
253
314
  }
254
315
  // ─── Internal Helpers ───────────────────────────────────────────────────────
255
316
  /**
@@ -1,29 +1,61 @@
1
1
  // Project/App: gsd-pi
2
2
  // File Purpose: Always-on ambient health signal rendered below the editor.
3
- import { runProviderChecks, summariseProviderIssues } from "./doctor-providers.js";
3
+ import { execFile } from "node:child_process";
4
+ import { runProviderChecks, runProviderChecksAsync, summariseProviderIssues } from "./doctor-providers.js";
4
5
  import { runEnvironmentChecks, runEnvironmentChecksAsync } from "./doctor-environment.js";
5
6
  import { loadEffectiveGSDPreferences } from "./preferences.js";
6
- import { nativeIsRepo, nativeLastCommitEpoch, nativeGetCurrentBranch, nativeCommitSubject } from "./native-git-bridge.js";
7
+ import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
7
8
  import { loadLedgerFromDisk, getProjectTotals } from "./metrics.js";
8
9
  import { projectRoot } from "./commands/context.js";
9
10
  import { buildHealthLines, detectHealthWidgetProjectState, } from "./health-widget-core.js";
10
11
  export const HEALTH_WIDGET_ACTIVE_HINTS = " /gsd auto to run · /gsd status to inspect · /gsd report for snapshots · /gsd notifications for history · /gsd help";
12
+ const LAST_COMMIT_LOOKUP_TIMEOUT_MS = 3_000;
13
+ const REFRESH_INTERVAL_MS = 60_000;
14
+ const PROJECT_STATE_CACHE_TTL_MS = REFRESH_INTERVAL_MS;
11
15
  // ── Data loader ────────────────────────────────────────────────────────────────
12
- // Last-commit lookup is subprocess-backed (native-git-bridge → git spawns),
13
- // so it is treated like the other expensive checks: skipped on first paint,
14
- // run only by the background refresh.
15
- function loadLastCommitInfo(basePath) {
16
+ const projectStateCache = new Map();
17
+ export function getCachedProjectState(basePath, force) {
18
+ const now = Date.now();
19
+ const cached = projectStateCache.get(basePath);
20
+ if (!force && cached && now - cached.computedAt <= PROJECT_STATE_CACHE_TTL_MS) {
21
+ return cached.state;
22
+ }
23
+ const state = detectHealthWidgetProjectState(basePath);
24
+ projectStateCache.set(basePath, { state, computedAt: now });
25
+ return state;
26
+ }
27
+ function runHealthWidgetGit(basePath, args) {
28
+ return new Promise((resolve) => {
29
+ const child = execFile("git", args, {
30
+ cwd: basePath,
31
+ timeout: LAST_COMMIT_LOOKUP_TIMEOUT_MS,
32
+ encoding: "utf-8",
33
+ env: GIT_NO_PROMPT_ENV,
34
+ }, (err, stdout) => resolve(err ? null : String(stdout).trimEnd()));
35
+ child.on("error", () => resolve(null));
36
+ });
37
+ }
38
+ async function loadLastCommitInfoAsync(basePath) {
16
39
  try {
17
- if (nativeIsRepo(basePath)) {
18
- const branch = nativeGetCurrentBranch(basePath);
19
- const epoch = nativeLastCommitEpoch(basePath, branch || "HEAD");
20
- if (epoch > 0) {
21
- return { epoch, message: nativeCommitSubject(basePath, branch || "HEAD") || null };
22
- }
40
+ if ((await runHealthWidgetGit(basePath, ["rev-parse", "--git-dir"])) === null) {
41
+ return { epoch: null, message: null };
23
42
  }
43
+ const branch = await runHealthWidgetGit(basePath, ["branch", "--show-current"]);
44
+ const ref = branch || "HEAD";
45
+ const raw = await runHealthWidgetGit(basePath, ["log", "-1", "--format=%ct%x00%s", ref]);
46
+ if (!raw)
47
+ return { epoch: null, message: null };
48
+ const separator = raw.indexOf("\0");
49
+ const epochText = separator >= 0 ? raw.slice(0, separator) : raw;
50
+ const epoch = parseInt(epochText.trim(), 10) || 0;
51
+ if (epoch <= 0)
52
+ return { epoch: null, message: null };
53
+ const message = separator >= 0 ? raw.slice(separator + 1).trim() : "";
54
+ return { epoch, message: message || null };
55
+ }
56
+ catch {
57
+ return { epoch: null, message: null };
24
58
  }
25
- catch { /* non-fatal */ }
26
- return { epoch: null, message: null };
27
59
  }
28
60
  function loadHealthWidgetData(basePath, options) {
29
61
  // `includeChecks` gates the expensive subprocess-backed checks (provider +
@@ -38,7 +70,7 @@ function loadHealthWidgetData(basePath, options) {
38
70
  let environmentWarningCount = 0;
39
71
  let lastCommitEpoch = null;
40
72
  let lastCommitMessage = null;
41
- const projectState = detectHealthWidgetProjectState(basePath);
73
+ const projectState = getCachedProjectState(basePath, options?.forceProjectState);
42
74
  try {
43
75
  const prefs = loadEffectiveGSDPreferences();
44
76
  budgetCeiling = prefs?.preferences?.budget_ceiling;
@@ -66,12 +98,6 @@ function loadHealthWidgetData(basePath, options) {
66
98
  }
67
99
  catch { /* non-fatal */ }
68
100
  }
69
- // ── Last commit info ── (git spawns — gated like the other expensive checks)
70
- if (includeChecks) {
71
- const commit = loadLastCommitInfo(basePath);
72
- lastCommitEpoch = commit.epoch;
73
- lastCommitMessage = commit.message;
74
- }
75
101
  return {
76
102
  projectState,
77
103
  budgetCeiling,
@@ -85,17 +111,15 @@ function loadHealthWidgetData(basePath, options) {
85
111
  };
86
112
  }
87
113
  // Non-blocking variant used by the widget's background refresh: the cheap fields
88
- // come from the synchronous snapshot, then provider + environment checks are
89
- // layered in off the event-loop critical path (env checks run concurrently via
90
- // runEnvironmentChecksAsync). Keeps the always-on widget from stalling the UI on
91
- // its initial enrichment or its 60s refresh.
114
+ // come from the synchronous snapshot, then provider, environment, and last-commit
115
+ // checks are layered in off the event-loop critical path.
92
116
  async function loadHealthWidgetDataAsync(basePath) {
93
117
  const data = loadHealthWidgetData(basePath, { includeChecks: false });
94
118
  let providerIssue = data.providerIssue;
95
119
  let environmentErrorCount = 0;
96
120
  let environmentWarningCount = 0;
97
121
  try {
98
- providerIssue = summariseProviderIssues(runProviderChecks());
122
+ providerIssue = summariseProviderIssues(await runProviderChecksAsync());
99
123
  }
100
124
  catch { /* non-fatal */ }
101
125
  try {
@@ -108,7 +132,7 @@ async function loadHealthWidgetDataAsync(basePath) {
108
132
  }
109
133
  }
110
134
  catch { /* non-fatal */ }
111
- const commit = loadLastCommitInfo(basePath);
135
+ const commit = await loadLastCommitInfoAsync(basePath);
112
136
  return {
113
137
  ...data,
114
138
  providerIssue,
@@ -120,7 +144,6 @@ async function loadHealthWidgetDataAsync(basePath) {
120
144
  };
121
145
  }
122
146
  // ── Widget init ────────────────────────────────────────────────────────────────
123
- const REFRESH_INTERVAL_MS = 60_000;
124
147
  /**
125
148
  * Initialize the always-on gsd-health widget (belowEditor).
126
149
  * Call once from the extension entry point after context is available.
@@ -129,13 +152,16 @@ export function initHealthWidget(ctx) {
129
152
  if (!ctx.hasUI)
130
153
  return;
131
154
  const basePath = projectRoot();
155
+ // Re-init must reflect filesystem changes immediately; the TTL cache is for
156
+ // interval refreshes, not this one-off synchronous paint.
157
+ projectStateCache.delete(basePath);
132
158
  // String-array fallback — used in RPC mode (factory is a no-op there).
133
159
  // Skip the expensive provider/environment doctor checks here: this runs
134
160
  // synchronously on the interactive-startup path, where running them would
135
161
  // block first paint by ~0.9s (lsof/docker probes, otherwise run again
136
162
  // immediately by the factory below). The factory's async refresh fills in
137
163
  // real health once the screen is up.
138
- const initialData = loadHealthWidgetData(basePath, { includeChecks: false });
164
+ const initialData = loadHealthWidgetData(basePath, { includeChecks: false, forceProjectState: true });
139
165
  ctx.ui.setWidget("gsd-health", buildHealthLines(initialData), { placement: "belowEditor" });
140
166
  // Factory-based widget for TUI mode — replaces the string-array above
141
167
  ctx.ui.setWidget("gsd-health", (_tui, _theme) => {
@@ -40,8 +40,10 @@ export function dbPath(basePath) {
40
40
  * Used by the renderer to derive the phase number from the DB's milestone_id.
41
41
  */
42
42
  export function milestoneIdToPhaseNum(milestoneId) {
43
- // No $ anchor: accepts bare (M012) and team-suffixed (M012-abc123) IDs.
43
+ // No $ anchor: accepts bare (M012), team-suffixed (M012-abc123), and legacy numeric IDs.
44
44
  const m = milestoneId.match(/^M0*(\d+)/i);
45
+ if (!m && /^\d+$/.test(milestoneId))
46
+ return Number.parseInt(milestoneId, 10);
45
47
  return m ? Number.parseInt(m[1], 10) : 1;
46
48
  }
47
49
  /** Team-mode suffix from milestone ids like M001-abc123. */
@@ -12,7 +12,7 @@ import { readFileSync, existsSync, mkdirSync, statSync, unlinkSync } from "node:
12
12
  import { logWarning } from "./workflow-logger.js";
13
13
  import { isClosedStatus } from "./status-guards.js";
14
14
  import { dirname, join, relative } from "node:path";
15
- import { getAllMilestones, getMilestone, getMilestoneScopedArtifacts, getSliceScopedArtifacts, getMilestoneSlices, getSliceTasks, getTask, getSlice, insertArtifact, deleteArtifactByPath, getGateResults, } from "./gsd-db.js";
15
+ import { getAllMilestones, getMilestone, getMilestoneScopedArtifacts, getSliceScopedArtifacts, getMilestoneSlices, getSliceTasks, getTask, getSlice, insertArtifact, deleteArtifactByPath, getGateResults, isDbAvailable, } from "./gsd-db.js";
16
16
  import { resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTasksDir, gsdProjectionRoot, gsdRoot, buildMilestoneFileName, buildTaskFileName, } from "./paths.js";
17
17
  import { saveFile, clearParseCache, registerCacheClearCallback } from "./files.js";
18
18
  import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
@@ -518,13 +518,8 @@ export async function renderRoadmapFromDb(basePath, milestoneId) {
518
518
  * @returns true if the roadmap was written, false on skip/error
519
519
  */
520
520
  export async function renderRoadmapCheckboxes(basePath, milestoneId) {
521
- const slices = getMilestoneSlices(milestoneId);
522
- if (slices.length === 0) {
523
- process.stderr.write(`markdown-renderer: no slices found for milestone ${milestoneId}\n`);
524
- return false;
525
- }
526
- await renderRoadmapFromDb(basePath, milestoneId);
527
- return true;
521
+ const rendered = await renderRoadmapFromDb(basePath, milestoneId);
522
+ return !("skipped" in rendered);
528
523
  }
529
524
  /**
530
525
  * Project milestone-level artifacts (CONTEXT, RESEARCH, VALIDATION, etc.) from
@@ -604,11 +599,15 @@ function isAutoRecoveryPlaceholderPlan(content) {
604
599
  * projection (the 4S/0T-vs-5S/13T drift class). The artifacts table is an
605
600
  * output sink, never a render input.
606
601
  *
607
- * @returns true if the plan was written, false on skip/error
602
+ * @returns true if the plan was written, false when the DB slice has no tasks
603
+ * @throws when the DB connection is unavailable or the render write fails
608
604
  */
609
605
  export async function renderPlanCheckboxes(basePath, milestoneId, sliceId, outputPath) {
610
606
  const tasks = getSliceTasks(milestoneId, sliceId);
611
607
  if (tasks.length === 0) {
608
+ if (!isDbAvailable()) {
609
+ throw new Error(`database unavailable while rendering plan checkboxes for ${milestoneId}/${sliceId}`);
610
+ }
612
611
  process.stderr.write(`markdown-renderer: no tasks found for ${milestoneId}/${sliceId}\n`);
613
612
  return false;
614
613
  }
@@ -94,27 +94,48 @@ function getActiveDecisions() {
94
94
  return [];
95
95
  }
96
96
  }
97
- /**
98
- * True when a memory row has a `structured_fields` JSON payload containing
99
- * the given `markerKey: "value"` pair. Matches the LIKE pattern used by
100
- * `backfillDecisionsToMemories` so the scanner is consistent with the
101
- * backfill's idempotency check.
102
- */
103
- function memoryHasSourceMarker(markerKey, value) {
97
+ function emptyMemorySourceMarkers() {
98
+ return { decisionIds: new Set(), knowledgeIds: new Set() };
99
+ }
100
+ function getMemorySourceMarkers() {
101
+ const markers = emptyMemorySourceMarkers();
104
102
  if (!isDbAvailable())
105
- return false;
103
+ return markers;
106
104
  const adapter = _getAdapter();
107
105
  if (!adapter)
108
- return false;
106
+ return markers;
109
107
  try {
110
- const pattern = `%"${markerKey}":"${value}"%`;
111
- const row = adapter
112
- .prepare("SELECT 1 FROM memories WHERE structured_fields LIKE :pattern LIMIT 1")
113
- .get({ ":pattern": pattern });
114
- return row !== undefined;
108
+ const rows = adapter
109
+ .prepare("SELECT structured_fields FROM memories WHERE structured_fields IS NOT NULL")
110
+ .all();
111
+ for (const row of rows) {
112
+ collectMemorySourceMarker(markers, row["structured_fields"]);
113
+ }
115
114
  }
116
115
  catch {
117
- return false;
116
+ return markers;
117
+ }
118
+ return markers;
119
+ }
120
+ function collectMemorySourceMarker(markers, raw) {
121
+ if (typeof raw !== "string" || raw.length === 0)
122
+ return;
123
+ try {
124
+ const parsed = JSON.parse(raw);
125
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
126
+ return;
127
+ const fields = parsed;
128
+ const decisionId = fields["sourceDecisionId"];
129
+ if (typeof decisionId === "string" && decisionId.length > 0) {
130
+ markers.decisionIds.add(decisionId);
131
+ }
132
+ const knowledgeId = fields["sourceKnowledgeId"];
133
+ if (typeof knowledgeId === "string" && knowledgeId.length > 0) {
134
+ markers.knowledgeIds.add(knowledgeId);
135
+ }
136
+ }
137
+ catch {
138
+ return;
118
139
  }
119
140
  }
120
141
  // ─── Public API ──────────────────────────────────────────────────────────────
@@ -127,10 +148,14 @@ const SAMPLE_LIMIT = 5;
127
148
  export function scanConsolidationGaps(basePath) {
128
149
  // ── Decisions ────────────────────────────────────────────────────────
129
150
  const decisions = getActiveDecisions();
151
+ const knowledgeRows = parseKnowledgeRows(knowledgeMdContent(basePath));
152
+ const memorySourceMarkers = decisions.length > 0 || knowledgeRows.length > 0
153
+ ? getMemorySourceMarkers()
154
+ : emptyMemorySourceMarkers();
130
155
  const decisionSamples = [];
131
156
  let decisionMigrated = 0;
132
157
  for (const decision of decisions) {
133
- if (memoryHasSourceMarker("sourceDecisionId", decision.id)) {
158
+ if (memorySourceMarkers.decisionIds.has(decision.id)) {
134
159
  decisionMigrated += 1;
135
160
  continue;
136
161
  }
@@ -142,16 +167,14 @@ export function scanConsolidationGaps(basePath) {
142
167
  }
143
168
  }
144
169
  // ── KNOWLEDGE.md ─────────────────────────────────────────────────────
145
- const knowledgeRows = parseKnowledgeRows(knowledgeMdContent(basePath));
146
170
  const knowledgeByTable = { rules: 0, patterns: 0, lessons: 0 };
147
171
  const knowledgeSamples = [];
148
172
  let knowledgeMigrated = 0;
149
173
  for (const row of knowledgeRows) {
150
174
  knowledgeByTable[row.table] += 1;
151
- // Phase 6 will introduce a `sourceKnowledgeId` marker as part of the
152
- // KNOWLEDGE.md backfill. Until that path ships, this check returns
153
- // false for every row, which is the honest state of the consolidation.
154
- if (memoryHasSourceMarker("sourceKnowledgeId", row.id)) {
175
+ // KNOWLEDGE.md backfill writes `sourceKnowledgeId`; rows without that
176
+ // marker are still reported as consolidation gaps.
177
+ if (memorySourceMarkers.knowledgeIds.has(row.id)) {
155
178
  knowledgeMigrated += 1;
156
179
  continue;
157
180
  }
@@ -45,6 +45,27 @@ function scanHasExtraIdentities(a, b) {
45
45
  hasExtra(a.slices, b.slices) ||
46
46
  hasExtra(a.tasks, b.tasks));
47
47
  }
48
+ function paddedMilestoneId(id) {
49
+ return /^\d+$/.test(id) ? `M${id.padStart(3, "0")}` : null;
50
+ }
51
+ function replaceSetPrefix(values, from, to) {
52
+ for (const value of [...values]) {
53
+ if (value !== from && !value.startsWith(`${from}/`))
54
+ continue;
55
+ values.delete(value);
56
+ values.add(`${to}${value.slice(from.length)}`);
57
+ }
58
+ }
59
+ function alignNumericMarkdownIdsWithDb(markdownScan, dbScan) {
60
+ for (const dbId of dbScan.milestones) {
61
+ const paddedId = paddedMilestoneId(dbId);
62
+ if (!paddedId || dbScan.milestones.has(paddedId) || !markdownScan.milestones.has(paddedId))
63
+ continue;
64
+ replaceSetPrefix(markdownScan.milestones, paddedId, dbId);
65
+ replaceSetPrefix(markdownScan.slices, paddedId, dbId);
66
+ replaceSetPrefix(markdownScan.tasks, paddedId, dbId);
67
+ }
68
+ }
48
69
  /**
49
70
  * True when the DB holds any milestone/slice/task identity the markdown lacks —
50
71
  * i.e. a `/gsd recover --confirm` (markdown → DB) would DELETE authoritative DB
@@ -126,6 +147,7 @@ export async function checkMarkdownHierarchyAgainstDb(basePath) {
126
147
  refreshWorkflowDatabaseFromDisk();
127
148
  const dbScan = scanDbHierarchy();
128
149
  const beforeDb = dbScan.counts;
150
+ alignNumericMarkdownIdsWithDb(markdownScan, dbScan);
129
151
  // Discussion-phase scratch: a milestone dir with no ROADMAP and no DB row is
130
152
  // a pre-registration discussion artifact (CONTEXT/CONTEXT-DRAFT only — the
131
153
  // queued DB row is inserted only at discussion handoff). Treating it as
@@ -123,7 +123,8 @@ export function clearReservedMilestoneIds() {
123
123
  reservedMilestoneIds.clear();
124
124
  }
125
125
  // ─── Discovery ──────────────────────────────────────────────────────────────
126
- function scanMilestoneIdsFromDir(dir) {
126
+ function scanMilestoneIdsFromDir(basePath, dir) {
127
+ const legacyNumericIds = idsWithLegacyNumericDirs(basePath);
127
128
  return readdirSync(dir, { withFileTypes: true })
128
129
  .filter((d) => d.isDirectory())
129
130
  .map((d) => {
@@ -131,6 +132,12 @@ function scanMilestoneIdsFromDir(dir) {
131
132
  if (MILESTONE_ID_RE.test(d.name)) {
132
133
  return d.name;
133
134
  }
135
+ // Legacy recovery/imports may contain bare numeric milestone directories
136
+ // (for example `milestones/15/15-ROADMAP.md`). The DB can preserve that
137
+ // id, and rebuild renders it, so drift scans must see it too.
138
+ if (/^\d+$/.test(d.name)) {
139
+ return d.name;
140
+ }
134
141
  // Legacy layout: M001-abcdef-slug descriptor directories
135
142
  const legacyMatch = d.name.match(/^(M\d{3}(?:-[a-z0-9]{6})?)-/);
136
143
  if (legacyMatch) {
@@ -144,12 +151,35 @@ function scanMilestoneIdsFromDir(dir) {
144
151
  if (MILESTONE_ID_RE.test(fromSlug)) {
145
152
  return fromSlug;
146
153
  }
154
+ const numericId = String(phaseNum);
155
+ if (legacyNumericIds.has(numericId)) {
156
+ return numericId;
157
+ }
147
158
  return `M${String(phaseNum).padStart(3, "0")}`;
148
159
  }
149
160
  return null;
150
161
  })
151
162
  .filter((id) => id !== null);
152
163
  }
164
+ function idsWithLegacyNumericDirs(basePath) {
165
+ const legacyDir = join(gsdProjectionRoot(basePath), "milestones");
166
+ const ids = new Set();
167
+ try {
168
+ for (const entry of readdirSync(legacyDir, { withFileTypes: true })) {
169
+ if (entry.isDirectory() && /^\d+$/.test(entry.name))
170
+ ids.add(entry.name);
171
+ }
172
+ }
173
+ catch (err) {
174
+ // A missing legacy directory is the common, benign case (nothing to
175
+ // consult). Only surface a real failure when the directory exists but
176
+ // could not be read.
177
+ if (existsSync(legacyDir)) {
178
+ logWarning("engine", `idsWithLegacyNumericDirs: ${legacyDir} exists but readdirSync failed — ${getErrorMessage(err)}`);
179
+ }
180
+ }
181
+ return ids;
182
+ }
153
183
  /** Scan the milestones directory and return IDs sorted by queue order (or numeric fallback). */
154
184
  export function findMilestoneIds(basePath) {
155
185
  const root = gsdProjectionRoot(basePath);
@@ -163,7 +193,7 @@ export function findMilestoneIds(basePath) {
163
193
  const ids = new Set();
164
194
  for (const dir of dirs) {
165
195
  try {
166
- for (const id of scanMilestoneIdsFromDir(dir))
196
+ for (const id of scanMilestoneIdsFromDir(basePath, dir))
167
197
  ids.add(id);
168
198
  }
169
199
  catch (err) {
@@ -10,6 +10,8 @@ import { logWarning } from "./workflow-logger.js";
10
10
  import { resolveTasksDir } from "./paths.js";
11
11
  /** Large enough for unbounded milestone-history git log scans in big repos. */
12
12
  const GIT_LOG_MAX_BUFFER = 16 * 1024 * 1024;
13
+ const LOG_FIELD_SEPARATOR = "\x1f";
14
+ const LOG_RECORD_SEPARATOR = "\x1e";
13
15
  /**
14
16
  * Check whether a milestone produced implementation artifacts (non-`.gsd/`
15
17
  * files) in git history. The primary signal is the branch diff against the
@@ -179,7 +181,7 @@ function getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId) {
179
181
  // Primary: path-scoped log against .gsd/milestones/<id>. Fast and unbounded
180
182
  // by depth when .gsd/ is tracked in git.
181
183
  const scoped = scanGsdTaggedCommits(basePath, milestoneId, [
182
- "log", "--format=%H%x1f%B%x1e", "HEAD", "--", `.gsd/milestones/${milestoneId}`,
184
+ "log", "--full-diff", "--name-only", "--format=%x1e%H%x1f%B%x1f", "HEAD", "--", `.gsd/milestones/${milestoneId}`,
183
185
  ]);
184
186
  if (!scoped.ok)
185
187
  return scoped;
@@ -195,7 +197,7 @@ function getChangedFilesFromMilestoneTaggedCommits(basePath, milestoneId) {
195
197
  // reintroducing the rolling-depth failure class removed in #4699 where
196
198
  // milestone evidence aged out behind unrelated activity.
197
199
  const unscoped = scanGsdTaggedCommits(basePath, milestoneId, [
198
- "log", "--format=%H%x1f%B%x1e", "HEAD",
200
+ "log", "--name-only", "--format=%x1e%H%x1f%B%x1f", "HEAD",
199
201
  ]);
200
202
  if (!unscoped.ok)
201
203
  return scoped.matched ? scoped : unscoped;
@@ -280,8 +282,7 @@ function backfillChangedFilesFromUntaggedMilestoneCommits(basePath, milestoneId)
280
282
  continue;
281
283
  if (commitMessageHasGsdTrailer(record.message))
282
284
  continue;
283
- const commitFiles = getChangedFilesForCommit(basePath, record.hash);
284
- const implementationFiles = commitFiles.map(normalizeRepoPath).filter(isImplementationPath);
285
+ const implementationFiles = record.files.map(normalizeRepoPath).filter(isImplementationPath);
285
286
  if (implementationFiles.length === 0)
286
287
  continue;
287
288
  if (!implementationFiles.some((file) => hintSet.has(file)))
@@ -306,24 +307,28 @@ function backfillChangedFilesFromUntaggedMilestoneCommits(basePath, milestoneId)
306
307
  }
307
308
  }
308
309
  function getCommitRecords(basePath) {
309
- const logOutput = execFileSync("git", ["log", "--format=%H%x1f%P%x1f%cI%x1f%B%x1e", "HEAD"], {
310
+ const logOutput = execFileSync("git", ["log", "--name-only", "--format=%x1e%H%x1f%P%x1f%cI%x1f%B%x1f", "HEAD"], {
310
311
  cwd: basePath,
311
312
  stdio: ["ignore", "pipe", "pipe"],
312
313
  encoding: "utf-8",
313
314
  maxBuffer: GIT_LOG_MAX_BUFFER,
314
315
  });
315
316
  return logOutput
316
- .split("\x1e")
317
- .map((record) => record.trim())
317
+ .split(LOG_RECORD_SEPARATOR)
318
318
  .filter(Boolean)
319
319
  .flatMap((record) => {
320
- const parts = record.split("\x1f");
321
- if (parts.length < 4)
320
+ const parts = record.split(LOG_FIELD_SEPARATOR);
321
+ if (parts.length < 5)
322
322
  return [];
323
- const [hash, parents, committedAt, ...messageParts] = parts;
324
- return [{ hash: hash.trim(), parents: parents.trim(), committedAt: committedAt.trim(), message: messageParts.join("\x1f") }];
323
+ const [hash, parents, committedAt] = parts;
324
+ const files = parseNameOnlyFiles(parts.at(-1) ?? "");
325
+ const message = parts.slice(3, -1).join(LOG_FIELD_SEPARATOR);
326
+ return [{ hash: hash.trim(), parents: parents.trim(), committedAt: committedAt.trim(), message, files }];
325
327
  });
326
328
  }
329
+ function parseNameOnlyFiles(rawFiles) {
330
+ return rawFiles.split(/\r?\n/).map((file) => file.trim()).filter(Boolean);
331
+ }
327
332
  function isFullCommitSha(value) {
328
333
  return /^[0-9a-f]{40}$/i.test(value);
329
334
  }
@@ -336,23 +341,24 @@ function scanGsdTaggedCommits(basePath, milestoneId, gitArgs) {
336
341
  maxBuffer: GIT_LOG_MAX_BUFFER,
337
342
  });
338
343
  const records = logOutput
339
- .split("\x1e")
340
- .map((record) => record.trim())
344
+ .split(LOG_RECORD_SEPARATOR)
341
345
  .filter(Boolean)
342
346
  .flatMap((record) => {
343
- const sep = record.indexOf("\x1f");
344
- if (sep === -1)
347
+ const parts = record.split(LOG_FIELD_SEPARATOR);
348
+ if (parts.length < 3)
349
+ return [];
350
+ const hash = parts[0].trim();
351
+ if (!hash)
345
352
  return [];
346
- const hash = record.slice(0, sep).trim();
347
- const message = record.slice(sep + 1);
348
- return [{ hash, message }];
353
+ const files = parseNameOnlyFiles(parts.at(-1) ?? "");
354
+ const message = parts.slice(1, -1).join(LOG_FIELD_SEPARATOR);
355
+ return [{ message, files }];
349
356
  });
350
357
  const files = new Set();
351
358
  let matched = false;
352
- for (const { hash, message } of records) {
359
+ for (const { message, files: commitFiles } of records) {
353
360
  if (!commitMessageHasGsdTrailer(message))
354
361
  continue;
355
- const commitFiles = getChangedFilesForCommit(basePath, hash);
356
362
  if (!commitMatchesMilestone(basePath, message, milestoneId, commitFiles))
357
363
  continue;
358
364
  matched = true;