@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.965d1788

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 (411) hide show
  1. package/README.md +1 -1
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +11 -2
  4. package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
  5. package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
  6. package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
  7. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  8. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +1 -1
  9. package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +1 -1
  12. package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
  13. package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
  14. package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
  15. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
  16. package/dist/resources/extensions/gsd/auto-verification.js +5 -2
  17. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
  18. package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
  20. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
  21. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +16 -19
  22. package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
  23. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
  24. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
  25. package/dist/resources/extensions/gsd/closeout-consistency-gate.js +3 -1
  26. package/dist/resources/extensions/gsd/commands-context.js +19 -1
  27. package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
  28. package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
  29. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
  30. package/dist/resources/extensions/gsd/db/queries.js +60 -0
  31. package/dist/resources/extensions/gsd/db-workspace.js +55 -3
  32. package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
  33. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +3 -0
  34. package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
  35. package/dist/resources/extensions/gsd/flat-phase-migration.js +45 -0
  36. package/dist/resources/extensions/gsd/forensics.js +2 -32
  37. package/dist/resources/extensions/gsd/git-service.js +5 -4
  38. package/dist/resources/extensions/gsd/gitignore.js +3 -0
  39. package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
  40. package/dist/resources/extensions/gsd/health-widget.js +55 -29
  41. package/dist/resources/extensions/gsd/json-persistence.js +20 -0
  42. package/dist/resources/extensions/gsd/knowledge-backfill.js +18 -11
  43. package/dist/resources/extensions/gsd/layout-policy.js +3 -1
  44. package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
  45. package/dist/resources/extensions/gsd/memory-backfill.js +28 -21
  46. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
  47. package/dist/resources/extensions/gsd/metrics.js +4 -4
  48. package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
  49. package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
  50. package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
  51. package/dist/resources/extensions/gsd/orphan-stash-audit.js +92 -16
  52. package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
  53. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/quick.js +45 -2
  55. package/dist/resources/extensions/gsd/safety/file-change-validator.js +5 -2
  56. package/dist/resources/extensions/gsd/session-forensics.js +11 -1
  57. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  58. package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
  59. package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
  60. package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
  61. package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
  62. package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
  63. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
  64. package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
  65. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
  66. package/dist/resources/extensions/gsd/state.js +12 -611
  67. package/dist/resources/extensions/gsd/tools/complete-milestone.js +4 -2
  68. package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
  69. package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
  70. package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
  71. package/dist/resources/extensions/gsd/undo.js +43 -6
  72. package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
  73. package/dist/resources/extensions/gsd/unit-registry.js +32 -4
  74. package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
  75. package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
  76. package/dist/resources/extensions/gsd/verification-gate.js +7 -0
  77. package/dist/resources/extensions/gsd/workflow-manifest.js +4 -1
  78. package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
  79. package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
  80. package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
  81. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  82. package/dist/web/standalone/.next/BUILD_ID +1 -1
  83. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  84. package/dist/web/standalone/.next/build-manifest.json +3 -3
  85. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  86. package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
  87. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  98. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  103. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  104. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  105. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/index.html +1 -1
  107. package/dist/web/standalone/.next/server/app/index.rsc +2 -2
  108. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  109. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
  110. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  111. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  112. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  113. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  115. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  118. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  119. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  120. package/dist/web/standalone/.next/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
  121. package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
  122. package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
  123. package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
  124. package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
  125. package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
  126. package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
  127. package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
  128. package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
  129. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  130. package/package.json +1 -1
  131. package/packages/cloud-mcp-gateway/package.json +2 -2
  132. package/packages/contracts/dist/workflow.d.ts +1 -0
  133. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  134. package/packages/contracts/dist/workflow.js +2 -0
  135. package/packages/contracts/dist/workflow.js.map +1 -1
  136. package/packages/contracts/package.json +1 -1
  137. package/packages/daemon/package.json +4 -4
  138. package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
  139. package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
  140. package/packages/gsd-agent-core/dist/agent-session.js +3 -0
  141. package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
  142. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
  143. package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
  144. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
  145. package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
  146. package/packages/gsd-agent-core/dist/index.d.ts +2 -0
  147. package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
  148. package/packages/gsd-agent-core/dist/index.js +2 -0
  149. package/packages/gsd-agent-core/dist/index.js.map +1 -1
  150. package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
  151. package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
  152. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
  153. package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
  154. package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
  155. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
  156. package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
  157. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
  158. package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
  159. package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
  160. package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
  161. package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
  162. package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
  163. package/packages/gsd-agent-core/package.json +5 -5
  164. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
  165. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  166. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
  167. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  168. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
  169. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
  170. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
  171. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
  172. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
  173. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  174. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
  175. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  176. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
  177. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
  178. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
  179. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
  180. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
  181. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
  182. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
  183. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
  184. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
  185. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
  186. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
  187. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
  188. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
  189. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
  190. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
  191. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
  192. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
  193. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
  194. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
  195. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
  196. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
  197. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
  198. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
  199. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
  200. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
  201. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  202. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
  203. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
  204. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
  205. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
  206. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
  207. package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
  208. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
  209. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
  210. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
  211. package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
  212. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  213. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
  214. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  215. package/packages/gsd-agent-modes/package.json +7 -7
  216. package/packages/mcp-server/README.md +1 -1
  217. package/packages/mcp-server/dist/server.d.ts +1 -1
  218. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  219. package/packages/mcp-server/dist/server.js +3 -3
  220. package/packages/mcp-server/dist/server.js.map +1 -1
  221. package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
  222. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  223. package/packages/mcp-server/dist/workflow-tools.js +34 -20
  224. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  225. package/packages/mcp-server/package.json +4 -4
  226. package/packages/native/package.json +1 -1
  227. package/packages/pi-agent-core/package.json +1 -1
  228. package/packages/pi-ai/package.json +1 -1
  229. package/packages/pi-coding-agent/README.md +3 -2
  230. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
  231. package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
  232. package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
  233. package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
  234. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
  235. package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
  236. package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
  237. package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
  238. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
  239. package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
  240. package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
  241. package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
  242. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
  243. package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
  244. package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
  245. package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
  246. package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
  247. package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  248. package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
  249. package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  250. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
  251. package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
  252. package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
  253. package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
  254. package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
  255. package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
  256. package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
  257. package/packages/pi-coding-agent/package.json +7 -7
  258. package/packages/pi-tui/package.json +2 -2
  259. package/packages/rpc-client/package.json +2 -2
  260. package/pkg/dist/theme/theme-schema.d.ts +75 -75
  261. package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
  262. package/pkg/dist/theme/theme-schema.js +1 -1
  263. package/pkg/dist/theme/theme-schema.js.map +1 -1
  264. package/pkg/dist/theme/theme.d.ts.map +1 -1
  265. package/pkg/dist/theme/theme.js +11 -7
  266. package/pkg/dist/theme/theme.js.map +1 -1
  267. package/pkg/package.json +1 -1
  268. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
  269. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
  270. package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
  271. package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
  272. package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
  273. package/src/resources/extensions/gsd/auto/session.ts +6 -0
  274. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +3 -1
  275. package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
  276. package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
  277. package/src/resources/extensions/gsd/auto-post-unit.ts +1 -1
  278. package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
  279. package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
  280. package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
  281. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
  282. package/src/resources/extensions/gsd/auto-verification.ts +5 -1
  283. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
  284. package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
  285. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
  286. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
  287. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -19
  288. package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
  289. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
  290. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
  291. package/src/resources/extensions/gsd/closeout-consistency-gate.ts +2 -1
  292. package/src/resources/extensions/gsd/commands-context.ts +18 -1
  293. package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
  294. package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
  295. package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
  296. package/src/resources/extensions/gsd/db/queries.ts +79 -0
  297. package/src/resources/extensions/gsd/db-workspace.ts +61 -3
  298. package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
  299. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +3 -0
  300. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  301. package/src/resources/extensions/gsd/flat-phase-migration.ts +44 -0
  302. package/src/resources/extensions/gsd/forensics.ts +2 -33
  303. package/src/resources/extensions/gsd/git-service.ts +6 -5
  304. package/src/resources/extensions/gsd/gitignore.ts +3 -0
  305. package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
  306. package/src/resources/extensions/gsd/health-widget.ts +69 -32
  307. package/src/resources/extensions/gsd/json-persistence.ts +20 -0
  308. package/src/resources/extensions/gsd/knowledge-backfill.ts +16 -10
  309. package/src/resources/extensions/gsd/layout-policy.ts +2 -1
  310. package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
  311. package/src/resources/extensions/gsd/memory-backfill.ts +26 -20
  312. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
  313. package/src/resources/extensions/gsd/metrics.ts +4 -4
  314. package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
  315. package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
  316. package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
  317. package/src/resources/extensions/gsd/orphan-stash-audit.ts +108 -20
  318. package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
  319. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  320. package/src/resources/extensions/gsd/quick.ts +43 -2
  321. package/src/resources/extensions/gsd/safety/file-change-validator.ts +8 -2
  322. package/src/resources/extensions/gsd/session-forensics.ts +11 -1
  323. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
  324. package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
  325. package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
  326. package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
  327. package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
  328. package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
  329. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
  330. package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
  331. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
  332. package/src/resources/extensions/gsd/state.ts +32 -732
  333. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +4 -3
  334. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
  335. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
  336. package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
  337. package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
  338. package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
  339. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +28 -0
  340. package/src/resources/extensions/gsd/tests/complete-slice-reopen-handoff.test.ts +69 -0
  341. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
  342. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
  343. package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
  344. package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
  345. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +51 -0
  346. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
  347. package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
  348. package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
  349. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
  350. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
  351. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +40 -0
  352. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +37 -2
  353. package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
  354. package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +4 -0
  355. package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
  356. package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
  357. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +130 -3
  358. package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
  359. package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
  360. package/src/resources/extensions/gsd/tests/json-persistence-atomic.test.ts +47 -0
  361. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +35 -0
  362. package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
  363. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
  364. package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +31 -1
  365. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +6 -0
  366. package/src/resources/extensions/gsd/tests/metrics.test.ts +1 -0
  367. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
  368. package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
  369. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +91 -7
  370. package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
  371. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
  372. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
  373. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +10 -0
  374. package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
  375. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
  376. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
  377. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
  378. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
  379. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
  380. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
  381. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
  382. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
  383. package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
  384. package/src/resources/extensions/gsd/tests/undo.test.ts +74 -0
  385. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
  386. package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
  387. package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
  388. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +69 -4
  389. package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
  390. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +54 -0
  391. package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
  392. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
  393. package/src/resources/extensions/gsd/tools/complete-milestone.ts +3 -2
  394. package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
  395. package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
  396. package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
  397. package/src/resources/extensions/gsd/undo.ts +47 -6
  398. package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
  399. package/src/resources/extensions/gsd/unit-registry.ts +32 -4
  400. package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
  401. package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
  402. package/src/resources/extensions/gsd/verification-gate.ts +8 -0
  403. package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
  404. package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
  405. package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
  406. package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
  407. package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
  408. package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
  409. package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
  410. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → -ji77QJck2IKIwrGza1Bs}/_buildManifest.js +0 -0
  411. /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → -ji77QJck2IKIwrGza1Bs}/_ssgManifest.js +0 -0
