From 4a36a165edf26085a42da848c568beb07d8cc029 Mon Sep 17 00:00:00 2001 From: jbmml <52386063+jbmml@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:55:07 -0400 Subject: [PATCH 1/2] fix(install-engine): fail checkSurvivingPlaceholders on a zero-file scan A nonexistent or mis-rooted rootDir walked zero template files and returned passed: true, so the Setup 9(d) identity-placeholder gate could not fail in exactly the case it exists to catch. Count the files actually read and require at least one before the check can pass. The result gains a scanned field for diagnostics; existing callers (Doctor.ts sums total only) are unaffected. Fixes #2058 Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CvY8QkJKBWyXcZeL2Q2UXc --- LifeOS/install/skills/LifeOS/Tools/InstallEngine.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/LifeOS/install/skills/LifeOS/Tools/InstallEngine.ts b/LifeOS/install/skills/LifeOS/Tools/InstallEngine.ts index b5b1b76b4d..146119ff03 100644 --- a/LifeOS/install/skills/LifeOS/Tools/InstallEngine.ts +++ b/LifeOS/install/skills/LifeOS/Tools/InstallEngine.ts @@ -491,14 +491,16 @@ const IDENTITY_PLACEHOLDERS = [ * or abort. */ export function checkSurvivingPlaceholders(rootDir: string): { - passed: boolean; files: Array<{ file: string; placeholder: string; count: number }>; total: number; + passed: boolean; files: Array<{ file: string; placeholder: string; count: number }>; total: number; scanned: number; } { const files: Array<{ file: string; placeholder: string; count: number }> = []; let total = 0; + let scanned = 0; const processFile = (filePath: string): void => { if (!TEMPLATE_EXTENSIONS.has(fileExtension(filePath))) return; let src: string; try { src = readFileSync(filePath, "utf-8"); } catch { return; } + scanned++; for (const placeholder of IDENTITY_PLACEHOLDERS) { const count = src.split(placeholder).length - 1; if (count > 0) { files.push({ file: filePath, placeholder, count }); total += count; } @@ -515,7 +517,9 @@ export function checkSurvivingPlaceholders(rootDir: string): { }; if (existsSync(rootDir) && lstatSync(rootDir).isFile()) processFile(rootDir); else walk(rootDir); - return { passed: total === 0, files, total }; + // A scan that reached zero template files cannot vouch for anything: a mis-rooted or + // nonexistent rootDir must FAIL the Setup 9(d) gate, not pass it. + return { passed: scanned > 0 && total === 0, files, total, scanned }; } /** From 286870d53e72020a9b6c96cbee004eada137d19a Mon Sep 17 00:00:00 2001 From: jbmml <52386063+jbmml@users.noreply.github.com> Date: Thu, 3 Sep 2026 23:00:13 -0400 Subject: [PATCH 2/2] fix(install-engine): apply the same change to the sibling copy at LifeOS/Tools/ LifeOS/Tools/InstallEngine.ts and LifeOS/install/skills/LifeOS/Tools/InstallEngine.ts are byte-identical; Setup.md step 9(d) runs the outer one, Doctor.ts imports the deployed one. Both now fail a zero-file scan. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CvY8QkJKBWyXcZeL2Q2UXc --- LifeOS/Tools/InstallEngine.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/LifeOS/Tools/InstallEngine.ts b/LifeOS/Tools/InstallEngine.ts index b5b1b76b4d..146119ff03 100644 --- a/LifeOS/Tools/InstallEngine.ts +++ b/LifeOS/Tools/InstallEngine.ts @@ -491,14 +491,16 @@ const IDENTITY_PLACEHOLDERS = [ * or abort. */ export function checkSurvivingPlaceholders(rootDir: string): { - passed: boolean; files: Array<{ file: string; placeholder: string; count: number }>; total: number; + passed: boolean; files: Array<{ file: string; placeholder: string; count: number }>; total: number; scanned: number; } { const files: Array<{ file: string; placeholder: string; count: number }> = []; let total = 0; + let scanned = 0; const processFile = (filePath: string): void => { if (!TEMPLATE_EXTENSIONS.has(fileExtension(filePath))) return; let src: string; try { src = readFileSync(filePath, "utf-8"); } catch { return; } + scanned++; for (const placeholder of IDENTITY_PLACEHOLDERS) { const count = src.split(placeholder).length - 1; if (count > 0) { files.push({ file: filePath, placeholder, count }); total += count; } @@ -515,7 +517,9 @@ export function checkSurvivingPlaceholders(rootDir: string): { }; if (existsSync(rootDir) && lstatSync(rootDir).isFile()) processFile(rootDir); else walk(rootDir); - return { passed: total === 0, files, total }; + // A scan that reached zero template files cannot vouch for anything: a mis-rooted or + // nonexistent rootDir must FAIL the Setup 9(d) gate, not pass it. + return { passed: scanned > 0 && total === 0, files, total, scanned }; } /**