gsd-pi 2.48.0 → 2.49.0-dev.9e177e9

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 (369) hide show
  1. package/dist/headless-ui.js +12 -2
  2. package/dist/headless.js +29 -13
  3. package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
  4. package/dist/resources/extensions/gsd/auto/phases.js +11 -11
  5. package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
  6. package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
  7. package/dist/resources/extensions/gsd/auto/session.js +4 -0
  8. package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
  9. package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +34 -7
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +34 -27
  12. package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
  13. package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
  14. package/dist/resources/extensions/gsd/auto-start.js +4 -31
  15. package/dist/resources/extensions/gsd/auto-timers.js +2 -2
  16. package/dist/resources/extensions/gsd/auto-verification.js +4 -7
  17. package/dist/resources/extensions/gsd/auto-worktree.js +262 -115
  18. package/dist/resources/extensions/gsd/auto.js +7 -5
  19. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
  20. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
  21. package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
  22. package/dist/resources/extensions/gsd/commands/handlers/auto.js +43 -3
  23. package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
  24. package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
  25. package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
  26. package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
  27. package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
  28. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
  29. package/dist/resources/extensions/gsd/doctor.js +9 -1
  30. package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
  31. package/dist/resources/extensions/gsd/git-service.js +20 -20
  32. package/dist/resources/extensions/gsd/gsd-db.js +124 -1
  33. package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
  34. package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
  35. package/dist/resources/extensions/gsd/preferences-types.js +2 -1
  36. package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
  37. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
  38. package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
  39. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +223 -56
  40. package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
  41. package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
  42. package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
  43. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  44. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  45. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  46. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  47. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  48. package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
  49. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
  50. package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  51. package/dist/resources/extensions/gsd/prompts/run-uat.md +4 -4
  52. package/dist/resources/extensions/gsd/repo-identity.js +29 -0
  53. package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
  54. package/dist/resources/extensions/gsd/session-forensics.js +6 -11
  55. package/dist/resources/extensions/gsd/session-lock.js +67 -56
  56. package/dist/resources/extensions/gsd/state.js +34 -7
  57. package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
  58. package/dist/resources/extensions/gsd/templates/plan.md +16 -0
  59. package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
  60. package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
  61. package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
  62. package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
  63. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
  64. package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
  65. package/dist/resources/extensions/gsd/worktree-command.js +1 -1
  66. package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
  67. package/dist/resources/extensions/gsd/worktree.js +3 -2
  68. package/dist/resources/extensions/remote-questions/config.js +3 -5
  69. package/dist/resources/extensions/search-the-web/native-search.js +8 -3
  70. package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
  71. package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
  72. package/dist/web/standalone/.next/BUILD_ID +1 -1
  73. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  74. package/dist/web/standalone/.next/build-manifest.json +4 -4
  75. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  76. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  77. package/dist/web/standalone/.next/required-server-files.json +4 -4
  78. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  79. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  80. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  81. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  86. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  89. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  90. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  91. package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
  92. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
  93. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  94. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +3 -3
  95. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  99. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  100. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  101. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  102. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  103. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  110. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  143. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  148. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  149. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  154. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
  163. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  165. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  166. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  167. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  169. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  173. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/index.html +1 -1
  179. package/dist/web/standalone/.next/server/app/index.rsc +4 -4
  180. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  181. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
  182. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  183. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +3 -3
  184. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  185. package/dist/web/standalone/.next/server/app/page.js +2 -2
  186. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  187. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  188. package/dist/web/standalone/.next/server/chunks/229.js +2 -2
  189. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  190. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  191. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/middleware.js +2 -2
  193. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  195. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  196. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  197. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  198. package/dist/web/standalone/.next/static/chunks/4024.7c75ac378de0f2b5.js +9 -0
  199. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  200. package/dist/web/standalone/.next/static/chunks/app/{layout-9ecfd95f343793f0.js → layout-a16c7a7ecdf0c2cf.js} +1 -1
  201. package/dist/web/standalone/.next/static/chunks/app/page-6654a8cca61a3d1c.js +1 -0
  202. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  203. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  204. package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
  205. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  206. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  207. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  208. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  209. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  210. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  211. package/dist/web/standalone/server.js +1 -1
  212. package/dist/worktree-cli.js +1 -1
  213. package/package.json +1 -1
  214. package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  215. package/packages/pi-agent-core/dist/agent-loop.js +4 -1
  216. package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
  217. package/packages/pi-agent-core/src/agent-loop.ts +4 -1
  218. package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
  219. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  220. package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
  221. package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
  222. package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
  223. package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
  224. package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
  225. package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
  226. package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
  227. package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
  228. package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
  229. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  230. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -32
  231. package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  232. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
  233. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
  234. package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
  235. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  236. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
  237. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  238. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
  239. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  240. package/packages/pi-coding-agent/package.json +1 -1
  241. package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
  242. package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
  243. package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
  244. package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
  245. package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
  246. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
  247. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
  248. package/pkg/package.json +1 -1
  249. package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +8 -4
  250. package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
  251. package/src/resources/extensions/gsd/auto/phases.ts +10 -11
  252. package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
  253. package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
  254. package/src/resources/extensions/gsd/auto/session.ts +5 -0
  255. package/src/resources/extensions/gsd/auto/types.ts +13 -0
  256. package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
  257. package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
  258. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -5
  259. package/src/resources/extensions/gsd/auto-loop.ts +1 -1
  260. package/src/resources/extensions/gsd/auto-post-unit.ts +36 -31
  261. package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
  262. package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
  263. package/src/resources/extensions/gsd/auto-start.ts +7 -27
  264. package/src/resources/extensions/gsd/auto-timers.ts +2 -2
  265. package/src/resources/extensions/gsd/auto-verification.ts +4 -7
  266. package/src/resources/extensions/gsd/auto-worktree.ts +309 -110
  267. package/src/resources/extensions/gsd/auto.ts +11 -10
  268. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
  269. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
  270. package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
  271. package/src/resources/extensions/gsd/commands/handlers/auto.ts +46 -3
  272. package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
  273. package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
  274. package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
  275. package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
  276. package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
  277. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
  278. package/src/resources/extensions/gsd/doctor.ts +9 -1
  279. package/src/resources/extensions/gsd/extension-manifest.json +1 -1
  280. package/src/resources/extensions/gsd/git-service.ts +19 -26
  281. package/src/resources/extensions/gsd/gsd-db.ts +150 -2
  282. package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
  283. package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
  284. package/src/resources/extensions/gsd/preferences-types.ts +5 -1
  285. package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
  286. package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
  287. package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
  288. package/src/resources/extensions/gsd/prompts/discuss-headless.md +223 -56
  289. package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
  290. package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
  291. package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
  292. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  293. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  294. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  295. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  296. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  297. package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
  298. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
  299. package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
  300. package/src/resources/extensions/gsd/prompts/run-uat.md +4 -4
  301. package/src/resources/extensions/gsd/repo-identity.ts +28 -0
  302. package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
  303. package/src/resources/extensions/gsd/session-forensics.ts +6 -11
  304. package/src/resources/extensions/gsd/session-lock.ts +92 -64
  305. package/src/resources/extensions/gsd/state.ts +38 -5
  306. package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
  307. package/src/resources/extensions/gsd/templates/plan.md +16 -0
  308. package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
  309. package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
  310. package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
  311. package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
  312. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +2 -2
  313. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
  314. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
  315. package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +1 -1
  316. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +14 -12
  317. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
  318. package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
  319. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +9 -12
  320. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
  321. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
  322. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
  323. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +1 -1
  324. package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
  325. package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
  326. package/src/resources/extensions/gsd/tests/git-service.test.ts +68 -9
  327. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  328. package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
  329. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -0
  330. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  331. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  332. package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +2 -2
  333. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +6 -6
  334. package/src/resources/extensions/gsd/tests/quality-gates.test.ts +347 -0
  335. package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
  336. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
  337. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
  338. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
  339. package/src/resources/extensions/gsd/tests/run-uat.test.ts +87 -15
  340. package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
  341. package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
  342. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
  343. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
  344. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
  345. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
  346. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
  347. package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
  348. package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
  349. package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
  350. package/src/resources/extensions/gsd/types.ts +30 -0
  351. package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
  352. package/src/resources/extensions/gsd/verification-gate.ts +0 -2
  353. package/src/resources/extensions/gsd/worktree-command.ts +1 -1
  354. package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
  355. package/src/resources/extensions/gsd/worktree.ts +3 -2
  356. package/src/resources/extensions/remote-questions/config.ts +3 -5
  357. package/src/resources/extensions/search-the-web/native-search.ts +8 -3
  358. package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
  359. package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
  360. package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
  361. package/dist/resources/extensions/gsd/resource-version.js +0 -97
  362. package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
  363. package/dist/web/standalone/.next/static/chunks/app/page-12dd5ece0df4badc.js +0 -1
  364. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  365. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  366. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
  367. package/src/resources/extensions/gsd/resource-version.ts +0 -101
  368. /package/dist/web/standalone/.next/static/{zGWUKFTfjCQerNgsPpAbF → vNN0h0emdEi8l_npi8poE}/_buildManifest.js +0 -0
  369. /package/dist/web/standalone/.next/static/{zGWUKFTfjCQerNgsPpAbF → vNN0h0emdEi8l_npi8poE}/_ssgManifest.js +0 -0
