gsd-pi 2.43.0 → 2.44.0-dev.0b97ffd

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 (693) hide show
  1. package/README.md +30 -12
  2. package/dist/cli.js +13 -1
  3. package/dist/help-text.js +24 -0
  4. package/dist/resources/extensions/bg-shell/overlay.js +3 -0
  5. package/dist/resources/extensions/github-sync/sync.js +2 -1
  6. package/dist/resources/extensions/gsd/auto/loop.js +0 -2
  7. package/dist/resources/extensions/gsd/auto/phases.js +7 -12
  8. package/dist/resources/extensions/gsd/auto-dashboard.js +19 -18
  9. package/dist/resources/extensions/gsd/auto-direct-dispatch.js +34 -19
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +36 -21
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +128 -14
  12. package/dist/resources/extensions/gsd/auto-prompts.js +202 -92
  13. package/dist/resources/extensions/gsd/auto-recovery.js +83 -135
  14. package/dist/resources/extensions/gsd/auto-start.js +10 -0
  15. package/dist/resources/extensions/gsd/auto-supervisor.js +14 -0
  16. package/dist/resources/extensions/gsd/auto-timeout-recovery.js +4 -7
  17. package/dist/resources/extensions/gsd/auto-verification.js +5 -10
  18. package/dist/resources/extensions/gsd/auto-worktree.js +123 -30
  19. package/dist/resources/extensions/gsd/auto.js +1 -4
  20. package/dist/resources/extensions/gsd/bootstrap/db-tools.js +611 -0
  21. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +28 -3
  22. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +7 -0
  23. package/dist/resources/extensions/gsd/commands/catalog.js +3 -1
  24. package/dist/resources/extensions/gsd/commands/handlers/ops.js +15 -1
  25. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
  26. package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
  27. package/dist/resources/extensions/gsd/commands-maintenance.js +78 -3
  28. package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -31
  29. package/dist/resources/extensions/gsd/db-writer.js +95 -4
  30. package/dist/resources/extensions/gsd/dispatch-guard.js +35 -30
  31. package/dist/resources/extensions/gsd/doctor-checks.js +28 -11
  32. package/dist/resources/extensions/gsd/doctor-environment.js +28 -0
  33. package/dist/resources/extensions/gsd/doctor-types.js +0 -15
  34. package/dist/resources/extensions/gsd/doctor.js +46 -282
  35. package/dist/resources/extensions/gsd/file-watcher.js +5 -1
  36. package/dist/resources/extensions/gsd/files.js +14 -198
  37. package/dist/resources/extensions/gsd/git-service.js +4 -0
  38. package/dist/resources/extensions/gsd/gitignore.js +4 -0
  39. package/dist/resources/extensions/gsd/gsd-db.js +819 -197
  40. package/dist/resources/extensions/gsd/guided-flow.js +18 -8
  41. package/dist/resources/extensions/gsd/markdown-renderer.js +862 -0
  42. package/dist/resources/extensions/gsd/md-importer.js +182 -4
  43. package/dist/resources/extensions/gsd/native-git-bridge.js +10 -1
  44. package/dist/resources/extensions/gsd/parallel-eligibility.js +14 -19
  45. package/dist/resources/extensions/gsd/parallel-orchestrator.js +38 -0
  46. package/dist/resources/extensions/gsd/parsers-legacy.js +239 -0
  47. package/dist/resources/extensions/gsd/preferences-types.js +1 -0
  48. package/dist/resources/extensions/gsd/preferences-validation.js +9 -0
  49. package/dist/resources/extensions/gsd/preferences.js +1 -0
  50. package/dist/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  51. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  52. package/dist/resources/extensions/gsd/prompts/execute-task.md +15 -5
  53. package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  54. package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  55. package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  56. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  57. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  58. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  59. package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  60. package/dist/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  61. package/dist/resources/extensions/gsd/reactive-graph.js +33 -3
  62. package/dist/resources/extensions/gsd/skill-health.js +3 -1
  63. package/dist/resources/extensions/gsd/state.js +484 -30
  64. package/dist/resources/extensions/gsd/tools/complete-milestone.js +128 -0
  65. package/dist/resources/extensions/gsd/tools/complete-slice.js +244 -0
  66. package/dist/resources/extensions/gsd/tools/complete-task.js +204 -0
  67. package/dist/resources/extensions/gsd/tools/plan-milestone.js +205 -0
  68. package/dist/resources/extensions/gsd/tools/plan-slice.js +155 -0
  69. package/dist/resources/extensions/gsd/tools/plan-task.js +94 -0
  70. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +152 -0
  71. package/dist/resources/extensions/gsd/tools/replan-slice.js +146 -0
  72. package/dist/resources/extensions/gsd/triage-resolution.js +17 -1
  73. package/dist/resources/extensions/gsd/undo.js +197 -3
  74. package/dist/resources/extensions/gsd/visualizer-data.js +53 -16
  75. package/dist/resources/extensions/gsd/workspace-index.js +63 -39
  76. package/dist/web/standalone/.next/BUILD_ID +1 -1
  77. package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -17
  78. package/dist/web/standalone/.next/build-manifest.json +4 -4
  79. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  80. package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
  81. package/dist/web/standalone/.next/required-server-files.json +4 -4
  82. package/dist/web/standalone/.next/routes-manifest.json +6 -0
  83. package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
  84. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  86. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  87. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  88. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  89. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  90. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  91. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  92. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  93. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  94. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  95. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  96. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
  97. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
  98. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -4
  100. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  102. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  103. package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
  104. package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
  106. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
  107. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
  108. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
  109. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
  110. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
  111. package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
  112. package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
  113. package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
  114. package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
  115. package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
  116. package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
  117. package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
  118. package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
  119. package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
  120. package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
  121. package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
  122. package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
  123. package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
  124. package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
  126. package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
  127. package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
  128. package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  129. package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
  130. package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
  131. package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
  132. package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  133. package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
  134. package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
  135. package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
  136. package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
  137. package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
  138. package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
  139. package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
  140. package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
  141. package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
  142. package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
  143. package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
  144. package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  145. package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
  146. package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
  147. package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
  148. package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
  149. package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
  150. package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
  151. package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
  152. package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
  153. package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
  154. package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
  155. package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
  156. package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
  157. package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
  158. package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
  159. package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
  160. package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
  161. package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
  162. package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
  163. package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
  164. package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
  165. package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -0
  166. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -0
  167. package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -0
  168. package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
  169. package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
  170. package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
  171. package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
  172. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
  173. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
  174. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
  175. package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
  176. package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
  177. package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
  178. package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
  179. package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
  180. package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
  181. package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  182. package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
  183. package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
  184. package/dist/web/standalone/.next/server/app/index.html +1 -1
  185. package/dist/web/standalone/.next/server/app/index.rsc +5 -5
  186. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  187. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
  188. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  189. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -4
  190. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  191. package/dist/web/standalone/.next/server/app/page.js +2 -2
  192. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  193. package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -17
  194. package/dist/web/standalone/.next/server/chunks/229.js +3 -3
  195. package/dist/web/standalone/.next/server/chunks/471.js +3 -3
  196. package/dist/web/standalone/.next/server/functions-config-manifest.json +1 -0
  197. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  198. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  199. package/dist/web/standalone/.next/server/middleware.js +2 -2
  200. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  201. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  202. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  203. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  204. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  205. package/dist/web/standalone/.next/static/alS4hoANx0TK4UVZY27da/_buildManifest.js +1 -0
  206. package/dist/web/standalone/.next/static/chunks/{4024.c195dc1fdd2adbea.js → 4024.0de81b543b28b9fe.js} +2 -2
  207. package/dist/web/standalone/.next/static/chunks/app/_global-error/{page-d07a2c023f1aef1e.js → page-d83ba70a25a85472.js} +1 -1
  208. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-f2a7482d42a5614b.js +1 -0
  209. package/dist/web/standalone/.next/static/chunks/app/api/boot/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  210. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/input/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  211. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/resize/{route-d07a2c023f1aef1e.js → route-d83ba70a25a85472.js} +1 -1
  212. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d83ba70a25a85472.js +1 -0
  213. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d83ba70a25a85472.js +1 -0
  214. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d83ba70a25a85472.js +1 -0
  215. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d83ba70a25a85472.js +1 -0
  216. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d83ba70a25a85472.js +1 -0
  217. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d83ba70a25a85472.js +1 -0
  218. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d83ba70a25a85472.js +1 -0
  219. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d83ba70a25a85472.js +1 -0
  220. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d83ba70a25a85472.js +1 -0
  221. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d83ba70a25a85472.js +1 -0
  222. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d83ba70a25a85472.js +1 -0
  223. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d83ba70a25a85472.js +1 -0
  224. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d83ba70a25a85472.js +1 -0
  225. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d83ba70a25a85472.js +1 -0
  226. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d83ba70a25a85472.js +1 -0
  227. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d83ba70a25a85472.js +1 -0
  228. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d83ba70a25a85472.js +1 -0
  229. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d83ba70a25a85472.js +1 -0
  230. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d83ba70a25a85472.js +1 -0
  231. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d83ba70a25a85472.js +1 -0
  232. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d83ba70a25a85472.js +1 -0
  233. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d83ba70a25a85472.js +1 -0
  234. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d83ba70a25a85472.js +1 -0
  235. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d83ba70a25a85472.js +1 -0
  236. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d83ba70a25a85472.js +1 -0
  237. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d83ba70a25a85472.js +1 -0
  238. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d83ba70a25a85472.js +1 -0
  239. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d83ba70a25a85472.js +1 -0
  240. package/dist/web/standalone/.next/static/chunks/app/api/switch-root/route-d83ba70a25a85472.js +1 -0
  241. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d83ba70a25a85472.js +1 -0
  242. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d83ba70a25a85472.js +1 -0
  243. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d83ba70a25a85472.js +1 -0
  244. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d83ba70a25a85472.js +1 -0
  245. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d83ba70a25a85472.js +1 -0
  246. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d83ba70a25a85472.js +1 -0
  247. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d83ba70a25a85472.js +1 -0
  248. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d83ba70a25a85472.js +1 -0
  249. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +1 -0
  250. package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +1 -0
  251. package/dist/web/standalone/.next/static/chunks/{main-app-2f2ee7b85712c2bd.js → main-app-fdab67f7802d7832.js} +1 -1
  252. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d83ba70a25a85472.js +1 -0
  253. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d83ba70a25a85472.js +1 -0
  254. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +1 -0
  255. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d83ba70a25a85472.js +1 -0
  256. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d83ba70a25a85472.js +1 -0
  257. package/dist/web/standalone/.next/static/chunks/{webpack-fa307370fcf9fb2c.js → webpack-9014b5adb127a98a.js} +1 -1
  258. package/dist/web/standalone/.next/static/css/8a727f372cf53002.css +1 -0
  259. package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
  260. package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
  261. package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
  262. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
  263. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
  264. package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
  265. package/dist/web/standalone/server.js +1 -1
  266. package/package.json +4 -4
  267. package/packages/pi-ai/dist/models.custom.d.ts +173 -0
  268. package/packages/pi-ai/dist/models.custom.d.ts.map +1 -0
  269. package/packages/pi-ai/dist/models.custom.js +170 -0
  270. package/packages/pi-ai/dist/models.custom.js.map +1 -0
  271. package/packages/pi-ai/dist/models.d.ts.map +1 -1
  272. package/packages/pi-ai/dist/models.js +16 -1
  273. package/packages/pi-ai/dist/models.js.map +1 -1
  274. package/packages/pi-ai/dist/models.test.d.ts +2 -0
  275. package/packages/pi-ai/dist/models.test.d.ts.map +1 -0
  276. package/packages/pi-ai/dist/models.test.js +67 -0
  277. package/packages/pi-ai/dist/models.test.js.map +1 -0
  278. package/packages/pi-ai/src/models.custom.ts +172 -0
  279. package/packages/pi-ai/src/models.test.ts +85 -0
  280. package/packages/pi-ai/src/models.ts +17 -1
  281. package/packages/pi-coding-agent/dist/core/agent-session.d.ts +10 -3
  282. package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  283. package/packages/pi-coding-agent/dist/core/agent-session.js +21 -34
  284. package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  285. package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
  286. package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
  287. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts +2 -2
  288. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  289. package/packages/pi-coding-agent/dist/core/compaction/branch-summarization.js.map +1 -1
  290. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts +2 -2
  291. package/packages/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  292. package/packages/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  293. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js +4 -4
  294. package/packages/pi-coding-agent/dist/core/compaction-orchestrator.js.map +1 -1
  295. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  296. package/packages/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  297. package/packages/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  298. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts +6 -0
  299. package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  300. package/packages/pi-coding-agent/dist/core/extensions/loader.js +80 -0
  301. package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  302. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js +63 -0
  303. package/packages/pi-coding-agent/dist/core/extensions/loader.test.js.map +1 -1
  304. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
  305. package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
  306. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +37 -0
  307. package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  308. package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  309. package/packages/pi-coding-agent/dist/core/fallback-resolver.d.ts.map +1 -1
  310. package/packages/pi-coding-agent/dist/core/fallback-resolver.js +2 -3
  311. package/packages/pi-coding-agent/dist/core/fallback-resolver.js.map +1 -1
  312. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js +12 -2
  313. package/packages/pi-coding-agent/dist/core/fallback-resolver.test.js.map +1 -1
  314. package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
  315. package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
  316. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts +38 -0
  317. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.d.ts.map +1 -0
  318. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js +192 -0
  319. package/packages/pi-coding-agent/dist/core/lifecycle-hooks.js.map +1 -0
  320. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts +5 -0
  321. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  322. package/packages/pi-coding-agent/dist/core/lsp/client.js +69 -21
  323. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  324. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts +2 -0
  325. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.d.ts.map +1 -0
  326. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +255 -0
  327. package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -0
  328. package/packages/pi-coding-agent/dist/core/model-registry.d.ts +15 -0
  329. package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
  330. package/packages/pi-coding-agent/dist/core/model-registry.js +40 -3
  331. package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
  332. package/packages/pi-coding-agent/dist/core/package-commands.d.ts +25 -0
  333. package/packages/pi-coding-agent/dist/core/package-commands.d.ts.map +1 -0
  334. package/packages/pi-coding-agent/dist/core/package-commands.js +253 -0
  335. package/packages/pi-coding-agent/dist/core/package-commands.js.map +1 -0
  336. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts +2 -0
  337. package/packages/pi-coding-agent/dist/core/package-commands.test.d.ts.map +1 -0
  338. package/packages/pi-coding-agent/dist/core/package-commands.test.js +225 -0
  339. package/packages/pi-coding-agent/dist/core/package-commands.test.js.map +1 -0
  340. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
  341. package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
  342. package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
  343. package/packages/pi-coding-agent/dist/core/sdk.js +4 -0
  344. package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
  345. package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
  346. package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
  347. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
  348. package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
  349. package/packages/pi-coding-agent/dist/index.d.ts +3 -1
  350. package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
  351. package/packages/pi-coding-agent/dist/index.js +1 -0
  352. package/packages/pi-coding-agent/dist/index.js.map +1 -1
  353. package/packages/pi-coding-agent/dist/main.d.ts.map +1 -1
  354. package/packages/pi-coding-agent/dist/main.js +11 -199
  355. package/packages/pi-coding-agent/dist/main.js.map +1 -1
  356. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts +6 -0
  357. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
  358. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +21 -0
  359. package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
  360. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
  361. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  362. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +8 -15
  363. package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
  364. package/packages/pi-coding-agent/dist/modes/print-mode.d.ts.map +1 -1
  365. package/packages/pi-coding-agent/dist/modes/print-mode.js +45 -34
  366. package/packages/pi-coding-agent/dist/modes/print-mode.js.map +1 -1
  367. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +1 -0
  368. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  369. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +7 -2
  370. package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  371. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  372. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +2 -1
  373. package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  374. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
  375. package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
  376. package/packages/pi-coding-agent/package.json +1 -1
  377. package/packages/pi-coding-agent/src/core/agent-session.ts +26 -37
  378. package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
  379. package/packages/pi-coding-agent/src/core/compaction/branch-summarization.ts +2 -2
  380. package/packages/pi-coding-agent/src/core/compaction/compaction.ts +3 -3
  381. package/packages/pi-coding-agent/src/core/compaction-orchestrator.ts +4 -4
  382. package/packages/pi-coding-agent/src/core/extensions/index.ts +5 -0
  383. package/packages/pi-coding-agent/src/core/extensions/loader.test.ts +96 -0
  384. package/packages/pi-coding-agent/src/core/extensions/loader.ts +89 -0
  385. package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
  386. package/packages/pi-coding-agent/src/core/extensions/types.ts +44 -0
  387. package/packages/pi-coding-agent/src/core/fallback-resolver.test.ts +15 -2
  388. package/packages/pi-coding-agent/src/core/fallback-resolver.ts +2 -3
  389. package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
  390. package/packages/pi-coding-agent/src/core/lifecycle-hooks.ts +274 -0
  391. package/packages/pi-coding-agent/src/core/lsp/client.ts +83 -21
  392. package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +288 -0
  393. package/packages/pi-coding-agent/src/core/model-registry.ts +39 -3
  394. package/packages/pi-coding-agent/src/core/package-commands.test.ts +240 -0
  395. package/packages/pi-coding-agent/src/core/package-commands.ts +310 -0
  396. package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
  397. package/packages/pi-coding-agent/src/core/sdk.ts +4 -0
  398. package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
  399. package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
  400. package/packages/pi-coding-agent/src/index.ts +7 -0
  401. package/packages/pi-coding-agent/src/main.ts +11 -232
  402. package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +20 -0
  403. package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +9 -16
  404. package/packages/pi-coding-agent/src/modes/print-mode.ts +42 -32
  405. package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +8 -2
  406. package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -1
  407. package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
  408. package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
  409. package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  410. package/pkg/dist/modes/interactive/theme/theme.js +8 -15
  411. package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
  412. package/pkg/package.json +1 -1
  413. package/src/resources/extensions/bg-shell/overlay.ts +4 -0
  414. package/src/resources/extensions/github-sync/sync.ts +2 -1
  415. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -8
  416. package/src/resources/extensions/gsd/auto/loop.ts +0 -2
  417. package/src/resources/extensions/gsd/auto/phases.ts +7 -20
  418. package/src/resources/extensions/gsd/auto/types.ts +0 -1
  419. package/src/resources/extensions/gsd/auto-dashboard.ts +20 -16
  420. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +34 -19
  421. package/src/resources/extensions/gsd/auto-dispatch.ts +38 -21
  422. package/src/resources/extensions/gsd/auto-post-unit.ts +150 -15
  423. package/src/resources/extensions/gsd/auto-prompts.ts +186 -103
  424. package/src/resources/extensions/gsd/auto-recovery.ts +77 -142
  425. package/src/resources/extensions/gsd/auto-start.ts +14 -0
  426. package/src/resources/extensions/gsd/auto-supervisor.ts +14 -0
  427. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +6 -7
  428. package/src/resources/extensions/gsd/auto-verification.ts +4 -9
  429. package/src/resources/extensions/gsd/auto-worktree.ts +126 -30
  430. package/src/resources/extensions/gsd/auto.ts +0 -9
  431. package/src/resources/extensions/gsd/bootstrap/db-tools.ts +675 -4
  432. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +31 -3
  433. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +7 -0
  434. package/src/resources/extensions/gsd/commands/catalog.ts +3 -1
  435. package/src/resources/extensions/gsd/commands/handlers/ops.ts +15 -1
  436. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
  437. package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
  438. package/src/resources/extensions/gsd/commands-maintenance.ts +86 -3
  439. package/src/resources/extensions/gsd/dashboard-overlay.ts +17 -13
  440. package/src/resources/extensions/gsd/db-writer.ts +105 -4
  441. package/src/resources/extensions/gsd/dispatch-guard.ts +32 -30
  442. package/src/resources/extensions/gsd/doctor-checks.ts +25 -11
  443. package/src/resources/extensions/gsd/doctor-environment.ts +31 -0
  444. package/src/resources/extensions/gsd/doctor-types.ts +0 -23
  445. package/src/resources/extensions/gsd/doctor.ts +45 -295
  446. package/src/resources/extensions/gsd/file-watcher.ts +4 -1
  447. package/src/resources/extensions/gsd/files.ts +16 -220
  448. package/src/resources/extensions/gsd/git-service.ts +4 -0
  449. package/src/resources/extensions/gsd/gitignore.ts +4 -0
  450. package/src/resources/extensions/gsd/gsd-db.ts +1157 -370
  451. package/src/resources/extensions/gsd/guided-flow.ts +20 -8
  452. package/src/resources/extensions/gsd/markdown-renderer.ts +1098 -0
  453. package/src/resources/extensions/gsd/md-importer.ts +211 -2
  454. package/src/resources/extensions/gsd/native-git-bridge.ts +12 -1
  455. package/src/resources/extensions/gsd/parallel-eligibility.ts +14 -18
  456. package/src/resources/extensions/gsd/parallel-orchestrator.ts +43 -0
  457. package/src/resources/extensions/gsd/parsers-legacy.ts +271 -0
  458. package/src/resources/extensions/gsd/preferences-types.ts +3 -0
  459. package/src/resources/extensions/gsd/preferences-validation.ts +9 -0
  460. package/src/resources/extensions/gsd/preferences.ts +1 -0
  461. package/src/resources/extensions/gsd/prompts/complete-slice.md +22 -9
  462. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  463. package/src/resources/extensions/gsd/prompts/execute-task.md +15 -5
  464. package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
  465. package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
  466. package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
  467. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -10
  468. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -7
  469. package/src/resources/extensions/gsd/prompts/reactive-execute.md +3 -3
  470. package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -7
  471. package/src/resources/extensions/gsd/prompts/replan-slice.md +6 -6
  472. package/src/resources/extensions/gsd/reactive-graph.ts +33 -3
  473. package/src/resources/extensions/gsd/skill-health.ts +2 -1
  474. package/src/resources/extensions/gsd/state.ts +547 -29
  475. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
  476. package/src/resources/extensions/gsd/tests/atomic-task-closeout.test.ts +8 -120
  477. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
  478. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +20 -11
  479. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
  480. package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
  481. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +600 -513
  482. package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
  483. package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
  484. package/src/resources/extensions/gsd/tests/auto-stash-merge.test.ts +121 -0
  485. package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +540 -668
  486. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
  487. package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
  488. package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
  489. package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
  490. package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
  491. package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
  492. package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
  493. package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +39 -60
  494. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +375 -0
  495. package/src/resources/extensions/gsd/tests/complete-task.test.ts +387 -0
  496. package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
  497. package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
  498. package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
  499. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
  500. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -2
  501. package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
  502. package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
  503. package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
  504. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +512 -0
  505. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +644 -84
  506. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
  507. package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
  508. package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
  509. package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
  510. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +192 -161
  511. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
  512. package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
  513. package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +30 -90
  514. package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
  515. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +57 -80
  516. package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +164 -0
  517. package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
  518. package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +55 -153
  519. package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
  520. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
  521. package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +53 -97
  522. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
  523. package/src/resources/extensions/gsd/tests/doctor.test.ts +109 -149
  524. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
  525. package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
  526. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
  527. package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
  528. package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +278 -0
  529. package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +232 -0
  530. package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
  531. package/src/resources/extensions/gsd/tests/git-service.test.ts +291 -390
  532. package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
  533. package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
  534. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
  535. package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
  536. package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +440 -0
  537. package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
  538. package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
  539. package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
  540. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -270
  541. package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
  542. package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
  543. package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
  544. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
  545. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +643 -0
  546. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  547. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +1161 -0
  548. package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
  549. package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
  550. package/src/resources/extensions/gsd/tests/memory-store.test.ts +81 -94
  551. package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
  552. package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +429 -0
  553. package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
  554. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
  555. package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
  556. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +89 -97
  557. package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +127 -164
  558. package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
  559. package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
  560. package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
  561. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
  562. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
  563. package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
  564. package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
  565. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
  566. package/src/resources/extensions/gsd/tests/parsers.test.ts +548 -612
  567. package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
  568. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +176 -113
  569. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +7 -0
  570. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +179 -0
  571. package/src/resources/extensions/gsd/tests/plan-task.test.ts +145 -0
  572. package/src/resources/extensions/gsd/tests/planning-crossval.test.ts +305 -0
  573. package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
  574. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +139 -0
  575. package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
  576. package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
  577. package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
  578. package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
  579. package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
  580. package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
  581. package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +325 -0
  582. package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
  583. package/src/resources/extensions/gsd/tests/replan-handler.test.ts +410 -0
  584. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
  585. package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
  586. package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
  587. package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
  588. package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
  589. package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +1 -1
  590. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +296 -0
  591. package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
  592. package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
  593. package/src/resources/extensions/gsd/tests/schema-v9-sequence.test.ts +176 -0
  594. package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
  595. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
  596. package/src/resources/extensions/gsd/tests/shared-wal.test.ts +209 -0
  597. package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
  598. package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
  599. package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
  600. package/src/resources/extensions/gsd/tests/token-cost-display.test.ts +118 -0
  601. package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
  602. package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
  603. package/src/resources/extensions/gsd/tests/tool-naming.test.ts +19 -13
  604. package/src/resources/extensions/gsd/tests/undo.test.ts +321 -1
  605. package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
  606. package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
  607. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +1 -1
  608. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +0 -142
  609. package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
  610. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
  611. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
  612. package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
  613. package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
  614. package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
  615. package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
  616. package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
  617. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
  618. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
  619. package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
  620. package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +12 -5
  621. package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
  622. package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
  623. package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
  624. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
  625. package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
  626. package/src/resources/extensions/gsd/tools/complete-milestone.ts +176 -0
  627. package/src/resources/extensions/gsd/tools/complete-slice.ts +300 -0
  628. package/src/resources/extensions/gsd/tools/complete-task.ts +245 -0
  629. package/src/resources/extensions/gsd/tools/plan-milestone.ts +249 -0
  630. package/src/resources/extensions/gsd/tools/plan-slice.ts +194 -0
  631. package/src/resources/extensions/gsd/tools/plan-task.ts +116 -0
  632. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +203 -0
  633. package/src/resources/extensions/gsd/tools/replan-slice.ts +192 -0
  634. package/src/resources/extensions/gsd/triage-resolution.ts +20 -1
  635. package/src/resources/extensions/gsd/types.ts +50 -0
  636. package/src/resources/extensions/gsd/undo.ts +247 -3
  637. package/src/resources/extensions/gsd/visualizer-data.ts +54 -17
  638. package/src/resources/extensions/gsd/workspace-index.ts +64 -46
  639. package/dist/resources/extensions/gsd/auto-observability.js +0 -56
  640. package/dist/resources/extensions/gsd/observability-validator.js +0 -422
  641. package/dist/resources/extensions/gsd/roadmap-mutations.js +0 -110
  642. package/dist/web/standalone/.next/static/VvclDCW6TAWjEyLU-EYL1/_buildManifest.js +0 -1
  643. package/dist/web/standalone/.next/static/chunks/app/_not-found/page-e07acdb7dd069836.js +0 -1
  644. package/dist/web/standalone/.next/static/chunks/app/api/bridge-terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  645. package/dist/web/standalone/.next/static/chunks/app/api/browse-directories/route-d07a2c023f1aef1e.js +0 -1
  646. package/dist/web/standalone/.next/static/chunks/app/api/captures/route-d07a2c023f1aef1e.js +0 -1
  647. package/dist/web/standalone/.next/static/chunks/app/api/cleanup/route-d07a2c023f1aef1e.js +0 -1
  648. package/dist/web/standalone/.next/static/chunks/app/api/dev-mode/route-d07a2c023f1aef1e.js +0 -1
  649. package/dist/web/standalone/.next/static/chunks/app/api/doctor/route-d07a2c023f1aef1e.js +0 -1
  650. package/dist/web/standalone/.next/static/chunks/app/api/export-data/route-d07a2c023f1aef1e.js +0 -1
  651. package/dist/web/standalone/.next/static/chunks/app/api/files/route-d07a2c023f1aef1e.js +0 -1
  652. package/dist/web/standalone/.next/static/chunks/app/api/forensics/route-d07a2c023f1aef1e.js +0 -1
  653. package/dist/web/standalone/.next/static/chunks/app/api/git/route-d07a2c023f1aef1e.js +0 -1
  654. package/dist/web/standalone/.next/static/chunks/app/api/history/route-d07a2c023f1aef1e.js +0 -1
  655. package/dist/web/standalone/.next/static/chunks/app/api/hooks/route-d07a2c023f1aef1e.js +0 -1
  656. package/dist/web/standalone/.next/static/chunks/app/api/inspect/route-d07a2c023f1aef1e.js +0 -1
  657. package/dist/web/standalone/.next/static/chunks/app/api/knowledge/route-d07a2c023f1aef1e.js +0 -1
  658. package/dist/web/standalone/.next/static/chunks/app/api/live-state/route-d07a2c023f1aef1e.js +0 -1
  659. package/dist/web/standalone/.next/static/chunks/app/api/onboarding/route-d07a2c023f1aef1e.js +0 -1
  660. package/dist/web/standalone/.next/static/chunks/app/api/preferences/route-d07a2c023f1aef1e.js +0 -1
  661. package/dist/web/standalone/.next/static/chunks/app/api/projects/route-d07a2c023f1aef1e.js +0 -1
  662. package/dist/web/standalone/.next/static/chunks/app/api/recovery/route-d07a2c023f1aef1e.js +0 -1
  663. package/dist/web/standalone/.next/static/chunks/app/api/remote-questions/route-d07a2c023f1aef1e.js +0 -1
  664. package/dist/web/standalone/.next/static/chunks/app/api/session/browser/route-d07a2c023f1aef1e.js +0 -1
  665. package/dist/web/standalone/.next/static/chunks/app/api/session/command/route-d07a2c023f1aef1e.js +0 -1
  666. package/dist/web/standalone/.next/static/chunks/app/api/session/events/route-d07a2c023f1aef1e.js +0 -1
  667. package/dist/web/standalone/.next/static/chunks/app/api/session/manage/route-d07a2c023f1aef1e.js +0 -1
  668. package/dist/web/standalone/.next/static/chunks/app/api/settings-data/route-d07a2c023f1aef1e.js +0 -1
  669. package/dist/web/standalone/.next/static/chunks/app/api/shutdown/route-d07a2c023f1aef1e.js +0 -1
  670. package/dist/web/standalone/.next/static/chunks/app/api/skill-health/route-d07a2c023f1aef1e.js +0 -1
  671. package/dist/web/standalone/.next/static/chunks/app/api/steer/route-d07a2c023f1aef1e.js +0 -1
  672. package/dist/web/standalone/.next/static/chunks/app/api/terminal/input/route-d07a2c023f1aef1e.js +0 -1
  673. package/dist/web/standalone/.next/static/chunks/app/api/terminal/resize/route-d07a2c023f1aef1e.js +0 -1
  674. package/dist/web/standalone/.next/static/chunks/app/api/terminal/sessions/route-d07a2c023f1aef1e.js +0 -1
  675. package/dist/web/standalone/.next/static/chunks/app/api/terminal/stream/route-d07a2c023f1aef1e.js +0 -1
  676. package/dist/web/standalone/.next/static/chunks/app/api/terminal/upload/route-d07a2c023f1aef1e.js +0 -1
  677. package/dist/web/standalone/.next/static/chunks/app/api/undo/route-d07a2c023f1aef1e.js +0 -1
  678. package/dist/web/standalone/.next/static/chunks/app/api/update/route-d07a2c023f1aef1e.js +0 -1
  679. package/dist/web/standalone/.next/static/chunks/app/api/visualizer/route-d07a2c023f1aef1e.js +0 -1
  680. package/dist/web/standalone/.next/static/chunks/app/layout-745c6ed5fea5fb06.js +0 -1
  681. package/dist/web/standalone/.next/static/chunks/app/page-801b53eff6e83579.js +0 -1
  682. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/app-error-d07a2c023f1aef1e.js +0 -1
  683. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/forbidden-d07a2c023f1aef1e.js +0 -1
  684. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-e6255954dccfcf0a.js +0 -1
  685. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/not-found-d07a2c023f1aef1e.js +0 -1
  686. package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/unauthorized-d07a2c023f1aef1e.js +0 -1
  687. package/dist/web/standalone/.next/static/css/123c0bb039697968.css +0 -1
  688. package/src/resources/extensions/gsd/auto-observability.ts +0 -74
  689. package/src/resources/extensions/gsd/observability-validator.ts +0 -456
  690. package/src/resources/extensions/gsd/roadmap-mutations.ts +0 -134
  691. package/src/resources/extensions/gsd/tests/doctor-task-done-missing-summary-slice-loop.test.ts +0 -174
  692. package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +0 -474
  693. /package/dist/web/standalone/.next/static/{VvclDCW6TAWjEyLU-EYL1 → alS4hoANx0TK4UVZY27da}/_ssgManifest.js +0 -0
