gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.d8826a445

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 (300) hide show
  1. package/README.md +8 -5
  2. package/dist/headless-recover.d.ts +23 -0
  3. package/dist/headless-recover.js +93 -0
  4. package/dist/headless.js +9 -0
  5. package/dist/help-text.js +1 -0
  6. package/dist/resources/.managed-resources-content-hash +1 -1
  7. package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
  8. package/dist/resources/extensions/gsd/auto/phases.js +7 -2
  9. package/dist/resources/extensions/gsd/auto/session.js +3 -0
  10. package/dist/resources/extensions/gsd/auto-dispatch.js +7 -58
  11. package/dist/resources/extensions/gsd/auto-post-unit.js +14 -28
  12. package/dist/resources/extensions/gsd/auto-start.js +1 -8
  13. package/dist/resources/extensions/gsd/auto-worktree.js +244 -216
  14. package/dist/resources/extensions/gsd/auto.js +86 -7
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
  16. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
  17. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +17 -16
  18. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
  19. package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
  20. package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
  21. package/dist/resources/extensions/gsd/commands-logs.js +2 -2
  22. package/dist/resources/extensions/gsd/commands-scan.js +2 -2
  23. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  24. package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
  25. package/dist/resources/extensions/gsd/db-writer.js +106 -95
  26. package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
  27. package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
  28. package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
  29. package/dist/resources/extensions/gsd/gsd-db.js +268 -8
  30. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  31. package/dist/resources/extensions/gsd/guided-flow.js +141 -32
  32. package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
  33. package/dist/resources/extensions/gsd/metrics.js +287 -1
  34. package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
  35. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
  36. package/dist/resources/extensions/gsd/paths.js +114 -9
  37. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  38. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  39. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  40. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  41. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  42. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  43. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
  44. package/dist/resources/extensions/gsd/queue-order.js +6 -1
  45. package/dist/resources/extensions/gsd/rethink.js +2 -2
  46. package/dist/resources/extensions/gsd/state.js +91 -372
  47. package/dist/resources/extensions/gsd/templates/project.md +10 -0
  48. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
  49. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
  50. package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
  51. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
  52. package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
  53. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
  54. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  55. package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
  56. package/dist/resources/extensions/gsd/workspace.js +59 -0
  57. package/dist/resources/extensions/gsd/worktree-command.js +4 -3
  58. package/dist/resources/extensions/gsd/worktree-resolver.js +15 -2
  59. package/dist/resources/extensions/gsd/write-intercept.js +3 -3
  60. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  61. package/dist/web/standalone/.next/BUILD_ID +1 -1
  62. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  63. package/dist/web/standalone/.next/build-manifest.json +2 -2
  64. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  65. package/dist/web/standalone/.next/required-server-files.json +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  67. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  75. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  76. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  83. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  84. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  85. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  86. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  87. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  88. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  89. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  90. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  91. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  92. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  93. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  94. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  95. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  96. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  97. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  98. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  99. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  100. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  101. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  102. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  103. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  104. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  105. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  106. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  107. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  108. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  109. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  110. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  111. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  112. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  113. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  114. package/dist/web/standalone/.next/server/app/index.html +1 -1
  115. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  116. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  117. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  118. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  119. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  120. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  121. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  122. package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
  123. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  124. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  125. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  126. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  127. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  128. package/dist/web/standalone/server.js +1 -1
  129. package/package.json +1 -1
  130. package/packages/mcp-server/README.md +2 -11
  131. package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
  132. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  133. package/packages/mcp-server/dist/remote-questions.js +28 -0
  134. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  135. package/packages/mcp-server/dist/server.d.ts +28 -0
  136. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  137. package/packages/mcp-server/dist/server.js +94 -4
  138. package/packages/mcp-server/dist/server.js.map +1 -1
  139. package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
  140. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  141. package/packages/mcp-server/dist/workflow-tools.js +56 -2
  142. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  143. package/packages/mcp-server/src/mcp-server.test.ts +226 -0
  144. package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
  145. package/packages/mcp-server/src/remote-questions.test.ts +103 -0
  146. package/packages/mcp-server/src/remote-questions.ts +35 -0
  147. package/packages/mcp-server/src/server.ts +129 -6
  148. package/packages/mcp-server/src/workflow-tools.ts +62 -3
  149. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  150. package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
  151. package/src/resources/extensions/gsd/auto/phases.ts +8 -2
  152. package/src/resources/extensions/gsd/auto/session.ts +4 -0
  153. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -62
  154. package/src/resources/extensions/gsd/auto-post-unit.ts +15 -27
  155. package/src/resources/extensions/gsd/auto-start.ts +1 -8
  156. package/src/resources/extensions/gsd/auto-worktree.ts +286 -251
  157. package/src/resources/extensions/gsd/auto.ts +102 -7
  158. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
  159. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
  160. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -17
  161. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
  162. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
  163. package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
  164. package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
  165. package/src/resources/extensions/gsd/commands-logs.ts +2 -2
  166. package/src/resources/extensions/gsd/commands-scan.ts +2 -2
  167. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  168. package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
  169. package/src/resources/extensions/gsd/db-writer.ts +123 -94
  170. package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
  171. package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
  172. package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
  173. package/src/resources/extensions/gsd/gsd-db.ts +269 -8
  174. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  175. package/src/resources/extensions/gsd/guided-flow.ts +181 -32
  176. package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
  177. package/src/resources/extensions/gsd/metrics.ts +321 -1
  178. package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
  179. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
  180. package/src/resources/extensions/gsd/paths.ts +122 -9
  181. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  182. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  183. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  184. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  185. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  186. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  187. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
  188. package/src/resources/extensions/gsd/queue-order.ts +6 -1
  189. package/src/resources/extensions/gsd/rethink.ts +2 -2
  190. package/src/resources/extensions/gsd/state.ts +91 -389
  191. package/src/resources/extensions/gsd/templates/project.md +10 -0
  192. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
  193. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
  194. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
  195. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
  196. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
  197. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
  198. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
  199. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
  200. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
  201. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
  202. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  203. package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
  204. package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
  205. package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
  206. package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
  207. package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
  208. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
  209. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
  210. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
  211. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
  212. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
  213. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
  214. package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
  215. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
  216. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
  217. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
  218. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
  219. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
  220. package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
  221. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
  222. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
  223. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
  224. package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
  225. package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
  226. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
  227. package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
  228. package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
  229. package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
  230. package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
  231. package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
  232. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
  233. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
  234. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +371 -0
  235. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
  236. package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
  237. package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
  238. package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
  239. package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
  240. package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
  241. package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
  242. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
  243. package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
  244. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
  245. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
  246. package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
  247. package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
  248. package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
  249. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
  250. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
  251. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
  252. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
  253. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
  254. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
  255. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +28 -16
  256. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
  257. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
  258. package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
  259. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
  260. package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
  261. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
  262. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  263. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
  264. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
  265. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
  266. package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +453 -0
  267. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
  268. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
  269. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +102 -0
  270. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
  271. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
  272. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
  273. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
  274. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
  275. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
  276. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
  277. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +26 -3
  278. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
  279. package/src/resources/extensions/gsd/tests/workspace.test.ts +190 -0
  280. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
  281. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
  282. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
  283. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
  284. package/src/resources/extensions/gsd/tests/write-gate.test.ts +67 -52
  285. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
  286. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
  287. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
  288. package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
  289. package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
  290. package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
  291. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
  292. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  293. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  294. package/src/resources/extensions/gsd/workspace.ts +95 -0
  295. package/src/resources/extensions/gsd/worktree-command.ts +4 -3
  296. package/src/resources/extensions/gsd/worktree-resolver.ts +16 -2
  297. package/src/resources/extensions/gsd/write-intercept.ts +3 -3
  298. package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
  299. /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → AT5qi39nKXkdmQIOIoh0f}/_buildManifest.js +0 -0
  300. /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → AT5qi39nKXkdmQIOIoh0f}/_ssgManifest.js +0 -0