@@ -171,7 +171,7 @@ test('(k) run-uat prompt template', () => {
171
171
  const milestoneId = 'M001';
172
172
  const sliceId = 'S01';
173
173
  const uatPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
174
- const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT-RESULT.md';
174
+ const uatResultPath = '.gsd/milestones/M001/slices/S01/S01-UAT.md';
175
175
  const uatType = 'live-runtime';
176
176
  const inlinedContext = '<!-- no context -->';
177
177
  let promptResult: string | undefined;
@@ -234,7 +234,7 @@ test('(k2) run-uat prompt references gsd_summary_save, not direct write', () =>
234
234
  milestoneId: 'M001',
235
235
  sliceId: 'S01',
236
236
  uatPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
237
- uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT-RESULT.md',
237
+ uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
238
238
  uatType: 'artifact-driven',
239
239
  inlinedContext: '<!-- no context -->',
240
240
  });
@@ -265,14 +265,13 @@ test('(l) dispatch preconditions via resolveSliceFile', () => {
265
265
  'resolveSliceFile(..., "UAT") returns non-null when UAT file exists (dispatch trigger state)',
266
266
  );
267
267
 
268
- const uatResultFilePath = resolveSliceFile(base, 'M001', 'S01', 'UAT-RESULT');
269
- assert.deepStrictEqual(
270
- uatResultFilePath,
271
- null,
272
- 'resolveSliceFile(..., "UAT-RESULT") returns null when result file missing (dispatch trigger state)',
268
+ // UAT spec without a verdict line means UAT has not been run yet
269
+ const rawContent = readFileSync(uatFilePath!, 'utf-8');
270
+ assert.ok(
271
+ !/verdict:\s*[\w-]+/i.test(rawContent),
272
+ 'UAT file without verdict indicates UAT has not been run (dispatch trigger state)',
273
273
  );
274
274
 
275
- const rawContent = readFileSync(uatFilePath!, 'utf-8');
276
275
  assert.deepStrictEqual(
277
276
  extractUatType(rawContent),
278
277
  'artifact-driven',
@@ -286,13 +285,18 @@ test('(l) dispatch preconditions via resolveSliceFile', () => {
286
285
  test('test block at line 307', () => {
287
286
  const base = createFixtureBase();
288
287
  try {
289
- writeSliceFile(base, 'M001', 'S01', 'UAT', makeUatContent('artifact-driven'));
290
- writeSliceFile(base, 'M001', 'S01', 'UAT-RESULT', '# UAT Result\n\nverdict: PASS\n');
288
+ // Write UAT file with a verdict — simulates completed UAT
289
+ writeSliceFile(base, 'M001', 'S01', 'UAT', '# UAT Result\n\nverdict: PASS\n');
291
290
 
292
- const uatResultFilePath = resolveSliceFile(base, 'M001', 'S01', 'UAT-RESULT');
291
+ const uatFilePath = resolveSliceFile(base, 'M001', 'S01', 'UAT');
293
292
  assert.ok(
294
- uatResultFilePath !== null,
295
- 'resolveSliceFile(..., "UAT-RESULT") returns non-null when result file exists (idempotent skip state)',
293
+ uatFilePath !== null,
294
+ 'resolveSliceFile(..., "UAT") returns non-null when UAT file exists',
295
+ );
296
+ const content = readFileSync(uatFilePath!, 'utf-8');
297
+ assert.ok(
298
+ /verdict:\s*[\w-]+/i.test(content),
299
+ 'UAT file with verdict indicates UAT has been completed (idempotent skip state)',
296
300
  );
297
301
  } finally {
298
302
  cleanup(base);
@@ -343,6 +347,74 @@ test('(m) non-artifact UAT skip', async () => {
343
347
  }
344
348
  });
345
349
 
350
+ test('(o) verdict gate: PARTIAL is acceptable for mixed/human-experience/live-runtime UAT types', () => {
351
+ // This test verifies the contract that extractUatType correctly identifies
352
+ // the modes where PARTIAL should not block progression.
353
+ // The verdict gate in auto-dispatch.ts uses this to build acceptableVerdicts.
354
+ const mixedType = extractUatType(makeUatContent('mixed'));
355
+ const humanExpType = extractUatType(makeUatContent('human-experience'));
356
+ const liveRuntimeType = extractUatType(makeUatContent('live-runtime'));
357
+ const artifactType = extractUatType(makeUatContent('artifact-driven'));
358
+ const browserType = extractUatType(makeUatContent('browser-executable'));
359
+ const runtimeExecType = extractUatType(makeUatContent('runtime-executable'));
360
+
361
+ // These modes should allow PARTIAL (non-fully-automatable)
362
+ const partialAcceptableModes = ['mixed', 'human-experience', 'live-runtime'];
363
+ assert.ok(
364
+ partialAcceptableModes.includes(mixedType!),
365
+ `mixed → "${mixedType}" is in partialAcceptableModes`,
366
+ );
367
+ assert.ok(
368
+ partialAcceptableModes.includes(humanExpType!),
369
+ `human-experience → "${humanExpType}" is in partialAcceptableModes`,
370
+ );
371
+ assert.ok(
372
+ partialAcceptableModes.includes(liveRuntimeType!),
373
+ `live-runtime → "${liveRuntimeType}" is in partialAcceptableModes`,
374
+ );
375
+
376
+ // These modes should NOT allow PARTIAL (fully automatable)
377
+ assert.ok(
378
+ !partialAcceptableModes.includes(artifactType!),
379
+ `artifact-driven → "${artifactType}" is NOT in partialAcceptableModes`,
380
+ );
381
+ assert.ok(
382
+ !partialAcceptableModes.includes(browserType!),
383
+ `browser-executable → "${browserType}" is NOT in partialAcceptableModes`,
384
+ );
385
+ assert.ok(
386
+ !partialAcceptableModes.includes(runtimeExecType!),
387
+ `runtime-executable → "${runtimeExecType}" is NOT in partialAcceptableModes`,
388
+ );
389
+ });
390
+
391
+ test('(p) run-uat prompt allows PASS when human-only checks remain as NEEDS-HUMAN', () => {
392
+ const promptResult = loadPromptFromWorktree('run-uat', {
393
+ workingDirectory: '/tmp/test-project',
394
+ milestoneId: 'M001',
395
+ sliceId: 'S01',
396
+ uatPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
397
+ uatResultPath: '.gsd/milestones/M001/slices/S01/S01-UAT.md',
398
+ uatType: 'mixed',
399
+ inlinedContext: '<!-- no context -->',
400
+ });
401
+
402
+ // PASS verdict should be usable when automatable checks pass (even with NEEDS-HUMAN remaining)
403
+ assert.ok(
404
+ /PASS.*automatable checks passed/i.test(promptResult),
405
+ 'prompt defines PASS as valid when all automatable checks passed',
406
+ );
407
+ assert.ok(
408
+ /PARTIAL.*automatable checks.*(skipped|inconclusive)/i.test(promptResult),
409
+ 'prompt reserves PARTIAL for when automatable checks themselves are inconclusive',
410
+ );
411
+ // human-experience mode should NOT force PARTIAL when automatable checks pass
412
+ assert.ok(
413
+ !promptResult.includes('use an overall verdict of `PARTIAL`'),
414
+ 'prompt does not force PARTIAL verdict for human-experience mode',
415
+ );
416
+ });
417
+
346
418
  test('(n) stale replay guard', async () => {
347
419
  const base = createFixtureBase();
348
420
  try {
@@ -364,7 +436,7 @@ test('(n) stale replay guard', async () => {
364
436
  );
365
437
 
366
438
  writeSliceFile(base, 'M001', 'S01', 'UAT', makeUatContent('artifact-driven'));
367
- writeSliceFile(base, 'M001', 'S01', 'UAT-RESULT', '---\nverdict: FAIL\n---\n');
439
+ writeSliceFile(base, 'M001', 'S01', 'UAT', '---\nverdict: FAIL\n---\n');
368
440
 
369
441
  const state = {
370
442
  activeMilestone: { id: 'M001', title: 'Test roadmap' },
@@ -381,7 +453,7 @@ test('(n) stale replay guard', async () => {
381
453
  assert.deepStrictEqual(
382
454
  result,
383
455
  null,
384
- 'existing UAT-RESULT with FAIL verdict does not re-dispatch; verdict gate owns blocking',
456
+ 'existing UAT with FAIL verdict does not re-dispatch; verdict gate owns blocking',
385
457
  );
386
458
  } finally {
387
459
  cleanup(base);
@@ -0,0 +1,223 @@
1
+ /**
2
+ * session-lock-transient-read.test.ts — Tests for transient lock file unreadability (#2324).
3
+ *
4
+ * Regression coverage for:
5
+ * #2324 onCompromised declares lock lost when the lock file is temporarily
6
+ * unreadable (NFS/CIFS latency, macOS APFS snapshot, concurrent process
7
+ * briefly holding the file).
8
+ *
9
+ * Tests:
10
+ * - readExistingLockDataWithRetry retries on transient read failure
11
+ * - readExistingLockDataWithRetry returns data when file becomes readable after retries
12
+ * - readExistingLockDataWithRetry returns null only when ALL retries exhausted
13
+ * - onCompromised does not declare compromise when lock file is transiently unreadable
14
+ */
15
+
16
+ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, renameSync, unlinkSync, chmodSync } from 'node:fs';
17
+ import { join } from 'node:path';
18
+ import { tmpdir } from 'node:os';
19
+ import { execSync, spawn } from 'node:child_process';
20
+
21
+ import {
22
+ acquireSessionLock,
23
+ getSessionLockStatus,
24
+ releaseSessionLock,
25
+ readExistingLockDataWithRetry,
26
+ type SessionLockData,
27
+ } from '../session-lock.ts';
28
+ import { gsdRoot } from '../paths.ts';
29
+ import { createTestContext } from './test-helpers.ts';
30
+
31
+ const { assertEq, assertTrue, report } = createTestContext();
32
+
33
+ async function main(): Promise<void> {
34
+
35
+ // ─── 1. readExistingLockDataWithRetry succeeds on first read when file is fine ─
36
+ console.log('\n=== 1. readExistingLockDataWithRetry reads file normally ===');
37
+ {
38
+ const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
39
+ mkdirSync(join(base, '.gsd'), { recursive: true });
40
+
41
+ try {
42
+ const lockFile = join(gsdRoot(base), 'auto.lock');
43
+ const lockData: SessionLockData = {
44
+ pid: process.pid,
45
+ startedAt: new Date().toISOString(),
46
+ unitType: 'execute-task',
47
+ unitId: 'M001/S01/T01',
48
+ unitStartedAt: new Date().toISOString(),
49
+ sessionFile: 'test-session.json',
50
+ };
51
+ writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
52
+
53
+ const result = readExistingLockDataWithRetry(lockFile);
54
+ assertTrue(result !== null, 'data returned for readable file');
55
+ assertEq(result!.pid, process.pid, 'correct PID read');
56
+ assertEq(result!.sessionFile, 'test-session.json', 'correct sessionFile read');
57
+ } finally {
58
+ rmSync(base, { recursive: true, force: true });
59
+ }
60
+ }
61
+
62
+ // ─── 2. readExistingLockDataWithRetry returns null for truly missing file ──
63
+ console.log('\n=== 2. readExistingLockDataWithRetry returns null for missing file ===');
64
+ {
65
+ const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
66
+ mkdirSync(join(base, '.gsd'), { recursive: true });
67
+
68
+ try {
69
+ const lockFile = join(gsdRoot(base), 'auto.lock');
70
+ // File doesn't exist
71
+ const result = readExistingLockDataWithRetry(lockFile, { maxAttempts: 2, delayMs: 10 });
72
+ assertEq(result, null, 'null for truly missing file after retries');
73
+ } finally {
74
+ rmSync(base, { recursive: true, force: true });
75
+ }
76
+ }
77
+
78
+ // ─── 3. readExistingLockDataWithRetry recovers after transient rename ──────
79
+ console.log('\n=== 3. readExistingLockDataWithRetry recovers after transient unavailability ===');
80
+ {
81
+ const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
82
+ mkdirSync(join(base, '.gsd'), { recursive: true });
83
+
84
+ try {
85
+ const lockFile = join(gsdRoot(base), 'auto.lock');
86
+ const tmpFile = lockFile + '.hidden';
87
+ const lockData: SessionLockData = {
88
+ pid: process.pid,
89
+ startedAt: new Date().toISOString(),
90
+ unitType: 'execute-task',
91
+ unitId: 'M001/S01/T01',
92
+ unitStartedAt: new Date().toISOString(),
93
+ sessionFile: 'recovery-session.json',
94
+ };
95
+ writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
96
+
97
+ // Simulate transient unavailability: move file away, spawn a child process
98
+ // to restore it after 100ms. The child runs outside our event loop so it
99
+ // fires even during busy-wait retries.
100
+ renameSync(lockFile, tmpFile);
101
+ spawn('bash', ['-c', `sleep 0.1 && mv "${tmpFile}" "${lockFile}"`], { stdio: 'ignore', detached: true }).unref();
102
+
103
+ // With retries (3 attempts, 200ms delay), it should recover on 2nd or 3rd attempt
104
+ const result = readExistingLockDataWithRetry(lockFile, { maxAttempts: 3, delayMs: 200 });
105
+ assertTrue(result !== null, 'data recovered after transient unavailability');
106
+ if (result) {
107
+ assertEq(result.pid, process.pid, 'correct PID after recovery');
108
+ assertEq(result.sessionFile, 'recovery-session.json', 'correct sessionFile after recovery');
109
+ }
110
+ } finally {
111
+ rmSync(base, { recursive: true, force: true });
112
+ }
113
+ }
114
+
115
+ // ─── 4. readExistingLockDataWithRetry recovers from transient permission error ─
116
+ console.log('\n=== 4. readExistingLockDataWithRetry recovers from transient permission error ===');
117
+ {
118
+ const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
119
+ mkdirSync(join(base, '.gsd'), { recursive: true });
120
+
121
+ try {
122
+ const lockFile = join(gsdRoot(base), 'auto.lock');
123
+ const lockData: SessionLockData = {
124
+ pid: process.pid,
125
+ startedAt: new Date().toISOString(),
126
+ unitType: 'execute-task',
127
+ unitId: 'M001/S01/T01',
128
+ unitStartedAt: new Date().toISOString(),
129
+ sessionFile: 'perm-session.json',
130
+ };
131
+ writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
132
+
133
+ // Remove read permission to simulate NFS/CIFS latency, then spawn a child
134
+ // to restore permissions after 100ms (runs outside our event loop).
135
+ chmodSync(lockFile, 0o000);
136
+ spawn('bash', ['-c', `sleep 0.1 && chmod 644 "${lockFile}"`], { stdio: 'ignore', detached: true }).unref();
137
+
138
+ const result = readExistingLockDataWithRetry(lockFile, { maxAttempts: 3, delayMs: 200 });
139
+ assertTrue(result !== null, 'data recovered after transient permission error');
140
+ if (result) {
141
+ assertEq(result.pid, process.pid, 'correct PID after permission recovery');
142
+ }
143
+
144
+ // Ensure permissions restored for cleanup
145
+ try { chmodSync(lockFile, 0o644); } catch { /* best-effort */ }
146
+ } finally {
147
+ rmSync(base, { recursive: true, force: true });
148
+ }
149
+ }
150
+
151
+ // ─── 5. getSessionLockStatus does not false-positive on transient read failure ─
152
+ console.log('\n=== 5. getSessionLockStatus tolerates transient lock file unavailability ===');
153
+ {
154
+ const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
155
+ mkdirSync(join(base, '.gsd'), { recursive: true });
156
+
157
+ try {
158
+ const result = acquireSessionLock(base);
159
+ assertTrue(result.acquired, 'lock acquired');
160
+
161
+ // Validate works initially
162
+ const status1 = getSessionLockStatus(base);
163
+ assertTrue(status1.valid, 'lock valid before transient failure');
164
+
165
+ // Temporarily hide the lock file
166
+ const lockFile = join(gsdRoot(base), 'auto.lock');
167
+ const tmpFile = lockFile + '.hidden';
168
+ renameSync(lockFile, tmpFile);
169
+
170
+ // Schedule restoration
171
+ setTimeout(() => {
172
+ try { renameSync(tmpFile, lockFile); } catch { /* best-effort */ }
173
+ }, 30);
174
+
175
+ // Small delay to ensure restoration runs, then check — with the OS lock
176
+ // still held, getSessionLockStatus should return valid=true even if the
177
+ // lock file was briefly missing (it checks _releaseFunction first).
178
+ await new Promise(r => setTimeout(r, 60));
179
+ const status2 = getSessionLockStatus(base);
180
+ assertTrue(status2.valid, 'lock still valid after transient file disappearance (OS lock held)');
181
+
182
+ // Restore if not yet restored
183
+ try { renameSync(tmpFile, lockFile); } catch { /* already restored */ }
184
+
185
+ releaseSessionLock(base);
186
+ } finally {
187
+ rmSync(base, { recursive: true, force: true });
188
+ }
189
+ }
190
+
191
+ // ─── 6. Retry defaults: 3 attempts with 200ms delay ────────────────────────
192
+ console.log('\n=== 6. Default retry params: function works with defaults ===');
193
+ {
194
+ const base = mkdtempSync(join(tmpdir(), 'gsd-transient-'));
195
+ mkdirSync(join(base, '.gsd'), { recursive: true });
196
+
197
+ try {
198
+ const lockFile = join(gsdRoot(base), 'auto.lock');
199
+ const lockData: SessionLockData = {
200
+ pid: process.pid,
201
+ startedAt: new Date().toISOString(),
202
+ unitType: 'execute-task',
203
+ unitId: 'M001/S01/T01',
204
+ unitStartedAt: new Date().toISOString(),
205
+ sessionFile: 'status-session.json',
206
+ };
207
+ writeFileSync(lockFile, JSON.stringify(lockData, null, 2));
208
+
209
+ // Call with no options — uses defaults (3 attempts, 200ms)
210
+ const result = readExistingLockDataWithRetry(lockFile);
211
+ assertTrue(result !== null, 'default params work for readable file');
212
+ } finally {
213
+ rmSync(base, { recursive: true, force: true });
214
+ }
215
+ }
216
+
217
+ report();
218
+ }
219
+
220
+ main().catch((error) => {
221
+ console.error(error);
222
+ process.exit(1);
223
+ });
@@ -75,7 +75,7 @@ test("buildSkillActivationBlock activates skills via prefer_skills when context
75
75
  prefer_skills: ["react"],
76
76
  });
77
77
 
78
- assert.match(result, /Call Skill\('react'\)/);
78
+ assert.match(result, /Call Skill\(\{ skill: 'react' \}\)/);
79
79
  assert.doesNotMatch(result, /swiftui/);
80
80
  } finally {
81
81
  cleanup(base);
@@ -92,7 +92,7 @@ test("buildSkillActivationBlock includes always_use_skills from preferences usin
92
92
  always_use_skills: ["swift-testing"],
93
93
  });
94
94
 
95
- assert.equal(result, "<skill_activation>Call Skill('swift-testing').</skill_activation>");
95
+ assert.equal(result, "<skill_activation>Call Skill({ skill: 'swift-testing' }).</skill_activation>");
96
96
  } finally {
97
97
  cleanup(base);
98
98
  }
@@ -120,8 +120,8 @@ test("buildSkillActivationBlock includes skill_rules matches and task-plan skill
120
120
  skill_rules: [{ when: "prisma database schema", use: ["prisma"] }],
121
121
  });
122
122
 
123
- assert.match(result, /Call Skill\('accessibility'\)/);
124
- assert.match(result, /Call Skill\('prisma'\)/);
123
+ assert.match(result, /Call Skill\(\{ skill: 'accessibility' \}\)/);
124
+ assert.match(result, /Call Skill\(\{ skill: 'prisma' \}\)/);
125
125
  } finally {
126
126
  cleanup(base);
127
127
  }
@@ -191,3 +191,43 @@ test("buildSkillActivationBlock does not activate skills from extraContext or ta
191
191
  cleanup(base);
192
192
  }
193
193
  });
194
+
195
+ test("buildSkillActivationBlock rejects skill names with special characters", () => {
196
+ const base = makeTempBase();
197
+ try {
198
+ // Skill names with quotes, braces, or other non-alphanumeric characters are
199
+ // rejected by the SAFE_SKILL_NAME guard to prevent prompt injection.
200
+ writeSkill(base, "my-skill's", "Skill with apostrophe in name.");
201
+ loadOnlyTestSkills(base);
202
+
203
+ const result = buildBlock(base, {}, {
204
+ always_use_skills: ["my-skill's"],
205
+ });
206
+
207
+ // Unsafe skill name is filtered out — empty result
208
+ assert.equal(result, "");
209
+ } finally {
210
+ cleanup(base);
211
+ }
212
+ });
213
+
214
+ test("buildSkillActivationBlock allows valid skill names and rejects invalid ones", () => {
215
+ const base = makeTempBase();
216
+ try {
217
+ writeSkill(base, "react", "React skill.");
218
+ writeSkill(base, "bad'name", "Injection attempt.");
219
+ writeSkill(base, "good-skill-2", "Another valid skill.");
220
+ loadOnlyTestSkills(base);
221
+
222
+ const result = buildBlock(base, {}, {
223
+ always_use_skills: ["react", "bad'name", "good-skill-2"],
224
+ });
225
+
226
+ assert.match(result, /skill_activation/);
227
+ assert.match(result, /Call Skill\(\{ skill: 'react' \}\)/);
228
+ assert.match(result, /Call Skill\(\{ skill: 'good-skill-2' \}\)/);
229
+ assert.doesNotMatch(result, /bad'name/);
230
+ } finally {
231
+ cleanup(base);
232
+ }
233
+ });
@@ -44,7 +44,7 @@ console.log('\n── Tool naming: registration count ──');
44
44
  const pi = makeMockPi();
45
45
  registerDbTools(pi);
46
46
 
47
- assert.deepStrictEqual(pi.tools.length, 26, 'Should register exactly 26 tools (13 canonical + 13 aliases)');
47
+ assert.deepStrictEqual(pi.tools.length, 27, 'Should register exactly 27 tools (13 canonical + 13 aliases + 1 gate tool)');
48
48
 
49
49
  // ─── Both names exist for each pair ──────────────────────────────────────────
50
50
 
@@ -6,7 +6,8 @@ import { tmpdir } from "node:os";
6
6
  import { randomUUID } from "node:crypto";
7
7
 
8
8
  import { deriveState, isValidationTerminal } from "../state.ts";
9
- import { resolveExpectedArtifactPath, verifyExpectedArtifact, diagnoseExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.ts";
9
+ import { resolveExpectedArtifactPath, diagnoseExpectedArtifact } from "../auto-artifact-paths.ts";
10
+ import { verifyExpectedArtifact, buildLoopRemediationSteps } from "../auto-recovery.ts";
10
11
  import { resolveDispatch, type DispatchContext } from "../auto-dispatch.ts";
11
12
  import type { GSDState } from "../types.ts";
12
13
  import { clearPathCache } from "../paths.ts";
@@ -226,8 +226,6 @@ describe("verification-gate: execution", () => {
226
226
 
227
227
  test("all commands pass → gate passes", () => {
228
228
  const result = runVerificationGate({
229
- basePath: tmp,
230
- unitId: "T01",
231
229
  cwd: tmp,
232
230
  preferenceCommands: ["echo hello", "echo world"],
233
231
  });
@@ -243,8 +241,6 @@ describe("verification-gate: execution", () => {
243
241
 
244
242
  test("one command fails → gate fails with exit code + stderr", () => {
245
243
  const result = runVerificationGate({
246
- basePath: tmp,
247
- unitId: "T01",
248
244
  cwd: tmp,
249
245
  preferenceCommands: ["echo ok", "sh -c 'echo err >&2; exit 1'"],
250
246
  });
@@ -257,8 +253,6 @@ describe("verification-gate: execution", () => {
257
253
 
258
254
  test("no commands discovered → gate passes with 0 checks", () => {
259
255
  const result = runVerificationGate({
260
- basePath: tmp,
261
- unitId: "T01",
262
256
  cwd: tmp,
263
257
  });
264
258
  assert.equal(result.passed, true);
@@ -268,8 +262,6 @@ describe("verification-gate: execution", () => {
268
262
 
269
263
  test("command not found → exit code 127", () => {
270
264
  const result = runVerificationGate({
271
- basePath: tmp,
272
- unitId: "T01",
273
265
  cwd: tmp,
274
266
  preferenceCommands: ["__nonexistent_command_xyz_42__"],
275
267
  });
@@ -289,8 +281,6 @@ describe("verification-gate: execution", () => {
289
281
  const script = [
290
282
  `import { runVerificationGate } from ${JSON.stringify(pathToFileURL(gatePath).href)};`,
291
283
  `runVerificationGate({`,
292
- ` basePath: ${JSON.stringify(tmp)},`,
293
- ` unitId: "T-DEP",`,
294
284
  ` cwd: ${JSON.stringify(tmp)},`,
295
285
  ` preferenceCommands: ["echo dep0190-check"],`,
296
286
  `});`,
@@ -317,8 +307,6 @@ describe("verification-gate: execution", () => {
317
307
 
318
308
  test("each check has durationMs", () => {
319
309
  const result = runVerificationGate({
320
- basePath: tmp,
321
- unitId: "T01",
322
310
  cwd: tmp,
323
311
  preferenceCommands: ["echo fast"],
324
312
  });
@@ -330,8 +318,6 @@ describe("verification-gate: execution", () => {
330
318
  test("one command fails — remaining commands still run (non-short-circuit)", () => {
331
319
  // First fails, second and third should still execute
332
320
  const result = runVerificationGate({
333
- basePath: tmp,
334
- unitId: "T02",
335
321
  cwd: tmp,
336
322
  preferenceCommands: [
337
323
  "sh -c 'exit 1'",
@@ -351,8 +337,6 @@ describe("verification-gate: execution", () => {
351
337
  test("gate execution uses cwd for spawnSync", () => {
352
338
  // pwd should report the temp dir
353
339
  const result = runVerificationGate({
354
- basePath: tmp,
355
- unitId: "T02",
356
340
  cwd: tmp,
357
341
  preferenceCommands: ["pwd"],
358
342
  });
@@ -846,3 +846,70 @@ test("GitService is rebuilt with originalBasePath after exitMilestone", () => {
846
846
 
847
847
  assert.equal(gitServiceBasePath, "/project"); // project root, not worktree
848
848
  });
849
+
850
+ // ─── Isolation Degradation Tests (#2483) ──────────────────────────────────
851
+
852
+ test("enterMilestone sets isolationDegraded when worktree creation throws (#2483)", () => {
853
+ const s = makeSession();
854
+ const deps = makeDeps({
855
+ getAutoWorktreePath: () => null,
856
+ createAutoWorktree: () => {
857
+ throw new Error("empty repo");
858
+ },
859
+ });
860
+ const ctx = makeNotifyCtx();
861
+ const resolver = new WorktreeResolver(s, deps);
862
+
863
+ resolver.enterMilestone("M001", ctx);
864
+
865
+ assert.equal(s.isolationDegraded, true);
866
+ assert.equal(s.basePath, "/project"); // unchanged — error recovery
867
+ });
868
+
869
+ test("enterMilestone is no-op when isolationDegraded is true (#2483)", () => {
870
+ const s = makeSession();
871
+ s.isolationDegraded = true;
872
+ const deps = makeDeps();
873
+ const ctx = makeNotifyCtx();
874
+ const resolver = new WorktreeResolver(s, deps);
875
+
876
+ resolver.enterMilestone("M001", ctx);
877
+
878
+ assert.equal(s.basePath, "/project"); // unchanged
879
+ assert.equal(findCalls(deps.calls, "createAutoWorktree").length, 0);
880
+ assert.equal(findCalls(deps.calls, "enterAutoWorktree").length, 0);
881
+ assert.equal(findCalls(deps.calls, "shouldUseWorktreeIsolation").length, 0);
882
+ });
883
+
884
+ test("mergeAndExit is no-op when isolationDegraded is true (#2483)", () => {
885
+ const s = makeSession({
886
+ basePath: "/project",
887
+ originalBasePath: "/project",
888
+ });
889
+ s.isolationDegraded = true;
890
+ const deps = makeDeps({
891
+ getIsolationMode: () => "worktree",
892
+ });
893
+ const ctx = makeNotifyCtx();
894
+ const resolver = new WorktreeResolver(s, deps);
895
+
896
+ resolver.mergeAndExit("M001", ctx);
897
+
898
+ assert.equal(findCalls(deps.calls, "mergeMilestoneToMain").length, 0);
899
+ assert.equal(findCalls(deps.calls, "teardownAutoWorktree").length, 0);
900
+ assert.equal(findCalls(deps.calls, "getIsolationMode").length, 0);
901
+ assert.ok(
902
+ ctx.messages.some(
903
+ (m) => m.level === "info" && m.msg.includes("isolation was degraded"),
904
+ ),
905
+ );
906
+ });
907
+
908
+ test("isolationDegraded is reset by session.reset() (#2483)", () => {
909
+ const s = new AutoSession();
910
+ s.isolationDegraded = true;
911
+
912
+ s.reset();
913
+
914
+ assert.equal(s.isolationDegraded, false);
915
+ });
@@ -27,7 +27,7 @@ import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, readFileSync
27
27
  import { join } from 'node:path';
28
28
  import { tmpdir } from 'node:os';
29
29
 
30
- import { syncProjectRootToWorktree } from '../auto-worktree-sync.ts';
30
+ import { syncProjectRootToWorktree } from '../auto-worktree.ts';
31
31
  import { syncGsdStateToWorktree, syncWorktreeStateBack } from '../auto-worktree.ts';
32
32
  import { describe, test } from 'node:test';
33
33
  import assert from 'node:assert/strict';