@opengsd/gsd-pi 1.2.0-dev.b1abb545 → 1.2.0-dev.fb12b103

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 (520) hide show
  1. package/dist/cli-style.d.ts +17 -0
  2. package/dist/cli-style.js +28 -0
  3. package/dist/cli.js +1 -1
  4. package/dist/headless-events.d.ts +4 -2
  5. package/dist/headless-events.js +7 -29
  6. package/dist/models-resolver.d.ts +3 -13
  7. package/dist/models-resolver.js +3 -22
  8. package/dist/resource-loader.js +2 -14
  9. package/dist/resources/.managed-resources-content-hash +1 -1
  10. package/dist/resources/extensions/async-jobs/async-bash-tool.js +30 -64
  11. package/dist/resources/extensions/async-jobs/await-tool.js +80 -12
  12. package/dist/resources/extensions/async-jobs/index.js +65 -0
  13. package/dist/resources/extensions/async-jobs/job-manager.js +12 -1
  14. package/dist/resources/extensions/bg-shell/bg-shell-command.js +6 -6
  15. package/dist/resources/extensions/bg-shell/bg-shell-tool.js +10 -7
  16. package/dist/resources/extensions/bg-shell/overlay.js +9 -6
  17. package/dist/resources/extensions/bg-shell/process-manager.js +54 -25
  18. package/dist/resources/extensions/bg-shell/readiness-detector.js +11 -0
  19. package/dist/resources/extensions/bg-shell/utilities.js +5 -2
  20. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
  21. package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
  22. package/dist/resources/extensions/browser-tools/index.js +69 -12
  23. package/dist/resources/extensions/claude-code-cli/models.js +9 -0
  24. package/dist/resources/extensions/claude-code-cli/stream-adapter.js +38 -6
  25. package/dist/resources/extensions/gsd/auto/orchestrator.js +40 -9
  26. package/dist/resources/extensions/gsd/auto/phases.js +6 -1
  27. package/dist/resources/extensions/gsd/auto-dispatch.js +12 -1
  28. package/dist/resources/extensions/gsd/auto-model-selection.js +25 -6
  29. package/dist/resources/extensions/gsd/auto-post-unit.js +19 -8
  30. package/dist/resources/extensions/gsd/auto-prompts.js +15 -10
  31. package/dist/resources/extensions/gsd/auto-start.js +21 -21
  32. package/dist/resources/extensions/gsd/auto-tool-tracking.js +18 -0
  33. package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +7 -16
  34. package/dist/resources/extensions/gsd/auto-worktree-repair.js +10 -2
  35. package/dist/resources/extensions/gsd/auto-worktree.js +35 -352
  36. package/dist/resources/extensions/gsd/auto.js +8 -20
  37. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -2
  38. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +32 -12
  39. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
  40. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +151 -20
  41. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +30 -4
  42. package/dist/resources/extensions/gsd/branch-patterns.js +2 -0
  43. package/dist/resources/extensions/gsd/browser-daemon-auto-prep.js +83 -0
  44. package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
  45. package/dist/resources/extensions/gsd/captures.js +5 -15
  46. package/dist/resources/extensions/gsd/closeout-recovery.js +3 -2
  47. package/dist/resources/extensions/gsd/commands/catalog.js +6 -62
  48. package/dist/resources/extensions/gsd/constants.js +0 -2
  49. package/dist/resources/extensions/gsd/crash-recovery.js +4 -12
  50. package/dist/resources/extensions/gsd/db/engine.js +755 -0
  51. package/dist/resources/extensions/gsd/db/queries.js +372 -0
  52. package/dist/resources/extensions/gsd/db/sql-constants.js +11 -0
  53. package/dist/resources/extensions/gsd/db/writers/cascades.js +194 -0
  54. package/dist/resources/extensions/gsd/db/writers/import-restore.js +182 -0
  55. package/dist/resources/extensions/gsd/db/writers/memory.js +149 -0
  56. package/dist/resources/extensions/gsd/db/writers/reconcile.js +458 -0
  57. package/dist/resources/extensions/gsd/db/writers/status.js +70 -0
  58. package/dist/resources/extensions/gsd/doctor-environment.js +5 -11
  59. package/dist/resources/extensions/gsd/doctor-format.js +9 -6
  60. package/dist/resources/extensions/gsd/doctor-git-checks.js +4 -3
  61. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +21 -16
  62. package/dist/resources/extensions/gsd/error-classifier.js +9 -0
  63. package/dist/resources/extensions/gsd/exec-sandbox.js +30 -10
  64. package/dist/resources/extensions/gsd/git-service.js +1 -0
  65. package/dist/resources/extensions/gsd/gitignore.js +3 -0
  66. package/dist/resources/extensions/gsd/gsd-db.js +171 -2048
  67. package/dist/resources/extensions/gsd/guidance.js +98 -0
  68. package/dist/resources/extensions/gsd/guided-flow.js +51 -5
  69. package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
  70. package/dist/resources/extensions/gsd/mcp-tool-name.js +5 -13
  71. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +1 -1
  72. package/dist/resources/extensions/gsd/migrate/safety.js +20 -9
  73. package/dist/resources/extensions/gsd/migration-auto-check.js +24 -3
  74. package/dist/resources/extensions/gsd/model-cost-table.js +1 -0
  75. package/dist/resources/extensions/gsd/model-router.js +3 -0
  76. package/dist/resources/extensions/gsd/notification-store.js +11 -4
  77. package/dist/resources/extensions/gsd/parallel-merge.js +14 -11
  78. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +11 -7
  79. package/dist/resources/extensions/gsd/paths.js +37 -24
  80. package/dist/resources/extensions/gsd/pre-execution-checks.js +91 -3
  81. package/dist/resources/extensions/gsd/preferences-models.js +14 -48
  82. package/dist/resources/extensions/gsd/preferences.js +14 -0
  83. package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  84. package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  85. package/dist/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  86. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  87. package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -1
  88. package/dist/resources/extensions/gsd/prompts/system.md +5 -2
  89. package/dist/resources/extensions/gsd/provider-error-guidance.js +1 -5
  90. package/dist/resources/extensions/gsd/provider-switch-observer.js +1 -1
  91. package/dist/resources/extensions/gsd/publication.js +87 -0
  92. package/dist/resources/extensions/gsd/recovery-classification.js +41 -87
  93. package/dist/resources/extensions/gsd/safety/destructive-confirmation.js +108 -0
  94. package/dist/resources/extensions/gsd/safety/evidence-collector.js +37 -4
  95. package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +7 -2
  96. package/dist/resources/extensions/gsd/safety/file-change-validator.js +10 -0
  97. package/dist/resources/extensions/gsd/state-transition-matrix.js +38 -0
  98. package/dist/resources/extensions/gsd/state.js +1 -20
  99. package/dist/resources/extensions/gsd/status-guards.js +56 -8
  100. package/dist/resources/extensions/gsd/stop-notice.js +57 -0
  101. package/dist/resources/extensions/gsd/tool-surface-readiness.js +56 -0
  102. package/dist/resources/extensions/gsd/tools/complete-slice.js +24 -43
  103. package/dist/resources/extensions/gsd/tools/exec-tool.js +10 -8
  104. package/dist/resources/extensions/gsd/tools/plan-slice.js +12 -6
  105. package/dist/resources/extensions/gsd/tools/reopen-milestone.js +11 -29
  106. package/dist/resources/extensions/gsd/tools/reopen-slice.js +14 -33
  107. package/dist/resources/extensions/gsd/tools/skip-slice.js +18 -36
  108. package/dist/resources/extensions/gsd/uat-policy.js +2 -1
  109. package/dist/resources/extensions/gsd/undo.js +8 -7
  110. package/dist/resources/extensions/gsd/unit-closeout.js +138 -0
  111. package/dist/resources/extensions/gsd/unit-context-composer.js +74 -1
  112. package/dist/resources/extensions/gsd/unit-context-manifest.js +4 -27
  113. package/dist/resources/extensions/gsd/unit-registry.js +337 -0
  114. package/dist/resources/extensions/gsd/unit-tool-contracts.js +9 -182
  115. package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
  116. package/dist/resources/extensions/gsd/workflow-tool-surface.js +1 -1
  117. package/dist/resources/extensions/gsd/worktree-git-recovery.js +293 -0
  118. package/dist/resources/extensions/gsd/worktree-lifecycle.js +9 -1
  119. package/dist/resources/extensions/gsd/worktree-manager.js +45 -28
  120. package/dist/resources/extensions/gsd/worktree-placement.js +59 -0
  121. package/dist/resources/extensions/gsd/worktree-reentry.js +12 -8
  122. package/dist/resources/extensions/gsd/worktree-root.js +28 -6
  123. package/dist/resources/extensions/gsd/worktree-safety.js +8 -5
  124. package/dist/resources/extensions/gsd/worktree-session-state.js +12 -11
  125. package/dist/resources/extensions/search-the-web/native-search.js +5 -3
  126. package/dist/resources/extensions/shared/browser-contract.js +59 -0
  127. package/dist/resources/extensions/shared/gsd-browser-cli.js +96 -5
  128. package/dist/resources/shared/package.json +3 -0
  129. package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
  130. package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
  131. package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
  132. package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
  133. package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  134. package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  135. package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  136. package/dist/resources/skills/gsd-browser/SKILL.md +1 -1
  137. package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
  138. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  139. package/dist/web/standalone/.next/BUILD_ID +1 -1
  140. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  141. package/dist/web/standalone/.next/build-manifest.json +3 -3
  142. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  143. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  144. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  145. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  146. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  147. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  148. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  153. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  156. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  157. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  158. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  159. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  160. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  161. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  162. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  163. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  164. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  165. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  166. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  167. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  168. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  169. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  170. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  171. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  172. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  173. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  174. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  175. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  176. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  177. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  178. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  179. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  180. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  181. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  182. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  183. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  184. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  185. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  186. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  187. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  188. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  189. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  190. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  191. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  192. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  193. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/update/route.js.nft.json +1 -1
  195. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  196. package/dist/web/standalone/.next/server/app/index.html +1 -1
  197. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  198. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  199. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  200. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  201. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  202. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  203. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  204. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  205. package/dist/web/standalone/.next/server/chunks/{5047.js → 5942.js} +2 -2
  206. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  207. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  209. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  210. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  211. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  212. package/dist/web/standalone/.next/static/chunks/{796.cf859a427a2cb2ac.js → 796.e0bdc932325d7e03.js} +1 -1
  213. package/dist/web/standalone/.next/static/chunks/{webpack-fbea77b5f9953368.js → webpack-f0285ce91d4ec9ef.js} +1 -1
  214. package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
  215. package/dist/web/standalone/node_modules/postcss/lib/container.js +18 -26
  216. package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +14 -47
  217. package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
  218. package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
  219. package/dist/web/standalone/node_modules/postcss/lib/input.js +29 -54
  220. package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +37 -47
  221. package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +9 -26
  222. package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +55 -57
  223. package/dist/web/standalone/node_modules/postcss/lib/node.js +31 -99
  224. package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
  225. package/dist/web/standalone/node_modules/postcss/lib/parser.js +9 -10
  226. package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
  227. package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +11 -30
  228. package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
  229. package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
  230. package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
  231. package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +28 -69
  232. package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +2 -6
  233. package/dist/web/standalone/node_modules/postcss/package.json +48 -48
  234. package/dist/web/standalone/package.json +1 -1
  235. package/dist/worktree-cli.js +3 -6
  236. package/dist/worktree-status-banner.js +7 -11
  237. package/package.json +1 -1
  238. package/packages/cloud-mcp-gateway/package.json +2 -2
  239. package/packages/contracts/dist/rpc.d.ts +1 -0
  240. package/packages/contracts/dist/rpc.d.ts.map +1 -1
  241. package/packages/contracts/dist/rpc.js.map +1 -1
  242. package/packages/contracts/dist/workflow.d.ts +4 -0
  243. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  244. package/packages/contracts/dist/workflow.js.map +1 -1
  245. package/packages/contracts/package.json +1 -1
  246. package/packages/daemon/package.json +4 -4
  247. package/packages/gsd-agent-core/package.json +5 -5
  248. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +5 -0
  249. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  250. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +8 -0
  251. package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
  252. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
  253. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +7 -0
  254. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
  255. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
  256. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js +8 -1
  257. package/packages/gsd-agent-modes/dist/modes/interactive/controllers/input-controller.js.map +1 -1
  258. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.d.ts.map +1 -1
  259. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js +11 -1
  260. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-chat-render.js.map +1 -1
  261. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.d.ts.map +1 -1
  262. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js +4 -4
  263. package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-auth.js.map +1 -1
  264. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  265. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +3 -1
  266. package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
  267. package/packages/gsd-agent-modes/package.json +7 -7
  268. package/packages/mcp-server/dist/cli.js +6 -3
  269. package/packages/mcp-server/dist/cli.js.map +1 -1
  270. package/packages/mcp-server/dist/workflow-tools.d.ts +8 -0
  271. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  272. package/packages/mcp-server/dist/workflow-tools.js +46 -21
  273. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  274. package/packages/mcp-server/package.json +3 -3
  275. package/packages/native/package.json +1 -1
  276. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts +1 -0
  277. package/packages/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  278. package/packages/pi-agent-core/dist/harness/env/nodejs.js +34 -3
  279. package/packages/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  280. package/packages/pi-agent-core/dist/index.d.ts +1 -0
  281. package/packages/pi-agent-core/dist/index.d.ts.map +1 -1
  282. package/packages/pi-agent-core/dist/index.js +3 -0
  283. package/packages/pi-agent-core/dist/index.js.map +1 -1
  284. package/packages/pi-agent-core/package.json +1 -1
  285. package/packages/pi-ai/dist/image-models.generated.d.ts +2 -2
  286. package/packages/pi-ai/dist/image-models.generated.js +6 -6
  287. package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
  288. package/packages/pi-ai/dist/models.generated.d.ts +478 -484
  289. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  290. package/packages/pi-ai/dist/models.generated.js +500 -533
  291. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  292. package/packages/pi-ai/package.json +1 -1
  293. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts +2 -2
  294. package/packages/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/auth-storage.js +19 -13
  296. package/packages/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/capability-patches.d.ts.map +1 -1
  298. package/packages/pi-coding-agent/dist/core/capability-patches.js +3 -1
  299. package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
  300. package/packages/pi-coding-agent/dist/core/provider-readiness.d.ts.map +1 -1
  301. package/packages/pi-coding-agent/dist/core/provider-readiness.js +13 -6
  302. package/packages/pi-coding-agent/dist/core/provider-readiness.js.map +1 -1
  303. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts +11 -0
  304. package/packages/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  305. package/packages/pi-coding-agent/dist/core/tools/bash.js +53 -11
  306. package/packages/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  307. package/packages/pi-coding-agent/dist/index.d.ts +1 -1
  308. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  309. package/packages/pi-coding-agent/dist/index.js +1 -1
  310. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  311. package/packages/pi-coding-agent/dist/utils/shell.d.ts +28 -2
  312. package/packages/pi-coding-agent/dist/utils/shell.d.ts.map +1 -1
  313. package/packages/pi-coding-agent/dist/utils/shell.js +56 -10
  314. package/packages/pi-coding-agent/dist/utils/shell.js.map +1 -1
  315. package/packages/pi-coding-agent/package.json +7 -7
  316. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  317. package/packages/pi-tui/dist/tui.js +9 -0
  318. package/packages/pi-tui/dist/tui.js.map +1 -1
  319. package/packages/pi-tui/package.json +2 -2
  320. package/packages/rpc-client/package.json +2 -2
  321. package/pkg/package.json +1 -1
  322. package/src/resources/extensions/async-jobs/async-bash-cancel.test.ts +360 -0
  323. package/src/resources/extensions/async-jobs/async-bash-tool.ts +33 -56
  324. package/src/resources/extensions/async-jobs/await-tool.test.ts +139 -0
  325. package/src/resources/extensions/async-jobs/await-tool.ts +82 -12
  326. package/src/resources/extensions/async-jobs/index.ts +79 -0
  327. package/src/resources/extensions/async-jobs/job-manager.ts +21 -1
  328. package/src/resources/extensions/bg-shell/bg-shell-command.ts +6 -6
  329. package/src/resources/extensions/bg-shell/bg-shell-tool.ts +10 -6
  330. package/src/resources/extensions/bg-shell/overlay.ts +9 -5
  331. package/src/resources/extensions/bg-shell/process-manager.ts +50 -25
  332. package/src/resources/extensions/bg-shell/readiness-detector.ts +12 -0
  333. package/src/resources/extensions/bg-shell/tests/lifecycle-and-utilities.test.ts +48 -1
  334. package/src/resources/extensions/bg-shell/utilities.ts +5 -2
  335. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
  336. package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
  337. package/src/resources/extensions/browser-tools/index.ts +71 -13
  338. package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
  339. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +29 -1
  340. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
  341. package/src/resources/extensions/claude-code-cli/models.ts +9 -0
  342. package/src/resources/extensions/claude-code-cli/stream-adapter.ts +40 -4
  343. package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +28 -0
  344. package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -1
  345. package/src/resources/extensions/gsd/auto/orchestrator.ts +46 -10
  346. package/src/resources/extensions/gsd/auto/phases.ts +10 -1
  347. package/src/resources/extensions/gsd/auto-dispatch.ts +12 -0
  348. package/src/resources/extensions/gsd/auto-model-selection.ts +25 -5
  349. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -7
  350. package/src/resources/extensions/gsd/auto-prompts.ts +40 -26
  351. package/src/resources/extensions/gsd/auto-start.ts +21 -22
  352. package/src/resources/extensions/gsd/auto-tool-tracking.ts +19 -0
  353. package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +10 -17
  354. package/src/resources/extensions/gsd/auto-worktree-repair.ts +13 -2
  355. package/src/resources/extensions/gsd/auto-worktree.ts +41 -364
  356. package/src/resources/extensions/gsd/auto.ts +20 -24
  357. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -5
  358. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +33 -12
  359. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
  360. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +180 -15
  361. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +29 -3
  362. package/src/resources/extensions/gsd/branch-patterns.ts +3 -0
  363. package/src/resources/extensions/gsd/browser-daemon-auto-prep.ts +108 -0
  364. package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
  365. package/src/resources/extensions/gsd/captures.ts +5 -16
  366. package/src/resources/extensions/gsd/closeout-recovery.ts +2 -1
  367. package/src/resources/extensions/gsd/commands/catalog.ts +6 -68
  368. package/src/resources/extensions/gsd/constants.ts +0 -3
  369. package/src/resources/extensions/gsd/crash-recovery.ts +3 -9
  370. package/src/resources/extensions/gsd/db/engine.ts +809 -0
  371. package/src/resources/extensions/gsd/db/queries.ts +453 -0
  372. package/src/resources/extensions/gsd/db/sql-constants.ts +12 -0
  373. package/src/resources/extensions/gsd/db/writers/cascades.ts +237 -0
  374. package/src/resources/extensions/gsd/db/writers/import-restore.ts +310 -0
  375. package/src/resources/extensions/gsd/db/writers/memory.ts +220 -0
  376. package/src/resources/extensions/gsd/db/writers/reconcile.ts +500 -0
  377. package/src/resources/extensions/gsd/db/writers/status.ts +88 -0
  378. package/src/resources/extensions/gsd/doctor-environment.ts +5 -13
  379. package/src/resources/extensions/gsd/doctor-format.ts +12 -7
  380. package/src/resources/extensions/gsd/doctor-git-checks.ts +3 -3
  381. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +22 -17
  382. package/src/resources/extensions/gsd/error-classifier.ts +11 -0
  383. package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
  384. package/src/resources/extensions/gsd/git-service.ts +1 -0
  385. package/src/resources/extensions/gsd/gitignore.ts +3 -0
  386. package/src/resources/extensions/gsd/gsd-db.ts +173 -2373
  387. package/src/resources/extensions/gsd/guidance.ts +139 -0
  388. package/src/resources/extensions/gsd/guided-flow.ts +50 -5
  389. package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
  390. package/src/resources/extensions/gsd/mcp-tool-name.ts +6 -11
  391. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +1 -1
  392. package/src/resources/extensions/gsd/migrate/safety.ts +18 -7
  393. package/src/resources/extensions/gsd/migration-auto-check.ts +28 -3
  394. package/src/resources/extensions/gsd/model-cost-table.ts +1 -0
  395. package/src/resources/extensions/gsd/model-router.ts +3 -0
  396. package/src/resources/extensions/gsd/notification-store.ts +26 -3
  397. package/src/resources/extensions/gsd/parallel-merge.ts +12 -9
  398. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +10 -7
  399. package/src/resources/extensions/gsd/paths.ts +42 -22
  400. package/src/resources/extensions/gsd/pre-execution-checks.ts +109 -3
  401. package/src/resources/extensions/gsd/preferences-models.ts +12 -47
  402. package/src/resources/extensions/gsd/preferences.ts +18 -0
  403. package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
  404. package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
  405. package/src/resources/extensions/gsd/prompts/refine-slice.md +1 -1
  406. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  407. package/src/resources/extensions/gsd/prompts/run-uat.md +1 -1
  408. package/src/resources/extensions/gsd/prompts/system.md +5 -2
  409. package/src/resources/extensions/gsd/provider-error-guidance.ts +4 -9
  410. package/src/resources/extensions/gsd/provider-switch-observer.ts +1 -1
  411. package/src/resources/extensions/gsd/publication.ts +122 -0
  412. package/src/resources/extensions/gsd/recovery-classification.ts +47 -88
  413. package/src/resources/extensions/gsd/safety/destructive-confirmation.ts +134 -0
  414. package/src/resources/extensions/gsd/safety/evidence-collector.ts +36 -4
  415. package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +7 -2
  416. package/src/resources/extensions/gsd/safety/file-change-validator.ts +14 -0
  417. package/src/resources/extensions/gsd/state-transition-matrix.ts +42 -0
  418. package/src/resources/extensions/gsd/state.ts +4 -21
  419. package/src/resources/extensions/gsd/status-guards.ts +59 -8
  420. package/src/resources/extensions/gsd/stop-notice.ts +75 -0
  421. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +123 -0
  422. package/src/resources/extensions/gsd/tests/auto-model-selection.test.ts +22 -0
  423. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +16 -19
  424. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +3 -1
  425. package/src/resources/extensions/gsd/tests/auto-post-unit-evidence-crossref-4909.test.ts +46 -0
  426. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +2 -2
  427. package/src/resources/extensions/gsd/tests/auto-worktree-repair.test.ts +4 -2
  428. package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
  429. package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
  430. package/src/resources/extensions/gsd/tests/browser-daemon-auto-prep.test.ts +144 -0
  431. package/src/resources/extensions/gsd/tests/checkout-branch-stash-guard.test.ts +66 -1
  432. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +44 -0
  433. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +8 -7
  434. package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +303 -0
  435. package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
  436. package/src/resources/extensions/gsd/tests/dynamic-bash-no-cap.test.ts +132 -0
  437. package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +157 -0
  438. package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +193 -0
  439. package/src/resources/extensions/gsd/tests/exec-tool.test.ts +29 -1
  440. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
  441. package/src/resources/extensions/gsd/tests/file-change-validator.test.ts +33 -1
  442. package/src/resources/extensions/gsd/tests/guidance.test.ts +125 -0
  443. package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +58 -15
  444. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +74 -59
  445. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +3 -2
  446. package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
  447. package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +3 -1
  448. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +85 -1
  449. package/src/resources/extensions/gsd/tests/model-unittype-mapping.test.ts +32 -1
  450. package/src/resources/extensions/gsd/tests/notification-store.test.ts +32 -0
  451. package/src/resources/extensions/gsd/tests/oauth-api-model-routing.test.ts +167 -0
  452. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +193 -1
  453. package/src/resources/extensions/gsd/tests/provider-error-guidance.test.ts +3 -3
  454. package/src/resources/extensions/gsd/tests/publication.test.ts +120 -0
  455. package/src/resources/extensions/gsd/tests/recovery-classification-illegal-transition.test.ts +30 -0
  456. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +248 -1
  457. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +1 -0
  458. package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +38 -0
  459. package/src/resources/extensions/gsd/tests/session-switch-clears-pending-autostart.test.ts +108 -0
  460. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +43 -6
  461. package/src/resources/extensions/gsd/tests/state-transition-matrix.test.ts +36 -0
  462. package/src/resources/extensions/gsd/tests/status-guards.test.ts +38 -0
  463. package/src/resources/extensions/gsd/tests/stop-notice.test.ts +70 -0
  464. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +76 -0
  465. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +8 -0
  466. package/src/resources/extensions/gsd/tests/tool-surface-readiness.test.ts +155 -0
  467. package/src/resources/extensions/gsd/tests/uat-policy.test.ts +24 -29
  468. package/src/resources/extensions/gsd/tests/unit-closeout.test.ts +209 -0
  469. package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +67 -2
  470. package/src/resources/extensions/gsd/tests/unit-registry.test.ts +163 -0
  471. package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
  472. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  473. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +2 -2
  474. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +41 -4
  475. package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +22 -1
  476. package/src/resources/extensions/gsd/tests/worktree-placement.test.ts +113 -0
  477. package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +1 -1
  478. package/src/resources/extensions/gsd/tests/worktree-safety.test.ts +3 -1
  479. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +12 -6
  480. package/src/resources/extensions/gsd/tests/worktree-teardown-safety.test.ts +2 -2
  481. package/src/resources/extensions/gsd/tests/write-gate.test.ts +42 -0
  482. package/src/resources/extensions/gsd/tool-surface-readiness.ts +76 -0
  483. package/src/resources/extensions/gsd/tools/complete-slice.ts +23 -58
  484. package/src/resources/extensions/gsd/tools/exec-tool.ts +9 -8
  485. package/src/resources/extensions/gsd/tools/plan-slice.ts +12 -6
  486. package/src/resources/extensions/gsd/tools/reopen-milestone.ts +11 -38
  487. package/src/resources/extensions/gsd/tools/reopen-slice.ts +14 -42
  488. package/src/resources/extensions/gsd/tools/skip-slice.ts +18 -44
  489. package/src/resources/extensions/gsd/uat-policy.ts +2 -1
  490. package/src/resources/extensions/gsd/undo.ts +9 -8
  491. package/src/resources/extensions/gsd/unit-closeout.ts +201 -0
  492. package/src/resources/extensions/gsd/unit-context-composer.ts +111 -1
  493. package/src/resources/extensions/gsd/unit-context-manifest.ts +4 -28
  494. package/src/resources/extensions/gsd/unit-registry.ts +412 -0
  495. package/src/resources/extensions/gsd/unit-tool-contracts.ts +27 -192
  496. package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
  497. package/src/resources/extensions/gsd/workflow-tool-surface.ts +4 -1
  498. package/src/resources/extensions/gsd/worktree-git-recovery.ts +314 -0
  499. package/src/resources/extensions/gsd/worktree-lifecycle.ts +10 -1
  500. package/src/resources/extensions/gsd/worktree-manager.ts +47 -28
  501. package/src/resources/extensions/gsd/worktree-placement.ts +63 -0
  502. package/src/resources/extensions/gsd/worktree-reentry.ts +10 -7
  503. package/src/resources/extensions/gsd/worktree-root.ts +29 -6
  504. package/src/resources/extensions/gsd/worktree-safety.ts +8 -5
  505. package/src/resources/extensions/gsd/worktree-session-state.ts +11 -11
  506. package/src/resources/extensions/search-the-web/native-search.ts +5 -3
  507. package/src/resources/extensions/shared/browser-contract.ts +66 -0
  508. package/src/resources/extensions/shared/gsd-browser-cli.ts +119 -5
  509. package/src/resources/shared/package.json +3 -0
  510. package/src/resources/skills/create-skill/references/executable-code.md +1 -1
  511. package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
  512. package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
  513. package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
  514. package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
  515. package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
  516. package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
  517. package/src/resources/skills/gsd-browser/SKILL.md +1 -1
  518. package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
  519. /package/dist/web/standalone/.next/static/{3PtrU9qGPEXwNLWkIyiqk → mU4QIDlpVHDdjDpeEKh5W}/_buildManifest.js +0 -0
  520. /package/dist/web/standalone/.next/static/{3PtrU9qGPEXwNLWkIyiqk → mU4QIDlpVHDdjDpeEKh5W}/_ssgManifest.js +0 -0
