mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-13 18:32:12 +03:00
* fix(ci): clear the base-reds the 2026-08-30 afternoon merge batch left on release/v3.8.51 (round 5)
- docs-counts / check-docs-counts-sync test: #12103 (Perplexity Agent) made it 352
providers; README, AGENTS.md, llm.txt (+42 i18n mirrors), package.json description
and the 4 README diagrams still said 351.
- api-route-typecheck: #11971 passes a third `{ featureEnabled }` argument to
appendNoThinkingVariants() that the helper never accepted (TS2554 — and the flag
silently did nothing); the helper now honours it. src/lib/skills/interception.ts
narrowed a mapped object with a `Record<string, string>` predicate (TS2677) —
predicate typed with the actual element shape.
Gates: check:docs-counts OK (test 28/28), check:docs-sync PASS, check:api-typecheck
OK (289 frozen). Refs #12103, #11971
* docs(env): document RATE_LIMIT_EXECUTION_MAX_WAIT_MS (#12027 added it to .env.example only)
* fix(ci): round 5b — freeze the react-hooks compiler-rule violations, align 7 tests to merged contracts
No new ESLint warnings: the exact CI command (lint:json --max-warnings 0) reports 278
problems on the tip — 226 from eslint-plugin-react-hooks 7 compiler rules
(set-state-in-effect 167, immutability 36, refs/static-components/purity/
preserve-manual-memoization) that were masked until the lockfile change of
dfc84ba030 invalidated the ESLint cache, plus 46 no-explicit-any in
tests/unit/call-log-cap.test.ts (#12026). Velocity phase: frozen with
`eslint --suppress-all` (+668 suppressions); the 5 now-unused
`eslint-disable react-hooks/immutability` directives and one unused import removed.
Verified: lint:json --max-warnings 0 → 0 problems.
Tests aligned to contracts merged this afternoon (all reproduced red on the pure tip):
- providers-constants-split: 235 → 236 (Perplexity Agent, #12103)
- sse-auth: a forced pin outside allowedConnections now yields no credential
instead of silently falling back (#12080)
- with-chat-admission-10786: withInjectionGuard(postHandler, { logger: null }) (#12117)
- hard-session-lease-bypass-inventory: classify src/app/api/oauth/codex/import/route.ts (#12116)
- usage-service-hardening: OpenCode Go official usage API shape (#12124)
- i18n placeholder parity: apiManager.restrictedToConnections rewritten as a plain
ICU plural (`{count, plural, one {# connection} other {# connections}}`) in en,
vi, pt-BR and the 40 __MISSING__ mirrors — the parity extractor counts every
`{word}` including the old literal `{s}`
Refs #12103, #12080, #12117, #12116, #12124, #12026
* fix(ci): run the ESLint warnings job on the box with an 8 GB heap; reserved-prefix set 398 → 400
The cold full lint with the react-hooks 7 compiler rules is killed on the 7 GB hosted
runner with no message (status null → exit 1, JSON never written) — it only looked
green while the ESLint cache was warm. tests/unit/provider-node-reserved-prefix.test.ts
aligned to the two prefixes the afternoon batch registered (#12103).
* test(ci): document the lint-guard runner exception; #9147 event-loop gap 400 → 800 ms
quality-rail-gate-membership pinned lint-guard to ubuntu-latest; the cold full lint is
OOM-killed there, so the job now runs on omni-light with an 8 GB heap — the test keeps
fast-gates pinned and asserts the documented exception. With the catalog at 352
providers the hosted shards measure 410–633 ms gaps on 9147-catalog-eventloop-yield
(3 runs); 800 ms still fails a true pin. Re-tighten with the v4.0 catalog split.
* chore(quality): summarize the ESLint report on failure — a red lint:json printed nothing
--format json --output-file swallows every problem; a red 'No new ESLint warnings' job
gave zero output (three blind debugging rounds in #12144), and a killed process (OOM,
status null) was equally silent. On any non-zero exit the runner now prints the problem
count and the first 60 'file:line rule — message' lines from the report.
* chore(lint): freeze react-hooks/immutability for the 5 UI test harnesses in the suppressions file
The rule fires for these files in CI but not locally (compiler analysis divergence),
so the inline eslint-disable directives read as 'unused directive' warnings locally.
A suppressions entry is symmetric: suppressed where the rule fires, tolerated as
unpruned (--pass-on-unpruned-suppressions) where it does not. Found via the new
lint:json failure summary.
101 lines
3.4 KiB
JavaScript
101 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Single ESLint pass that always writes a JSON report for quality:collect.
|
|
*
|
|
* Existence reason: one inventory of net-new issues (vs suppressions) should
|
|
* feed both the blocking lint gate and the eslintWarnings ratchet — not two
|
|
* cold full-tree walks on different runners.
|
|
*
|
|
* Exit code: ESLint's own (0 = clean, 1 = errors). Warnings do not fail by
|
|
* default (same as `npm run lint`); pass --max-warnings=0 for lint-guard.
|
|
*/
|
|
import { spawnSync } from "node:child_process";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
const outFile = path.resolve(
|
|
root,
|
|
process.env.ESLINT_RESULTS_JSON || path.join(".artifacts", "eslint-results.json")
|
|
);
|
|
|
|
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
|
|
const extra = process.argv.slice(2);
|
|
const eslintBin = path.join(
|
|
root,
|
|
"node_modules",
|
|
".bin",
|
|
process.platform === "win32" ? "eslint.cmd" : "eslint"
|
|
);
|
|
const args = [
|
|
".",
|
|
"--cache",
|
|
"--cache-location",
|
|
".eslintcache",
|
|
"--suppressions-location",
|
|
"config/quality/eslint-suppressions.json",
|
|
// An "unpruned" suppression means a previously-frozen violation was legitimately
|
|
// fixed — release-time housekeeping (same bucket as ratchet drift), never a
|
|
// contributor-blocking defect. Without this flag ESLint 9.x exits 2 for that
|
|
// reason alone, which would fail this script's own JSON pass on a clean tree
|
|
// (same failure class already fixed in validate-release-green.mjs — #7837 / #11600).
|
|
"--pass-on-unpruned-suppressions",
|
|
"--format",
|
|
"json",
|
|
"--output-file",
|
|
outFile,
|
|
...extra,
|
|
];
|
|
|
|
const result = spawnSync(eslintBin, args, {
|
|
cwd: root,
|
|
encoding: "utf8",
|
|
shell: process.platform === "win32",
|
|
maxBuffer: 256 * 1024 * 1024,
|
|
});
|
|
|
|
if (result.stdout) process.stdout.write(result.stdout);
|
|
if (result.stderr) process.stderr.write(result.stderr);
|
|
|
|
if (!fs.existsSync(outFile)) {
|
|
// ESLint may crash before writing; leave an empty array so collectors don't explode.
|
|
fs.writeFileSync(outFile, "[]\n");
|
|
}
|
|
|
|
// 2026-08-30 (#12144): with --format json --output-file a red run printed NOTHING — three
|
|
// blind debugging rounds. On failure, summarize the problems from the report so the CI log
|
|
// says WHAT failed; status null means the process was killed (OOM), also silent before.
|
|
if (result.status !== 0) {
|
|
if (result.status === null) {
|
|
console.error(
|
|
`[lint:json] eslint was killed (signal ${result.signal || "?"}) — likely OOM; no report written.`
|
|
);
|
|
}
|
|
try {
|
|
const report = JSON.parse(fs.readFileSync(outFile, "utf8"));
|
|
const problems = [];
|
|
for (const f of report) {
|
|
for (const m of f.messages || []) {
|
|
problems.push(
|
|
`${path.relative(root, f.filePath)}:${m.line ?? 0} ${m.severity === 2 ? "error" : "warning"} ${m.ruleId ?? "(core)"} — ${String(m.message).split("\n")[0].slice(0, 120)}`
|
|
);
|
|
}
|
|
}
|
|
console.error(
|
|
`[lint:json] exit ${result.status}: ${problems.length} problem(s) in the report:`
|
|
);
|
|
for (const line of problems.slice(0, 60)) console.error(" ✗ " + line);
|
|
if (problems.length > 60) {
|
|
console.error(
|
|
` … and ${problems.length - 60} more (full report: ${path.relative(root, outFile)})`
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.error(`[lint:json] could not summarize ${outFile}: ${err && err.message}`);
|
|
}
|
|
}
|
|
|
|
process.exit(result.status === null ? 1 : result.status);
|