/** * What to judge, and what has already been judged. * * Both AI passes are loaded once per run on purpose: a skill amended halfway * through would judge two batches by two different rules, and the ledger key * would then be lying about which rules produced which verdict. * * The partition is the interesting part. Caching is by VIEW GROUP rather than * by single shot, so a group re-judges whole whenever any member's pixels * moved: a comparative finding never loses the shot it compares against. Per * shot caching made a scoped re-check report a dark/light or responsive finding * as gone when only its partner had changed, which is exactly the false "fixed" * an automatic fix loop must never see. */ import { type PriorFinding } from "../judge/engine.js"; import type { Roster } from "../judge/dialogue.js"; import { type PanelRubric } from "../judge/rubric.js"; import { type Skill } from "../skills/load.js"; import { type Ledger, type PanelIdentity } from "../judge/ledger.js"; import type { VerifiedFinding } from "../judge/verify.js"; import { type ResolvedConfig, type ShotRecord } from "../types.js"; import { type Parsed } from "../util.js"; /** One judge call this run owes: one panel over one view group. */ export interface PanelWork { panel: PanelRubric; identity: PanelIdentity; groupId: string; shots: ShotRecord[]; } export interface JudgePlan { /** The panels judging this run, after any --panels narrowing. */ panels: PanelRubric[]; refute: Skill; model: string; /** * The AIs judging this run, in the order they take their turn: the first * proposes and the second rules on what it filed. * * Absent, or holding one entry, is the single-AI pipeline exactly as it was, * which is what every project gets until it asks for a second judge. */ roster?: Roster; /** The challenge instructions, loaded once, or absent when one AI judges. */ challenge?: Skill; ledger: Ledger; /** One ledger identity per panel, keyed by panel name, computed once per run. */ identities: Map; /** The (view group x panel) pairs with no usable verdict on record. */ toJudge: PanelWork[]; /** The distinct shots those pairs cover, for logs and callbacks. */ toJudgeShots: ShotRecord[]; /** * Verdicts read back from the ledger. Their refutation state is whatever * was stored: a --no-verify run and the never-refuted band arrive with * verified false, and refute-on-read is the pass that repairs them. */ cachedFindings: (VerifiedFinding & { cached: boolean; })[]; /** Shots whose every applicable in-scope panel was served from cache. */ cached: number; /** What lookout already has open on these views, so one defect stays one issue. */ prior: PriorFinding[]; /** * What the project declared, for the refuter's own section: its never-file * lines (and, once declared, its design direction). The judges see the same * lines through the rubric's extensions slot; this is the refuter's copy. */ declared?: string; } /** * Who judges, from the primary model and an optional `:` challenger. * * Refused rather than ignored when the AI is not one lookout can judge with: a * flag somebody typed and lookout silently dropped is how a run comes to cost * one judge's money while its operator believes two were watching. */ export declare function rosterOf(model: string, challenger?: string): Roster; /** The roster as the ledger hashes it. */ export declare function rosterKeys(roster: Roster): string[]; export declare function planJudging(resolved: ResolvedConfig, shots: ShotRecord[], parsed: Parsed, /** Overridable so tests can exercise multi-panel partitions before the flip. */ judges?: PanelRubric[]): Promise;