gsd-pi 2.80.0-dev.2848d9009 → 2.80.0-dev.3065f7bfa

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 (377) hide show
  1. package/README.md +4 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/extensions/gsd/auto/loop.js +32 -1
  4. package/dist/resources/extensions/gsd/auto/phases.js +178 -108
  5. package/dist/resources/extensions/gsd/auto/resolve.js +17 -0
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +20 -30
  7. package/dist/resources/extensions/gsd/auto/session.js +8 -0
  8. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +5 -32
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +16 -0
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +17 -4
  11. package/dist/resources/extensions/gsd/auto-prompts.js +103 -16
  12. package/dist/resources/extensions/gsd/auto-recovery.js +43 -1
  13. package/dist/resources/extensions/gsd/auto-start.js +197 -6
  14. package/dist/resources/extensions/gsd/auto-supervisor.js +8 -1
  15. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +2 -2
  16. package/dist/resources/extensions/gsd/auto-worktree.js +111 -1
  17. package/dist/resources/extensions/gsd/auto.js +86 -26
  18. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +42 -2
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +49 -36
  20. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +15 -5
  21. package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +33 -20
  22. package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +7 -1
  23. package/dist/resources/extensions/gsd/bootstrap/memory-tools.js +9 -3
  24. package/dist/resources/extensions/gsd/bootstrap/query-tools.js +8 -2
  25. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +259 -28
  26. package/dist/resources/extensions/gsd/bootstrap/system-context.js +82 -23
  27. package/dist/resources/extensions/gsd/clean-root-preflight.js +24 -6
  28. package/dist/resources/extensions/gsd/commands-handlers.js +23 -9
  29. package/dist/resources/extensions/gsd/context-budget.js +37 -2
  30. package/dist/resources/extensions/gsd/crash-recovery.js +56 -10
  31. package/dist/resources/extensions/gsd/db/unit-dispatches.js +39 -0
  32. package/dist/resources/extensions/gsd/db-base-schema.js +4 -2
  33. package/dist/resources/extensions/gsd/db-migration-steps.js +6 -0
  34. package/dist/resources/extensions/gsd/ecosystem/gsd-extension-api.js +2 -0
  35. package/dist/resources/extensions/gsd/git-service.js +36 -4
  36. package/dist/resources/extensions/gsd/gsd-db.js +46 -13
  37. package/dist/resources/extensions/gsd/guided-flow.js +99 -38
  38. package/dist/resources/extensions/gsd/memory-store.js +69 -12
  39. package/dist/resources/extensions/gsd/migrate/command.js +40 -1
  40. package/dist/resources/extensions/gsd/migration-auto-check.js +87 -0
  41. package/dist/resources/extensions/gsd/native-git-bridge.js +32 -8
  42. package/dist/resources/extensions/gsd/orphan-stash-audit.js +101 -0
  43. package/dist/resources/extensions/gsd/parallel-orchestrator.js +13 -3
  44. package/dist/resources/extensions/gsd/planning-path-scope.js +26 -0
  45. package/dist/resources/extensions/gsd/pre-execution-checks.js +22 -0
  46. package/dist/resources/extensions/gsd/prompt-loader.js +28 -2
  47. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
  48. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  49. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -2
  50. package/dist/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  52. package/dist/resources/extensions/gsd/prompts/quick-task.md +1 -5
  53. package/dist/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  54. package/dist/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  55. package/dist/resources/extensions/gsd/quick.js +34 -2
  56. package/dist/resources/extensions/gsd/tools/context-mode-tool-result.js +15 -0
  57. package/dist/resources/extensions/gsd/tools/exec-search-tool.js +5 -0
  58. package/dist/resources/extensions/gsd/tools/exec-tool.js +3 -15
  59. package/dist/resources/extensions/gsd/tools/memory-tools.js +1 -0
  60. package/dist/resources/extensions/gsd/tools/plan-slice.js +9 -0
  61. package/dist/resources/extensions/gsd/tools/plan-task.js +9 -0
  62. package/dist/resources/extensions/gsd/tools/resume-tool.js +5 -0
  63. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +1 -1
  64. package/dist/resources/extensions/gsd/unit-context-composer.js +12 -3
  65. package/dist/resources/extensions/gsd/unit-runtime.js +22 -0
  66. package/dist/resources/extensions/gsd/workflow-protocol.js +131 -0
  67. package/dist/resources/extensions/gsd/worktree-resolver.js +68 -21
  68. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  69. package/dist/web/standalone/.next/BUILD_ID +1 -1
  70. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  71. package/dist/web/standalone/.next/build-manifest.json +2 -2
  72. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  73. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/index.html +1 -1
  90. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  95. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  97. package/dist/web/standalone/.next/server/middleware-build-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/package.json +3 -3
  102. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  103. package/packages/mcp-server/dist/workflow-tools.js +22 -17
  104. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  105. package/packages/mcp-server/src/workflow-tools.test.ts +75 -2
  106. package/packages/mcp-server/src/workflow-tools.ts +30 -16
  107. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  108. package/packages/native/tsconfig.tsbuildinfo +1 -1
  109. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  110. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  111. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  112. package/packages/pi-agent-core/dist/agent.d.ts +5 -0
  113. package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
  114. package/packages/pi-agent-core/dist/agent.js +2 -0
  115. package/packages/pi-agent-core/dist/agent.js.map +1 -1
  116. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  117. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  118. package/packages/pi-agent-core/dist/index.js +2 -0
  119. package/packages/pi-agent-core/dist/index.js.map +1 -1
  120. package/packages/pi-agent-core/dist/token-audit.d.ts +47 -0
  121. package/packages/pi-agent-core/dist/token-audit.d.ts.map +1 -0
  122. package/packages/pi-agent-core/dist/token-audit.js +221 -0
  123. package/packages/pi-agent-core/dist/token-audit.js.map +1 -0
  124. package/packages/pi-agent-core/dist/types.d.ts +9 -0
  125. package/packages/pi-agent-core/dist/types.d.ts.map +1 -1
  126. package/packages/pi-agent-core/dist/types.js.map +1 -1
  127. package/packages/pi-agent-core/src/agent-loop.test.ts +128 -0
  128. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  129. package/packages/pi-agent-core/src/agent.ts +8 -0
  130. package/packages/pi-agent-core/src/index.ts +2 -0
  131. package/packages/pi-agent-core/src/token-audit.test.ts +189 -0
  132. package/packages/pi-agent-core/src/token-audit.ts +287 -0
  133. package/packages/pi-agent-core/src/types.ts +14 -0
  134. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  135. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js +32 -0
  136. package/packages/pi-coding-agent/dist/core/agent-session-abort-order.test.js.map +1 -1
  137. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js +18 -0
  138. package/packages/pi-coding-agent/dist/core/agent-session-tool-refresh.test.js.map +1 -1
  139. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -0
  140. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  141. package/packages/pi-coding-agent/dist/core/agent-session.js +44 -7
  142. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  143. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +3 -1
  144. package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
  145. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +11 -0
  146. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  147. package/packages/pi-coding-agent/dist/core/compaction/compaction.js +9 -0
  148. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  149. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts +2 -0
  150. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.d.ts.map +1 -0
  151. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js +103 -0
  152. package/packages/pi-coding-agent/dist/core/compaction-threshold.test.js.map +1 -0
  153. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts +15 -0
  154. package/packages/pi-coding-agent/dist/core/db-snapshot.d.ts.map +1 -0
  155. package/packages/pi-coding-agent/dist/core/db-snapshot.js +66 -0
  156. package/packages/pi-coding-agent/dist/core/db-snapshot.js.map +1 -0
  157. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts +2 -0
  158. package/packages/pi-coding-agent/dist/core/db-snapshot.test.d.ts.map +1 -0
  159. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js +24 -0
  160. package/packages/pi-coding-agent/dist/core/db-snapshot.test.js.map +1 -0
  161. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  162. package/packages/pi-coding-agent/dist/core/extensions/loader.js +8 -0
  163. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  164. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +3 -0
  165. package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  166. package/packages/pi-coding-agent/dist/core/extensions/runner.js +6 -6
  167. package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  168. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +5 -3
  169. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  170. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +39 -1
  171. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  173. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js +2 -0
  174. package/packages/pi-coding-agent/dist/core/hooks-runner.test.js.map +1 -1
  175. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts +2 -0
  176. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.d.ts.map +1 -0
  177. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js +46 -0
  178. package/packages/pi-coding-agent/dist/core/sdk-tool-filter.test.js.map +1 -0
  179. package/packages/pi-coding-agent/dist/core/sdk.d.ts +10 -2
  180. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  181. package/packages/pi-coding-agent/dist/core/sdk.js +74 -2
  182. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  183. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +20 -0
  184. package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  185. package/packages/pi-coding-agent/dist/core/settings-manager.js +25 -0
  186. package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  187. package/packages/pi-coding-agent/dist/core/skill-tool.test.js +22 -0
  188. package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -1
  189. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +6 -7
  190. package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
  191. package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -3
  192. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  193. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  194. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  195. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +3 -0
  196. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  197. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  198. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +13 -5
  199. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  200. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +53 -0
  201. package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
  202. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  203. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +3 -0
  204. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  205. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts +2 -0
  206. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.d.ts.map +1 -0
  207. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js +10 -0
  208. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage-safety-guard.test.js.map +1 -0
  209. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.d.ts.map +1 -1
  210. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js +3 -2
  211. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.js.map +1 -1
  212. package/packages/pi-coding-agent/src/core/agent-session-abort-order.test.ts +36 -0
  213. package/packages/pi-coding-agent/src/core/agent-session-tool-refresh.test.ts +25 -0
  214. package/packages/pi-coding-agent/src/core/agent-session.ts +48 -7
  215. package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +3 -1
  216. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +18 -0
  217. package/packages/pi-coding-agent/src/core/compaction-threshold.test.ts +121 -0
  218. package/packages/pi-coding-agent/src/core/db-snapshot.test.ts +32 -0
  219. package/packages/pi-coding-agent/src/core/db-snapshot.ts +66 -0
  220. package/packages/pi-coding-agent/src/core/extensions/loader.ts +10 -0
  221. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +5 -3
  222. package/packages/pi-coding-agent/src/core/extensions/runner.ts +8 -5
  223. package/packages/pi-coding-agent/src/core/extensions/types.ts +42 -1
  224. package/packages/pi-coding-agent/src/core/hooks-runner.test.ts +2 -0
  225. package/packages/pi-coding-agent/src/core/sdk-tool-filter.test.ts +60 -0
  226. package/packages/pi-coding-agent/src/core/sdk.ts +85 -3
  227. package/packages/pi-coding-agent/src/core/settings-manager.ts +39 -1
  228. package/packages/pi-coding-agent/src/core/skill-tool.test.ts +28 -0
  229. package/packages/pi-coding-agent/src/core/system-prompt.ts +8 -10
  230. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +4 -0
  231. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +56 -0
  232. package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +22 -7
  233. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +3 -0
  234. package/packages/pi-coding-agent/src/resources/extensions/memory/storage-safety-guard.test.ts +14 -0
  235. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.ts +3 -2
  236. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  237. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  238. package/packages/pi-tui/dist/tui.js +18 -8
  239. package/packages/pi-tui/dist/tui.js.map +1 -1
  240. package/packages/pi-tui/src/tui.ts +20 -8
  241. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  242. package/src/resources/extensions/gsd/auto/loop-deps.ts +2 -2
  243. package/src/resources/extensions/gsd/auto/loop.ts +50 -8
  244. package/src/resources/extensions/gsd/auto/phases.ts +251 -172
  245. package/src/resources/extensions/gsd/auto/resolve.ts +23 -1
  246. package/src/resources/extensions/gsd/auto/run-unit.ts +25 -30
  247. package/src/resources/extensions/gsd/auto/session.ts +8 -0
  248. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +8 -34
  249. package/src/resources/extensions/gsd/auto-dispatch.ts +16 -0
  250. package/src/resources/extensions/gsd/auto-post-unit.ts +18 -4
  251. package/src/resources/extensions/gsd/auto-prompts.ts +112 -15
  252. package/src/resources/extensions/gsd/auto-recovery.ts +54 -0
  253. package/src/resources/extensions/gsd/auto-start.ts +230 -9
  254. package/src/resources/extensions/gsd/auto-supervisor.ts +7 -0
  255. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -2
  256. package/src/resources/extensions/gsd/auto-worktree.ts +123 -0
  257. package/src/resources/extensions/gsd/auto.ts +98 -21
  258. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +49 -1
  259. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +50 -36
  260. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -5
  261. package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +34 -19
  262. package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +8 -1
  263. package/src/resources/extensions/gsd/bootstrap/memory-tools.ts +10 -3
  264. package/src/resources/extensions/gsd/bootstrap/query-tools.ts +9 -2
  265. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +305 -28
  266. package/src/resources/extensions/gsd/bootstrap/system-context.ts +90 -22
  267. package/src/resources/extensions/gsd/clean-root-preflight.ts +32 -7
  268. package/src/resources/extensions/gsd/commands-handlers.ts +34 -15
  269. package/src/resources/extensions/gsd/context-budget.ts +44 -2
  270. package/src/resources/extensions/gsd/crash-recovery.ts +67 -10
  271. package/src/resources/extensions/gsd/db/unit-dispatches.ts +41 -0
  272. package/src/resources/extensions/gsd/db-base-schema.ts +4 -2
  273. package/src/resources/extensions/gsd/db-migration-steps.ts +8 -0
  274. package/src/resources/extensions/gsd/ecosystem/gsd-extension-api.ts +3 -0
  275. package/src/resources/extensions/gsd/git-service.ts +46 -8
  276. package/src/resources/extensions/gsd/gsd-db.ts +50 -13
  277. package/src/resources/extensions/gsd/guided-flow.ts +123 -45
  278. package/src/resources/extensions/gsd/memory-store.ts +77 -12
  279. package/src/resources/extensions/gsd/migrate/command.ts +47 -1
  280. package/src/resources/extensions/gsd/migration-auto-check.ts +129 -0
  281. package/src/resources/extensions/gsd/native-git-bridge.ts +39 -6
  282. package/src/resources/extensions/gsd/orphan-stash-audit.ts +117 -0
  283. package/src/resources/extensions/gsd/parallel-orchestrator.ts +13 -3
  284. package/src/resources/extensions/gsd/planning-path-scope.ts +35 -0
  285. package/src/resources/extensions/gsd/pre-execution-checks.ts +23 -0
  286. package/src/resources/extensions/gsd/preferences-types.ts +1 -1
  287. package/src/resources/extensions/gsd/prompt-loader.ts +27 -2
  288. package/src/resources/extensions/gsd/prompts/complete-milestone.md +16 -13
  289. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  290. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -2
  291. package/src/resources/extensions/gsd/prompts/parallel-research-slices.md +1 -1
  292. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/quick-task.md +1 -5
  294. package/src/resources/extensions/gsd/prompts/replan-slice.md +2 -2
  295. package/src/resources/extensions/gsd/prompts/validate-milestone.md +2 -2
  296. package/src/resources/extensions/gsd/quick.ts +37 -2
  297. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +2 -2
  298. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +417 -10
  299. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +56 -13
  300. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +14 -1
  301. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +168 -6
  302. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +15 -6
  303. package/src/resources/extensions/gsd/tests/compaction-snapshot.test.ts +14 -1
  304. package/src/resources/extensions/gsd/tests/complete-milestone-excerpt.test.ts +31 -0
  305. package/src/resources/extensions/gsd/tests/complete-slice-composer.test.ts +3 -2
  306. package/src/resources/extensions/gsd/tests/context-budget.test.ts +10 -1
  307. package/src/resources/extensions/gsd/tests/context-store.test.ts +7 -1
  308. package/src/resources/extensions/gsd/tests/crash-handler-secondary.test.ts +55 -0
  309. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +22 -0
  310. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +5 -1
  311. package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +313 -0
  312. package/src/resources/extensions/gsd/tests/exec-history.test.ts +15 -0
  313. package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +65 -0
  314. package/src/resources/extensions/gsd/tests/execute-task-rendering.test.ts +5 -2
  315. package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +219 -0
  316. package/src/resources/extensions/gsd/tests/finalize-survivor-branch.test.ts +132 -0
  317. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +54 -0
  318. package/src/resources/extensions/gsd/tests/isolation-none-branch-guard.test.ts +6 -3
  319. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +239 -1
  320. package/src/resources/extensions/gsd/tests/journal-query-tool.test.ts +32 -0
  321. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -0
  322. package/src/resources/extensions/gsd/tests/memory-decay-factor.test.ts +90 -0
  323. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +1 -0
  324. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +48 -0
  325. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +127 -0
  326. package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +242 -0
  327. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +34 -2
  328. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -0
  329. package/src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts +133 -0
  330. package/src/resources/extensions/gsd/tests/orphan-stash-audit.test.ts +201 -0
  331. package/src/resources/extensions/gsd/tests/parallel-orchestrator-fast-forward.test.ts +113 -0
  332. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +50 -0
  333. package/src/resources/extensions/gsd/tests/plan-task.test.ts +21 -0
  334. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +45 -5
  335. package/src/resources/extensions/gsd/tests/prompt-duplication-cuts.test.ts +230 -0
  336. package/src/resources/extensions/gsd/tests/prompt-path-audit.test.ts +40 -0
  337. package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +19 -0
  338. package/src/resources/extensions/gsd/tests/query-tools-db-open.test.ts +3 -3
  339. package/src/resources/extensions/gsd/tests/quick-external-gsd.test.ts +40 -0
  340. package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +38 -17
  341. package/src/resources/extensions/gsd/tests/schema-v27-v28-sequence.test.ts +156 -0
  342. package/src/resources/extensions/gsd/tests/select-resumable-milestone.test.ts +96 -0
  343. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +106 -0
  344. package/src/resources/extensions/gsd/tests/signal-handlers.test.ts +27 -0
  345. package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +20 -0
  346. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +49 -1
  347. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +55 -0
  348. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  349. package/src/resources/extensions/gsd/tests/status-db-open.test.ts +9 -0
  350. package/src/resources/extensions/gsd/tests/system-context-memory.test.ts +112 -0
  351. package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +7 -9
  352. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +291 -0
  353. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +136 -4
  354. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +30 -0
  355. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +37 -0
  356. package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -4
  357. package/src/resources/extensions/gsd/tests/workflow-protocol-excerpt.test.ts +99 -0
  358. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +3 -0
  359. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +1 -0
  360. package/src/resources/extensions/gsd/tests/worktree-path-injection.test.ts +22 -19
  361. package/src/resources/extensions/gsd/tests/worktree-project-root-degrade.test.ts +66 -0
  362. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +167 -4
  363. package/src/resources/extensions/gsd/tools/context-mode-tool-result.ts +25 -0
  364. package/src/resources/extensions/gsd/tools/exec-search-tool.ts +7 -7
  365. package/src/resources/extensions/gsd/tools/exec-tool.ts +4 -23
  366. package/src/resources/extensions/gsd/tools/memory-tools.ts +1 -0
  367. package/src/resources/extensions/gsd/tools/plan-slice.ts +13 -0
  368. package/src/resources/extensions/gsd/tools/plan-task.ts +10 -0
  369. package/src/resources/extensions/gsd/tools/resume-tool.ts +7 -7
  370. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +1 -1
  371. package/src/resources/extensions/gsd/unit-context-composer.ts +19 -4
  372. package/src/resources/extensions/gsd/unit-runtime.ts +25 -0
  373. package/src/resources/extensions/gsd/workflow-protocol.ts +160 -0
  374. package/src/resources/extensions/gsd/worktree-resolver.ts +85 -19
  375. package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +0 -97
  376. /package/dist/web/standalone/.next/static/{vaQ47E8aNlbbNEDoeME6G → O2WvnXROue6l7nAp_4dhC}/_buildManifest.js +0 -0
  377. /package/dist/web/standalone/.next/static/{vaQ47E8aNlbbNEDoeME6G → O2WvnXROue6l7nAp_4dhC}/_ssgManifest.js +0 -0
