@opengsd/gsd-pi 1.5.0-dev.553dc518 → 1.5.0-dev.c4c8885f

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 (302) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/ask-user-questions.js +1 -1
  3. package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
  4. package/dist/resources/extensions/gsd/activity-log.js +1 -1
  5. package/dist/resources/extensions/gsd/auto/orchestrator.js +16 -10
  6. package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -5
  7. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +7 -2
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +69 -17
  9. package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
  11. package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
  12. package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
  13. package/dist/resources/extensions/gsd/auto-start.js +54 -31
  14. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
  15. package/dist/resources/extensions/gsd/auto-timers.js +9 -2
  16. package/dist/resources/extensions/gsd/auto.js +97 -31
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
  18. package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +67 -6
  19. package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
  20. package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
  21. package/dist/resources/extensions/gsd/config-overlay.js +7 -2
  22. package/dist/resources/extensions/gsd/db/engine.js +33 -0
  23. package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
  24. package/dist/resources/extensions/gsd/db-adapter.js +30 -3
  25. package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  26. package/dist/resources/extensions/gsd/flat-phase-migration.js +18 -0
  27. package/dist/resources/extensions/gsd/gsd-db.js +12 -11
  28. package/dist/resources/extensions/gsd/guided-flow.js +16 -7
  29. package/dist/resources/extensions/gsd/journal.js +1 -1
  30. package/dist/resources/extensions/gsd/markdown-renderer.js +34 -91
  31. package/dist/resources/extensions/gsd/md-importer.js +89 -23
  32. package/dist/resources/extensions/gsd/metrics.js +2 -2
  33. package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
  34. package/dist/resources/extensions/gsd/paths.js +87 -52
  35. package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
  36. package/dist/resources/extensions/gsd/preferences-models.js +66 -15
  37. package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
  38. package/dist/resources/extensions/gsd/preferences-validation.js +66 -10
  39. package/dist/resources/extensions/gsd/preferences.js +20 -0
  40. package/dist/resources/extensions/gsd/rule-registry.js +106 -10
  41. package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
  42. package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
  43. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
  44. package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
  45. package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
  46. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
  47. package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
  48. package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
  49. package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
  50. package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
  51. package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
  52. package/dist/resources/extensions/gsd/uok/audit.js +3 -0
  53. package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
  54. package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
  55. package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
  56. package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
  57. package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
  58. package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
  59. package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
  60. package/dist/resources/extensions/gsd/workspace.js +5 -14
  61. package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
  62. package/dist/resources/extensions/shared/interview-ui.js +62 -9
  63. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  64. package/dist/web/standalone/.next/BUILD_ID +1 -1
  65. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  66. package/dist/web/standalone/.next/build-manifest.json +2 -2
  67. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  68. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  83. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  84. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  85. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  86. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  87. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  88. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  89. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  90. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  91. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  92. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  93. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  94. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  95. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  96. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  97. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  98. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  99. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  100. package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
  101. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  102. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  103. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  104. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  105. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  106. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  107. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  108. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  109. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  110. package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
  111. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  112. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  114. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  115. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  116. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  117. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  118. package/dist/web/standalone/.next/server/app/index.html +1 -1
  119. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  122. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  123. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  124. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  125. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  126. package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
  127. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  128. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  130. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  131. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  132. package/dist/worktree-cli-create.d.ts +10 -0
  133. package/dist/worktree-cli-create.js +11 -0
  134. package/dist/worktree-cli-format.d.ts +12 -0
  135. package/dist/worktree-cli-format.js +38 -0
  136. package/dist/worktree-cli-plan.d.ts +16 -0
  137. package/dist/worktree-cli-plan.js +19 -0
  138. package/dist/worktree-cli-session.d.ts +13 -0
  139. package/dist/worktree-cli-session.js +18 -0
  140. package/dist/worktree-cli-status.d.ts +35 -0
  141. package/dist/worktree-cli-status.js +54 -0
  142. package/dist/worktree-cli.d.ts +1 -16
  143. package/dist/worktree-cli.js +28 -122
  144. package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
  145. package/integrations/hermes/pyproject.toml +1 -1
  146. package/package.json +1 -1
  147. package/packages/cloud-mcp-gateway/package.json +2 -2
  148. package/packages/contracts/package.json +1 -1
  149. package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
  150. package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
  151. package/packages/daemon/dist/orchestrator-agent.js +113 -0
  152. package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
  153. package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
  154. package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
  155. package/packages/daemon/dist/orchestrator-tools.js +137 -0
  156. package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
  157. package/packages/daemon/dist/orchestrator.d.ts +4 -56
  158. package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
  159. package/packages/daemon/dist/orchestrator.js +13 -303
  160. package/packages/daemon/dist/orchestrator.js.map +1 -1
  161. package/packages/daemon/dist/orchestrator.test.js +11 -1
  162. package/packages/daemon/dist/orchestrator.test.js.map +1 -1
  163. package/packages/daemon/package.json +4 -4
  164. package/packages/gsd-agent-core/package.json +5 -5
  165. package/packages/gsd-agent-modes/package.json +7 -7
  166. package/packages/mcp-server/package.json +4 -4
  167. package/packages/native/package.json +1 -1
  168. package/packages/pi-agent-core/package.json +1 -1
  169. package/packages/pi-ai/package.json +1 -1
  170. package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
  171. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  172. package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
  173. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  174. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
  175. package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
  176. package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
  177. package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
  178. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
  179. package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
  180. package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
  181. package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
  182. package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
  183. package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
  184. package/packages/pi-coding-agent/dist/core/skills.js +4 -1
  185. package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
  186. package/packages/pi-coding-agent/dist/index.d.ts +1 -0
  187. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  188. package/packages/pi-coding-agent/dist/index.js +1 -0
  189. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  190. package/packages/pi-coding-agent/package.json +7 -7
  191. package/packages/pi-tui/package.json +2 -2
  192. package/packages/rpc-client/package.json +2 -2
  193. package/pkg/package.json +1 -1
  194. package/src/resources/extensions/ask-user-questions.ts +1 -1
  195. package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
  196. package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
  197. package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
  198. package/src/resources/extensions/gsd/activity-log.ts +1 -1
  199. package/src/resources/extensions/gsd/auto/orchestrator.ts +17 -11
  200. package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -4
  201. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +9 -2
  202. package/src/resources/extensions/gsd/auto-dispatch.ts +77 -16
  203. package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
  204. package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
  205. package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
  206. package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
  207. package/src/resources/extensions/gsd/auto-start.ts +58 -29
  208. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
  209. package/src/resources/extensions/gsd/auto-timers.ts +9 -2
  210. package/src/resources/extensions/gsd/auto.ts +103 -30
  211. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
  212. package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +66 -6
  213. package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
  214. package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
  215. package/src/resources/extensions/gsd/config-overlay.ts +7 -1
  216. package/src/resources/extensions/gsd/db/engine.ts +36 -0
  217. package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
  218. package/src/resources/extensions/gsd/db-adapter.ts +29 -3
  219. package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
  220. package/src/resources/extensions/gsd/flat-phase-migration.ts +20 -0
  221. package/src/resources/extensions/gsd/gsd-db.ts +20 -11
  222. package/src/resources/extensions/gsd/guided-flow.ts +19 -7
  223. package/src/resources/extensions/gsd/journal.ts +1 -1
  224. package/src/resources/extensions/gsd/markdown-renderer.ts +52 -103
  225. package/src/resources/extensions/gsd/md-importer.ts +93 -22
  226. package/src/resources/extensions/gsd/metrics.ts +2 -2
  227. package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
  228. package/src/resources/extensions/gsd/paths.ts +108 -51
  229. package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
  230. package/src/resources/extensions/gsd/preferences-models.ts +74 -20
  231. package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
  232. package/src/resources/extensions/gsd/preferences-types.ts +24 -1
  233. package/src/resources/extensions/gsd/preferences-validation.ts +77 -9
  234. package/src/resources/extensions/gsd/preferences.ts +20 -0
  235. package/src/resources/extensions/gsd/rule-registry.ts +111 -9
  236. package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
  237. package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
  238. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
  239. package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
  240. package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
  241. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
  242. package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
  243. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
  244. package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
  245. package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
  246. package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
  247. package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
  248. package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
  249. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
  250. package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
  251. package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
  252. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +27 -0
  253. package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +22 -1
  254. package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
  255. package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
  256. package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
  257. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
  258. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
  259. package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
  260. package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
  261. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
  262. package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
  263. package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
  264. package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
  265. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
  266. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
  267. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
  268. package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
  269. package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
  270. package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
  271. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
  272. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
  273. package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
  274. package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
  275. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +93 -0
  276. package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
  277. package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
  278. package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
  279. package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
  280. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
  281. package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
  282. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
  283. package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
  284. package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
  285. package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
  286. package/src/resources/extensions/gsd/types.ts +23 -4
  287. package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
  288. package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
  289. package/src/resources/extensions/gsd/uok/audit.ts +2 -0
  290. package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
  291. package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
  292. package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
  293. package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
  294. package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
  295. package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
  296. package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
  297. package/src/resources/extensions/gsd/workspace.ts +5 -21
  298. package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
  299. package/src/resources/extensions/shared/interview-ui.ts +58 -11
  300. package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
  301. /package/dist/web/standalone/.next/static/{ZpFDazP-38PXBPiI_LdZf → NPMNi2HV8BuK3baQsEUhk}/_buildManifest.js +0 -0
  302. /package/dist/web/standalone/.next/static/{ZpFDazP-38PXBPiI_LdZf → NPMNi2HV8BuK3baQsEUhk}/_ssgManifest.js +0 -0
