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.
- package/README.md +5 -7
- package/dist/help-text.js +1 -1
- package/dist/resource-loader.js +6 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/gsd/auto/detect-stuck.js +41 -5
- package/dist/resources/extensions/gsd/auto/loop.js +235 -36
- package/dist/resources/extensions/gsd/auto/phases.js +14 -7
- package/dist/resources/extensions/gsd/auto/session.js +36 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +49 -4
- package/dist/resources/extensions/gsd/auto-post-unit.js +26 -12
- package/dist/resources/extensions/gsd/auto-worktree.js +185 -201
- package/dist/resources/extensions/gsd/auto.js +139 -49
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +26 -20
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +67 -55
- package/dist/resources/extensions/gsd/crash-recovery.js +160 -47
- package/dist/resources/extensions/gsd/db/auto-workers.js +227 -0
- package/dist/resources/extensions/gsd/db/command-queue.js +105 -0
- package/dist/resources/extensions/gsd/db/milestone-leases.js +210 -0
- package/dist/resources/extensions/gsd/db/runtime-kv.js +91 -0
- package/dist/resources/extensions/gsd/db/unit-dispatches.js +322 -0
- package/dist/resources/extensions/gsd/db-writer.js +96 -16
- package/dist/resources/extensions/gsd/delegation-policy.js +155 -0
- package/dist/resources/extensions/gsd/docs/COORDINATION.md +42 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +4 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +22 -6
- package/dist/resources/extensions/gsd/doctor.js +12 -2
- package/dist/resources/extensions/gsd/gsd-db.js +355 -3
- package/dist/resources/extensions/gsd/guided-flow-queue.js +1 -1
- package/dist/resources/extensions/gsd/guided-flow.js +116 -26
- package/dist/resources/extensions/gsd/interrupted-session.js +18 -15
- package/dist/resources/extensions/gsd/metrics.js +287 -1
- package/dist/resources/extensions/gsd/paths.js +79 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/dist/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/dist/resources/extensions/gsd/state.js +21 -6
- package/dist/resources/extensions/gsd/templates/project.md +10 -0
- package/dist/resources/extensions/gsd/workflow-mcp.js +2 -2
- package/dist/resources/extensions/gsd/workspace.js +59 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +79 -2
- package/dist/resources/extensions/gsd/write-intercept.js +3 -3
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/mcp-server/README.md +2 -11
- package/packages/mcp-server/dist/remote-questions.d.ts +27 -0
- package/packages/mcp-server/dist/remote-questions.d.ts.map +1 -1
- package/packages/mcp-server/dist/remote-questions.js +28 -0
- package/packages/mcp-server/dist/remote-questions.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts +28 -0
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +94 -4
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/src/mcp-server.test.ts +226 -0
- package/packages/mcp-server/src/remote-questions.test.ts +103 -0
- package/packages/mcp-server/src/remote-questions.ts +35 -0
- package/packages/mcp-server/src/server.ts +129 -6
- package/packages/mcp-server/src/workflow-tools.ts +1 -1
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/src/resources/extensions/gsd/auto/detect-stuck.ts +37 -5
- package/src/resources/extensions/gsd/auto/loop.ts +263 -41
- package/src/resources/extensions/gsd/auto/phases.ts +15 -7
- package/src/resources/extensions/gsd/auto/session.ts +40 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +63 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +27 -12
- package/src/resources/extensions/gsd/auto-worktree.ts +218 -225
- package/src/resources/extensions/gsd/auto.ts +166 -43
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +26 -21
- package/src/resources/extensions/gsd/bootstrap/tests/write-gate-basepath.test.ts +103 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -55
- package/src/resources/extensions/gsd/crash-recovery.ts +177 -43
- package/src/resources/extensions/gsd/db/auto-workers.ts +273 -0
- package/src/resources/extensions/gsd/db/command-queue.ts +149 -0
- package/src/resources/extensions/gsd/db/milestone-leases.ts +274 -0
- package/src/resources/extensions/gsd/db/runtime-kv.ts +127 -0
- package/src/resources/extensions/gsd/db/unit-dispatches.ts +446 -0
- package/src/resources/extensions/gsd/db-writer.ts +113 -17
- package/src/resources/extensions/gsd/delegation-policy.ts +197 -0
- package/src/resources/extensions/gsd/docs/COORDINATION.md +42 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +4 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +24 -6
- package/src/resources/extensions/gsd/doctor.ts +10 -2
- package/src/resources/extensions/gsd/gsd-db.ts +354 -3
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -1
- package/src/resources/extensions/gsd/guided-flow.ts +152 -26
- package/src/resources/extensions/gsd/interrupted-session.ts +19 -12
- package/src/resources/extensions/gsd/metrics.ts +321 -1
- package/src/resources/extensions/gsd/paths.ts +67 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -3
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +8 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-project.md +22 -7
- package/src/resources/extensions/gsd/prompts/guided-discuss-requirements.md +6 -2
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +8 -1
- package/src/resources/extensions/gsd/state.ts +44 -6
- package/src/resources/extensions/gsd/templates/project.md +10 -0
- package/src/resources/extensions/gsd/tests/auto-discuss-milestone-deadlock-4973.test.ts +14 -14
- package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/auto-loop-symlink-worktree.test.ts +190 -0
- package/src/resources/extensions/gsd/tests/auto-session-scope.test.ts +331 -0
- package/src/resources/extensions/gsd/tests/auto-workers.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +176 -0
- package/src/resources/extensions/gsd/tests/command-queue.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/crash-recovery-via-db.test.ts +203 -0
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +169 -59
- package/src/resources/extensions/gsd/tests/db-writer-path-containment.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/db-writer-root-artifact.test.ts +221 -0
- package/src/resources/extensions/gsd/tests/db-writer-scope.test.ts +230 -0
- package/src/resources/extensions/gsd/tests/delegation-policy.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/detect-stuck-respects-retry.test.ts +173 -0
- package/src/resources/extensions/gsd/tests/dispatch-backgroundable-annotation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/draft-promotion.test.ts +3 -23
- package/src/resources/extensions/gsd/tests/gate-1b-orphan-discrimination.test.ts +193 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound-corrections.test.ts +246 -0
- package/src/resources/extensions/gsd/tests/gate-1b-recovery-bound.test.ts +218 -0
- package/src/resources/extensions/gsd/tests/gsd-db-failed-open-restore.test.ts +117 -0
- package/src/resources/extensions/gsd/tests/gsd-db-workspace-scope.test.ts +226 -0
- package/src/resources/extensions/gsd/tests/gsd-root-canonical.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/gsd-root-home-guard.test.ts +68 -5
- package/src/resources/extensions/gsd/tests/guided-flow-prompt-consolidation.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -12
- package/src/resources/extensions/gsd/tests/integration/doctor-proactive.test.ts +24 -10
- package/src/resources/extensions/gsd/tests/integration/doctor-runtime.test.ts +35 -23
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +369 -0
- package/src/resources/extensions/gsd/tests/interrupted-session-auto.test.ts +72 -25
- package/src/resources/extensions/gsd/tests/interrupted-session-ui.test.ts +72 -25
- package/src/resources/extensions/gsd/tests/memory-pressure-stuck-state.test.ts +9 -6
- package/src/resources/extensions/gsd/tests/metrics-atomic-merge.test.ts +222 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-hardening.test.ts +400 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-not-acquired.test.ts +141 -0
- package/src/resources/extensions/gsd/tests/metrics-lock-retry-sleep.test.ts +287 -0
- package/src/resources/extensions/gsd/tests/metrics-prune-cache-invalidation.test.ts +149 -0
- package/src/resources/extensions/gsd/tests/metrics-scope.test.ts +378 -0
- package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +329 -0
- package/src/resources/extensions/gsd/tests/parallel-milestone-isolation.test.ts +106 -0
- package/src/resources/extensions/gsd/tests/path-cache-decoupled.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/path-normalization-unified.test.ts +175 -0
- package/src/resources/extensions/gsd/tests/paths-cache.test.ts +170 -0
- package/src/resources/extensions/gsd/tests/paused-session-via-db.test.ts +119 -0
- package/src/resources/extensions/gsd/tests/pending-autostart-scope.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +3 -17
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +150 -7
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +138 -16
- package/src/resources/extensions/gsd/tests/resume-missing-worktree-warning.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/runtime-kv.test.ts +120 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-completion.test.ts +133 -28
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +134 -0
- package/src/resources/extensions/gsd/tests/sync-layer-scope.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +162 -0
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/unit-dispatches.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +41 -1
- package/src/resources/extensions/gsd/tests/validator-scope-parity.test.ts +239 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/workspace.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/write-gate-predicates.test.ts +35 -35
- package/src/resources/extensions/gsd/tests/write-gate.test.ts +94 -71
- package/src/resources/extensions/gsd/tests/write-intercept.test.ts +1 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +2 -2
- package/src/resources/extensions/gsd/workspace.ts +95 -0
- package/src/resources/extensions/gsd/worktree-resolver.ts +78 -2
- package/src/resources/extensions/gsd/write-intercept.ts +3 -3
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +0 -213
- package/src/resources/extensions/gsd/tests/auto-stale-lock-self-kill.test.ts +0 -87
- package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +0 -159
- /package/dist/web/standalone/.next/static/{oZGTPvJBQX_IDKKnuV8Bt → Y5UeGFkXTYM9WIQOWHkot}/_buildManifest.js +0 -0
- /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
|
-
/**
|
|
258
|
-
let
|
|
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
|
-
*
|
|
1030
|
-
*
|
|
1031
|
-
*
|
|
1032
|
-
*
|
|
1033
|
-
*
|
|
1034
|
-
*
|
|
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
|
-
//
|
|
1184
|
-
//
|
|
1185
|
-
//
|
|
1186
|
-
//
|
|
1187
|
-
//
|
|
1188
|
-
//
|
|
1189
|
-
//
|
|
1190
|
-
// The
|
|
1191
|
-
//
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
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
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
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
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1340
|
-
|
|
1291
|
+
/* non-fatal */
|
|
1292
|
+
logError("worktree", `DB reconciliation failed during teardown: ${err instanceof Error ? err.message : String(err)}`);
|
|
1341
1293
|
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
|
|
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
|
|
1360
|
-
const
|
|
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
|
-
|
|
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
|
|
1464
|
+
return getActiveWorkspace()?.projectRoot ?? null;
|
|
1474
1465
|
}
|
|
1475
1466
|
|
|
1476
1467
|
export function _resetAutoWorktreeOriginalBaseForTests(): void {
|
|
1477
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
2298
|
+
setActiveWorkspace(null);
|
|
2306
2299
|
nudgeGitBranchCache(previousCwd);
|
|
2307
2300
|
|
|
2308
2301
|
// 15. Anchor cwd at the project root on success-return. Step 12 removed
|