@@ -13,6 +13,9 @@ import { parseRoadmap, parsePlan } from '../parsers-legacy.ts';
13
13
  import { parseSummary } from '../files.ts';
14
14
  import { deriveState } from '../state.ts';
15
15
  import { invalidateAllCaches } from '../cache.ts';
16
+ import { ensureDbOpen } from '../bootstrap/dynamic-tools.ts';
17
+ import { closeDatabase, getAllMilestones } from '../gsd-db.ts';
18
+ import { importWrittenMigrationToDb } from '../migrate/command.ts';
16
19
  import type {
17
20
  GSDProject,
18
21
  GSDMilestone,
@@ -250,6 +253,51 @@ test('Scenario 1: Incomplete project — write, parse, deriveState', async () =>
250
253
  }
251
254
  });
252
255
 
256
+ test('Scenario 1b: written migration imports into authoritative DB state', async () => {
257
+ const base = mkdtempSync(join(tmpdir(), 'gsd-writer-db-import-'));
258
+ try {
259
+ const project = buildIncompleteProject();
260
+ await writeGSDDirectory(project, base);
261
+
262
+ assert.equal(await ensureDbOpen(base), true, 'db import: ensureDbOpen creates authoritative DB');
263
+
264
+ invalidateAllCaches();
265
+ const before = await deriveState(base);
266
+ assert.equal(before.activeMilestone, null, 'db import: markdown-only migration is invisible before DB import');
267
+
268
+ const imported = await importWrittenMigrationToDb(base);
269
+ assert.deepStrictEqual(imported.hierarchy, { milestones: 1, slices: 2, tasks: 3 }, 'db import: hierarchy counts');
270
+
271
+ invalidateAllCaches();
272
+ const after = await deriveState(base);
273
+ assert.deepStrictEqual(after.phase, 'executing', 'db import: deriveState sees imported DB hierarchy');
274
+ assert.deepStrictEqual(after.activeMilestone?.id, 'M001', 'db import: active milestone');
275
+ assert.deepStrictEqual(after.activeSlice?.id, 'S02', 'db import: active slice');
276
+ assert.deepStrictEqual(after.activeTask?.id, 'T03', 'db import: active task');
277
+ } finally {
278
+ closeDatabase();
279
+ rmSync(base, { recursive: true, force: true });
280
+ }
281
+ });
282
+
283
+ test('Scenario 1c: DB import verification fails when preview counts do not match', async () => {
284
+ const base = mkdtempSync(join(tmpdir(), 'gsd-writer-db-check-'));
285
+ try {
286
+ const project = buildIncompleteProject();
287
+ await writeGSDDirectory(project, base);
288
+
289
+ const preview = generatePreview(project);
290
+ await assert.rejects(
291
+ () => importWrittenMigrationToDb(base, { ...preview, totalTasks: preview.totalTasks + 1 }),
292
+ /migration DB import verification failed: tasks 3\/4/,
293
+ );
294
+ assert.deepStrictEqual(getAllMilestones(), [], 'db import: failed verification rolls back hierarchy rewrite');
295
+ } finally {
296
+ closeDatabase();
297
+ rmSync(base, { recursive: true, force: true });
298
+ }
299
+ });
300
+
253
301
  // ─── Scenario 2: Fully complete project ────────────────────────────────
