/** * Artifact contract for sharded `vally experiment` runs. * * Every experiment run — sharded or not — writes two stable artifacts in * addition to the per-variant `results.jsonl` / `run-summary.jsonl`: * * - **`plan-snapshot.json`** captures everything `summarizePlan` and the * markdown reporter need that is NOT recoverable from the per-trial * JSONL alone (planned stimulus sets, thresholds, grader configs, * plan-time diagnostics), re-keyed off the experiment-relative eval * path so it is machine-independent. Merge reconstructs the report * from this plus the concatenated JSONL. * * - **`shard-manifest.json`** records run + shard identity in two blocks. * The `experiment` block is byte-identical across every shard of a run * (merge asserts agreement; a future `experiment compare` consumes it). * The `shard` block is per-shard bookkeeping that only merge reads. * * This module is the single schema authority for both files. It owns the * TypeScript shapes, the format `version`, and pure assembly helpers. All * filesystem I/O and absolute→relative path mapping lives in the CLI * runner; these helpers operate on already-relativized plain data so they * stay pure and unit-testable. */ import type { EnvironmentConfig, StimulusGraderConfig } from "../eval/types.js"; import type { ScopedDiagnostic } from "../pipeline/plan.js"; /** Current artifact format version. Merge refuses anything newer. */ export declare const SHARD_ARTIFACT_VERSION: 1; /** A planned stimulus together with the grader configs that scored it. * Grader configs are persisted in full so the snapshot is self-describing; * `summarizePlan` itself only needs to know whether any grader was present, * but persisting the configs future-proofs the artifact. The resolved * per-stimulus `environment` is persisted too because the markdown reporter * renders a per-stimulus Environment column from it; without it, merge would * reconstruct the report with stimulus-level environment overrides missing. */ export interface PlanSnapshotStimulus { /** Resolved stimulus name (unique within its eval). */ name: string; /** Effective grader configs for this stimulus (may be empty). */ graders: StimulusGraderConfig[]; /** Resolved per-stimulus environment override, if any. Persisted as-is, * so path-valued fields hold absolute, machine-specific paths and are NOT * yet machine-independent like the rest of the snapshot. Re-relativizing * these is deferred until the consumer that reads them exists; planDigest * is unaffected, as it hashes the pre-resolution experiment-relative env. */ environment?: EnvironmentConfig; } /** One `(eval × variant × model)` combination, mirroring * {@link EvalPlanMetadata} but keyed on the experiment-relative eval path * (`evalFile`) instead of the absolute `evalFilePath`. */ export interface PlanSnapshotEval { evalName: string; /** Eval file path RELATIVE to the experiment directory. */ evalFile: string; variant: string; model?: string; plannedStimulusCount: number; inputStimulusCount: number; plannedStimulusNames: string[]; runs: number; threshold?: number; failure?: string; evalDescription?: string; judgeModel?: string; /** Resolved eval-level environment, if any. Same caveat as * {@link PlanSnapshotStimulus.environment}: path-valued fields are absolute * and not yet machine-independent. */ environment?: EnvironmentConfig; executorName?: string; /** Planned stimuli with their grader configs, in planned (source) order. */ stimuli: PlanSnapshotStimulus[]; } /** A {@link ScopedDiagnostic} re-keyed onto the experiment-relative eval * path. Run-level diagnostics (no scope) carry `scope: undefined`. */ export interface PlanSnapshotDiagnostic extends Omit { scope?: { evalName: string; evalFile: string; variant: string; model?: string; }; } /** Persisted plan metadata needed to rebuild a {@link RunSummary} from * per-trial JSONL during merge. */ export interface PlanSnapshot { type: "experiment-plan-snapshot"; version: typeof SHARD_ARTIFACT_VERSION; /** Fingerprint of the whole planned run (see `computePlanDigest`). Must * match the manifest's `experiment.planDigest`. */ planDigest: string; vallyVersion: string; evals: PlanSnapshotEval[]; diagnostics: PlanSnapshotDiagnostic[]; } /** Stamp the schema envelope onto already-relativized snapshot data. */ export declare function assemblePlanSnapshot(input: { planDigest: string; vallyVersion: string; evals: PlanSnapshotEval[]; diagnostics: PlanSnapshotDiagnostic[]; }): PlanSnapshot; /** The strategy that selected this shard's work. The built-in single-shard * (unsharded) case uses `{ name: "none", specifier: "none" }`. */ export interface ShardStrategyRef { /** Strategy name (e.g. `round-robin`, `by-stimulus`, `none`). */ name: string; /** Module specifier or built-in identifier the strategy was loaded from. */ specifier: string; } /** Run identity. IDENTICAL across every shard of a run; merge asserts that * all shards agree on this block before reconciling. */ export interface ExperimentIdentity { runId: string; name: string; /** Locator for the experiment spec (relative path or basename). */ experimentFile: string; experimentHash: string; vallyVersion: string; baseline: string; /** Variant names in declaration order. */ variantNames: string[]; /** JSON-pointer axes that were varied. */ vary: string[]; /** Fingerprint of the whole planned run; the cross-shard equality check. */ planDigest: string; } /** Per-shard bookkeeping. Only merge consumes this block. */ export interface ShardIdentity { /** 1-based shard index. */ index: number; /** Total shard count. */ total: number; strategy: ShardStrategyRef; /** Shard keys this shard was assigned (the planned subset). */ selectedShardKeys: string[]; /** Shard keys this shard actually produced a result for. */ completedShardKeys: string[]; status: "complete" | "interrupted"; } /** The `shard-manifest.json` artifact. Written last and atomically, so its * presence with `status: "complete"` is a reliable per-shard done signal. */ export interface ShardManifest { type: "experiment-shard-manifest"; version: typeof SHARD_ARTIFACT_VERSION; experiment: ExperimentIdentity; shard: ShardIdentity; } /** Stamp the schema envelope onto run + shard identity blocks. */ export declare function assembleShardManifest(input: { experiment: ExperimentIdentity; shard: ShardIdentity; }): ShardManifest; /** Build the `shard` block for an unsharded run (treated as shard 1/1). * Every planned key is both selected and — for a clean run — completed. */ export declare function singleShardIdentity(input: { selectedShardKeys: string[]; completedShardKeys: string[]; status?: "complete" | "interrupted"; }): ShardIdentity; /** The `experiment-manifest.json` artifact written by `experiment merge`. * Promotes the agreed-upon {@link ExperimentIdentity} (byte-identical across * every merged shard) into a standalone, enveloped run identity. This is the * stable contract a future `experiment compare` reads — it is intentionally a * superset-free wrapper of the manifest's `experiment` block so the two stay * in lockstep. */ export interface ExperimentManifest { type: "experiment-manifest"; version: typeof SHARD_ARTIFACT_VERSION; experiment: ExperimentIdentity; } /** Stamp the schema envelope onto the agreed run identity. */ export declare function assembleExperimentManifest(input: { experiment: ExperimentIdentity; }): ExperimentManifest; //# sourceMappingURL=manifest.d.ts.map