@@ -0,0 +1,209 @@
1
+ // GSD-2 + Regression tests for missing-worktree warning on resume (M4 fix)
2
+ //
3
+ // When paused-session.json records a worktreePath that no longer exists on disk,
4
+ // the resume path must emit a logWarning("session", ...) describing the situation
5
+ // rather than silently falling back to project-root mode.
6
+ //
7
+ // Strategy: drive the exported _warnIfWorktreeMissingForTest seam directly
8
+ // (mirrors the exact conditional used at the two resume sites in auto.ts),
9
+ // and independently verify the scope fallback via createWorkspace/scopeMilestone
10
+ // as in auto-session-scope.test.ts.
11
+
12
+ import { describe, test, beforeEach, afterEach } from "node:test";
13
+ import assert from "node:assert/strict";
14
+ import { mkdtempSync, mkdirSync, rmSync, realpathSync } from "node:fs";
15
+ import { join } from "node:path";
16
+ import { tmpdir } from "node:os";
17
+
18
+ import {
19
+ logWarning,
20
+ peekLogs,
21
+ _resetLogs,
22
+ setStderrLoggingEnabled,
23
+ } from "../workflow-logger.ts";
24
+ import { _warnIfWorktreeMissingForTest } from "../auto.ts";
25
+ import { AutoSession } from "../auto/session.ts";
26
+ import { createWorkspace, scopeMilestone } from "../workspace.ts";
27
+
28
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
29
+
30
+ function makeProjectDir(): string {
31
+ const dir = realpathSync(mkdtempSync(join(tmpdir(), "gsd-resume-warn-test-")));
32
+ mkdirSync(join(dir, ".gsd", "milestones"), { recursive: true });
33
+ return dir;
34
+ }
35
+
36
+ // Mirror the rebuildScope() fallback from auto.ts when worktree is missing.
37
+ function applyProjectRootScope(
38
+ s: AutoSession,
39
+ projectDir: string,
40
+ milestoneId: string,
41
+ ): void {
42
+ const workspace = createWorkspace(projectDir);
43
+ s.scope = scopeMilestone(workspace, milestoneId);
44
+ }
45
+
46
+ // ─── Tests ───────────────────────────────────────────────────────────────────
47
+
48
+ describe("resume: missing worktree warning emission", () => {
49
+ let projectDir: string;
50
+
51
+ beforeEach(() => {
52
+ projectDir = makeProjectDir();
53
+ _resetLogs();
54
+ setStderrLoggingEnabled(false);
55
+ });
56
+
57
+ afterEach(() => {
58
+ setStderrLoggingEnabled(true);
59
+ rmSync(projectDir, { recursive: true, force: true });
60
+ });
61
+
62
+ test("logWarning is called when worktreePath is set but directory is missing", () => {
63
+ const missingPath = join(projectDir, ".gsd", "worktrees", "M001-nonexistent");
64
+ // missingPath was never created — existsSync returns false
65
+
66
+ const warned = _warnIfWorktreeMissingForTest(missingPath, "M001");
67
+
68
+ assert.equal(warned, true, "_warnIfWorktreeMissingForTest should return true when path is missing");
69
+
70
+ const logs = peekLogs();
71
+ assert.equal(logs.length, 1, "exactly one warning should be emitted");
72
+ assert.equal(logs[0].severity, "warn");
73
+ assert.equal(logs[0].component, "session");
74
+ assert.ok(
75
+ logs[0].message.includes(missingPath),
76
+ `warning message should include the missing path; got: ${logs[0].message}`,
77
+ );
78
+ assert.ok(
79
+ logs[0].message.includes("missing"),
80
+ "warning message should mention 'missing'",
81
+ );
82
+ });
83
+
84
+ test("logWarning message includes milestone ID", () => {
85
+ const missingPath = join(projectDir, ".gsd", "worktrees", "M042-gone");
86
+ _warnIfWorktreeMissingForTest(missingPath, "M042");
87
+
88
+ const logs = peekLogs();
89
+ assert.equal(logs.length, 1);
90
+ assert.equal(logs[0].context?.milestoneId, "M042");
91
+ });
92
+
93
+ test("logWarning is NOT called when worktreePath is null", () => {
94
+ const warned = _warnIfWorktreeMissingForTest(null, "M001");
95
+
96
+ assert.equal(warned, false);
97
+ assert.equal(peekLogs().length, 0, "no warning when worktreePath is null");
98
+ });
99
+
100
+ test("logWarning is NOT called when worktreePath is undefined", () => {
101
+ const warned = _warnIfWorktreeMissingForTest(undefined, "M001");
102
+
103
+ assert.equal(warned, false);
104
+ assert.equal(peekLogs().length, 0, "no warning when worktreePath is undefined");
105
+ });
106
+
107
+ test("logWarning is NOT called when worktreePath exists on disk", () => {
108
+ const existingWorktree = join(projectDir, ".gsd", "worktrees", "M001");
109
+ mkdirSync(existingWorktree, { recursive: true });
110
+
111
+ const warned = _warnIfWorktreeMissingForTest(existingWorktree, "M001");
112
+
113
+ assert.equal(warned, false, "no warning when path exists");
114
+ assert.equal(peekLogs().length, 0);
115
+ });
116
+
117
+ test("warning message mentions project-root fallback action", () => {
118
+ const missingPath = join(projectDir, ".gsd", "worktrees", "M099-deleted");
119
+ _warnIfWorktreeMissingForTest(missingPath, "M099");
120
+
121
+ const logs = peekLogs();
122
+ assert.equal(logs.length, 1);
123
+ assert.ok(
124
+ logs[0].message.includes("project-root mode"),
125
+ "warning should mention project-root mode fallback",
126
+ );
127
+ assert.ok(
128
+ logs[0].message.includes("gsd-debug"),
129
+ "warning should suggest /gsd-debug recovery action",
130
+ );
131
+ });
132
+ });
133
+
134
+ describe("resume: scope fallback to project-root mode when worktree is missing", () => {
135
+ let s: AutoSession;
136
+ let projectDir: string;
137
+
138
+ beforeEach(() => {
139
+ projectDir = makeProjectDir();
140
+ s = new AutoSession();
141
+ _resetLogs();
142
+ setStderrLoggingEnabled(false);
143
+ });
144
+
145
+ afterEach(() => {
146
+ setStderrLoggingEnabled(true);
147
+ rmSync(projectDir, { recursive: true, force: true });
148
+ });
149
+
150
+ test("scope.workspace.mode is 'project' after fallback from missing worktree", () => {
151
+ const mid = "M001";
152
+ s.originalBasePath = projectDir;
153
+ s.currentMilestoneId = mid;
154
+
155
+ // Simulate auto.ts resume path: worktreePath is set but missing → use projectDir
156
+ const missingPath = join(projectDir, ".gsd", "worktrees", mid);
157
+ _warnIfWorktreeMissingForTest(missingPath, mid);
158
+
159
+ // Fallback: use originalBasePath (project root)
160
+ applyProjectRootScope(s, projectDir, mid);
161
+
162
+ assert.ok(s.scope, "scope should be set after fallback");
163
+ assert.equal(s.scope.workspace.mode, "project");
164
+ });
165
+
166
+ test("scope.milestoneId is preserved after project-root fallback", () => {
167
+ const mid = "M002";
168
+ s.originalBasePath = projectDir;
169
+ s.currentMilestoneId = mid;
170
+
171
+ const missingPath = join(projectDir, ".gsd", "worktrees", mid);
172
+ _warnIfWorktreeMissingForTest(missingPath, mid);
173
+
174
+ applyProjectRootScope(s, projectDir, mid);
175
+
176
+ assert.ok(s.scope, "scope should be set");
177
+ assert.equal(s.scope.milestoneId, mid);
178
+ });
179
+
180
+ test("does not throw when worktree path is missing and scope fallback is applied", () => {
181
+ const mid = "M003";
182
+ s.originalBasePath = projectDir;
183
+ s.currentMilestoneId = mid;
184
+
185
+ const missingPath = join(projectDir, ".gsd", "worktrees", mid);
186
+
187
+ assert.doesNotThrow(() => {
188
+ _warnIfWorktreeMissingForTest(missingPath, mid);
189
+ applyProjectRootScope(s, projectDir, mid);
190
+ }, "resume with missing worktree must not throw");
191
+ });
192
+
193
+ test("warning is emitted once per missing worktree — no double-emission", () => {
194
+ const mid = "M004";
195
+ const missingPath = join(projectDir, ".gsd", "worktrees", mid);
196
+
197
+ _warnIfWorktreeMissingForTest(missingPath, mid);
198
+
199
+ // Simulating the second call as would happen if the resume-re-entry site
200
+ // also fires (e.g. pausedSession and freshStartAssessment both carry the path)
201
+ _warnIfWorktreeMissingForTest(missingPath, mid);
202
+
203
+ const logs = peekLogs();
204
+ // Two calls → two warnings (one per site — consistent with the two sites in auto.ts)
205
+ assert.equal(logs.length, 2, "each call to the seam emits one warning");
206
+ assert.equal(logs[0].component, "session");
207
+ assert.equal(logs[1].component, "session");
208
+ });
209
+ });
@@ -149,7 +149,7 @@ test("rogue detection: DB not available → returns empty array (graceful degrad
149
149
  }