254
302
 
255
303
  test('Scenario 2: Fully complete project — deriveState phase', async () => {
@@ -0,0 +1,127 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtempSync, rmSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import test from "node:test";
6
+
7
+ import { ensureDbOpen } from "../bootstrap/dynamic-tools.ts";
8
+ import {
9
+ _getAdapter,
10
+ closeDatabase,
11
+ getAllMilestones,
12
+ getSliceTasks,
13
+ } from "../gsd-db.ts";
14
+ import {
15
+ autoImportMarkdownHierarchyIfDbMismatch,
16
+ countMarkdownHierarchy,
17
+ } from "../migration-auto-check.ts";
18
+ import { writeGSDDirectory } from "../migrate/writer.ts";
19
+ import type { GSDProject } from "../migrate/types.ts";
20
+
21
+ function makeBase(): string {
22
+ return mkdtempSync(join(tmpdir(), "gsd-migration-auto-check-"));
23
+ }
24
+
25
+ function cleanup(base: string): void {
26
+ closeDatabase();
27
+ rmSync(base, { recursive: true, force: true });
28
+ }
29
+
30
+ function projectFixture(): GSDProject {
31
+ return {
32
+ projectContent: "# Legacy Project\n",
33
+ decisionsContent: "",
34
+ requirements: [],
35
+ milestones: [
36
+ {
37
+ id: "M001",
38
+ title: "Legacy Milestone",
39
+ vision: "Carry forward previous work",
40
+ successCriteria: ["Existing task is visible"],
41
+ research: null,
42
+ boundaryMap: [],
43
+ slices: [
44
+ {
45
+ id: "S01",
46
+ title: "Legacy Slice",
47
+ risk: "medium",
48
+ depends: [],
49
+ done: false,
50
+ demo: "Legacy slice demo",
51
+ goal: "Legacy slice demo",
52
+ research: null,
53
+ summary: null,
54
+ tasks: [
55
+ {
56
+ id: "T01",
57
+ title: "Legacy Task",
58
+ description: "Task carried from markdown",
59
+ done: false,
60
+ estimate: "",
61
+ files: ["src/index.ts"],
62
+ mustHaves: [],
63
+ summary: null,
64
+ },
65
+ ],
66
+ },
67
+ ],
68
+ },
69
+ ],
70
+ };
71
+ }
72
+
73
+ test("migration auto-check imports markdown hierarchy when DB is empty", async () => {
74
+ const base = makeBase();
75
+ try {
76
+ await writeGSDDirectory(projectFixture(), base);
77
+ assert.deepEqual(countMarkdownHierarchy(base), { milestones: 1, slices: 1, tasks: 1 });
78
+
79
+ assert.equal(await ensureDbOpen(base), true);
80
+ assert.equal(getAllMilestones().length, 0, "fresh authoritative DB starts empty");
81
+
82
+ const result = await autoImportMarkdownHierarchyIfDbMismatch(base);
83
+ assert.equal(result.action, "imported");
84
+ assert.equal(result.reason, "db-empty");
85
+ assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 1 });
86
+ assert.equal(getAllMilestones().length, 1);
87
+ assert.equal(getSliceTasks("M001", "S01").length, 1);
88
+ } finally {
89
+ cleanup(base);
90
+ }
91
+ });
92
+
93
+ test("migration auto-check repairs DB hierarchy count mismatch", async () => {
94
+ const base = makeBase();
95
+ try {
96
+ await writeGSDDirectory(projectFixture(), base);
97
+ await autoImportMarkdownHierarchyIfDbMismatch(base);
98
+
99
+ _getAdapter()!.prepare("DELETE FROM tasks WHERE milestone_id = ? AND slice_id = ? AND id = ?").run("M001", "S01", "T01");
100
+ assert.equal(getSliceTasks("M001", "S01").length, 0, "test fixture simulates stale DB task count");
101
+
102
+ const result = await autoImportMarkdownHierarchyIfDbMismatch(base);
103
+ assert.equal(result.action, "imported");
104
+ assert.equal(result.reason, "count-mismatch");
105
+ assert.deepEqual(result.beforeDb, { milestones: 1, slices: 1, tasks: 0 });
106
+ assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 1 });
107
+ assert.equal(getSliceTasks("M001", "S01").length, 1);
108
+ } finally {
109
+ cleanup(base);
110
+ }
111
+ });
112
+
113
+ test("migration auto-check leaves matching DB hierarchy alone", async () => {
114
+ const base = makeBase();
115
+ try {
116
+ await writeGSDDirectory(projectFixture(), base);
117
+ await autoImportMarkdownHierarchyIfDbMismatch(base);
118
+
119
+ const result = await autoImportMarkdownHierarchyIfDbMismatch(base);
120
+ assert.equal(result.action, "none");
121
+ assert.equal(result.reason, "in-sync");
122
+ assert.deepEqual(result.markdown, { milestones: 1, slices: 1, tasks: 1 });
123
+ assert.deepEqual(result.afterDb, { milestones: 1, slices: 1, tasks: 1 });
124
+ } finally {
125
+ cleanup(base);
126
+ }
127
+ });
@@ -0,0 +1,242 @@
1
+ // GSD-2 + src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts
2
+ // Regression: postflight stash pop must run even when mergeAndExit throws.
3
+
4
+ import test from "node:test";
5
+ import assert from "node:assert/strict";
6
+
7
+ import { _runMilestoneMergeWithStashRestore } from "../auto/phases.js";
8
+ import type { IterationContext } from "../auto/types.js";
9
+ import { MergeConflictError } from "../git-service.js";
10
+ import type {
11
+ PostflightResult,
12
+ PreflightResult,
13
+ } from "../clean-root-preflight.js";
14
+
15
+ interface CallLog {
16
+ preflightCalls: number;
17
+ mergeCalls: number;
18
+ postflightCalls: number;
19
+ stopAutoCalls: Array<string | undefined>;
20
+ notifyCalls: Array<{ message: string; level: string }>;
21
+ milestoneMergedInPhases: boolean;
22
+ }
23
+
24
+ function buildIc(opts: {
25
+ preflightResult: PreflightResult;
26
+ mergeBehavior: "succeed" | (() => never);
27
+ postflightResult: PostflightResult;
28
+ }): { ic: IterationContext; log: CallLog } {
29
+ const log: CallLog = {
30
+ preflightCalls: 0,
31
+ mergeCalls: 0,
32
+ postflightCalls: 0,
33
+ stopAutoCalls: [],
34
+ notifyCalls: [],
35
+ milestoneMergedInPhases: false,
36
+ };
37
+
38
+ const session = {
39
+ basePath: "/tmp/proj",
40
+ originalBasePath: "/tmp/proj",
41
+ get milestoneMergedInPhases() {
42
+ return log.milestoneMergedInPhases;
43
+ },
44
+ set milestoneMergedInPhases(v: boolean) {
45
+ log.milestoneMergedInPhases = v;
46
+ },
47
+ };
48
+
49
+ const ctx = {
50
+ ui: {
51
+ notify: (message: string, level: string) => {
52
+ log.notifyCalls.push({ message, level });
53
+ },
54
+ },
55
+ };
56
+
57
+ const deps = {
58
+ preflightCleanRoot: () => {
59
+ log.preflightCalls += 1;
60
+ return opts.preflightResult;
61
+ },
62
+ postflightPopStash: () => {
63
+ log.postflightCalls += 1;
64
+ return opts.postflightResult;
65
+ },
66
+ resolver: {
67
+ mergeAndExit: () => {
68
+ log.mergeCalls += 1;
69
+ if (opts.mergeBehavior !== "succeed") {
70
+ opts.mergeBehavior();
71
+ }
72
+ },
73
+ },
74
+ stopAuto: async (_c?: unknown, _p?: unknown, reason?: string) => {
75
+ log.stopAutoCalls.push(reason);
76
+ },
77
+ };
78
+
79
+ const ic = {
80
+ ctx,
81
+ pi: {},
82
+ s: session,
83
+ deps,
84
+ } as unknown as IterationContext;
85
+
86
+ return { ic, log };
87
+ }
88
+
89
+ const STASH_PUSHED: PreflightResult = {
90
+ stashPushed: true,
91
+ stashMarker: "gsd-preflight-stash:M002:42:1700000000000:abc",
92
+ summary: "Stashed uncommitted changes before merge (milestone M002).",
93
+ };
94
+
95
+ const STASH_NOT_PUSHED: PreflightResult = {
96
+ stashPushed: false,
97
+ summary: "",
98
+ };
99
+
100
+ const POP_OK: PostflightResult = {
101
+ restored: true,
102
+ needsManualRecovery: false,
103
+ message: "Restored stashed changes after milestone M002 merge.",
104
+ stashRef: "stash@{0}",
105
+ };
106
+
107
+ const POP_NEEDS_RECOVERY: PostflightResult = {
108
+ restored: false,
109
+ needsManualRecovery: true,
110
+ message: "git stash pop stash@{0} failed: conflict in lib/models.ts",
111
+ };
112
+
113
+ test("happy path: merge succeeds and stash is popped", async () => {
114
+ const { ic, log } = buildIc({
115
+ preflightResult: STASH_PUSHED,
116
+ mergeBehavior: "succeed",
117
+ postflightResult: POP_OK,
118
+ });
119
+
120
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
121
+
122
+ assert.equal(result, null, "happy path returns null (loop continues)");
123
+ assert.equal(log.preflightCalls, 1);
124
+ assert.equal(log.mergeCalls, 1);
125
+ assert.equal(log.postflightCalls, 1, "postflight pop must run on success");
126
+ assert.equal(log.stopAutoCalls.length, 0, "no stopAuto on happy path");
127
+ assert.equal(log.milestoneMergedInPhases, true, "merge flag set");
128
+ });
129
+
130
+ test("regression #5538-followup: postflight pop runs even when mergeAndExit throws non-conflict error", async () => {
131
+ // The original bug: when mergeAndExit threw, the catch block called
132
+ // stopAuto + return break BEFORE postflight pop ran. The user's
133
+ // gsd-preflight-stash:M00x stash was orphaned. This test exercises that
134
+ // exact scenario and asserts the pop is now invoked.
135
+ const { ic, log } = buildIc({
136
+ preflightResult: STASH_PUSHED,
137
+ mergeBehavior: () => {
138
+ throw new Error("native git merge failed: index lock present");
139
+ },
140
+ postflightResult: POP_OK,
141
+ });
142
+
143
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
144
+
145
+ assert.deepEqual(result, { action: "break", reason: "merge-failed" });
146
+ assert.equal(log.mergeCalls, 1);
147
+ assert.equal(
148
+ log.postflightCalls,
149
+ 1,
150
+ "postflight pop must run even on merge failure (was the bug)",
151
+ );
152
+ assert.equal(log.stopAutoCalls.length, 1);
153
+ assert.match(log.stopAutoCalls[0] ?? "", /Merge error on milestone M002/);
154
+ assert.equal(
155
+ log.milestoneMergedInPhases,
156
+ false,
157
+ "merge flag must NOT be set when merge throws",
158
+ );
159
+ });
160
+
161
+ test("regression #5538-followup: postflight pop runs even when mergeAndExit throws MergeConflictError", async () => {
162
+ const { ic, log } = buildIc({
163
+ preflightResult: STASH_PUSHED,
164
+ mergeBehavior: () => {
165
+ throw new MergeConflictError(
166
+ ["lib/models.ts", "app/page.tsx"],
167
+ "squash",
168
+ "milestone/M002",
169
+ "main",
170
+ );
171
+ },
172
+ postflightResult: POP_OK,
173
+ });
174
+
175
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
176
+
177
+ assert.deepEqual(result, { action: "break", reason: "merge-conflict" });
178
+ assert.equal(log.mergeCalls, 1);
179
+ assert.equal(
180
+ log.postflightCalls,
181
+ 1,
182
+ "postflight pop must run on merge conflict (was the bug)",
183
+ );
184
+ assert.equal(log.stopAutoCalls.length, 1);
185
+ assert.match(log.stopAutoCalls[0] ?? "", /Merge conflict on milestone M002/);
186
+ });
187
+
188
+ test("clean tree: no stash to pop, merge succeeds, no pop attempted", async () => {
189
+ const { ic, log } = buildIc({
190
+ preflightResult: STASH_NOT_PUSHED,
191
+ mergeBehavior: "succeed",
192
+ postflightResult: POP_OK,
193
+ });
194
+
195
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
196
+
197
+ assert.equal(result, null);
198
+ assert.equal(log.postflightCalls, 0, "no pop when nothing was stashed");
199
+ assert.equal(log.milestoneMergedInPhases, true);
200
+ });
201
+
202
+ test("merge succeeds but stash pop needs manual recovery -> postflight-stash-restore-failed break", async () => {
203
+ const { ic, log } = buildIc({
204
+ preflightResult: STASH_PUSHED,
205
+ mergeBehavior: "succeed",
206
+ postflightResult: POP_NEEDS_RECOVERY,
207
+ });
208
+
209
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
210
+
211
+ assert.deepEqual(result, {
212
+ action: "break",
213
+ reason: "postflight-stash-restore-failed",
214
+ });
215
+ assert.equal(log.postflightCalls, 1);
216
+ assert.equal(log.stopAutoCalls.length, 1);
217
+ assert.match(
218
+ log.stopAutoCalls[0] ?? "",
219
+ /Post-merge stash restore failed for milestone M002/,
220
+ );
221
+ });
222
+
223
+ test("merge error is reported even when stash pop also failed (merge-error takes priority)", async () => {
224
+ const { ic, log } = buildIc({
225
+ preflightResult: STASH_PUSHED,
226
+ mergeBehavior: () => {
227
+ throw new Error("network unreachable during push");
228
+ },
229
+ postflightResult: POP_NEEDS_RECOVERY,
230
+ });
231
+
232
+ const result = await _runMilestoneMergeWithStashRestore(ic, "M002");
233
+
234
+ assert.deepEqual(result, { action: "break", reason: "merge-failed" });
235
+ assert.equal(log.postflightCalls, 1, "postflight pop still attempted");
236
+ assert.equal(log.stopAutoCalls.length, 1, "stopAuto called once, not twice");
237
+ assert.match(
238
+ log.stopAutoCalls[0] ?? "",
239
+ /Merge error/,
240
+ "stopAuto message reflects merge error, not stash failure",
241
+ );
242
+ });
@@ -12,11 +12,18 @@
12
12
 
