fix: resolve external-state worktree DB path (#2952) (#3303)

* fix: resolve external-state worktree DB path in resolveProjectRootDbPath (#2952)

Add regex check for ~/.gsd/projects/<hash>/worktrees/<MID> path layout
so DB writes from external-state worktrees target the canonical project
DB instead of creating an isolated local DB that is lost on teardown.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: retrigger CI (flaky #2912 MERGE_HEAD test)

* chore: retrigger CI

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: trek-e <trek-e@users.noreply.github.com>
This commit is contained in:
Tom Boucher 2026-04-05 00:51:46 -04:00 committed by GitHub
parent b6fcc1bb49
commit c609d813d3
2 changed files with 42 additions and 0 deletions

View file

@ -57,6 +57,18 @@ export function resolveProjectRootDbPath(basePath: string): string {
}
}
// External-state layout: ~/.gsd/projects/<hash>/worktrees/<MID>/...
// Resolve to ~/.gsd/projects/<hash>/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");
}

View file

@ -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/<hash>/worktrees/<MID>
// Should resolve to: ~/.gsd/projects/<hash>/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 ===');
{