Files
OmniRoute/scripts/check/check-open-sse-typecheck.mjs
Diego Rodrigues de Sa e Souza 089e70cbc5 fix(quality): validate typecheck baseline schema (#12419)
The API-route and Open-SSE typecheck gates treated every top-level baseline value as a diagnostic map, so policy metadata such as _relax_velocity_2026_08_30 was iterated character by character and reported as fabricated numeric TypeScript improvements. Both gates now share one fail-closed baseline boundary: underscore-prefixed top-level keys are reserved for metadata and skipped, and real file entries must be plain objects.

Worth a follow-up: check-dashboard-typecheck still prints the same fabricated entries, so it looks like a third gate with the same defect that this PR's scope does not cover.

Validated in a combined worktree with the batch's ready set boarded onto the current tip: parse sweep clean on every changed TypeScript file, typecheck:core clean, check:dashboard-typecheck OK (207 pre-existing errors, all within baseline), check:cycles OK, check-file-size OK, 203/205 focused node tests and 94/94 vitest — the two failures belong to #12427, which is held back.
2026-09-02 10:01:51 -03:00

118 lines
4.4 KiB
JavaScript

#!/usr/bin/env node
// scripts/check/check-open-sse-typecheck.mjs
// open-sse workspace typecheck gate (#8781).
//
// The open-sse workspace declares path aliases (e.g. `@/*` → `../src/*`) in its own
// tsconfig.json, but those aliases are not resolvable by Node's bare module resolution —
// they only work because Next.js/Turbopack bundles the entire tree. Additionally,
// package.json historically declared `main`/`exports` entries that do not exist on disk.
//
// This gate runs `tsc -p open-sse/tsconfig.json` and diffs the result against a frozen
// per-file/per-TS-code count baseline (config/quality/open-sse-typecheck-baseline.json),
// following this repo's stale-enforcement allowlist convention. A live count that EXCEEDS
// the baselined count for a given (file, TS code) pair is a regression and fails the gate;
// a live count that is lower is an improvement and does not fail (use --update to ratchet
// the baseline down).
//
// Run:
// node scripts/check/check-open-sse-typecheck.mjs
// node scripts/check/check-open-sse-typecheck.mjs --update # re-freeze baseline
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { diffAgainstBaseline, parseTscOutput } from "./typecheckBaseline.mjs";
export { diffAgainstBaseline, parseTscOutput } from "./typecheckBaseline.mjs";
const ROOT = process.cwd();
const TSCONFIG = path.join(ROOT, "open-sse", "tsconfig.json");
const BASELINE_PATH = path.join(ROOT, "config/quality/open-sse-typecheck-baseline.json");
const UPDATE = process.argv.includes("--update");
function runTsc() {
try {
const stdout = execFileSync(
process.platform === "win32" ? "npx.cmd" : "npx",
["tsc", "--pretty", "false", "--noEmit", "-p", TSCONFIG],
{ encoding: "utf8", maxBuffer: 64 * 1024 * 1024, cwd: ROOT }
);
return stdout;
} catch (err) {
// tsc exits non-zero when there are type errors — stdout still has the report.
if (err.stdout) return String(err.stdout);
throw err;
}
}
function loadBaseline() {
if (!fs.existsSync(BASELINE_PATH)) return {};
return JSON.parse(fs.readFileSync(BASELINE_PATH, "utf8"));
}
function writeBaseline(counts) {
fs.writeFileSync(BASELINE_PATH, JSON.stringify(counts, null, 2) + "\n");
}
function main() {
if (!fs.existsSync(TSCONFIG)) {
process.stderr.write(`[open-sse-typecheck] FAIL — tsconfig not found at ${TSCONFIG}\n`);
process.exit(2);
}
console.log("[open-sse-typecheck] Running tsc scoped to open-sse/ workspace…");
const stdout = runTsc();
const live = parseTscOutput(stdout);
const baseline = loadBaseline();
const { regressions, improvements } = diffAgainstBaseline(live, baseline);
const liveErrorCount = Object.values(live).reduce(
(sum, codes) => sum + Object.values(codes).reduce((s, c) => s + c, 0),
0
);
console.log(`openSseTypecheckErrors=${liveErrorCount}`);
if (UPDATE) {
writeBaseline(live);
console.log(`[open-sse-typecheck] baseline rewritten (${liveErrorCount} errors frozen).`);
process.exit(0);
}
if (improvements.length > 0) {
console.log(
`[open-sse-typecheck] ${improvements.length} baselined error(s) no longer present ` +
`— run 'node scripts/check/check-open-sse-typecheck.mjs --update' to ratchet the baseline down:\n` +
improvements
.map(
(i) => ` - ${i.file} ${i.code} (baseline ${i.baselineCount} -> live ${i.liveCount})`
)
.join("\n")
);
}
if (regressions.length > 0) {
process.stderr.write(
`[open-sse-typecheck] FAIL — ${regressions.length} new/regressed TypeScript error(s) ` +
`under open-sse/ workspace not covered by the frozen baseline:\n` +
regressions
.map((r) => `${r.file} ${r.code} (baseline ${r.baselineCount}, live ${r.liveCount})`)
.join("\n") +
`\n\nIf this is a genuine new open-sse type error (e.g. an undeclared @/ alias),\n` +
`fix it in the source, not in the baseline.\n` +
`If it's pre-existing type looseness you're intentionally not fixing in this PR,\n` +
`do NOT widen the baseline for new regressions — that defeats the gate.\n`
);
process.exit(1);
}
console.log(
`[open-sse-typecheck] OK — ${liveErrorCount} pre-existing error(s), all within frozen baseline.`
);
process.exit(0);
}
if (import.meta.url === pathToFileURL(process.argv[1] || "").href) {
main();
}