import type { DroppedCriterion, RepairedFinding, VerifiedFinding } from "../judge/verify.js"; import type { ContractLapse } from "../judge/reply.js"; import { type ResolvedConfig, type ShotRecord } from "../types.js"; import { type JudgePass } from "./batches.js"; import { type FormFactorTally } from "./tally.js"; import type { JudgePlan } from "./plan.js"; import type { CheckScope } from "./scope.js"; export interface CheckOutcome { runId: string; model: string; rubricVersion: number; /** The judge panels in scope this run, so "not asked" never reads as "not re-found". */ panels: string[]; shotsConsidered: number; judged: number; cached: number; findings: (VerifiedFinding & { cached?: boolean; })[]; refuted: { title: string; shotId: string; verifierNote: string; judge?: string; }[]; /** Confirmed findings whose plain sentence the refuter had to supply. */ repaired: RepairedFinding[]; /** * Acceptance criteria the refuter could not let stand, and why. * * A criterion no screenshot can settle blocks nothing in a verify-fix: it * comes back not-verifiable and quietly degrades the ruling to "the defect * was not re-filed". Caught at filing time it costs nothing, and the panel * that wrote it can be taught. */ droppedCriteria: DroppedCriterion[]; /** Findings filed with a problem written for one reader; `skills improve` reads these. */ degraded: ContractLapse[]; rejected: number; /** * Shots this run could not vouch for: a panel call that failed, or a reply * that left them out of both findings and cleanShotIds. They are not cached * whole, and they are not clean; some panel did not rule on them. */ unjudged: number; /** * Which panel left which shots unruled, beside the count. * * `unjudged` says a verdict is missing; this says whose it was and about * what. A panel that keeps dropping the same kind of shot has instructions * that are not landing, and `skills improve` can only be taught that if the * run writes down more than a number. */ unaccounted: { panel: string; groupId: string; shotIds: string[]; }[]; /** What this run judged, per platform and form factor; the phone shots are counted apart from the desktop ones. */ formFactors: FormFactorTally[]; /** Panel calls whose judge subprocess failed. The run continued without them. */ failedBatches: { panel: string; shots: number; message: string; }[]; deterministicErrors: number; costUsd: number; reportPath: string; /** * Labelled composite of everything judged, defect-carrying tiles marked. The * calling session sees what lookout saw for the cost of one Read, instead of * spending more context on a dozen full-resolution screenshots than on the * findings themselves. */ contactSheet?: string | null; } export interface RunCheckOptions { /** Called once the cache partition is known, before any judging begins. */ onStart?: (toJudge: ShotRecord[]) => Promise; /** * Invoked after each batch is judged AND verified, in order, never * concurrently. Findings are narrated to the event log as they land, so the * UI shows them while the rest of the app is still being judged. */ onBatch?: (e: { index: number; total: number; shots: ShotRecord[]; findings: VerifiedFinding[]; shotsById: Map; }) => Promise; } export declare function recordOutcome(args: { resolved: ResolvedConfig; scope: CheckScope; plan: JudgePlan; pass: JudgePass; log: (line: string) => void; /** True when no --targets/--routes narrowed the run: the one moment every live group is visible. */ fullScope?: boolean; }): Promise;