gsd-pi 2.81.0 → 2.82.0-dev.20138ae42

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 (740) hide show
  1. package/README.md +64 -33
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +10 -1
  4. package/dist/resources/extensions/browser-tools/tools/screenshot.js +1 -0
  5. package/dist/resources/extensions/browser-tools/tools/zoom.js +1 -0
  6. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  7. package/dist/resources/extensions/cmux/index.js +5 -0
  8. package/dist/resources/extensions/gsd/auto/infra-errors.js +9 -3
  9. package/dist/resources/extensions/gsd/auto/loop.js +116 -13
  10. package/dist/resources/extensions/gsd/auto/orchestrator.js +124 -6
  11. package/dist/resources/extensions/gsd/auto/phases.js +220 -98
  12. package/dist/resources/extensions/gsd/auto/run-unit.js +66 -3
  13. package/dist/resources/extensions/gsd/auto/session.js +9 -0
  14. package/dist/resources/extensions/gsd/auto/verification-retry-policy.js +43 -0
  15. package/dist/resources/extensions/gsd/auto/workflow-memory-pressure.js +12 -0
  16. package/dist/resources/extensions/gsd/auto-dashboard.js +247 -178
  17. package/dist/resources/extensions/gsd/auto-dispatch.js +27 -17
  18. package/dist/resources/extensions/gsd/auto-model-selection.js +2 -0
  19. package/dist/resources/extensions/gsd/auto-post-unit.js +240 -128
  20. package/dist/resources/extensions/gsd/auto-prompts.js +13 -5
  21. package/dist/resources/extensions/gsd/auto-recovery.js +34 -179
  22. package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -0
  23. package/dist/resources/extensions/gsd/auto-start.js +105 -35
  24. package/dist/resources/extensions/gsd/auto-unit-closeout.js +33 -5
  25. package/dist/resources/extensions/gsd/auto-verification.js +38 -26
  26. package/dist/resources/extensions/gsd/auto-worktree.js +119 -1
  27. package/dist/resources/extensions/gsd/auto.js +400 -108
  28. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +38 -12
  29. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +9 -8
  30. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -4
  31. package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +4 -8
  32. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +21 -9
  33. package/dist/resources/extensions/gsd/bootstrap/system-context.js +55 -12
  34. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  35. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  36. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  37. package/dist/resources/extensions/gsd/commands/handlers/core.js +38 -1
  38. package/dist/resources/extensions/gsd/commands/handlers/notifications-handler.js +4 -10
  39. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +9 -0
  40. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  41. package/dist/resources/extensions/gsd/commands-handlers.js +15 -2
  42. package/dist/resources/extensions/gsd/context-store.js +112 -0
  43. package/dist/resources/extensions/gsd/crash-recovery.js +31 -5
  44. package/dist/resources/extensions/gsd/db/unit-dispatches.js +3 -2
  45. package/dist/resources/extensions/gsd/db-writer.js +150 -84
  46. package/dist/resources/extensions/gsd/dispatch-guard.js +2 -2
  47. package/dist/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  48. package/dist/resources/extensions/gsd/doctor-git-checks.js +41 -6
  49. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +28 -11
  50. package/dist/resources/extensions/gsd/doctor.js +2 -28
  51. package/dist/resources/extensions/gsd/export-html.js +27 -425
  52. package/dist/resources/extensions/gsd/git-service.js +41 -2
  53. package/dist/resources/extensions/gsd/gsd-db.js +8 -23
  54. package/dist/resources/extensions/gsd/guided-flow.js +93 -111
  55. package/dist/resources/extensions/gsd/guided-unit-context.js +23 -0
  56. package/dist/resources/extensions/gsd/health-widget-core.js +1 -1
  57. package/dist/resources/extensions/gsd/health-widget.js +4 -10
  58. package/dist/resources/extensions/gsd/knowledge-backfill.js +144 -0
  59. package/dist/resources/extensions/gsd/knowledge-capture.js +136 -0
  60. package/dist/resources/extensions/gsd/knowledge-parser.js +154 -0
  61. package/dist/resources/extensions/gsd/knowledge-projection.js +210 -0
  62. package/dist/resources/extensions/gsd/markdown-renderer.js +6 -96
  63. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  64. package/dist/resources/extensions/gsd/memory-backfill.js +73 -17
  65. package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +222 -0
  66. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  67. package/dist/resources/extensions/gsd/migrate/parsers.js +10 -0
  68. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  69. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  70. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  71. package/dist/resources/extensions/gsd/migration-auto-check.js +12 -17
  72. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  73. package/dist/resources/extensions/gsd/native-git-bridge.js +62 -26
  74. package/dist/resources/extensions/gsd/notification-overlay.js +35 -40
  75. package/dist/resources/extensions/gsd/parallel-merge.js +53 -30
  76. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +25 -33
  77. package/dist/resources/extensions/gsd/pending-auto-start.js +52 -0
  78. package/dist/resources/extensions/gsd/post-execution-checks.js +73 -2
  79. package/dist/resources/extensions/gsd/pre-execution-checks.js +28 -1
  80. package/dist/resources/extensions/gsd/prompt-loader.js +1 -1
  81. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  82. package/dist/resources/extensions/gsd/prompts/complete-slice.md +14 -12
  83. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +28 -10
  84. package/dist/resources/extensions/gsd/prompts/discuss.md +29 -11
  85. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  86. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  87. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  88. package/dist/resources/extensions/gsd/prompts/queue.md +4 -4
  89. package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  90. package/dist/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  91. package/dist/resources/extensions/gsd/prompts/system.md +2 -2
  92. package/dist/resources/extensions/gsd/provider-switch-observer.js +146 -0
  93. package/dist/resources/extensions/gsd/recovery-classification.js +15 -1
  94. package/dist/resources/extensions/gsd/session-lock.js +40 -0
  95. package/dist/resources/extensions/gsd/smart-entry-routing.js +36 -0
  96. package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +131 -0
  97. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +252 -0
  98. package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +45 -0
  99. package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +82 -0
  100. package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +50 -0
  101. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +124 -0
  102. package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-worker.js +32 -0
  103. package/dist/resources/extensions/gsd/state-reconciliation/errors.js +41 -0
  104. package/dist/resources/extensions/gsd/state-reconciliation/index.js +99 -0
  105. package/dist/resources/extensions/gsd/state-reconciliation/registry.js +24 -0
  106. package/dist/resources/extensions/gsd/state-reconciliation/spawn-gate.js +43 -0
  107. package/dist/resources/extensions/gsd/state-reconciliation/types.js +3 -0
  108. package/dist/resources/extensions/gsd/state-reconciliation.js +5 -26
  109. package/dist/resources/extensions/gsd/status-guards.js +4 -0
  110. package/dist/resources/extensions/gsd/templates/knowledge.md +2 -2
  111. package/dist/resources/extensions/gsd/templates/plan.md +8 -5
  112. package/dist/resources/extensions/gsd/templates/task-plan.md +4 -2
  113. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -8
  114. package/dist/resources/extensions/gsd/tools/complete-slice.js +6 -8
  115. package/dist/resources/extensions/gsd/tools/plan-milestone.js +7 -1
  116. package/dist/resources/extensions/gsd/tools/plan-slice.js +89 -14
  117. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  118. package/dist/resources/extensions/gsd/tui/render-kit.js +74 -0
  119. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  120. package/dist/resources/extensions/gsd/validation.js +23 -1
  121. package/dist/resources/extensions/gsd/verification-gate.js +68 -7
  122. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  123. package/dist/resources/extensions/gsd/watch/header-renderer.js +92 -69
  124. package/dist/resources/extensions/gsd/watch/splash-palette.js +10 -0
  125. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  126. package/dist/resources/extensions/gsd/workflow-projections.js +6 -8
  127. package/dist/resources/extensions/gsd/worktree-lifecycle.js +769 -319
  128. package/dist/resources/extensions/gsd/worktree-telemetry.js +3 -1
  129. package/dist/resources/extensions/shared/html-shell.js +388 -0
  130. package/dist/resources/extensions/subagent/index.js +448 -78
  131. package/dist/resources/extensions/subagent/launch.js +77 -0
  132. package/dist/resources/extensions/subagent/run-store.js +148 -0
  133. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  134. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  135. package/dist/resources/extensions/visual-brief/index.js +5 -0
  136. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  137. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  138. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  139. package/dist/web/standalone/.next/BUILD_ID +1 -1
  140. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  141. package/dist/web/standalone/.next/build-manifest.json +4 -4
  142. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  143. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  144. package/dist/web/standalone/.next/required-server-files.json +3 -3
  145. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  146. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  148. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  149. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  150. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  151. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  152. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  153. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  154. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  155. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  156. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  157. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  158. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  159. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  160. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  161. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  162. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  163. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  164. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  165. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  166. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  167. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  168. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  171. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  173. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  175. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
  185. package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
  186. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  187. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  188. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  189. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  190. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  191. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  192. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  193. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  194. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  195. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  196. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  197. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  199. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  200. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  201. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  202. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  203. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  204. package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
  205. package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  206. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  207. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  208. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  209. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  210. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  211. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  212. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  213. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  214. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
  215. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  216. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  217. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  218. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  219. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  220. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  221. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  222. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  223. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  224. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  225. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  226. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  227. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  228. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  229. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  230. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  231. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  232. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
  233. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
  234. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  235. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  236. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  237. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  238. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
  239. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  240. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +2 -2
  241. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  242. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  243. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  244. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  245. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  246. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  247. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  248. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  249. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  250. package/dist/web/standalone/.next/server/app/index.html +1 -1
  251. package/dist/web/standalone/.next/server/app/index.rsc +5 -8
  252. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  253. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -8
  254. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  255. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  256. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  257. package/dist/web/standalone/.next/server/app/page.js +2 -2
  258. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  259. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  260. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  261. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  262. package/dist/web/standalone/.next/server/chunks/63.js +3 -3
  263. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  264. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  265. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  266. package/dist/web/standalone/.next/server/middleware.js +2 -2
  267. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  268. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  269. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  270. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  271. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  272. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  273. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  274. package/dist/web/standalone/.next/static/chunks/app/_not-found/{page-2f24283c162b6ab3.js → page-f2a7482d42a5614b.js} +1 -1
  275. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  276. package/dist/web/standalone/.next/static/chunks/app/page-752f1e2ebdaa3e45.js +1 -0
  277. package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +1 -0
  278. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  279. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  280. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  281. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  282. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  283. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  284. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  285. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  286. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  287. package/dist/web/standalone/server.js +1 -1
  288. package/dist/welcome-screen.d.ts +0 -7
  289. package/dist/welcome-screen.js +60 -69
  290. package/package.json +6 -5
  291. package/packages/contracts/dist/rpc.test.js +7 -0
  292. package/packages/contracts/dist/rpc.test.js.map +1 -1
  293. package/packages/contracts/dist/workflow.d.ts +21 -0
  294. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  295. package/packages/contracts/dist/workflow.js +24 -0
  296. package/packages/contracts/dist/workflow.js.map +1 -1
  297. package/packages/contracts/src/rpc.test.ts +8 -0
  298. package/packages/contracts/src/workflow.ts +24 -0
  299. package/packages/daemon/package.json +2 -2
  300. package/packages/mcp-server/README.md +14 -3
  301. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  302. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  303. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  304. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  305. package/packages/mcp-server/package.json +2 -2
  306. package/packages/mcp-server/src/workflow-tools-parity.test.ts +244 -0
  307. package/packages/mcp-server/src/workflow-tools.test.ts +23 -1
  308. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  309. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  310. package/packages/native/package.json +1 -1
  311. package/packages/native/tsconfig.json +2 -1
  312. package/packages/native/tsconfig.tsbuildinfo +1 -1
  313. package/packages/pi-agent-core/package.json +1 -1
  314. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
  315. package/packages/pi-ai/dist/index.d.ts +2 -2
  316. package/packages/pi-ai/dist/index.d.ts.map +1 -1
  317. package/packages/pi-ai/dist/index.js +1 -1
  318. package/packages/pi-ai/dist/index.js.map +1 -1
  319. package/packages/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  320. package/packages/pi-ai/dist/providers/openai-codex-responses.js +82 -1
  321. package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  322. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts +2 -0
  323. package/packages/pi-ai/dist/providers/openai-codex-responses.test.d.ts.map +1 -0
  324. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js +52 -0
  325. package/packages/pi-ai/dist/providers/openai-codex-responses.test.js.map +1 -0
  326. package/packages/pi-ai/dist/providers/simple-options.d.ts +2 -4
  327. package/packages/pi-ai/dist/providers/simple-options.d.ts.map +1 -1
  328. package/packages/pi-ai/dist/providers/simple-options.js +5 -6
  329. package/packages/pi-ai/dist/providers/simple-options.js.map +1 -1
  330. package/packages/pi-ai/dist/providers/simple-options.test.d.ts +2 -0
  331. package/packages/pi-ai/dist/providers/simple-options.test.d.ts.map +1 -0
  332. package/packages/pi-ai/dist/providers/simple-options.test.js +50 -0
  333. package/packages/pi-ai/dist/providers/simple-options.test.js.map +1 -0
  334. package/packages/pi-ai/dist/providers/transform-messages.d.ts +11 -0
  335. package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
  336. package/packages/pi-ai/dist/providers/transform-messages.js +20 -0
  337. package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
  338. package/packages/pi-ai/package.json +1 -1
  339. package/packages/pi-ai/src/index.ts +7 -2
  340. package/packages/pi-ai/src/providers/openai-codex-responses.test.ts +63 -0
  341. package/packages/pi-ai/src/providers/openai-codex-responses.ts +91 -1
  342. package/packages/pi-ai/src/providers/simple-options.test.ts +60 -0
  343. package/packages/pi-ai/src/providers/simple-options.ts +5 -6
  344. package/packages/pi-ai/src/providers/transform-messages.ts +24 -0
  345. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
  346. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts +2 -0
  347. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.d.ts.map +1 -0
  348. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js +66 -0
  349. package/packages/pi-coding-agent/dist/core/agent-session-thinking-level.test.js.map +1 -0
  350. package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
  351. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  352. package/packages/pi-coding-agent/dist/core/system-prompt.js +4 -4
  353. package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
  354. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts +2 -0
  355. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.d.ts.map +1 -0
  356. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js +47 -0
  357. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/assistant-message-design.test.js.map +1 -0
  358. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +76 -9
  359. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts +2 -0
  361. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.d.ts.map +1 -0
  362. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js +40 -0
  363. package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/user-message-design.test.js.map +1 -0
  364. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +0 -1
  365. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -1
  366. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +30 -29
  367. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -1
  368. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +10 -3
  369. package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -1
  370. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +13 -13
  372. package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  373. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -3
  374. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
  375. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +58 -3
  376. package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
  377. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts +2 -2
  378. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.d.ts.map +1 -1
  379. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +12 -6
  380. package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
  381. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  382. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +33 -42
  383. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  384. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +0 -1
  385. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  386. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +86 -82
  387. package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
  388. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts +35 -0
  389. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.d.ts.map +1 -0
  390. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js +152 -0
  391. package/packages/pi-coding-agent/dist/modes/interactive/components/transcript-design.js.map +1 -0
  392. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts +16 -0
  393. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.d.ts.map +1 -0
  394. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js +73 -0
  395. package/packages/pi-coding-agent/dist/modes/interactive/components/tui-style-kit.js.map +1 -0
  396. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +1 -1
  397. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  398. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js +12 -8
  399. package/packages/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  400. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
  401. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
  402. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
  403. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
  404. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  405. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +105 -1
  406. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  407. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  408. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +27 -26
  409. package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
  410. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +9 -6
  411. package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -1
  412. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts +2 -0
  413. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.d.ts.map +1 -0
  414. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js +17 -0
  415. package/packages/pi-coding-agent/dist/tests/system-prompt-file-safety.test.js.map +1 -0
  416. package/packages/pi-coding-agent/package.json +1 -1
  417. package/packages/pi-coding-agent/src/core/agent-session-thinking-level.test.ts +79 -0
  418. package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
  419. package/packages/pi-coding-agent/src/core/system-prompt.ts +4 -4
  420. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/assistant-message-design.test.ts +56 -0
  421. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +113 -9
  422. package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/user-message-design.test.ts +48 -0
  423. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +10 -3
  424. package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +43 -42
  425. package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +14 -14
  426. package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +64 -3
  427. package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +13 -7
  428. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +33 -44
  429. package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +84 -104
  430. package/packages/pi-coding-agent/src/modes/interactive/components/transcript-design.ts +196 -0
  431. package/packages/pi-coding-agent/src/modes/interactive/components/tui-style-kit.ts +94 -0
  432. package/packages/pi-coding-agent/src/modes/interactive/components/user-message.ts +14 -9
  433. package/packages/pi-coding-agent/src/modes/interactive/theme/theme-highlight.test.ts +23 -0
  434. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +106 -1
  435. package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +27 -26
  436. package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +9 -6
  437. package/packages/pi-coding-agent/src/tests/system-prompt-file-safety.test.ts +22 -0
  438. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  439. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js +14 -1
  440. package/packages/pi-tui/dist/__tests__/overlay-layout.test.js.map +1 -1
  441. package/packages/pi-tui/dist/overlay-layout.d.ts.map +1 -1
  442. package/packages/pi-tui/dist/overlay-layout.js +9 -6
  443. package/packages/pi-tui/dist/overlay-layout.js.map +1 -1
  444. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  445. package/packages/pi-tui/dist/tui.js +5 -0
  446. package/packages/pi-tui/dist/tui.js.map +1 -1
  447. package/packages/pi-tui/package.json +1 -1
  448. package/packages/pi-tui/src/__tests__/overlay-layout.test.ts +20 -1
  449. package/packages/pi-tui/src/overlay-layout.ts +10 -7
  450. package/packages/pi-tui/src/tui.ts +6 -0
  451. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  452. package/packages/rpc-client/package.json +1 -1
  453. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  454. package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts +2 -0
  455. package/pkg/dist/modes/interactive/theme/theme-highlight.test.d.ts.map +1 -0
  456. package/pkg/dist/modes/interactive/theme/theme-highlight.test.js +17 -0
  457. package/pkg/dist/modes/interactive/theme/theme-highlight.test.js.map +1 -0
  458. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  459. package/pkg/dist/modes/interactive/theme/theme.js +105 -1
  460. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  461. package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
  462. package/pkg/dist/modes/interactive/theme/themes.js +27 -26
  463. package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
  464. package/pkg/package.json +1 -1
  465. package/src/resources/GSD-WORKFLOW.md +10 -1
  466. package/src/resources/extensions/browser-tools/tools/screenshot.ts +1 -0
  467. package/src/resources/extensions/browser-tools/tools/zoom.ts +1 -0
  468. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  469. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  470. package/src/resources/extensions/cmux/index.ts +6 -0
  471. package/src/resources/extensions/gsd/auto/contracts.ts +59 -16
  472. package/src/resources/extensions/gsd/auto/infra-errors.ts +9 -3
  473. package/src/resources/extensions/gsd/auto/loop-deps.ts +9 -5
  474. package/src/resources/extensions/gsd/auto/loop.ts +121 -14
  475. package/src/resources/extensions/gsd/auto/orchestrator.ts +129 -6
  476. package/src/resources/extensions/gsd/auto/phases.ts +180 -20
  477. package/src/resources/extensions/gsd/auto/run-unit.ts +69 -4
  478. package/src/resources/extensions/gsd/auto/session.ts +10 -0
  479. package/src/resources/extensions/gsd/auto/verification-retry-policy.ts +82 -0
  480. package/src/resources/extensions/gsd/auto/workflow-memory-pressure.ts +13 -0
  481. package/src/resources/extensions/gsd/auto-dashboard.ts +301 -183
  482. package/src/resources/extensions/gsd/auto-dispatch.ts +29 -7
  483. package/src/resources/extensions/gsd/auto-model-selection.ts +2 -1
  484. package/src/resources/extensions/gsd/auto-post-unit.ts +273 -140
  485. package/src/resources/extensions/gsd/auto-prompts.ts +13 -5
  486. package/src/resources/extensions/gsd/auto-recovery.ts +36 -209
  487. package/src/resources/extensions/gsd/auto-runtime-state.ts +5 -0
  488. package/src/resources/extensions/gsd/auto-start.ts +114 -31
  489. package/src/resources/extensions/gsd/auto-unit-closeout.ts +51 -0
  490. package/src/resources/extensions/gsd/auto-verification.ts +46 -38
  491. package/src/resources/extensions/gsd/auto-worktree.ts +127 -1
  492. package/src/resources/extensions/gsd/auto.ts +441 -107
  493. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +54 -12
  494. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +9 -8
  495. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +16 -4
  496. package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +5 -8
  497. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +19 -7
  498. package/src/resources/extensions/gsd/bootstrap/system-context.ts +58 -15
  499. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  500. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  501. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  502. package/src/resources/extensions/gsd/commands/handlers/core.ts +41 -1
  503. package/src/resources/extensions/gsd/commands/handlers/notifications-handler.ts +4 -10
  504. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +12 -0
  505. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  506. package/src/resources/extensions/gsd/commands-handlers.ts +19 -2
  507. package/src/resources/extensions/gsd/context-store.ts +120 -1
  508. package/src/resources/extensions/gsd/crash-recovery.ts +30 -4
  509. package/src/resources/extensions/gsd/db/unit-dispatches.ts +4 -3
  510. package/src/resources/extensions/gsd/db-writer.ts +167 -84
  511. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -2
  512. package/src/resources/extensions/gsd/docs/preferences-reference.md +1 -1
  513. package/src/resources/extensions/gsd/doctor-git-checks.ts +44 -6
  514. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +25 -13
  515. package/src/resources/extensions/gsd/doctor-types.ts +2 -0
  516. package/src/resources/extensions/gsd/doctor.ts +2 -27
  517. package/src/resources/extensions/gsd/export-html.ts +27 -427
  518. package/src/resources/extensions/gsd/git-service.ts +47 -1
  519. package/src/resources/extensions/gsd/gsd-db.ts +10 -23
  520. package/src/resources/extensions/gsd/guided-flow.ts +126 -128
  521. package/src/resources/extensions/gsd/guided-unit-context.ts +30 -0
  522. package/src/resources/extensions/gsd/health-widget-core.ts +1 -1
  523. package/src/resources/extensions/gsd/health-widget.ts +6 -10
  524. package/src/resources/extensions/gsd/journal.ts +2 -0
  525. package/src/resources/extensions/gsd/knowledge-backfill.ts +164 -0
  526. package/src/resources/extensions/gsd/knowledge-capture.ts +160 -0
  527. package/src/resources/extensions/gsd/knowledge-parser.ts +174 -0
  528. package/src/resources/extensions/gsd/knowledge-projection.ts +241 -0
  529. package/src/resources/extensions/gsd/markdown-renderer.ts +10 -96
  530. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  531. package/src/resources/extensions/gsd/memory-backfill.ts +89 -17
  532. package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +277 -0
  533. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  534. package/src/resources/extensions/gsd/migrate/parsers.ts +11 -0
  535. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  536. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  537. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  538. package/src/resources/extensions/gsd/migration-auto-check.ts +15 -23
  539. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  540. package/src/resources/extensions/gsd/native-git-bridge.ts +68 -25
  541. package/src/resources/extensions/gsd/notification-overlay.ts +50 -46
  542. package/src/resources/extensions/gsd/parallel-merge.ts +61 -34
  543. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +33 -35
  544. package/src/resources/extensions/gsd/pending-auto-start.ts +79 -0
  545. package/src/resources/extensions/gsd/post-execution-checks.ts +87 -2
  546. package/src/resources/extensions/gsd/pre-execution-checks.ts +32 -1
  547. package/src/resources/extensions/gsd/prompt-loader.ts +1 -1
  548. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  549. package/src/resources/extensions/gsd/prompts/complete-slice.md +14 -12
  550. package/src/resources/extensions/gsd/prompts/discuss-headless.md +28 -10
  551. package/src/resources/extensions/gsd/prompts/discuss.md +29 -11
  552. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +4 -4
  553. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +3 -3
  554. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -4
  555. package/src/resources/extensions/gsd/prompts/queue.md +4 -4
  556. package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
  557. package/src/resources/extensions/gsd/prompts/rewrite-docs.md +1 -1
  558. package/src/resources/extensions/gsd/prompts/system.md +2 -2
  559. package/src/resources/extensions/gsd/provider-switch-observer.ts +185 -0
  560. package/src/resources/extensions/gsd/recovery-classification.ts +18 -1
  561. package/src/resources/extensions/gsd/session-lock.ts +41 -0
  562. package/src/resources/extensions/gsd/smart-entry-routing.ts +77 -0
  563. package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +172 -0
  564. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +344 -0
  565. package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +66 -0
  566. package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +101 -0
  567. package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +68 -0
  568. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +185 -0
  569. package/src/resources/extensions/gsd/state-reconciliation/drift/stale-worker.ts +46 -0
  570. package/src/resources/extensions/gsd/state-reconciliation/errors.ts +67 -0
  571. package/src/resources/extensions/gsd/state-reconciliation/index.ts +142 -0
  572. package/src/resources/extensions/gsd/state-reconciliation/registry.ts +27 -0
  573. package/src/resources/extensions/gsd/state-reconciliation/spawn-gate.ts +60 -0
  574. package/src/resources/extensions/gsd/state-reconciliation/types.ts +83 -0
  575. package/src/resources/extensions/gsd/state-reconciliation.ts +21 -53
  576. package/src/resources/extensions/gsd/status-guards.ts +5 -0
  577. package/src/resources/extensions/gsd/templates/knowledge.md +2 -2
  578. package/src/resources/extensions/gsd/templates/plan.md +8 -5
  579. package/src/resources/extensions/gsd/templates/task-plan.md +4 -2
  580. package/src/resources/extensions/gsd/tests/artifact-retry-cap.test.ts +1 -1
  581. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +170 -0
  582. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  583. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +783 -176
  584. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +487 -4
  585. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +292 -4
  586. package/src/resources/extensions/gsd/tests/auto-phases-lifecycle.test.ts +53 -2
  587. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  588. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  589. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +20 -5
  590. package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +19 -0
  591. package/src/resources/extensions/gsd/tests/auto-unit-closeout.test.ts +68 -0
  592. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  593. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  594. package/src/resources/extensions/gsd/tests/browser-tools-compatibility-declarations.test.ts +62 -0
  595. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  596. package/src/resources/extensions/gsd/tests/clear-stale-autostart.test.ts +11 -2
  597. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  598. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +4 -1
  599. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +5 -9
  600. package/src/resources/extensions/gsd/tests/complete-task.test.ts +3 -1
  601. package/src/resources/extensions/gsd/tests/context-store-decisions-from-memories.test.ts +312 -0
  602. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +43 -2
  603. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +28 -1
  604. package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +208 -0
  605. package/src/resources/extensions/gsd/tests/db-writer.test.ts +13 -8
  606. package/src/resources/extensions/gsd/tests/decisions-projection-from-memories.test.ts +453 -0
  607. package/src/resources/extensions/gsd/tests/decisions-stop-table-writes.test.ts +348 -0
  608. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +79 -4
  609. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +83 -0
  610. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +27 -0
  611. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  612. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  613. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +8 -4
  614. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +11 -7
  615. package/src/resources/extensions/gsd/tests/guided-discuss-project-prompt-rendering.test.ts +2 -0
  616. package/src/resources/extensions/gsd/tests/guided-dispatch-root.test.ts +106 -0
  617. package/src/resources/extensions/gsd/tests/guided-flow-session-isolation.test.ts +59 -11
  618. package/src/resources/extensions/gsd/tests/guided-flow.test.ts +21 -0
  619. package/src/resources/extensions/gsd/tests/guided-tool-contract.test.ts +65 -0
  620. package/src/resources/extensions/gsd/tests/header-renderer.test.ts +40 -0
  621. package/src/resources/extensions/gsd/tests/headless-milestone-parity.test.ts +17 -7
  622. package/src/resources/extensions/gsd/tests/health-widget.test.ts +14 -4
  623. package/src/resources/extensions/gsd/tests/hook-model-resolution.test.ts +5 -0
  624. package/src/resources/extensions/gsd/tests/infra-error.test.ts +2 -2
  625. package/src/resources/extensions/gsd/tests/infra-errors-cooldown.test.ts +9 -0
  626. package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +44 -0
  627. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +20 -0
  628. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +129 -1
  629. package/src/resources/extensions/gsd/tests/integration/integration-lifecycle.test.ts +13 -5
  630. package/src/resources/extensions/gsd/tests/integration/integration-proof.test.ts +1 -1
  631. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  632. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +116 -24
  633. package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +6 -1
  634. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -1
  635. package/src/resources/extensions/gsd/tests/knowledge-backfill-projection.test.ts +323 -0
  636. package/src/resources/extensions/gsd/tests/knowledge-capture.test.ts +242 -0
  637. package/src/resources/extensions/gsd/tests/knowledge.test.ts +47 -2
  638. package/src/resources/extensions/gsd/tests/load-knowledge-block-rules-only.test.ts +209 -0
  639. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1 -1
  640. package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +316 -0
  641. package/src/resources/extensions/gsd/tests/merge-conflict-stops-loop.test.ts +46 -11
  642. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  643. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +24 -1
  644. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  645. package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +26 -18
  646. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +63 -2
  647. package/src/resources/extensions/gsd/tests/notification-overlay.test.ts +78 -41
  648. package/src/resources/extensions/gsd/tests/notifications-handler.test.ts +44 -0
  649. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +12 -217
  650. package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +121 -1
  651. package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +38 -6
  652. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  653. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +29 -5
  654. package/src/resources/extensions/gsd/tests/plan-milestone-sketch-render.test.ts +157 -0
  655. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +26 -0
  656. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +2 -0
  657. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +225 -1
  658. package/src/resources/extensions/gsd/tests/plan-task.test.ts +17 -0
  659. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +81 -3
  660. package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +86 -0
  661. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  662. package/src/resources/extensions/gsd/tests/pre-execution-checks.test.ts +53 -0
  663. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +1 -1
  664. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +32 -1
  665. package/src/resources/extensions/gsd/tests/prompt-loader.test.ts +23 -0
  666. package/src/resources/extensions/gsd/tests/provider-errors.test.ts +20 -1
  667. package/src/resources/extensions/gsd/tests/provider-switch-observer.test.ts +252 -0
  668. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  669. package/src/resources/extensions/gsd/tests/resume-dispatch-worktree.test.ts +7 -3
  670. package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +6 -3
  671. package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +16 -4
  672. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +34 -0
  673. package/src/resources/extensions/gsd/tests/smart-entry-routing.test.ts +113 -0
  674. package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +53 -2
  675. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +71 -58
  676. package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +1048 -0
  677. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +64 -1
  678. package/src/resources/extensions/gsd/tests/summary-render-parity.test.ts +7 -3
  679. package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +4 -0
  680. package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +121 -1
  681. package/src/resources/extensions/gsd/tests/tui-render-kit.test.ts +66 -0
  682. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  683. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +110 -1
  684. package/src/resources/extensions/gsd/tests/verification-retry-policy.test.ts +83 -0
  685. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  686. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +7 -1
  687. package/src/resources/extensions/gsd/tests/workflow-memory-pressure.test.ts +21 -1
  688. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +1 -1
  689. package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +39 -0
  690. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +211 -59
  691. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +597 -118
  692. package/src/resources/extensions/gsd/tests/worktree-telemetry.test.ts +59 -2
  693. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +72 -0
  694. package/src/resources/extensions/gsd/tools/complete-milestone.ts +8 -10
  695. package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -8
  696. package/src/resources/extensions/gsd/tools/plan-milestone.ts +5 -1
  697. package/src/resources/extensions/gsd/tools/plan-slice.ts +98 -12
  698. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  699. package/src/resources/extensions/gsd/tui/render-kit.ts +109 -0
  700. package/src/resources/extensions/gsd/types.ts +1 -1
  701. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  702. package/src/resources/extensions/gsd/validation.ts +23 -1
  703. package/src/resources/extensions/gsd/verification-gate.ts +78 -6
  704. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  705. package/src/resources/extensions/gsd/watch/header-renderer.ts +121 -79
  706. package/src/resources/extensions/gsd/watch/splash-palette.ts +11 -0
  707. package/src/resources/extensions/gsd/workflow-logger.ts +4 -0
  708. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  709. package/src/resources/extensions/gsd/workflow-projections.ts +6 -8
  710. package/src/resources/extensions/gsd/worktree-lifecycle.ts +1196 -518
  711. package/src/resources/extensions/gsd/worktree-telemetry.ts +7 -2
  712. package/src/resources/extensions/shared/html-shell.ts +412 -0
  713. package/src/resources/extensions/subagent/index.ts +567 -103
  714. package/src/resources/extensions/subagent/launch.ts +131 -0
  715. package/src/resources/extensions/subagent/run-store.ts +218 -0
  716. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  717. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  718. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  719. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  720. package/src/resources/extensions/visual-brief/index.ts +8 -0
  721. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  722. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  723. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  724. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  725. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  726. package/dist/web/standalone/.next/static/chunks/app/layout-9ecfd95f343793f0.js +0 -1
  727. package/dist/web/standalone/.next/static/chunks/app/page-200592a7f3baf579.js +0 -1
  728. package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +0 -1
  729. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +0 -1
  730. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  731. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  732. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  733. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  734. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  735. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  736. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  737. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  738. package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +0 -1544
  739. /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → xRy4LqKSNKdT7y6ATYyEl}/_buildManifest.js +0 -0
  740. /package/dist/web/standalone/.next/static/{drLMkgfHQ8lzS229_HWYR → xRy4LqKSNKdT7y6ATYyEl}/_ssgManifest.js +0 -0
