/**
 * Artifact registry — single source of truth for AILF's external artifact types.
 *
 * Every artifact that lives in GCS (or on the local filesystem after W0050)
 * declares itself here exactly once: association axes, layout, path builder,
 * entry schema, mime, cap, and (for per-entry layouts) format/parse helpers.
 * Eval writers, the API Gateway's signing endpoint, and the Studio hook all
 * consume this same record.
 *
 * Adding a new artifact type = one entry here. No call-site changes needed in
 * the generic writer / signer / hook — they all iterate the registry.
 *
 * ## Association axes (D0033 / W0049)
 *
 * Each descriptor declares the pipeline dimensions it is evidence about. At
 * module load a structural invariant rejects descriptors that declare an
 * unbounded axis (`task`, `model`, `trial`) but a `"bulk"` layout — such a
 * shape would serialize as a single JSON array that blows past the object-
 * size cap at scale. The invariant converts that class of mistake into a
 * process-won't-start error rather than a silent data bug.
 *
 * @see docs/decisions/D0032-run-anchored-artifact-store.md
 * @see docs/decisions/D0033-unified-run-anchored-artifact-capture.md
 * @see docs/design-docs/unified-run-artifacts.md (§ M1, § M5)
 */
import { z } from "zod";
import type { AssociationAxis, AssociationValues, EntryKey, RunId } from "./types/branded-ids.js";
/** Layouts supported by the artifact store. */
export type ArtifactLayout = "bulk" | "per-entry";
/** MIME types the registry knows how to place on disk. */
export type ArtifactMime = "application/json" | "application/x-ndjson" | "text/markdown" | "application/yaml";
/**
 * Who is permitted to write the artifact (D0050).
 *
 * `"pipeline"` artifacts are written during a pipeline run by a pipeline
 * step (the legacy default — every pre-D0050 descriptor is implicitly
 * `"pipeline"`).
 *
 * `"post-hoc"` artifacts are written **after** the run is finalized, by
 * a separate command/action (e.g. `ailf interpret`, a Studio action, an
 * API endpoint). Post-hoc artifacts may accumulate multiple versions
 * within a single run prefix; the writer-side guard
 * (`assertWritePolicyMatches`) prevents pipeline writers from emitting
 * post-hoc descriptors and vice versa.
 */
export type WritePolicy = "pipeline" | "post-hoc";
/**
 * Source-of-version for descriptors that opt into the versioning axis
 * (D0050). The path builder consumes the version segment to produce
 * `runs/{runId}/{slug}-{version}.{ext}`.
 *
 * - `"schemaVersion"` — version tracks the writer's serialized-shape
 *   schema (e.g. attribution payload schema).
 * - `"promptVersion"` — version tracks the prompt the artifact was
 *   produced under (e.g. a regenerated grader prompt).
 * - `"diagnosisVersion"` — version tracks the diagnosis run that produced
 *   the artifact (e.g. `ailf interpret`'s versioned output).
 *
 * The union is intentionally narrow; D0050 leaves a function-shaped
 * versioner as a future extension if version semantics get richer.
 */
export type VersionedBy = "schemaVersion" | "promptVersion" | "diagnosisVersion";
/**
 * Identity of the calling context when invoking the artifact writer.
 * Mirrors `WritePolicy` but at the writer-instance level (a writer is
 * either a pipeline writer or a post-hoc writer; descriptors declare
 * which kind of writer is permitted to emit them).
 */
export type WriteSource = WritePolicy;
/**
 * Behavior when a payload exceeds a descriptor's `capBytes`:
 *   - `"reject"` — drop the write and log a warning (default for bounded entries).
 *   - `"trailing-truncate"` — serialize up to the cap, mark the entry truncated.
 *   - `"fielded-truncate"` — a descriptor-specific truncator trims the largest
 *     fields first (used by richly-nested payloads whose trailing bytes aren't
 *     the right thing to drop).
 *   - `"trial-oversize"` — drop overflow rows for the current trial, keep the
 *     rest of the trial's artifacts intact. Reserved for NDJSON row-streamed
 *     artifacts; no descriptor uses it since D0088 flipped `traces` to
 *     per-entry JSON, but the policy is retained for a future unbounded type.
 */
