@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
@@ -13,13 +13,12 @@ import { logWarning } from "./workflow-logger.js";
13
13
  import { isClosedStatus } from "./status-guards.js";
14
14
  import { dirname, join, relative } from "node:path";
15
15
  import { getAllMilestones, getMilestone, getMilestoneScopedArtifacts, getSliceScopedArtifacts, getMilestoneSlices, getSliceTasks, getTasksBySliceIds, getTask, getSlice, insertArtifact, deleteArtifactByPath, getGateResults, isDbAvailable, } from "./gsd-db.js";
16
- import { resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTasksDir, gsdProjectionRoot, gsdRoot, buildMilestoneFileName, buildTaskFileName, } from "./paths.js";
16
+ import { resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTasksDir, targetMilestoneFile, targetSliceFile, gsdProjectionRoot, gsdRoot, buildTaskFileName, } from "./paths.js";
17
17
  import { saveFile, clearParseCache, registerCacheClearCallback } from "./files.js";
18
18
  import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
19
19
  import { invalidateStateCache } from "./state.js";
20
20
  import { clearPathCache, milestonesDir, legacyMilestonesDir, isLegacyMilestonesLayout, resolveMilestonePath, relSliceFile, canonicalPhaseDirName } from "./paths.js";
21
21
  import { readCompatMarker, writeCompatMarker, computeProjectionSha } from "./compat/compat-marker.js";
22
- import { planFileName, milestoneIdToPhaseNum, sliceIdToPlanNum, } from "./layout-policy.js";
23
22
  // ─── Compat marker invalidation ───────────────────────────────────────────
24
23
  // Every successful projection write pushes its (basePath, projectionPath,
25
24
  // entities) here; invalidateCaches() drains it and refreshes .gsd/.compat.json
@@ -134,19 +133,6 @@ function sanitizeInlineRoadmapText(value) {
134
133
  .replace(/\s+/g, " ")
135
134
  .trim();
136
135
  }