@@ -0,0 +1,1048 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: ADR-017 contract tests for drift-driven State Reconciliation.
3
+ // Covers sketch-flag (#5700), merge-state (#5701), stale-render (#5702),
4
+ // stale-worker (#5703), unregistered-milestone (#5704), roadmap-divergence
5
+ // (#5705), and missing-completion-timestamp (#5706) drift end-to-end, plus
6
+ // the repair-throw and persistent-drift error paths and Recovery
7
+ // Classification mapping for ReconciliationFailedError.
8
+
9
+ import test from "node:test";
10
+ import assert from "node:assert/strict";
11
+ import { execFileSync } from "node:child_process";
12
+ import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import { tmpdir } from "node:os";
15
+ import { randomUUID } from "node:crypto";
16
+
17
+ import {
18
+ openDatabase,
19
+ closeDatabase,
20
+ insertMilestone,
21
+ insertSlice,
22
+ insertTask,
23
+ getMilestone,
24
+ getSlice,
25
+ getSliceTasks,
26
+ setSliceSummaryMd,
27
+ updateSliceStatus,
28
+ updateTaskStatus,
29
+ } from "../gsd-db.ts";
30
+ import { clearParseCache } from "../files.ts";
31
+ import { clearPathCache } from "../paths.ts";
32
+ import { detectStaleRenders } from "../markdown-renderer.ts";
33
+ import { invalidateStateCache } from "../state.ts";
34
+ import {
35
+ reconcileBeforeDispatch,
36
+ reconcileBeforeSpawn,
37
+ ReconciliationFailedError,
38
+ type DriftHandler,
39
+ type DriftRecord,
40
+ } from "../state-reconciliation.ts";
41
+ import { classifyFailure } from "../recovery-classification.ts";
42
+ import type { GSDState } from "../types.ts";
43
+
44
+ function makeState(overrides: Partial<GSDState> = {}): GSDState {
45
+ return {
46
+ activeMilestone: { id: "M001", title: "Milestone" },
47
+ activeSlice: null,
48
+ activeTask: null,
49
+ phase: "planning",
50
+ recentDecisions: [],
51
+ blockers: [],
52
+ nextAction: "Plan milestone",
53
+ registry: [],
54
+ requirements: { active: 0, validated: 0, deferred: 0, outOfScope: 0, blocked: 0, total: 0 },
55
+ progress: { milestones: { done: 0, total: 1 } },
56
+ ...overrides,
57
+ };
58
+ }
59
+
60
+ function makeFixtureBase(): string {
61
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-drift-"));
62
+ mkdirSync(join(base, ".gsd", "milestones", "M001", "slices", "S02"), { recursive: true });
63
+ return base;
64
+ }
65
+
66
+ function cleanup(base: string): void {
67
+ try {
68
+ closeDatabase();
69
+ } catch {
70
+ /* noop */
71
+ }
72
+ try {
73
+ rmSync(base, { recursive: true, force: true });
74
+ } catch {
75
+ /* noop */
76
+ }
77
+ }
78
+
79
+ test("ADR-017 (#5700): sketch-flag drift detected and repaired end-to-end", async (t) => {
80
+ const base = makeFixtureBase();
81
+ t.after(() => cleanup(base));
82
+
83
+ openDatabase(join(base, ".gsd", "gsd.db"));
84
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
85
+ insertSlice({
86
+ id: "S02",
87
+ milestoneId: "M001",
88
+ title: "Feature",
89
+ status: "pending",
90
+ risk: "medium",
91
+ depends: [],
92
+ demo: "S02 demo.",
93
+ sequence: 1,
94
+ isSketch: true,
95
+ sketchScope: "limited",
96
+ });
97
+
98
+ // Simulate the post-crash scenario: PLAN.md exists on disk but the
99
+ // is_sketch flag is still 1.
100
+ writeFileSync(
101
+ join(base, ".gsd", "milestones", "M001", "slices", "S02", "S02-PLAN.md"),
102
+ "# S02 Plan\n",
103
+ );
104
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 1, "pre: flagged as sketch");
105
+
106
+ const state = makeState({ activeMilestone: { id: "M001", title: "Test" } });
107
+ const result = await reconcileBeforeDispatch(base, {
108
+ invalidateStateCache: () => {},
109
+ deriveState: async () => state,
110
+ });
111
+
112
+ assert.equal(result.ok, true);
113
+ assert.equal(getSlice("M001", "S02")?.is_sketch, 0, "post: flag cleared");
114
+ assert.equal(result.repaired.length, 1);
115
+ assert.equal(result.repaired[0]?.kind, "stale-sketch-flag");
116
+ if (result.repaired[0]?.kind === "stale-sketch-flag") {
117
+ assert.equal(result.repaired[0].mid, "M001");
118
+ assert.equal(result.repaired[0].sid, "S02");
119
+ }
120
+ });
121
+
122
+ test("ADR-017 (#5700): repair failure throws ReconciliationFailedError with shape", async () => {
123
+ const seenDrift: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
124
+ const handler: DriftHandler = {
125
+ kind: "stale-sketch-flag",
126
+ detect: () => [seenDrift],
127
+ repair: () => {
128
+ throw new Error("simulated repair failure");
129
+ },
130
+ };
131
+
132
+ await assert.rejects(
133
+ () =>
134
+ reconcileBeforeDispatch("/project", {
135
+ invalidateStateCache: () => {},
136
+ deriveState: async () => makeState(),
137
+ registry: [handler],
138
+ }),
139
+ (err: unknown) => {
140
+ assert.ok(err instanceof ReconciliationFailedError, "must be ReconciliationFailedError");
141
+ assert.equal(err.failures.length, 1);
142
+ assert.equal(err.failures[0]?.drift.kind, "stale-sketch-flag");
143
+ assert.ok(err.failures[0]?.cause instanceof Error);
144
+ assert.equal((err.failures[0]?.cause as Error).message, "simulated repair failure");
145
+ assert.equal(err.pass, 0);
146
+ assert.equal(err.persistentDrift.length, 0);
147
+ return true;
148
+ },
149
+ );
150
+ });
151
+
152
+ test("ADR-017 (#5700): detector failure throws ReconciliationFailedError with shape", async () => {
153
+ const handler: DriftHandler = {
154
+ kind: "stale-sketch-flag",
155
+ detect: () => {
156
+ throw new Error("simulated detect failure");
157
+ },
158
+ repair: () => {
159
+ /* detect fails before repair */
160
+ },
161
+ };
162
+
163
+ await assert.rejects(
164
+ () =>
165
+ reconcileBeforeDispatch("/project", {
166
+ invalidateStateCache: () => {},
167
+ deriveState: async () => makeState(),
168
+ registry: [handler],
169
+ }),
170
+ (err: unknown) => {
171
+ assert.ok(err instanceof ReconciliationFailedError, "must be ReconciliationFailedError");
172
+ assert.equal(err.failures.length, 1);
173
+ assert.equal(err.failures[0]?.drift.kind, "stale-sketch-flag");
174
+ assert.ok(err.failures[0]?.cause instanceof Error);
175
+ assert.equal((err.failures[0]?.cause as Error).message, "simulated detect failure");
176
+ assert.equal(err.pass, 0);
177
+ assert.equal(err.detectionFailures.length, 0);
178
+ assert.equal(err.persistentDrift.length, 0);
179
+ return true;
180
+ },
181
+ );
182
+ });
183
+
184
+ test("ADR-017 (#5700): persistent drift after cap=2 throws ReconciliationFailedError", async () => {
185
+ // Detect always returns one drift; repair is a no-op (drift never goes away).
186
+ const persistent: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
187
+ const handler: DriftHandler = {
188
+ kind: "stale-sketch-flag",
189
+ detect: () => [persistent],
190
+ repair: () => {
191
+ /* no-op: drift cannot be cleared */
192
+ },
193
+ };
194
+
195
+ await assert.rejects(
196
+ () =>
197
+ reconcileBeforeDispatch("/project", {
198
+ invalidateStateCache: () => {},
199
+ deriveState: async () => makeState(),
200
+ registry: [handler],
201
+ }),
202
+ (err: unknown) => {
203
+ assert.ok(err instanceof ReconciliationFailedError);
204
+ assert.equal(err.failures.length, 0);
205
+ assert.equal(err.persistentDrift.length, 1);
206
+ assert.equal(err.persistentDrift[0]?.kind, "stale-sketch-flag");
207
+ return true;
208
+ },
209
+ );
210
+ });
211
+
212
+ test("ADR-017 (#5700): classifyFailure recognizes ReconciliationFailedError", () => {
213
+ const err = new ReconciliationFailedError({
214
+ failures: [
215
+ {
216
+ drift: { kind: "stale-sketch-flag", mid: "M001", sid: "S02" },
217
+ cause: new Error("boom"),
218
+ },
219
+ ],
220
+ pass: 0,
221
+ });
222
+
223
+ const result = classifyFailure({ error: err });
224
+
225
+ assert.equal(result.failureKind, "reconciliation-drift");
226
+ assert.equal(result.action, "escalate");
227
+ assert.equal(result.exitReason, "reconciliation-drift");
228
+ assert.match(result.remediation, /persistent or repair-failed drift kinds/);
229
+ });
230
+
231
+ // ─── #5701: merge-state drift ────────────────────────────────────────────────
232
+
233
+ function makeGitBase(): string {
234
+ const base = join(tmpdir(), `gsd-adr017-merge-${randomUUID()}`);
235
+ mkdirSync(base, { recursive: true });
236
+ execFileSync("git", ["init", "--initial-branch=main"], { cwd: base, stdio: "ignore" });
237
+ execFileSync("git", ["config", "user.email", "test@test.com"], { cwd: base, stdio: "ignore" });
238
+ execFileSync("git", ["config", "user.name", "Test"], { cwd: base, stdio: "ignore" });
239
+ writeFileSync(join(base, ".gitkeep"), "");
240
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
241
+ execFileSync("git", ["commit", "-m", "initial"], { cwd: base, stdio: "ignore" });
242
+ return base;
243
+ }
244
+
245
+ function rmTreeQuiet(base: string): void {
246
+ try {
247
+ rmSync(base, { recursive: true, force: true });
248
+ } catch {
249
+ /* noop */
250
+ }
251
+ }
252
+
253
+ test("ADR-017 (#5701): merge-state drift detected and repaired end-to-end", async (t) => {
254
+ const base = makeGitBase();
255
+ t.after(() => rmTreeQuiet(base));
256
+
257
+ // Build a clean fast-forward-resolvable merge: feature branch with one file,
258
+ // then start merge --no-commit on main so MERGE_HEAD exists with no conflicts.
259
+ execFileSync("git", ["checkout", "-b", "feature"], { cwd: base, stdio: "ignore" });
260
+ writeFileSync(join(base, "feature.txt"), "feature content");
261
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
262
+ execFileSync("git", ["commit", "-m", "add feature"], { cwd: base, stdio: "ignore" });
263
+ execFileSync("git", ["checkout", "main"], { cwd: base, stdio: "ignore" });
264
+ execFileSync("git", ["merge", "--no-ff", "--no-commit", "feature"], { cwd: base, stdio: "ignore" });
265
+
266
+ assert.ok(existsSync(join(base, ".git", "MERGE_HEAD")), "pre: MERGE_HEAD exists");
267
+
268
+ const result = await reconcileBeforeDispatch(base, {
269
+ invalidateStateCache: () => {},
270
+ deriveState: async () => makeState(),
271
+ });
272
+
273
+ assert.equal(result.ok, true);
274
+ assert.equal(
275
+ existsSync(join(base, ".git", "MERGE_HEAD")),
276
+ false,
277
+ "post: MERGE_HEAD cleared after reconciliation",
278
+ );
279
+ const mergeRepaired = result.repaired.find((d) => d.kind === "unmerged-merge-state");
280
+ assert.ok(mergeRepaired, "repaired list should include the merge-state drift record");
281
+ if (mergeRepaired?.kind === "unmerged-merge-state") {
282
+ assert.equal(mergeRepaired.basePath, base);
283
+ }
284
+ });
285
+
286
+ test("ADR-017 (#5701): merge-state drift is detected in linked worktrees", async (t) => {
287
+ const base = makeGitBase();
288
+ const worktree = join(tmpdir(), `gsd-adr017-worktree-${randomUUID()}`);
289
+ t.after(() => {
290
+ rmTreeQuiet(worktree);
291
+ rmTreeQuiet(base);
292
+ });
293
+
294
+ execFileSync("git", ["checkout", "-b", "feature"], { cwd: base, stdio: "ignore" });
295
+ writeFileSync(join(base, "feature.txt"), "feature content");
296
+ execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
297
+ execFileSync("git", ["commit", "-m", "add feature"], { cwd: base, stdio: "ignore" });
298
+ execFileSync("git", ["checkout", "main"], { cwd: base, stdio: "ignore" });
299
+ execFileSync("git", ["worktree", "add", "-b", "wt-main", worktree, "main"], {
300
+ cwd: base,
301
+ stdio: "ignore",
302
+ });
303
+ execFileSync("git", ["merge", "--no-ff", "--no-commit", "feature"], {
304
+ cwd: worktree,
305
+ stdio: "ignore",
306
+ });
307
+
308
+ const mergeHeadPath = execFileSync("git", ["rev-parse", "--git-path", "MERGE_HEAD"], {
309
+ cwd: worktree,
310
+ encoding: "utf-8",
311
+ }).trim();
312
+ assert.ok(existsSync(mergeHeadPath), "pre: MERGE_HEAD exists in resolved worktree gitdir");
313
+ assert.equal(existsSync(join(worktree, ".git", "MERGE_HEAD")), false);
314
+
315
+ const result = await reconcileBeforeDispatch(worktree, {
316
+ invalidateStateCache: () => {},
317
+ deriveState: async () => makeState(),
318
+ });
319
+
320
+ assert.equal(result.ok, true);
321
+ assert.equal(existsSync(mergeHeadPath), false, "post: MERGE_HEAD cleared after reconciliation");
322
+ assert.ok(
323
+ result.repaired.some((d) => d.kind === "unmerged-merge-state"),
324
+ "repaired list should include the worktree merge-state drift record",
325
+ );
326
+ });
327
+
328
+ test("ADR-017 (#5701): no merge state → detector returns no drift", async (t) => {
329
+ const base = makeGitBase();
330
+ t.after(() => rmTreeQuiet(base));
331
+
332
+ const result = await reconcileBeforeDispatch(base, {
333
+ invalidateStateCache: () => {},
334
+ deriveState: async () => makeState(),
335
+ });
336
+
337
+ assert.equal(result.ok, true);
338
+ assert.equal(
339
+ result.repaired.some((d) => d.kind === "unmerged-merge-state"),
340
+ false,
341
+ "no merge drift should be reported when the repo is clean",
342
+ );
343
+ });
344
+
345
+ // ─── #5702: stale-render drift ───────────────────────────────────────────────
346
+
347
+ function clearRendererCaches(): void {
348
+ clearParseCache();
349
+ clearPathCache();
350
+ invalidateStateCache();
351
+ }
352
+
353
+ function makeStalePlanContent(sliceId: string, tasks: Array<{ id: string; title: string; done: boolean }>): string {
354
+ const lines: string[] = [];
355
+ lines.push(`# ${sliceId}: Test Slice`);
356
+ lines.push("");
357
+ lines.push("**Goal:** Test slice goal");
358
+ lines.push("**Demo:** Test demo");
359
+ lines.push("");
360
+ lines.push("## Must-Haves");
361
+ lines.push("");
362
+ lines.push("- Everything works");
363
+ lines.push("");
364
+ lines.push("## Tasks");
365
+ lines.push("");
366
+ for (const t of tasks) {
367
+ const checkbox = t.done ? "[x]" : "[ ]";
368
+ lines.push(`- ${checkbox} **${t.id}: ${t.title}** \`est:1h\``);
369
+ }
370
+ lines.push("");
371
+ return lines.join("\n");
372
+ }
373
+
374
+ function makeStaleRoadmapContent(slices: Array<{ id: string; title: string; done: boolean }>): string {
375
+ const lines: string[] = [];
376
+ lines.push("# M001 Roadmap");
377
+ lines.push("");
378
+ lines.push("**Vision:** Test milestone");
379
+ lines.push("");
380
+ lines.push("## Slices");
381
+ lines.push("");
382
+ for (const s of slices) {
383
+ const checkbox = s.done ? "[x]" : "[ ]";
384
+ lines.push(`- ${checkbox} **${s.id}: ${s.title}** \`risk:medium\` \`depends:[]\``);
385
+ }
386
+ lines.push("");
387
+ return lines.join("\n");
388
+ }
389
+
390
+ test("ADR-017 (#5702): stale-render drift detected and repaired end-to-end", async (t) => {
391
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-render-"));
392
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
393
+ mkdirSync(join(sliceDir, "tasks"), { recursive: true });
394
+ t.after(() => {
395
+ try { closeDatabase(); } catch { /* noop */ }
396
+ rmTreeQuiet(base);
397
+ });
398
+
399
+ openDatabase(join(base, ".gsd", "gsd.db"));
400
+ clearRendererCaches();
401
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
402
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending" });
403
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "First task", status: "done" });
404
+ insertTask({ id: "T02", sliceId: "S01", milestoneId: "M001", title: "Second task", status: "done" });
405
+
406
+ // Plan with both tasks unchecked — DB says done, file disagrees.
407
+ const planPath = join(sliceDir, "S01-PLAN.md");
408
+ writeFileSync(planPath, makeStalePlanContent("S01", [
409
+ { id: "T01", title: "First task", done: false },
410
+ { id: "T02", title: "Second task", done: false },
411
+ ]));
412
+ clearRendererCaches();
413
+
414
+ const result = await reconcileBeforeDispatch(base, {
415
+ invalidateStateCache: () => {},
416
+ deriveState: async () => makeState(),
417
+ });
418
+
419
+ assert.equal(result.ok, true);
420
+ const renderRepaired = result.repaired.find((d) => d.kind === "stale-render");
421
+ assert.ok(renderRepaired, "repaired list should include the stale-render drift");
422
+
423
+ const repairedContent = readFileSync(planPath, "utf-8");
424
+ assert.match(repairedContent, /\[x\][^\n]*T01:/, "T01 checkbox should be checked after repair");
425
+ assert.match(repairedContent, /\[x\][^\n]*T02:/, "T02 checkbox should be checked after repair");
426
+ });
427
+
428
+ test("ADR-017 (#5702): stale-render detector reason strings match repair contract", (t) => {
429
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-render-reasons-"));
430
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
431
+ mkdirSync(join(sliceDir, "tasks"), { recursive: true });
432
+ t.after(() => {
433
+ try { closeDatabase(); } catch { /* noop */ }
434
+ rmTreeQuiet(base);
435
+ });
436
+
437
+ openDatabase(join(base, ".gsd", "gsd.db"));
438
+ clearRendererCaches();
439
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
440
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "complete" });
441
+ insertTask({
442
+ id: "T01",
443
+ sliceId: "S01",
444
+ milestoneId: "M001",
445
+ title: "First task",
446
+ status: "done",
447
+ fullSummaryMd: "# T01 Summary\n",
448
+ });
449
+ setSliceSummaryMd("M001", "S01", "# S01 Summary\n", "# S01 UAT\n");
450
+
451
+ writeFileSync(
452
+ join(base, ".gsd", "milestones", "M001", "M001-ROADMAP.md"),
453
+ makeStaleRoadmapContent([{ id: "S01", title: "Slice", done: false }]),
454
+ );
455
+ writeFileSync(
456
+ join(sliceDir, "S01-PLAN.md"),
457
+ makeStalePlanContent("S01", [{ id: "T01", title: "First task", done: false }]),
458
+ );
459
+ clearRendererCaches();
460
+
461
+ const reasons = detectStaleRenders(base).map((entry) => entry.reason).sort();
462
+
463
+ assert.deepEqual(reasons, [
464
+ "S01 is closed in DB but unchecked in roadmap",
465
+ "S01 is complete with UAT in DB but UAT.md missing on disk",
466
+ "S01 is complete with summary in DB but SUMMARY.md missing on disk",
467
+ "T01 is complete with summary in DB but SUMMARY.md missing on disk",
468
+ "T01 is done in DB but unchecked in plan",
469
+ ].sort());
470
+ });
471
+
472
+ // ─── #5703: stale-worker drift ───────────────────────────────────────────────
473
+
474
+ const DEAD_PID = 999_999_999; // far above any realistic system PID; process.kill(pid, 0) → ESRCH
475
+
476
+ function writeFakeSessionLock(base: string, pid: number): string {
477
+ const gsdDir = join(base, ".gsd");
478
+ mkdirSync(gsdDir, { recursive: true });
479
+ const lockFile = join(gsdDir, "auto.lock");
480
+ // Mirror SessionLockData minimum shape
481
+ writeFileSync(
482
+ lockFile,
483
+ JSON.stringify({
484
+ pid,
485
+ startedAt: new Date().toISOString(),
486
+ unitType: "starting",
487
+ unitId: "bootstrap",
488
+ }),
489
+ );
490
+ // Also create the proper-lockfile directory artifact at <gsdDir>.lock
491
+ mkdirSync(`${gsdDir}.lock`, { recursive: true });
492
+ return lockFile;
493
+ }
494
+
495
+ test("ADR-017 (#5703): stale-worker drift detected and orphaned lock cleared", async (t) => {
496
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-worker-"));
497
+ t.after(() => rmSync(base, { recursive: true, force: true }));
498
+
499
+ const lockFile = writeFakeSessionLock(base, DEAD_PID);
500
+ assert.ok(existsSync(lockFile), "pre: lock file written");
501
+
502
+ const result = await reconcileBeforeDispatch(base, {
503
+ invalidateStateCache: () => {},
504
+ deriveState: async () => makeState(),
505
+ });
506
+
507
+ assert.equal(result.ok, true);
508
+ assert.equal(existsSync(lockFile), false, "post: orphaned lock file removed");
509
+ const workerRepaired = result.repaired.find((d) => d.kind === "stale-worker");
510
+ assert.ok(workerRepaired, "repaired list should include the stale-worker drift");
511
+ if (workerRepaired?.kind === "stale-worker") {
512
+ assert.equal(workerRepaired.pid, DEAD_PID);
513
+ }
514
+ });
515
+
516
+ test("ADR-017 (#5703): live worker lock is not cleared", async (t) => {
517
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-worker-live-"));
518
+ t.after(() => rmSync(base, { recursive: true, force: true }));
519
+
520
+ // PID 1 (init/launchd): process.kill(1, 0) returns EPERM as non-root, which
521
+ // isPidAlive correctly treats as alive. process.pid would be rejected by the
522
+ // self-PID guard in isPidAlive (treated as not alive).
523
+ const ALIVE_PID = 1;
524
+ const lockFile = writeFakeSessionLock(base, ALIVE_PID);
525
+ assert.ok(existsSync(lockFile), "pre: lock file written");
526
+
527
+ const result = await reconcileBeforeDispatch(base, {
528
+ invalidateStateCache: () => {},
529
+ deriveState: async () => makeState(),
530
+ });
531
+
532
+ assert.equal(result.ok, true);
533
+ assert.equal(
534
+ existsSync(lockFile),
535
+ true,
536
+ "live lock must NOT be cleared (would steal the lock from a running session)",
537
+ );
538
+ assert.equal(
539
+ result.repaired.some((d) => d.kind === "stale-worker"),
540
+ false,
541
+ "no stale-worker drift should be reported when the lock owner is alive",
542
+ );
543
+ });
544
+
545
+ // ─── #5704: unregistered-milestone drift ────────────────────────────────────
546
+
547
+ test("ADR-017 (#5704): unregistered-milestone drift fails closed without importing markdown", async (t) => {
548
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-projmd-"));
549
+ const milestoneDir = join(base, ".gsd", "milestones", "M042");
550
+ mkdirSync(milestoneDir, { recursive: true });
551
+ // Roadmap with one slice — meaningful content, will be picked up by importer
552
+ writeFileSync(
553
+ join(milestoneDir, "M042-ROADMAP.md"),
554
+ [
555
+ "# M042: Test Milestone",
556
+ "",
557
+ "**Vision:** Verify unregistered-milestone drift",
558
+ "",
559
+ "## Slices",
560
+ "",
561
+ "- [ ] **S01: Foundation** `risk:medium` `depends:[]`",
562
+ "",
563
+ ].join("\n"),
564
+ );
565
+ t.after(() => {
566
+ try { closeDatabase(); } catch { /* noop */ }
567
+ rmSync(base, { recursive: true, force: true });
568
+ });
569
+
570
+ openDatabase(join(base, ".gsd", "gsd.db"));
571
+ // Pre-condition: filesystem has the milestone, DB does NOT.
572
+ assert.equal(getMilestone("M042"), null, "pre: DB has no row for M042");
573
+
574
+ await assert.rejects(
575
+ reconcileBeforeDispatch(base, {
576
+ invalidateStateCache: () => {},
577
+ deriveState: async () => makeState(),
578
+ }),
579
+ (err: unknown) => {
580
+ assert.ok(err instanceof ReconciliationFailedError);
581
+ assert.match(String(err.message), /unregistered-milestone/);
582
+ assert.equal(err.failures[0]?.drift.kind, "unregistered-milestone");
583
+ assert.match(String(err.failures[0]?.cause), /M042/);
584
+ assert.match(String(err.failures[0]?.cause), /markdown projection/);
585
+ assert.match(String(err.failures[0]?.cause), /recovery\/migration/);
586
+ return true;
587
+ },
588
+ );
589
+ assert.equal(getMilestone("M042"), null, "post: DB still has no row for M042");
590
+ });
591
+
592
+ test("ADR-017 (#5704): registered milestone (DB row present) → no drift", async (t) => {
593
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-projmd-clean-"));
594
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
595
+ mkdirSync(milestoneDir, { recursive: true });
596
+ writeFileSync(
597
+ join(milestoneDir, "M001-ROADMAP.md"),
598
+ [
599
+ "# M001: Test",
600
+ "",
601
+ "**Vision:** Already-registered milestone",
602
+ "",
603
+ "## Slices",
604
+ "",
605
+ "- [ ] **S01: Slice** `risk:low` `depends:[]`",
606
+ "",
607
+ ].join("\n"),
608
+ );
609
+ t.after(() => {
610
+ try { closeDatabase(); } catch { /* noop */ }
611
+ rmSync(base, { recursive: true, force: true });
612
+ });
613
+
614
+ openDatabase(join(base, ".gsd", "gsd.db"));
615
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
616
+
617
+ const result = await reconcileBeforeDispatch(base, {
618
+ invalidateStateCache: () => {},
619
+ deriveState: async () => makeState(),
620
+ });
621
+
622
+ assert.equal(result.ok, true);
623
+ assert.equal(
624
+ result.repaired.some((d) => d.kind === "unregistered-milestone"),
625
+ false,
626
+ "no drift should be reported when the milestone is already in the DB",
627
+ );
628
+ });
629
+
630
+ // ─── #5705: roadmap-divergence drift ─────────────────────────────────────────
631
+
632
+ test("ADR-017 (#5705): roadmap-divergence re-renders projection without syncing depends into DB", async (t) => {
633
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-"));
634
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
635
+ const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
636
+ mkdirSync(milestoneDir, { recursive: true });
637
+ // ROADMAP.md declares S02 depends on [S01]
638
+ writeFileSync(
639
+ roadmapPath,
640
+ [
641
+ "# M001: Test",
642
+ "",
643
+ "**Vision:** Verify roadmap-divergence drift",
644
+ "",
645
+ "## Slices",
646
+ "",
647
+ "- [ ] **S01: Foundation** `risk:medium` `depends:[]`",
648
+ "- [ ] **S02: Feature** `risk:medium` `depends:[S01]`",
649
+ "",
650
+ ].join("\n"),
651
+ );
652
+ t.after(() => {
653
+ try { closeDatabase(); } catch { /* noop */ }
654
+ rmSync(base, { recursive: true, force: true });
655
+ });
656
+
657
+ openDatabase(join(base, ".gsd", "gsd.db"));
658
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
659
+ // Seed DB with S02 depending on [] — diverges from ROADMAP.md
660
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "medium", depends: [], demo: "", sequence: 1 });
661
+ insertSlice({ id: "S02", milestoneId: "M001", title: "Feature", status: "pending", risk: "medium", depends: [], demo: "", sequence: 2 });
662
+
663
+ assert.deepEqual(getSlice("M001", "S02")?.depends, [], "pre: DB has S02.depends = []");
664
+
665
+ const result = await reconcileBeforeDispatch(base, {
666
+ invalidateStateCache: () => {},
667
+ deriveState: async () => makeState(),
668
+ });
669
+
670
+ assert.equal(result.ok, true);
671
+ assert.deepEqual(getSlice("M001", "S02")?.depends, [], "post: DB depends remains authoritative");
672
+ assert.match(
673
+ readFileSync(roadmapPath, "utf-8"),
674
+ /- \[ \] \*\*S02: Feature\*\* `risk:medium` `depends:\[\]`/,
675
+ "post: ROADMAP projection is regenerated from DB depends",
676
+ );
677
+ const roadmapRepaired = result.repaired.find((d) => d.kind === "roadmap-divergence");
678
+ assert.ok(roadmapRepaired, "repaired list should include the roadmap-divergence drift");
679
+ if (roadmapRepaired?.kind === "roadmap-divergence") {
680
+ assert.equal(roadmapRepaired.milestoneId, "M001");
681
+ }
682
+ });
683
+
684
+ test("ADR-017 (#5705): ROADMAP-only slice is removed from projection and not inserted into DB", async (t) => {
685
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-newslice-"));
686
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
687
+ const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
688
+ mkdirSync(milestoneDir, { recursive: true });
689
+ // ROADMAP.md declares S01 and S02; DB will only have S01.
690
+ writeFileSync(
691
+ roadmapPath,
692
+ [
693
+ "# M001: Test",
694
+ "",
695
+ "**Vision:** Verify new-slice insertion via roadmap-divergence repair",
696
+ "",
697
+ "## Slices",
698
+ "",
699
+ "- [ ] **S01: Foundation** `risk:medium` `depends:[]`",
700
+ "- [ ] **S02: Feature** `risk:medium` `depends:[S01]`",
701
+ "",
702
+ ].join("\n"),
703
+ );
704
+ t.after(() => {
705
+ try { closeDatabase(); } catch { /* noop */ }
706
+ rmSync(base, { recursive: true, force: true });
707
+ });
708
+
709
+ openDatabase(join(base, ".gsd", "gsd.db"));
710
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
711
+ // Only insert S01 — S02 is intentionally absent from the DB.
712
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "medium", depends: [], demo: "", sequence: 1 });
713
+
714
+ assert.equal(getSlice("M001", "S02"), null, "pre: S02 has no DB row");
715
+
716
+ const result = await reconcileBeforeDispatch(base, {
717
+ invalidateStateCache: () => {},
718
+ deriveState: async () => makeState(),
719
+ });
720
+
721
+ assert.equal(result.ok, true);
722
+ assert.equal(getSlice("M001", "S02"), null, "post: S02 still has no DB row");
723
+ const rendered = readFileSync(roadmapPath, "utf-8");
724
+ assert.match(rendered, /- \[ \] \*\*S01: Foundation\*\*/);
725
+ assert.doesNotMatch(rendered, /S02: Feature/, "post: ROADMAP-only S02 removed from projection");
726
+ const roadmapRepaired = result.repaired.find((d) => d.kind === "roadmap-divergence");
727
+ assert.ok(roadmapRepaired, "repaired list should include the roadmap-divergence drift");
728
+ if (roadmapRepaired?.kind === "roadmap-divergence") {
729
+ assert.equal(roadmapRepaired.milestoneId, "M001");
730
+ }
731
+ });
732
+
733
+ test("ADR-017 (#5705): ROADMAP sequence drift re-renders from DB order without mutating DB", async (t) => {
734
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-sequence-"));
735
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
736
+ const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
737
+ mkdirSync(milestoneDir, { recursive: true });
738
+ writeFileSync(
739
+ roadmapPath,
740
+ [
741
+ "# M001: Test",
742
+ "",
743
+ "**Vision:** Verify sequence drift",
744
+ "",
745
+ "## Slices",
746
+ "",
747
+ "- [ ] **S02: Feature** `risk:medium` `depends:[]`",
748
+ "- [ ] **S01: Foundation** `risk:medium` `depends:[]`",
749
+ "",
750
+ ].join("\n"),
751
+ );
752
+ t.after(() => {
753
+ try { closeDatabase(); } catch { /* noop */ }
754
+ rmSync(base, { recursive: true, force: true });
755
+ });
756
+
757
+ openDatabase(join(base, ".gsd", "gsd.db"));
758
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
759
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "medium", depends: [], demo: "", sequence: 1 });
760
+ insertSlice({ id: "S02", milestoneId: "M001", title: "Feature", status: "pending", risk: "medium", depends: [], demo: "", sequence: 2 });
761
+
762
+ const result = await reconcileBeforeDispatch(base, {
763
+ invalidateStateCache: () => {},
764
+ deriveState: async () => makeState(),
765
+ });
766
+
767
+ assert.equal(result.ok, true);
768
+ assert.equal(getSlice("M001", "S01")?.sequence, 1, "post: S01 DB sequence remains authoritative");
769
+ assert.equal(getSlice("M001", "S02")?.sequence, 2, "post: S02 DB sequence remains authoritative");
770
+ const rendered = readFileSync(roadmapPath, "utf-8");
771
+ assert.ok(
772
+ rendered.indexOf("S01: Foundation") < rendered.indexOf("S02: Feature"),
773
+ "post: ROADMAP projection follows DB sequence",
774
+ );
775
+ assert.ok(result.repaired.some((d) => d.kind === "roadmap-divergence"));
776
+ });
777
+
778
+ test("ADR-017 (#5705): ROADMAP checkbox drift re-renders from DB status without mutating DB", async (t) => {
779
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-checkbox-"));
780
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
781
+ const roadmapPath = join(milestoneDir, "M001-ROADMAP.md");
782
+ mkdirSync(milestoneDir, { recursive: true });
783
+ writeFileSync(
784
+ roadmapPath,
785
+ [
786
+ "# M001: Test",
787
+ "",
788
+ "**Vision:** Verify checkbox drift",
789
+ "",
790
+ "## Slices",
791
+ "",
792
+ "- [x] **S01: Foundation** `risk:medium` `depends:[]`",
793
+ "",
794
+ ].join("\n"),
795
+ );
796
+ t.after(() => {
797
+ try { closeDatabase(); } catch { /* noop */ }
798
+ rmSync(base, { recursive: true, force: true });
799
+ });
800
+
801
+ openDatabase(join(base, ".gsd", "gsd.db"));
802
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
803
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "medium", depends: [], demo: "", sequence: 1 });
804
+
805
+ const result = await reconcileBeforeDispatch(base, {
806
+ invalidateStateCache: () => {},
807
+ deriveState: async () => makeState(),
808
+ });
809
+
810
+ assert.equal(result.ok, true);
811
+ assert.equal(getSlice("M001", "S01")?.status, "pending", "post: DB status remains authoritative");
812
+ assert.match(
813
+ readFileSync(roadmapPath, "utf-8"),
814
+ /- \[ \] \*\*S01: Foundation\*\*/,
815
+ "post: ROADMAP checkbox reflects DB status",
816
+ );
817
+ assert.ok(result.repaired.some((d) => d.kind === "roadmap-divergence"));
818
+ });
819
+
820
+ test("ADR-017 (#5705): in-sync ROADMAP and DB → no roadmap-divergence drift", async (t) => {
821
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-clean-"));
822
+ const milestoneDir = join(base, ".gsd", "milestones", "M001");
823
+ mkdirSync(milestoneDir, { recursive: true });
824
+ writeFileSync(
825
+ join(milestoneDir, "M001-ROADMAP.md"),
826
+ [
827
+ "# M001: Test",
828
+ "",
829
+ "**Vision:** Already in sync",
830
+ "",
831
+ "## Slices",
832
+ "",
833
+ "- [ ] **S01: Foundation** `risk:low` `depends:[]`",
834
+ "",
835
+ ].join("\n"),
836
+ );
837
+ t.after(() => {
838
+ try { closeDatabase(); } catch { /* noop */ }
839
+ rmSync(base, { recursive: true, force: true });
840
+ });
841
+
842
+ openDatabase(join(base, ".gsd", "gsd.db"));
843
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
844
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Foundation", status: "pending", risk: "low", depends: [], demo: "", sequence: 1 });
845
+
846
+ const result = await reconcileBeforeDispatch(base, {
847
+ invalidateStateCache: () => {},
848
+ deriveState: async () => makeState(),
849
+ });
850
+
851
+ assert.equal(result.ok, true);
852
+ assert.equal(
853
+ result.repaired.some((d) => d.kind === "roadmap-divergence"),
854
+ false,
855
+ "no roadmap-divergence drift should be reported when DB matches markdown",
856
+ );
857
+ });
858
+
859
+ // ─── #5706: missing-completion-timestamp drift ──────────────────────────────
860
+
861
+ test("ADR-017 (#5706): task with SUMMARY but null completed_at → backfilled", async (t) => {
862
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-completion-task-"));
863
+ const tasksDir = join(base, ".gsd", "milestones", "M001", "slices", "S01", "tasks");
864
+ mkdirSync(tasksDir, { recursive: true });
865
+ t.after(() => {
866
+ try { closeDatabase(); } catch { /* noop */ }
867
+ rmSync(base, { recursive: true, force: true });
868
+ });
869
+
870
+ openDatabase(join(base, ".gsd", "gsd.db"));
871
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
872
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending", risk: "low", depends: [], demo: "", sequence: 1 });
873
+ insertTask({ id: "T01", sliceId: "S01", milestoneId: "M001", title: "Task", status: "pending" });
874
+
875
+ // Move T01 to complete WITHOUT setting completed_at (simulating drift after
876
+ // an external recovery path or a partial state migration).
877
+ updateTaskStatus("M001", "S01", "T01", "complete", undefined);
878
+ // SUMMARY.md attests to completion on disk.
879
+ const summaryPath = join(tasksDir, "T01-SUMMARY.md");
880
+ writeFileSync(summaryPath, "# T01 Summary\n");
881
+ const summaryMtimeMs = statSync(summaryPath).mtime.getTime();
882
+
883
+ const taskBefore = getSliceTasks("M001", "S01").find((t) => t.id === "T01");
884
+ assert.equal(taskBefore?.status, "complete");
885
+ assert.equal(taskBefore?.completed_at, null, "pre: completed_at is null");
886
+
887
+ const result = await reconcileBeforeDispatch(base, {
888
+ invalidateStateCache: () => {},
889
+ deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Test" } }),
890
+ });
891
+
892
+ assert.equal(result.ok, true);
893
+ const taskAfter = getSliceTasks("M001", "S01").find((t) => t.id === "T01");
894
+ assert.ok(taskAfter?.completed_at, "post: completed_at populated");
895
+ const completedAtMs = Date.parse(taskAfter?.completed_at ?? "");
896
+ assert.ok(Number.isFinite(completedAtMs), "post: completed_at is parseable ISO string");
897
+ assert.equal(completedAtMs, summaryMtimeMs, "post: completed_at derived from SUMMARY mtime");
898
+ const drift = result.repaired.find((d) => d.kind === "missing-completion-timestamp");
899
+ assert.ok(drift, "drift recorded");
900
+ if (drift?.kind === "missing-completion-timestamp") {
901
+ assert.equal(drift.entity, "task");
902
+ assert.deepEqual(drift.ids, ["M001/S01/T01"]);
903
+ }
904
+ });
905
+
906
+ test("ADR-017 (#5706): repair is idempotent — re-running preserves the timestamp", async (t) => {
907
+ const base = mkdtempSync(join(tmpdir(), "gsd-adr017-completion-idempotent-"));
908
+ const sliceDir = join(base, ".gsd", "milestones", "M001", "slices", "S01");
909
+ mkdirSync(sliceDir, { recursive: true });
910
+ t.after(() => {
911
+ try { closeDatabase(); } catch { /* noop */ }
912
+ rmSync(base, { recursive: true, force: true });
913
+ });
914
+
915
+ openDatabase(join(base, ".gsd", "gsd.db"));
916
+ insertMilestone({ id: "M001", title: "Test", status: "active" });
917
+ insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "pending", risk: "low", depends: [], demo: "", sequence: 1 });
918
+ updateSliceStatus("M001", "S01", "complete", undefined);
919
+ const summaryPath = join(sliceDir, "S01-SUMMARY.md");
920
+ writeFileSync(summaryPath, "# S01 Summary\n");
921
+ const summaryMtimeMs = statSync(summaryPath).mtime.getTime();
922
+
923
+ const firstResult = await reconcileBeforeDispatch(base, {
924
+ invalidateStateCache: () => {},
925
+ deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Test" } }),
926
+ });
927
+ assert.equal(firstResult.ok, true);
928
+ const tsAfterFirst = getSlice("M001", "S01")?.completed_at;
929
+ assert.ok(tsAfterFirst, "first pass: completed_at populated");
930
+ const completedAtMs = Date.parse(tsAfterFirst ?? "");
931
+ assert.ok(Number.isFinite(completedAtMs), "first pass: completed_at is parseable ISO string");
932
+ assert.equal(completedAtMs, summaryMtimeMs, "first pass: completed_at derived from SUMMARY mtime");
933
+
934
+ // Second pass — drift is already cleared, no record should appear, and
935
+ // the existing timestamp is untouched.
936
+ const secondResult = await reconcileBeforeDispatch(base, {
937
+ invalidateStateCache: () => {},
938
+ deriveState: async () => makeState({ activeMilestone: { id: "M001", title: "Test" } }),
939
+ });
940
+ assert.equal(secondResult.ok, true);
941
+ assert.equal(
942
+ secondResult.repaired.some((d) => d.kind === "missing-completion-timestamp"),
943
+ false,
944
+ "second pass: no drift detected after first repair",
945
+ );
946
+ assert.equal(getSlice("M001", "S01")?.completed_at, tsAfterFirst, "timestamp unchanged");
947
+ });
948
+
949
+ // ─── #5707: caller closure (reconcileBeforeSpawn) ────────────────────────────
950
+
951
+ test("ADR-017 (#5707): reconcileBeforeSpawn returns ok=true on clean reconciliation", async () => {
952
+ const result = await reconcileBeforeSpawn("/project", {
953
+ invalidateStateCache: () => {},
954
+ deriveState: async () => makeState(),
955
+ registry: [],
956
+ });
957
+ assert.equal(result.ok, true);
958
+ });
959
+
960
+ test("ADR-017 (#5707): reconcileBeforeSpawn surfaces blockers as ok=false", async () => {
961
+ const result = await reconcileBeforeSpawn("/project", {
962
+ invalidateStateCache: () => {},
963
+ deriveState: async () => makeState({ phase: "blocked", blockers: ["lock missing"] }),
964
+ registry: [],
965
+ });
966
+ assert.equal(result.ok, false);
967
+ if (!result.ok) {
968
+ assert.match(result.reason, /lock missing/);
969
+ }
970
+ });
971
+
972
+ test("ADR-017 (#5707): reconcileBeforeSpawn catches ReconciliationFailedError → ok=false", async () => {
973
+ const persistent: DriftRecord = { kind: "stale-sketch-flag", mid: "M001", sid: "S02" };
974
+ const handler: DriftHandler = {
975
+ kind: "stale-sketch-flag",
976
+ detect: () => [persistent],
977
+ repair: () => { /* no-op: drift cannot be cleared, persists past cap=2 */ },
978
+ };
979
+
980
+ const result = await reconcileBeforeSpawn("/project", {
981
+ invalidateStateCache: () => {},
982
+ deriveState: async () => makeState(),
983
+ registry: [handler],
984
+ });
985
+
986
+ assert.equal(result.ok, false);
987
+ if (!result.ok) {
988
+ assert.match(result.reason, /stale-sketch-flag/);
989
+ }
990
+ });
991
+
992
+ test("ADR-017 (#5707): reconcileBeforeSpawn reports repaired drift in ok=true reason", async () => {
993
+ let detectCalls = 0;
994
+ const handler: DriftHandler = {
995
+ kind: "stale-sketch-flag",
996
+ detect: () => {
997
+ detectCalls++;
998
+ return detectCalls === 1
999
+ ? [{ kind: "stale-sketch-flag", mid: "M001", sid: "S02" }]
1000
+ : [];
1001
+ },
1002
+ repair: () => { /* repair "succeeds" — second detect returns empty */ },
1003
+ };
1004
+
1005
+ const result = await reconcileBeforeSpawn("/project", {
1006
+ invalidateStateCache: () => {},
1007
+ deriveState: async () => makeState(),
1008
+ registry: [handler],
1009
+ });
1010
+
1011
+ assert.equal(result.ok, true);
1012
+ if (result.ok) {
1013
+ assert.match(result.reason ?? "", /stale-sketch-flag/);
1014
+ }
1015
+ });
1016
+
1017
+ // ─── Lifecycle and classification ────────────────────────────────────────────
1018
+
1019
+ test("ADR-017 (#5700): cascading drift triggers second pass within cap", async () => {
1020
+ // First pass detects drift A; repair "fixes" it. Second pass detects drift B
1021
+ // (cascading); repair fixes it. Third call would see no drift. Cap=2 means
1022
+ // we have exactly two repair passes available.
1023
+ const detectedSequence: DriftRecord[][] = [
1024
+ [{ kind: "stale-sketch-flag", mid: "M001", sid: "S02" }],
1025
+ [{ kind: "stale-sketch-flag", mid: "M001", sid: "S03" }],
1026
+ [],
1027
+ ];
1028
+ let detectCallIdx = 0;
1029
+ const repaired: DriftRecord[] = [];
1030
+
1031
+ const handler: DriftHandler = {
1032
+ kind: "stale-sketch-flag",
1033
+ detect: () => detectedSequence[detectCallIdx++] ?? [],
1034
+ repair: (record) => {
1035
+ repaired.push(record);
1036
+ },
1037
+ };
1038
+
1039
+ const result = await reconcileBeforeDispatch("/project", {
1040
+ invalidateStateCache: () => {},
1041
+ deriveState: async () => makeState(),
1042
+ registry: [handler],
1043
+ });
1044
+
1045
+ assert.equal(result.ok, true);
1046
+ assert.equal(result.repaired.length, 2, "both passes' repairs collected");
1047
+ assert.equal(repaired.length, 2);
1048
+ });