/**
 * `failure_modes`, one row per run × mode × failure category (D0091).
 *
 * The failure-taxonomy table: how many low-scoring judgments fell into each
 * classified failure category, per mode, with a derived severity. Answers
 * "which failure categories dominate and how does the mix drift over time".
 *
 * Grain is one `failureModes` entry (per run × mode × category; the writer
 * emits one entry per classified category, zero-count categories skipped). A
 * run whose gap-analysis produced no classified failures yields zero rows.
 *
 * `severity` is derived with the same `severityForCount` bucketing the
 * artifact manifest preview uses, one source of truth, no drift.
 *
 * The `failureModes` emit object (`{ category, count, title, judgments }`) has
 * no canonical core type, so its read-projection is a local minimal interface;
 * the heavy `judgments` array stays in GCS, reachable via `gs_uri`.
 *
 * @see packages/eval/src/orchestration/steps/gap-analysis-step.ts, emit site
 * @see docs/design-docs/bigquery-analytics-fact-loader.md
 */
import { z } from "zod";
import { severityForCount } from "../../artifact-registry.js";
import { defineFactTable, } from "../descriptor.js";
import { axisString, LOADED_AT_COLUMN, nonNegIntOrNull, runFactBase, runFactBaseShape, RUN_FACT_BASE_COLUMNS, strOrNull, } from "./common.js";
const rowSchema = z.object({
    ...runFactBaseShape,
    mode: z.string(),
    category: z.string(),
    count: z.number(),
    severity: z.enum(["low", "medium", "high", "critical"]),
    title: z.string().nullable(),
});
const schema = [
    ...RUN_FACT_BASE_COLUMNS,
    { name: "mode", type: "STRING" },
    { name: "category", type: "STRING", description: "Failure category id." },
    {
        name: "count",
        type: "INT64",
        mode: "REQUIRED",
        description: "Classified judgments.",
    },
    {
        name: "severity",
        type: "STRING",
        mode: "REQUIRED",
        description: "low | medium | high | critical.",
    },
    {
        name: "title",
        type: "STRING",
        description: "Human-readable category title.",
    },
    LOADED_AT_COLUMN,
];
// ---------------------------------------------------------------------------
// Extractor
// ---------------------------------------------------------------------------
function extract(ctx) {
    return ctx.artifacts
        .entries("failureModes")
        .map((entry) => {
        const payload = entry.payload;
        // Prefer the payload category; fall back to the entry's `category` axis.
        const category = strOrNull(payload.category) ?? axisString(entry.association.category);
        const count = nonNegIntOrNull(payload.count) ?? 0;
        return {
            ...runFactBase(ctx, entry.sourceUri),
            mode: axisString(entry.association.mode),
            category,
            count,
            severity: severityForCount(count),
            title: strOrNull(payload.title),
        };
    });
}
export const failureModes = defineFactTable({
    table: "failure_modes",
    dataset: "ailf_facts",
    sourceArtifacts: ["failureModes"],
    schema,
    rowSchema,
    partitionField: "completed_at",
    clusterFields: ["mode", "category"],
    mergeKey: ["run_id", "mode", "category"],
    extract,
});
export { rowSchema as failureModeFactRowSchema };
