@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
@@ -2,7 +2,7 @@
2
2
  // File Purpose: Verifies UOK kernel path selection and legacy fallback telemetry.
3
3
  import test from "node:test";
4
4
  import assert from "node:assert/strict";
5
- import { mkdtempSync, readFileSync, rmSync } from "node:fs";
5
+ import { mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
6
6
  import { join } from "node:path";
7
7
  import { tmpdir } from "node:os";
8
8
 
@@ -14,6 +14,16 @@ import type { LoopDeps } from "../auto/loop-deps.ts";
14
14
  import { gsdRoot } from "../paths.ts";
15
15
  import type { GSDPreferences } from "../preferences.ts";
16
16
  import { getLegacyTelemetry, resetLegacyTelemetry } from "../legacy-telemetry.ts";
17
+ import { _getAdapter, closeDatabase, openDatabase } from "../gsd-db.ts";
18
+ import { UokGateRunner } from "../uok/gate-runner.ts";
19
+ import { applyModelPolicyFilter } from "../uok/model-policy.ts";
20
+ import {
21
+ clearUnifiedAuditOverrideForTests,
22
+ isUnifiedAuditEnabled,
23
+ setUnifiedAuditEnabled,
24
+ } from "../uok/audit-toggle.ts";
25
+ import { writeEscalationArtifact } from "../escalation.ts";
26
+ import { peekLogs, _resetLogs } from "../workflow-logger.ts";
17
27
 
18
28
  function makeBasePath(): string {
19
29
  return mkdtempSync(join(tmpdir(), "gsd-uok-kernel-"));
@@ -104,7 +114,8 @@ test("runAutoLoopWithUok uses kernel path by default and records uok-kernel pari
104
114
  assert.ok(args.calls.kernelDeps);
105
115
  assert.notEqual(args.calls.kernelDeps, args.deps);
106
116
  assert.ok(args.calls.kernelDeps?.uokObserver);
107
- assert.equal(process.env.GSD_UOK_AUDIT_UNIFIED, "0");
117
+ assert.equal(isUnifiedAuditEnabled(), true);
118
+ assert.equal(isUnifiedAuditEnabled(basePath), false);
108
119
 
109
120
  const events = readParityEvents(basePath);
110
121
  assert.equal(events.length, 2);
@@ -120,6 +131,37 @@ test("runAutoLoopWithUok uses kernel path by default and records uok-kernel pari
120
131
  }
121
132
  });
122
133
 
134
+ test("runAutoLoopWithUok keeps audit disabled for legacy-wrapper while restoring process override", async () => {
135
+ const basePath = makeBasePath();
136
+ clearUnifiedAuditOverrideForTests();
137
+ try {
138
+ resetLegacyTelemetry();
139
+ const args = makeArgs(basePath, {
140
+ uok: {
141
+ enabled: false,
142
+ audit_unified: { enabled: false },
143
+ },
144
+ });
145
+ await runAutoLoopWithUok(args);
146
+
147
+ assert.equal(args.calls.kernel, 0);
148
+ assert.equal(args.calls.legacy, 1);
149
+ assert.equal(isUnifiedAuditEnabled(), true);
150
+ assert.equal(isUnifiedAuditEnabled(basePath), false);
151
+
152
+ const events = readParityEvents(basePath);
153
+ assert.equal(events.length, 2);
154
+ assert.equal(events[0]?.path, "legacy-wrapper");
155
+ assert.equal(events[1]?.path, "legacy-wrapper");
156
+ assert.equal(events[1]?.status, "ok");
157
+ assert.equal(getLegacyTelemetry()["legacy.uokFallbackUsed"], 1);
158
+ } finally {
159
+ clearUnifiedAuditOverrideForTests();
160
+ resetLegacyTelemetry();
161
+ rmSync(basePath, { recursive: true, force: true });
162
+ }
163
+ });
164
+
123
165
  test("runAutoLoopWithUok uses legacy path when explicit legacy fallback is enabled", async () => {
124
166
  const basePath = makeBasePath();
125
167
  try {
@@ -176,3 +218,185 @@ test("runAutoLoopWithUok respects GSD_UOK_FORCE_LEGACY emergency switch", async
176
218
  rmSync(basePath, { recursive: true, force: true });
177
219
  }
178
220
  });
221
+
222
+ test("runAutoLoopWithUok records error exit and restores previous audit override when kernel fails", async () => {
223
+ const basePath = makeBasePath();
224
+ clearUnifiedAuditOverrideForTests();
225
+ setUnifiedAuditEnabled(false);
226
+ try {
227
+ resetLegacyTelemetry();
228
+ const args = makeArgs(basePath, {
229
+ uok: {
230
+ enabled: true,
231
+ audit_unified: { enabled: true },
232
+ gitops: { enabled: false },
233
+ },
234
+ });
235
+ args.runKernelLoop = async (_ctx, _pi, _s, loopDeps): Promise<void> => {
236
+ args.calls.kernel += 1;
237
+ args.calls.kernelDeps = loopDeps;
238
+ throw new Error("kernel exploded");
239
+ };
240
+
241
+ await assert.rejects(
242
+ () => runAutoLoopWithUok(args),
243
+ /kernel exploded/,
244
+ );
245
+
246
+ assert.equal(args.calls.kernel, 1);
247
+ assert.equal(args.calls.legacy, 0);
248
+ assert.equal(isUnifiedAuditEnabled(), false);
249
+
250
+ const events = readParityEvents(basePath);
251
+ assert.equal(events.length, 2);
252
+ assert.equal(events[0]?.phase, "enter");
253
+ assert.equal(events[1]?.phase, "exit");
254
+ assert.equal(events[1]?.status, "error");
255
+ assert.match(String(events[1]?.error), /kernel exploded/);
256
+ } finally {
257
+ clearUnifiedAuditOverrideForTests();
258
+ resetLegacyTelemetry();
259
+ rmSync(basePath, { recursive: true, force: true });
260
+ }
261
+ });
262
+
263
+ test("runAutoLoopWithUok treats kernel-enter audit failures as telemetry-only", async (t) => {
264
+ const basePath = makeBasePath();
265
+ const dbPath = join(basePath, ".gsd", "gsd.db");
266
+ clearUnifiedAuditOverrideForTests();
267
+ _resetLogs();
268
+ mkdirSync(join(basePath, ".gsd"), { recursive: true });
269
+ assert.equal(openDatabase(dbPath), true, "DB must open for this scenario");
270
+ t.after(() => {
271
+ clearUnifiedAuditOverrideForTests();
272
+ closeDatabase();
273
+ _resetLogs();
274
+ rmSync(basePath, { recursive: true, force: true });
275
+ });
276
+
277
+ _getAdapter()!.exec("DROP TABLE audit_events");
278
+
279
+ const args = makeArgs(basePath, {
280
+ uok: {
281
+ enabled: true,
282
+ audit_unified: { enabled: true },
283
+ gitops: { enabled: false },
284
+ },
285
+ });
286
+ args.runKernelLoop = async (_ctx, _pi, _s, loopDeps): Promise<void> => {
287
+ args.calls.kernel += 1;
288
+ args.calls.kernelDeps = loopDeps;
289
+ const observer = loopDeps.uokObserver;
290
+ assert.ok(observer, "kernel path must still install a turn observer");
291
+ observer.onTurnStart({
292
+ basePath,
293
+ traceId: "trace-audit-down",
294
+ turnId: "turn-audit-down",
295
+ iteration: 1,
296
+ unitType: "execute-task",
297
+ unitId: "M001/S01/T01",
298
+ startedAt: new Date().toISOString(),
299
+ });
300
+ observer.onTurnResult({
301
+ traceId: "trace-audit-down",
302
+ turnId: "turn-audit-down",
303
+ iteration: 1,
304
+ unitType: "execute-task",
305
+ unitId: "M001/S01/T01",
306
+ status: "completed",
307
+ failureClass: "none",
308
+ phaseResults: [],
309
+ startedAt: new Date().toISOString(),
310
+ finishedAt: new Date().toISOString(),
311
+ });
312
+
313
+ const gateRunner = new UokGateRunner();
314
+ gateRunner.register({
315
+ id: "test-gate",
316
+ type: "policy",
317
+ execute: async () => ({
318
+ outcome: "pass",
319
+ failureClass: "none",
320
+ rationale: "ok",
321
+ }),
322
+ });
323
+ await gateRunner.run("test-gate", {
324
+ basePath,
325
+ traceId: "trace-audit-down",
326
+ turnId: "turn-audit-down",
327
+ unitType: "execute-task",
328
+ unitId: "M001/S01/T01",
329
+ });
330
+
331
+ const filtered = applyModelPolicyFilter(
332
+ [{ id: "model-a", provider: "openai", api: "responses" }],
333
+ {
334
+ basePath,
335
+ traceId: "trace-audit-down",
336
+ turnId: "turn-audit-down",
337
+ unitType: "execute-task",
338
+ },
339
+ );
340
+ assert.equal(filtered.eligible.length, 1);
341
+
342
+ mkdirSync(join(basePath, ".gsd", "milestones", "M001", "slices", "S01", "tasks"), {
343
+ recursive: true,
344
+ });
345
+ writeEscalationArtifact(basePath, {
346
+ version: 1,
347
+ taskId: "T01",
348
+ sliceId: "S01",
349
+ milestoneId: "M001",
350
+ question: "Choose a path",
351
+ options: [
352
+ { id: "a", label: "A", tradeoffs: "First path" },
353
+ { id: "b", label: "B", tradeoffs: "Second path" },
354
+ ],
355
+ recommendation: "a",
356
+ recommendationRationale: "Test recommendation",
357
+ continueWithDefault: false,
358
+ createdAt: new Date().toISOString(),
359
+ });
360
+ };
361
+
362
+ await runAutoLoopWithUok(args);
363
+
364
+ assert.equal(args.calls.kernel, 1);
365
+ assert.equal(args.calls.legacy, 0);
366
+
367
+ const events = readParityEvents(basePath);
368
+ assert.equal(events.length, 3);
369
+ assert.equal(events[0]?.phase, "enter");
370
+ assert.equal(events[1]?.phase, "telemetry-error");
371
+ assert.equal(events[1]?.telemetry, "uok-kernel-enter");
372
+ assert.equal(events[2]?.phase, "exit");
373
+ assert.equal(events[2]?.status, "ok");
374
+ assert.equal(isUnifiedAuditEnabled(), true);
375
+ assert.equal(isUnifiedAuditEnabled(basePath), false);
376
+ assert.ok(
377
+ peekLogs().some(entry =>
378
+ entry.severity === "warn" &&
379
+ entry.component === "db" &&
380
+ entry.message.includes("uok-kernel-enter audit emit failed"),
381
+ ),
382
+ "degraded audit path should emit a non-fatal warning",
383
+ );
384
+
385
+ assert.doesNotThrow(() => {
386
+ writeEscalationArtifact(basePath, {
387
+ version: 1,
388
+ taskId: "T02",
389
+ sliceId: "S01",
390
+ milestoneId: "M001",
391
+ question: "Continue?",
392
+ options: [
393
+ { id: "yes", label: "Yes", tradeoffs: "Continue" },
394
+ { id: "no", label: "No", tradeoffs: "Stop" },
395
+ ],
396
+ recommendation: "yes",
397
+ recommendationRationale: "Audit stayed disabled after degraded enter",
398
+ continueWithDefault: true,
399
+ createdAt: new Date().toISOString(),
400
+ });
401
+ });
402
+ });
@@ -12,8 +12,13 @@ import {
12
12
  registerToolCompatibility,
13
13
  resetToolCompatibilityRegistry,
14
14
  } from "@gsd/pi-coding-agent";
15
+ import {
16
+ clearUnifiedAuditOverrideForTests,
17
+ setUnifiedAuditEnabled,
18
+ } from "../uok/audit-toggle.ts";
15
19
 
16
20
  test.afterEach(() => {
21
+ clearUnifiedAuditOverrideForTests();
17
22
  resetToolCompatibilityRegistry();
18
23
  });
19
24
 
@@ -32,6 +37,7 @@ test("uok model policy builds requirement vectors from unit metadata", () => {
32
37
  test("uok model policy enforces provider/api/tool constraints and emits decision audit events", () => {
33
38
  const basePath = mkdtempSync(join(tmpdir(), "gsd-uok-model-policy-"));
34
39
  try {
40
+ setUnifiedAuditEnabled(true);
35
41
  mkdirSync(join(basePath, ".gsd"), { recursive: true });
36
42
  registerToolCompatibility("screenshot", { producesImages: true });
37
43
 
@@ -6,10 +6,10 @@ import assert from 'node:assert/strict';
6
6
  import { existsSync, mkdtempSync, mkdirSync, rmSync, readFileSync, writeFileSync } from 'node:fs';
7
7
  import { join } from 'node:path';
8
8
  import { tmpdir } from 'node:os';
9
- import { regenerateIfMissing, renderPlanContent, renderStateProjection } from '../workflow-projections.ts';
9
+ import { regenerateIfMissing, renderPlanContent, renderStateProjection, renderSummaryProjection } from '../workflow-projections.ts';
10
10
  import type { SliceRow, TaskRow } from '../gsd-db.ts';
11
11
  import { closeDatabase, insertMilestone, insertSlice, insertTask, openDatabase } from '../gsd-db.ts';
12
- import { clearPathCache, _clearGsdRootCache } from '../paths.ts';
12
+ import { clearPathCache, _clearGsdRootCache, normalizeRealPath, resolveMilestoneFile, resolveTaskFile } from '../paths.ts';
13
13
  import { invalidateStateCache } from '../state.ts';
14
14
  import { clearParseCache } from '../files.ts';
15
15
 
@@ -243,6 +243,217 @@ test('workflow-projections: regenerateIfMissing PLAN restores slice plan and tas
243
243
  }
244
244
  });
245
245
 
246
+ test('workflow-projections: regenerateIfMissing ROADMAP is idempotent for flat-phase roadmap projections', async () => {
247
+ const base = mkdtempSync(join(tmpdir(), 'gsd-projections-flat-roadmap-'));
248
+ const dbPath = join(base, '.gsd', 'gsd.db');
249
+ const phaseDir = join(base, '.gsd', 'phases', '01-milestone');
250
+ mkdirSync(phaseDir, { recursive: true });
251
+ openDatabase(dbPath);
252
+ clearParseCache();
253
+ clearPathCache();
254
+ _clearGsdRootCache();
255
+ invalidateStateCache();
256
+
257
+ try {
258
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active', planning: { vision: 'Ship a layout-aware roadmap.' } });
259
+ insertSlice({
260
+ id: 'S01',
261
+ milestoneId: 'M001',
262
+ title: 'Flat slice',
263
+ status: 'pending',
264
+ demo: 'Roadmap exists at the flat-phase path.',
265
+ planning: { goal: 'Keep ROADMAP regeneration idempotent.' },
266
+ });
267
+
268
+ const roadmapPath = join(phaseDir, '01-ROADMAP.md');
269
+ writeFileSync(roadmapPath, '# existing flat roadmap\n');
270
+
271
+ const regenerated = await regenerateIfMissing(base, 'M001', 'S01', 'ROADMAP');
272
+
273
+ assert.equal(regenerated, false, 'existing flat-phase ROADMAP is detected without rewriting');
274
+ assert.equal(readFileSync(roadmapPath, 'utf-8'), '# existing flat roadmap\n');
275
+ assert.equal(normalizeRealPath(resolveMilestoneFile(base, 'M001', 'ROADMAP') ?? ''), normalizeRealPath(roadmapPath));
276
+ } finally {
277
+ closeDatabase();
278
+ rmSync(base, { recursive: true, force: true });
279
+ }
280
+ });
281
+
282
+ test('workflow-projections: regenerateIfMissing ROADMAP regenerates missing flat-phase roadmap projections', async () => {
283
+ const base = mkdtempSync(join(tmpdir(), 'gsd-projections-missing-roadmap-'));
284
+ const dbPath = join(base, '.gsd', 'gsd.db');
285
+ const phaseDir = join(base, '.gsd', 'phases', '01-milestone');
286
+ mkdirSync(phaseDir, { recursive: true });
287
+ openDatabase(dbPath);
288
+ clearParseCache();
289
+ clearPathCache();
290
+ _clearGsdRootCache();
291
+ invalidateStateCache();
292
+
293
+ try {
294
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active', planning: { vision: 'Ship a layout-aware roadmap.' } });
295
+ insertSlice({
296
+ id: 'S01',
297
+ milestoneId: 'M001',
298
+ title: 'Flat slice',
299
+ status: 'pending',
300
+ demo: 'Roadmap regenerates at the flat-phase path.',
301
+ planning: { goal: 'Recover ROADMAP from DB.' },
302
+ });
303
+
304
+ const roadmapPath = join(phaseDir, '01-ROADMAP.md');
305
+ const legacyRoadmapPath = join(base, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md');
306
+ assert.ok(!existsSync(roadmapPath), 'precondition: flat-phase ROADMAP is absent');
307
+
308
+ const regenerated = await regenerateIfMissing(base, 'M001', 'S01', 'ROADMAP');
309
+
310
+ assert.equal(regenerated, true, 'missing flat-phase ROADMAP is regenerated');
311
+ assert.match(readFileSync(roadmapPath, 'utf-8'), /Ship a layout-aware roadmap/);
312
+ assert.equal(existsSync(legacyRoadmapPath), false, 'regeneration does not create a legacy ROADMAP path');
313
+ assert.equal(normalizeRealPath(resolveMilestoneFile(base, 'M001', 'ROADMAP') ?? ''), normalizeRealPath(roadmapPath));
314
+ } finally {
315
+ closeDatabase();
316
+ rmSync(base, { recursive: true, force: true });
317
+ }
318
+ });
319
+
320
+ test('workflow-projections: regenerateIfMissing SUMMARY is idempotent for flat-phase task summaries', async () => {
321
+ const base = mkdtempSync(join(tmpdir(), 'gsd-projections-flat-summary-'));
322
+ const dbPath = join(base, '.gsd', 'gsd.db');
323
+ const phaseDir = join(base, '.gsd', 'phases', '01-milestone');
324
+ mkdirSync(phaseDir, { recursive: true });
325
+ openDatabase(dbPath);
326
+ clearParseCache();
327
+ clearPathCache();
328
+ _clearGsdRootCache();
329
+ invalidateStateCache();
330
+
331
+ try {
332
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
333
+ insertSlice({
334
+ id: 'S01',
335
+ milestoneId: 'M001',
336
+ title: 'Flat slice',
337
+ status: 'complete',
338
+ demo: 'Summary regenerates once.',
339
+ planning: { goal: 'Recover flat-phase summaries from DB.' },
340
+ });
341
+ insertTask({
342
+ id: 'T01',
343
+ sliceId: 'S01',
344
+ milestoneId: 'M001',
345
+ title: 'Completed task',
346
+ status: 'complete',
347
+ oneLiner: 'Completed the flat task.',
348
+ narrative: 'The task was completed through the DB-backed projection path.',
349
+ verificationResult: 'passed',
350
+ duration: '5m',
351
+ keyFiles: ['src/example.ts'],
352
+ keyDecisions: ['Use centralized task summary paths.'],
353
+ });
354
+
355
+ const summaryPath = join(phaseDir, 'T01-SUMMARY.md');
356
+ assert.ok(!existsSync(summaryPath), 'precondition: flat-phase task summary absent');
357
+
358
+ const first = await regenerateIfMissing(base, 'M001', 'S01', 'SUMMARY');
359
+ const second = await regenerateIfMissing(base, 'M001', 'S01', 'SUMMARY');
360
+
361
+ assert.equal(first, true, 'first call regenerates the missing task summary');
362
+ assert.equal(second, false, 'second call sees the flat-phase task summary and does not rewrite it');
363
+ assert.equal(normalizeRealPath(resolveTaskFile(base, 'M001', 'S01', 'T01', 'SUMMARY') ?? ''), normalizeRealPath(summaryPath));
364
+ assert.match(readFileSync(summaryPath, 'utf-8'), /# T01: Completed the flat task\./);
365
+ } finally {
366
+ closeDatabase();
367
+ rmSync(base, { recursive: true, force: true });
368
+ }
369
+ });
370
+
371
+ test('workflow-projections: flat-phase SUMMARY regeneration ignores stale nested task summaries', async () => {
372
+ const base = mkdtempSync(join(tmpdir(), 'gsd-projections-flat-stale-nested-'));
373
+ const dbPath = join(base, '.gsd', 'gsd.db');
374
+ const phaseDir = join(base, '.gsd', 'phases', '01-milestone');
375
+ const nestedTasksDir = join(phaseDir, 'tasks');
376
+ mkdirSync(nestedTasksDir, { recursive: true });
377
+ writeFileSync(join(nestedTasksDir, 'T01-SUMMARY.md'), '# stale nested summary\n');
378
+ openDatabase(dbPath);
379
+ clearParseCache();
380
+ clearPathCache();
381
+ _clearGsdRootCache();
382
+ invalidateStateCache();
383
+
384
+ try {
385
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
386
+ insertSlice({
387
+ id: 'S01',
388
+ milestoneId: 'M001',
389
+ title: 'Flat slice',
390
+ status: 'complete',
391
+ demo: 'Summary regenerates at phase root.',
392
+ planning: { goal: 'Recover canonical flat-phase summaries from DB.' },
393
+ });
394
+ insertTask({
395
+ id: 'T01',
396
+ sliceId: 'S01',
397
+ milestoneId: 'M001',
398
+ title: 'Completed task',
399
+ status: 'complete',
400
+ });
401
+
402
+ const rootSummaryPath = join(phaseDir, 'T01-SUMMARY.md');
403
+ assert.ok(!existsSync(rootSummaryPath), 'precondition: canonical flat-phase task summary absent');
404
+
405
+ const regenerated = await regenerateIfMissing(base, 'M001', 'S01', 'SUMMARY');
406
+
407
+ assert.equal(regenerated, true, 'missing phase-root summary is regenerated despite stale nested summary');
408
+ assert.equal(normalizeRealPath(resolveTaskFile(base, 'M001', 'S01', 'T01', 'SUMMARY') ?? ''), normalizeRealPath(rootSummaryPath));
409
+ assert.match(readFileSync(rootSummaryPath, 'utf-8'), /# T01: Completed task/);
410
+ assert.equal(readFileSync(join(nestedTasksDir, 'T01-SUMMARY.md'), 'utf-8'), '# stale nested summary\n');
411
+ } finally {
412
+ closeDatabase();
413
+ rmSync(base, { recursive: true, force: true });
414
+ }
415
+ });
416
+
417
+ test('workflow-projections: renderSummaryProjection uses milestone title when creating fresh flat-phase dirs', async () => {
418
+ const base = mkdtempSync(join(tmpdir(), 'gsd-projections-fresh-summary-'));
419
+ const dbPath = join(base, '.gsd', 'gsd.db');
420
+ mkdirSync(join(base, '.gsd'), { recursive: true });
421
+ openDatabase(dbPath);
422
+ clearParseCache();
423
+ clearPathCache();
424
+ _clearGsdRootCache();
425
+ invalidateStateCache();
426
+
427
+ try {
428
+ insertMilestone({ id: 'M001', title: 'Milestone', status: 'active' });
429
+ insertSlice({
430
+ id: 'S01',
431
+ milestoneId: 'M001',
432
+ title: 'Flat slice',
433
+ status: 'complete',
434
+ });
435
+ insertTask({
436
+ id: 'T01',
437
+ sliceId: 'S01',
438
+ milestoneId: 'M001',
439
+ title: 'Completed task',
440
+ status: 'complete',
441
+ });
442
+
443
+ const titleSummaryPath = join(base, '.gsd', 'phases', '01-milestone', 'T01-SUMMARY.md');
444
+ const idSummaryPath = join(base, '.gsd', 'phases', '01-m001', 'T01-SUMMARY.md');
445
+
446
+ renderSummaryProjection(base, 'M001', 'S01', 'T01');
447
+
448
+ assert.ok(existsSync(titleSummaryPath), 'fresh summary projection uses the milestone title slug');
449
+ assert.equal(existsSync(idSummaryPath), false, 'fresh summary projection does not create an id-slug orphan dir');
450
+ assert.equal(normalizeRealPath(resolveTaskFile(base, 'M001', 'S01', 'T01', 'SUMMARY') ?? ''), normalizeRealPath(titleSummaryPath));
451
+ } finally {
452
+ closeDatabase();
453
+ rmSync(base, { recursive: true, force: true });
454
+ }
455
+ });
456
+
246
457
  test('workflow-projections: renderStateProjection does not clobber non-empty STATE.md when manifest has milestones', async () => {
247
458
  const base = mkdtempSync(join(tmpdir(), 'gsd-projection-stale-'));
248
459
  const gsdDir = join(base, '.gsd');
@@ -10,7 +10,6 @@
10
10
  * for recovery, and invalidates caches.
11
11
  */
12
12
 
13
- import { join } from "node:path";
14
13
  import { existsSync } from "node:fs";
15
14
 
16
15
  import {
@@ -21,7 +20,7 @@ import {
21
20
  getLatestAssessmentByScope,
22
21
  updateMilestoneStatus,
23
22
  } from "../gsd-db.js";
24
- import { gsdProjectionRoot, clearPathCache, relMilestoneFile } from "../paths.js";
23
+ import { clearPathCache, resolveMilestoneFile, targetMilestoneFile } from "../paths.js";
25
24
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
26
25
  import { isClosedStatus, isDeferredStatus } from "../status-guards.js";
27
26
  import { saveFile, clearParseCache } from "../files.js";
@@ -210,9 +209,14 @@ export async function handleCompleteMilestone(
210
209
  // ── Filesystem operations (outside transaction) ─────────────────────────
211
210
  const summaryMd = renderMilestoneSummaryMarkdown(params, completedAt);
212
211
 
213
- // Layout-aware: flat-phase → phases/NN-slug/NN-SUMMARY.md;
214
- // legacy milestones/MID/MID-SUMMARY.md.
215
- const summaryPath = join(artifactBasePath, relMilestoneFile(artifactBasePath, params.milestoneId, "SUMMARY"));
212
+ const summaryPath =
213
+ resolveMilestoneFile(artifactBasePath, params.milestoneId, "SUMMARY") ??
214
+ targetMilestoneFile(
215
+ artifactBasePath,
216
+ params.milestoneId,
217
+ "SUMMARY",
218
+ getMilestone(params.milestoneId)?.title,
219
+ );
216
220
 
217
221
  // Guard (#4598): if SUMMARY.md already exists on disk, do not overwrite it.
218
222
  // This handles re-dispatch scenarios (DB/disk state divergence) where a prior
@@ -1,6 +1,6 @@
1
- import { join } from "node:path";
2
1
  import { existsSync, unlinkSync } from "node:fs";
3
- import { relSliceFile, relMilestoneFile } from "../paths.js";
2
+ import { join } from "node:path";
3
+ import { relSliceFile, resolveMilestoneFile, resolveMilestonePath, targetMilestoneFile } from "../paths.js";
4
4
  import { clearParseCache } from "../files.js";
5
5
  import { isClosedStatus } from "../status-guards.js";
6
6
  import { isNonEmptyString } from "../validation.js";
@@ -290,12 +290,23 @@ export async function handleReassessRoadmap(
290
290
  params.sliceChanges.removed.length > 0;
291
291
 
292
292
  if (hasStructuralChanges) {
293
- // Layout-aware: flat-phase phases/NN-slug/NN-VALIDATION.md; legacy → milestones/MID/MID-VALIDATION.md
294
- const validationFile = join(basePath, relMilestoneFile(basePath, params.milestoneId, "VALIDATION"));
295
- try {
296
- if (existsSync(validationFile)) unlinkSync(validationFile);
297
- } catch (e) {
298
- logWarning("tool", `validation file cleanup failed: ${(e as Error).message}`);
293
+ const milestoneDir = resolveMilestonePath(basePath, params.milestoneId);
294
+ const validationFiles = new Set([
295
+ resolveMilestoneFile(basePath, params.milestoneId, "VALIDATION"),
296
+ targetMilestoneFile(
297
+ basePath,
298
+ params.milestoneId,
299
+ "VALIDATION",
300
+ getMilestone(params.milestoneId)?.title,
301
+ ),
302
+ milestoneDir ? join(milestoneDir, `${params.milestoneId}-VALIDATION.md`) : null,
303
+ ].filter((file): file is string => Boolean(file)));
304
+ for (const validationFile of validationFiles) {
305
+ try {
306
+ if (existsSync(validationFile)) unlinkSync(validationFile);
307
+ } catch (e) {
308
+ logWarning("tool", `validation file cleanup failed: ${(e as Error).message}`);
309
+ }
299
310
  }
300
311
  }
301
312
 
@@ -5,6 +5,11 @@
5
5
  * tasks back to "pending". This is intentional — if you're reopening a
6
6
  * slice, you're re-doing the work. Partial resets create ambiguous state.
7
7
  *
8
+ * Also recovers a desynced slice (#1205): when a UAT→planning fallback leaves
9
+ * the slice open (e.g. "pending") but its tasks still "complete", this clears
10
+ * that state so the planner can re-plan without hitting "already complete"
11
+ * rejections.
12
+ *
8
13
  * The parent milestone must still be open (not complete).
9
14
  */
10
15
 
@@ -24,8 +24,8 @@ import { writeManifest } from "../workflow-manifest.js";
24
24
  import { appendEvent } from "../workflow-events.js";
25
25
  import { logWarning } from "../workflow-logger.js";
26
26
  import { existsSync, unlinkSync } from "node:fs";
27
- import { join } from "node:path";
28
- import { resolveTasksDir, clearPathCache } from "../paths.js";
27
+ import { join, basename, dirname } from "node:path";
28
+ import { resolveTasksDir, resolveSlicePath, clearPathCache } from "../paths.js";
29
29
 
30
30
  export interface ReopenTaskParams {
31
31
  milestoneId: string;
@@ -106,10 +106,18 @@ export async function handleReopenTask(
106
106
  // ── Clean up stale filesystem artifacts (M12 fix) ────────────────────────
107
107
  // Without this, the DB-filesystem reconciler sees the SUMMARY.md and
108
108
  // auto-corrects the task back to "complete", making reopen a no-op (#3161).
109
+ // Legacy layout keeps the summary under a tasks/ subdir; flat-phase writes
110
+ // TID-SUMMARY.md directly in the phase dir. A tasks/ subdir may still exist
111
+ // in flat-phase for auxiliary artifacts — its mere existence must NOT redirect
112
+ // summary cleanup into tasks/ (#1208).
109
113
  try {
110
- const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
111
- if (tasksDir) {
112
- const summaryPath = join(tasksDir, `${params.taskId}-SUMMARY.md`);
114
+ const slicePath = resolveSlicePath(basePath, params.milestoneId, params.sliceId);
115
+ if (slicePath) {
116
+ const isLegacySlice = basename(dirname(slicePath)) === "slices";
117
+ const summaryDir = isLegacySlice
118
+ ? (resolveTasksDir(basePath, params.milestoneId, params.sliceId) ?? slicePath)
119
+ : slicePath;
120
+ const summaryPath = join(summaryDir, `${params.taskId}-SUMMARY.md`);
113
121
  if (existsSync(summaryPath)) unlinkSync(summaryPath);
114
122
  }
115
123
  } catch (cleanupErr) {