From c7e6d946bdb6fe3bac515a5eb2e4341fd413e950 Mon Sep 17 00:00:00 2001 From: Aleksandar Grbic Date: Mon, 20 Jul 2026 12:09:14 +0200 Subject: [PATCH] fix(boringstack): extractFailures parses lint:meta violations on .json files (unblocks i18n park) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build10 cleared every code wall (apps/api fully green) but parked at 1 on a single unused i18n key. Reproduced: extractFailures returned 0 signatures for that apps/ui output → opaqueGateError. Root: sourceFileFromLine matched only /\.[cm]?[jt]sx?$/, so a lint:meta violation whose file HEADER is a .json locale file (i18n-locale-keys-used reports common.json) never set currentFile → consumeLintMeta dropped it. Consequences: no structured error (opaque, unactionable) AND #61's completion-phase couldn't see rule=i18n-locale-keys-used, so WS-B reverted the model's wiring edits → trapped at 1. Fix: accept .json in sourceFileFromLine (a bare path-only line is unambiguous). Repro now yields 1 signature (rule=i18n-locale-keys-used); 49 extract-failures tests green (no mis-parse regression). Validating end-to-end via build11. --- .../src/loop/boringstack/extract-failures.ts | 8 +++++- .../boringstack-extract-failures.test.ts | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/core/src/loop/boringstack/extract-failures.ts b/packages/core/src/loop/boringstack/extract-failures.ts index ea983b38..bb37fcbf 100644 --- a/packages/core/src/loop/boringstack/extract-failures.ts +++ b/packages/core/src/loop/boringstack/extract-failures.ts @@ -430,7 +430,13 @@ function structuredFailure( function sourceFileFromLine(line: string, app: string): string | null { const withoutColon = line.endsWith(":") ? line.slice(0, -1) : line; - if (!/^\S+\.[cm]?[jt]sx?$/u.test(withoutColon.trim())) { + // JS/TS source files AND `.json` — the latter because meta-rules like + // `i18n-locale-keys-used` report their file HEADER as an i18n locale `.json`. Without + // `.json` here, `consumeLintMeta` never sets `currentFile` for such a violation, drops + // it, and the whole apps/ui gate falls through to opaqueGateError — which hid a lone + // unused-locale-key from the structured error path (so #61's completion-phase couldn't + // see it and WS-B reverted the model's wiring). A bare path-only line is unambiguous. + if (!/^\S+\.([cm]?[jt]sx?|json)$/u.test(withoutColon.trim())) { return null; } diff --git a/packages/core/tests/boringstack-extract-failures.test.ts b/packages/core/tests/boringstack-extract-failures.test.ts index 8034d389..474a5e98 100644 --- a/packages/core/tests/boringstack-extract-failures.test.ts +++ b/packages/core/tests/boringstack-extract-failures.test.ts @@ -61,6 +61,32 @@ error: script "lint:meta" exited with code 1`; ).toBe(true); }); + test("captures a lint-meta violation whose file header is a .json locale file (i18n-locale-keys-used)", () => { + // build10 trap: the ONLY failure was an unused i18n key, whose lint:meta file header is a + // `.json` locale file. sourceFileFromLine matched only JS/TS, so currentFile never set → + // the violation was dropped → the whole apps/ui gate fell to opaqueGateError → #61's + // completion-phase couldn't see rule=i18n-locale-keys-used and WS-B reverted the wiring. + const cwd = "/private/tmp/clone"; + const out = `::tsforge-app apps/ui:: +[generate:api] Wrote src/lib/api/schema.d.ts +::tsforge-eslint-json apps/ui:: +[{"filePath":"${cwd}/apps/ui/x.tsx","messages":[],"errorCount":0,"warningCount":0}] +::tsforge-eslint-json-end:: +[lint:meta] 1 violation(s): + + ${cwd}/apps/ui/src/lib/i18n/locales/en/common.json + i18n-locale-keys-used: Locale key \`features.supplier.loadError\` is defined but never referenced in src — dead translation surface (remove it from every locale, or wire it up). + +error: script "lint:meta" exited with code 1`; + const sigs = extractFailures(out, cwd); + + expect(sigs.size).toBe(1); + const sig = [...sigs][0] ?? ""; + + expect(sig).toContain("i18n-locale-keys-used"); + expect(sig).toContain("common.json"); + }); + test("carries the preceding eslint file header into the failure signature", () => { const cwd = "/tmp/clone"; const out = `${cwd}/apps/api/tests/api/note/note.routes.test.ts