@@ -5,7 +5,7 @@
5
5
 
6
6
  import assert from 'node:assert/strict';
7
7
  import { test, describe, beforeEach } from "node:test";
8
- import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
8
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync, realpathSync } from "node:fs";
9
9
  import { tmpdir } from "node:os";
10
10
  import { join } from "node:path";
11
11
  import { emitJournalEvent } from "../journal.ts";
@@ -544,3 +544,97 @@ describe("RuleRegistry", () => {
544
544
  assert.deepStrictEqual(result.matchedRule, "<no-match>", "matchedRule is '<no-match>' on fallback");
545
545
  });
546
546
  });
547
+
548
+ describe("resolveHookArtifactPath", () => {
549
+ test("resolves a phase-level artifact from the .gsd/phases layout", () => {
550
+ const originalGsdHome = process.env.GSD_HOME;
551
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-hook-phase-"));
552
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-hook-home-"));
553
+ try {
554
+ process.env.GSD_HOME = tempGsdHome;
555
+ const phaseDir = join(realpathSync(projectRoot), ".gsd", "phases", "50-some-phase");
556
+ mkdirSync(phaseDir, { recursive: true });
557
+ const artifactPath = join(phaseDir, "BROWSER-RUNTIME-EVIDENCE.md");
558
+ writeFileSync(artifactPath, "---\nverdict: advisory\n---\n", "utf-8");
559
+
560
+ const resolved = resolveHookArtifactPath(projectRoot, "M050/S02/T01", "BROWSER-RUNTIME-EVIDENCE.md");
561
+ assert.equal(resolved, artifactPath, "resolves the canonical phase-level artifact");
562
+ } finally {
563
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
564
+ else process.env.GSD_HOME = originalGsdHome;
565
+ rmSync(projectRoot, { recursive: true, force: true });
566
+ rmSync(tempGsdHome, { recursive: true, force: true });
567
+ }
568
+ });
569
+
570
+ test("falls back to the legacy milestones/slices/tasks layout", () => {
571
+ const originalGsdHome = process.env.GSD_HOME;
572
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-hook-legacy-"));
573
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-hook-home-"));
574
+ try {
575
+ process.env.GSD_HOME = tempGsdHome;
576
+ const tasksDir = join(projectRoot, ".gsd", "milestones", "M050", "slices", "S02", "tasks");
577
+ mkdirSync(tasksDir, { recursive: true });
578
+ const artifactPath = join(tasksDir, "T01-REVIEW.md");
579
+ writeFileSync(artifactPath, "---\nverdict: pass\n---\n", "utf-8");
580
+
581
+ const resolved = resolveHookArtifactPath(projectRoot, "M050/S02/T01", "REVIEW.md");
582
+ assert.equal(resolved, artifactPath, "resolves the task-prefixed legacy artifact");
583
+ } finally {
584
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
585
+ else process.env.GSD_HOME = originalGsdHome;
586
+ rmSync(projectRoot, { recursive: true, force: true });
587
+ rmSync(tempGsdHome, { recursive: true, force: true });
588
+ }
589
+ });
590
+
591
+ test("legacy: nested task artifact wins over a milestone-root file of the same name", () => {
592
+ const originalGsdHome = process.env.GSD_HOME;
593
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-hook-legacy-root-"));
594
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-hook-home-"));
595
+ try {
596
+ process.env.GSD_HOME = tempGsdHome;
597
+ const milestoneDir = join(projectRoot, ".gsd", "milestones", "M050");
598
+ const tasksDir = join(milestoneDir, "slices", "S02", "tasks");
599
+ mkdirSync(tasksDir, { recursive: true });
600
+ // The correct task-scoped artifact lives in the nested slices/tasks tree.
601
+ const nestedPath = join(tasksDir, "T01-REVIEW.md");
602
+ writeFileSync(nestedPath, "---\nverdict: pass\n---\n", "utf-8");
603
+ // A same-named decoy at the legacy milestone root must NOT win — before the
604
+ // fix, resolveMilestonePath returned the milestone root and it was probed
605
+ // ahead of the nested task path (#1264 Bugbot follow-up).
606
+ writeFileSync(join(milestoneDir, "REVIEW.md"), "---\nverdict: failed\n---\n", "utf-8");
607
+
608
+ const resolved = resolveHookArtifactPath(projectRoot, "M050/S02/T01", "REVIEW.md");
609
+ assert.equal(resolved, nestedPath, "task-scoped legacy path wins over the milestone-root file");
610
+ } finally {
611
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
612
+ else process.env.GSD_HOME = originalGsdHome;
613
+ rmSync(projectRoot, { recursive: true, force: true });
614
+ rmSync(tempGsdHome, { recursive: true, force: true });
615
+ }
616
+ });
617
+
618
+ test("legacy: missing-artifact fallback points at the nested task path, not the milestone root", () => {
619
+ const originalGsdHome = process.env.GSD_HOME;
620
+ const projectRoot = mkdtempSync(join(tmpdir(), "gsd-hook-legacy-miss-"));
621
+ const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-hook-home-"));
622
+ try {
623
+ process.env.GSD_HOME = tempGsdHome;
624
+ const milestoneDir = join(projectRoot, ".gsd", "milestones", "M050");
625
+ mkdirSync(milestoneDir, { recursive: true });
626
+ // Content-bearing legacy milestone dir (a non-META file) so resolveMilestonePath
627
+ // returns it, but the requested gate artifact does not exist anywhere.
628
+ writeFileSync(join(milestoneDir, "M050-ROADMAP.md"), "# roadmap\n", "utf-8");
629
+
630
+ const resolved = resolveHookArtifactPath(projectRoot, "M050/S02/T01", "REVIEW.md");
631
+ const expected = join(milestoneDir, "slices", "S02", "tasks", "T01-REVIEW.md");
632
+ assert.equal(resolved, expected, "diagnostic fallback uses the nested task path for a legacy task-scoped unit");
633
+ } finally {
634
+ if (originalGsdHome === undefined) delete process.env.GSD_HOME;
635
+ else process.env.GSD_HOME = originalGsdHome;
636
+ rmSync(projectRoot, { recursive: true, force: true });
637
+ rmSync(tempGsdHome, { recursive: true, force: true });
638
+ }
639
+ });
640
+ });
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Sibling single-model fields honor `fallbacks[]` (#1229).
3
+ *
4
+ * `post_unit_hooks[].model`, `auto_supervisor.model`, and
5
+ * `reactive_execution.subagent_model` accept the same
6
+ * `{ model, provider?, fallbacks? }` object form as `models.<phase>` so a
7
+ * transient provider trip falls back to the next entry instead of hard-failing
8
+ * (and, for a blocking hook, pausing the whole auto-mode run).
9
+ */
10
+
11
+ import test from "node:test";
12
+ import assert from "node:assert/strict";
13
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { tmpdir } from "node:os";
16
+
17
+ import {
18
+ normalizeModelFieldConfig,
19
+ resolveModelWithFallbacksForUnit,
20
+ resolveAutoSupervisorConfig,
21
+ } from "../preferences-models.ts";
22
+ import { validatePreferences } from "../preferences-validation.ts";
23
+
24
+ function withPreferences<T>(frontmatter: string[], fn: () => T): T {
25
+ const oldHome = process.env.GSD_HOME;
26
+ const home = mkdtempSync(join(tmpdir(), "gsd-sibling-models-"));
27
+ try {
28
+ process.env.GSD_HOME = home;
29
+ writeFileSync(join(home, "preferences.md"), ["---", ...frontmatter, "---", ""].join("\n"));
30
+ return fn();
31
+ } finally {
32
+ if (oldHome === undefined) delete process.env.GSD_HOME;
33
+ else process.env.GSD_HOME = oldHome;
34
+ rmSync(home, { recursive: true, force: true });
35
+ }
36
+ }
37
+
38
+ // ─── normalizeModelFieldConfig ───────────────────────────────────────────────
39
+
40
+ test("normalizeModelFieldConfig: bare string → primary with empty fallbacks", () => {
41
+ assert.deepEqual(normalizeModelFieldConfig("my-model"), { primary: "my-model", fallbacks: [] });
42
+ });
43
+
44
+ test("normalizeModelFieldConfig: object form carries fallbacks", () => {
45
+ assert.deepEqual(
46
+ normalizeModelFieldConfig({ model: "primary", fallbacks: ["fb-1", "fb-2"] }),
47
+ { primary: "primary", fallbacks: ["fb-1", "fb-2"] },
48
+ );
49
+ });
50
+
51
+ test("normalizeModelFieldConfig: provider prefix is prepended to bare model", () => {
52
+ assert.deepEqual(
53
+ normalizeModelFieldConfig({ model: "gpt-5.4", provider: "openai-codex" }),
54
+ { primary: "openai-codex/gpt-5.4", fallbacks: [] },
55
+ );
56
+ });
57
+
58
+ test("normalizeModelFieldConfig: unset / blank / model-less → undefined", () => {
59
+ assert.equal(normalizeModelFieldConfig(undefined), undefined);
60
+ assert.equal(normalizeModelFieldConfig(" "), undefined);
61
+ assert.equal(normalizeModelFieldConfig({ model: "" }), undefined);
62
+ });
63
+
64
+ // ─── post_unit_hooks[].model fallback resolution ─────────────────────────────
65
+
66
+ test("hook/<name> resolves the hook's object-form model with fallbacks", () => {
67
+ withPreferences(
68
+ [
69
+ "post_unit_hooks:",
70
+ " - name: plan-review",
71
+ " after: [plan-slice]",
72
+ " prompt: review the plan",
73
+ " model:",
74
+ " model: primary-model",
75
+ " fallbacks:",
76
+ " - fallback-a",
77
+ " - fallback-b",
78
+ ],
79
+ () => {
80
+ const resolved = resolveModelWithFallbacksForUnit("hook/plan-review");
81
+ assert.deepEqual(resolved, { primary: "primary-model", fallbacks: ["fallback-a", "fallback-b"] });
82
+ },
83
+ );
84
+ });
85
+
86
+ test("hook/<name> still resolves the legacy bare-string model form", () => {
87
+ withPreferences(
88
+ [
89
+ "post_unit_hooks:",
90
+ " - name: plan-review",
91
+ " after: [plan-slice]",
92
+ " prompt: review the plan",
93
+ " model: solo-model",
94
+ ],
95
+ () => {
96
+ assert.deepEqual(
97
+ resolveModelWithFallbacksForUnit("hook/plan-review"),
98
+ { primary: "solo-model", fallbacks: [] },
99
+ );
100
+ },
101
+ );
102
+ });
103
+
104
+ test("hook/<name> resolves undefined when the hook has no model", () => {
105
+ withPreferences(
106
+ [
107
+ "post_unit_hooks:",
108
+ " - name: plan-review",
109
+ " after: [plan-slice]",
110
+ " prompt: review the plan",
111
+ ],
112
+ () => {
113
+ assert.equal(resolveModelWithFallbacksForUnit("hook/plan-review"), undefined);
114
+ assert.equal(resolveModelWithFallbacksForUnit("hook/unknown-hook"), undefined);
115
+ },
116
+ );
117
+ });
118
+
119
+ // ─── auto_supervisor.model ───────────────────────────────────────────────────
120
+
121
+ test("auto_supervisor.model object form resolves with fallbacks", () => {
122
+ withPreferences(
123
+ [
124
+ "auto_supervisor:",
125
+ " model:",
126
+ " model: supervisor-primary",
127
+ " fallbacks: [supervisor-fb]",
128
+ ],
129
+ () => {
130
+ assert.deepEqual(resolveModelWithFallbacksForUnit("supervisor"), {
131
+ primary: "supervisor-primary",
132
+ fallbacks: ["supervisor-fb"],
133
+ });
134
+ const supervisor = resolveAutoSupervisorConfig();
135
+ assert.equal(supervisor.model, "supervisor-primary");
136
+ assert.deepEqual(supervisor.modelFallbacks, ["supervisor-fb"]);
137
+ },
138
+ );
139
+ });
140
+
141
+ // ─── validation accepts the object form ──────────────────────────────────────
142
+
143
+ test("validatePreferences accepts object-form hook and subagent models", () => {
144
+ const { preferences: validated, errors } = validatePreferences({
145
+ post_unit_hooks: [
146
+ {
147
+ name: "plan-review",
148
+ after: ["plan-slice"],
149
+ prompt: "review",
150
+ model: { model: "primary", fallbacks: ["fb-1"] },
151
+ },
152
+ ],
153
+ reactive_execution: {
154
+ enabled: true,
155
+ subagent_model: { model: "sub-primary", fallbacks: ["sub-fb"] },
156
+ },
157
+ } as never);
158
+
159
+ assert.deepEqual(errors, []);
160
+ assert.deepEqual(validated.post_unit_hooks?.[0].model, { model: "primary", fallbacks: ["fb-1"] });
161
+ assert.deepEqual(validated.reactive_execution?.subagent_model, { model: "sub-primary", fallbacks: ["sub-fb"] });
162
+ });
163
+
164
+ test("validatePreferences rejects a model-less object form", () => {
165
+ const { errors } = validatePreferences({
166
+ post_unit_hooks: [
167
+ { name: "plan-review", after: ["plan-slice"], prompt: "review", model: { fallbacks: ["fb"] } },
168
+ ],
169
+ } as never);
170
+ assert.ok(errors.some((e) => e.includes('requires a non-empty "model"')));
171
+ });
172
+
173
+ test("validatePreferences accepts object-form auto_supervisor.model", () => {
174
+ const { preferences: validated, errors } = validatePreferences({
175
+ auto_supervisor: { model: { model: "sup-primary", fallbacks: ["sup-fb"] } },
176
+ } as never);
177
+ assert.deepEqual(errors, []);
178
+ assert.deepEqual(validated.auto_supervisor?.model, { model: "sup-primary", fallbacks: ["sup-fb"] });
179
+ });
180
+
181
+ test("validatePreferences rejects a model-less auto_supervisor.model object", () => {
182
+ const { errors } = validatePreferences({
183
+ auto_supervisor: { model: { fallbacks: ["sup-fb"] } },
184
+ } as never);
185
+ assert.ok(
186
+ errors.some((e) => e.includes("auto_supervisor.model") && e.includes('requires a non-empty "model"')),
187
+ `expected supervisor model error, got: ${JSON.stringify(errors)}`,
188
+ );
189
+ });
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Regression tests for #1269 — the object model form's `thinking` sub-field on
3
+ * the three single-model fields (`post_unit_hooks[].model`,
4
+ * `auto_supervisor.model`, and `reactive_execution.subagent_model`) was
5
+ * silently dropped after validation/normalization, so a configured
6
+ * hook/supervisor/subagent reasoning level never applied.
7
+ *
8
+ * These exercise the real preference pipeline through the exported runtime APIs:
9
+ * sanitize → validatePreferences
10
+ * normalize → normalizeModelFieldConfig
11
+ * resolve → resolveThinkingLevelForUnit (supervisor + hook/* early path)
12
+ *
13
+ * The `supervisor` and `hook/*` unit types have no phase-bucket chain, so the
14
+ * pre-#1269 resolver walk never covered them and returned `undefined`.
15
+ */
16
+
17
+ import test from "node:test";
18
+ import assert from "node:assert/strict";
19
+ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
20
+ import { join } from "node:path";
21
+ import { tmpdir } from "node:os";
22
+
23
+ import { validatePreferences } from "../preferences-validation.ts";
24
+ import { normalizeModelFieldConfig, resolveThinkingLevelForUnit } from "../preferences-models.ts";
25
+ import { invalidateAllCaches } from "../cache.ts";
26
+
27
+ /** Read the per-field `thinking` off a validated/normalized model config. */
28
+ function thinkingOf(model: unknown): string | undefined {
29
+ return (model as { thinking?: string } | undefined)?.thinking;
30
+ }
31
+
32
+ /** Write a project-level `.gsd/PREFERENCES.md` and run `fn` with its basePath. */
33
+ function withProjectPreferences<T>(frontmatter: string, fn: (basePath: string) => T): T {
34
+ const base = mkdtempSync(join(tmpdir(), "gsd-1269-thinking-"));
35
+ mkdirSync(join(base, ".gsd"), { recursive: true });
36
+ writeFileSync(join(base, ".gsd", "PREFERENCES.md"), `---\n${frontmatter}\n---\n`, "utf-8");
37
+ invalidateAllCaches();
38
+ try {
39
+ return fn(base);
40
+ } finally {
41
+ invalidateAllCaches();
42
+ rmSync(base, { recursive: true, force: true });
43
+ }
44
+ }
45
+
46
+ // ── sanitize: validatePreferences preserves a valid object-form thinking ──────
47
+
48
+ test("validatePreferences preserves auto_supervisor.model object-form thinking", () => {
49
+ const { preferences, errors } = validatePreferences({
50
+ auto_supervisor: { model: { model: "supervisor-model", thinking: "high" } },
51
+ });
52
+ assert.equal(errors.length, 0);
53
+ assert.equal(thinkingOf(preferences.auto_supervisor?.model), "high");
54
+ });
55
+
56
+ test("validatePreferences preserves post_unit_hooks[].model object-form thinking", () => {
57
+ const { preferences, errors } = validatePreferences({
58
+ post_unit_hooks: [
59
+ {
60
+ name: "code-review",
61
+ after: ["execute-task"],
62
+ prompt: "Review the diff",
63
+ model: { model: "hook-model", thinking: "low" },
64
+ },
65
+ ],
66
+ });
67
+ assert.equal(errors.length, 0);
68
+ assert.equal(thinkingOf(preferences.post_unit_hooks?.[0]?.model), "low");
69
+ });
70
+
71
+ test("validatePreferences preserves reactive_execution.subagent_model object-form thinking", () => {
72
+ const { preferences, errors } = validatePreferences({
73
+ reactive_execution: {
74
+ enabled: true,
75
+ max_parallel: 2,
76
+ isolation_mode: "same-tree",
77
+ subagent_model: { model: "subagent-model", thinking: "medium" },
78
+ },
79
+ });
80
+ assert.equal(errors.length, 0);
81
+ assert.equal(thinkingOf(preferences.reactive_execution?.subagent_model), "medium");
82
+ });
83
+
84
+ // ── sanitize: an invalid thinking level is warned and stripped ────────────────
85
+
86
+ test("validatePreferences warns and strips an invalid object-form thinking level", () => {
87
+ const { preferences, errors, warnings } = validatePreferences({
88
+ auto_supervisor: { model: { model: "supervisor-model", thinking: "ludicrous" } },
89
+ } as never);
90
+ // The model survives; only the bogus thinking is dropped, so a typo can never
91
+ // reach the resolver and masquerade as explicit configuration.
92
+ assert.equal(errors.length, 0);
93
+ assert.equal(thinkingOf(preferences.auto_supervisor?.model), undefined);
94
+ assert.ok(
95
+ warnings.some((w) => w.includes("auto_supervisor.model") && w.includes("thinking")),
96
+ `expected a thinking warning, got: ${JSON.stringify(warnings)}`,
97
+ );
98
+ });
99
+
100
+ // ── normalize: normalizeModelFieldConfig carries thinking through ─────────────
101
+
102
+ test("normalizeModelFieldConfig carries object-form thinking through", () => {
103
+ const config = normalizeModelFieldConfig({ model: "hook-model", thinking: "xhigh" });
104
+ assert.equal(config?.primary, "hook-model");
105
+ assert.equal(config?.thinking, "xhigh");
106
+ });
107
+
108
+ test("normalizeModelFieldConfig leaves thinking absent when omitted", () => {
109
+ const fromObject = normalizeModelFieldConfig({ model: "hook-model" });
110
+ assert.equal(fromObject?.primary, "hook-model");
111
+ assert.equal(fromObject?.thinking, undefined);
112
+
113
+ const fromString = normalizeModelFieldConfig("hook-model");
114
+ assert.equal(fromString?.primary, "hook-model");
115
+ assert.equal(fromString?.thinking, undefined);
116
+ });
117
+
118
+ // ── resolve: the single-field unit types surface their per-field thinking ──────
119
+
120
+ test("resolveThinkingLevelForUnit('supervisor') returns auto_supervisor.model thinking", () => {
121
+ withProjectPreferences(
122
+ ["auto_supervisor:", " model:", " model: supervisor-model", " thinking: high"].join("\n"),
123
+ (base) => {
124
+ assert.equal(resolveThinkingLevelForUnit("supervisor", base), "high");
125
+ },
126
+ );
127
+ });
128
+
129
+ test("resolveThinkingLevelForUnit('hook/<name>') returns the matching hook model thinking", () => {
130
+ withProjectPreferences(
131
+ [
132
+ "post_unit_hooks:",
133
+ " - name: code-review",
134
+ " after:",
135
+ " - execute-task",
136
+ " prompt: Review the diff",
137
+ " model:",
138
+ " model: hook-model",
139
+ " thinking: low",
140
+ ].join("\n"),
141
+ (base) => {
142
+ assert.equal(resolveThinkingLevelForUnit("hook/code-review", base), "low");
143
+ },
144
+ );
145
+ });
146
+
147
+ test("resolveThinkingLevelForUnit('supervisor') stays undefined when thinking is omitted", () => {
148
+ // Bare-string model form carries no thinking → dispatch falls back to the
149
+ // session/floor level, unchanged from pre-#1269 behavior.
150
+ withProjectPreferences(
151
+ ["auto_supervisor:", " model: supervisor-model"].join("\n"),
152
+ (base) => {
153
+ assert.equal(resolveThinkingLevelForUnit("supervisor", base), undefined);
154
+ },
155
+ );
156
+ });
@@ -109,3 +109,49 @@ test("appendDiscoveredSkillsFallback does not duplicate skills already in the pr
109
109
  location: "/tmp/already-loaded/SKILL.md",
110
110
  }]), prompt);
