diff --git a/auditor/checks/inference.ts b/auditor/checks/inference.ts index f3f0ec3..8103e9d 100644 --- a/auditor/checks/inference.ts +++ b/auditor/checks/inference.ts @@ -426,14 +426,22 @@ async function runModeRunnerInference( error: "unparseable", diagnostic: (e as Error).message, model, }; } - const content: string = body?.response ?? ""; + const content: string = typeof body?.response === "string" ? body.response : ""; const parsed = extractJson(content); + // Number-coerced extractors so a non-numeric upstream value (string, + // null, NaN) collapses to 0 instead of poisoning downstream + // arithmetic. Caught 2026-04-27 by kimi_architect self-audit — + // optional-chaining + ?? only catches null/undefined, not type drift. + const num = (v: unknown): number => { + const n = typeof v === "number" ? v : Number(v); + return Number.isFinite(n) ? n : 0; + }; return { parsed, - latency_ms: body?.latency_ms ?? 0, - enriched_chars: body?.enriched_prompt_chars ?? 0, - bug_fingerprints: body?.sources?.bug_fingerprints_count ?? 0, - matrix_kept: body?.sources?.matrix_chunks_kept ?? 0, + latency_ms: num(body?.latency_ms), + enriched_chars: num(body?.enriched_prompt_chars), + bug_fingerprints: num(body?.sources?.bug_fingerprints_count), + matrix_kept: num(body?.sources?.matrix_chunks_kept), error: parsed ? undefined : "unparseable", diagnostic: parsed ? undefined : content.slice(0, 200), model, diff --git a/auditor/index.ts b/auditor/index.ts index c56f301..05a5972 100644 --- a/auditor/index.ts +++ b/auditor/index.ts @@ -143,6 +143,16 @@ async function runCycle(state: State): Promise { if (state.audit_count_per_pr[prKey] >= MAX_AUDITS_PER_PR) { console.log(`[auditor] PR #${pr.number} reached cap (${MAX_AUDITS_PER_PR} audits) — daemon will skip further audits until reset`); } + // Persist state immediately after each successful audit so the + // increment survives a crash. Pre-2026-04-27 the cycle saved + // once at the end (main.ts:140), which lost the count if the + // daemon was killed mid-cycle. Fix lifted from kimi_architect's + // own audit on this very file. saveState is idempotent + cheap + // (one JSON write), so per-audit cost is negligible. + try { await saveState(state); } + catch (e) { + console.error(`[auditor] saveState mid-cycle failed: ${(e as Error).message} — count held in memory`); + } } catch (e) { console.error(`[auditor] audit failed: ${(e as Error).message}`); }