@@ -35,6 +35,7 @@ import {
35
35
  insertTask,
36
36
  openDatabase,
37
37
  } from "../../gsd-db.ts";
38
+ import { createGsdIntegrationProject } from "./gsd-integration-fixture.ts";
38
39
 
39
40
  function run(cmd: string, cwd: string): string {
40
41
  // Safe: all inputs are hardcoded test strings, not user input
@@ -42,17 +43,12 @@ function run(cmd: string, cwd: string): string {
42
43
  }
43
44
 
44
45
  function createTempRepo(): string {
45
- const dir = realpathSync(mkdtempSync(join(tmpdir(), "wt-ms-merge-test-")));
46
- run("git init", dir);
47
- run("git config user.email test@test.com", dir);
48
- run("git config user.name Test", dir);
49
- writeFileSync(join(dir, "README.md"), "# test\n");
50
- mkdirSync(join(dir, ".gsd"), { recursive: true });
51
- writeFileSync(join(dir, ".gsd", "STATE.md"), "# State\n");
52
- run("git add .", dir);
53
- run("git commit -m init", dir);
54
- run("git branch -M main", dir);
55
- return dir;
46
+ return createGsdIntegrationProject({
47
+ prefix: "wt-ms-merge-test-",
48
+ initialFiles: {
49
+ ".gsd/STATE.md": "# State\n",
50
+ },
51
+ }).root;
56
52
  }
57
53
 
58
54
  function createTempRepoWithExternalGsd(): { repo: string; externalState: string } {
@@ -746,7 +742,7 @@ describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
746
742
  });