13
13
  import { describe, test, beforeEach, afterEach } from "node:test";
14
14
  import assert from "node:assert/strict";
15
- import { chmodSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
15
+ import { chmodSync, existsSync, mkdtempSync, writeFileSync, readFileSync, rmSync } from "node:fs";
16
16
  import { join } from "node:path";
17
17
  import { tmpdir } from "node:os";
18
18
  import { execFileSync } from "node:child_process";
19
- import { nativeIsRepo, nativeCommit, nativeResetHard, nativeBranchDelete } from "../native-git-bridge.js";
19
+ import {
20
+ assertWorktreeMaterialized,
21
+ nativeBranchDelete,
22
+ nativeCommit,
23
+ nativeIsRepo,
24
+ nativeResetHard,
25
+ nativeWorktreeAdd,
26
+ } from "../native-git-bridge.js";
20
27
 
21
28
  // Note: prior static-analysis tests that scanned native-git-bridge.ts for
22
29
  // the raw shell-spawn pattern were removed under #4827 — the integration
@@ -115,4 +122,29 @@ describe("native-git-bridge #4180: fallback runtime behaviour", () => {
115
122
  /GSD_GIT_ERROR|git branch -D does-not-exist failed/,
116
123
  );
117
124
  });
125
+
126
+ test("assertWorktreeMaterialized rejects directories without a .git file", (t) => {
127
+ const dir = mkdtempSync(join(tmpdir(), "ngb-worktree-missing-git-"));
128
+ t.after(() => rmSync(dir, { recursive: true, force: true }));
129
+
130
+ assert.throws(
131
+ () => assertWorktreeMaterialized(dir),
132
+ /missing \.git file/,
133
+ );
134
+ });
135
+
136
+ test("nativeWorktreeAdd materializes a valid .git marker", (t) => {
137
+ const wtPath = join(repo, ".gsd", "worktrees", "M001");
138
+ t.after(() => {
139
+ try { git(["worktree", "remove", "--force", wtPath], repo); } catch { /* noop */ }
140
+ });
141
+
142
+ nativeWorktreeAdd(repo, wtPath, "milestone/M001", true, "HEAD");
143
+
144
+ assert.equal(
145
+ existsSync(join(wtPath, ".git")),
146
+ true,
147
+ "created worktree must have the .git file required by later health checks",
148
+ );
149
+ });
118
150
  });
