From da68678e7e54214cfffceabde922186c0da7d999 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Mon, 27 Jul 2026 19:38:56 -0700 Subject: [PATCH] fix: app discovery uses the same criterion as the upward walk cursor[bot]: discoverAppCandidates only matched dev-config markers while the upward walk also honors an existing .impeccable/live/config.json, so booting from a repo root without --target missed a nested live-configured static site and fell through to the wrong root. Both paths now share isAppRoot; regression test covers the static-site shape. This work was produced with AI assistance (Claude Code). Co-Authored-By: Claude Code --- skill/scripts/live/roots.mjs | 5 ++++- tests/live-roots.test.mjs | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/skill/scripts/live/roots.mjs b/skill/scripts/live/roots.mjs index 1ea95afd4..ac8750068 100644 --- a/skill/scripts/live/roots.mjs +++ b/skill/scripts/live/roots.mjs @@ -141,7 +141,10 @@ export function discoverAppCandidates(rootDir, depth = CANDIDATE_SCAN_DEPTH) { if (!entry.isDirectory()) continue; if (entry.name.startsWith('.') || CANDIDATE_SCAN_IGNORED.has(entry.name)) continue; const abs = path.join(dir, entry.name); - if (hasDevConfig(abs)) { + // Same criterion as the upward walk (isAppRoot): a live-configured + // plain-static site with no bundler markers is still an app, and + // missing it here would silently fall back to the wrong root. + if (isAppRoot(abs)) { found.push(abs); continue; // nested apps below an app root are that app's business } diff --git a/tests/live-roots.test.mjs b/tests/live-roots.test.mjs index b65535b73..ccc93d325 100644 --- a/tests/live-roots.test.mjs +++ b/tests/live-roots.test.mjs @@ -341,3 +341,20 @@ describe('review regressions: pid reuse', () => { } }); }); + +describe('review regressions: discovery parity', () => { + it('discovers a live-configured static site with no bundler markers', () => { + const repo = realpathSync(mkdtempSync(join(tmpdir(), 'impeccable-roots-static-'))); + try { + mkdirSync(join(repo, '.git'), { recursive: true }); + write(repo, 'package.json', '{"name":"cli"}'); + write(repo, 'docs-site/.impeccable/live/config.json', '{"files":["index.html"]}'); + write(repo, 'docs-site/index.html', ''); + const { manifest, selection } = resolveRoots({ cwd: repo }); + assert.equal(selection, undefined); + assert.equal(manifest.appRoot, join(repo, 'docs-site')); + } finally { + rmSync(repo, { recursive: true, force: true }); + } + }); +});