export type ArtifactTruncationPolicy = "reject" | "trailing-truncate" | "fielded-truncate" | "trial-oversize";
/** The union of every artifact type known to AILF. */
export type ArtifactType = "runManifest" | "scoreSummary" | "pipelineResult" | "pipelineContext" | "documentManifest" | "prComment" | "readinessReport" | "reportSnapshot" | "autoComparison" | "gapReport" | "sinkResults" | "notificationResults" | "callbackRequest" | "callbackResponse" | "configSnapshot" | "evalConfigGenerated" | "comparisonReport" | "discoveryReport" | "failureModes" | "renderedPrompts" | "rawResults" | "testOutputs" | "graderPrompts" | "graderJudgments" | "symbolPreflight" | "consumerVerification" | "traces" | "diagnosis" | "perEntryAttribution" | "attributionMeta";
/**
 * Result of parsing a per-entry key into a sanitized filename component.
 * Success carries the sanitized value; failure carries a reason for 4xx responses.
 */
export type ParsedEntryKey = {
    ok: true;
    sanitized: EntryKey;
} | {
    ok: false;
    reason: string;
};
/**
 * Declaration of which pipeline axes an artifact is evidence about. Axis order
 * drives both the entry-key layout (join order) and the invariant check
 * (unbounded axis ⇒ per-entry).
 */
export interface AssociationDeclaration {
    readonly axes: readonly AssociationAxis[];
}
/**
 * Optional inline preview declaration. When present, the writer extracts a
 * small summary shape at write time and attaches it to the manifest entry so
 * Studio list views can render without fetching the external payload.
 *
 * Schema-only in W0049 — the write-time `extract()` wiring lands in W0051.
 * `extract` takes `unknown` here so the registry can store heterogeneously
 * typed descriptors without variance issues; W0051 tightens the parameter
 * type to the owning descriptor's `TEntry` at the concrete-descriptor level.
 */
export interface ManifestPreviewDeclaration<TPreview = unknown> {
    readonly schema: z.ZodType<TPreview>;
    readonly extract: (entry: unknown) => TPreview;
    readonly capBytes: number;
}
/**
 * Callback signature for `ArtifactDescriptor.objectPath`. Aliased so
 * external path builders (e.g. `diagnosisPathBuilder`) declare a named
 * return type rather than re-typing the inline arrow signature.
 */
export type ArtifactObjectPath = (runId: RunId, entryKey?: string, version?: string) => string;
/**
 * Opt-in marker for the bulk-versioned-with-report-axis carve-out
 * (Plan 01-03 / ITER2-BLOCKER-01). Any descriptor that needs to combine
 * `layout: "bulk"` with a `report` axis (e.g. the post-hoc diagnosis
 * artifact) must set
 * `pathSafetyMarker: BULK_VERSIONED_WITH_REPORT_AXIS` to opt in to the
 * carve-out in `assertValidArtifactDescriptor`.
 *
 * `Symbol.for(...)` is registry-keyed: it survives minification (the
 * key is a string literal that minifiers cannot rename) and yields
 * cross-module-instance equality by spec. This makes it bundler-stable
 * — a deliberate replacement for any source-text reflection on the
 * descriptor's `objectPath` closure body.
 *
 * The marker alone does NOT trigger the carve-out — the four structural
 * conditions (bulk + versionedBy + run+report axes) must also hold.
 * Conversely, the structural conditions alone do NOT trigger the
 * carve-out without the marker. This is an explicit opt-in.
 */
export declare const BULK_VERSIONED_WITH_REPORT_AXIS: unique symbol;
/**
 * Per-type declaration consumed by writers, signers, and readers.
 *
 * @typeParam TEntry   - Shape of a single entry payload.
 * @typeParam TPreview - Shape of the optional manifest preview (W0051).
 */
