/**
 * # session/run-identity — the run-identity layer of the config matrix (kestrel-5zl.7)
 *
 * The config matrix (5zl.8 `buildGrid` + leaderboard) is a grid of `(tape × fill_model × config)` CELLS,
 * each holding the replicate runs graded in that cell. This module is the **identity layer** that grid
 * consumes — three PURE derivations (no wall clock, no RNG — RUNTIME §0) that never write a byte back onto
 * the graded bus or the {@link import("../blotter").Blotter}: they are READS of a finalized record, never a
 * new stamp (stamping a run's own id INTO the record it is derived from would be circular):
 *
 *   1. {@link SimRunId} — `(gradedBus) → sha256(serializeBus(gradedBus))`: a DETERMINISTIC identity for one
 *      run. Byte-identical graded bus ⇒ the same 64-hex id; a byte-different graded bus ⇒ a different id, so
 *      it IDENTIFIES replicates (each re-run of a cell is a distinct run). It is EXACTLY the graded-bus
 *      content hash the projector already computes as `blotter.session.bus.sha256`
 *      (`src/blotter/project.ts`) — surfaced here as a named identity, a pure read, not a second stamp:
 *      `SimRunId(bus) === project(bus).session.bus.sha256`.
 *
 *   2. {@link CellKey} — `(tape, fill_model, config) → the grid CELL key`: which `(tape × fill_model ×
 *      config)` cell a run belongs to. Runs sharing a CellKey are REPLICATES in one comparison cell.
 *      Deterministic in its three inputs. The config axis is the {@link cellConfigId cell ConfigId}
 *      (`deriveConfigId(cellCanonicalConfig(config))`, 5zl.6) — the full ConfigId folds the sampling axis
 *      (`temperature`/`thinkingLevel`), `format`, and `label` beyond the envelope, so configs differing ONLY
 *      in temperature are DISTINCT cells (the m9i.2 owner tweak — never pooled as replicates, which would
 *      break the temperature×thinking sweep) — MINUS the `fillSeed` REPLICATE axis, which is STRIPPED, so a
 *      seed ensemble is N replicates in ONE cell (ADR-0016 §3), each a distinct {@link SimRunId}, never N
 *      distinct columns. A seed is measurement noise, not behavior.
 *
 *   3. {@link cellOf} — `(finalizedBlotter) → CellKey`, derived by a PURE read of the Blotter's session
 *      header: the tape axis from `session.instruments`, the fill_model axis from `session.fill_model`, and
 *      the config axis from `session.cell_config_id` when present (the driver-stamped seed-stripped cell axis)
 *      else `session.config_id` (a seedless run — its cell axis equals its full ConfigId; m9i.2).
 *      `cellOf(blotter)` EQUALS `CellKey(tape, fill_model, config)` for the inputs that produced the run —
 *      that reconciliation is what lets buildGrid key any finalized Blotter into the right cell.
 *
 * ## DESIGN NOTE — the config axis is the cell ConfigId: behavior splits, the seed is a replicate
 * The 5zl.7 tracer keyed the config axis on the a57.14 ENVELOPE identity (the subset that survives onto the
 * Blotter), so temperature-only variants shared a cell — different experimental subjects pooled as
 * replicates. The m9i.2 owner review made keying on the ConfigId BLOCKING before any benchmark rows exist:
 * the driver stamps `config_id = deriveConfigId(canonical config)` (the FULL, seeded ConfigId — the run's
 * identity, feeding {@link SimRunId}) on the graded META alongside the envelope, and the projector carries it
 * to `session.config_id`. But the CELL axis is the FULL ConfigId with the `fillSeed` REPLICATE axis STRIPPED
 * ({@link cellConfigId}) — a seed is measurement noise, not behavior, so a seed ensemble is N replicates in
 * ONE cell (ADR-0016 §3), each a distinct SimRunId. The driver stamps that seed-stripped axis as
 * `cell_config_id` ONLY when it diverges from `config_id` (i.e. a seed was present), so a seedless run is
 * byte-identical to before and its cell axis is just `config_id`. {@link CellKey} and {@link cellOf} both key
 * on the cell ConfigId (one definition in `config.ts`, no drift). A pre-m9i.2 Blotter (declared envelope,
 * neither id) falls back to the envelope-identity axis — a LEGACY cell, deliberately distinct from every
 * ConfigId-keyed cell (an unknown sampling axis is never pooled with a known one). This was a pre-rows
 * semantic fix: no persisted CellKeys existed to migrate. {@link SequenceCellKey} (5zl.16.5) composes
 * per-session {@link cellOf} keys, so the multi-session sequence cell absorbs the same axis change with no
 * further definition.
 *
 * ## The tape axis
 * A finalized Blotter carries NO input-tape content hash — `session.bus.sha256` is the GRADED bus hash (the
 * SimRunId), not the tape. The only replicate-invariant, tape-identifying field on the Blotter is
 * `session.instruments`, so it is the tape axis (sufficient for the epic's one-tape × N-configs demo). A
 * precise tape-content id across same-symbol tapes is the same additive-bump decision, deferred.
 */
