Surface the build-path finding in doctor, and keep cwd out of the lookup

Round two of review findings, all four valid.

`doctor` builds its own finding list and never called `checkBuildPathUnset`,
so `config-build-path-unset` could not appear in the report even though
doctor.md documents it. That is also the only path left once stalenessCheck is
off, which is exactly when someone is looking for it.

The lookup chain included `process.cwd()`, which lets an ambient invoking
directory decide another project's workflow: run from workspace A with
--target resolving onto workspace B, and B inherited A's buildPath ahead of
the repository default. The chain is now the resolved project then the repo
root, matching `checkBuildPathUnset` exactly; cwd stands in only when no
project resolved at all.

Two prose contradictions, both mine. new-work said to write the value "when
the user says yes" and then to "record the answer either way", which reads as
persist-on-yes-only and leaves the decline to be asked again next session. It
now says the write always happens and the answer picks the value. The README
still pointed existing projects at re-running init, which is the problem this
PR exists to solve; it now names the toggle as the migration path.

The workspace-isolation test earned a correction of its own: the first version
passed a relative --target, which resolves against the caller's cwd and puts
projectRoot back on the calling workspace, so it asserted nothing.

Written with AI assistance (Claude Code).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Paul Bakaus
2026-08-13 22:41:32 -04:00
co-authored by Claude Opus 5
parent c0e7f2d778
commit 816ffe92d0
5 changed files with 36 additions and 7 deletions
+20
View File
@@ -1123,6 +1123,26 @@ describe('context.mjs CLI', () => {
write('apps/dashboard/.impeccable/config.json', JSON.stringify({ buildPath: 'comp' }));
assert.match(runFromWorkspace().stdout, /BUILD_PATH_DEFAULT: comp/);
});
// Running from one workspace while targeting another must not hand the
// target the caller's preference. The invoking directory is not evidence
// about the project being worked on.
it('does not let the invoking workspace lend its value to the target', () => {
writeWorkspace();
write('apps/marketing/src/App.jsx', 'export default function App() { return "m"; }\n');
write('.impeccable/config.json', JSON.stringify({ buildPath: 'code' }));
write('apps/marketing/.impeccable/config.json', JSON.stringify({ buildPath: 'comp' }));
// Absolute, so the target actually resolves onto dashboard. A relative
// path would be read against the caller's cwd, which resolves the
// project back to marketing and tests nothing.
const res = spawnSync(process.execPath, [SCRIPT_PATH, '--target', path.join(scratch, 'apps', 'dashboard', 'src', 'App.jsx')], {
cwd: path.join(scratch, 'apps', 'marketing'),
encoding: 'utf8',
env: { ...process.env, IMPECCABLE_NO_UPDATE_CHECK: '1', IMPECCABLE_NO_STALENESS_CHECK: '1' },
});
// The repo-root default, not marketing's comp.
assert.match(res.stdout, /BUILD_PATH_DEFAULT: code/);
});
});
});