mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-09 00:32:13 +03:00
Merge branch 'release/v3.8.50' into fix/antigravity-per-model-output-cap
This commit is contained in:
13
.github/workflows/quality.yml
vendored
13
.github/workflows/quality.yml
vendored
@@ -155,7 +155,18 @@ jobs:
|
||||
- run: npm run check:fetch-targets
|
||||
# docs-all / openapi-routes / docs-symbols live in docs-gates (path-filtered).
|
||||
- run: npm run check:deps
|
||||
- run: npm run check:file-size
|
||||
# #8522: --base-ref mode for PR events — compare against max(frozen, base) so
|
||||
# inherited drift (base already over frozen cap) doesn't red an innocent PR.
|
||||
# workflow_dispatch (no PR base) falls back to absolute comparison.
|
||||
- name: File-size ratchet (base-relative on PR)
|
||||
env:
|
||||
PR_BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
run: |
|
||||
if [ -n "$PR_BASE_SHA" ]; then
|
||||
npm run check:file-size -- --base-ref "$PR_BASE_SHA"
|
||||
else
|
||||
npm run check:file-size
|
||||
fi
|
||||
- run: npm run check:error-helper
|
||||
- run: npm run check:migration-numbering
|
||||
- run: npm run check:public-creds
|
||||
|
||||
1
changelog.d/fixes/8522-fix.plan.md
Normal file
1
changelog.d/fixes/8522-fix.plan.md
Normal file
@@ -0,0 +1 @@
|
||||
- fix(quality): add base-relative file-size check so inherited drift does not red innocent PRs (#8522)
|
||||
@@ -0,0 +1 @@
|
||||
- fix(quality): tighten eslintWarnings baseline 5000->0 to match the gate's suppressions-applied measurement (unblocks require-tighten on every code PR)
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"_comment": "Congelamento em massa gerado em 2026-08-05 durante a migracao para TypeScript 7 (branch release/v3.8.50). A mudanca de toolchain elevou a contagem de violacoes ESLint de forma ampla e mecanica: 4344 violacoes em 676 arquivos, concentradas em @typescript-eslint/no-explicit-any (4063) e no-restricted-imports (203). Todas sao PRE-EXISTENTES ao congelamento - nenhuma foi introduzida para passar o gate. Politica (CLAUDE.md): novas violacoes DEVEM ser corrigidas, nunca adicionadas aqui; esta allowlist so cobre a divida herdada da migracao. As entradas devem ser reduzidas conforme a divida for paga (o gate quality-ratchet impede crescimento). Regenerado a partir de `npx eslint . --format json`; entradas individuais nao levam justificativa propria por serem de origem unica e mecanica - a justificativa e esta.",
|
||||
"open-sse/executors/blackbox-web.ts": {
|
||||
"@typescript-eslint/no-explicit-any": {
|
||||
"count": 1
|
||||
@@ -1292,11 +1291,6 @@
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/lib/usage/providerLimits.ts": {
|
||||
"no-restricted-imports": {
|
||||
"count": 1
|
||||
}
|
||||
},
|
||||
"src/lib/usage/providerWindowCosts.ts": {
|
||||
"no-restricted-syntax": {
|
||||
"count": 1
|
||||
@@ -3388,4 +3382,4 @@
|
||||
"count": 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
"_comment": "Catraca de qualidade. 'down' = nao pode aumentar; 'up' = nao pode cair. Atualize via 'npm run quality:ratchet -- --update' (somente quando melhora). Cada valor e um numero REAL medido, nunca um chute. Cobertura entra na Fase 4 a partir de um run de cobertura mergeada no CI.",
|
||||
"metrics": {
|
||||
"eslintWarnings": {
|
||||
"value": 5000,
|
||||
"value": 0,
|
||||
"direction": "down",
|
||||
"_rebaseline_2026_08_05_ts7_migration": "Rebaseline para 5000 (direction: down) em 2026-08-05 por conta da migracao para TypeScript 7 na release/v3.8.50. A mudanca de toolchain elevou a contagem de warnings de forma ampla e mecanica. Medicao no tip: 4139 warnings (folga de ~860 para o teto). A divida esta congelada em config/quality/eslint-suppressions.json (ver _comment la). Apertar via `npm run quality:ratchet -- --update` conforme a divida for paga."
|
||||
"_rebaseline_2026_08_05_post_prune": "Apertado 5000->0 em 2026-08-05: o gate mede via lint:json COM as suppressions aplicadas (config/quality/eslint-suppressions.json congela a divida da migracao TS7), entao a contagem real do gate e 0. O 5000 anterior foi medido SEM suppressions (4139 brutos) e fazia o require-tighten reprovar todo PR de codigo (delta 5000>slack). Divida TS7 continua rastreada nas suppressions; warning NOVO (fora delas) agora e red imediato, que e a politica."
|
||||
},
|
||||
"eslintErrors": {
|
||||
"value": 0,
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// igual ao próprio teto ficava presa no baseline para sempre — ver #8584.
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { pathToFileURL } from "node:url";
|
||||
|
||||
const ROOT = process.cwd();
|
||||
@@ -22,6 +23,7 @@ const BASELINE_PATH = path.resolve(
|
||||
getArg("--baseline", path.join(ROOT, "config/quality/file-size-baseline.json"))
|
||||
);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
const BASE_REF = getArg("--base-ref"); // SHA for PR base-relative mode (#8522)
|
||||
const SCAN_DIRS = ["src", "open-sse", "electron", "bin"];
|
||||
// Test files live under tests/ plus co-located *.test.ts(x) inside the source dirs.
|
||||
const TEST_SCAN_DIRS = ["tests", ...SCAN_DIRS];
|
||||
@@ -37,20 +39,39 @@ const SKIP_DIRS = new Set(["node_modules", "dist-electron", ".next", ".build", "
|
||||
* (loc < frozen), entao uma entrada igual ao proprio teto nunca saia da lista,
|
||||
* por mais abaixo do cap que estivesse (3 casos reais no v3.8.49).
|
||||
*
|
||||
* Quando `baseLocByFile` e fornecido (modo PR), a violacao e computada contra
|
||||
* o MAIOR entre o valor congelado e o valor na base -- assim um PR inocente
|
||||
* (head === base no arquivo) nao e penalizado por drift herdado (#8522).
|
||||
*
|
||||
* @param {Object} currentLocByFile — LOC atuais (head)
|
||||
* @param {Object} frozen — baseline congelado
|
||||
* @param {number} cap — teto para arquivos novos
|
||||
* @param {Object} [baseLocByFile] — LOC na branch base (opcional, modo PR)
|
||||
* @returns {{violations: string[], improvements: [string, number][], redundant: string[]}}
|
||||
*/
|
||||
export function evaluateFileSizes(currentLocByFile, frozen, cap) {
|
||||
export function evaluateFileSizes(currentLocByFile, frozen, cap, baseLocByFile) {
|
||||
const violations = [];
|
||||
const improvements = [];
|
||||
const redundant = [];
|
||||
for (const [file, loc] of Object.entries(currentLocByFile)) {
|
||||
if (file in frozen) {
|
||||
if (loc > frozen[file])
|
||||
const threshold = baseLocByFile
|
||||
? Math.max(frozen[file], baseLocByFile[file] ?? frozen[file])
|
||||
: frozen[file];
|
||||
if (loc > threshold)
|
||||
violations.push(`${file}: ${loc} > congelado ${frozen[file]} (não pode crescer)`);
|
||||
else if (loc < frozen[file]) improvements.push([file, loc]);
|
||||
else if (loc <= cap) redundant.push(file);
|
||||
} else if (loc > cap) {
|
||||
violations.push(`${file}: ${loc} > cap ${cap} (arquivo novo acima do limite)`);
|
||||
if (!baseLocByFile) {
|
||||
violations.push(`${file}: ${loc} > cap ${cap} (arquivo novo acima do limite)`);
|
||||
} else {
|
||||
// Modo PR: so viola se cresceu alem do que ja estava na base
|
||||
const baseLoc = baseLocByFile[file] ?? 0;
|
||||
const prThreshold = Math.max(cap, baseLoc);
|
||||
if (loc > prThreshold)
|
||||
violations.push(`${file}: ${loc} > cap ${cap} (arquivo novo acima do limite)`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return { violations, improvements, redundant };
|
||||
@@ -108,6 +129,30 @@ function collectTestLoc() {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computa LOC por arquivo a partir de um ref git (branch, SHA, tag).
|
||||
* Usado pelo modo --base-ref para obter a contagem na base do PR (#8522).
|
||||
* @param {string} ref — git ref (e.g. SHA da branch base)
|
||||
* @param {string[]} files — lista de paths relativos ao ROOT
|
||||
* @returns {Object} mapa file → line count
|
||||
*/
|
||||
function getBaseLoc(ref, files) {
|
||||
const out = {};
|
||||
for (const file of files) {
|
||||
try {
|
||||
const buf = execFileSync("git", ["show", `${ref}:${file}`], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
timeout: 5000,
|
||||
});
|
||||
out[file] = buf.split("\n").length;
|
||||
} catch {
|
||||
// Arquivo nao existe na base (novo no PR) — tratado como 0
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(BASELINE_PATH)) {
|
||||
console.error(`[file-size] FAIL — ${path.basename(BASELINE_PATH)} ausente.`);
|
||||
@@ -117,7 +162,17 @@ function main() {
|
||||
const cap = baseline.cap;
|
||||
const frozen = baseline.frozen || {};
|
||||
const current = collectLoc();
|
||||
const { violations, improvements, redundant } = evaluateFileSizes(current, frozen, cap);
|
||||
|
||||
// Modo PR: computa LOC na branch base para comparacao relativa (#8522)
|
||||
const baseLoc = BASE_REF ? getBaseLoc(BASE_REF, Object.keys(current)) : undefined;
|
||||
if (BASE_REF) {
|
||||
const baseKeys = Object.keys(baseLoc).length;
|
||||
console.log(
|
||||
`[file-size] modo PR (--base-ref ${BASE_REF.slice(0, 12)}): ${baseKeys} arquivos da base computados`
|
||||
);
|
||||
}
|
||||
|
||||
const { violations, improvements, redundant } = evaluateFileSizes(current, frozen, cap, baseLoc);
|
||||
|
||||
// Test-file gate (Layer 1 anti-reinflation): same shrink-only + new-≤cap semantics,
|
||||
// reusing evaluateFileSizes against the testFrozen baseline + testCap.
|
||||
@@ -129,7 +184,7 @@ function main() {
|
||||
improvements: testImprovements,
|
||||
redundant: testRedundant,
|
||||
} = typeof testCap === "number"
|
||||
? evaluateFileSizes(currentTests, testFrozen, testCap)
|
||||
? evaluateFileSizes(currentTests, testFrozen, testCap, BASE_REF ? baseLoc : undefined)
|
||||
: { violations: [], improvements: [], redundant: [] };
|
||||
|
||||
if (UPDATE) {
|
||||
|
||||
@@ -25,21 +25,22 @@ test("8522: innocent PR (base already over frozen cap) must NOT be a violation",
|
||||
const frozen = { "src/foo.ts": 100 };
|
||||
const cap = 100;
|
||||
|
||||
const { violations } = evaluateFileSizes(currentLocByFile, frozen, cap);
|
||||
// With baseLocByFile, the gate compares against max(frozen, base) = max(100, 110) = 110,
|
||||
// so 110 > 110 is false — innocent PR passes.
|
||||
const { violations } = evaluateFileSizes(currentLocByFile, frozen, cap, baseLocByFile);
|
||||
|
||||
// The gate has no base-ref input; it compares head LOC (110) to frozen (100)
|
||||
// and flags a violation. But the PR introduced ZERO growth — it is a false
|
||||
// positive on inherited drift.
|
||||
assert.deepEqual(violations, [], "innocent PR flagged for inherited drift");
|
||||
});
|
||||
|
||||
test("8522: PR that DOES grow a frozen file above frozen cap is a violation", () => {
|
||||
// Sanity: the gate must still catch a PR that grows the file above its cap.
|
||||
// Base is at the frozen cap (100), but PR grew it to 112.
|
||||
const baseLocByFile = { "src/foo.ts": 100 };
|
||||
const currentLocByFile = { "src/foo.ts": 112 }; // PR grew it +12
|
||||
const frozen = { "src/foo.ts": 100 };
|
||||
const cap = 100;
|
||||
|
||||
const { violations } = evaluateFileSizes(currentLocByFile, frozen, cap);
|
||||
// With baseLocByFile: threshold = max(100, 100) = 100, 112 > 100 → violation
|
||||
const { violations } = evaluateFileSizes(currentLocByFile, frozen, cap, baseLocByFile);
|
||||
assert.equal(violations.length, 1, "own-growth PR must be a violation");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user