export interface ArtifactDescriptor<TEntry = unknown, TPreview = unknown> {
    /** The artifact type identifier (matches the key in ARTIFACT_REGISTRY). */
    readonly type: ArtifactType;
    /** Bulk (one object per run) or per-entry (one object per entryKey). */
    readonly layout: ArtifactLayout;
    /** Kebab-case filename stem. Used by both bulk paths and per-entry dir names. */
    readonly slug: string;
    /** Pipeline axes this artifact is evidence about (D0033). */
    readonly association: AssociationDeclaration;
    /** Zod schema for validating a single entry payload. */
    readonly entrySchema: z.ZodType<TEntry>;
    /** Wire-level content type (drives filename extension + upload Content-Type). */
    readonly mime: ArtifactMime;
    /** Upper bound on serialized entry bytes. Enforced by the writer. */
    readonly capBytes: number;
    /** Behavior when a payload exceeds `capBytes`. Defaults to `"reject"`. */
    readonly truncation?: ArtifactTruncationPolicy;
    /**
     * Some artifacts are expected not to fire every run (e.g. `prComment` only
     * fires on PR-context runs). Marking them `optional` keeps the manifest
     * catalog honest about what is absent vs. failed.
     */
    readonly optional?: boolean;
    /**
     * Who writes this artifact (D0050). When unset, defaults to `"pipeline"`
     * — matching every pre-D0050 descriptor's behavior. `"post-hoc"`
     * artifacts are emitted by a separate command after the run finalizes
     * and may accumulate multiple versions per run prefix.
     */
    readonly writePolicy?: WritePolicy;
    /**
     * Source of the version segment in the path (D0050). When set, the
     * descriptor's `objectPath` produces a versioned path
     * (`runs/{runId}/{slug}-{version}.{ext}`) and `version` is required.
     * When unset, the path is unversioned (legacy behavior).
     */
    readonly versionedBy?: VersionedBy;
    /**
     * Optional opt-in marker for the bulk-versioned-with-report-axis
     * carve-out in `assertValidArtifactDescriptor` (Plan 01-03 /
     * ITER2-BLOCKER-01). See `BULK_VERSIONED_WITH_REPORT_AXIS` for full
     * semantics. Descriptors without this marker are subject to the
     * standard bounded-axis rule.
     */
    readonly pathSafetyMarker?: typeof BULK_VERSIONED_WITH_REPORT_AXIS;
    /**
     * Build the GCS object path for this artifact.
     * - bulk: returns `runs/{runId}/{slug}.{ext}`; `entryKey` is ignored.
     * - per-entry: requires `entryKey`; returns `runs/{runId}/{slug}/{sanitized}.{ext}`.
     * - versioned (`versionedBy` set): requires `version`; returns
     *   `runs/{runId}/{slug}-{version}.{ext}` for bulk-shaped versioned
     *   descriptors. `entryKey` is ignored on versioned bulk paths.
     */
    readonly objectPath: ArtifactObjectPath;
    /**
     * Extract the positional args (beyond `runId`) to pass to `objectPath`
     * when this descriptor needs more than `runId` to build its path.
     *
     * The default plain-bulk path is `objectPath(runId)`. The default
     * per-entry path is `objectPath(runId, entryKey)`. Descriptors whose
     * path needs additional axes from `association` and/or fields from the
     * payload (e.g. the bulk-versioned-with-report-axis carve-out used by
     * the diagnosis descriptor — see `BULK_VERSIONED_WITH_REPORT_AXIS`)
     * set this function. The writer calls it and spreads the result:
     *   `objectPath(runId, ...extractPathArgs(association, payload))`.
     *
     * Returning `[undefined]` (or any `undefined` element) is fine — the
     * underlying builder is expected to throw a meaningful error in that
     * case, which the writer surfaces via its existing try/catch.
     */
    readonly extractPathArgs?: (association: AssociationValues, payload: unknown) => readonly (string | undefined)[];
    /**
     * Build a filename-safe entry key from association values. Only meaningful
     * for `layout === "per-entry"` — bulk descriptors omit it.
     */
    readonly formatEntryKey?: (assoc: AssociationValues) => EntryKey;
    /**
     * Validate a per-entry key received on the wire (URL param, etc.) and
     * return its sanitized filename component. Only meaningful for
     * `layout === "per-entry"`; bulk descriptors omit it.
     */
    readonly parseEntryKey?: (key: string) => ParsedEntryKey;
    /**
     * Optional inline preview for triage-friendly list views. Populated at
     * write time in W0051 — in this phase the field is schema-only and is
     * carried on the descriptor so downstream tooling can see what the
     * preview shape will be.
     */
    readonly manifestPreview?: ManifestPreviewDeclaration<TPreview>;
}
/**
 * Bulk-shaped path builder with a `{version}` segment appended to the
 * filename stem (D0050). Used by descriptors that opt into the versioning
 * axis via `versionedBy`. Multiple versions coexist under the same run
 * prefix as siblings:
 *
 *   `runs/{runId}/{slug}-v1.{ext}`
 *   `runs/{runId}/{slug}-v2.{ext}`
 *
 * The version segment is sanitized via `sanitizeEntryKey` so versions
 * containing `/` or wire separators don't accidentally nest into
 * subdirectories. Empty / whitespace-only versions are rejected — a
 * versioned descriptor with no version is a programmer error, not silently
 * collapsed to an unversioned path.
 */
