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
@@ -165,6 +165,9 @@ test("executeMilestoneStatus returns milestone metadata and slice counts", async
165
165
  assert.equal(parsed.sliceCount, 1);
166
166
  assert.equal(parsed.slices[0].id, "S01");
167
167
  assert.equal(parsed.slices[0].taskCounts.pending, 1);
168
+ assert.equal(result.details.status, "active");
169
+ assert.equal(result.details.title, "Milestone One");
170
+ assert.deepEqual(result.details.slices, parsed.slices);
168
171
  } finally {
169
172
  closeDatabase();
170
173
  cleanup(base);
@@ -39,6 +39,7 @@ function makeDeps(
39
39
  getAutoWorktreePath: () => null,
40
40
  autoCommitCurrentBranch: () => {},
41
41
  getCurrentBranch: () => "main",
42
+ checkoutBranch: () => {},
42
43
  autoWorktreeBranch: (milestoneId: string) => `milestone/${milestoneId}`,
43
44
  resolveMilestoneFile: (_basePath: string, milestoneId: string) =>
44
45
  `/project/.gsd/milestones/${milestoneId}/${milestoneId}-ROADMAP.md`,
@@ -79,7 +79,7 @@ async function waitFor(condition: () => boolean, label: string): Promise<void> {
79
79
  assert.fail(`Timed out waiting for ${label} after ${timeoutMs}ms`);
80
80
  }
81
81
 
82
- test("runUnit changes cwd to basePath before creating a new session", async (t) => {
82
+ test("runUnit passes basePath as workspaceRoot without changing process cwd", async (t) => {
83
83
  _resetPendingResolve();
84
84
 
85
85
  const originalCwd = process.cwd();
@@ -93,13 +93,15 @@ test("runUnit changes cwd to basePath before creating a new session", async (t)
93
93
 
94
94
  process.chdir(drifted);
95
95
 
96
+ let newSessionWorkspaceRoot: string | undefined;
96
97
  let cwdAtNewSession: string | undefined;
97
98
  const session = {
98
99
  active: true,
99
100
  basePath: base,
100
101
  verbose: false,
101
102
  cmdCtx: {
102
- newSession: () => {
103
+ newSession: (options?: { workspaceRoot?: string }) => {
104
+ newSessionWorkspaceRoot = options?.workspaceRoot;
103
105
  cwdAtNewSession = process.cwd();
104
106
  return Promise.resolve({ cancelled: false });
105
107
  },
@@ -119,10 +121,12 @@ test("runUnit changes cwd to basePath before creating a new session", async (t)
119
121
 
120
122
  const result = await resultPromise;
121
123
  assert.equal(result.status, "completed");
122
- assert.equal(cwdAtNewSession, base);
124
+ assert.equal(newSessionWorkspaceRoot, base);
125
+ assert.equal(cwdAtNewSession, drifted);
126
+ assert.equal(process.cwd(), drifted);
123
127
  });
124
128
 
125
- test("runUnit cancels before creating a session when basePath chdir fails", async (t) => {
129
+ test("runUnit does not chdir or cancel when basePath is not a live directory", async (t) => {
126
130
  _resetPendingResolve();
127
131
 
128
132
  const originalCwd = process.cwd();
@@ -136,14 +140,14 @@ test("runUnit cancels before creating a session when basePath chdir fails", asyn
136
140
 
137
141
  process.chdir(drifted);
138
142
 
139
- let newSessionCalled = false;
143
+ let newSessionWorkspaceRoot: string | undefined;
140
144
  const session = {
141
145
  active: true,
142
146
  basePath: base,
143
147
  verbose: false,
144
148
  cmdCtx: {
145
- newSession: () => {
146
- newSessionCalled = true;
149
+ newSession: (options?: { workspaceRoot?: string }) => {
150
+ newSessionWorkspaceRoot = options?.workspaceRoot;
147
151
  return Promise.resolve({ cancelled: false });
148
152
  },
149
153
  },
@@ -156,15 +160,14 @@ test("runUnit cancels before creating a session when basePath chdir fails", asyn
156
160
  } as any;
157
161
  const ctx = { ui: { notify: () => {} }, model: { id: "test-model" } } as any;
158
162
 
159
- const result = await runUnit(ctx, pi, session, "task", "T01", "prompt");
163
+ const resultPromise = runUnit(ctx, pi, session, "task", "T01", "prompt");
164
+ await waitFor(() => pi.calls.length === 1, "runUnit dispatch");
165
+ resolveAgentEnd({ messages: [{ role: "assistant" }] });
160
166
 
161
- assert.equal(result.status, "cancelled");
162
- assert.equal(result.errorContext?.category, "session-failed");
163
- assert.equal(result.errorContext?.isTransient, true);
164
- assert.match(result.errorContext?.message ?? "", /Failed to chdir to basePath before newSession/);
165
- assert.ok(result.errorContext?.message.includes(base), "error should include the failed basePath");
166
- assert.equal(newSessionCalled, false, "newSession must not run after chdir failure");
167
- assert.equal(pi.calls.length, 0, "unit must not dispatch after chdir failure");
167
+ const result = await resultPromise;
168
+ assert.equal(result.status, "completed");
169
+ assert.equal(newSessionWorkspaceRoot, base);
170
+ assert.equal(process.cwd(), drifted);
168
171
  });
169
172
 
170
173
  test("direct dispatch redirects to the canonical milestone worktree before newSession", async (t) => {
@@ -185,12 +188,12 @@ test("direct dispatch redirects to the canonical milestone worktree before newSe
185
188
 
186
189
  process.chdir(drifted);
187
190
 
188
- let cwdAtNewSession: string | undefined;
191
+ let newSessionWorkspaceRoot: string | undefined;
189
192
  let sentPrompt: string | undefined;
190
193
  const ctx = {
191
194
  ui: { notify: () => {} },
192
- newSession: async () => {
193
- cwdAtNewSession = process.cwd();
195
+ newSession: async (options?: { workspaceRoot?: string }) => {
196
+ newSessionWorkspaceRoot = options?.workspaceRoot;
194
197
  return { cancelled: false };
195
198
  },
196
199
  } as any;
@@ -202,7 +205,7 @@ test("direct dispatch redirects to the canonical milestone worktree before newSe
202
205
 
203
206
  await dispatchDirectPhase(ctx, pi, "research-milestone", base);
204
207
 
205
- assert.equal(cwdAtNewSession, worktreeRoot);
208
+ assert.equal(newSessionWorkspaceRoot, worktreeRoot);
206
209
  assert.equal(process.cwd(), drifted);
207
210
  assert.ok(sentPrompt?.includes(worktreeRoot), "prompt should name the canonical worktree root");
208
211
  });
@@ -0,0 +1,66 @@
1
+ // GSD-2 + Worktree dispatch guard: degrade empty worktrees over real project roots.
2
+
3
+ import { describe, test } from "node:test";
4
+ import assert from "node:assert/strict";
5
+
6
+ import { shouldDegradeEmptyWorktreeToProjectRoot } from "../auto/phases.ts";
7
+ import type { ProjectClassification } from "../detection.ts";
8
+
9
+ function classification(kind: ProjectClassification["kind"]): ProjectClassification {
10
+ return {
11
+ kind,
12
+ signals: {
13
+ detectedFiles: [],
14
+ isGitRepo: true,
15
+ isMonorepo: false,
16
+ xcodePlatforms: [],
17
+ hasCI: false,
18
+ hasTests: false,
19
+ verificationCommands: [],
20
+ },
21
+ trackedFiles: [],
22
+ untrackedFiles: [],
23
+ contentFiles: [],
24
+ markers: [],
25
+ reason: kind,
26
+ };
27
+ }
28
+
29
+ describe("worktree project-root degradation", () => {
30
+ test("degrades when worktree is greenfield but project root has content", () => {
31
+ assert.equal(
32
+ shouldDegradeEmptyWorktreeToProjectRoot(
33
+ classification("greenfield"),
34
+ classification("typed-existing"),
35
+ ),
36
+ true,
37
+ );
38
+ assert.equal(
39
+ shouldDegradeEmptyWorktreeToProjectRoot(
40
+ classification("greenfield"),
41
+ classification("untyped-existing"),
42
+ ),
43
+ true,
44
+ );
45
+ });
46
+
47
+ test("keeps true greenfield worktrees in worktree mode", () => {
48
+ assert.equal(
49
+ shouldDegradeEmptyWorktreeToProjectRoot(
50
+ classification("greenfield"),
51
+ classification("greenfield"),
52
+ ),
53
+ false,
54
+ );
55
+ });
56
+
57
+ test("does not degrade when project root classification is invalid", () => {
58
+ assert.equal(
59
+ shouldDegradeEmptyWorktreeToProjectRoot(
60
+ classification("greenfield"),
61
+ classification("invalid-repo"),
62
+ ),
63
+ false,
64
+ );
65
+ });
66
+ });
@@ -1,3 +1,5 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: WorktreeResolver unit and regression tests.
1
3
  import test from "node:test";
2
4
  import assert from "node:assert/strict";
3
5
  import { mkdtempSync, rmSync, mkdirSync, realpathSync } from "node:fs";
@@ -9,6 +11,17 @@ import {
9
11
  type NotifyCtx,
10
12
  } from "../worktree-resolver.js";
11
13
  import { AutoSession } from "../auto/session.js";
14
+ import {
15
+ closeDatabase,
16
+ insertMilestone,
17
+ openDatabase,
18
+ } from "../gsd-db.js";
19
+ import { registerAutoWorker } from "../db/auto-workers.js";
20
+ import {
21
+ claimMilestoneLease,
22
+ getMilestoneLease,
23
+ releaseMilestoneLease,
24
+ } from "../db/milestone-leases.js";
12
25
 
13
26
  // ─── Helpers ─────────────────────────────────────────────────────────────────
14
27
 
@@ -19,11 +32,12 @@ interface CallLog {
19
32
  }
20
33
 
21
34
  function makeSession(
22
- overrides?: Partial<{ basePath: string; originalBasePath: string }>,
35
+ overrides?: Partial<AutoSession>,
23
36
  ): AutoSession {
24
37
  const s = new AutoSession();
25
38
  s.basePath = overrides?.basePath ?? "/project";
26
39
  s.originalBasePath = overrides?.originalBasePath ?? "/project";
40
+ Object.assign(s, overrides);
27
41
  return s;
28
42
  }
29
43
 
@@ -104,6 +118,9 @@ function makeDeps(
104
118
  calls.push({ fn: "getCurrentBranch", args: [basePath] });
105
119
  return "main";
106
120
  },
121
+ checkoutBranch: (basePath: string, branch: string) => {
122
+ calls.push({ fn: "checkoutBranch", args: [basePath, branch] });
123
+ },
107
124
  autoWorktreeBranch: (milestoneId: string) => {
108
125
  calls.push({ fn: "autoWorktreeBranch", args: [milestoneId] });
109
126
  return `milestone/${milestoneId}`;
@@ -182,6 +199,17 @@ function findCalls(calls: CallLog[], fn: string): CallLog[] {
182
199
  return calls.filter((c) => c.fn === fn);
183
200
  }
184
201
 
202
+ function makeDbBase(): string {
203
+ const base = mkdtempSync(join(tmpdir(), "gsd-worktree-resolver-"));
204
+ mkdirSync(join(base, ".gsd"), { recursive: true });
205
+ return base;
206
+ }
207
+
208
+ function cleanupDbBase(base: string): void {
209
+ try { closeDatabase(); } catch { /* noop */ }
210
+ try { rmSync(base, { recursive: true, force: true }); } catch { /* noop */ }
211
+ }
212
+
185
213
  // ─── Getter Tests ────────────────────────────────────────────────────────────
186
214
 
187
215
  test("workPath returns s.basePath", () => {
@@ -371,6 +399,43 @@ test("enterMilestone does not create double-nested worktree when originalBasePat
371
399
  );
372
400
  });
373
401
 
402
+ test("enterMilestone reacquires a released same-milestone lease before worktree entry", (t) => {
403
+ const base = makeDbBase();
404
+ t.after(() => cleanupDbBase(base));
405
+ openDatabase(join(base, ".gsd", "gsd.db"));
406
+ insertMilestone({ id: "M001", title: "Test milestone", status: "active" });
407
+
408
+ const workerId = registerAutoWorker({ projectRootRealpath: base });
409
+ const originalClaim = claimMilestoneLease(workerId, "M001");
410
+ assert.equal(originalClaim.ok, true);
411
+ if (!originalClaim.ok) throw new Error("expected test lease claim");
412
+ assert.equal(releaseMilestoneLease(workerId, "M001", originalClaim.token), true);
413
+
414
+ const s = makeSession({
415
+ basePath: base,
416
+ originalBasePath: base,
417
+ workerId,
418
+ currentMilestoneId: "M001",
419
+ milestoneLeaseToken: originalClaim.token,
420
+ });
421
+ const deps = makeDeps({
422
+ createAutoWorktree: (basePath: string, milestoneId: string) => join(basePath, ".gsd", "worktrees", milestoneId),
423
+ });
424
+ const ctx = makeNotifyCtx();
425
+ const resolver = new WorktreeResolver(s, deps);
426
+
427
+ resolver.enterMilestone("M001", ctx);
428
+
429
+ const row = getMilestoneLease("M001");
430
+ assert.ok(row);
431
+ assert.equal(row.worker_id, workerId);
432
+ assert.equal(row.status, "held");
433
+ assert.equal(row.fencing_token, originalClaim.token + 1);
434
+ assert.equal(s.milestoneLeaseToken, originalClaim.token + 1);
435
+ assert.equal(s.basePath, join(base, ".gsd", "worktrees", "M001"));
436
+ assert.equal(ctx.messages.some((m) => m.level === "error"), false);
437
+ });
438
+
374
439
  // ─── enterMilestone Tests (branch mode) ──────────────────────────────────────
375
440
 
376
441
  test("enterMilestone in branch mode calls enterBranchModeForMilestone and rebuilds GitService", () => {
@@ -740,21 +805,92 @@ test("mergeAndExit in branch mode merges when on milestone branch", () => {
740
805
  assert.ok(ctx.messages.some((m) => m.msg.includes("branch mode")));
741
806
  });
742
807
 
743
- test("mergeAndExit in branch mode skips when not on milestone branch", () => {
808
+ test("mergeAndExit in branch mode checks out the milestone branch and merges (#5538-followup)", () => {
809
+ // Regression: previously this case silently returned without merging,
810
+ // stranding the milestone's commits on the branch (the test12345 repro).
811
+ // The fix forces a checkout first; merge proceeds when checkout succeeds.
744
812
  const s = makeSession({ basePath: "/project", originalBasePath: "/project" });
813
+ let currentBranch = "main";
814
+ const checkoutInvocations: Array<{ basePath: string; branch: string }> = [];
745
815
  const deps = makeDeps({
746
816
  isInAutoWorktree: () => false,
747
817
  getIsolationMode: () => "branch",
748
- getCurrentBranch: () => "main",
818
+ getCurrentBranch: () => currentBranch,
749
819
  autoWorktreeBranch: () => "milestone/M001",
820
+ checkoutBranch: (basePath: string, branch: string) => {
821
+ checkoutInvocations.push({ basePath, branch });
822
+ currentBranch = branch;
823
+ },
750
824
  });
751
825
  const ctx = makeNotifyCtx();
752
826
  const resolver = new WorktreeResolver(s, deps);
753
827
 
754
828
  resolver.mergeAndExit("M001", ctx);
755
829
 
830
+ assert.equal(checkoutInvocations.length, 1, "must attempt checkout when on wrong branch");
831
+ assert.deepEqual(checkoutInvocations[0], { basePath: "/project", branch: "milestone/M001" });
832
+ assert.equal(findCalls(deps.calls, "mergeMilestoneToMain").length, 1);
833
+ });
834
+
835
+ test("mergeAndExit in branch mode throws when checkout fails", () => {
836
+ // Regression for the silent-skip bug: if the working tree is on the wrong
837
+ // branch and checkout fails, we must throw so the caller pauses auto-mode
838
+ // — never silently advance with the milestone unmerged.
839
+ const s = makeSession({ basePath: "/project", originalBasePath: "/project" });
840
+ const deps = makeDeps({
841
+ isInAutoWorktree: () => false,
842
+ getIsolationMode: () => "branch",
843
+ getCurrentBranch: () => "main",
844
+ autoWorktreeBranch: () => "milestone/M001",
845
+ checkoutBranch: () => {
846
+ throw new Error("dirty working tree blocks checkout");
847
+ },
848
+ });
849
+ const ctx = makeNotifyCtx();
850
+ const resolver = new WorktreeResolver(s, deps);
851
+
852
+ assert.throws(
853
+ () => resolver.mergeAndExit("M001", ctx),
854
+ /dirty working tree blocks checkout/,
855
+ );
856
+ assert.equal(
857
+ findCalls(deps.calls, "mergeMilestoneToMain").length,
858
+ 0,
859
+ "merge must not run when checkout failed",
860
+ );
861
+ const errorNotify = ctx.messages.find((m) => m.level === "error");
862
+ assert.ok(errorNotify, "an error notification must be emitted");
863
+ assert.match(errorNotify!.msg, /milestone\/M001 failed/);
864
+ assert.match(errorNotify!.msg, /Resolve manually/);
865
+ assert.equal(
866
+ ctx.messages.some((m) => m.level === "warning" && m.msg.includes("Milestone merge failed")),
867
+ false,
868
+ "checkout failures with explicit recovery guidance must not emit a duplicate warning",
869
+ );
870
+ });
871
+
872
+ test("mergeAndExit in branch mode throws when checkout reports success but HEAD is still wrong", () => {
873
+ // Defense in depth: even if checkoutBranch returns without throwing, we
874
+ // re-verify and throw if HEAD didn't actually move. Prevents merging on
875
+ // top of the wrong branch on platforms where the checkout is a no-op.
876
+ const s = makeSession({ basePath: "/project", originalBasePath: "/project" });
877
+ const deps = makeDeps({
878
+ isInAutoWorktree: () => false,
879
+ getIsolationMode: () => "branch",
880
+ getCurrentBranch: () => "main", // never changes — simulates no-op checkout
881
+ autoWorktreeBranch: () => "milestone/M001",
882
+ checkoutBranch: () => {
883
+ // Pretend success — but getCurrentBranch will still return "main".
884
+ },
885
+ });
886
+ const ctx = makeNotifyCtx();
887
+ const resolver = new WorktreeResolver(s, deps);
888
+
889
+ assert.throws(
890
+ () => resolver.mergeAndExit("M001", ctx),
891
+ /reported success but current branch is main/,
892
+ );
756
893
  assert.equal(findCalls(deps.calls, "mergeMilestoneToMain").length, 0);
757
- assert.equal(ctx.messages.length, 0);
758
894
  });
759
895
 
760
896
  test("mergeAndExit in branch mode handles merge failure gracefully", () => {
@@ -977,6 +1113,33 @@ test("mergeAndEnterNext enters next milestone even if merge fails", () => {
977
1113
  );
978
1114
  });
979
1115
 
1116
+ test("mergeAndEnterNext halts after branch-mode user-notified checkout failure", () => {
1117
+ const s = makeSession({ basePath: "/project", originalBasePath: "/project" });
1118
+ const deps = makeDeps({
1119
+ isInAutoWorktree: () => false,
1120
+ getIsolationMode: () => "branch",
1121
+ getCurrentBranch: () => "main",
1122
+ autoWorktreeBranch: () => "milestone/M001",
1123
+ checkoutBranch: () => {
1124
+ throw new Error("dirty working tree blocks checkout");
1125
+ },
1126
+ });
1127
+ const ctx = makeNotifyCtx();
1128
+ const resolver = new WorktreeResolver(s, deps);
1129
+
1130
+ assert.throws(
1131
+ () => resolver.mergeAndEnterNext("M001", "M002", ctx),
1132
+ /dirty working tree blocks checkout/,
1133
+ );
1134
+ assert.equal(
1135
+ findCalls(deps.calls, "enterBranchModeForMilestone").length,
1136
+ 0,
1137
+ "must not enter the next milestone after a user-notified branch-mode failure",
1138
+ );
1139
+ assert.equal(findCalls(deps.calls, "mergeMilestoneToMain").length, 0);
1140
+ assert.ok(ctx.messages.some((m) => m.level === "error" && m.msg.includes("Resolve manually")));
1141
+ });
1142
+
980
1143
  // ─── GitService Rebuild Atomicity ────────────────────────────────────────────
981
1144
 
982
1145
  test("GitService is rebuilt with the NEW basePath after enterMilestone", () => {
@@ -0,0 +1,25 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Shared Context Mode tool result helpers.
3
+
4
+ export interface ToolExecutionResult {
5
+ content: Array<{ type: "text"; text: string }>;
6
+ details: Record<string, unknown>;
7
+ isError?: boolean;
8
+ }
9
+
10
+ export type ContextModeToolName = "gsd_exec" | "gsd_exec_search" | "gsd_resume";
11
+
12
+ export function contextModeDisabledResult(operation: ContextModeToolName): ToolExecutionResult {
13
+ return {
14
+ content: [
15
+ {
16
+ type: "text",
17
+ text:
18
+ `${operation} is disabled by \`context_mode.enabled: false\` in preferences. ` +
19
+ "Remove that override or set it to true to re-enable Context Mode tools.",
20
+ },
21
+ ],
22
+ details: { operation, error: "context_mode_disabled" },
23
+ isError: true,
24
+ };
25
+ }
@@ -4,6 +4,8 @@
4
4
  // re-discover past runs without re-executing. Read-only; no DB writes.
5
5
 
6
6
  import { searchExecHistory, type ExecSearchOptions } from "../exec-history.js";
7
+ import { isContextModeEnabled, type ContextModeConfig } from "../preferences-types.js";
8
+ import { contextModeDisabledResult, type ToolExecutionResult } from "./context-mode-tool-result.js";
7
9
 
8
10
  export interface ExecSearchToolParams {
9
11
  query?: string;
@@ -12,16 +14,14 @@ export interface ExecSearchToolParams {
12
14
  limit?: number;
13
15
  }
14
16
 
15
- export interface ToolExecutionResult {
16
- content: Array<{ type: "text"; text: string }>;
17
- details: Record<string, unknown>;
18
- isError?: boolean;
19
- }
20
-
21
17
  export function executeExecSearch(
22
18
  params: ExecSearchToolParams,
23
- opts: { baseDir: string },
19
+ opts: { baseDir: string; preferences?: { context_mode?: ContextModeConfig } | null },
24
20
  ): ToolExecutionResult {
21
+ if (!isContextModeEnabled(opts.preferences)) {
22
+ return contextModeDisabledResult("gsd_exec_search");
23
+ }
24
+
25
25
  const searchOpts: ExecSearchOptions = {
26
26
  query: typeof params.query === "string" ? params.query : undefined,
27
27
  runtime: params.runtime,
@@ -12,6 +12,7 @@ import {
12
12
  type ExecSandboxResult,
13
13
  } from "../exec-sandbox.js";
14
14
  import { isContextModeEnabled, type ContextModeConfig } from "../preferences-types.js";
15
+ import { contextModeDisabledResult, type ToolExecutionResult } from "./context-mode-tool-result.js";
15
16
 
16
17
  export interface ExecToolParams {
17
18
  runtime: ExecSandboxRequest["runtime"];
@@ -20,17 +21,12 @@ export interface ExecToolParams {
20
21
  timeout_ms?: number;
21
22
  }
22
23
 
23
- export interface ToolExecutionResult {
24
- content: Array<{ type: "text"; text: string }>;
25
- details: Record<string, unknown>;
26
- isError?: boolean;
27
- }
28
-
29
24
  export interface ExecToolDeps {
30
25
  baseDir: string;
31
26
  preferences: { context_mode?: ContextModeConfig } | null;
32
27
  /** Optional override for testing. */
33
28
  run?: (req: ExecSandboxRequest, opts: ExecSandboxOptions) => Promise<ExecSandboxResult>;
29
+ env?: NodeJS.ProcessEnv;
34
30
  now?: () => Date;
35
31
  generateId?: () => string;
36
32
  }
@@ -77,21 +73,6 @@ function isEnabled(prefs: ExecToolDeps["preferences"]): boolean {
77
73
  return isContextModeEnabled(prefs);
78
74
  }
79
75
 
80
- function disabledResult(): ToolExecutionResult {
81
- return {
82
- content: [
83
- {
84
- type: "text",
85
- text:
86
- "gsd_exec is disabled by `context_mode.enabled: false` in preferences. Remove that " +
87
- "override (or set it to true) to re-enable sandboxed tool-output execution.",
88
- },
89
- ],
90
- details: { operation: "gsd_exec", error: "context_mode_disabled" },
91
- isError: true,
92
- };
93
- }
94
-
95
76
  function paramError(message: string): ToolExecutionResult {
96
77
  return {
97
78
  content: [{ type: "text", text: `Error: ${message}` }],
@@ -104,7 +85,7 @@ export async function executeGsdExec(
104
85
  params: ExecToolParams,
105
86
  deps: ExecToolDeps,
106
87
  ): Promise<ToolExecutionResult> {
107
- if (!isEnabled(deps.preferences)) return disabledResult();
88
+ if (!isEnabled(deps.preferences)) return contextModeDisabledResult("gsd_exec");
108
89
 
109
90
  const runtime = params.runtime;
110
91
  if (runtime !== "bash" && runtime !== "node" && runtime !== "python") {
@@ -121,7 +102,7 @@ export async function executeGsdExec(
121
102
  const opts = buildExecOptions(
122
103
  deps.baseDir,
123
104
  deps.preferences?.context_mode,
124
- { now: deps.now, generateId: deps.generateId },
105
+ { env: deps.env, now: deps.now, generateId: deps.generateId },
125
106
  );
126
107
  const run = deps.run ?? runExecSandbox;
127
108
 
@@ -308,6 +308,7 @@ function includeSupersededMemories(rankedActive: Memory[]): Memory[] {
308
308
  scope: (row["scope"] as string) ?? "project",
309
309
  tags,
310
310
  structured_fields: structuredFields,
311
+ last_hit_at: (row["last_hit_at"] as string | null) ?? null,
311
312
  };
312
313
  });
313
314
  } catch {
@@ -18,6 +18,7 @@ import { renderAllProjections } from "../workflow-projections.js";
18
18
  import { writeManifest } from "../workflow-manifest.js";
19
19
  import { appendEvent } from "../workflow-events.js";
20
20
  import { logWarning } from "../workflow-logger.js";
21
+ import { validatePlanningPathScope } from "../planning-path-scope.js";
21
22
 
22
23
  export interface PlanSliceTaskInput {
23
24
  taskId: string;
@@ -141,6 +142,18 @@ export async function handlePlanSlice(
141
142
  return { error: `validation failed: ${(err as Error).message}` };
142
143
  }
143
144
 
145
+ const pathScopeError = validatePlanningPathScope(
146
+ basePath,
147
+ params.tasks.flatMap((task, index) => [
148
+ { field: `tasks[${index}].files`, values: task.files },
149
+ { field: `tasks[${index}].inputs`, values: task.inputs },
150
+ { field: `tasks[${index}].expectedOutput`, values: task.expectedOutput },
151
+ ]),
152
+ );
153
+ if (pathScopeError) {
154
+ return { error: `validation failed: ${pathScopeError}` };
155
+ }
156
+
144
157
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
145
158
  // Guards must be inside the transaction so the state they check cannot
146
159
  // change between the read and the write (#2723).
@@ -8,6 +8,7 @@ import { renderAllProjections } from "../workflow-projections.js";
8
8
  import { writeManifest } from "../workflow-manifest.js";
9
9
  import { appendEvent } from "../workflow-events.js";
10
10
  import { logWarning } from "../workflow-logger.js";
11
+ import { validatePlanningPathScope } from "../planning-path-scope.js";
11
12
 
12
13
  export interface PlanTaskParams {
13
14
  milestoneId: string;
@@ -66,6 +67,15 @@ export async function handlePlanTask(
66
67
  return { error: `validation failed: ${(err as Error).message}` };
67
68
  }
68
69
 
70
+ const pathScopeError = validatePlanningPathScope(basePath, [
71
+ { field: "files", values: params.files },
72
+ { field: "inputs", values: params.inputs },
73
+ { field: "expectedOutput", values: params.expectedOutput },
74
+ ]);
75
+ if (pathScopeError) {
76
+ return { error: `validation failed: ${pathScopeError}` };
77
+ }
78
+
69
79
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
70
80
  // Guards must be inside the transaction so the state they check cannot
71
81
  // change between the read and the write (#2723).
@@ -3,22 +3,22 @@
3
3
  // re-deriving project memory state.
4
4
 
5
5
  import { readCompactionSnapshot } from "../compaction-snapshot.js";
6
+ import { isContextModeEnabled, type ContextModeConfig } from "../preferences-types.js";
7
+ import { contextModeDisabledResult, type ToolExecutionResult } from "./context-mode-tool-result.js";
6
8
 
7
9
  export interface ResumeToolParams {
8
10
  /** Ignored — reserved for future variant (e.g. dated snapshots). */
9
11
  _variant?: string;
10
12
  }
11
13
 
12
- export interface ToolExecutionResult {
13
- content: Array<{ type: "text"; text: string }>;
14
- details: Record<string, unknown>;
15
- isError?: boolean;
16
- }
17
-
18
14
  export function executeResume(
19
15
  _params: ResumeToolParams,
20
- opts: { baseDir: string },
16
+ opts: { baseDir: string; preferences?: { context_mode?: ContextModeConfig } | null },
21
17
  ): ToolExecutionResult {
18
+ if (!isContextModeEnabled(opts.preferences)) {
19
+ return contextModeDisabledResult("gsd_resume");
20
+ }
21
+
22
22
  const snapshot = readCompactionSnapshot(opts.baseDir);
23
23
  if (snapshot == null) {
24
24
  return {
@@ -813,7 +813,7 @@ export async function executeMilestoneStatus(
813
813
 
814
814
  return {
815
815
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
816
- details: { operation: "milestone_status", milestoneId: milestone.id, sliceCount: slices.length },
816
+ details: { operation: "milestone_status", ...result },
817
817
  };
818
818
  });
819
819
  } catch (err) {