150
150
  });
151
151
 
152
- test("rogue detection: slice summary on disk, no DB rowauto-remediated (not rogue)", () => {
152
+ test("rogue detection: slice summary on disk, no DB completiondetected as rogue without DB import", () => {
153
153
  const basePath = createTmpBase();
154
154
  const dbPath = join(basePath, ".gsd", "gsd.db");
155
155
  mkdirSync(join(basePath, ".gsd"), { recursive: true });
@@ -160,10 +160,9 @@ test("rogue detection: slice summary on disk, no DB row → auto-remediated (not
160
160
  const summaryPath = createSliceSummaryOnDisk(basePath, "M001", "S01");
161
161
  assert.ok(existsSync(summaryPath), "Slice summary file should exist on disk");
162
162
 
163
- // Fix #3633: stale slice DB status is auto-remediated via updateSliceStatus()
164
- // instead of being reported as rogue, so rogues array should be empty.
165
163
  const rogues = detectRogueFileWrites("complete-slice", "M001/S01", basePath);
166
- assert.equal(rogues.length, 0, "Should auto-remediate stale slice, not report as rogue");
164
+ assert.equal(rogues.length, 1, "Should report stale disk summary instead of mutating DB");
165
+ assert.equal(rogues[0]?.path, summaryPath);
167
166
  } finally {
168
167
  closeDatabase();
169
168
  rmSync(basePath, { recursive: true, force: true });
@@ -1,10 +1,8 @@
1
1
  /**
2
2
  * slice-disk-reconcile.test.ts — #2533
3
3
  *
4
- * Slices that exist on disk (in ROADMAP.md) but are missing from the SQLite
5
- * database cause permanent "No slice eligible check dependency ordering"
6
- * blocks. deriveStateFromDb must reconcile disk slices into the DB, just as
7
- * it already does for milestones (#2416).
4
+ * DB-authoritative state: slices that exist only in ROADMAP.md are projections
5
+ * and must not be imported into the SQLite database by deriveStateFromDb().
8
6
  *
9
7
  * Scenario: M001 has a ROADMAP with S01-S04. S01 and S02 have SUMMARY files
10
8
  * (complete on disk). S03 depends on S01. Only S04 is in the DB (depends on
@@ -70,7 +68,7 @@ const ROADMAP_CONTENT = `# M001: Test Milestone
70
68
  `;
71
69
 
72
70
  async function testMissingSlicesCauseBlock(): Promise<void> {
73
- console.log("\n--- Test: missing DB slices cause permanent block (pre-fix) ---");
71
+ console.log("\n--- Test: missing DB slices are not imported from ROADMAP.md ---");
74
72
 
75
73
  const base = createFixtureBase();
76
74
  const dbPath = join(base, ".gsd", "gsd.db");
@@ -96,51 +94,11 @@ async function testMissingSlicesCauseBlock(): Promise<void> {
96
94
  invalidateStateCache();
97
95
  const state = await deriveStateFromDb(base);
98
96
 
99
- // After the fix, slices S01-S03 should be reconciled into DB
100
97
  const dbSlices = getMilestoneSlices("M001");
101
- assertTrue(
102
- dbSlices.length === 4,
103
- `All 4 roadmap slices should be in DB after reconciliation, got ${dbSlices.length}`,
104
- );
105
-
106
- // S01 and S02 should be marked complete (have SUMMARY files)
107
- const s01 = dbSlices.find(s => s.id === "S01");
108
- assertTrue(s01 !== undefined, "S01 should exist in DB after reconciliation");
109
- if (s01) {
110
- assertEq(s01.status, "complete", "S01 should be 'complete' (has SUMMARY on disk)");
111
- }
112
-
113
- const s02 = dbSlices.find(s => s.id === "S02");
114
- assertTrue(s02 !== undefined, "S02 should exist in DB after reconciliation");
115
- if (s02) {
116
- assertEq(s02.status, "complete", "S02 should be 'complete' (has SUMMARY on disk)");
117
- }
118
-
119
- // S03 should be pending (no SUMMARY)
120
- const s03 = dbSlices.find(s => s.id === "S03");
121
- assertTrue(s03 !== undefined, "S03 should exist in DB after reconciliation");
122
- if (s03) {
123
- assertEq(s03.status, "pending", "S03 should be 'pending' (no SUMMARY on disk)");
124
- }
125
-
126
- // The state should NOT be blocked — S03 should be eligible (S01 dep satisfied)
127
- assertTrue(
128
- state.phase !== "blocked",
129
- `Phase should not be 'blocked' after reconciliation, got '${state.phase}'`,
130
- );
131
-
132
- // Active slice should be S03 (S01 dep met, S03 is first incomplete with satisfied deps)
133
- assertTrue(
134
- state.activeSlice !== null,
135
- "There should be an active slice after reconciliation",
136
- );
137
- if (state.activeSlice) {
138
- assertEq(
139
- state.activeSlice.id,
140
- "S03",
141
- "Active slice should be S03 (its dependency S01 is complete) (#2533)",
142
- );
143
- }
98
+ assertEq(dbSlices.length, 1, `Only DB slice S04 should remain, got ${dbSlices.length}`);
99
+ assertEq(dbSlices[0]?.id, "S04", "Disk-only slices are not inserted");
100
+ assertEq(state.phase, "blocked", "DB-only S04 remains blocked on missing DB dependency S03");
101
+ assertEq(state.activeSlice, null, "No active slice is inferred from roadmap-only rows");
144
102
  } finally {
145
103
  closeDatabase();
146
104
  cleanup(base);
@@ -148,7 +106,7 @@ async function testMissingSlicesCauseBlock(): Promise<void> {
148
106
  }
149
107
 
150
108
  async function testSliceReconciliationIdempotent(): Promise<void> {
151
- console.log("\n--- Test: slice reconciliation is idempotent ---");
109
+ console.log("\n--- Test: disk-only slices remain absent on repeated derives ---");
152
110
 
153
111
  const base = createFixtureBase();
154
112
  const dbPath = join(base, ".gsd", "gsd.db");
@@ -178,11 +136,7 @@ async function testSliceReconciliationIdempotent(): Promise<void> {
178
136
  assertEq(s01.status, "complete", "S01 status should remain 'complete' (not overwritten)");
179
137
  }
180
138
 
181
- // S02-S04 should have been added
182
- assertTrue(
183
- dbSlices.length === 4,
184
- `Should have 4 slices after reconciliation (existing + new), got ${dbSlices.length}`,
185
- );
139
+ assertEq(dbSlices.length, 1, `Only existing DB slice should remain, got ${dbSlices.length}`);
186
140
  } finally {
187
141
  closeDatabase();
188
142
  cleanup(base);
@@ -218,7 +172,7 @@ async function testNoRoadmapSkipsReconciliation(): Promise<void> {
218
172
  }
219
173
 
220
174
  async function main(): Promise<void> {
221
- console.log("\n=== #2533: deriveStateFromDb reconciles disk slices ===");
175
+ console.log("\n=== deriveStateFromDb does not reconcile disk slices ===");
222
176
 
223
177
  await testMissingSlicesCauseBlock();
224
178
  await testSliceReconciliationIdempotent();
@@ -1,10 +1,9 @@
1
1
  /**
2
- * stale-slice-rows.test.ts — #3658
2
+ * stale-slice-rows.test.ts
3
3
  *
4
- * Verify that state.ts contains slice-level status reconciliation that
5
- * updates stale DB rows (status "pending") when disk artifacts (SUMMARY)
6
- * prove the slice is complete. Without this, the dependency resolver builds
7
- * doneSliceIds from stale DB rows and downstream slices stay blocked.
4
+ * Verify that state.ts no longer treats slice SUMMARY.md projections as
5
+ * authority for DB slice status. Slice rows must be updated through DB-backed
6
+ * completion/import APIs.
8
7
  */
9
8
 
10
9
  import { describe, test } from "node:test";
@@ -16,26 +15,26 @@ import { fileURLToPath } from "node:url";
16
15
  const __dirname = dirname(fileURLToPath(import.meta.url));
17
16
  const sourceFile = join(__dirname, "..", "state.ts");
18
17
 
19
- describe("stale slice row reconciliation (#3658)", () => {
18
+ describe("stale slice row DB-authoritative boundary", () => {
20
19
  const source = readFileSync(sourceFile, "utf-8");
21
20
 
22
- test("imports updateSliceStatus from gsd-db", () => {
23
- assert.match(source, /import\s*\{[^}]*updateSliceStatus[^}]*\}\s*from/);
21
+ test("does not import updateSliceStatus into state derivation", () => {
22
+ assert.doesNotMatch(source, /import\s*\{[^}]*updateSliceStatus[^}]*\}\s*from/);
24
23
  });
25
24
 
26
- test("checks isStatusDone before reconciling slice rows", () => {
27
- assert.match(source, /isStatusDone\(dbSlice\.status\)/);
25
+ test("does not scan DB slice rows for disk SUMMARY reconciliation", () => {
26
+ assert.doesNotMatch(source, /dbSlice/);
28
27
  });
29
28
 
30
- test("resolves SUMMARY file to detect completed slices on disk", () => {
31
- assert.match(source, /resolveSliceFile\(basePath,\s*mid,\s*dbSlice\.id,\s*["']SUMMARY["']\)/);
29
+ test("does not resolve slice SUMMARY to mutate DB state", () => {
30
+ assert.doesNotMatch(source, /resolveSliceFile\(basePath,\s*mid,\s*dbSlice\.id,\s*["']SUMMARY["']\)/);
32
31
  });
33
32
 
34
- test("calls updateSliceStatus to reconcile stale rows", () => {
35
- assert.match(source, /updateSliceStatus\(mid,\s*dbSlice\.id,\s*["']complete["']\)/);
33
+ test("does not call updateSliceStatus from state derivation", () => {
34
+ assert.doesNotMatch(source, /updateSliceStatus\(/);
36
35
  });
37
36
 
38
- test("references issue #3599 in reconciliation comment", () => {
39
- assert.match(source, /#3599/);
37
+ test("documents markdown projections as non-authoritative", () => {
38
+ assert.match(source, /Markdown files are projections only/);
40
39
  });
41
40
  });
@@ -69,6 +69,7 @@ function makeMilestoneRow(overrides: Partial<MilestoneRow> = {}): MilestoneRow {
69
69
  definition_of_done: [],
70
70
  requirement_coverage: "",
71
71
  boundary_map_markdown: "",
72
+ sequence: 0,
72
73
  ...overrides,
73
74
  };
74
75
  }
@@ -858,11 +858,11 @@ describe("state-machine-full-walkthrough", () => {
858
858
  });
859
859
 
860
860
  // ═══════════════════════════════════════════════════════════════════════════
861
- // RECONCILIATION
861
+ // DB-AUTHORITATIVE DERIVATION
862
862
  // ═══════════════════════════════════════════════════════════════════════════
863
863
 
864
- describe("Reconciliation", () => {
865
- test("DB: task with SUMMARY on disk but DB says pending → reconciliation fixes status (#2514)", async () => {
864
+ describe("DB-authoritative derivation", () => {
865
+ test("DB: task with SUMMARY on disk but DB says pending → DB remains authoritative", async () => {
866
866
  const base = createFixtureBase();
867
867
  const dbPath = join(base, ".gsd", "gsd.db");
868
868
  openDatabase(dbPath);
@@ -875,19 +875,19 @@ describe("state-machine-full-walkthrough", () => {
875
875
  writeRoadmap(base, "M001", standardRoadmap());
876
876
  writePlan(base, "M001", "S01", standardPlan());
877
877
 
878
- // Write SUMMARY files on disk for both tasks (simulating session disconnect)
878
+ // Write SUMMARY files on disk for both tasks. These are projections and
879
+ // must not complete pending DB tasks during runtime derivation.
879
880
  writeTaskSummary(base, "M001", "S01", "T01");
880
881
  writeTaskSummary(base, "M001", "S01", "T02");
881
882
 
882
883
  invalidateStateCache();
883
884
  const state = await deriveStateFromDb(base);
884
885
 
885
- // Reconciliation should detect SUMMARY→DB mismatch and update
886
- // All tasks done summarizing (not executing)
887
- assert.equal(state.phase, "summarizing", "reconciliation should advance past pending tasks");
886
+ assert.equal(state.phase, "executing", "disk SUMMARY projections must not complete DB tasks");
887
+ assert.equal(state.activeTask?.id, "T01", "first pending DB task remains active");
888
888
  });
889
889
 
890
- test("empty DB with disk milestones → disk-to-DB sync (#2631)", async () => {
890
+ test("empty DB with disk milestones → no runtime disk-to-DB sync", async () => {
891
891
  const base = createFixtureBase();
892
892
  writeContext(base, "M001", "# M001: Test\n\nContext.");
893
893
 
@@ -899,11 +899,10 @@ describe("state-machine-full-walkthrough", () => {
899
899
  invalidateStateCache();
900
900
  const state = await deriveState(base);
901
901
 
902
- // After deriveState, DB should have the disk milestone
902
+ // Runtime derivation must not import disk milestones into the DB.
903
903
  const after = getAllMilestones();
904
- assert.ok(after.length > 0, "DB should have milestones after reconciliation");
905
- assert.equal(after[0]!.id, "M001");
906
- assert.ok(state.activeMilestone !== null);
904
+ assert.equal(after.length, 0, "DB should remain empty without explicit migration");
905
+ assert.equal(state.activeMilestone, null, "disk milestone is ignored while DB is authoritative");
907
906
  });
908
907
 
909
908
  test("ghost milestone (empty dir) → NOT in registry", async () => {
@@ -1063,7 +1062,7 @@ describe("state-machine-full-walkthrough", () => {
1063
1062
  // ═══════════════════════════════════════════════════════════════════════════
1064
1063
 
1065
1064
  describe("Recovery: DB has slice but no task rows (partial migration)", () => {
1066
- test("DB tasks empty but PLAN on disk has tasks → reconciles to executing", async () => {
1065
+ test("DB tasks empty but PLAN on disk has tasks → stays planning", async () => {
1067
1066
  const base = createFixtureBase();
1068
1067
  const dbPath = join(base, ".gsd", "gsd.db");
1069
1068
  openDatabase(dbPath);
@@ -1078,15 +1077,13 @@ describe("state-machine-full-walkthrough", () => {
1078
1077
  invalidateStateCache();
1079
1078
  const state = await deriveStateFromDb(base);
1080
1079
 
1081
- // FIX (#3600): plan-file tasks are now reconciled into the DB,
1082
- // so the phase correctly advances to executing instead of planning.
1083
- assert.equal(state.phase, "executing",
1084
- "reconciled plan-file tasks → executing (not stuck in planning)");
1080
+ assert.equal(state.phase, "planning",
1081
+ "PLAN.md projection must not import DB tasks during runtime derivation");
1085
1082
  });
1086
1083
  });
1087
1084
 
1088
1085
  describe("Failure: partial SUMMARY reconciliation", () => {
1089
- test("only one task has SUMMARY, other still pending → executing next task", async () => {
1086
+ test("only one task has SUMMARY, other still pending → executing first DB-pending task", async () => {
1090
1087
  const base = createFixtureBase();
1091
1088
  const dbPath = join(base, ".gsd", "gsd.db");
1092
1089
  openDatabase(dbPath);
@@ -1104,9 +1101,8 @@ describe("state-machine-full-walkthrough", () => {
1104
1101
  invalidateStateCache();
1105
1102
  const state = await deriveStateFromDb(base);
1106
1103
 
1107
- // T01 reconciled to complete, T02 still pending → executing T02
1108
1104
  assert.equal(state.phase, "executing");
1109
- assert.equal(state.activeTask?.id, "T02", "should advance to next pending task");
1105
+ assert.equal(state.activeTask?.id, "T01", "disk SUMMARY must not advance past pending DB task");
1110
1106
  });
1111
1107
  });
1112
1108
 
@@ -1255,7 +1251,7 @@ describe("state-machine-full-walkthrough", () => {
1255
1251
  });
1256
1252
 
1257
1253
  describe("Failure: missing task plan files in DB path", () => {
1258
- test("DB has tasks but no T##-PLAN.md files → planning phase", async () => {
1254
+ test("DB has tasks but no T##-PLAN.md files → executing phase", async () => {
1259
1255
  const base = createFixtureBase();
1260
1256
  const dbPath = join(base, ".gsd", "gsd.db");
1261
1257
  openDatabase(dbPath);
@@ -1273,8 +1269,8 @@ describe("state-machine-full-walkthrough", () => {
1273
1269
  invalidateStateCache();
1274
1270
  const state = await deriveStateFromDb(base);
1275
1271
 
1276
- assert.equal(state.phase, "planning",
1277
- "missing T##-PLAN.md files should keep state in planning");
1272
+ assert.equal(state.phase, "executing",
1273
+ "DB tasks are authoritative even when task plan projections are missing");
1278
1274
  });
1279
1275
  });
1280
1276
 
@@ -1591,7 +1587,7 @@ describe("state-machine-full-walkthrough", () => {
1591
1587
  });
1592
1588
 
1593
1589
  describe("Failure: multiple reconciliation in single derivation", () => {
1594
- test("DB has 3 stale tasks, all with SUMMARY on disk → all reconciled in one pass", async () => {
1590
+ test("DB has 3 stale tasks, all with SUMMARY on disk → first DB-pending task remains active", async () => {
1595
1591
  const base = createFixtureBase();
1596
1592
  const dbPath = join(base, ".gsd", "gsd.db");
1597
1593
  openDatabase(dbPath);
@@ -1641,9 +1637,9 @@ describe("state-machine-full-walkthrough", () => {
1641
1637
  invalidateStateCache();
1642
1638
  const state = await deriveStateFromDb(base);
1643
1639
 
1644
- // All 3 should be reconciled in one pass → summarizing
1645
- assert.equal(state.phase, "summarizing",
1646
- "all 3 stale tasks should be reconciled to complete in one derivation");
1640
+ assert.equal(state.phase, "executing",
1641
+ "disk SUMMARY projections must not reconcile DB task state");
1642
+ assert.equal(state.activeTask?.id, "T01", "first non-closed DB task remains active");
1647
1643
  });
1648
1644
  });
1649
1645
  });
@@ -1,6 +1,6 @@
1
1
  // GSD Extension - Steer Worktree Path Resolution Test
2
- // Regression test for #3476: /gsd steer must write overrides to the worktree .gsd/,
3
- // not the project root .gsd/, when a worktree is active.
2
+ // Worktrees share the canonical project .gsd state root. /gsd steer writes
3
+ // overrides to that canonical root even when invoked with a worktree path.
4
4
 
5
5
  import { describe, test, beforeEach, afterEach } from "node:test";
6
6
  import assert from "node:assert/strict";
@@ -27,32 +27,31 @@ describe("steer worktree path resolution (#3476)", () => {
27
27
  rmSync(projectRoot, { recursive: true, force: true });
28
28
  });
29
29
 
30
- test("appendOverride writes to worktree .gsd/ when worktree path is used", async () => {
30
+ test("appendOverride writes to canonical project .gsd/ when worktree path is used", async () => {
31
31
  await appendOverride(worktreePath, "Use Postgres instead of SQLite", "M001/S01/T01");
32
32
 
33
- // Override should be in the worktree .gsd/
33
+ // Override should be in the canonical project .gsd/
34
34
  const wtOverrides = join(worktreePath, ".gsd", "OVERRIDES.md");
35
- assert.ok(existsSync(wtOverrides), "override file exists in worktree .gsd/");
35
+ const rootOverrides = join(projectRoot, ".gsd", "OVERRIDES.md");
36
+ assert.ok(!existsSync(wtOverrides), "no override file in worktree-local .gsd/");
37
+ assert.ok(existsSync(rootOverrides), "override file exists in project root .gsd/");
36
38
 
37
- const content = readFileSync(wtOverrides, "utf-8");
39
+ const content = readFileSync(rootOverrides, "utf-8");
38
40
  assert.ok(content.includes("Use Postgres instead of SQLite"), "override content is correct");
39
-
40
- // Override should NOT be in the project root .gsd/
41
- const rootOverrides = join(projectRoot, ".gsd", "OVERRIDES.md");
42
- assert.ok(!existsSync(rootOverrides), "no override file in project root .gsd/");
43
41
  });
44
42
 
45
- test("loadActiveOverrides reads from worktree .gsd/ when worktree path is used", async () => {
43
+ test("loadActiveOverrides reads canonical project .gsd/ when worktree path is used", async () => {
46
44
  await appendOverride(worktreePath, "Switch to JWT auth", "M001/S02/T01");
47
45
 
48
- // Loading from worktree should find the override
46
+ // Loading from worktree resolves to the canonical project state root.
49
47
  const wtOverrides = await loadActiveOverrides(worktreePath);
50
48
  assert.equal(wtOverrides.length, 1, "one active override in worktree");
51
49
  assert.equal(wtOverrides[0].change, "Switch to JWT auth");
52
50
 
53
- // Loading from project root should find nothing
51
+ // Loading from project root sees the same canonical override.
54
52
  const rootOverrides = await loadActiveOverrides(projectRoot);
55
- assert.equal(rootOverrides.length, 0, "no overrides in project root");
53
+ assert.equal(rootOverrides.length, 1, "same override visible from project root");
54
+ assert.equal(rootOverrides[0].change, "Switch to JWT auth");
56
55
  });
57
56
 
58
57
  test("appendOverride falls back to project root when no worktree exists", async () => {
@@ -135,7 +135,7 @@ test("stopAutoRemote sends SIGTERM to a live process and returns found:true", {
135
135
 
136
136
  // ─── Lock path: original project root vs worktree ────────────────────────
137
137
 
138
- test("lock file should be discoverable at project root, not worktree path", () => {
138
+ test("lock file should be discoverable from project root and worktree path", () => {
139
139
  const projectRoot = makeTmpBase();
140
140
  const worktreePath = join(projectRoot, ".gsd", "worktrees", "M001");
141
141
  mkdirSync(join(worktreePath, ".gsd"), { recursive: true });
@@ -149,9 +149,10 @@ test("lock file should be discoverable at project root, not worktree path", () =
149
149
  assert.ok(lock, "lock should be found at project root");
150
150
  assert.equal(lock!.unitType, "execute-task");
151
151
 
152
- // Worktree path should NOT have a lock
152
+ // Worktree path resolves to the same canonical project .gsd lock.
153
153
  const worktreeLock = readCrashLock(worktreePath);
154
- assert.equal(worktreeLock, null, "lock should NOT exist at worktree path");
154
+ assert.ok(worktreeLock, "lock should be discoverable via worktree path");
155
+ assert.equal(worktreeLock!.unitType, "execute-task");
155
156
  } finally {
156
157
  cleanup(projectRoot);
157
158
  }