export declare function versionedPathBuilder(slug: string, mime: ArtifactMime): (runId: RunId, _entryKey?: string, version?: string) => string;
/**
 * Two-axis post-hoc path builder for the diagnosis descriptor (Plan 01-03,
 * D-09). Filename: `diagnosis-{diagnosisVersion}-{cardSetShortHash}.json`
 * where `cardSetShortHash = sha256(cardVersion).slice(0, 12)`.
 *
 * The compound version argument is `${diagnosisVersion}|${cardVersion}`
 * to fit the existing 3-arg `objectPath` signature without growing the
 * shared `ArtifactObjectPath` shape. Callers should use
 * `encodeDiagnosisPathVersion()` rather than building the compound
 * string by hand.
 *
 * The diagnosis descriptor opts into the bulk-versioned-with-report-axis
 * carve-out via `pathSafetyMarker: BULK_VERSIONED_WITH_REPORT_AXIS`
 * (ITER2-BLOCKER-01). The builder itself does not need to know about
 * the marker — it just builds the path; the carve-out is a registration-
 * time concern in `assertValidArtifactDescriptor`.
 */
export declare function diagnosisPathBuilder(): ArtifactObjectPath;
/**
 * Pack the diagnosis descriptor's two version segments into the existing
 * single-string `version` slot of `ArtifactObjectPath`. Separator is `|`;
 * `diagnosisVersion` MUST NOT contain `|` (the function rejects that case).
 */
export declare function encodeDiagnosisPathVersion(diagnosisVersion: string, cardVersion: string): string;
/**
 * Convert an entry key (wire format, e.g. `{taskId}::{modelId}`) to a
 * filename-safe component.
 *
 * - `::` → `--` so the wire separator doesn't show up in the filename.
 * - `/` → `_` so task names like "Content Lake with @sanity/client" don't
 *   create unintended GCS subdirectories and so `ls` against the per-entry
 *   directory shows one row per entry.
 *
 * Single colons (`:`) are preserved — modelIds like
 * `anthropic:messages:claude-opus-4-6` are valid GCS object names.
 *
 * NOTE: this mapping is not bijective. A taskId containing literal `--`
 * combined with a modelId could in theory collide with one whose taskId
 * contains `::`. In practice, production taskIds don't exercise these
 * combinations.
 */
export declare function sanitizeEntryKey(key: string): string;
/** Test-only reset for the legacy-key warning flag. Not exported publicly. */
export declare function __resetLegacyTestOutputsWarning(): void;
/**
 * Bucket a classified-judgment count into a severity tier. Thresholds are
 * count-based: a category with 10+ judgments is critical triage; 5–9 is
 * high; 2–4 medium; 0–1 low. Exposed so producers can mirror this when
 * supplying explicit severity values on entries that have richer signal
 * (e.g. per-dimension aggregate).
 *
 * Exported so the `failure_modes` fact-table extractor (D0091) derives the
 * same severity the manifest preview shows: one source of truth, no drift.
 */
export declare function severityForCount(count: number): "low" | "medium" | "high" | "critical";
/**
 * The canonical artifact descriptor for every artifact type. Iterate with
 * `Object.values(ARTIFACT_REGISTRY)` or look up by `ARTIFACT_REGISTRY[type]`.
 *
 * Axes, layout, and caps come from docs/design-docs/unified-run-artifacts.md
 * § M5. The mapping is verified by the L1 contract tests.
 */
export declare const ARTIFACT_REGISTRY: {
    readonly [T in ArtifactType]: ArtifactDescriptor;
};
/** All artifact types in declaration order. */
export declare const ARTIFACT_TYPES: readonly ArtifactType[];
/**
 * Type guard — validates that an arbitrary string is a known artifact type.
 * Useful at API Gateway boundaries where the type comes from a URL parameter.
 */
