import { z } from "zod"; import { type JsonValue } from "../utils/canonicalize.js"; /** * Annotations: every structured opinion anyone or anything forms about a trace * — a checklist answer, a grader score, an LLM judgement, or the eval harness's * own record of what it launched. (A person's free-form note is not a row; it * is the run directory's `notes.md`.) One append-only `annotations.jsonl` per * run directory; this module owns the row shapes, the deterministic ids, and * the pure fold from rows to effective state. It does no locking and no * writing: `mutations.ts` owns those. */ export type AnnotatorKind = "human" | "grader" | "judge" | "harness"; export type Annotator = { kind: AnnotatorKind; id: string; }; export type Score = { kind: "binary"; pass: boolean; } | { kind: "scalar"; value: number; }; export type ChecklistPayload = { kind: "checklist"; checklist: string; version: number; hash: string; answers: Record; note: string; /** Milliseconds a person spent on this trace before signing off. */ activeMs?: number; }; /** One grader's verdict on one trace in one grading pass. A pass is `passSize` * rows sharing a `passId`; the fold counts a pass only when all of them are * present, so a crash mid-pass can never move effective scores. */ export type ScorePayload = { kind: "score"; passId: string; passSize: number; /** @deprecated Legacy field on old rows; accepted and ignored. */ completesPass?: boolean; name: string; score: Score; weight: number; mustPass: boolean; feedback?: string; /** A checklist grader's answer per point, in the order the test lists them. */ points?: ScorePoint[]; gradersModule?: string; /** The goal an LLM judge scored against (a test's own, or `eval grade --goal`). */ goal?: string; }; export type ScorePoint = { point: string; pass: boolean; evidence: string; }; export type SuiteIdentity = { source: string; sha?: string; }; /** Where a run's graders came from and what the directory's `graders/` holds * for them: the bundled `graders.ts` under `bundleFile` or the * `graders.json` under `jsonFile` (exactly one is set), and each judge file * the graders read by path under its stored name. Grading prefers this * snapshot, so a copied run grades the same anywhere. */ export type GradersIdentity = { source: string; bundleFile?: string; jsonFile?: string; judgeFiles: Record; /** "test": the test's own module. "config": the former `eval.graders` * fallback, which runs written before it was removed may still carry; * `--goal` sets those aside but never a test's own. */ origin: "test" | "config"; }; /** A harness pair stored under `graders/` as ``. * `sha256` covers both files and is the grader's revision. */ export type HarnessRecord = { name: string; visibility: "visible" | "holdout"; agency: string; json: string; sha256: string; maxCost?: number; mustPass?: boolean; }; export type RunOutcome = "ok" | "error" | "timeout" | "cost-cap" | "killed"; /** The eval harness's own row for a trace it launched: which test, which * suite, how the run ended, which flags. The one place harness knowledge * ("I killed it at the cost cap") lives. */ export type RunPayload = { kind: "run"; test: JsonValue; suite: SuiteIdentity | null; /** Absent when the run had no grading module (the goal judge grades it). */ graders?: GradersIdentity; /** The harness pairs this run is graded by. */ harness?: HarnessRecord[]; /** The stored copy of the test's `graderFiles/` directory: its name under * `graders/`, a hash over every path and content. */ graderFiles?: string; ended: RunOutcome; flags: Record; /** The harness's error message when `ended` is not "ok". */ error?: string; /** One invocation of a suite; absent on pre-batch directories. */ batch?: string; /** This test's 1-based repetition within the batch. */ trial?: number; }; export type AnnotationPayload = ChecklistPayload | ScorePayload | RunPayload; /** What a writer supplies; `v`, `id` and `createdAt` are filled at append. */ export type AnnotationDraft = { traceId: string; annotator: Annotator; /** The interactive labeling session (`agency label`) that produced the row, * when there was one. A session is one sitting: one person, one checklist, * one ordered list of traces. Two sittings answering the same questions * the same way are still two rows, because the id hashes this in. */ sessionId?: string; } & AnnotationPayload; export type Annotation = { v: 1; id: string; createdAt: string; } & AnnotationDraft; export type ChecklistAnnotation = Annotation & { kind: "checklist"; }; export declare const ChecklistAnnotationSchema: z.ZodObject<{ kind: z.ZodLiteral<"checklist">; checklist: z.ZodString; version: z.ZodNumber; hash: z.ZodString; answers: z.ZodRecord; note: z.ZodString; activeMs: z.ZodOptional; v: z.ZodLiteral<1>; id: z.ZodString; traceId: z.ZodString; createdAt: z.ZodString; annotator: z.ZodObject<{ kind: z.ZodEnum<{ human: "human"; grader: "grader"; judge: "judge"; harness: "harness"; }>; id: z.ZodString; }, z.core.$strict>; sessionId: z.ZodOptional; }, z.core.$strict>; export declare const AnnotationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"checklist">; checklist: z.ZodString; version: z.ZodNumber; hash: z.ZodString; answers: z.ZodRecord; note: z.ZodString; activeMs: z.ZodOptional; v: z.ZodLiteral<1>; id: z.ZodString; traceId: z.ZodString; createdAt: z.ZodString; annotator: z.ZodObject<{ kind: z.ZodEnum<{ human: "human"; grader: "grader"; judge: "judge"; harness: "harness"; }>; id: z.ZodString; }, z.core.$strict>; sessionId: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"score">; passId: z.ZodString; passSize: z.ZodNumber; completesPass: z.ZodOptional; name: z.ZodString; score: z.ZodDiscriminatedUnion<[z.ZodObject<{ kind: z.ZodLiteral<"binary">; pass: z.ZodBoolean; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"scalar">; value: z.ZodNumber; }, z.core.$strict>], "kind">; weight: z.ZodNumber; mustPass: z.ZodBoolean; feedback: z.ZodOptional; points: z.ZodOptional>>; gradersModule: z.ZodOptional; goal: z.ZodOptional; v: z.ZodLiteral<1>; id: z.ZodString; traceId: z.ZodString; createdAt: z.ZodString; annotator: z.ZodObject<{ kind: z.ZodEnum<{ human: "human"; grader: "grader"; judge: "judge"; harness: "harness"; }>; id: z.ZodString; }, z.core.$strict>; sessionId: z.ZodOptional; }, z.core.$strict>, z.ZodObject<{ kind: z.ZodLiteral<"run">; test: z.ZodType>; suite: z.ZodNullable; }, z.core.$strict>>; graders: z.ZodOptional; jsonFile: z.ZodOptional; judgeFiles: z.ZodRecord; origin: z.ZodEnum<{ test: "test"; config: "config"; }>; }, z.core.$strict>>; harness: z.ZodOptional; agency: z.ZodString; json: z.ZodString; sha256: z.ZodString; maxCost: z.ZodOptional; mustPass: z.ZodOptional; }, z.core.$strict>>>; graderFiles: z.ZodOptional; ended: z.ZodEnum<{ error: "error"; ok: "ok"; timeout: "timeout"; "cost-cap": "cost-cap"; killed: "killed"; }>; flags: z.ZodRecord>>; error: z.ZodOptional; batch: z.ZodOptional; trial: z.ZodOptional; v: z.ZodLiteral<1>; id: z.ZodString; traceId: z.ZodString; createdAt: z.ZodString; annotator: z.ZodObject<{ kind: z.ZodEnum<{ human: "human"; grader: "grader"; judge: "judge"; harness: "harness"; }>; id: z.ZodString; }, z.core.$strict>; sessionId: z.ZodOptional; }, z.core.$strict>], "kind">; /** The same opinion always gets the same id, so a retried append rewrites * nothing and doubles nothing. Score rows carry their `passId`, which makes * the id per pass: a second pass with the same verdict is a new row. */ export declare function annotationId(draft: AnnotationDraft): string; /** Complete a draft into a row: validated, with `v`, `id` and `createdAt`. */ export declare function completeAnnotation(draft: AnnotationDraft, createdAt: string): Annotation; /** @internal Used by readRunDirectory and the mutation owners, not by feature * callers. Tolerant: a malformed row is reported and skipped (grading has * already been paid for; one bad line must not hide the rest); a final line * without its newline is a torn write and is ignored. */ export declare function readAnnotations(annotationsPath: string, reportWarning: (message: string) => void): Annotation[]; export type EffectiveChecklistJudgement = { annotator: Annotator; answers: Record; note: string; }; export type EffectiveTraceAnnotations = { /** Keyed by `${annotator.kind}:${lineage}:${name}`, where the lineage is the * annotator id without its `@` suffix, so every version of one * grader or judge shares a key; the latest row from the latest COMPLETE pass * wins. */ scores: Record; /** How many complete grading passes scored this trace. The effective scores * come from the latest; the earlier ones are history, still on disk. */ gradingPasses: number; /** Keyed by `${checklist}:${annotator.kind}:${annotator.id}`, answers folded * per question in append order so a restored question keeps its answer. */ checklists: Record; run: Annotation | null; }; /** @internal Pure fold from rows to effective state, in append order. Feature * callers read `snapshot.effectiveAnnotations`; this is exposed for focused * tests. */ export declare function foldAnnotations(rows: readonly Annotation[]): Record; /** `goal-judge@1` and `goal-judge@2` are one judge at two revisions; the * newer supersedes the older rather than averaging with it. */ export declare function annotatorLineage(id: string): string;