fix(quality): clear the cycle's 11 net-new ESLint errors + make validate-release-green suppressions-aware

- executor-kiro/save-call-log/call-logs-correlation tests: replace 15 'as any'
  casts with typed shapes (net-new no-explicit-any errors from #6213/#6216);
  prune the now-empty suppression entries so the frozen baseline stays exact
- github-skills + usage/call-logs routes: raw toLowerCase().includes() search
  replaced by matchesSearch() (no-restricted-syntax — Turkish-safe search,
  behavior covered by tests/unit/call-logs-correlation-substring.test.ts and
  tests/unit/github-collector.test.ts)
- validate-release-green.mjs: run ESLint with --suppressions-location (match
  the npm run lint contract — frozen debt is not a release red) and raise the
  lint timeout 15->30min (a full pass takes ~14min alone; the 15min ceiling
  expired under concurrent suite load and surfaced as 'could not parse eslint
  json')
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-07-05 14:49:18 -03:00
parent 509fd5425c
commit fecf888fd9
7 changed files with 173 additions and 53 deletions

View File

@@ -53,7 +53,9 @@ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
/** Read the committed ratchet baseline value for a metric (null if unknown). */
export function baselineValue(metric, root = ROOT) {
try {
const raw = JSON.parse(readFileSync(join(root, "config/quality/quality-baseline.json"), "utf8"));
const raw = JSON.parse(
readFileSync(join(root, "config/quality/quality-baseline.json"), "utf8")
);
const metrics = raw.metrics || raw;
const v = metrics?.[metric]?.value;
return typeof v === "number" ? v : null;
@@ -178,7 +180,13 @@ function main() {
const hardCmd = (id, label, cmd, cmdArgs, opts) => {
announce(label);
const { code, out } = run(cmd, cmdArgs, opts);
record({ id, label, kind: "hard", ok: code === 0, detail: code === 0 ? "pass" : firstFailureLine(out) });
record({
id,
label,
kind: "hard",
ok: code === 0,
detail: code === 0 ? "pass" : firstFailureLine(out),
});
};
// A ratchet command (check:complexity, check:dead-code, …) exits 1 ONLY on a
@@ -189,7 +197,13 @@ function main() {
const driftCmd = (id, label, cmd, cmdArgs, okDetail = "within baseline", opts) => {
announce(label);
const { code, out } = run(cmd, cmdArgs, opts);
record({ id, label, kind: "drift", ok: code === 0, detail: code === 0 ? okDetail : firstFailureLine(out) });
record({
id,
label,
kind: "drift",
ok: code === 0,
detail: code === 0 ? okDetail : firstFailureLine(out),
});
};
process.stderr.write("🔎 Release-green validation (current working tree)\n\n");
@@ -198,14 +212,41 @@ function main() {
// ESLint: ONE pass → errors (hard) + warnings (drift)
{
announce("ESLint (errors + warnings — ~3-6min)");
const { out } = run("npx", ["eslint", ".", "--format", "json"], { timeout: 15 * 60 * 1000 });
announce("ESLint (errors + warnings — ~5-15min)");
// Suppressions-aware, matching `npm run lint` (Pacote 4 no-new-warnings): the frozen
// pre-existing debt in config/quality/eslint-suppressions.json must not count as
// errors here — only NET-NEW violations are release reds. Timeout raised: a full
// repo pass takes ~14min alone and this pre-flight often runs alongside test suites.
const { out } = run(
"npx",
[
"eslint",
".",
"--format",
"json",
"--suppressions-location",
"config/quality/eslint-suppressions.json",
],
{ timeout: 30 * 60 * 1000 }
);
const parsed = parseEslintJson(out);
if (!parsed) {
record({ id: "lint", label: "ESLint", kind: "hard", ok: false, detail: "could not parse eslint json" });
record({
id: "lint",
label: "ESLint",
kind: "hard",
ok: false,
detail: "could not parse eslint json",
});
} else {
const { errors, warnings } = eslintCounts(parsed);
record({ id: "lint-errors", label: "ESLint errors", kind: "hard", ok: errors === 0, detail: `${errors} error(s)` });
record({
id: "lint-errors",
label: "ESLint errors",
kind: "hard",
ok: errors === 0,
detail: `${errors} error(s)`,
});
const base = baselineValue("eslintWarnings");
const over = isDrift(warnings, base);
record({
@@ -283,9 +324,20 @@ function main() {
driftCmd("complexity", "Cyclomatic complexity (ratchet)", npmCmd, ["run", "check:complexity"]);
driftCmd("dead-code", "Dead-code (ratchet)", npmCmd, ["run", "check:dead-code"]);
driftCmd("type-coverage", "Type coverage (ratchet)", npmCmd, ["run", "check:type-coverage"]);
driftCmd("compression-budget", "Compression budget (ratchet)", npmCmd, ["run", "check:compression-budget"]);
driftCmd("openapi-coverage", "OpenAPI route coverage (ratchet)", npmCmd, ["run", "check:openapi-coverage"]);
driftCmd("workflow-lint", "Workflow lint (zizmor ratchet)", npmCmd, ["run", "check:workflows", "--", "--ratchet"]);
driftCmd("compression-budget", "Compression budget (ratchet)", npmCmd, [
"run",
"check:compression-budget",
]);
driftCmd("openapi-coverage", "OpenAPI route coverage (ratchet)", npmCmd, [
"run",
"check:openapi-coverage",
]);
driftCmd("workflow-lint", "Workflow lint (zizmor ratchet)", npmCmd, [
"run",
"check:workflows",
"--",
"--ratchet",
]);
driftCmd("codeql-ratchet", "CodeQL alerts (ratchet)", npmCmd, ["run", "check:codeql-ratchet"]);
// Docs sync + fabricated-docs (strict) is a real-defect gate (invented env vars /
@@ -298,15 +350,35 @@ function main() {
// with 15 such reds). They run SILENTLY for many minutes; the announce line above + these
// hard ceilings keep a long-but-healthy run from being mistaken for a hang (the ceiling also
// converts a genuine DB-handle hang into a visible failure instead of an infinite block).
hardCmd("unit", "Unit tests (full suite, CI concurrency — runs ~20-35min silently)", npmCmd, ["run", "test:unit:ci"], { timeout: 45 * 60 * 1000 });
hardCmd("vitest", "Vitest (MCP / autoCombo / cache — ~3-8min)", npmCmd, ["run", "test:vitest"], { timeout: 15 * 60 * 1000 });
hardCmd(
"unit",
"Unit tests (full suite, CI concurrency — runs ~20-35min silently)",
npmCmd,
["run", "test:unit:ci"],
{ timeout: 45 * 60 * 1000 }
);
hardCmd(
"vitest",
"Vitest (MCP / autoCombo / cache — ~3-8min)",
npmCmd,
["run", "test:vitest"],
{ timeout: 15 * 60 * 1000 }
);
// Integration tests run ONLY on the release PR full CI (PR→main), so an assertion
// regression here (e.g. a contributor flipping a Codex fingerprint key order) is
// invisible until release — run them in the pre-flight as a HARD gate.
hardCmd("integration", "Integration tests (~3-10min)", npmCmd, ["run", "test:integration"], { timeout: 20 * 60 * 1000 });
hardCmd("integration", "Integration tests (~3-10min)", npmCmd, ["run", "test:integration"], {
timeout: 20 * 60 * 1000,
});
}
if (WITH_BUILD) {
hardCmd("pack-artifact", "Package artifact (npm pack policy)", npmCmd, ["run", "check:pack-artifact"], { timeout: 20 * 60 * 1000 });
hardCmd(
"pack-artifact",
"Package artifact (npm pack policy)",
npmCmd,
["run", "check:pack-artifact"],
{ timeout: 20 * 60 * 1000 }
);
}
const { releaseGreen, hardFailures, drift } = computeVerdict(results);