@@ -110,6 +110,9 @@ function makeDeps(overrides?: Partial<WorktreeResolverDeps>): WorktreeResolverDe
110
110
  calls.push({ fn: "getCurrentBranch", args: [basePath] });
111
111
  return "main";
112
112
  },
113
+ checkoutBranch: (basePath: string, branch: string) => {
114
+ calls.push({ fn: "checkoutBranch", args: [basePath, branch] });
115
+ },
113
116
  autoWorktreeBranch: (milestoneId: string) => `milestone/${milestoneId}`,
114
117
  resolveMilestoneFile: (basePath: string, milestoneId: string, fileType: string) => {
115
118
  calls.push({ fn: "resolveMilestoneFile", args: [basePath, milestoneId, fileType] });
@@ -0,0 +1,133 @@
1
+ // GSD-2 + src/resources/extensions/gsd/tests/orphan-merge-bootstrap.test.ts
2
+ // Regression: bootstrap must actively merge orphan completed-but-unmerged
3
+ // milestones, not just seed `s.currentMilestoneId` (the seed approach was
4
+ // silently overwritten at auto-start.ts:948 — caught in audit of PR #5549).
5
+
6
+ import test from "node:test";
7
+ import assert from "node:assert/strict";
8
+
9
+ import { _mergeOrphanCompletedMilestone } from "../auto-start.js";
10
+ import type { WorktreeResolver } from "../worktree-resolver.js";
11
+
12
+ interface FakeResolverState {
13
+ mergeCalls: Array<{ milestoneId: string }>;
14
+ shouldThrow?: unknown;
15
+ }
16
+
17
+ function fakeResolver(state: FakeResolverState): WorktreeResolver {
18
+ return {
19
+ mergeAndExit: (milestoneId: string) => {
20
+ state.mergeCalls.push({ milestoneId });
21
+ if (state.shouldThrow) throw state.shouldThrow;
22
+ },
23
+ } as unknown as WorktreeResolver;
24
+ }
25
+
26
+ interface FakeUiState {
27
+ notifications: Array<{ message: string; level: string }>;
28
+ }
29
+
30
+ function fakeUi(state: FakeUiState): {
31
+ notify: (msg: string, level?: "info" | "warning" | "error" | "success") => void;
32
+ } {
33
+ return {
34
+ notify: (message: string, level?: "info" | "warning" | "error" | "success") => {
35
+ state.notifications.push({ message, level: level ?? "info" });
36
+ },
37
+ };
38
+ }
39
+
40
+ test("happy path: orphan merge runs, returns merged:true, emits info notify", () => {
41
+ const resolverState: FakeResolverState = { mergeCalls: [] };
42
+ const uiState: FakeUiState = { notifications: [] };
43
+ const result = _mergeOrphanCompletedMilestone(
44
+ fakeResolver(resolverState),
45
+ "M002",
46
+ fakeUi(uiState),
47
+ );
48
+
49
+ assert.deepEqual(result, { merged: true });
50
+ assert.deepEqual(resolverState.mergeCalls, [{ milestoneId: "M002" }]);
51
+ assert.equal(uiState.notifications.length, 1);
52
+ assert.deepEqual(uiState.notifications[0], {
53
+ message: "Detected unmerged completed milestone M002. Merging now.",
54
+ level: "info",
55
+ });
56
+ });
57
+
58
+ test("regression: mergeAndExit throwing (e.g. wrong-branch from PR #5549 commit 5) does not bubble out", () => {
59
+ // Commit 5 (68ef58a3c) made `_mergeBranchMode` throw on wrong branch
60
+ // instead of silently returning false. If `_mergeOrphanCompletedMilestone`
61
+ // didn't catch the throw, bootstrap would surface an unhandled exception
62
+ // to the slash-command caller — the exact regression risk that motivated
63
+ // wrapping in try/catch.
64
+ const boom = new Error("dirty working tree blocks checkout");
65
+ const resolverState: FakeResolverState = { mergeCalls: [], shouldThrow: boom };
66
+ const uiState: FakeUiState = { notifications: [] };
67
+
68
+ const result = _mergeOrphanCompletedMilestone(
69
+ fakeResolver(resolverState),
70
+ "M002",
71
+ fakeUi(uiState),
72
+ );
73
+
74
+ assert.equal(result.merged, false);
75
+ assert.equal(result.error, boom);
76
+
77
+ // First notify announces the merge attempt; second notify reports the failure.
78
+ assert.equal(uiState.notifications.length, 2);
79
+ assert.equal(uiState.notifications[0].level, "info");
80
+ assert.equal(uiState.notifications[1].level, "warning");
81
+ assert.match(
82
+ uiState.notifications[1].message,
83
+ /Could not merge orphan milestone M002/,
84
+ );
85
+ assert.match(uiState.notifications[1].message, /dirty working tree blocks checkout/);
86
+ assert.match(uiState.notifications[1].message, /Resolve manually/);
87
+ });
88
+
89
+ test("non-Error thrown values are still captured and notified", () => {
90
+ // Defensive: thrown strings, numbers, etc. must not crash the formatter.
91
+ const resolverState: FakeResolverState = {
92
+ mergeCalls: [],
93
+ // mimic a thrown non-Error value
94
+ shouldThrow: "git lock held",
95
+ };
96
+ const uiState: FakeUiState = { notifications: [] };
97
+
98
+ const result = _mergeOrphanCompletedMilestone(
99
+ fakeResolver(resolverState),
100
+ "M002",
101
+ fakeUi(uiState),
102
+ );
103
+
104
+ assert.equal(result.merged, false);
105
+ assert.equal(result.error, resolverState.shouldThrow);
106
+ assert.equal(uiState.notifications[1].level, "warning");
107
+ assert.match(uiState.notifications[1].message, /git lock held/);
108
+ });
109
+
110
+ test("the mergeAndExit call receives a notify-bound NotifyCtx the resolver can invoke", () => {
111
+ // The resolver's NotifyCtx must be wired to ui.notify so user-facing
112
+ // messages from inside mergeAndExit (e.g. "Milestone Mxxx merged") still
113
+ // reach the UI. Verify by having the fake resolver invoke the ctx.notify.
114
+ const uiState: FakeUiState = { notifications: [] };
115
+ const ui = fakeUi(uiState);
116
+
117
+ const resolver = {
118
+ mergeAndExit: (_milestoneId: string, ctx: { notify: (msg: string, level?: "info" | "warning" | "error" | "success") => void }) => {
119
+ ctx.notify("inner success message", "success");
120
+ },
121
+ } as unknown as WorktreeResolver;
122
+
123
+ const result = _mergeOrphanCompletedMilestone(resolver, "M002", ui);
124
+
125
+ assert.equal(result.merged, true);
126
+ // 1: outer "Detected unmerged completed milestone..."
127
+ // 2: inner "inner success message" emitted via the bound ctx.notify
128
+ assert.equal(uiState.notifications.length, 2);
129
+ assert.deepEqual(uiState.notifications[1], {
130
+ message: "inner success message",
131
+ level: "success",
132
+ });
133
+ });