gsd-pi 2.78.1-dev.e9d88a536 → 2.78.1-dev.eccf86e27

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 (212) hide show
  1. package/README.md +5 -7
  2. package/dist/help-text.js +1 -1
  3. package/dist/resource-loader.js +6 -1
  4. package/dist/resources/.managed-resources-content-hash +1 -1
  5. package/dist/resources/extensions/gsd/auto/detect-stuck.js +41 -5
  6. package/dist/resources/extensions/gsd/auto/loop.js +235 -36
  7. package/dist/resources/extensions/gsd/auto/phases.js +14 -7
  8. package/dist/resources/extensions/gsd/auto/session.js +36 -0
  9. package/dist/resources/extensions/gsd/auto-dispatch.js +49 -4
  10. package/dist/resources/extensions/gsd/auto-post-unit.js +26 -12
  11. package/dist/resources/extensions/gsd/auto-worktree.js +185 -201
  12. package/dist/resources/extensions/gsd/auto.js +139 -49
  13. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
  14. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +26 -20
  15. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
  16. package/dist/resources/extensions/gsd/crash-recovery.js +160 -47
  17. package/dist/resources/extensions/gsd/db/auto-workers.js +227 -0
  18. package/dist/resources/extensions/gsd/db/command-queue.js +105 -0
  19. package/dist/resources/extensions/gsd/db/milestone-leases.js +210 -0
  20. package/dist/resources/extensions/gsd/db/runtime-kv.js +91 -0
  21. package/dist/resources/extensions/gsd/db/unit-dispatches.js +322 -0
  22. package/dist/resources/extensions/gsd/db-writer.js +96 -16
  23. package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
  24. package/dist/resources/extensions/gsd/docs/COORDINATION.md +42 -0
  25. package/dist/resources/extensions/gsd/doctor-proactive.js +4 -0
  26. package/dist/resources/extensions/gsd/doctor-runtime-checks.js +22 -6
  27. package/dist/resources/extensions/gsd/doctor.js +12 -2
  28. package/dist/resources/extensions/gsd/gsd-db.js +355 -3
  29. package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
  30. package/dist/resources/extensions/gsd/guided-flow.js +116 -26
  31. package/dist/resources/extensions/gsd/interrupted-session.js +18 -15
  32. package/dist/resources/extensions/gsd/metrics.js +287 -1
  33. package/dist/resources/extensions/gsd/paths.js +79 -8
  34. package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  35. package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
  36. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  37. package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  38. package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  39. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  40. package/dist/resources/extensions/gsd/state.js +21 -6
  41. package/dist/resources/extensions/gsd/templates/project.md +10 -0
  42. package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
  43. package/dist/resources/extensions/gsd/workspace.js +59 -0
  44. package/dist/resources/extensions/gsd/worktree-resolver.js +79 -2
  45. package/dist/resources/extensions/gsd/write-intercept.js +3 -3
  46. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  47. package/dist/web/standalone/.next/BUILD_ID +1 -1
  48. package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
  49. package/dist/web/standalone/.next/build-manifest.json +2 -2
  50. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  51. package/dist/web/standalone/.next/required-server-files.json +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/index.html +1 -1
  69. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
  76. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  77. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  78. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  79. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  80. package/dist/web/standalone/server.js +1 -1
  81. package/package.json +1 -1
  82. package/packages/mcp-server/README.md +2 -11
  83. package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
  84. package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
  85. package/packages/mcp-server/dist/remote-questions.js +28 -0
  86. package/packages/mcp-server/dist/remote-questions.js.map +1 -1
  87. package/packages/mcp-server/dist/server.d.ts +28 -0
  88. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  89. package/packages/mcp-server/dist/server.js +94 -4
  90. package/packages/mcp-server/dist/server.js.map +1 -1
  91. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  92. package/packages/mcp-server/src/mcp-server.test.ts +226 -0
  93. package/packages/mcp-server/src/remote-questions.test.ts +103 -0
  94. package/packages/mcp-server/src/remote-questions.ts +35 -0
  95. package/packages/mcp-server/src/server.ts +129 -6
  96. package/packages/mcp-server/src/workflow-tools.ts +1 -1
  97. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  98. package/src/resources/extensions/gsd/auto/detect-stuck.ts +37 -5
  99. package/src/resources/extensions/gsd/auto/loop.ts +263 -41
  100. package/src/resources/extensions/gsd/auto/phases.ts +15 -7
  101. package/src/resources/extensions/gsd/auto/session.ts +40 -0
  102. package/src/resources/extensions/gsd/auto-dispatch.ts +63 -4
  103. package/src/resources/extensions/gsd/auto-post-unit.ts +27 -12
  104. package/src/resources/extensions/gsd/auto-worktree.ts +218 -225
  105. package/src/resources/extensions/gsd/auto.ts +166 -43
  106. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
  107. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +26 -21
  108. package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
  109. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
  110. package/src/resources/extensions/gsd/crash-recovery.ts +177 -43
  111. package/src/resources/extensions/gsd/db/auto-workers.ts +273 -0
  112. package/src/resources/extensions/gsd/db/command-queue.ts +149 -0
  113. package/src/resources/extensions/gsd/db/milestone-leases.ts +274 -0
  114. package/src/resources/extensions/gsd/db/runtime-kv.ts +127 -0
  115. package/src/resources/extensions/gsd/db/unit-dispatches.ts +446 -0
  116. package/src/resources/extensions/gsd/db-writer.ts +113 -17
  117. package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
  118. package/src/resources/extensions/gsd/docs/COORDINATION.md +42 -0
  119. package/src/resources/extensions/gsd/doctor-proactive.ts +4 -0
  120. package/src/resources/extensions/gsd/doctor-runtime-checks.ts +24 -6
  121. package/src/resources/extensions/gsd/doctor.ts +10 -2
  122. package/src/resources/extensions/gsd/gsd-db.ts +354 -3
  123. package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
  124. package/src/resources/extensions/gsd/guided-flow.ts +152 -26
  125. package/src/resources/extensions/gsd/interrupted-session.ts +19 -12
  126. package/src/resources/extensions/gsd/metrics.ts +321 -1
  127. package/src/resources/extensions/gsd/paths.ts +67 -8
  128. package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
  129. package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
  130. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
  131. package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
  132. package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
  133. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
  134. package/src/resources/extensions/gsd/state.ts +44 -6
  135. package/src/resources/extensions/gsd/templates/project.md +10 -0
  136. package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
  137. package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +72 -0
  138. package/src/resources/extensions/gsd/tests/auto-loop-symlink-worktree.test.ts +190 -0
  139. package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
  140. package/src/resources/extensions/gsd/tests/auto-workers.test.ts +105 -0
  141. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
  142. package/src/resources/extensions/gsd/tests/command-queue.test.ts +141 -0
  143. package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +203 -0
  144. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +169 -59
  145. package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
  146. package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
  147. package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
  148. package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
  149. package/src/resources/extensions/gsd/tests/detect-stuck-respects-retry.test.ts +173 -0
  150. package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
  151. package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
  152. package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
  153. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
  154. package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
  155. package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
  156. package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
  157. package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
  158. package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
  159. package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
  160. package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -12
  161. package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +24 -10
  162. package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +35 -23
  163. package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +369 -0
  164. package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +72 -25
  165. package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +72 -25
  166. package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +9 -6
  167. package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
  168. package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
  169. package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
  170. package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
  171. package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
  172. package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
  173. package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +152 -0
  174. package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
  175. package/src/resources/extensions/gsd/tests/parallel-milestone-isolation.test.ts +106 -0
  176. package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
  177. package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
  178. package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
  179. package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +119 -0
  180. package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
  181. package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +58 -0
  182. package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +3 -17
  183. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
  184. package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +138 -16
  185. package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
  186. package/src/resources/extensions/gsd/tests/runtime-kv.test.ts +120 -0
  187. package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +133 -28
  188. package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +17 -0
  189. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +134 -0
  190. package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +434 -0
  191. package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
  192. package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +98 -0
  193. package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
  194. package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
  195. package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +247 -0
  196. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +41 -1
  197. package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
  198. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
  199. package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
  200. package/src/resources/extensions/gsd/tests/workspace.test.ts +196 -0
  201. package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
  202. package/src/resources/extensions/gsd/tests/write-gate.test.ts +94 -71
  203. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
  204. package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
  205. package/src/resources/extensions/gsd/workspace.ts +95 -0
  206. package/src/resources/extensions/gsd/worktree-resolver.ts +78 -2
  207. package/src/resources/extensions/gsd/write-intercept.ts +3 -3
  208. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +0 -213
  209. package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +0 -87
  210. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +0 -159
  211. /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_buildManifest.js +0 -0
  212. /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_ssgManifest.js +0 -0
