diff --git a/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts b/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts index 1cfe2c82b..b674112db 100644 --- a/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +++ b/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts @@ -57,6 +57,18 @@ export function resolveProjectRootDbPath(basePath: string): string { } } + // External-state layout: ~/.gsd/projects//worktrees//... + // Resolve to ~/.gsd/projects//gsd.db (the canonical project DB) (#2952). + const extRe = /[/\\]\.gsd[/\\]projects[/\\][a-f0-9]+[/\\]worktrees(?:[/\\]|$)/; + const extMatch = extRe.exec(basePath); + if (extMatch) { + const matchStr = extMatch[0]; + // Find the "/worktrees" portion within the match and slice up to it + const wtIdx = matchStr.search(/[/\\]worktrees(?:[/\\]|$)/); + const projectStateRoot = basePath.slice(0, extMatch.index + wtIdx); + return join(projectStateRoot, "gsd.db"); + } + return join(basePath, ".gsd", "gsd.db"); } diff --git a/src/resources/extensions/gsd/tests/shared-wal.test.ts b/src/resources/extensions/gsd/tests/shared-wal.test.ts index 6fb425854..8bf0972dd 100644 --- a/src/resources/extensions/gsd/tests/shared-wal.test.ts +++ b/src/resources/extensions/gsd/tests/shared-wal.test.ts @@ -68,6 +68,36 @@ describe('shared-wal', async () => { 'forward-slash worktree path resolves correctly'); } + // ─── Test (e1): external-state worktree resolves to project state DB (#2952) ─── + console.log('\n=== shared-wal: resolve external-state worktree path (#2952) ==='); + { + // External-state layout: ~/.gsd/projects//worktrees/ + // Should resolve to: ~/.gsd/projects//gsd.db + const stateRoot = '/home/user/.gsd/projects/a1b2c3d4'; + const worktreePath = join(stateRoot, 'worktrees', 'M002'); + const result = resolveProjectRootDbPath(worktreePath); + assert.deepStrictEqual(result, join(stateRoot, 'gsd.db'), + 'external-state worktree path resolves to project state DB (#2952)'); + } + + // ─── Test (e2): external-state worktree nested subdir (#2952) ───────── + console.log('\n=== shared-wal: resolve external-state worktree nested subdir (#2952) ==='); + { + const stateRoot = '/home/user/.gsd/projects/deadbeef42'; + const nestedPath = join(stateRoot, 'worktrees', 'M003', 'src', 'lib'); + const result = resolveProjectRootDbPath(nestedPath); + assert.deepStrictEqual(result, join(stateRoot, 'gsd.db'), + 'external-state nested worktree subdir resolves to project state DB (#2952)'); + } + + // ─── Test (e3): external-state worktree with forward slashes (#2952) ── + console.log('\n=== shared-wal: resolve external-state worktree forward-slash (#2952) ==='); + { + const result = resolveProjectRootDbPath('/Users/dev/.gsd/projects/cafe0123/worktrees/M001'); + assert.deepStrictEqual(result, join('/Users/dev/.gsd/projects/cafe0123', 'gsd.db'), + 'external-state forward-slash worktree path resolves correctly (#2952)'); + } + // ─── Test (e): Concurrent writes — 3 connections to same WAL DB ─────── console.log('\n=== shared-wal: concurrent writes via WAL ==='); {