import type { BusEvent, FillModelStamp, SessionInstrument } from "../bus/index.ts";
import type { Blotter } from "../blotter/index.ts";
import type { AgentConfig } from "./agent.ts";
/**
 * The identity of ONE simulate run: `sha256(serializeBus(gradedBus))` — the canonical graded-bus content
 * hash, reusing the codebase's one bus serializer + sha convention (`src/bus/write.ts`,
 * `src/blotter/project.ts`). Pure and deterministic (no wall clock, no RNG): a byte-identical graded bus
 * yields the same 64-hex id, a byte-different one a different id. This is a READ of the record, NOT a new
 * stamp — it equals the receipt the projector already computes as `blotter.session.bus.sha256`
 * (`SimRunId(bus) === project(bus).session.bus.sha256`), so deriving it changes ZERO bus/Blotter bytes.
 * buildGrid (5zl.8) uses it to tell the replicates in one cell apart.
 */
export declare function SimRunId(gradedBus: readonly BusEvent[]): string;
/**
 * The three-axis identity of a comparison CELL (kestrel-5zl.7). Each axis is the codebase's canonical
 * content id ({@link axisId}) of that axis's data, so the struct depends only on the DATA — never on key
 * insertion order — and two runs whose axes are deep-equal produce a byte-identical struct. buildGrid
 * (5zl.8) groups runs by the {@link CellKey} string form; the struct is exposed for consumers that want to
 * read a single axis without re-deriving it.
 */
export interface CellKeyStruct {
    /** The tape axis — the canonical content id of the session instruments. */
    readonly tape: string;
    /** The judge axis — the canonical content id of the fill-model stamp (name/version/calibration_sha). */
    readonly fill_model: string;
    /** The config axis — the {@link cellConfigId cell ConfigId} (`sha256(canonical AgentConfig minus the
     * `fillSeed` replicate axis)`, 5zl.6): temperature-only variants are DISTINCT (m9i.2 owner tweak),
     * seed-only variants are the SAME (ADR-0016 replicates); the {@link NO_ENVELOPE} sentinel id for a
     * no-envelope run; or (legacy fallback) the envelope-identity id for a pre-tweak Blotter lacking `config_id`. */
    readonly config: string;
}
/**
 * The grid CELL key for a `(tape, fill_model, config)` triple (kestrel-5zl.7; config axis = the
 * {@link cellConfigId cell ConfigId}). Deterministic in its three inputs: same triple ⇒ same key; a
 * different tape, a different fill model, or a config whose cell ConfigId differs on any BEHAVIORAL field —
 * including the non-envelope sampling axis (`temperature`/`thinkingLevel`), `format`, and `label` — ⇒ a
 * different key, so temperature-only variants are DISTINCT cells, never pooled. But a config differing ONLY
 * in the `fillSeed` REPLICATE axis yields the SAME key — a seed ensemble is N replicates in one cell
 * (ADR-0016 §3), each a distinct {@link SimRunId}. The driver stamps the matching axis onto the graded META
 * (`cell_config_id`, or `config_id` when seedless), so a run's {@link cellOf} reconciles with the CellKey of
 * the inputs that produced it. Runs sharing a CellKey are REPLICATES in one comparison cell. Pure (no wall
 * clock, no RNG).
 */
export declare function CellKey(tape: readonly SessionInstrument[], fillModel: FillModelStamp, config: AgentConfig): string;
/**
 * The CELL a finalized run belongs to, derived by a PURE READ of the Blotter's session header (kestrel-5zl.7):
 * the tape axis from `session.instruments`, the fill_model axis from `session.fill_model`, and the config axis
 * from `session.config_id` (the driver-stamped FULL ConfigId — m9i.2 owner tweak; envelope-identity legacy
 * fallback, {@link configAxisOfSession}). Changes NO Blotter bytes. By construction `cellOf(blotter) ===
 * CellKey(tape, fill_model, config)` for the `(tape, fill_model, config)` that produced the run (the driver
 * stamps `config_id` under exactly {@link configAxisOfConfig}'s condition), which is the reconciliation
 * buildGrid (5zl.8) relies on to key any finalized Blotter into the right cell — while each replicate keeps
 * a distinct {@link SimRunId}.
 */
export declare function cellOf(blotter: Blotter): string;
/**
 * The identity of a whole multi-session **Instance**: `sha256` of the ORDERED per-session
 * {@link SimRunId}s (a hash of ordered hashes). Pure, deterministic, and ORDER-SENSITIVE — the Instance
 * identity is a function of its Sessions AND their order — mirroring the {@link SimRunId} pure-read
 * discipline: it writes no byte back onto any bus or Blotter (it is a read of the finalised sequence).
 * Same tapes + same agent turns ⇒ byte-identical per-session buses ⇒ identical SimRunIds ⇒ identical
 * InstanceRunId. A LENGTH-1 Instance's id is `sha256` of its single SimRunId — distinct from that
 * SimRunId's own bytes (a one-session Instance is still an Instance), but fully determined by it.
 */
export declare function InstanceRunId(orderedSimRunIds: readonly string[]): string;
/**
 * The grid CELL key for a multi-session **sequence cell** (kestrel-5zl.16.5): the tape axis extends from a
 * single tape to the ORDERED sequence of per-session {@link CellKey}s. Runs sharing a SequenceCellKey are
 * replicates of the same ordered `(tape × fill_model × config)` sequence. **The length-1 case returns the
 * single session's CellKey UNCHANGED** — so an existing single-session cell key is byte-identical and
 * existing grids are untouched (the degenerate case). A longer sequence hashes the ordered list into a
 * distinct, order-sensitive key. Pure (no wall clock, no RNG).
 */
export declare function SequenceCellKey(perSessionCellKeys: readonly string[]): string;
//# sourceMappingURL=run-identity.d.ts.map