diff --git a/config/quality/eslint-suppressions.json b/config/quality/eslint-suppressions.json index e0f5b93a58..08b6f36d5f 100644 --- a/config/quality/eslint-suppressions.json +++ b/config/quality/eslint-suppressions.json @@ -1392,11 +1392,6 @@ "count": 1 } }, - "tests/unit/executor-kiro.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 1 - } - }, "tests/unit/executor-nlpcloud.test.ts": { "@typescript-eslint/no-explicit-any": { "count": 2 @@ -2002,11 +1997,6 @@ "count": 9 } }, - "tests/unit/save-call-log-persistence.test.ts": { - "@typescript-eslint/no-explicit-any": { - "count": 7 - } - }, "tests/unit/schema-coercion.test.ts": { "@typescript-eslint/no-explicit-any": { "count": 12 diff --git a/scripts/quality/validate-release-green.mjs b/scripts/quality/validate-release-green.mjs index 78091ce8ab..3c1323b3d8 100644 --- a/scripts/quality/validate-release-green.mjs +++ b/scripts/quality/validate-release-green.mjs @@ -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); diff --git a/src/app/api/github-skills/route.ts b/src/app/api/github-skills/route.ts index e8fc469bcb..303d92568b 100644 --- a/src/app/api/github-skills/route.ts +++ b/src/app/api/github-skills/route.ts @@ -11,6 +11,7 @@ */ import { NextRequest, NextResponse } from "next/server"; import { searchGitHubSkills } from "@/lib/skills/githubCollector"; +import { matchesSearch } from "@/shared/utils/turkishText"; import { buildErrorBody, sanitizeErrorMessage } from "@omniroute/open-sse/utils/error"; export const dynamic = "force-dynamic"; @@ -31,9 +32,8 @@ export async function GET(request: NextRequest) { let filtered = repos; if (minScore > 0) filtered = filtered.filter((r) => r.score >= minScore); if (query) { - const q = query.toLowerCase(); filtered = filtered.filter( - (r) => r.fullName.toLowerCase().includes(q) || r.description.toLowerCase().includes(q) + (r) => matchesSearch(r.fullName, query) || matchesSearch(r.description, query) ); } diff --git a/src/app/api/usage/call-logs/route.ts b/src/app/api/usage/call-logs/route.ts index 8a3540f254..90ff750e4f 100644 --- a/src/app/api/usage/call-logs/route.ts +++ b/src/app/api/usage/call-logs/route.ts @@ -3,6 +3,7 @@ import { requireManagementAuth } from "@/lib/api/requireManagementAuth"; import { getCallLogs } from "@/lib/usageDb"; import { getCompletedDetails, getPendingById } from "@/lib/usage/usageHistory"; import { getProviderConnections } from "@/lib/localDb"; +import { matchesSearch } from "@/shared/utils/turkishText"; type CallLogListRowsInput = { logs: any[]; @@ -147,10 +148,8 @@ export async function GET(request: Request) { // (active + completed) that don't match β€” getCallLogs already filters // the DB rows but activeEntries/completedEntries bypass it. if (filter.correlationId) { - const cid = filter.correlationId.toLowerCase(); - return NextResponse.json( - rows.filter((r: any) => (r.correlationId || "").toLowerCase().includes(cid)) - ); + const cid = filter.correlationId; + return NextResponse.json(rows.filter((r: any) => matchesSearch(r.correlationId || "", cid))); } return NextResponse.json(rows); diff --git a/tests/unit/call-logs-correlation-substring.test.ts b/tests/unit/call-logs-correlation-substring.test.ts index a812d7c47d..c5fe01a53e 100644 --- a/tests/unit/call-logs-correlation-substring.test.ts +++ b/tests/unit/call-logs-correlation-substring.test.ts @@ -22,15 +22,54 @@ function seedLogs() { db.prepare( `INSERT OR REPLACE INTO call_logs (id, timestamp, method, path, status, model, provider, account, duration, tokens_in, tokens_out, correlation_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ).run("log-1", now, "POST", "/v1/chat/completions", 200, "gpt-4", "openai", "acc1", 100, 10, 20, "abc123-def456-ghi789"); + ).run( + "log-1", + now, + "POST", + "/v1/chat/completions", + 200, + "gpt-4", + "openai", + "acc1", + 100, + 10, + 20, + "abc123-def456-ghi789" + ); db.prepare( `INSERT OR REPLACE INTO call_logs (id, timestamp, method, path, status, model, provider, account, duration, tokens_in, tokens_out, correlation_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ).run("log-2", now, "POST", "/v1/chat/completions", 200, "claude-3", "anthropic", "acc2", 200, 15, 30, "xyz999-uvw888-tsr777"); + ).run( + "log-2", + now, + "POST", + "/v1/chat/completions", + 200, + "claude-3", + "anthropic", + "acc2", + 200, + 15, + 30, + "xyz999-uvw888-tsr777" + ); db.prepare( `INSERT OR REPLACE INTO call_logs (id, timestamp, method, path, status, model, provider, account, duration, tokens_in, tokens_out, correlation_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ).run("log-3", now, "POST", "/v1/chat/completions", 500, "gpt-4", "openai", "acc1", 50, 0, 0, null); + ).run( + "log-3", + now, + "POST", + "/v1/chat/completions", + 500, + "gpt-4", + "openai", + "acc1", + 50, + 0, + 0, + null + ); } // ── Exact match ─────────────────────────────────────────────────────────── @@ -72,11 +111,24 @@ test("correlationId substring match returns multiple when shared", async () => { db.prepare( `INSERT OR REPLACE INTO call_logs (id, timestamp, method, path, status, model, provider, account, duration, tokens_in, tokens_out, correlation_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` - ).run("log-4", now, "POST", "/v1/chat/completions", 200, "gpt-4", "openai", "acc1", 100, 10, 20, "abc123-RETRY-suffix"); + ).run( + "log-4", + now, + "POST", + "/v1/chat/completions", + 200, + "gpt-4", + "openai", + "acc1", + 100, + 10, + 20, + "abc123-RETRY-suffix" + ); const results = await callLogs.getCallLogs({ correlationId: "abc123" }); assert.equal(results.length, 2); - const ids = results.map((r: any) => r.id).sort(); + const ids = results.map((r: { id: string }) => r.id).sort(); assert.deepEqual(ids, ["log-1", "log-4"]); }); @@ -94,7 +146,7 @@ test("rows with null correlationId are excluded from substring search", async () seedLogs(); // Search for a unique substring that only matches log-1 const results = await callLogs.getCallLogs({ correlationId: "ghi789" }); - const ids = results.map((r: any) => r.id); + const ids = results.map((r: { id: string }) => r.id); assert.ok(!ids.includes("log-3"), "null correlation_id rows must be excluded"); assert.equal(results.length, 1); }); diff --git a/tests/unit/executor-kiro.test.ts b/tests/unit/executor-kiro.test.ts index ff14a94580..61f5a4493c 100644 --- a/tests/unit/executor-kiro.test.ts +++ b/tests/unit/executor-kiro.test.ts @@ -162,10 +162,10 @@ test("KiroExecutor.transformRequest removes the top-level model field", () => { const result = executor.transformRequest("kiro-model", body, true, {}); assert.equal("model" in result, false); - assert.equal( - (result as any).conversationState.currentMessage.userInputMessage.modelId, - "kiro-model" - ); + const kiroResult = result as unknown as { + conversationState: { currentMessage: { userInputMessage: { modelId: string } } }; + }; + assert.equal(kiroResult.conversationState.currentMessage.userInputMessage.modelId, "kiro-model"); }); test("KiroExecutor.transformRequest forwards additionalModelRequestFields (thinking) to AWS", () => { @@ -182,7 +182,10 @@ test("KiroExecutor.transformRequest forwards additionalModelRequestFields (think }, }; - const result = executor.transformRequest("kiro-model", body, true, {}) as any; + const result = executor.transformRequest("kiro-model", body, true, {}) as unknown as Record< + string, + unknown + >; // The thinking control must survive the strict allowlist β€” otherwise graded // reasoning never reaches CodeWhisperer (the field the openai-to-kiro // translator builds would be silently dropped). diff --git a/tests/unit/save-call-log-persistence.test.ts b/tests/unit/save-call-log-persistence.test.ts index e42b3f1a88..8d59ad18d6 100644 --- a/tests/unit/save-call-log-persistence.test.ts +++ b/tests/unit/save-call-log-persistence.test.ts @@ -23,7 +23,7 @@ test("saveCallLog persists to DB with correlationId", async () => { const row = db .prepare("SELECT id, correlation_id, status, model FROM call_logs WHERE id = ?") - .get(testId) as any; + .get(testId) as Record; assert.ok(row, "row should exist in call_logs"); assert.equal(row.id, testId); assert.equal(row.correlation_id, "test-correlation-id-123"); @@ -50,7 +50,7 @@ test("saveCallLog persists null correlationId when not provided", async () => { const row = db .prepare("SELECT id, correlation_id FROM call_logs WHERE id = ?") - .get(testId) as any; + .get(testId) as Record; assert.ok(row, "row should exist"); assert.equal(row.correlation_id, null, "correlation_id should be null when not provided"); @@ -74,7 +74,7 @@ test("getCallLogs returns correlationId", async () => { }); const logs = await getCallLogs({ limit: 100 }); - const found = logs.find((l: any) => l.id === testId); + const found = logs.find((l: { id: string }) => l.id === testId); assert.ok(found, "log entry should be found via getCallLogs"); assert.equal(found.correlationId, "cid-roundtrip-test"); @@ -83,12 +83,12 @@ test("getCallLogs returns correlationId", async () => { test("call_logs table has correlation_id column", () => { const db = getDbInstance(); - const columns = db.prepare("PRAGMA table_info(call_logs)").all() as any[]; - const colNames = columns.map((c: any) => c.name); + const columns = db.prepare("PRAGMA table_info(call_logs)").all() as { name: string }[]; + const colNames = columns.map((c) => c.name); assert.ok(colNames.includes("correlation_id"), "call_logs should have correlation_id column"); - const indexes = db.prepare("PRAGMA index_list(call_logs)").all() as any[]; - const idxNames = indexes.map((i: any) => i.name); + const indexes = db.prepare("PRAGMA index_list(call_logs)").all() as { name: string }[]; + const idxNames = indexes.map((i) => i.name); assert.ok( idxNames.includes("idx_cl_correlation_id"), "call_logs should have idx_cl_correlation_id index" @@ -97,8 +97,8 @@ test("call_logs table has correlation_id column", () => { test("call_logs table has model_pinned column", () => { const db = getDbInstance(); - const columns = db.prepare("PRAGMA table_info(call_logs)").all() as any[]; - const colNames = columns.map((c: any) => c.name); + const columns = db.prepare("PRAGMA table_info(call_logs)").all() as { name: string }[]; + const colNames = columns.map((c) => c.name); assert.ok(colNames.includes("model_pinned"), "call_logs should have model_pinned column"); }); @@ -118,7 +118,9 @@ test("saveCallLog persists modelPinned=true as 1", async () => { modelPinned: true, }); - const row = db.prepare("SELECT id, model_pinned FROM call_logs WHERE id = ?").get(testId) as any; + const row = db + .prepare("SELECT id, model_pinned FROM call_logs WHERE id = ?") + .get(testId) as Record; assert.ok(row, "row should exist"); assert.equal(row.model_pinned, 1, "model_pinned should be 1 when modelPinned=true"); @@ -141,7 +143,9 @@ test("saveCallLog persists modelPinned=false as 0", async () => { modelPinned: false, }); - const row = db.prepare("SELECT id, model_pinned FROM call_logs WHERE id = ?").get(testId) as any; + const row = db + .prepare("SELECT id, model_pinned FROM call_logs WHERE id = ?") + .get(testId) as Record; assert.ok(row, "row should exist"); assert.equal(row.model_pinned, 0, "model_pinned should be 0 when modelPinned=false"); @@ -165,7 +169,7 @@ test("getCallLogs returns modelPinned boolean", async () => { }); const logs = await getCallLogs({ limit: 100 }); - const found = logs.find((l: any) => l.id === testId); + const found = logs.find((l: { id: string }) => l.id === testId); assert.ok(found, "log entry should be found via getCallLogs"); assert.equal(found.modelPinned, true, "getCallLogs should return modelPinned as boolean true");