fix(#3749): route migrate-config, project_exists, and health repairs through the project-aware resolver (#3955)
* test(#3749): GSD_PROJECT-scoped migrate-config, project_exists, and repair paths * fix(#3749): route migrate-config, project_exists, and health repairs through the project-aware resolver * chore(#3749): changeset fragment (pr number backfilled after PR creation) * chore(#3749): backfill changeset PR number (3955) * test(#3749): assert on the POSIX-normalized project_path across platforms --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -4707,3 +4707,38 @@ describe('#3581: init.progress next_phase prefers the roadmap frontier', () => {
|
||||
assert.equal(out.next_phase, null, 'all-complete milestone: no frontier (completion flow owns the answer)');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── #3749: project_exists must follow project_path under GSD_PROJECT ───────
|
||||
describe('init.new-project — GSD_PROJECT scoping (#3749)', () => {
|
||||
test('project_exists tracks the namespaced PROJECT.md, not the root one', (t) => {
|
||||
const tmpDir = createTempProject('gsd-3749-init-');
|
||||
t.after(() => cleanup(tmpDir));
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'second-product'), { recursive: true });
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'second-product', 'PROJECT.md'), '# Second Product\n');
|
||||
|
||||
const r1 = runGsdTools(['query', 'init.new-project'], tmpDir, { GSD_PROJECT: 'second-product' });
|
||||
assert.ok(r1.success, r1.error);
|
||||
const out1 = JSON.parse(r1.output);
|
||||
assert.equal(out1['project_exists'], true,
|
||||
`#3749: project_path (${out1['project_path']}) names an existing file — project_exists must be true`);
|
||||
// project_path is POSIX-normalized by toPosixPath — compare with a literal
|
||||
// forward-slash path, not path.join (which yields backslashes on Windows).
|
||||
assert.ok(String(out1['project_path']).includes('.planning/second-product'));
|
||||
|
||||
// An unrelated root PROJECT.md must not change the verdict.
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'PROJECT.md'), '# unrelated\n');
|
||||
const r2 = runGsdTools(['query', 'init.new-project'], tmpDir, { GSD_PROJECT: 'second-product' });
|
||||
assert.ok(r2.success, r2.error);
|
||||
assert.equal(JSON.parse(r2.output)['project_exists'], true,
|
||||
'#3749: verdict must not flip when an unrelated root file appears');
|
||||
});
|
||||
|
||||
test('without GSD_PROJECT the root PROJECT.md still answers project_exists', (t) => {
|
||||
const tmpDir = createTempProject('gsd-3749-init2-');
|
||||
t.after(() => cleanup(tmpDir));
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'PROJECT.md'), '# Root Project\n');
|
||||
const r = runGsdTools(['query', 'init.new-project'], tmpDir);
|
||||
assert.ok(r.success, r.error);
|
||||
assert.equal(JSON.parse(r.output)['project_exists'], true, 'default (unscoped) behavior unchanged');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user