diff --git a/config/quality/quality-baseline.json b/config/quality/quality-baseline.json index b1cd01d12b..ddec82469c 100644 --- a/config/quality/quality-baseline.json +++ b/config/quality/quality-baseline.json @@ -120,7 +120,7 @@ "_rebaseline_2026_06_26_v3837_release": "343->345. v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle." }, "cognitiveComplexity": { - "value": 882, + "value": 883, "_rebaseline_2026_07_07_v3846_release_close": "877->882 (+5). v3.8.46 release close (generate-release Phase 0 pre-flight): drift herdado do merge burst do ciclo. Trust-but-verify: os fixes de base-red do captain (agentSkills path.resolve #6366, catalogo cache #6408, tipagem de teste, MitmProxyTab suppression) sao cognitive-net-zero — check:cognitive-complexity mede 882 identico com e sem os fixes (a catraca NAO roda no fast-path PR->release). Tighten via --update next cycle.", "_rebaseline_2026_07_03_v3844_ipfilter_release_green": "861->867 (+6). v3.8.44 cycle drift measured on release tip 32e4c906e during the #6131/#5975 release-green rebaseline. Inherited from the merge burst (Quality Ratchet does not run on PR->release fast-gates). route-edge-coverage +7 is my #5975 test comment; the rest is parallel-session drift. Tighten via --update next cycle.", "_rebaseline_2026_07_03_v3844_review_prs_fix_batch": "860->861 (+1). Inherited v3.8.44 cycle drift surfaced by the release-green pre-flight during the /review-prs fix-batch round; check:cognitive-complexity measures 861 on the release tip 72ee80649. Negligible +1 from the round's / parallel-session merge burst (cognitive-complexity does NOT run on PR->release fast-gates). Structural shrink tracked in #3501. Tighten via --update next cycle.", @@ -137,7 +137,8 @@ "_rebaseline_2026_06_22_v3833_release": "793→797 (+4) — pre-existing cycle drift on origin/release/v3.8.33.", "_rebaseline_2026_06_26_v3837_release": "816->826. v3.8.37 cycle drift surfaced by the release-green pre-flight (the Quality Ratchet does NOT run on PR->release fast-gates, so warnings/complexity accrued unmeasured across this cycle's 76 commits — provider adds DGrid/Pioneer/xAI, headroom proxy lifecycle #4649, ~50 SSE/translator fixes, Engine Combos #5062). Trust-but-verify: this release-finalize working tree touches ONLY CHANGELOG.md, docs/i18n/*/CHANGELOG.md mirrors, and these baselines — 0 production-code change, so all drift is inherited cycle drift (`any` warn-allowed in open-sse/ + tests/). Tighten via --require-tighten next cycle.", "_rebaseline_2026_06_26_v3838_release": "826->833. v3.8.38 release base measures 833 locally on origin/release/v3.8.38 (800b04ad6) while the committed baseline still says 826. This PR measures the same 833 after refactoring jsonToSse helpers back under the sonarjs/cognitive-complexity threshold, so it does not add a net cognitive-complexity violation. The baseline bump records inherited release-base drift only; structural shrink remains tracked by the existing chatCore decomposition work.", - "_rebaseline_2026_07_06_v3845_release_close": "867->877 (+10). v3.8.45 cycle drift measured by check:release-green (hermetic) on release tip 5ecca12aa5 during the /generate-release Phase 0 pre-flight. Inherited from the cycle's merge burst (cognitive-complexity does not run on PR->release fast-gates); the captain's pre-flight fixes are gate/test/workflow changes (complexity-neutral). Tighten via --update next cycle." + "_rebaseline_2026_07_06_v3845_release_close": "867->877 (+10). v3.8.45 cycle drift measured by check:release-green (hermetic) on release tip 5ecca12aa5 during the /generate-release Phase 0 pre-flight. Inherited from the cycle's merge burst (cognitive-complexity does not run on PR->release fast-gates); the captain's pre-flight fixes are gate/test/workflow changes (complexity-neutral). Tighten via --update next cycle.", + "_rebaseline_2026_07_07_6519_chirag_fallback_reasons": "882->883 (+1). PR #6519 (@chirag127, #6461): the preview route's fallbackReasons dedup loop adds one function over the cognitive threshold. Owner-approved rebaseline (contributor own-growth). Tighten via --update next cycle." }, "typeCoveragePct": { "value": 92.17, @@ -376,4 +377,4 @@ "_zizmor_rebaseline_2026_06_19_r1_redundancy": "zizmorFindings 139 -> 145. Quebra: +3 drift PRE-EXISTENTE da base release/v3.8.30 a23d0d678 (medido com minhas mudancas stashed = 142 > 139; o fast-path do release nao roda check:workflows --ratchet) + 3 do novo workflow mutation-redundancy.yml (R1 disableBail): exatamente 3 unpinned-uses de actions/checkout@v7, actions/setup-node@v6, actions/upload-artifact@v7 — a MESMA convencao @vN deliberada e INTOCADA de todos os workflows (ver _scanner_harden_workflows_2026_06_16), identica ao nightly-mutation.yml. SHA-pinar so este workflow violaria a convencao. NOTA DE COLISAO CROSS-PR: o PR #4321 (a11y) tambem rebaselina este metric 139->145 (+3 do job a11y) off a MESMA base — se ambos mergearem, o total real vira 148 (142 base + 3 a11y + 3 r1) e o segundo a mergear precisa reconciliar zizmorFindings -> 148 (mesmo padrao release-volatil dos baselines de complexity/eslint).", "_zizmor_rebaseline_2026_06_19_a11y_148_reconcile": "RECONCILIACAO CROSS-PR (release-volatil) ao mergear #4321 (a11y) APOS #4322 (R1): zizmorFindings 145 -> 148. O #4322 ja rebaselinou 139->145 (drift base 142 + 3 unpinned-uses do mutation-redundancy.yml). Este PR adiciona +3 unpinned-uses @vN do novo job 'a11y' (nightly-resilience.yml): actions/checkout@v7, actions/setup-node@v6, actions/cache@v5.0.5 — MESMA convencao @vN deliberada e INTOCADA de todos os workflows (ver _scanner_harden_workflows_2026_06_16). Total = 142 base + 3 r1 + 3 a11y = 148, MEDIDO com `node scripts/check/check-workflows.mjs --ratchet` na arvore release(com #4322)+#4321 = 148 exato. Nenhum template-injection/artipacked/cache-poisoning novo.", "_zizmor_rebaseline_2026_06_20_ci_build_artifact_reuse": "zizmorFindings 148 -> 152. Drift legitimo deste PR ao reutilizar o artefato next-build do job Build em package-artifact/electron-package-smoke e ao separar o build de compatibilidade Node 26: +4 unpinned-uses novos (2x actions/download-artifact@v8, actions/checkout@v7, actions/setup-node@v6). Mantida a convencao deliberada @vN dos workflows (sem SHA-pinning/manual update burden), conforme precedentes _scanner_harden_workflows_2026_06_16 e _zizmor_rebaseline_2026_06_19_*. Sem novos findings de template-injection/artipacked/cache-poisoning; medido localmente com zizmor 1.25.2 via `npm run check:workflows -- --ratchet` = 152." -} +} \ No newline at end of file diff --git a/src/app/api/compression/preview/route.ts b/src/app/api/compression/preview/route.ts index 3153298ffa..cc8fc7e360 100644 --- a/src/app/api/compression/preview/route.ts +++ b/src/app/api/compression/preview/route.ts @@ -230,6 +230,31 @@ export async function POST(req: Request) { ? summarizeEncoderCandidates(messages, DEFAULT_MIN_ROWS, countTextTokens) : null; + // #6461: when fallbackApplied=true, synthesize a deduped reason list from data the + // pipeline already produces on result.stats (engineBreakdown[].rejectReason, + // validationErrors, and inflation-guard entries in validationWarnings). Non-fallback + // runs return []/null — zero change on the happy path. + const fallbackReasons: string[] = []; + if (diff.fallbackApplied) { + const seen = new Set(); + const push = (s: unknown) => { + if (typeof s === "string" && s.length > 0 && !seen.has(s)) { + seen.add(s); + fallbackReasons.push(s); + } + }; + for (const step of engineBreakdown) { + if ((step as { rejected?: boolean }).rejected === true) { + push((step as { rejectReason?: string }).rejectReason); + } + } + for (const err of diff.validationErrors ?? []) push(err); + for (const warn of diff.validationWarnings ?? []) { + if (typeof warn === "string" && warn.startsWith("pipeline-inflation-guard:")) push(warn); + } + } + const fallbackReason = fallbackReasons[0] ?? null; + return NextResponse.json({ encoderComparison, original: originalText, @@ -246,7 +271,7 @@ export async function POST(req: Request) { mode: effectiveMode, intensity: null, outputMode: null, - skippedReasons: [], + skippedReasons: fallbackReasons, diff: diff.segments, preservedBlocks: diff.preservedBlocks, ruleRemovals: diff.ruleRemovals, @@ -261,7 +286,10 @@ export async function POST(req: Request) { validationWarnings: diff.validationWarnings, validationErrors: diff.validationErrors, fallbackApplied: diff.fallbackApplied, - ...(diff.fallbackReason && { fallbackReason: diff.fallbackReason }), + // Prefer the pipeline's canonical `diff.fallbackReason`; fall back to the + // first synthesized reason (#6461) when the pipeline did not set one. + fallbackReason: diff.fallbackReason ?? fallbackReason, + fallbackReasons, ...(diff.heatmap ? { heatmap: diff.heatmap } : {}), }); } catch (err: unknown) { diff --git a/tests/unit/compression/preview-fallback-reasons-6461.test.ts b/tests/unit/compression/preview-fallback-reasons-6461.test.ts new file mode 100644 index 0000000000..4de93f128f --- /dev/null +++ b/tests/unit/compression/preview-fallback-reasons-6461.test.ts @@ -0,0 +1,56 @@ +/** + * #6461 — the compression preview response must surface WHY a run fell back + * (deduped `fallbackReasons[]`, mirrored into `skippedReasons`) instead of the + * previously hard-coded `skippedReasons: []`. Non-fallback runs return [] on + * both — zero change on the happy path (regression guard, Rule #18). + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import { join } from "node:path"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; + +const TEST_DATA_DIR = mkdtempSync(join(tmpdir(), "preview-fallback-6461-")); +process.env.DATA_DIR = TEST_DATA_DIR; +process.env.API_KEY_SECRET = process.env.API_KEY_SECRET ?? "test-secret-32-chars-min-aaaaaaaa"; +delete process.env.INITIAL_PASSWORD; + +const core = await import("../../../src/lib/db/core.ts"); +const route = await import("../../../src/app/api/compression/preview/route.ts"); + +function makeReq(body: unknown) { + return new Request("http://localhost/api/compression/preview", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(body), + }); +} + +test.beforeEach(() => core.resetDbInstance()); +test.after(() => { + core.resetDbInstance(); + rmSync(TEST_DATA_DIR, { recursive: true, force: true }); +}); + +test("#6461 preview exposes fallbackReasons and mirrors it into skippedReasons", async () => { + const res = await route.POST( + makeReq({ + messages: [{ role: "user", content: "$ git status\nOn branch main\nnothing to commit" }], + engineId: "rtk", + }) + ); + assert.equal(res.status, 200); + const body = await res.json(); + // Structural contract added by #6461 (absent on the pre-patch response). + assert.ok(Array.isArray(body.fallbackReasons), "fallbackReasons must be an array"); + assert.deepEqual( + body.skippedReasons, + body.fallbackReasons, + "skippedReasons must mirror fallbackReasons" + ); + // Happy path (no fallback): both empty, and fallbackReason is null. + if (body.fallbackApplied !== true) { + assert.deepEqual(body.fallbackReasons, []); + assert.equal(body.fallbackReason ?? null, null); + } +});