137
- function resolveRoadmapProjectionPath(basePath, milestoneId) {
138
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
139
- const existing = resolveMilestonePath(basePath, milestoneId);
140
- const legacyBase = legacyMilestonesDir(basePath);
141
- const isLegacyLayout = existing
142
- ? existing.startsWith(legacyBase + "/") || existing.startsWith(legacyBase + "\\")
143
- : isLegacyMilestonesLayout(basePath);
144
- const phaseDir = existing ?? join(isLegacyLayout ? legacyBase : milestonesDir(basePath), isLegacyLayout ? milestoneId : canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
145
- const roadmapFileName = isLegacyLayout
146
- ? `${milestoneId}-ROADMAP.md`
147
- : `${String(phaseNum).padStart(2, "0")}-ROADMAP.md`;
148
- return join(phaseDir, roadmapFileName);
149
- }
150
136
  function isMilestoneFlatPhaseLayout(basePath, milestoneId) {
151
137
  const existing = resolveMilestonePath(basePath, milestoneId);
152
138
  const legacyBase = legacyMilestonesDir(basePath);
@@ -492,7 +478,7 @@ export async function renderRoadmapFromDb(basePath, milestoneId) {
492
478
  // clear "write the ROADMAP" failure) instead of a misleading stub.
493
479
  if (slices.length === 0 && !milestone.vision.trim()) {
494
480
  logWarning("projection", `renderRoadmapFromDb skipped unplanned milestone ${milestoneId} (zero slices, empty vision) — refusing to write a stub ROADMAP`);
495
- const absPath = resolveRoadmapProjectionPath(basePath, milestoneId);
481
+ const absPath = targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestone.title);
496
482
  if (existsSync(absPath)) {
497
483
  const artifactPath = toArtifactPath(absPath, basePath);
498
484
  unlinkSync(absPath);
@@ -506,7 +492,7 @@ export async function renderRoadmapFromDb(basePath, milestoneId) {
506
492
  }
507
493
  return { skipped: "unplanned-milestone" };
508
494
  }
509
- const absPath = resolveRoadmapProjectionPath(basePath, milestoneId);
495
+ const absPath = targetMilestoneFile(basePath, milestoneId, "ROADMAP", milestone.title);
510
496
  const artifactPath = toArtifactPath(absPath, basePath);
511
497
  const content = renderRoadmapMarkdown(milestone, slices);
512
498
  await writeAndStore(absPath, artifactPath, content, {
@@ -536,21 +522,15 @@ export async function renderMilestoneArtifactsFromDb(basePath, milestoneId) {
536
522
  const artifacts = getMilestoneScopedArtifacts(milestoneId);
537
523
  if (artifacts.length === 0)
538
524
  return false;
539
- const phaseDir = resolveMilestonePath(basePath, milestoneId) ??
540
- join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
541
- mkdirSync(phaseDir, { recursive: true });
542
- const legacyBase = legacyMilestonesDir(basePath);
543
- const isLegacy = phaseDir.startsWith(legacyBase + "/") || phaseDir.startsWith(legacyBase + "\\");
525
+ const milestone = getMilestone(milestoneId);
544
526
  let wrote = false;
545
527
  for (const artifact of artifacts) {
546
528
  if (artifact.artifact_type === "ROADMAP")
547
529
  continue;
548
530
  if (!artifact.full_content.trim())
549
531
  continue;
550
- const fileName = isLegacy
551
- ? `${milestoneId}-${artifact.artifact_type}.md`
552
- : buildMilestoneFileName(milestoneId, artifact.artifact_type);
553
- const absPath = join(phaseDir, fileName);
532
+ const absPath = targetMilestoneFile(basePath, milestoneId, artifact.artifact_type, milestone?.title);
533
+ mkdirSync(dirname(absPath), { recursive: true });
554
534
  const artifactPath = toArtifactPath(absPath, basePath);
555
535
  await writeAndStore(absPath, artifactPath, artifact.full_content, {
556
536
  artifact_type: artifact.artifact_type,
@@ -664,20 +644,11 @@ export async function renderSliceSummary(basePath, milestoneId, sliceId) {
664
644
  if (!slice) {
665
645
  return false; // No slice data — skip silently
666
646
  }
667
- // Flat-phase: resolve or create the phase dir, since resolveSlicePath only
668
- // finds existing dirs. In flat-phase the slice "path" is the phase dir.
669
- let slicePath = resolveSlicePath(basePath, milestoneId, sliceId);
670
- if (!slicePath) {
671
- const mDir = milestonesDir(basePath);
672
- const dirName = canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title);
673
- slicePath = join(mDir, dirName);
674
- mkdirSync(slicePath, { recursive: true });
675
- }
647
+ const milestoneTitle = getMilestone(milestoneId)?.title;
676
648
  let wrote = false;
677
- // Write SUMMARY — flat-phase: NN-MM-SUMMARY.md inside phase dir
678
649
  if (slice.full_summary_md) {
679
- const summaryName = planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), "SUMMARY");
680
- const summaryAbs = join(slicePath, summaryName);
650
+ const summaryAbs = targetSliceFile(basePath, milestoneId, sliceId, "SUMMARY", milestoneTitle);
651
+ mkdirSync(dirname(summaryAbs), { recursive: true });
681
652
  const summaryArtifact = toArtifactPath(summaryAbs, basePath);
682
653
  await writeAndStore(summaryAbs, summaryArtifact, slice.full_summary_md, {
683
654
  artifact_type: "SUMMARY",
@@ -686,10 +657,9 @@ export async function renderSliceSummary(basePath, milestoneId, sliceId) {
686
657
  }, basePath);
687
658
  wrote = true;
688
659
  }
689
- // Write UAT — flat-phase: NN-MM-UAT.md inside phase dir
690
660
  if (slice.full_uat_md) {
691
- const uatName = planFileName(milestoneIdToPhaseNum(milestoneId), sliceIdToPlanNum(sliceId), "UAT");
692
- const uatAbs = join(slicePath, uatName);
661
+ const uatAbs = targetSliceFile(basePath, milestoneId, sliceId, "UAT", milestoneTitle);
662
+ mkdirSync(dirname(uatAbs), { recursive: true });
693
663
  const uatArtifact = toArtifactPath(uatAbs, basePath);
694
664
  await writeAndStore(uatAbs, uatArtifact, slice.full_uat_md, {
695
665
  artifact_type: "UAT",
@@ -874,7 +844,7 @@ function detectStaleRendersImpl(basePath) {
874
844
  // TODO(flat-phase): roadmap checkbox parsing may not match flat-phase
875
845
  // roadmap format, causing false-positive drift loops. Skip during transition.
876
846
  /*
877
- const roadmapPath = resolveRoadmapProjectionPath(basePath, milestone.id);
847
+ const roadmapPath = targetMilestoneFile(basePath, milestone.id, "ROADMAP", milestone.title);
878
848
  if (existsSync(roadmapPath)) {
879
849
  try {
880
850
  const parsed = parseProjectionByIdentity(roadmapPath, parseRoadmap) as ReturnType<typeof parseRoadmap>;
@@ -961,34 +931,27 @@ function detectStaleRendersImpl(basePath) {
961
931
  }
962
932
  }
963
933
  }
964
- // Check missing slice summary/UAT files
934
+ // Check missing slice summary/UAT files. Use the same target helper as
935
+ // renderSliceSummary so detection and repair agree on flat-phase vs legacy
936
+ // output locations.
965
937
  const sliceRow = getSlice(milestone.id, slice.id);
966
938
  if (sliceRow && sliceRow.status === "complete") {
967
- // Use the SAME path construction as renderSliceSummary (planFileName format)
968
- // so the detector and repair always agree on the file location.
969
- const slicePath = resolveSlicePath(basePath, milestone.id, slice.id);
970
- if (slicePath) {
971
- const phaseNum = milestoneIdToPhaseNum(milestone.id);
972
- const planNum = sliceIdToPlanNum(slice.id);
973
- if (sliceRow.full_summary_md) {
974
- const summaryName = planFileName(phaseNum, planNum, "SUMMARY");
975
- const summaryAbsPath = join(slicePath, summaryName);
976
- if (!existsSync(summaryAbsPath)) {
977
- stale.push({
978
- path: summaryAbsPath,
979
- reason: `${slice.id} is complete with summary in DB but SUMMARY.md missing on disk`,
980
- });
981
- }
939
+ if (sliceRow.full_summary_md) {
940
+ const summaryAbsPath = targetSliceFile(basePath, milestone.id, slice.id, "SUMMARY", milestone.title);
941
+ if (!existsSync(summaryAbsPath)) {
942
+ stale.push({
943
+ path: summaryAbsPath,
944
+ reason: `${slice.id} is complete with summary in DB but SUMMARY.md missing on disk`,
945
+ });
982
946
  }
983
- if (sliceRow.full_uat_md) {
984
- const uatName = planFileName(phaseNum, planNum, "UAT");
985
- const uatAbsPath = join(slicePath, uatName);
986
- if (!existsSync(uatAbsPath)) {
987
- stale.push({
988
- path: uatAbsPath,
989
- reason: `${slice.id} is complete with UAT in DB but UAT.md missing on disk`,
990
- });
991
- }
947
+ }
948
+ if (sliceRow.full_uat_md) {
949
+ const uatAbsPath = targetSliceFile(basePath, milestone.id, slice.id, "UAT", milestone.title);
950
+ if (!existsSync(uatAbsPath)) {
951
+ stale.push({
952
+ path: uatAbsPath,
953
+ reason: `${slice.id} is complete with UAT in DB but UAT.md missing on disk`,
954
+ });
992
955
  }
993
956
  }
994
957
  }
@@ -1013,12 +976,8 @@ export function roadmapRenderMarksSliceDone(roadmapContent, sliceId) {
1013
976
  return parseRoadmap(roadmapContent).slices.some((slice) => slice.id === sliceId && slice.done);
1014
977
  }
1015
978
  export async function renderReplanFromDb(basePath, milestoneId, sliceId, replanData) {
1016
- // Flat-phase: replan file lives in the phase dir
1017
- const slicePath = resolveSlicePath(basePath, milestoneId, sliceId)
1018
- ?? join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1019
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1020
- const planNum = sliceIdToPlanNum(sliceId);
1021
- const absPath = join(slicePath, planFileName(phaseNum, planNum, "REPLAN"));
979
+ const absPath = targetSliceFile(basePath, milestoneId, sliceId, "REPLAN", getMilestone(milestoneId)?.title);
980
+ mkdirSync(dirname(absPath), { recursive: true });
1022
981
  const artifactPath = toArtifactPath(absPath, basePath);
1023
982
  const lines = [];
1024
983
  lines.push(`# ${sliceId} Replan`);
@@ -1045,24 +1004,8 @@ export async function renderReplanFromDb(basePath, milestoneId, sliceId, replanD
1045
1004
  return { replanPath: absPath, content };
1046
1005
  }
1047
1006
  export async function renderAssessmentFromDb(basePath, milestoneId, sliceId, assessmentData) {
1048
- // Flat-phase: the assessment file lives in the phase dir, NOT in a slices/SID/
1049
- // subdir. resolveSlicePath() detects a slices/SID/ dir unconditionally, so a
1050
- // stray slices/SID/ created by an earlier planning step would send this write
1051
- // to a hybrid path (wrong dir, flat filename) that verification cannot find.
1052
- // Guard with the same legacy-base check relSlicePath() uses so flat-phase
1053
- // milestones always target the phase dir.
1054
- const mDir = resolveMilestonePath(basePath, milestoneId);
1055
- const legacyBase = legacyMilestonesDir(basePath);
1056
- const isLegacyLayout = mDir
1057
- ? mDir.startsWith(legacyBase + "/") || mDir.startsWith(legacyBase + "\\")
1058
- : false;
1059
- const fallbackDir = join(milestonesDir(basePath), canonicalPhaseDirName(milestoneId, getMilestone(milestoneId)?.title));
1060
- const slicePath = isLegacyLayout
1061
- ? (resolveSlicePath(basePath, milestoneId, sliceId) ?? fallbackDir)
1062
- : (mDir ?? fallbackDir);
1063
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
1064
- const planNum = sliceIdToPlanNum(sliceId);
1065
- const absPath = join(slicePath, planFileName(phaseNum, planNum, "ASSESSMENT"));
1007
+ const absPath = targetSliceFile(basePath, milestoneId, sliceId, "ASSESSMENT", getMilestone(milestoneId)?.title);
1008
+ mkdirSync(dirname(absPath), { recursive: true });
1066
1009
  const artifactPath = toArtifactPath(absPath, basePath);
1067
1010
  const lines = [];
1068
1011
  lines.push(`# ${sliceId} Assessment`);
@@ -4,14 +4,14 @@
4
4
  //
5
5
  // Exports: parseDecisionsTable, parseRequirementsSections, migrateFromMarkdown
6
6
  import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
7
- import { join, basename } from 'node:path';
7
+ import { join, basename, dirname } from 'node:path';
8
8
  import { upsertDecision, upsertRequirement, insertArtifact, insertMilestone, insertSlice, insertTask, transaction, updateMilestoneStatus, updateSliceStatus, _getAdapter, } from './gsd-db.js';
9
9
  import { openWorkflowDatabasePath } from './db-workspace.js';
10
- import { resolveGsdRootFile, resolveMilestoneFile, resolveSliceFile, resolveTasksDir, legacyMilestonesDir, gsdRoot, resolveTaskFiles, resolveMilestonePath, } from './paths.js';
10
+ import { resolveGsdRootFile, resolveMilestoneFile, resolveSliceFile, resolveSlicePath, resolveTasksDir, legacyMilestonesDir, gsdRoot, resolveTaskFiles, resolveMilestonePath, } from './paths.js';
11
11
  import { findMilestoneIds } from './guided-flow.js';
12
12
  import { milestoneIdToPhaseNum } from './layout-policy.js';
13
13
  import { parseRoadmap, parsePlan } from './parsers-legacy.js';
14
- import { parseContextDependsOn } from './files.js';
14
+ import { parseContextDependsOn, parseSummary } from './files.js';
15
15
  import { logWarning } from './workflow-logger.js';
16
16
  // ─── DECISIONS.md Parser ───────────────────────────────────────────────────
17
17
  const VALID_MADE_BY = new Set(['human', 'agent', 'collaborative']);
@@ -662,21 +662,63 @@ export function migrateHierarchyToDb(basePath) {
662
662
  for (const taskEntry of plan.tasks) {
663
663
  // Per K002: use 'complete' not 'done'
664
664
  let taskStatus = taskEntry.done ? 'complete' : 'pending';
665
- // Pre-migration consistency (legacy layout only): if a task is marked
666
- // done but has no per-task SUMMARY.md, import it as pending so it gets
667
- // re-executed rather than silently entering the DB as complete.
668
- // Flat-phase has no per-task summary files by design the checkbox
669
- // state is the authoritative completion signal and is imported as-is.
670
- if (taskStatus === 'complete') {
671
- const tDir = resolveTasksDir(basePath, milestoneId, sliceEntry.id);
672
- if (tDir) {
673
- const summaryFile = join(tDir, `${taskEntry.id}-SUMMARY.md`);
674
- if (!existsSync(summaryFile)) {
675
- taskStatus = 'pending';
676
- process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary importing as pending\n`);
677
- }
665
+ // Resolve the per-task SUMMARY.md. Legacy layout keeps summaries under
666
+ // a tasks/ subdir; flat-phase writes TID-SUMMARY.md directly in the
667
+ // phase dir (execute-task's gsd_task_complete writes it there). A tasks/
668
+ // subdir may still exist in flat-phase for auxiliary artifactsits
669
+ // mere existence must NOT redirect summary resolution into tasks/ (#1208).
670
+ const slicePath = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
671
+ let taskSummaryFile = null;
672
+ let isLegacySlice = false;
673
+ // Legacy layout keeps per-task summaries under a tasks/ subdir. Its
674
+ // presence — not merely "is this a legacy slice" — is what makes a
675
+ // missing summary a downgrade signal. resolveTasksDir returns null when
676
+ // no such dir exists, so a legacy slice whose checkbox is the only
677
+ // completion signal (no tasks/ dir at all) is left complete on import.
678
+ let legacyTasksDir = null;
679
+ if (slicePath) {
680
+ isLegacySlice = basename(dirname(slicePath)) === 'slices';
681
+ legacyTasksDir = isLegacySlice
682
+ ? resolveTasksDir(basePath, milestoneId, sliceEntry.id)
683
+ : null;
684
+ const summaryDir = legacyTasksDir ?? slicePath;
685
+ taskSummaryFile = join(summaryDir, `${taskEntry.id}-SUMMARY.md`);
686
+ }
687
+ const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
688
+ // Pre-migration consistency (legacy tasks/ layout only): if a task is
689
+ // marked done but its tasks/ dir has no per-task SUMMARY.md, import it
690
+ // as pending so it gets re-executed rather than silently entering the DB
691
+ // as complete. A legacy slice with no tasks/ dir (checkbox-only
692
+ // completion) and flat-phase layout are both left as-is here.
693
+ if (taskStatus === 'complete' && legacyTasksDir !== null && !hasTaskSummary) {
694
+ taskStatus = 'pending';
695
+ process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} marked done but missing summary — importing as pending\n`);
696
+ }
697
+ // Lost-update guard (#1222): a per-task SUMMARY.md is the durable
698
+ // completion attestation written by gsd_task_complete. When the plan
699
+ // checkbox is stale-unchecked but the SUMMARY exists on disk, a
700
+ // re-import must NOT downgrade the completed task back to pending — that
701
+ // silent revert is the lost-update that hard-stopped auto-mode after the
702
+ // first execute-task. The surviving SUMMARY wins over a stale checkbox.
703
+ // reopen-task removes the SUMMARY, so this never re-completes an
704
+ // intentionally reopened task.
705
+ // Flat-phase summaries are phase-scoped (T01-SUMMARY.md); when a milestone
706
+ // has multiple slices, require the summary parent frontmatter to match so
707
+ // one slice's completion does not attest a sibling's same-named task.
708
+ let summaryAttestsCompletion = hasTaskSummary;
709
+ if (summaryAttestsCompletion && !isLegacySlice && roadmap.slices.length > 1 && taskSummaryFile) {
710
+ try {
711
+ const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
712
+ summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
713
+ }
714
+ catch {
715
+ summaryAttestsCompletion = false;
678
716
  }
679
717
  }
718
+ if (taskStatus === 'pending' && summaryAttestsCompletion) {
719
+ taskStatus = 'complete';
720
+ process.stderr.write(`gsd-migrate: ${milestoneId}/${sliceEntry.id}/${taskEntry.id} unchecked in plan but SUMMARY.md present — importing as complete (#1222)\n`);
721
+ }
680
722
  insertTask({
681
723
  id: taskEntry.id,
682
724
  sliceId: sliceEntry.id,
@@ -687,6 +729,12 @@ export function migrateHierarchyToDb(basePath) {
687
729
  files: taskEntry.files ?? [],
688
730
  verify: taskEntry.verify ?? '',
689
731
  },
732
+ // #1222 (metadata half): re-import derives status from the plan/summary
733
+ // but has none of the execution prose gsd_task_complete wrote to the DB.
734
+ // Preserve the existing row's execution columns and completed_at so a
735
+ // re-import cannot blank a completed task's summary metadata or refresh
736
+ // its completion timestamp — the DB row is strictly richer than the plan.
737
+ preserveCompletionMetadata: true,
690
738
  });
691
739
  counts.tasks++;
692
740
  }
@@ -699,14 +747,32 @@ export function migrateHierarchyToDb(basePath) {
699
747
  if (!sliceEntry.done) {
700
748
  const sliceSummaryPath = resolveSliceFile(basePath, milestoneId, sliceEntry.id, 'SUMMARY');
701
749
  const hasSliceSummary = sliceSummaryPath !== null && existsSync(sliceSummaryPath);
750
+ const slicePathForTasks = resolveSlicePath(basePath, milestoneId, sliceEntry.id);
751
+ const isLegacySliceLayout = slicePathForTasks !== null
752
+ && basename(dirname(slicePathForTasks)) === 'slices';
753
+ const taskSummaryDir = slicePathForTasks
754
+ ? (isLegacySliceLayout
755
+ ? (resolveTasksDir(basePath, milestoneId, sliceEntry.id) ?? slicePathForTasks)
756
+ : slicePathForTasks)
757
+ : null;
702
758
  const allTasksDone = plan.tasks.length > 0 && plan.tasks.every(t => {
703
- if (!t.done)
704
- return false;
705
- const tDir = resolveTasksDir(basePath, milestoneId, sliceEntry.id);
706
- // Flat-phase has no per-task summary files — checkbox state is authoritative.
707
- if (!tDir)
708
- return true;
709
- return existsSync(join(tDir, `${t.id}-SUMMARY.md`));
759
+ const taskSummaryFile = taskSummaryDir !== null
760
+ ? join(taskSummaryDir, `${t.id}-SUMMARY.md`)
761
+ : null;
762
+ const hasTaskSummary = taskSummaryFile !== null && existsSync(taskSummaryFile);
763
+ let summaryAttestsCompletion = hasTaskSummary;
764
+ if (summaryAttestsCompletion && !isLegacySliceLayout && roadmap.slices.length > 1 && taskSummaryFile) {
765
+ try {
766
+ const summaryContent = readFileSync(taskSummaryFile, 'utf-8');
767
+ summaryAttestsCompletion = parseSummary(summaryContent).frontmatter.parent === sliceEntry.id;
768
+ }
769
+ catch {
770
+ summaryAttestsCompletion = false;
771
+ }
772
+ }
773
+ // Match per-task import rules above: SUMMARY attestation wins over a stale
774
+ // checkbox (#1222); flat-phase checkboxes are authoritative without summary.
775
+ return summaryAttestsCompletion || (t.done && !isLegacySliceLayout);
710
776
  });
711
777
  if (allTasksDone && hasSliceSummary) {
712
778
  if (_getAdapter()) {
@@ -172,7 +172,7 @@ export function snapshotUnitMetrics(ctx, unitType, unitId, startedAt, model, opt
172
172
  ledger.units.push(unit);
173
173
  }
174
174
  saveLedger(basePath, ledger);
175
- if (isUnifiedAuditEnabled()) {
175
+ if (isUnifiedAuditEnabled(basePath)) {
176
176
  emitUokAuditEvent(basePath, buildAuditEnvelope({
177
177
  traceId: opts?.traceId ?? `metrics:${unitType}:${unitId}`,
178
178
  turnId: opts?.turnId,
@@ -324,7 +324,7 @@ export function snapshotUnitMetricsByScope(scope, ctx, unitType, unitId, started
324
324
  scopedLedger.units.push(unit);
325
325
  }
326
326
  saveLedger(base, scopedLedger);
327
- if (isUnifiedAuditEnabled()) {
327
+ if (isUnifiedAuditEnabled(base)) {
328
328
  emitUokAuditEvent(base, buildAuditEnvelope({
329
329
  traceId: opts?.traceId ?? `metrics:${unitType}:${unitId}`,
330
330
  turnId: opts?.turnId,
@@ -56,14 +56,40 @@ function replaceSetPrefix(values, from, to) {
56
56
  values.add(`${to}${value.slice(from.length)}`);
57
57
  }
58
58
  }
59
+ /**
60
+ * Rewrite one milestone identity across EVERY markdown identity set. The
61
+ * roadmapless subset is a view of `milestones`, so it must speak the same
62
+ * aligned vocabulary; otherwise the discussion-phase exclusion below keys off
63
+ * the stale bare id, fails its `dbScan.milestones.has(...)` membership check,
64
+ * and decrements the milestone count even though the aligned identity remains
65
+ * in the set, reporting false drift for a milestone that is actually in sync.
66
+ * Centralising the set list here keeps every alignment path from silently
67
+ * missing a set (the defect that regressed the suffixed roadmapless case).
68
+ */
69
+ function realignMilestonePrefix(markdownScan, from, to) {
70
+ replaceSetPrefix(markdownScan.milestones, from, to);
71
+ replaceSetPrefix(markdownScan.slices, from, to);
72
+ replaceSetPrefix(markdownScan.tasks, from, to);
73
+ replaceSetPrefix(markdownScan.milestonesWithoutRoadmap, from, to);
74
+ }
59
75
  function alignNumericMarkdownIdsWithDb(markdownScan, dbScan) {
60
76
  for (const dbId of dbScan.milestones) {
61
77
  const paddedId = paddedMilestoneId(dbId);
62
78
  if (!paddedId || dbScan.milestones.has(paddedId) || !markdownScan.milestones.has(paddedId))
63
79
  continue;
64
- replaceSetPrefix(markdownScan.milestones, paddedId, dbId);
65
- replaceSetPrefix(markdownScan.slices, paddedId, dbId);
66
- replaceSetPrefix(markdownScan.tasks, paddedId, dbId);
80
+ realignMilestonePrefix(markdownScan, paddedId, dbId);
81
+ }
82
+ }
83
+ function bareMilestoneId(id) {
84
+ const match = id.match(/^(M\d{3})-[a-z0-9]{6}$/);
85
+ return match?.[1] ?? null;
86
+ }
87
+ function alignBareMarkdownIdsWithSuffixedDb(markdownScan, dbScan) {
88
+ for (const dbId of dbScan.milestones) {
89
+ const bareId = bareMilestoneId(dbId);
90
+ if (!bareId || dbScan.milestones.has(bareId) || !markdownScan.milestones.has(bareId))
91
+ continue;
92
+ realignMilestonePrefix(markdownScan, bareId, dbId);
67
93
  }
68
94
  }
69
95
  /**
@@ -148,6 +174,7 @@ export async function checkMarkdownHierarchyAgainstDb(basePath) {
148
174
  const dbScan = scanDbHierarchy();
149
175
  const beforeDb = dbScan.counts;
150
176
  alignNumericMarkdownIdsWithDb(markdownScan, dbScan);
177
+ alignBareMarkdownIdsWithSuffixedDb(markdownScan, dbScan);
151
178
  // Discussion-phase scratch: a milestone dir with no ROADMAP and no DB row is
152
179
  // a pre-registration discussion artifact (CONTEXT/CONTEXT-DRAFT only — the
153
180
  // queued DB row is inserted only at discussion handoff). Treating it as