diff --git a/scripts/e2e/lib/kitchen-sink-plugin/assertions.mjs b/scripts/e2e/lib/kitchen-sink-plugin/assertions.mjs index 6562982e1bbb..129670f69226 100644 --- a/scripts/e2e/lib/kitchen-sink-plugin/assertions.mjs +++ b/scripts/e2e/lib/kitchen-sink-plugin/assertions.mjs @@ -109,7 +109,11 @@ function scanLogs() { /\blevel["']?\s*:\s*["']error["']/iu, /\[(?:error|ERROR)\]/u, ]; - const allow = [/0 errors?/iu, /expected no diagnostics errors?/iu, /diagnostics errors?:\s*$/iu]; + const allow = [ + /^\s*0 errors?\s*$/iu, + /^\s*expected no diagnostics errors?\s*$/iu, + /^\s*diagnostics errors?:\s*$/iu, + ]; const findings = []; let omittedFindings = false; for (const file of files) { diff --git a/test/scripts/kitchen-sink-plugin-assertions.test.ts b/test/scripts/kitchen-sink-plugin-assertions.test.ts index 2f2c6f60f129..58d9ac440e46 100644 --- a/test/scripts/kitchen-sink-plugin-assertions.test.ts +++ b/test/scripts/kitchen-sink-plugin-assertions.test.ts @@ -276,6 +276,28 @@ describe("kitchen-sink plugin assertions", () => { } }); + it("does not allow dirty error lines just because they mention zero errors", () => { + const parent = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-sink-scan-")); + const home = path.join(parent, "home"); + const scratchRoot = path.join(parent, "scratch"); + try { + mkdirSync(home, { recursive: true }); + mkdirSync(scratchRoot, { recursive: true }); + writeFileSync( + path.join(scratchRoot, "dirty.log"), + "[ERROR] 0 errors reported but fatal state remained\n", + ); + + const result = runScanLogs({ home, scratchRoot }); + + expect(result.status).not.toBe(0); + expect(`${result.stdout}\n${result.stderr}`).toContain("unexpected error-like log lines"); + expect(`${result.stdout}\n${result.stderr}`).toContain("fatal state remained"); + } finally { + rmSync(parent, { force: true, recursive: true }); + } + }); + it("bounds repeated kitchen-sink log scan findings", () => { const parent = mkdtempSync(path.join(tmpdir(), "openclaw-kitchen-sink-scan-")); const home = path.join(parent, "home");