@@ -76,6 +76,7 @@ import {
76
76
  nativeMergeAbort,
77
77
  } from "./native-git-bridge.js";
78
78
  import { gsdHome } from "./gsd-home.js";
79
+ import { type MilestoneScope, type GsdWorkspace, createWorkspace } from "./workspace.js";
79
80
 
80
81
  const PROJECT_PREFERENCES_FILE = "PREFERENCES.md";
81
82
  const LEGACY_PROJECT_PREFERENCES_FILE = "preferences.md";
@@ -254,14 +255,25 @@ function forceOverwriteAssessmentsWithVerdict(
254
255
 
255
256
  // ─── Module State ──────────────────────────────────────────────────────────
256
257
 
257
- /** Original project root before chdir into auto-worktree. */
258
- let originalBase: string | null = null;
258
+ /** Active workspace registry replaces the legacy `originalBase` singleton. */
259
+ let activeWorkspace: GsdWorkspace | null = null;
260
+
261
+ function setActiveWorkspace(ws: GsdWorkspace | null): void {
262
+ activeWorkspace = ws;
263
+ }
264
+
265
+ function getActiveWorkspace(): GsdWorkspace | null {
266
+ return activeWorkspace;
267
+ }
259
268
 
260
269
  function clearProjectRootStateFiles(basePath: string, milestoneId: string): void {
261
270
  const gsdDir = gsdRoot(basePath);
271
+ // Phase C pt 2: auto.lock removed from this list — the file is gone
272
+ // (migrated to the workers + unit_dispatches + runtime_kv tables). The
273
+ // remaining transient files (STATE.md, {MID}-META.json) are still
274
+ // worth removing on teardown.
262
275
  const transientFiles = [
263
276
  join(gsdDir, "STATE.md"),
264
- join(gsdDir, "auto.lock"),
265
277
  join(gsdDir, "milestones", milestoneId, `${milestoneId}-META.json`),
266
278
  ];
267
279
 
@@ -345,6 +357,41 @@ export const isSafeToAutoResolve = (filePath: string): boolean =>
345
357
  * gsd.db in the worktree so it rebuilds from fresh disk state (#853).
346
358
  * Non-fatal — sync failure should never block dispatch.
347
359
  */
360
+ /**
361
+ * Scope-typed variant of syncProjectRootToWorktree.
362
+ *
363
+ * Takes an explicit (rootScope, worktreeScope) pair where rootScope is the
364
+ * project root and worktreeScope is the auto-worktree. Direction is encoded
365
+ * in argument order. Asserts both scopes belong to the same workspace identity
366
+ * to prevent silent mismatch bugs.
367
+ */
368
+ export function syncProjectRootToWorktreeByScope(
369
+ rootScope: MilestoneScope,
370
+ worktreeScope: MilestoneScope,
371
+ ): void {
372
+ if (rootScope.workspace.identityKey !== worktreeScope.workspace.identityKey) {
373
+ throw new Error(
374
+ `syncProjectRootToWorktreeByScope: scope identity mismatch — ` +
375
+ `rootScope.identityKey="${rootScope.workspace.identityKey}" ` +
376
+ `worktreeScope.identityKey="${worktreeScope.workspace.identityKey}"`,
377
+ );
378
+ }
379
+ if (rootScope.milestoneId !== worktreeScope.milestoneId) {
380
+ throw new Error(
381
+ `syncProjectRootToWorktreeByScope: milestoneId mismatch — ` +
382
+ `rootScope.milestoneId="${rootScope.milestoneId}" worktreeScope.milestoneId="${worktreeScope.milestoneId}"`,
383
+ );
384
+ }
385
+ const projectRoot = rootScope.workspace.projectRoot;
386
+ const worktreePath_ = worktreeScope.workspace.worktreeRoot ?? worktreeScope.workspace.projectRoot;
387
+ const milestoneId = rootScope.milestoneId;
388
+ syncProjectRootToWorktree(projectRoot, worktreePath_, milestoneId);
389
+ }
390
+
391
+ /**
392
+ * @deprecated Use syncProjectRootToWorktreeByScope instead.
393
+ * TODO(C-future): remove once all callers migrated.
394
+ */
348
395
  export function syncProjectRootToWorktree(
349
396
  projectRoot: string,
350
397
  worktreePath_: string,
@@ -430,12 +477,44 @@ export function syncProjectRootToWorktree(
430
477
  }
431
478
  }
432
479
 
480
+ /**
481
+ * Scope-typed variant of syncStateToProjectRoot.
482
+ *
483
+ * Takes an explicit (worktreeScope, rootScope) pair. Direction is encoded in
484
+ * argument order (worktree → root). Asserts both scopes belong to the same
485
+ * workspace identity to prevent silent mismatch bugs.
486
+ */
487
+ export function syncStateToProjectRootByScope(
488
+ worktreeScope: MilestoneScope,
489
+ rootScope: MilestoneScope,
490
+ ): void {
491
+ if (worktreeScope.workspace.identityKey !== rootScope.workspace.identityKey) {
492
+ throw new Error(
493
+ `syncStateToProjectRootByScope: scope identity mismatch — ` +
494
+ `worktreeScope.identityKey="${worktreeScope.workspace.identityKey}" ` +
495
+ `rootScope.identityKey="${rootScope.workspace.identityKey}"`,
496
+ );
497
+ }
498
+ if (worktreeScope.milestoneId !== rootScope.milestoneId) {
499
+ throw new Error(
500
+ `syncStateToProjectRootByScope: milestoneId mismatch — ` +
501
+ `worktreeScope.milestoneId="${worktreeScope.milestoneId}" rootScope.milestoneId="${rootScope.milestoneId}"`,
502
+ );
503
+ }
504
+ const worktreePath_ = worktreeScope.workspace.worktreeRoot ?? worktreeScope.workspace.projectRoot;
505
+ const projectRoot = rootScope.workspace.projectRoot;
506
+ const milestoneId = worktreeScope.milestoneId;
507
+ syncStateToProjectRoot(worktreePath_, projectRoot, milestoneId);
508
+ }
509
+
433
510
  /**
434
511
  * Sync worktree diagnostics from worktree to project root.
435
512
  * Only runs when inside an auto-worktree (worktreePath differs from projectRoot).
436
513
  * DB/project-root state remains authoritative; markdown projections are not
437
514
  * copied from the worktree back to the project root.
438
515
  * Non-fatal — sync failure should never block dispatch.
516
+ * @deprecated Use syncStateToProjectRootByScope instead.
517
+ * TODO(C-future): remove once all callers migrated.
439
518
  */
440
519
  export function syncStateToProjectRoot(
441
520
  worktreePath_: string,
@@ -625,6 +704,30 @@ export function cleanStaleRuntimeUnits(
625
704
 
626
705
  // ─── Worktree ↔ Main Repo Sync (#1311) ──────────────────────────────────────
627
706
 
707
+ /**
708
+ * Scope-typed variant of syncGsdStateToWorktree.
709
+ *
710
+ * Takes an explicit (rootScope, worktreeScope) pair. Note: milestoneId is not
711
+ * used by syncGsdStateToWorktree — this variant only requires workspace
712
+ * identity. Asserts both scopes belong to the same workspace identity to
713
+ * prevent silent mismatch bugs.
714
+ */
715
+ export function syncGsdStateToWorktreeByScope(
716
+ rootScope: MilestoneScope,
717
+ worktreeScope: MilestoneScope,
718
+ ): { synced: string[] } {
719
+ if (rootScope.workspace.identityKey !== worktreeScope.workspace.identityKey) {
720
+ throw new Error(
721
+ `syncGsdStateToWorktreeByScope: scope identity mismatch — ` +
722
+ `rootScope.identityKey="${rootScope.workspace.identityKey}" ` +
723
+ `worktreeScope.identityKey="${worktreeScope.workspace.identityKey}"`,
724
+ );
725
+ }
726
+ const mainBasePath = rootScope.workspace.projectRoot;
727
+ const worktreePath_ = worktreeScope.workspace.worktreeRoot ?? worktreeScope.workspace.projectRoot;
728
+ return syncGsdStateToWorktree(mainBasePath, worktreePath_);
729
+ }
730
+
628
731
  /**
629
732
  * Sync .gsd/ state from the main repo into the worktree.
630
733
  *
@@ -639,6 +742,8 @@ export function cleanStaleRuntimeUnits(
639
742
  * Only adds missing content — never overwrites existing files in the worktree.
640
743
  * Worktree files are compatibility projections; DB/project root remains
641
744
  * authoritative for runtime state.
745
+ * @deprecated Use syncGsdStateToWorktreeByScope instead.
746
+ * TODO(C-future): remove once all callers migrated.
642
747
  */
643
748
  export function syncGsdStateToWorktree(
644
749
  mainBasePath: string,
@@ -1026,103 +1131,13 @@ export function enterBranchModeForMilestone(
1026
1131
  * Forward-merge plan checkbox state from the project root into a freshly
1027
1132
  * re-attached worktree (#778).
1028
1133
  *
1029
- * When auto-mode stops via crash (not graceful stop), the milestone branch
1030
- * HEAD may be behind the filesystem state at the project root because
1031
- * syncStateToProjectRoot() runs after every task completion but the final
1032
- * git commit may not have happened before the crash. On restart the worktree
1033
- * is re-attached to the branch HEAD, which has [ ] for the crashed task,
1034
- * causing verifyExpectedArtifact() to fail and triggering an infinite
1035
- * dispatch/skip loop.
1036
- *
1037
- * Fix: after re-attaching, read every *.md plan file in the milestone
1038
- * directory at the project root and apply any [x] checkbox states that are
1039
- * ahead of the worktree version (forward-only: never downgrade [x] → [ ]).
1040
- *
1041
- * This is forward-only compatibility for legacy projection copies. The DB
1042
- * remains authoritative; this never downgrades checked boxes in a local
1043
- * worktree projection.
1134
+ * Phase C: deleted. Writers in workflow-projections.ts, triage-resolution.ts,
1135
+ * rule-registry.ts, and auto-post-unit.ts now route through
1136
+ * s.canonicalProjectRoot, so non-symlinked worktrees no longer need a local
1137
+ * .gsd/ projection the project-root .gsd/ is the only authoritative source
1138
+ * for both reads and writes. copyPlanningArtifacts and reconcilePlanCheckboxes
1139
+ * (both formerly here) became dead.
1044
1140
  */
1045
- function reconcilePlanCheckboxes(
1046
- projectRoot: string,
1047
- wtPath: string,
1048
- milestoneId: string,
1049
- ): void {
1050
- const srcMilestone = join(projectRoot, ".gsd", "milestones", milestoneId);
1051
- const dstMilestone = join(wtPath, ".gsd", "milestones", milestoneId);
1052
- if (!existsSync(srcMilestone) || !existsSync(dstMilestone)) return;
1053
-
1054
- // Walk all markdown files in the milestone directory (plans, summaries, etc.)
1055
- function walkMd(dir: string): string[] {
1056
- const results: string[] = [];
1057
- try {
1058
- for (const entry of readdirSync(dir, { withFileTypes: true })) {
1059
- const full = join(dir, entry.name);
1060
- if (entry.isDirectory()) {
1061
- results.push(...walkMd(full));
1062
- } else if (entry.isFile() && entry.name.endsWith(".md")) {
1063
- results.push(full);
1064
- }
1065
- }
1066
- } catch (err) {
1067
- /* non-fatal */
1068
- logWarning("worktree", `walkMd directory read failed: ${err instanceof Error ? err.message : String(err)}`);
1069
- }
1070
- return results;
1071
- }
1072
-
1073
- for (const srcFile of walkMd(srcMilestone)) {
1074
- const rel = srcFile.slice(srcMilestone.length);
1075
- const dstFile = dstMilestone + rel;
1076
- if (!existsSync(dstFile)) continue; // only reconcile existing files
1077
-
1078
- let srcContent: string;
1079
- let dstContent: string;
1080
- try {
1081
- srcContent = readFileSync(srcFile, "utf-8");
1082
- dstContent = readFileSync(dstFile, "utf-8");
1083
- } catch (e) {
1084
- logWarning("worktree", `reconcilePlanCheckboxes read failed: ${(e as Error).message}`);
1085
- continue;
1086
- }
1087
-
1088
- if (srcContent === dstContent) continue;
1089
-
1090
- // Extract all checked task IDs from the source (project root)
1091
- // Pattern: - [x] **T<id>: or - [x] **S<id>: (case-insensitive x)
1092
- const checkedRe = /^- \[[xX]\] \*\*([TS]\d+):/gm;
1093
- const srcChecked = new Set<string>();
1094
- for (const m of srcContent.matchAll(checkedRe)) srcChecked.add(m[1]);
1095
-
1096
- if (srcChecked.size === 0) continue;
1097
-
1098
- // Forward-apply: replace [ ] → [x] for any IDs that are checked in src
1099
- let updated = dstContent;
1100
- let changed = false;
1101
- for (const id of srcChecked) {
1102
- const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1103
- const uncheckedRe = new RegExp(
1104
- `^(- )\\[ \\]( \\*\\*${escapedId}:)`,
1105
- "gm",
1106
- );
1107
- if (uncheckedRe.test(updated)) {
1108
- updated = updated.replace(
1109
- new RegExp(`^(- )\\[ \\]( \\*\\*${escapedId}:)`, "gm"),
1110
- "$1[x]$2",
1111
- );
1112
- changed = true;
1113
- }
1114
- }
1115
-
1116
- if (changed) {
1117
- try {
1118
- atomicWriteSync(dstFile, updated, "utf-8");
1119
- } catch (err) {
1120
- /* non-fatal */
1121
- logWarning("worktree", `plan checkbox reconcile write failed: ${err instanceof Error ? err.message : String(err)}`);
1122
- }
1123
- }
1124
- }
1125
- }
1126
1141
 
1127
1142
  export function createAutoWorktree(
1128
1143
  basePath: string,
@@ -1180,32 +1195,15 @@ export function createAutoWorktree(
1180
1195
  });
1181
1196
  }
1182
1197
 
1183
- // Copy .gsd/ planning artifacts from the source repo into the new worktree.
1184
- // Worktrees are fresh git checkouts untracked files don't carry over.
1185
- // Planning artifacts may be untracked if the project's .gitignore had a
1186
- // blanket .gsd/ rule (pre-v2.14.0). Without this copy, auto-mode loops
1187
- // on plan-slice because the plan file doesn't exist in the worktree.
1188
- //
1189
- // IMPORTANT: Skip when re-attaching to an existing branch (#759).
1190
- // The branch checkout already has committed artifacts with correct state
1191
- // (e.g. [x] for completed slices). Copying from the project root would
1192
- // overwrite them with stale data ([ ] checkboxes) because the root is
1193
- // not always fully synced.
1194
- if (!branchExists) {
1195
- copyPlanningArtifacts(basePath, info.path);
1196
- } else {
1197
- // Re-attaching to an existing branch: forward-merge any plan checkpoint
1198
- // state from the project root into the worktree (#778).
1199
- //
1200
- // If auto-mode stopped via crash, the milestone branch HEAD may lag behind
1201
- // the project root filesystem because syncStateToProjectRoot() ran after
1202
- // task completion but the auto-commit never fired. On restart the worktree
1203
- // is re-created from the branch HEAD (which has [ ] for the crashed task),
1204
- // causing verifyExpectedArtifact() to return false → stale-key eviction →
1205
- // infinite dispatch/skip loop. Reconciling here ensures the worktree sees
1206
- // the same [x] state that syncStateToProjectRoot() wrote to the root.
1207
- reconcilePlanCheckboxes(basePath, info.path, milestoneId);
1208
- }
1198
+ // Phase C: copyPlanningArtifacts and reconcilePlanCheckboxes were
1199
+ // deleted. Both addressed the same problem (worktree-local .gsd/
1200
+ // projection lagging behind project-root state) by maintaining a stale
1201
+ // copy. Now that auto-mode writers in workflow-projections.ts,
1202
+ // triage-resolution.ts, rule-registry.ts, and auto-post-unit.ts route
1203
+ // through s.canonicalProjectRoot, the worktree never needs a local
1204
+ // .gsd/ both reads and writes converge on the project-root .gsd/.
1205
+ // The original concerns (#759, #778) no longer apply because there is
1206
+ // no second copy to drift.
1209
1207
 
1210
1208
  // Run user-configured post-create hook (#597) — e.g. copy .env, symlink assets
1211
1209
  const hookError = runWorktreePostCreateHook(basePath, info.path);
@@ -1218,10 +1216,10 @@ export function createAutoWorktree(
1218
1216
 
1219
1217
  try {
1220
1218
  process.chdir(info.path);
1221
- originalBase = basePath;
1219
+ setActiveWorkspace(createWorkspace(basePath));
1222
1220
  } catch (err) {
1223
1221
  // If chdir fails, the worktree was created but we couldn't enter it.
1224
- // Don't store originalBase -- caller can retry or clean up.
1222
+ // Don't set activeWorkspace -- caller can retry or clean up.
1225
1223
  throw new GSDError(
1226
1224
  GSD_IO_ERROR,
1227
1225
  `Auto-worktree created at ${info.path} but chdir failed: ${err instanceof Error ? err.message : String(err)}`,
@@ -1232,60 +1230,13 @@ export function createAutoWorktree(
1232
1230
  return info.path;
1233
1231
  }
1234
1232
 
1235
- /**
1236
- * Copy .gsd/ planning artifacts from source repo to a new worktree.
1237
- * Copies milestones/, DECISIONS.md, REQUIREMENTS.md, PROJECT.md, QUEUE.md,
1238
- * STATE.md, KNOWLEDGE.md, and OVERRIDES.md.
1239
- * Skips runtime files (auto.lock, metrics.json, etc.) and the worktrees/ dir.
1240
- * Best-effort failures are non-fatal since auto-mode can recreate artifacts.
1241
- */
1242
- function copyPlanningArtifacts(srcBase: string, wtPath: string): void {
1243
- const srcGsd = join(srcBase, ".gsd");
1244
- const dstGsd = join(wtPath, ".gsd");
1245
- if (!existsSync(srcGsd)) return;
1246
- if (isSamePath(srcGsd, dstGsd)) return;
1247
-
1248
- // Copy milestones/ directory (planning files, roadmaps, plans, research)
1249
- safeCopyRecursive(join(srcGsd, "milestones"), join(dstGsd, "milestones"), {
1250
- force: true,
1251
- filter: (src) => !src.endsWith("-META.json"),
1252
- });
1253
-
1254
- // Copy top-level planning files
1255
- for (const file of [
1256
- "DECISIONS.md",
1257
- "REQUIREMENTS.md",
1258
- "PROJECT.md",
1259
- "QUEUE.md",
1260
- "STATE.md",
1261
- "KNOWLEDGE.md",
1262
- "OVERRIDES.md",
1263
- "mcp.json",
1264
- ]) {
1265
- safeCopy(join(srcGsd, file), join(dstGsd, file), { force: true });
1266
- }
1267
-
1268
- // Seed canonical PREFERENCES.md when available; fall back to legacy lowercase.
1269
- if (existsSync(join(srcGsd, PROJECT_PREFERENCES_FILE))) {
1270
- safeCopy(
1271
- join(srcGsd, PROJECT_PREFERENCES_FILE),
1272
- join(dstGsd, PROJECT_PREFERENCES_FILE),
1273
- { force: true },
1274
- );
1275
- } else if (existsSync(join(srcGsd, LEGACY_PROJECT_PREFERENCES_FILE))) {
1276
- safeCopy(
1277
- join(srcGsd, LEGACY_PROJECT_PREFERENCES_FILE),
1278
- join(dstGsd, LEGACY_PROJECT_PREFERENCES_FILE),
1279
- { force: true },
1280
- );
1281
- }
1282
-
1283
- // Shared WAL (R012): worktrees use the project root's DB directly.
1284
- // No longer copy gsd.db into the worktree — the DB path resolver in
1285
- // ensureDbOpen() detects the worktree location and opens the root DB.
1286
- // Compat note: reconcileWorktreeDb() in mergeMilestoneToMain handles
1287
- // worktrees that already have a local gsd.db from before this change.
1288
- }
1233
+ // Phase C: copyPlanningArtifacts removed. Planning artifacts now live
1234
+ // only at the project root .gsd/; auto-mode writers (workflow-projections,
1235
+ // triage-resolution, rule-registry, regenerateIfMissing,
1236
+ // resolveHookArtifactPath) all route through s.canonicalProjectRoot.
1237
+ // Worktrees are pure git checkouts they no longer maintain a parallel
1238
+ // .gsd/ projection. The gsd.db has always lived at the project root via
1239
+ // the shared-WAL R012 contract; that is unchanged.
1289
1240
 
1290
1241
  /**
1291
1242
  * Teardown an auto-worktree: chdir back to original base, then remove
@@ -1302,48 +1253,87 @@ export function teardownAutoWorktree(
1302
1253
  const { preserveBranch = false } = opts;
1303
1254
  const previousCwd = process.cwd();
1304
1255
 
1256
+ // Wrap the entire teardown body in a single try/finally so activeWorkspace
1257
+ // is ALWAYS cleared — even if process.chdir throws (e.g. originalBasePath
1258
+ // was deleted before teardown ran). Previously the finally only covered
1259
+ // removeWorktree, leaving the registry stale on a chdir failure (H3 fix).
1305
1260
  try {
1306
- process.chdir(originalBasePath);
1307
- originalBase = null;
1308
- } catch (err) {
1309
- throw new GSDError(
1310
- GSD_IO_ERROR,
1311
- `Failed to chdir back to ${originalBasePath} during teardown: ${err instanceof Error ? err.message : String(err)}`,
1312
- );
1313
- }
1261
+ try {
1262
+ process.chdir(originalBasePath);
1263
+ } catch (err) {
1264
+ throw new GSDError(
1265
+ GSD_IO_ERROR,
1266
+ `Failed to chdir back to ${originalBasePath} during teardown: ${err instanceof Error ? err.message : String(err)}`,
1267
+ );
1268
+ }
1314
1269
 
1315
- nudgeGitBranchCache(previousCwd);
1316
- removeWorktree(originalBasePath, milestoneId, {
1317
- branch,
1318
- deleteBranch: !preserveBranch,
1319
- });
1320
-
1321
- // Verify cleanup succeeded — warn if the worktree directory is still on disk.
1322
- // On Windows, bash-based cleanup can silently fail when paths contain
1323
- // backslashes (#1436), leaving ~1 GB+ orphaned directories.
1324
- const wtDir = worktreePath(originalBasePath, milestoneId);
1325
- if (existsSync(wtDir)) {
1326
- logWarning(
1327
- "reconcile",
1328
- `Worktree directory still exists after teardown: ${wtDir}. ` +
1329
- `This is likely an orphaned directory consuming disk space. ` +
1330
- `Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`,
1331
- { worktree: milestoneId },
1332
- );
1333
- // Attempt a direct filesystem removal as a fallback — but ONLY if the
1334
- // path is safely inside .gsd/worktrees/ to prevent #2365 data loss.
1335
- if (isInsideWorktreesDir(originalBasePath, wtDir)) {
1270
+ // Mirror cleanup steps from mergeMilestoneToMain abort path:
1271
+
1272
+ // 1. Remove transient state files (STATE.md, auto.lock, {MID}-META.json).
1273
+ // Non-fatal — must not block teardown.
1274
+ try {
1275
+ clearProjectRootStateFiles(originalBasePath, milestoneId);
1276
+ } catch (err) {
1277
+ logWarning("worktree", `clearProjectRootStateFiles failed during teardown: ${err instanceof Error ? err.message : String(err)}`);
1278
+ }
1279
+
1280
+ // 2. Reconcile worktree-local gsd.db into project root DB if both exist.
1281
+ // Non-fatal — handles legacy worktrees that have a local copy.
1282
+ if (isDbAvailable()) {
1336
1283
  try {
1337
- rmSync(wtDir, { recursive: true, force: true });
1284
+ const contract = resolveGsdPathContract(previousCwd, originalBasePath);
1285
+ const worktreeDbPath = join(contract.worktreeGsd ?? join(previousCwd, ".gsd"), "gsd.db");
1286
+ const mainDbPath = contract.projectDb;
1287
+ if (existsSync(worktreeDbPath) && !isSamePath(worktreeDbPath, mainDbPath)) {
1288
+ reconcileWorktreeDb(mainDbPath, worktreeDbPath);
1289
+ }
1338
1290
  } catch (err) {
1339
- // Non-fatal — the warning above tells the user how to clean up
1340
- logWarning("worktree", `worktree directory removal failed: ${err instanceof Error ? err.message : String(err)}`);
1291
+ /* non-fatal */
1292
+ logError("worktree", `DB reconciliation failed during teardown: ${err instanceof Error ? err.message : String(err)}`);
1341
1293
  }
1342
- } else {
1343
- console.error(
1344
- `[GSD] REFUSING fallback rmSync — path is outside .gsd/worktrees/: ${wtDir}`,
1294
+ }
1295
+
1296
+ nudgeGitBranchCache(previousCwd);
1297
+
1298
+ // 3. Remove the worktree. Errors propagate naturally — the outer finally
1299
+ // ensures activeWorkspace is cleared regardless.
1300
+ removeWorktree(originalBasePath, milestoneId, {
1301
+ branch,
1302
+ deleteBranch: !preserveBranch,
1303
+ });
1304
+
1305
+ // Verify cleanup succeeded — warn if the worktree directory is still on disk.
1306
+ // On Windows, bash-based cleanup can silently fail when paths contain
1307
+ // backslashes (#1436), leaving ~1 GB+ orphaned directories.
1308
+ const wtDir = worktreePath(originalBasePath, milestoneId);
1309
+ if (existsSync(wtDir)) {
1310
+ logWarning(
1311
+ "reconcile",
1312
+ `Worktree directory still exists after teardown: ${wtDir}. ` +
1313
+ `This is likely an orphaned directory consuming disk space. ` +
1314
+ `Remove it manually with: rm -rf "${wtDir.replaceAll("\\", "/")}"`,
1315
+ { worktree: milestoneId },
1345
1316
  );
1317
+ // Attempt a direct filesystem removal as a fallback — but ONLY if the
1318
+ // path is safely inside .gsd/worktrees/ to prevent #2365 data loss.
1319
+ if (isInsideWorktreesDir(originalBasePath, wtDir)) {
1320
+ try {
1321
+ rmSync(wtDir, { recursive: true, force: true });
1322
+ } catch (err) {
1323
+ // Non-fatal — the warning above tells the user how to clean up
1324
+ logWarning("worktree", `worktree directory removal failed: ${err instanceof Error ? err.message : String(err)}`);
1325
+ }
1326
+ } else {
1327
+ console.error(
1328
+ `[GSD] REFUSING fallback rmSync — path is outside .gsd/worktrees/: ${wtDir}`,
1329
+ );
1330
+ }
1346
1331
  }
1332
+ } finally {
1333
+ // Clear module state unconditionally — regardless of which step above
1334
+ // failed. A stale activeWorkspace causes getActiveAutoWorktreeContext()
1335
+ // to return wrong data for subsequent operations.
1336
+ setActiveWorkspace(null);
1347
1337
  }
1348
1338
  }
1349
1339
 
@@ -1356,8 +1346,9 @@ export function isInAutoWorktree(basePath: string): boolean {
1356
1346
  const targetPath = isGsdWorktreePath(basePath) ? basePath : process.cwd();
1357
1347
  if (!isGsdWorktreePath(targetPath)) return false;
1358
1348
 
1359
- const projectRoot = resolveWorktreeProjectRoot(basePath, originalBase);
1360
- const targetProjectRoot = resolveWorktreeProjectRoot(targetPath, originalBase);
1349
+ const storedBase = getAutoWorktreeOriginalBase();
1350
+ const projectRoot = resolveWorktreeProjectRoot(basePath, storedBase);
1351
+ const targetProjectRoot = resolveWorktreeProjectRoot(targetPath, storedBase);
1361
1352
  if (
1362
1353
  normalizeWorktreePathForCompare(projectRoot) !==
1363
1354
  normalizeWorktreePathForCompare(targetProjectRoot)
@@ -1453,7 +1444,7 @@ export function enterAutoWorktree(
1453
1444
 
1454
1445
  try {
1455
1446
  process.chdir(p);
1456
- originalBase = basePath;
1447
+ setActiveWorkspace(createWorkspace(basePath));
1457
1448
  } catch (err) {
1458
1449
  throw new GSDError(
1459
1450
  GSD_IO_ERROR,
@@ -1470,11 +1461,11 @@ export function enterAutoWorktree(
1470
1461
  * Returns null if not currently in an auto-worktree.
1471
1462
  */
1472
1463
  export function getAutoWorktreeOriginalBase(): string | null {
1473
- return originalBase;
1464
+ return getActiveWorkspace()?.projectRoot ?? null;
1474
1465
  }
1475
1466
 
1476
1467
  export function _resetAutoWorktreeOriginalBaseForTests(): void {
1477
- originalBase = null;
1468
+ setActiveWorkspace(null);
1478
1469
  }
1479
1470
 
1480
1471
  export function getActiveAutoWorktreeContext(): {
@@ -1482,7 +1473,9 @@ export function getActiveAutoWorktreeContext(): {
1482
1473
  worktreeName: string;
1483
1474
  branch: string;
1484
1475
  } | null {
1485
- if (!originalBase) return null;
1476
+ const ws = getActiveWorkspace();
1477
+ if (!ws) return null;
1478
+ const originalBase = ws.projectRoot;
1486
1479
  const cwd = process.cwd();
1487
1480
  if (!isGsdWorktreePath(cwd)) return null;
1488
1481
  const cwdProjectRoot = resolveWorktreeProjectRoot(cwd, originalBase);
@@ -1563,11 +1556,11 @@ export function mergeMilestoneToMain(
1563
1556
  // integration branch captures dirty files from OTHER milestones under a
1564
1557
  // misleading commit message, contaminating the main branch (#2929).
1565
1558
  //
1566
- // When originalBase is null (branch mode, no worktree), autoCommitDirtyState
1559
+ // When activeWorkspace is null (branch mode, no worktree), autoCommitDirtyState
1567
1560
  // runs unconditionally — the caller is responsible for cwd placement.
1568
1561
  {
1569
1562
  let shouldAutoCommit = true;
1570
- if (originalBase !== null) {
1563
+ if (getActiveWorkspace() !== null) {
1571
1564
  try {
1572
1565
  const currentBranch = nativeGetCurrentBranch(worktreeCwd);
1573
1566
  shouldAutoCommit = currentBranch === milestoneBranch;
@@ -2302,7 +2295,7 @@ export function mergeMilestoneToMain(
2302
2295
  }
2303
2296
 
2304
2297
  // 14. Clear module state
2305
- originalBase = null;
2298
+ setActiveWorkspace(null);
2306
2299
  nudgeGitBranchCache(previousCwd);
2307
2300
 
2308
2301
  // 15. Anchor cwd at the project root on success-return. Step 12 removed