@@ -4,8 +4,14 @@
4
4
  * Covers: squash-merge topology (one commit on main), rich commit message with
5
5
  * slice titles, worktree cleanup, nothing-to-commit edge case, auto-push with
6
6
  * bare remote. All tests use real git operations in temp repos.
7
+ *
8
+ * Note: execSync is used intentionally in these tests for git operations with
9
+ * controlled, hardcoded inputs (no user input). This is safe and necessary for
10
+ * testing real git behavior.
7
11
  */
8
12
 
13
+ import { describe, test, afterEach } from "node:test";
14
+ import assert from "node:assert/strict";
9
15
  import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, realpathSync, readFileSync } from "node:fs";
10
16
  import { join } from "node:path";
11
17
  import { tmpdir } from "node:os";
@@ -19,11 +25,8 @@ import {
19
25
  import { getSliceBranchName } from "../worktree.ts";
20
26
  import { nativeMergeSquash } from "../native-git-bridge.ts";
21
27
 
22
- import { createTestContext } from "./test-helpers.ts";
23
-
24
- const { assertEq, assertTrue, assertMatch, report } = createTestContext();
25
-
26
28
  function run(cmd: string, cwd: string): string {
29
+ // Safe: all inputs are hardcoded test strings, not user input
27
30
  return execSync(cmd, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
28
31
  }
29
32
 
@@ -56,7 +59,6 @@ function addSliceToMilestone(
56
59
  sliceTitle: string,
57
60
  commits: Array<{ file: string; content: string; message: string }>,
58
61
  ): void {
59
- // Detect worktree name for branch naming
60
62
  const normalizedPath = wtPath.replaceAll("\\", "/");
61
63
  const marker = "/.gsd/worktrees/";
62
64
  const idx = normalizedPath.indexOf(marker);
@@ -72,11 +74,10 @@ function addSliceToMilestone(
72
74
  }
73
75
  run(`git checkout milestone/${milestoneId}`, wtPath);
74
76
  run(`git merge --no-ff ${sliceBranch} -m "feat(${milestoneId}/${sliceId}): ${sliceTitle}"`, wtPath);
75
- // Clean up the slice branch
76
77
  run(`git branch -d ${sliceBranch}`, wtPath);
77
78
  }
78
79
 
79
- async function main(): Promise<void> {
80
+ describe("auto-worktree-milestone-merge", () => {
80
81
  const savedCwd = process.cwd();
81
82
  const tempDirs: string[] = [];
82
83
 
@@ -86,699 +87,570 @@ async function main(): Promise<void> {
86
87
  return d;
87
88
  }
88
89
 
89
- try {
90
- // ─── Test 1: Basic squash merge — one commit on main ───────────────
91
- console.log("\n=== basic squash merge — one commit on main ===");
92
- {
93
- const repo = freshRepo();
94
- const wtPath = createAutoWorktree(repo, "M010");
95
-
96
- // Add two slices with multiple commits each
97
- addSliceToMilestone(repo, wtPath, "M010", "S01", "Auth module", [
98
- { file: "auth.ts", content: "export const auth = true;\n", message: "add auth" },
99
- { file: "auth-utils.ts", content: "export const hash = () => {};\n", message: "add auth utils" },
100
- ]);
101
- addSliceToMilestone(repo, wtPath, "M010", "S02", "User dashboard", [
102
- { file: "dashboard.ts", content: "export const dash = true;\n", message: "add dashboard" },
103
- { file: "widgets.ts", content: "export const widgets = [];\n", message: "add widgets" },
104
- ]);
105
-
106
- const roadmap = makeRoadmap("M010", "User management", [
107
- { id: "S01", title: "Auth module" },
108
- { id: "S02", title: "User dashboard" },
109
- ]);
110
-
111
- const mainLogBefore = run("git log --oneline main", repo);
112
- const mainCommitCountBefore = mainLogBefore.split("\n").length;
113
-
114
- const result = mergeMilestoneToMain(repo, "M010", roadmap);
115
-
116
- // Exactly one new commit on main
117
- const mainLog = run("git log --oneline main", repo);
118
- const mainCommitCountAfter = mainLog.split("\n").length;
119
- assertEq(mainCommitCountAfter, mainCommitCountBefore + 1, "exactly one new commit on main");
120
-
121
- // Milestone branch deleted
122
- const branches = run("git branch", repo);
123
- assertTrue(!branches.includes("milestone/M010"), "milestone branch deleted");
124
-
125
- // Worktree directory removed
126
- const worktreeDir = join(repo, ".gsd", "worktrees", "M010");
127
- assertTrue(!existsSync(worktreeDir), "worktree directory removed");
128
-
129
- // Module state cleared
130
- assertEq(getAutoWorktreeOriginalBase(), null, "originalBase cleared after merge");
131
-
132
- // Files from both slices present on main
133
- assertTrue(existsSync(join(repo, "auth.ts")), "auth.ts on main");
134
- assertTrue(existsSync(join(repo, "dashboard.ts")), "dashboard.ts on main");
135
- assertTrue(existsSync(join(repo, "widgets.ts")), "widgets.ts on main");
136
-
137
- // Result shape
138
- assertTrue(result.commitMessage.length > 0, "commitMessage returned");
139
- assertTrue(typeof result.pushed === "boolean", "pushed is boolean");
90
+ afterEach(() => {
91
+ process.chdir(savedCwd);
92
+ for (const d of tempDirs) {
93
+ if (existsSync(d)) rmSync(d, { recursive: true, force: true });
140
94
  }
141
-
142
- // ─── Test 2: Rich commit message format ────────────────────────────
143
- console.log("\n=== rich commit message format ===");
144
- {
145
- const repo = freshRepo();
146
- const wtPath = createAutoWorktree(repo, "M020");
147
-
148
- addSliceToMilestone(repo, wtPath, "M020", "S01", "Core API", [
149
- { file: "api.ts", content: "export const api = true;\n", message: "add api" },
150
- ]);
151
- addSliceToMilestone(repo, wtPath, "M020", "S02", "Error handling", [
152
- { file: "errors.ts", content: "export class AppError {}\n", message: "add errors" },
153
- ]);
154
- addSliceToMilestone(repo, wtPath, "M020", "S03", "Logging infra", [
155
- { file: "logger.ts", content: "export const log = () => {};\n", message: "add logger" },
156
- ]);
157
-
158
- const roadmap = makeRoadmap("M020", "Backend foundation", [
159
- { id: "S01", title: "Core API" },
160
- { id: "S02", title: "Error handling" },
161
- { id: "S03", title: "Logging infra" },
162
- ]);
163
-
164
- const result = mergeMilestoneToMain(repo, "M020", roadmap);
165
-
166
- // Subject line: conventional commit format
167
- assertMatch(result.commitMessage, /^feat\(M020\):/, "subject has conventional commit prefix");
168
- assertTrue(result.commitMessage.includes("Backend foundation"), "subject includes milestone title");
169
-
170
- // Body: slice listing
171
- assertTrue(result.commitMessage.includes("- S01: Core API"), "body lists S01");
172
- assertTrue(result.commitMessage.includes("- S02: Error handling"), "body lists S02");
173
- assertTrue(result.commitMessage.includes("- S03: Logging infra"), "body lists S03");
174
-
175
- // Branch metadata
176
- assertTrue(result.commitMessage.includes("Branch: milestone/M020"), "body has branch metadata");
177
-
178
- // Verify the actual git commit message matches
179
- const gitMsg = run("git log -1 --format=%B main", repo).trim();
180
- assertMatch(gitMsg, /^feat\(M020\):/, "git commit message starts with feat(M020):");
181
- assertTrue(gitMsg.includes("- S01: Core API"), "git commit body has S01");
95
+ tempDirs.length = 0;
96
+ });
97
+
98
+ test("basic squash merge — one commit on main", () => {
99
+ const repo = freshRepo();
100
+ const wtPath = createAutoWorktree(repo, "M010");
101
+
102
+ addSliceToMilestone(repo, wtPath, "M010", "S01", "Auth module", [
103
+ { file: "auth.ts", content: "export const auth = true;\n", message: "add auth" },
104
+ { file: "auth-utils.ts", content: "export const hash = () => {};\n", message: "add auth utils" },
105
+ ]);
106
+ addSliceToMilestone(repo, wtPath, "M010", "S02", "User dashboard", [
107
+ { file: "dashboard.ts", content: "export const dash = true;\n", message: "add dashboard" },
108
+ { file: "widgets.ts", content: "export const widgets = [];\n", message: "add widgets" },
109
+ ]);
110
+
111
+ const roadmap = makeRoadmap("M010", "User management", [
112
+ { id: "S01", title: "Auth module" },
113
+ { id: "S02", title: "User dashboard" },
114
+ ]);
115
+
116
+ const mainLogBefore = run("git log --oneline main", repo);
117
+ const mainCommitCountBefore = mainLogBefore.split("\n").length;
118
+
119
+ const result = mergeMilestoneToMain(repo, "M010", roadmap);
120
+
121
+ const mainLog = run("git log --oneline main", repo);
122
+ const mainCommitCountAfter = mainLog.split("\n").length;
123
+ assert.strictEqual(mainCommitCountAfter, mainCommitCountBefore + 1, "exactly one new commit on main");
124
+
125
+ const branches = run("git branch", repo);
126
+ assert.ok(!branches.includes("milestone/M010"), "milestone branch deleted");
127
+
128
+ const worktreeDir = join(repo, ".gsd", "worktrees", "M010");
129
+ assert.ok(!existsSync(worktreeDir), "worktree directory removed");
130
+
131
+ assert.strictEqual(getAutoWorktreeOriginalBase(), null, "originalBase cleared after merge");
132
+
133
+ assert.ok(existsSync(join(repo, "auth.ts")), "auth.ts on main");
134
+ assert.ok(existsSync(join(repo, "dashboard.ts")), "dashboard.ts on main");
135
+ assert.ok(existsSync(join(repo, "widgets.ts")), "widgets.ts on main");
136
+
137
+ assert.ok(result.commitMessage.length > 0, "commitMessage returned");
138
+ assert.strictEqual(typeof result.pushed, "boolean", "pushed is boolean");
139
+ });
140
+
141
+ test("rich commit message format", () => {
142
+ const repo = freshRepo();
143
+ const wtPath = createAutoWorktree(repo, "M020");
144
+
145
+ addSliceToMilestone(repo, wtPath, "M020", "S01", "Core API", [
146
+ { file: "api.ts", content: "export const api = true;\n", message: "add api" },
147
+ ]);
148
+ addSliceToMilestone(repo, wtPath, "M020", "S02", "Error handling", [
149
+ { file: "errors.ts", content: "export class AppError {}\n", message: "add errors" },
150
+ ]);
151
+ addSliceToMilestone(repo, wtPath, "M020", "S03", "Logging infra", [
152
+ { file: "logger.ts", content: "export const log = () => {};\n", message: "add logger" },
153
+ ]);
154
+
155
+ const roadmap = makeRoadmap("M020", "Backend foundation", [
156
+ { id: "S01", title: "Core API" },
157
+ { id: "S02", title: "Error handling" },
158
+ { id: "S03", title: "Logging infra" },
159
+ ]);
160
+
161
+ const result = mergeMilestoneToMain(repo, "M020", roadmap);
162
+
163
+ assert.match(result.commitMessage, /^feat\(M020\):/, "subject has conventional commit prefix");
164
+ assert.ok(result.commitMessage.includes("Backend foundation"), "subject includes milestone title");
165
+ assert.ok(result.commitMessage.includes("- S01: Core API"), "body lists S01");
166
+ assert.ok(result.commitMessage.includes("- S02: Error handling"), "body lists S02");
167
+ assert.ok(result.commitMessage.includes("- S03: Logging infra"), "body lists S03");
168
+ assert.ok(result.commitMessage.includes("Branch: milestone/M020"), "body has branch metadata");
169
+
170
+ const gitMsg = run("git log -1 --format=%B main", repo).trim();
171
+ assert.match(gitMsg, /^feat\(M020\):/, "git commit message starts with feat(M020):");
172
+ assert.ok(gitMsg.includes("- S01: Core API"), "git commit body has S01");
173
+ });
174
+
175
+ test("nothing to commit — safe when no code changes (#1738, #1792)", () => {
176
+ const repo = freshRepo();
177
+ const wtPath = createAutoWorktree(repo, "M030");
178
+ const roadmap = makeRoadmap("M030", "Empty milestone", []);
179
+
180
+ let threw = false;
181
+ let errorMsg = "";
182
+ try {
183
+ mergeMilestoneToMain(repo, "M030", roadmap);
184
+ } catch (err: unknown) {
185
+ threw = true;
186
+ errorMsg = err instanceof Error ? err.message : String(err);
182
187
  }
188
+ assert.ok(!threw, `safe empty milestone should not throw (got: ${errorMsg})`);
183
189
 
184
- // ─── Test 3: Nothing to commit — preserves branch (#1738) ──────────
185
- console.log("\n=== nothing to commit safe when no code changes (#1738, #1792) ===");
186
- {
187
- const repo = freshRepo();
188
- const wtPath = createAutoWorktree(repo, "M030");
189
-
190
- // Don't add any slices/changes — milestone branch is identical to main
191
- const roadmap = makeRoadmap("M030", "Empty milestone", []);
192
-
193
- // Should NOT throw — milestone branch is identical to main, nothing to lose.
194
- // The anchor check (#1792) verifies no code files differ and passes through.
195
- let threw = false;
196
- let errorMsg = "";
197
- try {
198
- mergeMilestoneToMain(repo, "M030", roadmap);
199
- } catch (err: unknown) {
200
- threw = true;
201
- errorMsg = err instanceof Error ? err.message : String(err);
202
- }
203
- assertTrue(!threw, `safe empty milestone should not throw (got: ${errorMsg})`);
204
-
205
- // Main log unchanged (only init commit)
206
- const mainLog = run("git log --oneline main", repo);
207
- assertEq(mainLog.split("\n").length, 1, "main still has only init commit");
208
- }
190
+ const mainLog = run("git log --oneline main", repo);
191
+ assert.strictEqual(mainLog.split("\n").length, 1, "main still has only init commit");
192
+ });
209
193
 
210
- // ─── Test 4: Auto-push verify push mechanics work ──────────────
211
- // Note: loadEffectiveGSDPreferences uses a module-level const for project
212
- // prefs path (process.cwd() at import time), so temp repo prefs aren't
213
- // discoverable. We verify the push mechanics work by testing that
214
- // mergeMilestoneToMain successfully completes with a remote configured,
215
- // then manually push to verify the remote is set up correctly.
216
- console.log("\n=== auto-push with bare remote ===");
217
- {
218
- const repo = freshRepo();
219
-
220
- // Set up bare remote
221
- const bareDir = realpathSync(mkdtempSync(join(tmpdir(), "wt-ms-bare-")));
222
- tempDirs.push(bareDir);
223
- run("git init --bare", bareDir);
224
- run(`git remote add origin ${bareDir}`, repo);
225
- run("git push -u origin main", repo);
226
-
227
- const wtPath = createAutoWorktree(repo, "M040");
228
-
229
- addSliceToMilestone(repo, wtPath, "M040", "S01", "Push test", [
230
- { file: "pushed.ts", content: "export const pushed = true;\n", message: "add pushed file" },
231
- ]);
232
-
233
- const roadmap = makeRoadmap("M040", "Push verification", [
234
- { id: "S01", title: "Push test" },
235
- ]);
236
-
237
- const result = mergeMilestoneToMain(repo, "M040", roadmap);
238
-
239
- // Verify merge succeeded (commit on main)
240
- const mainLog = run("git log --oneline main", repo);
241
- assertTrue(mainLog.includes("feat(M040)"), "milestone commit on main");
242
-
243
- // Manually push to verify remote works
244
- run("git push origin main", repo);
245
- const remoteLog = run("git log --oneline main", bareDir);
246
- assertTrue(remoteLog.includes("feat(M040)"), "milestone commit reachable on remote after manual push");
247
-
248
- // Temp-repo prefs may or may not be discoverable depending on process cwd and
249
- // current preference-loading behavior. The important contract is that remote
250
- // push mechanics work and the returned value reflects what happened.
251
- assertTrue(typeof result.pushed === "boolean", "pushed flag remains boolean");
252
- }
194
+ test("auto-push with bare remote", () => {
195
+ const repo = freshRepo();
253
196
 
254
- // ─── Test 5: Auto-resolve .gsd/ state file conflicts (#530) ───────
255
- console.log("\n=== auto-resolve .gsd/ state file conflicts ===");
256
- {
257
- const repo = freshRepo();
258
- const wtPath = createAutoWorktree(repo, "M050");
259
-
260
- // Add a slice with real work
261
- addSliceToMilestone(repo, wtPath, "M050", "S01", "Conflict test", [
262
- { file: "feature.ts", content: "export const feature = true;\n", message: "add feature" },
263
- ]);
264
-
265
- // Modify .gsd/STATE.md on the milestone branch (simulates auto-mode state updates)
266
- writeFileSync(join(wtPath, ".gsd", "STATE.md"), "# State\n\n## Updated on milestone branch\n");
267
- run("git add .", wtPath);
268
- run('git commit -m "chore: update state on milestone branch"', wtPath);
269
-
270
- // Now modify .gsd/STATE.md on main too (simulates divergence)
271
- run("git checkout main", repo);
272
- writeFileSync(join(repo, ".gsd", "STATE.md"), "# State\n\n## Updated on main\n");
273
- run("git add .", repo);
274
- run('git commit -m "chore: update state on main"', repo);
275
-
276
- // Go back to worktree for the merge
277
- process.chdir(wtPath);
278
-
279
- const roadmap = makeRoadmap("M050", "Conflict resolution", [
280
- { id: "S01", title: "Conflict test" },
281
- ]);
282
-
283
- // Merge should succeed despite .gsd/STATE.md conflict — auto-resolved
284
- let threw = false;
285
- try {
286
- const result = mergeMilestoneToMain(repo, "M050", roadmap);
287
- assertTrue(result.commitMessage.includes("feat(M050)"), "merge commit created despite .gsd conflict");
288
- } catch (err) {
289
- threw = true;
290
- }
291
- assertTrue(!threw, "auto-resolves .gsd/ state file conflicts without throwing");
292
-
293
- // Feature file should be on main
294
- assertTrue(existsSync(join(repo, "feature.ts")), "feature.ts merged to main");
295
- }
197
+ const bareDir = realpathSync(mkdtempSync(join(tmpdir(), "wt-ms-bare-")));
198
+ tempDirs.push(bareDir);
199
+ run("git init --bare", bareDir);
200
+ run(`git remote add origin ${bareDir}`, repo);
201
+ run("git push -u origin main", repo);
296
202
 
297
- // ─── Test 6: Skip checkout when main already current (#757) ───────
298
- console.log("\n=== skip checkout when main already current (#757) ===");
299
- {
300
- const repo = freshRepo();
301
- const wtPath = createAutoWorktree(repo, "M060");
302
-
303
- addSliceToMilestone(repo, wtPath, "M060", "S01", "Skip checkout test", [
304
- { file: "skip-checkout.ts", content: "export const skip = true;\n", message: "add skip-checkout" },
305
- ]);
306
-
307
- const roadmap = makeRoadmap("M060", "Skip checkout verification", [
308
- { id: "S01", title: "Skip checkout test" },
309
- ]);
310
-
311
- // Verify main is already checked out at repo root (worktree default)
312
- const branchAtRoot = run("git rev-parse --abbrev-ref HEAD", repo);
313
- assertEq(branchAtRoot, "main", "main is already checked out at project root");
314
-
315
- // mergeMilestoneToMain should succeed without attempting to checkout main
316
- // (which would fail with "already used by worktree" error)
317
- let threw = false;
318
- try {
319
- const result = mergeMilestoneToMain(repo, "M060", roadmap);
320
- assertTrue(result.commitMessage.includes("feat(M060)"), "merge commit created");
321
- } catch (err) {
322
- threw = true;
323
- console.error("Unexpected error:", err);
324
- }
325
- assertTrue(!threw, "does not fail when main is already checked out at project root");
326
-
327
- // Verify the merge actually happened
328
- assertTrue(existsSync(join(repo, "skip-checkout.ts")), "skip-checkout.ts merged to main");
329
- }
203
+ const wtPath = createAutoWorktree(repo, "M040");
330
204
 
331
- // ─── Test 7: Repo using `master` as default branch (#1668) ────────
332
- console.log("\n=== master-branch repo no META.json, no prefs (#1668) ===");
333
- {
334
- const dir = realpathSync(mkdtempSync(join(tmpdir(), "wt-ms-master-test-")));
335
- tempDirs.push(dir);
336
- run("git init -b master", dir);
337
- run("git config user.email test@test.com", dir);
338
- run("git config user.name Test", dir);
339
- writeFileSync(join(dir, "README.md"), "# master-branch repo\n");
340
- mkdirSync(join(dir, ".gsd"), { recursive: true });
341
- writeFileSync(join(dir, ".gsd", "STATE.md"), "# State\n");
342
- run("git add .", dir);
343
- run("git commit -m init", dir);
344
- const defaultBranch = run("git rev-parse --abbrev-ref HEAD", dir);
345
- assertEq(defaultBranch, "master", "repo is on master branch");
346
-
347
- const wtPath = createAutoWorktree(dir, "M070");
348
- addSliceToMilestone(dir, wtPath, "M070", "S01", "Master branch test", [
349
- { file: "master-feature.ts", content: "export const masterFeature = true;\n", message: "add master feature" },
350
- ]);
351
-
352
- const metaFile = join(dir, ".gsd", "milestones", "M070", "M070-META.json");
353
- assertTrue(!existsSync(metaFile), "no META.json — integration branch not captured");
354
-
355
- const roadmap = makeRoadmap("M070", "Master branch milestone", [
356
- { id: "S01", title: "Master branch test" },
357
- ]);
358
-
359
- let threw = false;
360
- let errMsg = "";
361
- try {
362
- const result = mergeMilestoneToMain(dir, "M070", roadmap);
363
- assertTrue(result.commitMessage.includes("feat(M070)"), "merge commit created on master");
364
- } catch (err) {
365
- threw = true;
366
- errMsg = err instanceof Error ? err.message : String(err);
367
- }
368
- assertTrue(!threw, `should not throw on master-branch repo (got: ${errMsg})`);
369
-
370
- const finalBranch = run("git rev-parse --abbrev-ref HEAD", dir);
371
- assertEq(finalBranch, "master", "repo is still on master after merge");
372
- assertTrue(existsSync(join(dir, "master-feature.ts")), "feature merged to master");
373
- const branches = run("git branch", dir);
374
- assertTrue(!branches.includes("milestone/M070"), "milestone branch deleted after merge");
375
- }
205
+ addSliceToMilestone(repo, wtPath, "M040", "S01", "Push test", [
206
+ { file: "pushed.ts", content: "export const pushed = true;\n", message: "add pushed file" },
207
+ ]);
376
208
 
377
- // ─── Test 8: #1738 Bug 1 — dirty working tree detected by nativeMergeSquash ──
378
- console.log("\n=== #1738 bug 1: nativeMergeSquash detects dirty working tree ===");
379
- {
380
- const { nativeMergeSquash } = await import("../native-git-bridge.ts");
381
- const repo = freshRepo();
382
-
383
- run("git checkout -b milestone/M070", repo);
384
- writeFileSync(join(repo, "feature.ts"), "export const feature = true;\n");
385
- run("git add .", repo);
386
- run('git commit -m "add feature"', repo);
387
- run("git checkout main", repo);
388
-
389
- writeFileSync(join(repo, "feature.ts"), "// local dirty version\n");
390
-
391
- const result = nativeMergeSquash(repo, "milestone/M070");
392
- assertEq(result.success, false, "merge reports failure on dirty working tree");
393
- assertTrue(
394
- result.conflicts.includes("__dirty_working_tree__"),
395
- "conflicts include __dirty_working_tree__ sentinel",
396
- );
397
-
398
- run("git checkout -- . 2>/dev/null || true", repo);
399
- run("rm -f feature.ts", repo);
400
- }
209
+ const roadmap = makeRoadmap("M040", "Push verification", [
210
+ { id: "S01", title: "Push test" },
211
+ ]);
401
212
 
402
- // ─── Test 9: #1738 Bug 2 — branch preserved on empty squash commit ──
403
- console.log("\n=== #1738 bug 2: branch preserved when squash commit empty ===");
404
- {
405
- const repo = freshRepo();
406
- const wtPath = createAutoWorktree(repo, "M080");
407
-
408
- // Make no changes — squash will produce nothing to commit
409
- const roadmap = makeRoadmap("M080", "Empty milestone", []);
410
-
411
- // With the #1792 anchor check, empty milestones with no code changes
412
- // are safe to proceed — no data to lose.
413
- let threw = false;
414
- let errMsg = "";
415
- try {
416
- mergeMilestoneToMain(repo, "M080", roadmap);
417
- } catch (err: unknown) {
418
- threw = true;
419
- errMsg = err instanceof Error ? err.message : String(err);
420
- }
421
- assertTrue(!threw, `empty milestone with no code changes should not throw (got: ${errMsg})`);
422
- }
213
+ const result = mergeMilestoneToMain(repo, "M040", roadmap);
423
214
 
424
- // ─── Test 10: #1738 Bug 3 — clearProjectRootStateFiles cleans synced dirs ──
425
- console.log("\n=== #1738 bug 3: synced .gsd/ dirs cleaned before merge ===");
426
- {
427
- const repo = freshRepo();
428
- const wtPath = createAutoWorktree(repo, "M090");
429
-
430
- addSliceToMilestone(repo, wtPath, "M090", "S01", "Sync test", [
431
- { file: "sync-test.ts", content: "export const sync = true;\n", message: "add sync-test" },
432
- ]);
433
-
434
- // Simulate syncStateToProjectRoot: create untracked .gsd/ milestone files
435
- const msDir = join(repo, ".gsd", "milestones", "M090", "slices", "S01");
436
- mkdirSync(msDir, { recursive: true });
437
- writeFileSync(join(msDir, "S01-PLAN.md"), "# synced plan\n");
438
- writeFileSync(
439
- join(repo, ".gsd", "milestones", "M090", "M090-ROADMAP.md"),
440
- "# synced roadmap\n",
441
- );
442
-
443
- const runtimeDir = join(repo, ".gsd", "runtime", "units");
444
- mkdirSync(runtimeDir, { recursive: true });
445
- writeFileSync(join(runtimeDir, "unit-001.json"), '{"stale": true}');
446
-
447
- const roadmap = makeRoadmap("M090", "Sync cleanup test", [
448
- { id: "S01", title: "Sync test" },
449
- ]);
450
-
451
- let threw = false;
452
- try {
453
- const result = mergeMilestoneToMain(repo, "M090", roadmap);
454
- assertTrue(
455
- result.commitMessage.includes("feat(M090)"),
456
- "#1738 merge succeeds after cleaning synced dirs",
457
- );
458
- } catch (err: unknown) {
459
- threw = true;
460
- console.error("#1738 bug 3 regression:", err);
461
- }
462
- assertTrue(!threw, "#1738 merge does not fail on synced .gsd/ files");
463
- assertTrue(existsSync(join(repo, "sync-test.ts")), "sync-test.ts on main after merge");
464
- }
215
+ const mainLog = run("git log --oneline main", repo);
216
+ assert.ok(mainLog.includes("feat(M040)"), "milestone commit on main");
465
217
 
466
- // ─── Test 11: #1738 Bug 1+2 — dirty tree merge preserves branch end-to-end ──
467
- console.log("\n=== #1738 e2e: dirty tree rejection preserves branch ===");
468
- {
469
- const repo = freshRepo();
470
- const wtPath = createAutoWorktree(repo, "M100");
471
-
472
- addSliceToMilestone(repo, wtPath, "M100", "S01", "E2E test", [
473
- { file: "e2e.ts", content: "export const e2e = true;\n", message: "add e2e" },
474
- ]);
475
-
476
- writeFileSync(join(repo, "e2e.ts"), "// conflicting local file\n");
477
-
478
- const roadmap = makeRoadmap("M100", "E2E dirty tree", [
479
- { id: "S01", title: "E2E test" },
480
- ]);
481
-
482
- let threw = false;
483
- let errorMsg = "";
484
- try {
485
- mergeMilestoneToMain(repo, "M100", roadmap);
486
- } catch (err: unknown) {
487
- threw = true;
488
- errorMsg = err instanceof Error ? err.message : String(err);
489
- }
490
- assertTrue(threw, "#1738 e2e: throws on dirty working tree");
491
- assertTrue(
492
- errorMsg.includes("dirty") || errorMsg.includes("untracked") || errorMsg.includes("overwritten"),
493
- "#1738 e2e: error identifies dirty tree cause",
494
- );
495
-
496
- const branches = run("git branch", repo);
497
- assertTrue(
498
- branches.includes("milestone/M100"),
499
- "#1738 e2e: milestone branch preserved on dirty tree rejection",
500
- );
501
- }
218
+ run("git push origin main", repo);
219
+ const remoteLog = run("git log --oneline main", bareDir);
220
+ assert.ok(remoteLog.includes("feat(M040)"), "milestone commit reachable on remote after manual push");
502
221
 
503
- // ─── Test 12: Throw on unanchored code changes after empty commit (#1792)
504
- console.log("\n=== throw on unanchored code changes after empty commit (#1792) ===");
505
- {
506
- const repo = freshRepo();
507
- const wtPath = createAutoWorktree(repo, "M120");
508
-
509
- addSliceToMilestone(repo, wtPath, "M120", "S01", "Critical feature", [
510
- { file: "critical.ts", content: "export const critical = true;\n", message: "add critical feature" },
511
- ]);
512
-
513
- // Simulate: merge then revert — git considers branch "already merged"
514
- // but code is NOT on main (reverted).
515
- run(`git merge milestone/M120 --no-ff -m "merge M120"`, repo);
516
- run("git revert HEAD --no-edit -m 1", repo);
517
-
518
- const roadmap = makeRoadmap("M120", "Critical milestone", [
519
- { id: "S01", title: "Critical feature" },
520
- ]);
521
-
522
- let threw = false;
523
- let errMsg = "";
524
- try {
525
- mergeMilestoneToMain(repo, "M120", roadmap);
526
- } catch (err) {
527
- threw = true;
528
- errMsg = err instanceof Error ? err.message : String(err);
529
- }
530
- assertTrue(threw, "throws when milestone has unanchored code changes (#1792)");
531
- assertTrue(
532
- errMsg.includes("code file(s) not on"),
533
- "error message mentions unanchored code files (#1792)",
534
- );
535
-
536
- const branches = run("git branch", repo);
537
- assertTrue(
538
- branches.includes("milestone/M120"),
539
- "milestone branch preserved when code is unanchored (#1792)",
540
- );
541
- }
222
+ assert.strictEqual(typeof result.pushed, "boolean", "pushed flag remains boolean");
223
+ });
224
+
225
+ test("auto-resolve .gsd/ state file conflicts", () => {
226
+ const repo = freshRepo();
227
+ const wtPath = createAutoWorktree(repo, "M050");
228
+
229
+ addSliceToMilestone(repo, wtPath, "M050", "S01", "Conflict test", [
230
+ { file: "feature.ts", content: "export const feature = true;\n", message: "add feature" },
231
+ ]);
232
+
233
+ writeFileSync(join(wtPath, ".gsd", "STATE.md"), "# State\n\n## Updated on milestone branch\n");
234
+ run("git add .", wtPath);
235
+ run('git commit -m "chore: update state on milestone branch"', wtPath);
236
+
237
+ run("git checkout main", repo);
238
+ writeFileSync(join(repo, ".gsd", "STATE.md"), "# State\n\n## Updated on main\n");
239
+ run("git add .", repo);
240
+ run('git commit -m "chore: update state on main"', repo);
542
241
 
543
- // ─── Test 13: Safe teardown when nothing-to-commit and work already on main (#1792)
544
- console.log("\n=== safe teardown — nothing to commit, work already on main (#1792) ===");
545
- {
546
- const repo = freshRepo();
547
- const wtPath = createAutoWorktree(repo, "M130");
548
-
549
- addSliceToMilestone(repo, wtPath, "M130", "S01", "Already landed", [
550
- { file: "landed.ts", content: "export const landed = true;\n", message: "add landed feature" },
551
- ]);
552
-
553
- run("git merge --squash milestone/M130", repo);
554
- run('git commit -m "pre-land milestone work"', repo);
555
-
556
- const roadmap = makeRoadmap("M130", "Pre-landed milestone", [
557
- { id: "S01", title: "Already landed" },
558
- ]);
559
-
560
- let threw = false;
561
- let errMsg = "";
562
- try {
563
- mergeMilestoneToMain(repo, "M130", roadmap);
564
- } catch (err) {
565
- threw = true;
566
- errMsg = err instanceof Error ? err.message : String(err);
567
- }
568
- assertTrue(!threw, `safe nothing-to-commit should not throw (got: ${errMsg})`);
569
- assertTrue(existsSync(join(repo, "landed.ts")), "landed.ts present on main");
242
+ process.chdir(wtPath);
243
+
244
+ const roadmap = makeRoadmap("M050", "Conflict resolution", [
245
+ { id: "S01", title: "Conflict test" },
246
+ ]);
247
+
248
+ let threw = false;
249
+ try {
250
+ const result = mergeMilestoneToMain(repo, "M050", roadmap);
251
+ assert.ok(result.commitMessage.includes("feat(M050)"), "merge commit created despite .gsd conflict");
252
+ } catch (err) {
253
+ threw = true;
254
+ }
255
+ assert.ok(!threw, "auto-resolves .gsd/ state file conflicts without throwing");
256
+ assert.ok(existsSync(join(repo, "feature.ts")), "feature.ts merged to main");
257
+ });
258
+
259
+ test("skip checkout when main already current (#757)", () => {
260
+ const repo = freshRepo();
261
+ const wtPath = createAutoWorktree(repo, "M060");
262
+
263
+ addSliceToMilestone(repo, wtPath, "M060", "S01", "Skip checkout test", [
264
+ { file: "skip-checkout.ts", content: "export const skip = true;\n", message: "add skip-checkout" },
265
+ ]);
266
+
267
+ const roadmap = makeRoadmap("M060", "Skip checkout verification", [
268
+ { id: "S01", title: "Skip checkout test" },
269
+ ]);
270
+
271
+ const branchAtRoot = run("git rev-parse --abbrev-ref HEAD", repo);
272
+ assert.strictEqual(branchAtRoot, "main", "main is already checked out at project root");
273
+
274
+ let threw = false;
275
+ try {
276
+ const result = mergeMilestoneToMain(repo, "M060", roadmap);
277
+ assert.ok(result.commitMessage.includes("feat(M060)"), "merge commit created");
278
+ } catch (err) {
279
+ threw = true;
280
+ }
281
+ assert.ok(!threw, "does not fail when main is already checked out at project root");
282
+ assert.ok(existsSync(join(repo, "skip-checkout.ts")), "skip-checkout.ts merged to main");
283
+ });
284
+
285
+ test("master-branch repo — no META.json, no prefs (#1668)", () => {
286
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "wt-ms-master-test-")));
287
+ tempDirs.push(dir);
288
+ run("git init -b master", dir);
289
+ run("git config user.email test@test.com", dir);
290
+ run("git config user.name Test", dir);
291
+ writeFileSync(join(dir, "README.md"), "# master-branch repo\n");
292
+ mkdirSync(join(dir, ".gsd"), { recursive: true });
293
+ writeFileSync(join(dir, ".gsd", "STATE.md"), "# State\n");
294
+ run("git add .", dir);
295
+ run("git commit -m init", dir);
296
+ const defaultBranch = run("git rev-parse --abbrev-ref HEAD", dir);
297
+ assert.strictEqual(defaultBranch, "master", "repo is on master branch");
298
+
299
+ const wtPath = createAutoWorktree(dir, "M070");
300
+ addSliceToMilestone(dir, wtPath, "M070", "S01", "Master branch test", [
301
+ { file: "master-feature.ts", content: "export const masterFeature = true;\n", message: "add master feature" },
302
+ ]);
303
+
304
+ const metaFile = join(dir, ".gsd", "milestones", "M070", "M070-META.json");
305
+ assert.ok(!existsSync(metaFile), "no META.json — integration branch not captured");
306
+
307
+ const roadmap = makeRoadmap("M070", "Master branch milestone", [
308
+ { id: "S01", title: "Master branch test" },
309
+ ]);
310
+
311
+ let threw = false;
312
+ let errMsg = "";
313
+ try {
314
+ const result = mergeMilestoneToMain(dir, "M070", roadmap);
315
+ assert.ok(result.commitMessage.includes("feat(M070)"), "merge commit created on master");
316
+ } catch (err) {
317
+ threw = true;
318
+ errMsg = err instanceof Error ? err.message : String(err);
319
+ }
320
+ assert.ok(!threw, `should not throw on master-branch repo (got: ${errMsg})`);
321
+
322
+ const finalBranch = run("git rev-parse --abbrev-ref HEAD", dir);
323
+ assert.strictEqual(finalBranch, "master", "repo is still on master after merge");
324
+ assert.ok(existsSync(join(dir, "master-feature.ts")), "feature merged to master");
325
+ const branches = run("git branch", dir);
326
+ assert.ok(!branches.includes("milestone/M070"), "milestone branch deleted after merge");
327
+ });
328
+
329
+ test("#1738 bug 1: nativeMergeSquash detects dirty working tree", async () => {
330
+ const { nativeMergeSquash } = await import("../native-git-bridge.ts");
331
+ const repo = freshRepo();
332
+
333
+ run("git checkout -b milestone/M070", repo);
334
+ writeFileSync(join(repo, "feature.ts"), "export const feature = true;\n");
335
+ run("git add .", repo);
336
+ run('git commit -m "add feature"', repo);
337
+ run("git checkout main", repo);
338
+
339
+ writeFileSync(join(repo, "feature.ts"), "// local dirty version\n");
340
+
341
+ const result = nativeMergeSquash(repo, "milestone/M070");
342
+ assert.strictEqual(result.success, false, "merge reports failure on dirty working tree");
343
+ assert.ok(
344
+ result.conflicts.includes("__dirty_working_tree__"),
345
+ "conflicts include __dirty_working_tree__ sentinel",
346
+ );
347
+
348
+ run("git checkout -- . 2>/dev/null || true", repo);
349
+ run("rm -f feature.ts", repo);
350
+ });
351
+
352
+ test("#1738 bug 2: branch preserved when squash commit empty", () => {
353
+ const repo = freshRepo();
354
+ const wtPath = createAutoWorktree(repo, "M080");
355
+ const roadmap = makeRoadmap("M080", "Empty milestone", []);
356
+
357
+ let threw = false;
358
+ let errMsg = "";
359
+ try {
360
+ mergeMilestoneToMain(repo, "M080", roadmap);
361
+ } catch (err: unknown) {
362
+ threw = true;
363
+ errMsg = err instanceof Error ? err.message : String(err);
364
+ }
365
+ assert.ok(!threw, `empty milestone with no code changes should not throw (got: ${errMsg})`);
366
+ });
367
+
368
+ test("#1738 bug 3: synced .gsd/ dirs cleaned before merge", () => {
369
+ const repo = freshRepo();
370
+ const wtPath = createAutoWorktree(repo, "M090");
371
+
372
+ addSliceToMilestone(repo, wtPath, "M090", "S01", "Sync test", [
373
+ { file: "sync-test.ts", content: "export const sync = true;\n", message: "add sync-test" },
374
+ ]);
375
+
376
+ const msDir = join(repo, ".gsd", "milestones", "M090", "slices", "S01");
377
+ mkdirSync(msDir, { recursive: true });
378
+ writeFileSync(join(msDir, "S01-PLAN.md"), "# synced plan\n");
379
+ writeFileSync(
380
+ join(repo, ".gsd", "milestones", "M090", "M090-ROADMAP.md"),
381
+ "# synced roadmap\n",
382
+ );
383
+
384
+ const runtimeDir = join(repo, ".gsd", "runtime", "units");
385
+ mkdirSync(runtimeDir, { recursive: true });
386
+ writeFileSync(join(runtimeDir, "unit-001.json"), '{"stale": true}');
387
+
388
+ const roadmap = makeRoadmap("M090", "Sync cleanup test", [
389
+ { id: "S01", title: "Sync test" },
390
+ ]);
391
+
392
+ let threw = false;
393
+ try {
394
+ const result = mergeMilestoneToMain(repo, "M090", roadmap);
395
+ assert.ok(result.commitMessage.includes("feat(M090)"), "#1738 merge succeeds after cleaning synced dirs");
396
+ } catch (err: unknown) {
397
+ threw = true;
398
+ }
399
+ assert.ok(!threw, "#1738 merge does not fail on synced .gsd/ files");
400
+ assert.ok(existsSync(join(repo, "sync-test.ts")), "sync-test.ts on main after merge");
401
+ });
402
+
403
+ test("#1738 e2e: dirty tree is stashed before merge (#2151)", () => {
404
+ const repo = freshRepo();
405
+ const wtPath = createAutoWorktree(repo, "M100");
406
+
407
+ addSliceToMilestone(repo, wtPath, "M100", "S01", "E2E test", [
408
+ { file: "e2e.ts", content: "export const e2e = true;\n", message: "add e2e" },
409
+ ]);
410
+
411
+ writeFileSync(join(repo, "e2e.ts"), "// conflicting local file\n");
412
+
413
+ const roadmap = makeRoadmap("M100", "E2E dirty tree", [
414
+ { id: "S01", title: "E2E test" },
415
+ ]);
416
+
417
+ // Since #2151, dirty files are stashed before the squash merge instead
418
+ // of causing an immediate rejection. The merge should succeed.
419
+ let threw = false;
420
+ try {
421
+ const result = mergeMilestoneToMain(repo, "M100", roadmap);
422
+ assert.ok(result.commitMessage.includes("feat(M100)"), "#2151: merge succeeds after stashing dirty files");
423
+ } catch {
424
+ threw = true;
570
425
  }
426
+ assert.ok(!threw, "#2151: dirty tree no longer rejects — stash handles it");
427
+ });
428
+
429
+ test("throw on unanchored code changes after empty commit (#1792)", () => {
430
+ const repo = freshRepo();
431
+ const wtPath = createAutoWorktree(repo, "M120");
432
+
433
+ addSliceToMilestone(repo, wtPath, "M120", "S01", "Critical feature", [
434
+ { file: "critical.ts", content: "export const critical = true;\n", message: "add critical feature" },
435
+ ]);
436
+
437
+ run(`git merge milestone/M120 --no-ff -m "merge M120"`, repo);
438
+ run("git revert HEAD --no-edit -m 1", repo);
439
+
440
+ const roadmap = makeRoadmap("M120", "Critical milestone", [
441
+ { id: "S01", title: "Critical feature" },
442
+ ]);
443
+
444
+ let threw = false;
445
+ let errMsg = "";
446
+ try {
447
+ mergeMilestoneToMain(repo, "M120", roadmap);
448
+ } catch (err) {
449
+ threw = true;
450
+ errMsg = err instanceof Error ? err.message : String(err);
451
+ }
452
+ assert.ok(threw, "throws when milestone has unanchored code changes (#1792)");
453
+ assert.ok(errMsg.includes("code file(s) not on"), "error message mentions unanchored code files (#1792)");
454
+
455
+ const branches = run("git branch", repo);
456
+ assert.ok(branches.includes("milestone/M120"), "milestone branch preserved when code is unanchored (#1792)");
457
+ });
458
+
459
+ test("safe teardown — nothing to commit, work already on main (#1792)", () => {
460
+ const repo = freshRepo();
461
+ const wtPath = createAutoWorktree(repo, "M130");
462
+
463
+ addSliceToMilestone(repo, wtPath, "M130", "S01", "Already landed", [
464
+ { file: "landed.ts", content: "export const landed = true;\n", message: "add landed feature" },
465
+ ]);
466
+
467
+ run("git merge --squash milestone/M130", repo);
468
+ run('git commit -m "pre-land milestone work"', repo);
469
+
470
+ const roadmap = makeRoadmap("M130", "Pre-landed milestone", [
471
+ { id: "S01", title: "Already landed" },
472
+ ]);
473
+
474
+ let threw = false;
475
+ let errMsg = "";
476
+ try {
477
+ mergeMilestoneToMain(repo, "M130", roadmap);
478
+ } catch (err) {
479
+ threw = true;
480
+ errMsg = err instanceof Error ? err.message : String(err);
481
+ }
482
+ assert.ok(!threw, `safe nothing-to-commit should not throw (got: ${errMsg})`);
483
+ assert.ok(existsSync(join(repo, "landed.ts")), "landed.ts present on main");
484
+ });
485
+
486
+ test("stale branch ref — fast-forward before squash merge (#1846)", () => {
487
+ const repo = freshRepo();
488
+ const wtPath = createAutoWorktree(repo, "M140");
489
+
490
+ addSliceToMilestone(repo, wtPath, "M140", "S01", "Initial work", [
491
+ { file: "initial.ts", content: "export const initial = true;\n", message: "add initial" },
492
+ ]);
571
493
 
572
- // ─── Test 14: Stale branch ref — worktree HEAD ahead of branch (#1846)
573
- console.log("\n=== stale branch ref — fast-forward before squash merge (#1846) ===");
574
- {
575
- const repo = freshRepo();
576
- const wtPath = createAutoWorktree(repo, "M140");
577
-
578
- // Add a first slice normally — this advances both the branch ref and HEAD
579
- addSliceToMilestone(repo, wtPath, "M140", "S01", "Initial work", [
580
- { file: "initial.ts", content: "export const initial = true;\n", message: "add initial" },
581
- ]);
582
-
583
- // Now simulate the bug: detach HEAD in the worktree, then make commits
584
- // that advance HEAD but leave the milestone/M140 branch ref behind.
585
- const branchRefBefore = run("git rev-parse milestone/M140", wtPath);
586
- run("git checkout --detach HEAD", wtPath);
587
-
588
- // Add multiple commits on the detached HEAD (simulates agent work)
589
- writeFileSync(join(wtPath, "feature-a.ts"), "export const featureA = true;\n");
590
- run("git add .", wtPath);
591
- run('git commit -m "add feature-a"', wtPath);
592
-
593
- writeFileSync(join(wtPath, "feature-b.ts"), "export const featureB = true;\n");
594
- run("git add .", wtPath);
595
- run('git commit -m "add feature-b"', wtPath);
596
-
597
- writeFileSync(join(wtPath, "feature-c.ts"), "export const featureC = true;\n");
598
- run("git add .", wtPath);
599
- run('git commit -m "add feature-c"', wtPath);
600
-
601
- // Verify: branch ref is stale, HEAD is ahead
602
- const branchRefAfter = run("git rev-parse milestone/M140", wtPath);
603
- const worktreeHead = run("git rev-parse HEAD", wtPath);
604
- assertEq(branchRefBefore, branchRefAfter, "branch ref unchanged (stale)");
605
- assertTrue(worktreeHead !== branchRefAfter, "worktree HEAD ahead of branch ref");
606
-
607
- const roadmap = makeRoadmap("M140", "Stale ref milestone", [
608
- { id: "S01", title: "Initial work" },
609
- ]);
610
-
611
- // The fix should fast-forward the branch ref to worktree HEAD before
612
- // squash-merging, so ALL commits are captured.
613
- let threw = false;
614
- let errMsg = "";
615
- try {
616
- const result = mergeMilestoneToMain(repo, "M140", roadmap);
617
- assertTrue(result.commitMessage.includes("feat(M140)"), "merge commit created");
618
- } catch (err) {
619
- threw = true;
620
- errMsg = err instanceof Error ? err.message : String(err);
621
- }
622
- assertTrue(!threw, `should not throw with stale branch ref (got: ${errMsg})`);
623
-
624
- // ALL files from detached HEAD commits must be on main — not just
625
- // the ones from the stale branch ref
626
- assertTrue(existsSync(join(repo, "initial.ts")), "initial.ts on main");
627
- assertTrue(existsSync(join(repo, "feature-a.ts")), "feature-a.ts on main (#1846)");
628
- assertTrue(existsSync(join(repo, "feature-b.ts")), "feature-b.ts on main (#1846)");
629
- assertTrue(existsSync(join(repo, "feature-c.ts")), "feature-c.ts on main (#1846)");
494
+ const branchRefBefore = run("git rev-parse milestone/M140", wtPath);
495
+ run("git checkout --detach HEAD", wtPath);
496
+
497
+ writeFileSync(join(wtPath, "feature-a.ts"), "export const featureA = true;\n");
498
+ run("git add .", wtPath);
499
+ run('git commit -m "add feature-a"', wtPath);
500
+
501
+ writeFileSync(join(wtPath, "feature-b.ts"), "export const featureB = true;\n");
502
+ run("git add .", wtPath);
503
+ run('git commit -m "add feature-b"', wtPath);
504
+
505
+ writeFileSync(join(wtPath, "feature-c.ts"), "export const featureC = true;\n");
506
+ run("git add .", wtPath);
507
+ run('git commit -m "add feature-c"', wtPath);
508
+
509
+ const branchRefAfter = run("git rev-parse milestone/M140", wtPath);
510
+ const worktreeHead = run("git rev-parse HEAD", wtPath);
511
+ assert.strictEqual(branchRefBefore, branchRefAfter, "branch ref unchanged (stale)");
512
+ assert.ok(worktreeHead !== branchRefAfter, "worktree HEAD ahead of branch ref");
513
+
514
+ const roadmap = makeRoadmap("M140", "Stale ref milestone", [
515
+ { id: "S01", title: "Initial work" },
516
+ ]);
517
+
518
+ let threw = false;
519
+ let errMsg = "";
520
+ try {
521
+ const result = mergeMilestoneToMain(repo, "M140", roadmap);
522
+ assert.ok(result.commitMessage.includes("feat(M140)"), "merge commit created");
523
+ } catch (err) {
524
+ threw = true;
525
+ errMsg = err instanceof Error ? err.message : String(err);
630
526
  }
527
+ assert.ok(!threw, `should not throw with stale branch ref (got: ${errMsg})`);
528
+
529
+ assert.ok(existsSync(join(repo, "initial.ts")), "initial.ts on main");
530
+ assert.ok(existsSync(join(repo, "feature-a.ts")), "feature-a.ts on main (#1846)");
531
+ assert.ok(existsSync(join(repo, "feature-b.ts")), "feature-b.ts on main (#1846)");
532
+ assert.ok(existsSync(join(repo, "feature-c.ts")), "feature-c.ts on main (#1846)");
533
+ });
534
+
535
+ test("diverged worktree HEAD — throws on divergence (#1846)", () => {
536
+ const repo = freshRepo();
537
+ const wtPath = createAutoWorktree(repo, "M150");
631
538
 
632
- // ─── Test 15: Diverged worktree HEAD — throws instead of losing data (#1846) ─
633
- console.log("\n=== diverged worktree HEAD throws on divergence (#1846) ===");
634
- {
635
- const repo = freshRepo();
636
- const wtPath = createAutoWorktree(repo, "M150");
637
-
638
- addSliceToMilestone(repo, wtPath, "M150", "S01", "Base work", [
639
- { file: "base.ts", content: "export const base = true;\n", message: "add base" },
640
- ]);
641
-
642
- run("git checkout --detach HEAD", wtPath);
643
- writeFileSync(join(wtPath, "detached-work.ts"), "export const detached = true;\n");
644
- run("git add .", wtPath);
645
- run('git commit -m "detached work"', wtPath);
646
-
647
- run("git checkout milestone/M150", repo);
648
- writeFileSync(join(repo, "diverged-work.ts"), "export const diverged = true;\n");
649
- run("git add .", repo);
650
- run('git commit -m "diverged work on branch"', repo);
651
- run("git checkout main", repo);
652
-
653
- process.chdir(wtPath);
654
-
655
- const roadmap = makeRoadmap("M150", "Diverged milestone", [
656
- { id: "S01", title: "Base work" },
657
- ]);
658
-
659
- let threw = false;
660
- let errMsg = "";
661
- try {
662
- mergeMilestoneToMain(repo, "M150", roadmap);
663
- } catch (err) {
664
- threw = true;
665
- errMsg = err instanceof Error ? err.message : String(err);
666
- }
667
- assertTrue(threw, "throws when worktree HEAD diverged from branch ref (#1846)");
668
- assertTrue(errMsg.includes("diverged"), "error message mentions divergence (#1846)");
669
-
670
- const branches = run("git branch", repo);
671
- assertTrue(branches.includes("milestone/M150"), "milestone branch preserved on divergence (#1846)");
539
+ addSliceToMilestone(repo, wtPath, "M150", "S01", "Base work", [
540
+ { file: "base.ts", content: "export const base = true;\n", message: "add base" },
541
+ ]);
542
+
543
+ run("git checkout --detach HEAD", wtPath);
544
+ writeFileSync(join(wtPath, "detached-work.ts"), "export const detached = true;\n");
545
+ run("git add .", wtPath);
546
+ run('git commit -m "detached work"', wtPath);
547
+
548
+ run("git checkout milestone/M150", repo);
549
+ writeFileSync(join(repo, "diverged-work.ts"), "export const diverged = true;\n");
550
+ run("git add .", repo);
551
+ run('git commit -m "diverged work on branch"', repo);
552
+ run("git checkout main", repo);
553
+
554
+ process.chdir(wtPath);
555
+
556
+ const roadmap = makeRoadmap("M150", "Diverged milestone", [
557
+ { id: "S01", title: "Base work" },
558
+ ]);
559
+
560
+ let threw = false;
561
+ let errMsg = "";
562
+ try {
563
+ mergeMilestoneToMain(repo, "M150", roadmap);
564
+ } catch (err) {
565
+ threw = true;
566
+ errMsg = err instanceof Error ? err.message : String(err);
672
567
  }
568
+ assert.ok(threw, "throws when worktree HEAD diverged from branch ref (#1846)");
569
+ assert.ok(errMsg.includes("diverged"), "error message mentions divergence (#1846)");
673
570
 
674
- // ─── Test 16: #1853 Bug 1 — SQUASH_MSG cleaned up after squash-merge ──
675
- console.log("\n=== #1853 bug 1: SQUASH_MSG cleaned up after successful squash-merge ===");
676
- {
677
- const repo = freshRepo();
678
- const wtPath = createAutoWorktree(repo, "M160");
571
+ const branches = run("git branch", repo);
572
+ assert.ok(branches.includes("milestone/M150"), "milestone branch preserved on divergence (#1846)");
573
+ });
679
574
 
680
- addSliceToMilestone(repo, wtPath, "M160", "S01", "SQUASH_MSG cleanup test", [
681
- { file: "squash-cleanup.ts", content: "export const cleanup = true;\n", message: "add squash-cleanup" },
682
- ]);
575
+ test("#1853 bug 1: SQUASH_MSG cleaned up after successful squash-merge", () => {
576
+ const repo = freshRepo();
577
+ const wtPath = createAutoWorktree(repo, "M160");
683
578
 
684
- const roadmap = makeRoadmap("M160", "SQUASH_MSG cleanup", [
685
- { id: "S01", title: "SQUASH_MSG cleanup test" },
686
- ]);
579
+ addSliceToMilestone(repo, wtPath, "M160", "S01", "SQUASH_MSG cleanup test", [
580
+ { file: "squash-cleanup.ts", content: "export const cleanup = true;\n", message: "add squash-cleanup" },
581
+ ]);
687
582
 
688
- const squashMsgPath = join(repo, ".git", "SQUASH_MSG");
689
- writeFileSync(squashMsgPath, "leftover squash message\n");
690
- assertTrue(existsSync(squashMsgPath), "SQUASH_MSG planted before merge");
583
+ const roadmap = makeRoadmap("M160", "SQUASH_MSG cleanup", [
584
+ { id: "S01", title: "SQUASH_MSG cleanup test" },
585
+ ]);
691
586
 
692
- const result = mergeMilestoneToMain(repo, "M160", roadmap);
693
- assertTrue(result.commitMessage.includes("feat(M160)"), "merge commit created");
587
+ const squashMsgPath = join(repo, ".git", "SQUASH_MSG");
588
+ writeFileSync(squashMsgPath, "leftover squash message\n");
589
+ assert.ok(existsSync(squashMsgPath), "SQUASH_MSG planted before merge");
694
590
 
695
- assertTrue(
696
- !existsSync(squashMsgPath),
697
- "#1853: SQUASH_MSG must not persist after successful squash-merge",
698
- );
699
- }
591
+ const result = mergeMilestoneToMain(repo, "M160", roadmap);
592
+ assert.ok(result.commitMessage.includes("feat(M160)"), "merge commit created");
700
593
 
701
- // ─── Test 17: #1853 Bug 2 uncommitted worktree code survives teardown ──
702
- console.log("\n=== #1853 bug 2: uncommitted worktree changes committed before teardown ===");
703
- {
704
- const repo = freshRepo();
705
- const wtPath = createAutoWorktree(repo, "M170");
594
+ assert.ok(!existsSync(squashMsgPath), "#1853: SQUASH_MSG must not persist after successful squash-merge");
595
+ });
706
596
 
707
- addSliceToMilestone(repo, wtPath, "M170", "S01", "Teardown safety test", [
708
- { file: "safe-file.ts", content: "export const safe = true;\n", message: "add safe file" },
709
- ]);
597
+ test("#1853 bug 2: uncommitted worktree changes committed before teardown", () => {
598
+ const repo = freshRepo();
599
+ const wtPath = createAutoWorktree(repo, "M170");
710
600
 
711
- writeFileSync(join(wtPath, "uncommitted-agent-code.ts"), "export const lost = true;\n");
601
+ addSliceToMilestone(repo, wtPath, "M170", "S01", "Teardown safety test", [
602
+ { file: "safe-file.ts", content: "export const safe = true;\n", message: "add safe file" },
603
+ ]);
712
604
 
713
- const roadmap = makeRoadmap("M170", "Teardown safety", [
714
- { id: "S01", title: "Teardown safety test" },
715
- ]);
605
+ writeFileSync(join(wtPath, "uncommitted-agent-code.ts"), "export const lost = true;\n");
716
606
 
717
- const result = mergeMilestoneToMain(repo, "M170", roadmap);
718
- assertTrue(result.commitMessage.includes("feat(M170)"), "merge commit created");
607
+ const roadmap = makeRoadmap("M170", "Teardown safety", [
608
+ { id: "S01", title: "Teardown safety test" },
609
+ ]);
719
610
 
720
- assertTrue(
721
- existsSync(join(repo, "uncommitted-agent-code.ts")),
722
- "#1853: uncommitted worktree code must survive teardown",
723
- );
724
- }
611
+ const result = mergeMilestoneToMain(repo, "M170", roadmap);
612
+ assert.ok(result.commitMessage.includes("feat(M170)"), "merge commit created");
725
613
 
726
- // ─── Test 18: #1906 — codeFilesChanged false when only .gsd/ metadata merged ──
727
- console.log("\n=== #1906: codeFilesChanged=false when only .gsd/ metadata merged ===");
728
- {
729
- const repo = freshRepo();
730
- const wtPath = createAutoWorktree(repo, "M180");
731
-
732
- // Only add .gsd/ metadata files — no actual code
733
- mkdirSync(join(wtPath, ".gsd", "milestones", "M180"), { recursive: true });
734
- writeFileSync(
735
- join(wtPath, ".gsd", "milestones", "M180", "SUMMARY.md"),
736
- "# M180 Summary\n\nThis milestone was planned but not implemented.\n",
737
- );
738
- run("git add .", wtPath);
739
- run('git commit -m "chore: add milestone summary"', wtPath);
740
-
741
- const roadmap = makeRoadmap("M180", "Metadata-only milestone", []);
742
-
743
- const result = mergeMilestoneToMain(repo, "M180", roadmap);
744
- assertEq(
745
- result.codeFilesChanged,
746
- false,
747
- "#1906: codeFilesChanged must be false when only .gsd/ files were merged",
748
- );
749
- }
614
+ assert.ok(
615
+ existsSync(join(repo, "uncommitted-agent-code.ts")),
616
+ "#1853: uncommitted worktree code must survive teardown",
617
+ );
618
+ });
750
619
 
751
- // ─── Test 19: #1906 codeFilesChanged true when real code is merged ──
752
- console.log("\n=== #1906: codeFilesChanged=true when real code is merged ===");
753
- {
754
- const repo = freshRepo();
755
- const wtPath = createAutoWorktree(repo, "M190");
756
-
757
- addSliceToMilestone(repo, wtPath, "M190", "S01", "Real code", [
758
- { file: "real-code.ts", content: "export const real = true;\n", message: "add real code" },
759
- ]);
760
-
761
- const roadmap = makeRoadmap("M190", "Code milestone", [
762
- { id: "S01", title: "Real code" },
763
- ]);
764
-
765
- const result = mergeMilestoneToMain(repo, "M190", roadmap);
766
- assertEq(
767
- result.codeFilesChanged,
768
- true,
769
- "#1906: codeFilesChanged must be true when real code files were merged",
770
- );
771
- assertTrue(existsSync(join(repo, "real-code.ts")), "real-code.ts merged to main");
772
- }
620
+ test("#1906: codeFilesChanged=false when only .gsd/ metadata merged", () => {
621
+ const repo = freshRepo();
622
+ const wtPath = createAutoWorktree(repo, "M180");
773
623
 
774
- } finally {
775
- process.chdir(savedCwd);
776
- for (const d of tempDirs) {
777
- if (existsSync(d)) rmSync(d, { recursive: true, force: true });
778
- }
779
- }
624
+ mkdirSync(join(wtPath, ".gsd", "milestones", "M180"), { recursive: true });
625
+ writeFileSync(
626
+ join(wtPath, ".gsd", "milestones", "M180", "SUMMARY.md"),
627
+ "# M180 Summary\n\nThis milestone was planned but not implemented.\n",
628
+ );
629
+ run("git add .", wtPath);
630
+ run('git commit -m "chore: add milestone summary"', wtPath);
780
631
 
781
- report();
782
- }
632
+ const roadmap = makeRoadmap("M180", "Metadata-only milestone", []);
633
+
634
+ const result = mergeMilestoneToMain(repo, "M180", roadmap);
635
+ assert.strictEqual(result.codeFilesChanged, false,
636
+ "#1906: codeFilesChanged must be false when only .gsd/ files were merged");
637
+ });
638
+
639
+ test("#1906: codeFilesChanged=true when real code is merged", () => {
640
+ const repo = freshRepo();
641
+ const wtPath = createAutoWorktree(repo, "M190");
642
+
643
+ addSliceToMilestone(repo, wtPath, "M190", "S01", "Real code", [
644
+ { file: "real-code.ts", content: "export const real = true;\n", message: "add real code" },
645
+ ]);
646
+
647
+ const roadmap = makeRoadmap("M190", "Code milestone", [
648
+ { id: "S01", title: "Real code" },
649
+ ]);
783
650
 
784
- main();
651
+ const result = mergeMilestoneToMain(repo, "M190", roadmap);
652
+ assert.strictEqual(result.codeFilesChanged, true,
653
+ "#1906: codeFilesChanged must be true when real code files were merged");
654
+ assert.ok(existsSync(join(repo, "real-code.ts")), "real-code.ts merged to main");
655
+ });
656
+ });