export declare function isArtifactType(value: string): value is ArtifactType;
/**
 * Structural check run against a single descriptor. Exported so L1 contract
 * tests can construct an invalid descriptor inline and assert the throw.
 */
export declare function assertValidArtifactDescriptor(desc: ArtifactDescriptor): void;
/**
 * Thrown when a writer's identity (`writeSource`) doesn't match a
 * descriptor's `writePolicy`. Pipeline writers can't emit `"post-hoc"`
 * descriptors (the post-hoc artifact would land mid-run, before the run
 * finalizes); post-hoc writers can't emit `"pipeline"` descriptors
 * (those should have been written by the pipeline itself).
 *
 * The error type is intentionally distinct from `Error` so CI can match
 * on the class and surface mismatches clearly in failure logs.
 */
export declare class WritePolicyMismatchError extends Error {
    readonly code: "WRITE_POLICY_MISMATCH";
    readonly artifactType: ArtifactType;
    readonly descriptorPolicy: WritePolicy;
    readonly writerSource: WriteSource;
    constructor(opts: {
        artifactType: ArtifactType;
        descriptorPolicy: WritePolicy;
        writerSource: WriteSource;
    });
}
/**
 * Resolve a descriptor's effective write policy. Defaults to `"pipeline"`
 * when unset — preserves backward compatibility with every pre-D0050
 * descriptor that doesn't declare the field.
 */
export declare function resolveWritePolicy(desc: ArtifactDescriptor): WritePolicy;
/**
 * Writer-side guard. Call at the top of `emit()` / `appendNdjson()` in
 * every artifact writer that physically writes bytes (the in-memory test
 * doubles don't need it). Throws `WritePolicyMismatchError` on
 * mismatch; returns silently on a match. Pure function — no I/O, safe
 * to invoke from any layer.
 */
export declare function assertWritePolicyMatches(writerSource: WriteSource, descriptor: ArtifactDescriptor): void;
/**
 * Slim-shape preview for `"post-hoc"` descriptors. Replaces the
 * fixed-path semantics that pipeline-written artifacts use (a single
 * known path per descriptor) with `present: boolean` plus an optional
 * `latestVersion: string` — necessary because:
 *
 *   - A post-hoc artifact may have zero versions (never written) or
 *     multiple versions (regenerated). A fixed `path` cannot encode
 *     either.
 *   - Slim-shape consumers (Studio rollups) want to know "is there a
 *     diagnosis for this run?" without enumerating versioned siblings.
 *
 * Post-hoc writers populate `latestVersion` after a successful write
 * (last-write-wins per-version semantics, per D0050 open-question
 * resolution). The full versioned payload is fetched via the
 * descriptor's `objectPath(runId, undefined, latestVersion)`.
 */
export declare const postHocSlimPreviewSchema: z.ZodObject<{
    present: z.ZodBoolean;
    latestVersion: z.ZodOptional<z.ZodString>;
}, z.core.$strip>;
export type PostHocSlimPreview = z.infer<typeof postHocSlimPreviewSchema>;
/**
 * Build a post-hoc slim-shape preview. `latestVersion` is omitted when
 * absent rather than emitted as `undefined`, matching the optional-field
 * convention used elsewhere in the registry.
 */
export declare function buildPostHocSlimPreview(opts: {
    present: boolean;
    latestVersion?: string;
}): PostHocSlimPreview;
/**
 * Build the inline preview for a manifest entry at write time. Returns
 * `undefined` when the descriptor has no `manifestPreview` declaration,
 * when extraction throws, when the schema rejects the extracted shape, or
 * when cap-enforcement cannot bring the serialized preview under the
 * descriptor's `capBytes` budget.
 *
 * Failure is non-fatal: preview is triage metadata, never critical-path data.
 * The full payload still lands in the external artifact regardless.
 *
 * Cap enforcement (hard truncation) iteratively shortens the longest string
 * field on the preview object by ~10% per pass until the JSON-serialized
 * form fits under `capBytes` or no string remains to trim. Nested objects
 * are not recursed — previews are intentionally shallow (a handful of
 * top-level fields).
 */
export declare function buildManifestPreview<TPreview = unknown>(descriptor: ArtifactDescriptor<unknown, TPreview>, payload: unknown): TPreview | undefined;
