Merge pull request #1364 from gsd-build/fix/workspace-project-root-1362

fix: workspace project_root resolves to child repo, not parent
This commit is contained in:
Tom Boucher
2026-03-24 16:32:55 -04:00
committed by GitHub
2 changed files with 34 additions and 0 deletions

View File

@@ -58,6 +58,13 @@ function findProjectRoot(startDir) {
const root = path.parse(resolved).root;
const homedir = require('os').homedir();
// If startDir already contains .planning/, it IS the project root.
// Do not walk up to a parent workspace that also has .planning/ (#1362).
const ownPlanning = path.join(resolved, '.planning');
if (fs.existsSync(ownPlanning) && fs.statSync(ownPlanning).isDirectory()) {
return startDir;
}
// Check if startDir or any of its ancestors (up to AND including the
// candidate project root) contains a .git directory. This handles both
// `backend/` (direct sub-repo) and `backend/src/modules/` (nested inside),

View File

@@ -1489,6 +1489,33 @@ describe('findProjectRoot', () => {
assert.strictEqual(findProjectRoot(projectRoot), projectRoot);
});
test('does not walk past child with own .planning/ to workspace parent (#1362)', () => {
// Workspace layout: parent has .planning/, child git repo also has .planning/
// findProjectRoot should return the child (startDir), not the parent
fs.mkdirSync(path.join(projectRoot, '.planning'), { recursive: true });
const childRepo = path.join(projectRoot, 'authenticator');
fs.mkdirSync(path.join(childRepo, '.planning'), { recursive: true });
fs.mkdirSync(path.join(childRepo, '.git'), { recursive: true });
assert.strictEqual(findProjectRoot(childRepo), childRepo);
});
test('does not walk past nested dir whose git root has .planning/ (#1362)', () => {
// Workspace layout: parent has .planning/, child git repo also has .planning/
// cwd is deep inside child — should resolve to child root, not workspace root
fs.mkdirSync(path.join(projectRoot, '.planning'), { recursive: true });
const childRepo = path.join(projectRoot, 'authenticator');
fs.mkdirSync(path.join(childRepo, '.planning'), { recursive: true });
fs.mkdirSync(path.join(childRepo, '.git'), { recursive: true });
const deepDir = path.join(childRepo, 'src', 'lib');
fs.mkdirSync(deepDir, { recursive: true });
assert.strictEqual(findProjectRoot(deepDir), childRepo);
});
});
// ─── reapStaleTempFiles ─────────────────────────────────────────────────────