747
743
 
748
744
  test("#2156: mergeMilestoneToMain removes external-state worktrees using the milestone branch name", () => {
749
- const { repo, externalState } = freshRepoWithExternalGsd();
745
+ const { repo } = freshRepoWithExternalGsd();
750
746
  const wtPath = createAutoWorktree(repo, "M215");
751
747
 
752
748
  addSliceToMilestone(repo, wtPath, "M215", "S01", "External cleanup", [
@@ -754,9 +750,10 @@ describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
754
750
  ]);
755
751
 
756
752
  const realWtPath = realpathSync(wtPath);
757
- assert.ok(
758
- realWtPath.startsWith(externalState),
759
- `worktree should be registered under external .gsd state, got ${realWtPath}`,
753
+ assert.equal(
754
+ realWtPath,
755
+ join(repo, ".gsd-worktrees", "M215"),
756
+ `worktree should use canonical path under project root, got ${realWtPath}`,
760
757
  );
761
758
 
762
759
  // Recreate the exact divergence from #1852: local .gsd/ is replaced with a
@@ -783,6 +780,52 @@ describe("auto-worktree-milestone-merge", { timeout: 300_000 }, () => {
783
780
  );
784
781
  });
785
782
 
783
+ test("#2156 (legacy): mergeMilestoneToMain removes external-state worktrees created under .gsd/worktrees/", () => {
784
+ const { repo, externalState } = freshRepoWithExternalGsd();
785
+ // Worktrees created by older versions live at .gsd/worktrees/<MID>; git
786
+ // resolves the symlink and registers them under external state. Canonical
787
+ // .gsd-worktrees/ creation never crosses the symlink, so this coverage
788
+ // creates the legacy worktree explicitly. createAutoWorktree chdirs into
789
+ // the new worktree and mergeMilestoneToMain reads process.cwd() as the
790
+ // worktree cwd, so mirror that here.
791
+ const wtPath = join(repo, ".gsd", "worktrees", "M216");
792
+ run(`git worktree add -b milestone/M216 "${wtPath}"`, repo);
793
+ process.chdir(wtPath);
794
+
795
+ addSliceToMilestone(repo, wtPath, "M216", "S01", "Legacy external cleanup", [
796
+ { file: "legacy-external-cleanup.ts", content: "export const legacyExternalCleanup = true;\n", message: "add legacy external cleanup" },
797
+ ]);
798
+
799
+ const realWtPath = realpathSync(wtPath);
800
+ assert.ok(
801
+ realWtPath.startsWith(externalState),
802
+ `legacy worktree should be registered under external .gsd state, got ${realWtPath}`,
803
+ );
804
+
805
+ // Recreate the exact divergence from #1852: local .gsd/ is replaced with a
806
+ // stale real directory, so the computed path no longer matches git's record.
807
+ unlinkSync(join(repo, ".gsd"));
808
+ mkdirSync(join(repo, ".gsd", "worktrees", "M216"), { recursive: true });
809
+ writeFileSync(join(repo, ".gsd", "STATE.md"), "# Local stale state\n");
810
+ writeFileSync(join(repo, ".gsd", "worktrees", "M216", "stale.txt"), "stale local artifact\n");
811
+
812
+ const roadmap = makeRoadmap("M216", "Legacy external cleanup", [
813
+ { id: "S01", title: "Legacy external cleanup" },
814
+ ]);
815
+
816
+ mergeMilestoneToMain(repo, "M216", roadmap);
817
+
818
+ assert.ok(
819
+ !run("git worktree list", repo).includes("M216"),
820
+ "merged legacy milestone worktree should be removed from git worktree list",
821
+ );
822
+ assert.ok(!existsSync(realWtPath), "real external worktree directory should be removed");
823
+ assert.ok(
824
+ !run("git branch", repo).includes("milestone/M216"),
825
+ "milestone branch should be deleted after merge cleanup",
826
+ );
827
+ });
828
+
786
829
  test("#2912: MERGE_HEAD cleaned up after squash-merge conflict", () => {
787
830
  const repo = freshRepo();
788
831
  const wtPath = createAutoWorktree(repo, "M291");
@@ -11,6 +11,11 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, realpathSync
11
11
  import { join } from "node:path";
12
12
  import { tmpdir } from "node:os";
13
13
  import { execSync } from "node:child_process";
14
+ import {
15
+ commitAll,
16
+ createGsdIntegrationProject,
17
+ writeGsdMilestoneContext,
18
+ } from "./gsd-integration-fixture.ts";
14
19
 
15
20
  import {
16
21
  createAutoWorktree,
@@ -32,17 +37,12 @@ function run(command: string, cwd: string): string {
32
37
  }
33
38
 
34
39
  function createTempRepo(): string {
35
- const dir = realpathSync(mkdtempSync(join(tmpdir(), "auto-wt-test-")));
36
- run("git init", dir);
37
- run("git config user.email test@test.com", dir);
38
- run("git config user.name Test", dir);
39
- // Create initial commit on main
40
- writeFileSync(join(dir, "README.md"), "# test\n");
41
- run("git add .", dir);
42
- run("git commit -m init", dir);
43
- // Ensure branch is called main
44
- run("git branch -M main", dir);
45
- return dir;
40
+ return createGsdIntegrationProject("auto-wt-test-").root;
41
+ }
42
+
43
+ function commitMilestoneContext(repo: string, milestoneId: string): void {
44
+ writeGsdMilestoneContext(repo, milestoneId);
45
+ commitAll(repo, "add milestone");
46
46
  }
47
47
 
48
48
  describe("auto-worktree lifecycle", () => {
@@ -59,13 +59,7 @@ describe("auto-worktree lifecycle", () => {
59
59
 
60
60
  test("create → detect → teardown", () => {
61
61
  tempDir = createTempRepo();
62
-
63
- // Create .gsd/milestones/M003 with a dummy file (simulates planning artifacts)
64
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
65
- mkdirSync(msDir, { recursive: true });
66
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
67
- run("git add .", tempDir);
68
- run("git commit -m \"add milestone\"", tempDir);
62
+ commitMilestoneContext(tempDir, "M003");
69
63
 
70
64
  // ─── createAutoWorktree ──────────────────────────────────────────
71
65
  const wtPath = createAutoWorktree(tempDir, "M003");
@@ -112,11 +106,7 @@ describe("auto-worktree lifecycle", () => {
112
106
 
113
107
  test("re-entry: create again, exit without teardown, re-enter", () => {
114
108
  tempDir = createTempRepo();
115
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
116
- mkdirSync(msDir, { recursive: true });
117
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
118
- run("git add .", tempDir);
119
- run("git commit -m \"add milestone\"", tempDir);
109
+ commitMilestoneContext(tempDir, "M003");
120
110
 
121
111
  const wtPath2 = createAutoWorktree(tempDir, "M003");
122
112
  assert.ok(existsSync(wtPath2), "worktree re-created");
@@ -157,7 +147,7 @@ describe("auto-worktree lifecycle", () => {
157
147
  try {
158
148
  const wtPath = createAutoWorktree(tempDir, "M001");
159
149
  const realWtPath = realpathSync(wtPath);
160
- assert.ok(realWtPath.startsWith(storage), "git registered the symlink-resolved worktree path");
150
+ assert.equal(realWtPath, join(tempDir, ".gsd-worktrees", "M001"), "worktree uses canonical path under project root, not through the .gsd symlink");
161
151
 
162
152
  _resetAutoWorktreeOriginalBaseForTests();
163
153
  process.chdir(realWtPath);
@@ -192,13 +182,62 @@ describe("auto-worktree lifecycle", () => {
192
182
  }
193
183
  });
194
184
 
185
+ test("legacy symlink-resolved auto worktree is detected after module state reset", () => {
186
+ tempDir = createTempRepo();
187
+ const savedGsdHome = process.env.GSD_HOME;
188
+ const fakeHome = realpathSync(mkdtempSync(join(tmpdir(), "auto-wt-home-legacy-")));
189
+ const storage = join(fakeHome, ".gsd", "projects", "abc123def456");
190
+ mkdirSync(join(storage, "milestones", "M001"), { recursive: true });
191
+ writeFileSync(join(storage, "milestones", "M001", "CONTEXT.md"), "# M001\n");
192
+ symlinkSync(storage, join(tempDir, ".gsd"));
193
+ process.env.GSD_HOME = join(fakeHome, ".gsd");
194
+
195
+ try {
196
+ // Worktrees created by older versions live at .gsd/worktrees/<MID>;
197
+ // git resolves the symlink and registers them under external state.
198
+ // Detection must keep working for them — canonical .gsd-worktrees/
199
+ // creation never crosses the symlink, so create the legacy worktree
200
+ // explicitly.
201
+ const wtPath = join(tempDir, ".gsd", "worktrees", "M001");
202
+ run(`git worktree add -b milestone/M001 "${wtPath}"`, tempDir);
203
+ const realWtPath = realpathSync(wtPath);
204
+ assert.ok(realWtPath.startsWith(storage), "git registered the symlink-resolved worktree path");
205
+
206
+ _resetAutoWorktreeOriginalBaseForTests();
207
+ process.chdir(realWtPath);
208
+
209
+ assert.ok(isInAutoWorktree(tempDir), "structural detection works without module originalBase");
210
+ const resolved = getAutoWorktreePath(realWtPath, "M001");
211
+ assert.ok(resolved, "existing legacy worktree is found when basePath is the worktree path");
212
+ assert.equal(realpathSync(resolved!), realWtPath);
213
+
214
+ enterAutoWorktree(tempDir, "M001");
215
+ process.chdir(realWtPath);
216
+ assert.deepStrictEqual(
217
+ getActiveAutoWorktreeContext(),
218
+ {
219
+ originalBase: tempDir,
220
+ worktreeName: "M001",
221
+ branch: "milestone/M001",
222
+ },
223
+ "active context is detected from a symlink-resolved legacy worktree cwd",
224
+ );
225
+ } finally {
226
+ process.chdir(tempDir);
227
+ try {
228
+ teardownAutoWorktree(tempDir, "M001");
229
+ } catch {
230
+ // Best-effort cleanup for partially-created temp worktrees.
231
+ }
232
+ if (savedGsdHome === undefined) delete process.env.GSD_HOME;
233
+ else process.env.GSD_HOME = savedGsdHome;
234
+ rmSync(fakeHome, { recursive: true, force: true });
235
+ }
236
+ });
237
+
195
238
  test("coexistence with manual worktree", async () => {
196
239
  tempDir = createTempRepo();
197
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
198
- mkdirSync(msDir, { recursive: true });
199
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
200
- run("git add .", tempDir);
201
- run("git commit -m \"add milestone\"", tempDir);
240
+ commitMilestoneContext(tempDir, "M003");
202
241
 
203
242
  // Import createWorktree directly for manual worktree
204
243
  const { createWorktree } = await import("../../worktree-manager.ts");
@@ -221,11 +260,7 @@ describe("auto-worktree lifecycle", () => {
221
260
 
222
261
  test("split-brain prevention: originalBase cleared after teardown", () => {
223
262
  tempDir = createTempRepo();
224
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
225
- mkdirSync(msDir, { recursive: true });
226
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
227
- run("git add .", tempDir);
228
- run("git commit -m \"add milestone\"", tempDir);
263
+ commitMilestoneContext(tempDir, "M003");
229
264
 
230
265
  createAutoWorktree(tempDir, "M003");
231
266
  teardownAutoWorktree(tempDir, "M003");
@@ -235,11 +270,7 @@ describe("auto-worktree lifecycle", () => {
235
270
 
236
271
  test("#1526: getMainBranch returns milestone/<MID> in auto-worktree", async () => {
237
272
  tempDir = createTempRepo();
238
- const msDir = join(tempDir, ".gsd", "milestones", "M005");
239
- mkdirSync(msDir, { recursive: true });
240
- writeFileSync(join(msDir, "CONTEXT.md"), "# M005 Context\n");
241
- run("git add .", tempDir);
242
- run("git commit -m \"add milestone\"", tempDir);
273
+ commitMilestoneContext(tempDir, "M005");
243
274
 
244
275
  const { GitServiceImpl } = await import("../../git-service.ts");
245
276
 
@@ -259,11 +290,7 @@ describe("auto-worktree lifecycle", () => {
259
290
 
260
291
  test("#1713: stale worktree directory without .git file", async () => {
261
292
  tempDir = createTempRepo();
262
- const msDir = join(tempDir, ".gsd", "milestones", "M010");
263
- mkdirSync(msDir, { recursive: true });
264
- writeFileSync(join(msDir, "CONTEXT.md"), "# M010 Context\n");
265
- run("git add .", tempDir);
266
- run("git commit -m \"add milestone\"", tempDir);
293
+ commitMilestoneContext(tempDir, "M010");
267
294
 
268
295
  // Simulate a crash leaving a stale directory with no .git file.
269
296
  const { worktreePath } = await import("../../worktree-manager.ts");
@@ -284,11 +311,7 @@ describe("auto-worktree lifecycle", () => {
284
311
 
285
312
  test("#778: re-attach does not reconcile plan checkboxes into a worktree-local .gsd projection", async () => {
286
313
  tempDir = createTempRepo();
287
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
288
- mkdirSync(msDir, { recursive: true });
289
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
290
- run("git add .", tempDir);
291
- run("git commit -m \"add milestone\"", tempDir);
314
+ commitMilestoneContext(tempDir, "M003");
292
315
 
293
316
  const planRelPath = join(".gsd", "milestones", "M004", "slices", "S01", "S01-PLAN.md");
294
317
  const planDir = join(tempDir, ".gsd", "milestones", "M004", "slices", "S01");
@@ -348,11 +371,7 @@ describe("auto-worktree lifecycle", () => {
348
371
 
349
372
  test("#2791: mcp.json is not copied into worktree on creation after copyPlanningArtifacts removal", () => {
350
373
  tempDir = createTempRepo();
351
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
352
- mkdirSync(msDir, { recursive: true });
353
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
354
- run("git add .", tempDir);
355
- run("git commit -m \"add milestone\"", tempDir);
374
+ commitMilestoneContext(tempDir, "M003");
356
375
 
357
376
  // Create mcp.json in .gsd/ AFTER the commit (untracked, like real usage).
358
377
  // Phase C removed copyPlanningArtifacts, so creation should not seed a
@@ -377,11 +396,7 @@ describe("auto-worktree lifecycle", () => {
377
396
 
378
397
  test("#2791: mcp.json synced via syncGsdStateToWorktree (ROOT_STATE_FILES)", () => {
379
398
  tempDir = createTempRepo();
380
- const msDir = join(tempDir, ".gsd", "milestones", "M003");
381
- mkdirSync(msDir, { recursive: true });
382
- writeFileSync(join(msDir, "CONTEXT.md"), "# M003 Context\n");
383
- run("git add .", tempDir);
384
- run("git commit -m \"add milestone\"", tempDir);
399
+ commitMilestoneContext(tempDir, "M003");
385
400
 
386
401
  // Create worktree first (no mcp.json yet)
387
402
  const wtPath = createAutoWorktree(tempDir, "M003");
@@ -293,11 +293,12 @@ describe('git-service', async () => {
293
293
 
294
294
  assert.deepStrictEqual(
295
295
  RUNTIME_EXCLUSION_PATHS.length,
296
- 16,
297
- "exactly 16 runtime exclusion paths"
296
+ 17,
297
+ "exactly 17 runtime exclusion paths"
298
298
  );
299
299
 
300
300
  const expectedPaths = [
301
+ ".gsd-worktrees/",
301
302
  ".gsd/activity/",
302
303
  ".gsd/audit/",
303
304
  ".gsd/forensics/",
@@ -0,0 +1,80 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { mkdirSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { dirname, join } from "node:path";
5
+
6
+ export type GsdIntegrationProject = {
7
+ root: string;
8
+ };
9
+
10
+ export type CreateGsdIntegrationProjectOptions = {
11
+ prefix?: string;
12
+ initialFiles?: Record<string, string>;
13
+ };
14
+
15
+ export function projectRoot(project: GsdIntegrationProject | string): string {
16
+ return typeof project === "string" ? project : project.root;
17
+ }
18
+
19
+ export function git(project: GsdIntegrationProject | string, ...args: string[]): string {
20
+ return execFileSync("git", args, {
21
+ cwd: projectRoot(project),
22
+ encoding: "utf-8",
23
+ stdio: ["ignore", "pipe", "pipe"],
24
+ }).trim();
25
+ }
26
+
27
+ export function createGsdIntegrationProject(
28
+ options: CreateGsdIntegrationProjectOptions | string = {},
29
+ ): GsdIntegrationProject {
30
+ const resolvedOptions = typeof options === "string" ? { prefix: options } : options;
31
+ const prefix = resolvedOptions.prefix ?? "gsd-integration-";
32
+ const root = realpathSync(mkdtempSync(join(tmpdir(), prefix)));
33
+
34
+ git(root, "init");
35
+ git(root, "config", "user.email", "test@test.com");
36
+ git(root, "config", "user.name", "Test");
37
+ git(root, "config", "core.autocrlf", "false");
38
+
39
+ writeProjectFile(root, "README.md", "# test\n");
40
+ for (const [relativePath, content] of Object.entries(resolvedOptions.initialFiles ?? {})) {
41
+ writeProjectFile(root, relativePath, content);
42
+ }
43
+ git(root, "add", ".");
44
+ git(root, "commit", "-m", "init");
45
+ git(root, "branch", "-M", "main");
46
+
47
+ return { root };
48
+ }
49
+
50
+ export function writeProjectFile(
51
+ project: GsdIntegrationProject | string,
52
+ relativePath: string,
53
+ content: string,
54
+ ): string {
55
+ const filePath = join(projectRoot(project), relativePath);
56
+ mkdirSync(dirname(filePath), { recursive: true });
57
+ writeFileSync(filePath, content, "utf-8");
58
+ return filePath;
59
+ }
60
+
61
+ export function writeGsdMilestoneContext(
62
+ project: GsdIntegrationProject | string,
63
+ milestoneId: string,
64
+ content = `# ${milestoneId} Context\n`,
65
+ ): string {
66
+ return writeProjectFile(
67
+ project,
68
+ join(".gsd", "milestones", milestoneId, "CONTEXT.md"),
69
+ content,
70
+ );
71
+ }
72
+
73
+ export function commitAll(project: GsdIntegrationProject | string, message: string): void {
74
+ git(project, "add", ".");
75
+ git(project, "commit", "-m", message);
76
+ }
77
+
78
+ export function cleanupGsdIntegrationProject(project: GsdIntegrationProject | string): void {
79
+ rmSync(projectRoot(project), { recursive: true, force: true });
80
+ }
@@ -60,7 +60,9 @@ test("ensureProjectWorkflowMcpConfig creates .mcp.json with workflow and browser
60
60
  assert.equal(typeof browserArgs[mcpArgIndex + 6], "string");
61
61
  assert.ok((browserArgs[mcpArgIndex + 6] ?? "").length > 0, "identity-key must be non-empty");
62
62
  assert.equal(browserArgs[mcpArgIndex + 7], "--identity-project");
63
- assert.equal(browserArgs[mcpArgIndex + 8], projectRoot);
63
+ assert.equal(typeof browserArgs[mcpArgIndex + 8], "string");
64
+ assert.ok((browserArgs[mcpArgIndex + 8] ?? "").length > 0, "identity-project must be non-empty");
65
+ assert.doesNotMatch(browserArgs[mcpArgIndex + 8] ?? "", /[\\/]/, "identity-project must not be a filesystem path");
64
66
  assert.equal((browserServer as { cwd?: string })?.cwd, projectRoot);
65
67
 
66
68
  const settings = JSON.parse(readFileSync(join(projectRoot, ".claude", "settings.local.json"), "utf-8")) as {
@@ -1,6 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { createRequire } from "node:module";
3
- import { copyFileSync, mkdtempSync, renameSync, rmSync } from "node:fs";
3
+ import { copyFileSync, mkdirSync, mkdtempSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
5
  import { tmpdir } from "node:os";
6
6
  import test from "node:test";
@@ -288,6 +288,90 @@ test("migration auto-check refreshes a stale open DB handle before comparing", a
288
288
  }
289
289
  });
290
290
 
291
+ function writeScratchMilestoneDir(base: string, milestoneId: string, file?: string): void {
292
+ const dir = join(base, ".gsd", "milestones", milestoneId);
293
+ mkdirSync(dir, { recursive: true });
294
+ if (file) writeFileSync(join(dir, file), `# ${milestoneId} discussion context\n`);
295
+ }
296
+
297
+ test("migration auto-check ignores discussion-scratch milestone dirs (CONTEXT only, no DB row)", async () => {
298
+ const base = makeBase();
299
+ try {
300
+ await writeGSDDirectory(projectFixture(), base); // markdown: M001 / S01 / T01
301
+ assert.equal(await ensureDbOpen(base), true);
302
+ insertMilestone({ id: "M001", title: "Legacy Milestone", status: "active" });
303
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Legacy Slice", status: "pending", risk: "medium", depends: [], demo: "Legacy slice demo", sequence: 1 });
304
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "Legacy Task", status: "pending" });
305
+
306
+ // Mid-discussion artifacts: dirs with no ROADMAP and no DB row. The queued
307
+ // DB row is only inserted at discussion handoff, so these are expected to
308
+ // be DB-less — not drift, and recover must not be recommended (it would
309
+ // import them as ghost active milestones).
310
+ writeScratchMilestoneDir(base, "M002", "M002-CONTEXT.md");
311
+ writeScratchMilestoneDir(base, "M003", "M003-CONTEXT-DRAFT.md");
312
+ writeScratchMilestoneDir(base, "M004"); // empty dir
313
+
314
+ const result = await checkMarkdownHierarchyAgainstDb(base);
315
+ assert.equal(result.action, "none");
316
+ assert.equal(result.reason, "in-sync");
317
+ assert.deepEqual(result.markdown, { milestones: 1, slices: 1, tasks: 1 });
318
+ } finally {
319
+ cleanup(base);
320
+ }
321
+ });
322
+
323
+ test("migration auto-check stays quiet mid-first-discussion (scratch dir over empty DB)", async () => {
324
+ const base = makeBase();
325
+ try {
326
+ await writeGSDDirectory({ projectContent: "# P\n", decisionsContent: "", requirements: [], milestones: [] }, base);
327
+ assert.equal(await ensureDbOpen(base), true);
328
+ writeScratchMilestoneDir(base, "M001", "M001-CONTEXT.md");
329
+
330
+ const result = await checkMarkdownHierarchyAgainstDb(base);
331
+ assert.equal(result.action, "none");
332
+ assert.equal(result.reason, "no-markdown");
333
+ } finally {
334
+ cleanup(base);
335
+ }
336
+ });
337
+
338
+ test("migration auto-check still reports real drift with scratch dirs excluded from counts", async () => {
339
+ const base = makeBase();
340
+ try {
341
+ await writeGSDDirectory(projectFixture(), base); // markdown: M001 / S01 / T01, DB empty
342
+ assert.equal(await ensureDbOpen(base), true);
343
+ writeScratchMilestoneDir(base, "M002", "M002-CONTEXT.md");
344
+
345
+ const result = await checkMarkdownHierarchyAgainstDb(base);
346
+ assert.equal(result.action, "recovery-required");
347
+ assert.equal(result.reason, "db-empty");
348
+ assert.equal(result.recoveryCommand, "/gsd recover --confirm");
349
+ // The scratch dir must not inflate the reported markdown count.
350
+ assert.deepEqual(result.markdown, { milestones: 1, slices: 1, tasks: 1 });
351
+ } finally {
352
+ cleanup(base);
353
+ }
354
+ });
355
+
356
+ test("migration auto-check still compares a roadmapless milestone that HAS a DB row", async () => {
357
+ const base = makeBase();
358
+ try {
359
+ await writeGSDDirectory({ projectContent: "# P\n", decisionsContent: "", requirements: [], milestones: [] }, base);
360
+ assert.equal(await ensureDbOpen(base), true);
361
+ // Post-handoff queued milestone: CONTEXT-only dir WITH a DB row. It must
362
+ // stay in the comparison (both sides have it → in-sync).
363
+ insertMilestone({ id: "M001", title: "M001", status: "queued" });
364
+ writeScratchMilestoneDir(base, "M001", "M001-CONTEXT.md");
365
+
366
+ const result = await checkMarkdownHierarchyAgainstDb(base);
367
+ assert.equal(result.action, "none");
368
+ assert.equal(result.reason, "in-sync");
369
+ assert.deepEqual(result.markdown, { milestones: 1, slices: 0, tasks: 0 });
370
+ } finally {
371
+ cleanup(base);
372
+ }
373
+ });
374
+
291
375
  test("rebuildMarkdownProjectionsFromDb realigns markdown when DB holds extra rows", async () => {
292
376
  const base = makeBase();
293
377
  try {
@@ -14,7 +14,7 @@ import { tmpdir } from "node:os";
14
14
  import { resolveExpectedArtifactPath } from "../auto-artifact-paths.ts";
15
15
  import { unitPhaseLabel, unitVerb } from "../auto-dashboard.ts";
16
16
  import { classifyUnitPhase } from "../metrics.ts";
17
- import { resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
17
+ import { resolveDefaultSessionModel, resolveModelWithFallbacksForUnit } from "../preferences-models.ts";
18
18
  import { KNOWN_UNIT_LABELS } from "../preferences-types.ts";
19
19
 
20
20
  function withModelPreferences<T>(fn: () => T): T {
@@ -92,6 +92,37 @@ test("run-uat falls back to completion when uat bucket is not configured", () =>
92
92
  }
93
93
  });
94
94
 
95
+ test("default session model resolves from the explicit project base path", () => {
96
+ const oldHome = process.env.GSD_HOME;
97
+ const originalCwd = process.cwd();
98
+ const home = mkdtempSync(join(tmpdir(), "gsd-model-map-home-"));
99
+ const base = mkdtempSync(join(tmpdir(), "gsd-model-map-project-"));
100
+
101
+ try {
102
+ process.env.GSD_HOME = home;
103
+ mkdirSync(join(base, ".gsd"), { recursive: true });
104
+ writeFileSync(join(base, ".gsd", "PREFERENCES.md"), [
105
+ "---",
106
+ "models:",
107
+ " execution: gpt-5.5",
108
+ "---",
109
+ "",
110
+ ].join("\n"));
111
+ process.chdir(home);
112
+
113
+ assert.deepEqual(resolveDefaultSessionModel("openai-codex", base), {
114
+ provider: "openai-codex",
115
+ id: "gpt-5.5",
116
+ });
117
+ } finally {
118
+ process.chdir(originalCwd);
119
+ if (oldHome === undefined) delete process.env.GSD_HOME;
120
+ else process.env.GSD_HOME = oldHome;
121
+ rmSync(home, { recursive: true, force: true });
122
+ rmSync(base, { recursive: true, force: true });
123
+ }
124
+ });
125
+
95
126
  test("every known unit label with a dispatch phase resolves when all model buckets are configured", () => {
96
127
  withModelPreferences(() => {
97
128
  const missing = KNOWN_UNIT_LABELS.filter((unitType) => !resolveModelWithFallbacksForUnit(unitType));
@@ -306,6 +306,38 @@ describe("notification-store", () => {
306
306
  rmSync(lockPath, { force: true });
307
307
  });
308
308
 
309
+ test("structured meta persists kind and scope on the entry", () => {
310
+ initNotificationStore(tmp);
311
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M005" });
312
+
313
+ const entries = readNotifications();
314
+ assert.equal(entries.length, 1);
315
+ assert.equal(entries[0].kind, "auto-stop");
316
+ assert.equal(entries[0].scope, "M005");
317
+ });
318
+
319
+ test("dedup keys on kind+scope when present, not on prose", () => {
320
+ initNotificationStore(tmp);
321
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M005" });
322
+ // Rephrased message, same structured identity → deduped within the window
323
+ appendNotification("Auto-mode blocked — gate rejected", "warning", "notify", { kind: "auto-stop", scope: "M005" });
324
+ // Same kind, different scope → distinct
325
+ appendNotification("Auto-mode blocked — validation gate", "warning", "notify", { kind: "auto-stop", scope: "M006" });
326
+
327
+ assert.equal(readNotifications().length, 2);
328
+ });
329
+
330
+ test("readNotifications filters by kind and scope", () => {
331
+ initNotificationStore(tmp);
332
+ appendNotification("a", "info", "notify", { kind: "auto-stop", scope: "M005" });
333
+ appendNotification("b", "info", "notify", { kind: "provider-error-pause", scope: "M005" });
334
+ appendNotification("c", "info");
335
+
336
+ assert.equal(readNotifications(tmp, { kind: "auto-stop" }).length, 1);
337
+ assert.equal(readNotifications(tmp, { scope: "M005" }).length, 2);
338
+ assert.equal(readNotifications(tmp, { kind: "provider-error-pause", scope: "M005" })[0].message, "b");
339
+ });
340
+
309
341
  test("listeners are notified on append, markAllRead, and clear", () => {
310
342
  initNotificationStore(tmp);
311
343
  let calls = 0;