@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
@@ -50,7 +50,7 @@ import { readSessionLockData, isSessionLockProcessAlive } from "./session-lock.j
50
50
  import { nativeAddAll, nativeCommit, nativeHasCommittedHead, nativeIsRepo, nativeInit } from "./native-git-bridge.js";
51
51
  import { isInheritedRepo } from "./repo-identity.js";
52
52
  import { ensureGitignore, ensurePreferences, untrackRuntimeFiles } from "./gitignore.js";
53
- import { getIsolationMode, loadEffectiveGSDPreferences } from "./preferences.js";
53
+ import { getIsolationMode, loadEffectiveGSDPreferences, renderLanguageDirectiveForPrompt } from "./preferences.js";
54
54
  import { getAutoWorktreePath } from "./auto-worktree-path-resolution.js";
55
55
  import { resolveUokFlags } from "./uok/flags.js";
56
56
  import { ensurePlanV2Graph, isMissingFinalizedContextResult } from "./uok/plan-v2.js";
@@ -921,7 +921,7 @@ function resolveAvailableModel<T extends { id: string; provider: string }>(
921
921
  * Build the discuss-and-plan prompt for a new milestone.
922
922
  * Used by all three "new milestone" paths (first ever, no active, all complete).
923
923
  */
924
- function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string, pi: ExtensionAPI, ctx: ExtensionCommandContext, preparationContext?: string): string {
924
+ function buildDiscussPrompt(nextId: string, preamble: string, basePath: string, pi: ExtensionAPI, ctx: ExtensionCommandContext, preparationContext?: string): string {
925
925
  const milestoneRel = `.gsd/milestones/${nextId}`;
926
926
  const structuredQuestionsAvailable = getStructuredQuestionsAvailability(pi, ctx);
927
927
  const inlinedTemplates = [
@@ -931,7 +931,7 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string,
931
931
  inlineTemplate("roadmap", "Roadmap"),
932
932
  inlineTemplate("decisions", "Decisions"),
933
933
  ].join("\n\n---\n\n");
934
- return loadPrompt("discuss", {
934
+ return prependLanguageDirective(basePath, loadPrompt("discuss", {
935
935
  milestoneId: nextId,
936
936
  preamble,
937
937
  preparationContext: preparationContext ?? "",
@@ -941,14 +941,26 @@ function buildDiscussPrompt(nextId: string, preamble: string, _basePath: string,
941
941
  inlinedTemplates,
942
942
  commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
943
943
  multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
944
- });
944
+ }));
945
+ }
946
+
947
+ /**
948
+ * Prepend the configured response-language directive to a dispatched discuss
949
+ * prompt so the discuss/QA conversation is held in the language set in
950
+ * PREFERENCES.md (#1210). No-op when no language is configured.
951
+ */
952
+ function prependLanguageDirective(basePath: string, prompt: string): string {
953
+ const directive = renderLanguageDirectiveForPrompt(
954
+ loadEffectiveGSDPreferences(basePath)?.preferences,
955
+ );
956
+ return directive ? `${directive}\n\n${prompt}` : prompt;
945
957
  }
946
958
 
947
959
  /**
948
960
  * Build the discuss prompt for headless milestone creation.
949
961
  * Uses the discuss-headless prompt template with seed context injected.
950
962
  */
951
- function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePath: string): string {
963
+ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, basePath: string): string {
952
964
  const milestoneRel = `.gsd/milestones/${nextId}`;
953
965
  const inlinedTemplates = [
954
966
  inlineTemplate("project", "Project"),
@@ -957,7 +969,7 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
957
969
  inlineTemplate("roadmap", "Roadmap"),
958
970
  inlineTemplate("decisions", "Decisions"),
959
971
  ].join("\n\n---\n\n");
960
- return loadPrompt("discuss-headless", {
972
+ return prependLanguageDirective(basePath, loadPrompt("discuss-headless", {
961
973
  milestoneId: nextId,
962
974
  seedContext,
963
975
  contextPath: `${milestoneRel}/${nextId}-CONTEXT.md`,
@@ -965,7 +977,7 @@ function buildHeadlessDiscussPrompt(nextId: string, seedContext: string, _basePa
965
977
  inlinedTemplates,
966
978
  commitInstruction: buildDocsCommitInstruction(`docs(${nextId}): context, requirements, and roadmap`),
967
979
  multiMilestoneCommitInstruction: buildDocsCommitInstruction("docs: project plan — N milestones"),
968
- });
980
+ }));
969
981
  }
970
982
 
971
983
  /**
@@ -142,7 +142,7 @@ export function emitJournalEvent(basePath: string, entry: JournalEntry): void {
142
142
  // Silent failure — journal must never break auto-mode
143
143
  }
144
144
 
145
- if (!isUnifiedAuditEnabled()) return;
145
+ if (!isUnifiedAuditEnabled(basePath)) return;
146
146
  try {
147
147
  const causedBy = entry.causedBy
148
148
  ? `${entry.causedBy.flowId}:${entry.causedBy.seq}`
@@ -38,9 +38,10 @@ import {
38
38
  resolveSlicePath,
39
39
  resolveTaskFile,
40
40
  resolveTasksDir,
41
+ targetMilestoneFile,
42
+ targetSliceFile,
41
43
  gsdProjectionRoot,
42
44
  gsdRoot,
43
- buildMilestoneFileName,
44
45
  buildTaskFileName,
45
46
  buildSliceFileName,
46
47
  } from "./paths.js";
@@ -52,9 +53,6 @@ import { readCompatMarker, writeCompatMarker, computeProjectionSha } from "./com
52
53
  import type { RiskLevel } from "./types.js";
53
54
  import {
54
55
  phaseDirName,
55
- planFileName,
56
- milestoneIdToPhaseNum,
57
- sliceIdToPlanNum,
58
56
  derivePhaseSlug,
59
57
  } from "./layout-policy.js";
60
58
 
@@ -176,23 +174,6 @@ function sanitizeInlineRoadmapText(value: string | null | undefined): string {
176
174
  .trim();
177
175
  }
178
176
 
179
- function resolveRoadmapProjectionPath(basePath: string, milestoneId: string): string {
180
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
181
- const existing = resolveMilestonePath(basePath, milestoneId);
182
- const legacyBase = legacyMilestonesDir(basePath);
183
- const isLegacyLayout = existing
184
- ? existing.startsWith(legacyBase + "/") || existing.startsWith(legacyBase + "\\")
185
- : isLegacyMilestonesLayout(basePath);
186
- const phaseDir = existing ?? join(
187
- isLegacyLayout ? legacyBase : milestonesDir(basePath),
188
- isLegacyLayout ? milestoneId : canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title),
189
- );
190
- const roadmapFileName = isLegacyLayout
191
- ? `${milestoneId}-ROADMAP.md`
192
- : `${String(phaseNum).padStart(2, "0")}-ROADMAP.md`;
193
- return join(phaseDir, roadmapFileName);
194
- }
195
-
196
177
  function isMilestoneFlatPhaseLayout(basePath: string, milestoneId: string): boolean {
197
178
  const existing = resolveMilestonePath(basePath, milestoneId);
198
179
  const legacyBase = legacyMilestonesDir(basePath);
@@ -600,7 +581,7 @@ export async function renderRoadmapFromDb(
600
581
  "projection",
601
582
  `renderRoadmapFromDb skipped unplanned milestone ${milestoneId} (zero slices, empty vision) — refusing to write a stub ROADMAP`,
602
583
  );
603
- const absPath = resolveRoadmapProjectionPath(basePath, milestoneId);
584
+ const absPath = targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestone.title);
604
585
  if (existsSync(absPath)) {
605
586
  const artifactPath = toArtifactPath(absPath, basePath);
606
587
  unlinkSync(absPath);
@@ -614,7 +595,7 @@ export async function renderRoadmapFromDb(
614
595
  return { skipped: "unplanned-milestone" };
615
596
  }
616
597
 
617
- const absPath = resolveRoadmapProjectionPath(basePath, milestoneId);
598
+ const absPath = targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestone.title);
618
599
  const artifactPath = toArtifactPath(absPath, basePath);
619
600
  const content = renderRoadmapMarkdown(milestone, slices);
620
601
 
@@ -655,25 +636,20 @@ export async function renderMilestoneArtifactsFromDb(
655
636
  const artifacts = getMilestoneScopedArtifacts(milestoneId);
656
637
  if (artifacts.length === 0) return false;
657
638
 
658
- const phaseDir = resolveMilestonePath(basePath, milestoneId) ??
659
- join(
660
- milestonesDir(basePath),
661
- canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title),
662
- );
663
- mkdirSync(phaseDir, { recursive: true });
664
-
665
- const legacyBase = legacyMilestonesDir(basePath);
666
- const isLegacy = phaseDir.startsWith(legacyBase + "/") || phaseDir.startsWith(legacyBase + "\\");
639
+ const milestone = getMilestone(milestoneId);
667
640
 
668
641
  let wrote = false;
669
642
  for (const artifact of artifacts) {
670
643
  if (artifact.artifact_type === "ROADMAP") continue;
671
644
  if (!artifact.full_content.trim()) continue;
672
645
 
673
- const fileName = isLegacy
674
- ? `${milestoneId}-${artifact.artifact_type}.md`
675
- : buildMilestoneFileName(milestoneId, artifact.artifact_type);
676
- const absPath = join(phaseDir, fileName);
646
+ const absPath = targetMilestoneFile(
647
+ basePath,
648
+ milestoneId,
649
+ artifact.artifact_type,
650
+ milestone?.title,
651
+ );
652
+ mkdirSync(dirname(absPath), { recursive: true });
677
653
  const artifactPath = toArtifactPath(absPath, basePath);
678
654
  await writeAndStore(absPath, artifactPath, artifact.full_content, {
679
655
  artifact_type: artifact.artifact_type,
@@ -824,22 +800,12 @@ export async function renderSliceSummary(
824
800
  return false; // No slice data — skip silently
825
801
  }
826
802
 
827
- // Flat-phase: resolve or create the phase dir, since resolveSlicePath only
828
- // finds existing dirs. In flat-phase the slice "path" is the phase dir.
829
- let slicePath = resolveSlicePath(basePath, milestoneId, sliceId);
830
- if (!slicePath) {
831
- const mDir = milestonesDir(basePath);
832
- const dirName = canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title);
833
- slicePath = join(mDir, dirName);
834
- mkdirSync(slicePath, { recursive: true });
835
- }
836
-
803
+ const milestoneTitle = getMilestone(milestoneId)?.title;
837
804
  let wrote = false;
838
805
 
839
- // Write SUMMARY — flat-phase: NN-MM-SUMMARY.md inside phase dir
840
806
  if (slice.full_summary_md) {
841
- const summaryName = planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), "SUMMARY");
842
- const summaryAbs = join(slicePath, summaryName);
807
+ const summaryAbs = targetSliceFile(basePath, milestoneId, sliceId, "SUMMARY", milestoneTitle);
808
+ mkdirSync(dirname(summaryAbs), { recursive: true });
843
809
  const summaryArtifact = toArtifactPath(summaryAbs, basePath);
844
810
 
845
811
  await writeAndStore(summaryAbs, summaryArtifact, slice.full_summary_md, {
@@ -850,10 +816,9 @@ export async function renderSliceSummary(
850
816
  wrote = true;
851
817
  }
852
818
 
853
- // Write UAT — flat-phase: NN-MM-UAT.md inside phase dir
854
819
  if (slice.full_uat_md) {
855
- const uatName = planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), "UAT");
856
- const uatAbs = join(slicePath, uatName);
820
+ const uatAbs = targetSliceFile(basePath, milestoneId, sliceId, "UAT", milestoneTitle);
821
+ mkdirSync(dirname(uatAbs), { recursive: true });
857
822
  const uatArtifact = toArtifactPath(uatAbs, basePath);
858
823
 
859
824
  await writeAndStore(uatAbs, uatArtifact, slice.full_uat_md, {
@@ -1080,7 +1045,7 @@ function detectStaleRendersImpl(basePath: string): StaleEntry[] {
1080
1045
  // TODO(flat-phase): roadmap checkbox parsing may not match flat-phase
1081
1046
  // roadmap format, causing false-positive drift loops. Skip during transition.
1082
1047
  /*
1083
- const roadmapPath = resolveRoadmapProjectionPath(basePath, milestone.id);
1048
+ const roadmapPath = targetMilestoneFile(basePath, milestone.id, "ROADMAP", milestone.title);
1084
1049
  if (existsSync(roadmapPath)) {
1085
1050
  try {
1086
1051
  const parsed = parseProjectionByIdentity(roadmapPath, parseRoadmap) as ReturnType<typeof parseRoadmap>;
@@ -1172,36 +1137,28 @@ function detectStaleRendersImpl(basePath: string): StaleEntry[] {
1172
1137
  }
1173
1138
  }
1174
1139
 
1175
- // Check missing slice summary/UAT files
1140
+ // Check missing slice summary/UAT files. Use the same target helper as
1141
+ // renderSliceSummary so detection and repair agree on flat-phase vs legacy
1142
+ // output locations.
1176
1143
  const sliceRow = getSlice(milestone.id, slice.id);
1177
1144
  if (sliceRow && sliceRow.status === "complete") {
1178
- // Use the SAME path construction as renderSliceSummary (planFileName format)
1179
- // so the detector and repair always agree on the file location.
1180
- const slicePath = resolveSlicePath(basePath, milestone.id, slice.id);
1181
- if (slicePath) {
1182
- const phaseNum = milestoneIdToPhaseNum(milestone.id);
1183
- const planNum = sliceIdToPlanNum(slice.id);
1184
-
1185
- if (sliceRow.full_summary_md) {
1186
- const summaryName = planFileName(phaseNum, planNum, "SUMMARY");
1187
- const summaryAbsPath = join(slicePath, summaryName);
1188
- if (!existsSync(summaryAbsPath)) {
1189
- stale.push({
1190
- path: summaryAbsPath,
1191
- reason: `${slice.id} is complete with summary in DB but SUMMARY.md missing on disk`,
1192
- });
1193
- }
1145
+ if (sliceRow.full_summary_md) {
1146
+ const summaryAbsPath = targetSliceFile(basePath, milestone.id, slice.id, "SUMMARY", milestone.title);
1147
+ if (!existsSync(summaryAbsPath)) {
1148
+ stale.push({
1149
+ path: summaryAbsPath,
1150
+ reason: `${slice.id} is complete with summary in DB but SUMMARY.md missing on disk`,
1151
+ });
1194
1152
  }
1153
+ }
1195
1154
 
1196
- if (sliceRow.full_uat_md) {
1197
- const uatName = planFileName(phaseNum, planNum, "UAT");
1198
- const uatAbsPath = join(slicePath, uatName);
1199
- if (!existsSync(uatAbsPath)) {
1200
- stale.push({
1201
- path: uatAbsPath,
1202
- reason: `${slice.id} is complete with UAT in DB but UAT.md missing on disk`,
1203
- });
1204
- }
1155
+ if (sliceRow.full_uat_md) {
1156
+ const uatAbsPath = targetSliceFile(basePath, milestone.id, slice.id, "UAT", milestone.title);
1157
+ if (!existsSync(uatAbsPath)) {
1158
+ stale.push({
1159
+ path: uatAbsPath,
1160
+ reason: `${slice.id} is complete with UAT in DB but UAT.md missing on disk`,
1161
+ });
1205
1162
  }
1206
1163
  }
1207
1164
  }
@@ -1257,12 +1214,14 @@ export async function renderReplanFromDb(
1257
1214
  sliceId: string,
1258
1215
  replanData: ReplanData,
1259
1216
  ): Promise<{ replanPath: string; content: string }> {
1260
- // Flat-phase: replan file lives in the phase dir
1261
- const slicePath = resolveSlicePath(basePath, milestoneId, sliceId)
1262
- ?? join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1263
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1264
- const planNum = sliceIdToPlanNum(sliceId);
1265
- const absPath = join(slicePath, planFileName(phaseNum, planNum, "REPLAN"));
1217
+ const absPath = targetSliceFile(
1218
+ basePath,
1219
+ milestoneId,
1220
+ sliceId,
1221
+ "REPLAN",
1222
+ getMilestone(milestoneId)?.title,
1223
+ );
1224
+ mkdirSync(dirname(absPath), { recursive: true });
1266
1225
  const artifactPath = toArtifactPath(absPath, basePath);
1267
1226
 
1268
1227
  const lines: string[] = [];
@@ -1299,24 +1258,14 @@ export async function renderAssessmentFromDb(
1299
1258
  sliceId: string,
1300
1259
  assessmentData: AssessmentData,
1301
1260
  ): Promise<{ assessmentPath: string; content: string }> {
1302
- // Flat-phase: the assessment file lives in the phase dir, NOT in a slices/SID/
1303
- // subdir. resolveSlicePath() detects a slices/SID/ dir unconditionally, so a
1304
- // stray slices/SID/ created by an earlier planning step would send this write
1305
- // to a hybrid path (wrong dir, flat filename) that verification cannot find.
1306
- // Guard with the same legacy-base check relSlicePath() uses so flat-phase
1307
- // milestones always target the phase dir.
1308
- const mDir = resolveMilestonePath(basePath, milestoneId);
1309
- const legacyBase = legacyMilestonesDir(basePath);
1310
- const isLegacyLayout = mDir
1311
- ? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
1312
- : false;
1313
- const fallbackDir = join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1314
- const slicePath = isLegacyLayout
1315
- ? (resolveSlicePath(basePath, milestoneId, sliceId) ?? fallbackDir)
1316
- : (mDir ?? fallbackDir);
1317
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1318
- const planNum = sliceIdToPlanNum(sliceId);
1319
- const absPath = join(slicePath, planFileName(phaseNum, planNum, "ASSESSMENT"));
1261
+ const absPath = targetSliceFile(
1262
+ basePath,
1263
+ milestoneId,
1264
+ sliceId,
1265
+ "ASSESSMENT",
1266
+ getMilestone(milestoneId)?.title,
1267
+ );
1268
+ mkdirSync(dirname(absPath), { recursive: true });
1320
1269
  const artifactPath = toArtifactPath(absPath, basePath);
1321
1270
 
1322
1271
  const lines: string[] = [];
@@ -5,7 +5,7 @@
5
5
  // Exports: parseDecisionsTable, parseRequirementsSections, migrateFromMarkdown
6
6
 
7
7
  import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
8
- import { join, relative, basename } from 'node:path';
8
+ import { join, relative, basename, dirname } from 'node:path';
9
9
  import type { Decision, Requirement } from './types.js';
10
10
  import {
11
11
  upsertDecision,
@@ -24,6 +24,7 @@ import {
24
24
  resolveGsdRootFile,
25
25
  resolveMilestoneFile,
26
26
  resolveSliceFile,
27
+ resolveSlicePath,
27
28
  resolveTasksDir,
28
29
  legacyMilestonesDir,
29
30
  gsdRoot,
@@ -33,7 +34,7 @@ import {
33
34
  import { findMilestoneIds } from './guided-flow.js';
34
35
  import { milestoneIdToPhaseNum } from './layout-policy.js';
35
36
  import { parseRoadmap, parsePlan } from './parsers-legacy.js';
36
- import { parseContextDependsOn } from './files.js';
37
+ import { parseContextDependsOn, parseSummary } from './files.js';
37
38
  import { logWarning } from './workflow-logger.js';
38
39
 
39
40
  // ─── DECISIONS.md Parser ───────────────────────────────────────────────────
@@ -816,23 +817,68 @@ export function migrateHierarchyToDb(basePath: string): {
816
817
  // Per K002: use 'complete' not 'done'
817
818
  let taskStatus: string = taskEntry.done ? 'complete' : 'pending';
818
819
 
819
- // Pre-migration consistency (legacy layout only): if a task is marked
820
- // done but has no per-task SUMMARY.md, import it as pending so it gets
821
- // re-executed rather than silently entering the DB as complete.
822
- // Flat-phase has no per-task summary files by design — the checkbox
823
- // state is the authoritative completion signal and is imported as-is.
824
- if (taskStatus === 'complete') {
825
- const tDir = resolveTasksDir(basePath, milestoneId, sliceEntry.id);
826
- if (tDir) {
827
- const summaryFile = join(tDir, `${taskEntry.id}-SUMMARY.md`);
828
- if (!existsSync(summaryFile)) {
829
- taskStatus = 'pending';
830
- process.stderr.write(
831
- `gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary — importing as pending\n`,
832
- );
833
- }
820
+ // Resolve the per-task SUMMARY.md. Legacy layout keeps summaries under
821
+ // a tasks/ subdir; flat-phase writes TID-SUMMARY.md directly in the
822
+ // phase dir (execute-task's gsd_task_complete writes it there). A tasks/
823
+ // subdir may still exist in flat-phase for auxiliary artifacts — its
824
+ // mere existence must NOT redirect summary resolution into tasks/ (#1208).
825
+ const slicePath = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
826
+ let taskSummaryFile: string | null = null;
827
+ let isLegacySlice = false;
828
+ // Legacy layout keeps per-task summaries under a tasks/ subdir. Its
829
+ // presence — not merely "is this a legacy slice" — is what makes a
830
+ // missing summary a downgrade signal. resolveTasksDir returns null when
831
+ // no such dir exists, so a legacy slice whose checkbox is the only
832
+ // completion signal (no tasks/ dir at all) is left complete on import.
833
+ let legacyTasksDir: string | null = null;
834
+ if (slicePath) {
835
+ isLegacySlice = basename(dirname(slicePath)) === 'slices';
836
+ legacyTasksDir = isLegacySlice
837
+ ? resolveTasksDir(basePath, milestoneId, sliceEntry.id)
838
+ : null;
839
+ const summaryDir = legacyTasksDir ?? slicePath;
840
+ taskSummaryFile = join(summaryDir, `${taskEntry.id}-SUMMARY.md`);
841
+ }
842
+ const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
843
+
844
+ // Pre-migration consistency (legacy tasks/ layout only): if a task is
845
+ // marked done but its tasks/ dir has no per-task SUMMARY.md, import it
846
+ // as pending so it gets re-executed rather than silently entering the DB
847
+ // as complete. A legacy slice with no tasks/ dir (checkbox-only
848
+ // completion) and flat-phase layout are both left as-is here.
849
+ if (taskStatus === 'complete' && legacyTasksDir !== null && !hasTaskSummary) {
850
+ taskStatus = 'pending';
851
+ process.stderr.write(
852
+ `gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary — importing as pending\n`,
853
+ );
854
+ }
855
+
856
+ // Lost-update guard (#1222): a per-task SUMMARY.md is the durable
857
+ // completion attestation written by gsd_task_complete. When the plan
858
+ // checkbox is stale-unchecked but the SUMMARY exists on disk, a
859
+ // re-import must NOT downgrade the completed task back to pending — that
860
+ // silent revert is the lost-update that hard-stopped auto-mode after the
861
+ // first execute-task. The surviving SUMMARY wins over a stale checkbox.
862
+ // reopen-task removes the SUMMARY, so this never re-completes an
863
+ // intentionally reopened task.
864
+ // Flat-phase summaries are phase-scoped (T01-SUMMARY.md); when a milestone
865
+ // has multiple slices, require the summary parent frontmatter to match so
866
+ // one slice's completion does not attest a sibling's same-named task.
867
+ let summaryAttestsCompletion = hasTaskSummary;
868
+ if (summaryAttestsCompletion && !isLegacySlice && roadmap.slices.length > 1 && taskSummaryFile) {
869
+ try {
870
+ const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
871
+ summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
872
+ } catch {
873
+ summaryAttestsCompletion = false;
834
874
  }
835
875
  }
876
+ if (taskStatus === 'pending' && summaryAttestsCompletion) {
877
+ taskStatus = 'complete';
878
+ process.stderr.write(
879
+ `gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} unchecked in plan but SUMMARY.md present — importing as complete (#1222)\n`,
880
+ );
881
+ }
836
882
 
837
883
  insertTask({
838
884
  id: taskEntry.id,
@@ -844,6 +890,12 @@ export function migrateHierarchyToDb(basePath: string): {
844
890
  files: taskEntry.files ?? [],
845
891
  verify: taskEntry.verify ?? '',
846
892
  },
893
+ // #1222 (metadata half): re-import derives status from the plan/summary
894
+ // but has none of the execution prose gsd_task_complete wrote to the DB.
895
+ // Preserve the existing row's execution columns and completed_at so a
896
+ // re-import cannot blank a completed task's summary metadata or refresh
897
+ // its completion timestamp — the DB row is strictly richer than the plan.
898
+ preserveCompletionMetadata: true,
847
899
  });
848
900
  counts.tasks++;
849
901
  }
@@ -857,12 +909,31 @@ export function migrateHierarchyToDb(basePath: string): {
857
909
  if (!sliceEntry.done) {
858
910
  const sliceSummaryPath = resolveSliceFile(basePath, milestoneId, sliceEntry.id, 'SUMMARY');
859
911
  const hasSliceSummary = sliceSummaryPath !== null && existsSync(sliceSummaryPath);
912
+ const slicePathForTasks = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
913
+ const isLegacySliceLayout = slicePathForTasks !== null
914
+ && basename(dirname(slicePathForTasks)) === 'slices';
915
+ const taskSummaryDir = slicePathForTasks
916
+ ? (isLegacySliceLayout
917
+ ? (resolveTasksDir(basePath, milestoneId, sliceEntry.id) ?? slicePathForTasks)
918
+ : slicePathForTasks)
919
+ : null;
860
920
  const allTasksDone = plan.tasks.length > 0 && plan.tasks.every(t => {
861
- if (!t.done) return false;
862
- const tDir = resolveTasksDir(basePath, milestoneId, sliceEntry.id);
863
- // Flat-phase has no per-task summary files — checkbox state is authoritative.
864
- if (!tDir) return true;
865
- return existsSync(join(tDir, `${t.id}-SUMMARY.md`));
921
+ const taskSummaryFile = taskSummaryDir !== null
922
+ ? join(taskSummaryDir, `${t.id}-SUMMARY.md`)
923
+ : null;
924
+ const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
925
+ let summaryAttestsCompletion = hasTaskSummary;
926
+ if (summaryAttestsCompletion && !isLegacySliceLayout && roadmap.slices.length > 1 && taskSummaryFile) {
927
+ try {
928
+ const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
929
+ summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
930
+ } catch {
931
+ summaryAttestsCompletion = false;
932
+ }
933
+ }
934
+ // Match per-task import rules above: SUMMARY attestation wins over a stale
935
+ // checkbox (#1222); flat-phase checkboxes are authoritative without summary.
936
+ return summaryAttestsCompletion || (t.done && !isLegacySliceLayout);
866
937
  });
867
938
  if (allTasksDone && hasSliceSummary) {
868
939
  if (_getAdapter()) {
@@ -265,7 +265,7 @@ export function snapshotUnitMetrics(
265
265
  }
266
266
  saveLedger(basePath, ledger);
267
267
 
268
- if (isUnifiedAuditEnabled()) {
268
+ if (isUnifiedAuditEnabled(basePath)) {
269
269
  emitUokAuditEvent(
270
270
  basePath,
271
271
  buildAuditEnvelope({
@@ -458,7 +458,7 @@ export function snapshotUnitMetricsByScope(
458
458
  }
459
459
  saveLedger(base, scopedLedger);
460
460
 
461
- if (isUnifiedAuditEnabled()) {
461
+ if (isUnifiedAuditEnabled(base)) {
462
462
  emitUokAuditEvent(
463
463
  base,
464
464
  buildAuditEnvelope({
@@ -102,13 +102,41 @@ function replaceSetPrefix(values: Set<string>, from: string, to: string): void {
102
102
  }
103
103
  }
104
104
 
105
+ /**
106
+ * Rewrite one milestone identity across EVERY markdown identity set. The
107
+ * roadmapless subset is a view of `milestones`, so it must speak the same
108
+ * aligned vocabulary; otherwise the discussion-phase exclusion below keys off
109
+ * the stale bare id, fails its `dbScan.milestones.has(...)` membership check,
110
+ * and decrements the milestone count even though the aligned identity remains
111
+ * in the set, reporting false drift for a milestone that is actually in sync.
112
+ * Centralising the set list here keeps every alignment path from silently
113
+ * missing a set (the defect that regressed the suffixed roadmapless case).
114
+ */
115
+ function realignMilestonePrefix(markdownScan: HierarchyScan, from: string, to: string): void {
116
+ replaceSetPrefix(markdownScan.milestones, from, to);
117
+ replaceSetPrefix(markdownScan.slices, from, to);
118
+ replaceSetPrefix(markdownScan.tasks, from, to);
119
+ replaceSetPrefix(markdownScan.milestonesWithoutRoadmap, from, to);
120
+ }
121
+
105
122
  function alignNumericMarkdownIdsWithDb(markdownScan: HierarchyScan, dbScan: HierarchyScan): void {
106
123
  for (const dbId of dbScan.milestones) {
107
124
  const paddedId = paddedMilestoneId(dbId);
108
125
  if (!paddedId || dbScan.milestones.has(paddedId) || !markdownScan.milestones.has(paddedId)) continue;
109
- replaceSetPrefix(markdownScan.milestones, paddedId, dbId);
110
- replaceSetPrefix(markdownScan.slices, paddedId, dbId);
111
- replaceSetPrefix(markdownScan.tasks, paddedId, dbId);
126
+ realignMilestonePrefix(markdownScan, paddedId, dbId);
127
+ }
128
+ }
129
+
130
+ function bareMilestoneId(id: string): string | null {
131
+ const match = id.match(/^(M\d{3})-[a-z0-9]{6}$/);
132
+ return match?.[1] ?? null;
133
+ }
134
+
135
+ function alignBareMarkdownIdsWithSuffixedDb(markdownScan: HierarchyScan, dbScan: HierarchyScan): void {
136
+ for (const dbId of dbScan.milestones) {
137
+ const bareId = bareMilestoneId(dbId);
138
+ if (!bareId || dbScan.milestones.has(bareId) || !markdownScan.milestones.has(bareId)) continue;
139
+ realignMilestonePrefix(markdownScan, bareId, dbId);
112
140
  }
113
141
  }
114
142
 
@@ -208,6 +236,7 @@ export async function checkMarkdownHierarchyAgainstDb(
208
236
  const dbScan = scanDbHierarchy();
209
237
  const beforeDb = dbScan.counts;
210
238
  alignNumericMarkdownIdsWithDb(markdownScan, dbScan);
239
+ alignBareMarkdownIdsWithSuffixedDb(markdownScan, dbScan);
211
240
 
212
241
  // Discussion-phase scratch: a milestone dir with no ROADMAP and no DB row is
213
242
  // a pre-registration discussion artifact (CONTEXT/CONTEXT-DRAFT only — the