/**
 * GraderJudgment core domain types — canonical shapes for structured
 * grader output (Doc 03, GRAD-02).
 *
 * Authored INDEPENDENTLY of any Zod schema (D0045 doctrine — Plan 02's
 * `GraderJudgmentSchema` `satisfies` against this type, not the other
 * way around). A tautological `satisfies z.ZodType<z.infer<typeof
 * GraderJudgmentSchema>>` is forbidden.
 *
 * Phase 1 retained the existing pipeline core (`taskId`, `modelId`,
 * `dimension`, `reason`, `score`, `outputFailure?`) as required for
 * backward compat with Phase 0 callers (Doc 03 §"existing, unchanged")
 * and added the GRAD-02 additive fields (`judgmentId`, `subJudgments`,
 * `docCitations`, `failureMode`, `confidence`,
 * `hallucinationCheckedAgainst`, `metadata`) as additive in Phase 1;
 * required from Phase 3 GRAD-05.
 *
 * Phase 3 GRAD-05 has flipped the additive fields to required (this
 * file) and the corresponding Zod schema in
 * `packages/eval/src/adapters/grader-outputs/promptfoo-grader-output.ts`
 * is `.strict()` with `graderJudgmentsVersion = "1.0.0"`. The
 * read-only legacy parser at `…/legacy/promptfoo-grader-output-legacy.ts`
 * (against `LegacyGraderJudgment`) is the named consumer for already-
 * stored historical reports through GRAD-06 cutover.
 *
 * @see docs/decisions/D0049-shared-confidence-contract.md
 * @see docs/decisions/D0052-judgment-ref-granularity.md
 * @see docs/design-docs/actionability-ladder/03-structured-grader-judgments.md
 */
import type { JudgmentId } from "./branded-ids.js";
import type { Confidence } from "./confidence.js";
/**
 * Role enum for doc citations attached to a grader judgment (GRAD-02).
 * Closed string-literal union — Phase 3 may extend.
 */
export type DocCitationRole = "supports" | "contradicts" | "missing" | "irrelevant";
/**
 * A single doc the grader cited while reasoning. `documentId` is the
 * canonical D0052 reference (id, not slug); `slug` is a human-readable
 * annotation only. `hallucinated` is set true at adapter time when the
 * `slug` does not resolve against the task's `contextDocs` set.
 */
export interface DocCitation {
    /** Canonical D0052 document ref (id, not slug). */
    documentId: string;
    /** Optional human-readable annotation. Never the identity. */
    slug?: string;
    role: DocCitationRole;
    /** True when `slug` is not in the resolvable-set. */
    hallucinated?: boolean;
}
/**
 * Per-criterion sub-judgment — one entry per task-criterion bullet
 * (Doc 03 §"per-criterion sub-judgments"). The `criterionId` is the
 * stable identifier declared on the task's `criteria` array (Phase 2
 * GRAD-01 schema-sync), not synthesized at grade time.
 */
export interface CriterionSubJudgment {
    /** Stable criterion identifier — matches `CriterionRef.id` from the task definition (D0052). */
    criterionId: string;
    met: boolean;
    /** ≤280 chars — quote or paraphrase. */
    evidence: string;
    /** Grader self-confidence on this single criterion (D0049). */
    confidence: Confidence;
}
/**
 * The structured grader judgment — Phase 3 GRAD-05 shape.
 *
 * Existing pipeline core (Doc 03 §"existing, unchanged"): `taskId`,
 * `modelId`, `dimension`, `reason`, `score`. The pre-existing
 * `outputFailure?` remains optional. The
 * `contextDocs? (legacy alias: canonicalDocs)` annotation (StoredJudgment
 * extension) lives on the storage extension type, not here.
 *
 * Additive in Phase 1; required from Phase 3 GRAD-05: `judgmentId`,
 * `subJudgments`, `docCitations`, `failureMode`, `confidence`,
 * `hallucinationCheckedAgainst`, `metadata`. The corresponding Zod
 * schema in `packages/eval/src/adapters/grader-outputs/promptfoo-grader-output.ts`
 * is `.strict()` with `graderJudgmentsVersion = "1.0.0"`.
 */
