From 88f97349b577834126ad493e9329f8099d40c087 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Tue, 20 Oct 2026 13:28:34 +0400 Subject: [PATCH] ci: check model findings --- .github/scripts/i18n-review.mjs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/.github/scripts/i18n-review.mjs b/.github/scripts/i18n-review.mjs index 562ebd3a..c7bb0d91 100644 --- a/.github/scripts/i18n-review.mjs +++ b/.github/scripts/i18n-review.mjs @@ -278,7 +278,8 @@ async function collect() { // Text from the API can reach the log and the comment: one line, and no `::` // that the runner could read as a workflow command. -const plain = (text, max) => clip(text, max).replace(/::/g, ": :"); +const plain = (text, max) => + clip(String(text ?? "").replace(/[\u0000-\u001f\u007f]/g, " "), max).replace(/::/g, ": :"); /** The first JSON object in a model answer, or null. */ function firstJsonObject(text) { @@ -296,6 +297,25 @@ function firstJsonObject(text) { return null; } +/** + * The model's findings that name a locale and a key of this batch, with plain, + * bounded text. Everything the model wrote is untrusted: nothing else of it is + * kept, so the files the report step reads hold only checked values. + */ +function acceptedFindings(raw, batch, codes) { + const keys = new Set(batch.map((b) => b.item.key)); + const locales = new Set(codes); + const out = []; + for (const f of raw) { + const locale = String(f?.locale ?? ""); + const key = String(f?.key ?? ""); + const problem = plain(f?.problem, 300); + if (!locales.has(locale) || !keys.has(key) || !problem) continue; + out.push({ locale, key, problem, suggestion: plain(f?.suggestion, 500) }); + } + return out; +} + /** One request. Returns { findings } or { reason } when it failed. */ async function ask(lines, codes) { const fence = `strings-${randomBytes(8).toString("hex")}`; @@ -399,7 +419,7 @@ async function review() { break; } reviewed.push(...batch.map((b) => b.item.key)); - findings.push(...answer.findings); + findings.push(...acceptedFindings(answer.findings, batch, codes)); console.log(`Request ${index + 1}: ${batch.length} key(s), ${answer.findings.length} finding(s)`); } }