@@ -259,6 +259,7 @@ test("initMetrics creates ledger, snapshotUnitMetrics persists across resets", (
259
259
  // Verify file content
260
260
  const raw = readFileSync(join(tmpBase, ".gsd", "metrics.json"), "utf-8");
261
261
  const parsed: MetricsLedger = JSON.parse(raw);
262
+ assert.equal(raw, JSON.stringify(parsed) + "\n", "metrics.json should be serialized compactly");
262
263
  assert.equal(parsed.version, 1);
263
264
  assert.equal(parsed.units.length, 1);
264
265
 
@@ -429,3 +429,73 @@ test("rebuildMarkdownProjectionsFromDb realigns markdown when DB holds extra row
429
429
  cleanup(base);
430
430
  }
431
431
  });
432
+
433
+ test("rebuildMarkdownProjectionsFromDb realigns markdown for numeric milestone IDs", async () => {
434
+ const base = makeBase();
435
+ try {
436
+ await writeGSDDirectory({ projectContent: "# P\n", decisionsContent: "", requirements: [], milestones: [] }, base);
437
+ assert.equal(await ensureDbOpen(base), true);
438
+ insertMilestone({ id: "15", title: "Numeric milestone", status: "active" });
439
+ insertSlice({
440
+ id: "S01",
441
+ milestoneId: "15",
442
+ title: "Numeric slice",
443
+ status: "pending",
444
+ risk: "medium",
445
+ depends: [],
446
+ demo: "d",
447
+ sequence: 1,
448
+ });
449
+ insertTask({
450
+ id: "T01",
451
+ sliceId: "S01",
452
+ milestoneId: "15",
453
+ title: "Numeric task",
454
+ status: "pending",
455
+ });
456
+
457
+ const before = await checkMarkdownHierarchyAgainstDb(base);
458
+ assert.equal(before.recoveryCommand, "/gsd rebuild markdown");
459
+
460
+ const { rebuildMarkdownProjectionsFromDb } = await import("../commands-maintenance.ts");
461
+ const rebuild = await rebuildMarkdownProjectionsFromDb(base);
462
+ assert.ok(rebuild.rendered > 0, "expected markdown projections to render");
463
+
464
+ const after = await checkMarkdownHierarchyAgainstDb(base);
465
+ assert.equal(after.action, "none");
466
+ assert.equal(after.reason, "in-sync");
467
+ assert.deepEqual(after.markdown, { milestones: 1, slices: 1, tasks: 1 });
468
+ assert.deepEqual(after.beforeDb, { milestones: 1, slices: 1, tasks: 1 });
469
+ } finally {
470
+ cleanup(base);
471
+ }
472
+ });
473
+
474
+ test("rebuildMarkdownProjectionsFromDb realigns markdown for planned zero-slice milestones", async () => {
475
+ const base = makeBase();
476
+ try {
477
+ await writeGSDDirectory({ projectContent: "# P\n", decisionsContent: "", requirements: [], milestones: [] }, base);
478
+ assert.equal(await ensureDbOpen(base), true);
479
+ insertMilestone({
480
+ id: "M016",
481
+ title: "Queued milestone",
482
+ status: "queued",
483
+ planning: { vision: "Defined but not sliced yet" },
484
+ });
485
+
486
+ const before = await checkMarkdownHierarchyAgainstDb(base);
487
+ assert.equal(before.recoveryCommand, "/gsd rebuild markdown");
488
+
489
+ const { rebuildMarkdownProjectionsFromDb } = await import("../commands-maintenance.ts");
490
+ const rebuild = await rebuildMarkdownProjectionsFromDb(base);
491
+ assert.ok(rebuild.rendered > 0, "expected markdown projections to render");
492
+
493
+ const after = await checkMarkdownHierarchyAgainstDb(base);
494
+ assert.equal(after.action, "none");
495
+ assert.equal(after.reason, "in-sync");
496
+ assert.deepEqual(after.markdown, { milestones: 1, slices: 0, tasks: 0 });
497
+ assert.deepEqual(after.beforeDb, { milestones: 1, slices: 0, tasks: 0 });
498
+ } finally {
499
+ cleanup(base);
500
+ }
501
+ });
@@ -36,6 +36,7 @@ import {
36
36
  import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
37
37
  import { AutoSession } from "../auto/session.ts";
38
38
  import { acquireSessionLock, releaseSessionLock } from "../session-lock.ts";
39
+ import { clearGSDPreferencesCache } from "../preferences.ts";
39
40
  import { invalidateAllCaches } from "../cache.ts";
40
41
  import { invalidateStateCache } from "../state.ts";
41
42
  import {
@@ -63,12 +64,15 @@ interface FixtureOptions {
63
64
  dispatch?: UnifiedRule["where"];
64
65
  /** Drop the gate_runs table after seeding so emitUokGate's insertGateRun throws (:538 path). */
65
66
  dropGateRuns?: boolean;
67
+ /** Write project preferences that disable UOK gate telemetry. */
68
+ disableUokGates?: boolean;
66
69
  }
67
70
 
68
71
  interface Fixture {
69
72
  base: string;
70
73
  session: AutoSession;
71
74
  orchestrator: ReturnType<typeof createAutoOrchestrator>;
75
+ getAvailableCalls(): number;
72
76
  cleanup(): void;
73
77
  }
74
78
 
@@ -82,6 +86,7 @@ function makeFixture(opts: FixtureOptions = {}): Fixture {
82
86
 
83
87
  invalidateAllCaches();
84
88
  invalidateStateCache();
89
+ clearGSDPreferencesCache();
85
90
  openDatabase(join(base, ".gsd", "gsd.db"));
86
91
  insertMilestone({ id: "M001", title: "Milestone", status: "active" });
87
92
  insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "active", risk: "low", depends: [], demo: "", sequence: 1 });
@@ -100,6 +105,13 @@ function makeFixture(opts: FixtureOptions = {}): Fixture {
100
105
  join(sliceDir, "S01-PLAN.md"),
101
106
  ["# S01: Slice", "", "**Goal:** g", "**Demo:** d", "", "## Tasks", "", "- [ ] **T01: Task** `est:1h`", ""].join("\n"),
102
107
  );
108
+ if (opts.disableUokGates) {
109
+ writeFileSync(
110
+ join(base, ".gsd", "PREFERENCES.md"),
111
+ "---\nuok:\n gates:\n enabled: false\n---\n",
112
+ );
113
+ clearGSDPreferencesCache();
114
+ }
103
115
 
104
116
  acquireSessionLock(base);
105
117
 
@@ -112,8 +124,19 @@ function makeFixture(opts: FixtureOptions = {}): Fixture {
112
124
  session.currentMilestoneId = "M001";
113
125
  session.resourceVersionOnStart = null;
114
126
 
127
+ let getAvailableCalls = 0;
115
128
  const ctx: OrchestratorContext = {
116
- ctx: { model: {}, modelRegistry: { getAll: () => [], getAvailable: () => [] }, ui: { notify() {} } } as never,
129
+ ctx: {
130
+ model: {},
131
+ modelRegistry: {
132
+ getAll: () => [],
133
+ getAvailable: () => {
134
+ getAvailableCalls += 1;
135
+ return [];
136
+ },
137
+ },
138
+ ui: { notify() {} },
139
+ } as never,
117
140
  pi: { getActiveTools: () => [] } as never,
118
141
  dispatchBasePath: base,
119
142
  runtimeBasePath: base,
@@ -140,8 +163,10 @@ function makeFixture(opts: FixtureOptions = {}): Fixture {
140
163
  base,
141
164
  session,
142
165
  orchestrator,
166
+ getAvailableCalls: () => getAvailableCalls,
143
167
  cleanup() {
144
168
  resetRegistry();
169
+ clearGSDPreferencesCache();
145
170
  try { releaseSessionLock(base); } catch { /* */ }
146
171
  try { closeDatabase(); } catch { /* */ }
147
172
  try { rmSync(base, { recursive: true, force: true }); } catch { /* */ }
@@ -210,6 +235,23 @@ test("advance() logs an engine warning when the uok gate emit fails (orchestrato
210
235
  assert.ok(uokFail!.context?.gateId, "the failing gate id must be captured in context");
211
236
  });
212
237
 
238
+ test("advance() resolves disabled uok gate flags once before gate emission", async (t) => {
239
+ const f = makeFixture({ dropGateRuns: true, disableUokGates: true });
240
+ t.after(() => f.cleanup());
241
+
242
+ const { logs } = await captureLogs(() => f.orchestrator.advance());
243
+
244
+ assert.equal(
245
+ logs.some((e) => e.component === "engine" && /uok gate emit failed/u.test(e.message)),
246
+ false,
247
+ "disabled uok gates must not construct the runner or write gate rows",
248
+ );
249
+ assert.ok(
250
+ f.getAvailableCalls() <= 2,
251
+ `uok gate preferences should be resolved once per advance, not once per emitted gate (getAvailable calls: ${f.getAvailableCalls()})`,
252
+ );
253
+ });
254
+
213
255
  // orchestrator.ts:637 — mergePendingCompleteMilestone catches a
214
256
  // rebuildMarkdownProjectionsFromDb failure after the system-owned milestone
215
257
  // merge and logs `markdown projection rebuild after settlement merge failed`.
@@ -1,6 +1,7 @@
1
1
  // gsd-pi + src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts
2
- // Regression: orphaned gsd-preflight-stash entries from completed milestones
3
- // must be auto-applied at startup so the user's pre-merge work returns.
2
+ // Regression coverage for orphaned gsd-preflight-stash entries from completed
3
+ // milestones: safe startup recovery for untracked work, manual warnings for
4
+ // tracked or dirty-tree cases that would silently mutate user state.
4
5
 
5
6
  import { describe, test, beforeEach, afterEach } from "node:test";
6
7
  import assert from "node:assert/strict";
@@ -37,6 +38,13 @@ function pushPreflightStash(repo: string, milestoneId: string, fileName: string,
37
38
  return marker;
38
39
  }
39
40
 
41
+ function pushTrackedPreflightStash(repo: string, milestoneId: string, fileName: string, content: string): string {
42
+ writeFileSync(join(repo, fileName), content);
43
+ const marker = `gsd-preflight-stash:${milestoneId}:42:1700000000000:tracked`;
44
+ git(repo, "stash", "push", "-m", `gsd-preflight-stash [${marker}]`);
45
+ return marker;
46
+ }
47
+
40
48
  describe("auditOrphanedPreflightStashes", () => {
41
49
  let repo: string;
42
50
 
@@ -83,6 +91,23 @@ describe("auditOrphanedPreflightStashes", () => {
83
91
  assert.match(list, /gsd-preflight-stash:M002:/);
84
92
  });
85
93
 
94
+ test("does not auto-apply a tracked orphan preflight stash onto a clean working tree", () => {
95
+ pushTrackedPreflightStash(repo, "M006", "seed.txt", "seed\ntracked pre-merge work\n");
96
+ assert.equal(readFileSync(join(repo, "seed.txt"), "utf-8"), "seed\n");
97
+
98
+ const result = auditOrphanedPreflightStashes(repo, () => true);
99
+
100
+ assert.equal(result.applied.length, 0);
101
+ assert.equal(result.warnings.length, 1);
102
+ assert.match(result.warnings[0], /not auto-applied/);
103
+ assert.match(result.warnings[0], /stash modifies tracked files/);
104
+ assert.match(result.warnings[0], /git stash apply stash@\{\d+\}/);
105
+ assert.equal(readFileSync(join(repo, "seed.txt"), "utf-8"), "seed\n");
106
+
107
+ const list = git(repo, "stash", "list");
108
+ assert.match(list, /gsd-preflight-stash:M006:/);
109
+ });
110
+
86
111
  test("ignores stashes whose milestone is not complete", () => {
87
112
  pushPreflightStash(repo, "M003", "wip.txt", "still-working\n");
88
113
 
@@ -115,23 +140,25 @@ describe("auditOrphanedPreflightStashes", () => {
115
140
  assert.match(result.warnings[0], /db unavailable/);
116
141
  });
117
142
 
118
- test("collects a warning when stash apply fails (conflicting working tree)", () => {
119
- // Push a stash containing a change to seed.txt; then dirty seed.txt with
120
- // a conflicting modification before the audit runs so apply fails.
143
+ test("collects a warning instead of applying when the working tree is not clean", () => {
144
+ // Push a stash containing a change to seed.txt; then dirty seed.txt before
145
+ // the audit runs. Startup recovery must leave the tree untouched and ask
146
+ // the user to apply the retained backup stash manually.
121
147
  writeFileSync(join(repo, "seed.txt"), "stashed\n");
122
148
  const marker = `gsd-preflight-stash:M005:42:1700:zz`;
123
149
  git(repo, "stash", "push", "-m", `gsd-preflight-stash [${marker}]`);
124
150
 
125
- // Dirty the working tree so apply will conflict.
126
151
  writeFileSync(join(repo, "seed.txt"), "conflicting modification\n");
127
152
 
128
153
  const result = auditOrphanedPreflightStashes(repo, () => true);
129
154
 
130
155
  assert.equal(result.applied.length, 0);
131
156
  assert.equal(result.warnings.length, 1);
132
- assert.match(result.warnings[0], /Could not apply orphaned preflight stash/);
157
+ assert.match(result.warnings[0], /not auto-applied/);
158
+ assert.match(result.warnings[0], /working tree not clean/);
133
159
  assert.match(result.warnings[0], /M005/);
134
160
  assert.match(result.warnings[0], /git stash apply/);
161
+ assert.equal(readFileSync(join(repo, "seed.txt"), "utf-8"), "conflicting modification\n");
135
162
 
136
163
  const list = git(repo, "stash", "list");
137
164
  assert.match(list, /gsd-preflight-stash:M005:/);
@@ -169,6 +196,63 @@ describe("auditOrphanedPreflightStashes", () => {
169
196
  "second run must NOT warn — files are already restored from first run",
170
197
  );
171
198
  });
199
+
200
+ test("warns instead of silently skipping when only some stash untracked files were restored", () => {
201
+ // Cursor Bugbot caught: isAlreadyRestoredUntrackedStatus treated a dirty
202
+ // tree as fully restored when every *current* untracked path appeared in
203
+ // the stash, without requiring every *stash* untracked path to be present.
204
+ // A partial restore (only some files recovered) was misread as the
205
+ // idempotent steady state, so the audit no-oped and the remaining pre-merge
206
+ // files stayed missing with no warning.
207
+ writeFileSync(join(repo, "first.txt"), "first\n");
208
+ writeFileSync(join(repo, "second.txt"), "second\n");
209
+ const marker = `gsd-preflight-stash:M007:42:1700000000000:partial`;
210
+ git(repo, "stash", "push", "--include-untracked", "-m", `gsd-preflight-stash [${marker}]`);
211
+
212
+ // Both files are stashed away.
213
+ assert.equal(existsSync(join(repo, "first.txt")), false);
214
+ assert.equal(existsSync(join(repo, "second.txt")), false);
215
+
216
+ // Simulate a partial restore: only first.txt came back; second.txt is still
217
+ // missing from the working tree.
218
+ writeFileSync(join(repo, "first.txt"), "first\n");
219
+
220
+ const result = auditOrphanedPreflightStashes(repo, () => true);
221
+
222
+ // Must NOT be treated as already-restored: second.txt is still missing, so
223
+ // the audit must surface a manual-recovery warning rather than no-op.
224
+ assert.equal(result.applied.length, 0);
225
+ assert.equal(result.warnings.length, 1, "partial restore must warn, not silently no-op");
226
+ assert.match(result.warnings[0], /not auto-applied/);
227
+ assert.match(result.warnings[0], /working tree not clean/);
228
+ assert.match(result.warnings[0], /M007/);
229
+ assert.match(result.warnings[0], /git stash apply/);
230
+
231
+ // The stash entry must remain so the user can recover second.txt manually.
232
+ const list = git(repo, "stash", "list");
233
+ assert.match(list, /gsd-preflight-stash:M007:/);
234
+ });
235
+
236
+ test("fully restored multi-file untracked stash stays a silent no-op", () => {
237
+ // Guards the other direction of the set-equality fix: when EVERY stash
238
+ // untracked file is present in the working tree, the genuine idempotent
239
+ // steady state must still be recognized and skipped without warning, even
240
+ // when the stash captured more than one file.
241
+ writeFileSync(join(repo, "alpha.txt"), "alpha\n");
242
+ writeFileSync(join(repo, "beta.txt"), "beta\n");
243
+ const marker = `gsd-preflight-stash:M008:42:1700000000000:multi`;
244
+ git(repo, "stash", "push", "--include-untracked", "-m", `gsd-preflight-stash [${marker}]`);
245
+
246
+ const first = auditOrphanedPreflightStashes(repo, () => true);
247
+ assert.equal(first.applied.length, 1, "first run applies both files");
248
+ assert.equal(first.warnings.length, 0);
249
+ assert.equal(existsSync(join(repo, "alpha.txt")), true);
250
+ assert.equal(existsSync(join(repo, "beta.txt")), true);
251
+
252
+ const second = auditOrphanedPreflightStashes(repo, () => true);
253
+ assert.equal(second.applied.length, 0, "second run skips");
254
+ assert.equal(second.warnings.length, 0, "fully restored multi-file stash must not warn");
255
+ });
172
256
  });
173
257
 
174
258
  test("_isAlreadyRestoredApplyError detects the git stash apply already-exists error", () => {
@@ -145,6 +145,32 @@ describe("parallel-research-slices dispatch rule", () => {
145
145
  }
146
146
  });
147
147
 
148
+ test("dispatches parallel research for S01 even when milestone research exists", async () => {
149
+ writeRoadmap(base, "M001", [
150
+ { id: "S01", title: "Alpha" },
151
+ { id: "S02", title: "Beta" },
152
+ ]);
153
+ writeFileSync(
154
+ join(base, ".gsd", "milestones", "M001", "M001-RESEARCH.md"),
155
+ "# Milestone Research\n",
156
+ "utf-8",
157
+ );
158
+
159
+ const action = await resolveDispatch({
160
+ basePath: base,
161
+ mid: "M001",
162
+ midTitle: "Parallel Research Milestone",
163
+ state: baseState(),
164
+ prefs: undefined,
165
+ });
166
+
167
+ assert.equal(action.action, "dispatch");
168
+ if (action.action === "dispatch") {
169
+ assert.equal(action.unitType, "research-slice");
170
+ assert.equal(action.unitId, "M001/parallel-research");
171
+ }
172
+ });
173
+
148
174
  test("does not dispatch parallel research with only one ready slice", async () => {
149
175
  writeRoadmap(base, "M001", [{ id: "S01", title: "Alpha" }]);
150
176
 
@@ -54,7 +54,8 @@ const ALLOWED_IMPORTERS = new Set([
54
54
  // explicit degraded-mode fallback when DB has no task rows (warns on use)
55
55
  "gsd/reactive-graph.ts",
56
56
  // recovery path: explicit pre-migration/DB-unavailable fallback branches
57
- "gsd/auto-recovery.ts",
57
+ // (verifyExpectedArtifact extracted here from auto-recovery.ts)
58
+ "gsd/artifact-verification.ts",
58
59
  // diagnostics-only surfaces: report on projections, make no dispatch decisions
59
60
  "gsd/doctor.ts",
60
61
  // display/telemetry-only surfaces
@@ -22,7 +22,7 @@ import { invalidateAllCaches } from "../cache.ts";
22
22
  import type { GSDState } from "../types.ts";
23
23
 
24
24
  const PARALLEL_RESEARCH_RULE = "planning (multiple slices need research) → parallel-research-slices";
25
- const SINGLE_RESEARCH_RULE = "planning (no research, not S01) → research-slice";
25
+ const SINGLE_RESEARCH_RULE = "planning (no research) → research-slice";
26
26
  const VALIDATE_RULE = "validating-milestone → validate-milestone";
27
27
 
28
28
  // ─── Fixture helpers ──────────────────────────────────────────────────────
@@ -94,6 +94,16 @@ test("plan-slice prompt: absence checks use negated quiet searches", () => {
94
94
  assert.ok(result.includes("count commands exit 1 when they find zero matches"));
95
95
  });
96
96
 
97
+ test("plan-slice prompt: npm package existence rule is stated during generation", () => {
98
+ const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
99
+ assert.ok(result.includes("reference only packages that exist on the public npm registry"));
100
+ assert.ok(result.includes("remove or correct any package name that is not real"));
101
+ // The post-unit pre-execution gate parses package names from require/import
102
+ // patterns too, not just install commands, so the rule must cover that scope.
103
+ assert.ok(result.includes("require('...')"));
104
+ assert.ok(result.includes("import ... from '...'"));
105
+ });
106
+
97
107
  test("plan-slice prompt: footer references gsd_plan_slice tool, not direct write", () => {
98
108
  const result = loadPrompt("plan-slice", { ...BASE_VARS, commitInstruction: "Do not commit." });
99
109
  assert.ok(
@@ -6,7 +6,12 @@ import { mkdtempSync, readFileSync, rmSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
 
9
- import { buildCategorySummaries, handlePrefsWizard } from "../commands-prefs-wizard.ts";
9
+ import {
10
+ buildCategorySummaries,
11
+ handlePrefsWizard,
12
+ serializePreferencesToFrontmatter,
13
+ } from "../commands-prefs-wizard.ts";
14
+ import { parsePreferencesMarkdown, validatePreferences } from "../preferences.ts";
10
15
  import { KNOWN_PREFERENCE_KEYS } from "../preferences-types.ts";
11
16
 
12
17
  const PREF_SAMPLE_VALUES: Record<string, unknown> = {
@@ -103,6 +108,54 @@ const PREF_SAMPLE_VALUES: Record<string, unknown> = {
103
108
  },
104
109
  };
105
110
 
111
+ test("prefs serializer preserves nested hook on_block objects", () => {
112
+ const frontmatter = serializePreferencesToFrontmatter({
113
+ post_unit_hooks: [{
114
+ name: "plan-review",
115
+ after: ["execute-task"],
116
+ prompt: "write PLAN-REVIEW.md",
117
+ artifact: "PLAN-REVIEW.md",
118
+ criticality: "blocking",
119
+ on_block: { action: "pause" },
120
+ }],
121
+ });
122
+
123
+ assert.doesNotMatch(frontmatter, /\[object Object\]/);
124
+ assert.ok(frontmatter.includes(" on_block:\n action: pause\n"));
125
+
126
+ const parsed = parsePreferencesMarkdown(`---\n${frontmatter}---\n`);
127
+ assert.notEqual(parsed, null);
128
+
129
+ const { errors, preferences } = validatePreferences(parsed!);
130
+ assert.deepEqual(errors, []);
131
+ assert.deepEqual(preferences.post_unit_hooks?.[0]?.on_block, { action: "pause" });
132
+ });
133
+
134
+ test("prefs serializer correctly indents nested object when it is the first array-item property", () => {
135
+ // on_block is the FIRST property — exercises the first-key-is-object branch
136
+ const frontmatter = serializePreferencesToFrontmatter({
137
+ post_unit_hooks: [{
138
+ on_block: { action: "pause" }, // first property — exercises the first-key-is-object branch
139
+ name: "gate", // required fields come after so on_block stays first
140
+ after: ["execute-task"],
141
+ prompt: "review output",
142
+ }],
143
+ });
144
+
145
+ assert.doesNotMatch(frontmatter, /\[object Object\]/);
146
+ // Children of on_block must be indented deeper than on_block itself (not siblings)
147
+ assert.ok(
148
+ frontmatter.includes(" - on_block:\n action: pause\n"),
149
+ `Expected on_block children indented as nested YAML, got:\n${frontmatter}`,
150
+ );
151
+ // Sanity: parse + validate round-trip
152
+ const parsed = parsePreferencesMarkdown(`---\n${frontmatter}---\n`);
153
+ assert.notEqual(parsed, null);
154
+ const { errors, preferences } = validatePreferences(parsed!);
155
+ assert.deepEqual(errors, []);
156
+ assert.deepEqual(preferences.post_unit_hooks?.[0]?.on_block, { action: "pause" });
157
+ });
158
+
106
159
  test("prefs wizard save path preserves every known preference key", async () => {
107
160
  const missingSamples = [...KNOWN_PREFERENCE_KEYS].filter((key) => !(key in PREF_SAMPLE_VALUES));
108
161
  assert.deepEqual(missingSamples, [], "test fixture must cover every known preference key");
@@ -17,7 +17,8 @@ import {
17
17
  getSlice,
18
18
  } from "../gsd-db.ts";
19
19
  import { autoHealSketchFlags } from "../state-reconciliation/drift/sketch-flag.ts";
20
- import { deriveState, deriveStateFromDb } from "../state.ts";
20
+ import { deriveState, deriveStateFromDb, invalidateStateCache } from "../state.ts";
21
+ import { reconcileBeforeDispatch } from "../state-reconciliation.ts";
21
22
  import { resolveDispatch } from "../auto-dispatch.ts";
22
23
  import type { DispatchContext } from "../auto-dispatch.ts";
23
24
 
@@ -174,7 +175,7 @@ test("ADR-011: refining + flag flipped OFF mid-milestone → falls through to pl
174
175
  }
175
176
  });
176
177
 
177
- test("ADR-011: existing PLAN heals stale sketch flag via deriveStateFromDb (progressive_planning ON)", async (t) => {
178
+ test("ADR-011: existing PLAN + stale sketch flag heals via reconcileBeforeDispatch, then dispatch routes past refining (progressive_planning ON)", async (t) => {
178
179
  const originalCwd = process.cwd();
179
180
  const base = makeFixtureBase();
180
181
  t.after(() => cleanup(base, originalCwd));
@@ -188,11 +189,22 @@ test("ADR-011: existing PLAN heals stale sketch flag via deriveStateFromDb (prog
188
189
  );
189
190
  process.chdir(base);
190
191
 
191
- // deriveStateFromDb auto-heals the stale sketch flag when PLAN.md exists,
192
- // regardless of the progressive_planning preference.
192
+ // Derivation is pure (ADR-017): a stale sketch flag (PLAN on disk but
193
+ // is_sketch=1) yields 'refining' and must NOT mutate the DB. Healing is
194
+ // owned by reconcileBeforeDispatch, which runs before every dispatch.
195
+ invalidateStateCache();
196
+ const beforeReconcile = await deriveStateFromDb(base);
197
+ assert.equal(beforeReconcile.activeSlice?.id, "S02");
198
+ assert.equal(beforeReconcile.phase, "refining", "derive alone must not heal the stale sketch flag");
199
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 1, "derive must not mutate is_sketch");
200
+
201
+ const reconcile = await reconcileBeforeDispatch(base);
202
+ assert.equal(reconcile.ok, true);
203
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "reconcile clears the stale sketch flag once PLAN exists");
204
+
205
+ invalidateStateCache();
193
206
  const state = await deriveStateFromDb(base);
194
- assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "derive: flag cleared when PLAN exists");
195
- assert.equal(state.phase, "planning", "derive: phase advances past refining once flag is healed");
207
+ assert.equal(state.phase, "planning", "re-derive advances past refining once the flag is healed");
196
208
 
197
209
  const ctx: DispatchContext = {
198
210
  basePath: base,
@@ -229,7 +241,7 @@ test("ADR-011: autoHealSketchFlags flips is_sketch=0 when PLAN file exists", asy
229
241
  assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "post-heal: flag cleared");
230
242
  });
231
243
 
232
- test("ADR-011: deriveStateFromDb auto-heals stale sketch flag when PLAN exists", async (t) => {
244
+ test("ADR-011: reconcileBeforeDispatch auto-heals stale sketch flag when PLAN exists", async (t) => {
233
245
  const originalCwd = process.cwd();
234
246
  const base = makeFixtureBase();
235
247
  t.after(() => cleanup(base, originalCwd));
@@ -244,12 +256,23 @@ test("ADR-011: deriveStateFromDb auto-heals stale sketch flag when PLAN exists",
244
256
  );
245
257
  process.chdir(base);
246
258
 
247
- const state = await deriveStateFromDb(base);
248
- assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "derive should clear stale is_sketch");
249
- assert.equal(state.phase, "planning", "state should advance past refining once stale flag is healed");
259
+ // Pure derivation reports the stale flag as 'refining' without repairing it.
260
+ invalidateStateCache();
261
+ const stale = await deriveStateFromDb(base);
262
+ assert.equal(stale.phase, "refining", "derive alone must not heal the stale flag");
263
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 1, "is_sketch unchanged before reconcile");
264
+
265
+ // Reconciliation owns the repair (ADR-017 stale-sketch-flag drift handler).
266
+ const reconcile = await reconcileBeforeDispatch(base);
267
+ assert.equal(reconcile.ok, true);
268
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "reconcile should clear stale is_sketch");
269
+
270
+ invalidateStateCache();
271
+ const healed = await deriveStateFromDb(base);
272
+ assert.equal(healed.phase, "planning", "state advances past refining once the stale flag is healed");
250
273
  });
251
274
 
252
- test("ADR-011: deriveState uses canonical artifact root for sketch-flag healing", async (t) => {
275
+ test("ADR-011: deriveState stays pure across worktree/canonical-root; healing belongs to reconcile at the canonical artifact root", async (t) => {
253
276
  const originalCwd = process.cwd();
254
277
  const base = makeFixtureBase();
255
278
  t.after(() => cleanup(base, originalCwd));
@@ -265,9 +288,22 @@ test("ADR-011: deriveState uses canonical artifact root for sketch-flag healing"
265
288
  mkdirSync(worktreePath, { recursive: true });
266
289
  process.chdir(worktreePath);
267
290
 
268
- const state = await deriveState(worktreePath, { projectRootForReads: base });
269
- assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "deriveState should heal from canonical artifact root");
270
- assert.equal(state.phase, "planning", "state should advance past refining when PLAN exists at canonical root");
291
+ // Deriving from the worktree with reads routed to the canonical artifact
292
+ // root resolves DB state but never mutates it (ADR-017: derive is pure).
293
+ // The legacy projectRootForReads-driven heal is gone; healing now happens
294
+ // in reconcileBeforeDispatch, which auto runs against canonicalProjectRoot.
295
+ invalidateStateCache();
296
+ const beforeReconcile = await deriveState(worktreePath, { projectRootForReads: base });
297
+ assert.equal(beforeReconcile.phase, "refining", "derive alone must not heal, even via projectRootForReads");
298
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 1, "derive must not mutate is_sketch");
299
+
300
+ const reconcile = await reconcileBeforeDispatch(base);
301
+ assert.equal(reconcile.ok, true);
302
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "reconcile at the canonical root heals the stale flag");
303
+
304
+ invalidateStateCache();
305
+ const healed = await deriveState(worktreePath, { projectRootForReads: base });
306
+ assert.equal(healed.phase, "planning", "re-derive advances past refining once healed at the canonical root");
271
307
  });
272
308
 
273
309
  test("ADR-011: schema v16 is idempotent — re-opening DB preserves is_sketch and sketch_scope columns", async (t) => {