export interface GraderJudgment {
    /** Rubric template name (e.g. "task-completion", "code-correctness"). */
    dimension: string;
    /** The model that produced the response being graded. */
    modelId: string;
    /**
     * True when the model failed to produce meaningful output (empty
     * response, API error, or refusal). Distinguishes infrastructure
     * failures from genuinely incorrect responses — a score of 0 from no
     * output is fundamentally different from a score of 0 from wrong
     * output.
     */
    outputFailure?: boolean;
    /** The grader's natural-language reasoning. */
    reason: string;
    /** Numeric score in [0, 100] (normalized). */
    score: number;
    /** The task this judgment belongs to. */
    taskId: string;
    /**
     * D0052 granular branded id. Required from Phase 3 GRAD-05 — every
     * grader emission carries one.
     */
    judgmentId: JudgmentId;
    /** Per-criterion sub-judgments. */
    subJudgments: CriterionSubJudgment[];
    /** Doc citations with role + hallucinated flag. */
    docCitations: DocCitation[];
    /**
     * Per-dimension failure mode. Phase 3 GRAD-03 stamps the taxonomy
     * literal at the runtime grader-prompt; the value is a free-form
     * string for forward compat with future taxonomy extensions.
     */
    failureMode: string;
    /** Grader self-confidence per D0049. */
    confidence: Confidence;
    /** Hallucination cross-check (Pitfall #11) — union of task.context.docs and run.documentManifest. */
    hallucinationCheckedAgainst: string[];
    /** Metadata about the grader run. */
    metadata: {
        graderModel: string;
        graderJudgmentsVersion: string;
    };
}
/**
 * Wire-format subset of {@link GraderJudgment} — the fields a grader LLM
 * is responsible for emitting in its JSON response. The pipeline parses
 * untrusted grader output against this shape, then synthesizes the
 * remaining storage fields (`taskId`, `modelId`, `dimension`, `judgmentId`,
 * `metadata.{graderModel, graderJudgmentsVersion}`, and
 * `hallucinationCheckedAgainst`) from server-side context.
 *
 * The split exists because four of `GraderJudgment`'s required fields are
 * pipeline-owned semantics the LLM cannot produce correctly:
 *
 *   - `judgmentId` — D0052 branded id with `(taskId, modelId, dimension,
 *     runId)` uniqueness invariant. Minted by `generateJudgmentId`.
 *   - `metadata.graderJudgmentsVersion` — static constant co-located with
 *     the schema (`promptfoo-grader-output.ts:48`).
 *   - `metadata.graderModel` — the grader's deployment alias (pipeline
 *     knows from provider config; the LLM doesn't reliably know its own).
 *   - `hallucinationCheckedAgainst` — the resolvable-set union of
 *     `task.context.docs` and `run.documentManifest`, composed by
 *     `populateHallucinationFields` (gap-analysis-step.ts).
 *
 * Asking the LLM for any of these produces drift; `.strict()` on
 * `GraderJudgmentSchema` amplifies that drift into 100% parse failures
 * (the 2026-05-11 empty-gapReport regression — see W0273 and
 * `docs/audits/2026-05-22-empty-gap-analysis-regression.md`).
 *
 * `taskId`, `modelId`, and `dimension` are also pipeline-supplied (from
 * `result.description`, `result.providerId`, and the rubric-classifier
 * output in `calculate-scores.ts:475-479`) — kept out of the wire shape
 * for the same reason.
 */
export interface GraderEmittedJudgment {
    /** Numeric score in [0, 100] (normalized). */
    score: number;
    /** The grader's natural-language reasoning. */
    reason: string;
    /** Per-dimension failure mode (must match the legal-mode list in the rubric). */
    failureMode: string;
    /** Per-criterion sub-judgments. */
    subJudgments: CriterionSubJudgment[];
    /** Doc citations with role + hallucinated flag. */
    docCitations: DocCitation[];
    /** Grader self-confidence per D0049. */
    confidence: Confidence;
    /**
     * True when the candidate response was empty/whitespace/refused. The
     * pipeline also independently detects this from
     * `result.response.output` — both signals are OR'd.
     */
    outputFailure?: boolean;
}