111
111
  });
112
+
113
+ test("snapshotSkills({cwd}) detects skills added to the project .agents/skills/ mid-session", async () => {
114
+ await withTempSkillHome(async (home) => {
115
+ // Use a project dir under the temp home so cwd is isolated.
116
+ const projectDir = join(home, "myproject");
117
+ const projectSkills = join(projectDir, ".agents", "skills");
118
+ snapshotSkills({ cwd: projectDir });
119
+
120
+ // Add a skill to the project skills dir after the snapshot.
121
+ writeDiskSkill(projectSkills, "project-disk-skill", "Project skill added mid-session.");
122
+ const detected = detectNewSkills();
123
+
124
+ assert.deepEqual(detected.map(skill => skill.name), ["project-disk-skill"]);
125
+ assert.equal(detected[0].location, join(projectSkills, "project-disk-skill", "SKILL.md"));
126
+ });
127
+ });
128
+
129
+ test("snapshotSkills({cwd}) detects skills added to the project .claude/skills/ mid-session", async () => {
130
+ await withTempSkillHome(async (home) => {
131
+ const projectDir = join(home, "claude-project");
132
+ const claudeSkills = join(projectDir, ".claude", "skills");
133
+ snapshotSkills({ cwd: projectDir });
134
+
135
+ writeDiskSkill(claudeSkills, "claude-project-skill", "Claude-format skill added mid-session.");
136
+ const detected = detectNewSkills();
137
+
138
+ assert.deepEqual(detected.map(skill => skill.name), ["claude-project-skill"]);
139
+ });
140
+ });
141
+
142
+ test("snapshotSkills({cwd}) treats skills already in the project dir as baseline, not newly discovered", async () => {
143
+ await withTempSkillHome(async (home) => {
144
+ // projectDir is intentionally NOT process.cwd(): mirrors auto-mode passing a
145
+ // base path that differs from cwd (e.g. after chdir into a worktree). The
146
+ // baseline must scan the passed cwd's dirs, so a skill present *before* the
147
+ // snapshot is part of the baseline and is never reported as newly discovered.
148
+ // Regresses the fix where snapshotCurrentSkillNames fell back to process.cwd().
149
+ const projectDir = join(home, "preexisting-project");
150
+ const projectSkills = join(projectDir, ".agents", "skills");
151
+ writeDiskSkill(projectSkills, "preexisting-project-skill");
152
+
153
+ snapshotSkills({ cwd: projectDir });
154
+
155
+ assert.deepEqual(detectNewSkills(), []);
156
+ });
157
+ });
@@ -52,7 +52,12 @@ test("skipped validation dispatch persists the validation file and DB assessment
52
52
  const artifactPath = "milestones/M001/slices/S01/S01-ASSESSMENT.md";
53
53
  const assessmentPath = `.gsd/${artifactPath}`;
54
54
  assert.equal(getArtifact(artifactPath)?.artifact_type, "ASSESSMENT");
55
- assert.equal(getAssessment(assessmentPath)?.scope, "run-uat");
55
+ // #1258: the per-slice ASSESSMENT fabricated by the pre-validation backfill
56
+ // is a placeholder (no UAT ever ran for this slice), so it is filed under the
57
+ // `backfill` scope — never `run-uat` — so `readUatGateVerdict` cannot mistake
58
+ // it for a genuine UAT sign-off. Its verdict body stays PASS to satisfy the
59
+ // MV02 artifact-existence requirement.
60
+ assert.equal(getAssessment(assessmentPath)?.scope, "backfill");
56
61
  assert.equal(getAssessment(assessmentPath)?.status, "pass");
57
62
  assert.equal(
58
63
  getLatestAssessmentByScope("M001", "milestone-validation")?.status,
@@ -208,6 +208,38 @@ test("startAutoDetached reports failures asynchronously (#3733)", () => {
208
208
  );
209
209
  });
210
210
 
211
+ test("failed auto-start always clears leaked s.active so later /gsd auto is not bricked (#1235)", () => {
212
+ const autoSrc = readGsdFile("auto.ts");
213
+
214
+ // 1. startAutoDetached is the single funnel for any exception thrown out of
215
+ // startAuto — including a bootstrap throw that escapes before the loop's
216
+ // try/finally could run cleanupAfterLoopExit. The failure handler must
217
+ // clear leaked activation, or s.active + the on-disk auto lock persist and
218
+ // the re-entry guard silently no-ops every later /gsd auto until restart.
219
+ const detachedIdx = autoSrc.indexOf("export function startAutoDetached");
220
+ assert.ok(detachedIdx > -1, "startAutoDetached should exist");
221
+ const detachedBody = autoSrc.slice(detachedIdx, autoSrc.indexOf("\n}", detachedIdx));
222
+ assert.ok(
223
+ detachedBody.includes("if (s.active)") &&
224
+ detachedBody.includes("cleanupAfterLoopExit(ctx)"),
225
+ "startAutoDetached failure handler must run cleanupAfterLoopExit when auto-mode is still active",
226
+ );
227
+
228
+ // 2. bootstrapAutoSession sets s.active = true before some bail-outs return
229
+ // false (e.g. the SQLite-unavailable gate), so the fresh-start !ready path
230
+ // must clear the leaked activation before returning.
231
+ const startAutoIdx = autoSrc.indexOf("export async function startAuto(");
232
+ assert.ok(startAutoIdx > -1, "startAuto should exist");
233
+ const startAutoBody = autoSrc.slice(startAutoIdx);
234
+ const notReadyIdx = startAutoBody.indexOf("if (!ready) {");
235
+ assert.ok(notReadyIdx > -1, "fresh start should guard on bootstrap readiness with a block body");
236
+ const notReadyBlock = startAutoBody.slice(notReadyIdx, notReadyIdx + 600);
237
+ assert.ok(
238
+ notReadyBlock.includes("if (s.active) await cleanupAfterLoopExit(ctx)"),
239
+ "fresh-start !ready bail-out must clear leaked auto activation before returning",
240
+ );
241
+ });
242
+
211
243
  test("detached auto-start keeps a ref'ed handle until the run settles", async () => {
212
244
  const originalSetInterval = globalThis.setInterval;
213
245
  const originalClearInterval = globalThis.clearInterval;