kestrel.markets
Version:
A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.
719 lines (671 loc) • 41.3 kB
text/typescript
/**
* # session/grid — the Simulate config-comparison harness: buildGrid + CellAggregate + leaderboard (kestrel-5zl.8)
*
* THE POINT of Simulate: the SAME recorded tape graded under N configs → a GRID of graded
* {@link import("../blotter").Blotter}s → a pure comparison LEADERBOARD. This module is the two PURE
* derivations that turn a set of FINALIZED Blotters into that grid + table — no wall clock, no RNG
* (RUNTIME §0) — and it is a MEASUREMENT / reporting layer: it only READS the finalized record, and never
* writes a byte back onto the graded bus or the Blotter (zero golden / e2e churn), and stamps NO new schema
* field. It builds strictly on the run-identity layer (5zl.7 {@link cellOf}) + the config layer (5zl.6):
*
* 1. {@link buildGrid} — `(runs: readonly Blotter[]) → Grid`. Group the finalized Blotters by their 5zl.7
* {@link cellOf} CellKey (tape × fill_model × config-ConfigId). Runs sharing a CellKey are REPLICATES in
* one comparison cell; each replicate keeps a DISTINCT SimRunId, read straight off the finalized record
* as `blotter.session.bus.sha256` (which equals `SimRunId(gradedBus)` — the 5zl.7 pure-read
* reconciliation, so no raw bus is needed). Each cell is a {@link CellAggregate} over its replicate runs.
*
* 2. {@link CellAggregate} — the honest measurements across a cell's replicate runs, using the Session/day
* (one finalized Blotter = one run) as the INDEPENDENT EVIDENCE UNIT, and REPORTING UNCERTAINTY: every
* numeric axis is a {@link Dist} — `{ n, mean, min, max, stdev }` — a distribution/spread over the
* replicates, NEVER a single cherry-picked point (a single-run cell reports `n = 1`, spread `0` — still a
* distribution, never a bare scalar). The axes come straight off the Blotter: financial (EV / P&L), risk,
* process, fidelity, provider, cost — plus a {@link CertificationBreakdown}.
*
* 3. {@link leaderboard} — `(grid: Grid) → readonly LeaderboardRow[]`. A pure comparison table: ONE ROW PER
* CELL, the honest multi-axis measurements reported SIDE BY SIDE with per-axis uncertainty, and NO opaque
* omnibus score (the m9i principle: "report the financial, risk, process, attention, provider, and
* interface measurements WITHOUT an opaque omnibus score"). The axes are laid out; they are never blended
* into one number, and never sorted into a rank — the caller reads the axes it cares about.
*
* ## Determinism (RUNTIME §0 — a CI invariant)
* Both derivations are PURE and INPUT-ORDER-INSENSITIVE: the same runs in ANY order ⇒ a byte-identical grid
* and table. buildGrid sorts the cells by CellKey and each cell's replicates by SimRunId BEFORE any
* aggregation, so every reduction (mean, stdev, …) folds over a FIXED order — no order-dependent float drift.
*
* ## Honesty (the 5zl.8 mandate)
* The config axis of a cell is the FULL ConfigId (`session.config_id`, m9i.2 owner tweak) — a PROVISIONAL
* run keys by its own stamped ConfigId (or, absent any config identity, the NO_ENVELOPE sentinel), so it
* lands in its OWN cell, distinct from every differently-configured cell, and can NEVER be silently absorbed
* into a certified cell's numbers (a provisional and a certified run can only share a cell by sharing the
* ENTIRE config — and an identical config yields an identical completeness verdict). The cell's
* {@link CertificationBreakdown} carries the certified/provisional COUNTS and a WORST-CASE verdict
* (provisional if ANY replicate is provisional) — the projector's grade is surfaced, never laundered.
* Provider / cost read the run's `session.envelope`, absent (`null`) for a no-envelope / provisional cell —
* an honest gap, never a fabricated identity.
*/
import type { Blotter, ExperimentalEnvelope, FidelitySelfLimitation, Verdict } from "../blotter/index.ts";
import { InstanceRunId, SequenceCellKey, cellOf } from "./run-identity.ts";
// ─────────────────────────────────────────────────────────────────────────────
// Dist — the per-axis distribution over a cell's replicate runs (uncertainty, never one point)
// ─────────────────────────────────────────────────────────────────────────────
/**
* The distribution of one numeric measurement over a cell's replicate runs — the honest unit of the
* leaderboard. `n` is the number of independent evidence units (Session/day runs) the measurement rests on;
* `mean` is their arithmetic mean; `min`/`max`/`stdev` are the SPREAD — the uncertainty around the mean.
* A single-run cell still reports a Dist (`n = 1`, `min = max = mean`, `stdev = 0`) — never a bare scalar, so
* a consumer can never mistake one run for a converged estimate. `stdev` is the POPULATION standard deviation
* (divide by `n`, not `n − 1`): a descriptive spread of the runs in hand, not an inferential estimator.
*/
export interface Dist {
/** The number of replicate runs (Session/day evidence units) this distribution rests on. */
readonly n: number;
/** The arithmetic mean of the per-run measurements. */
readonly mean: number;
/** The smallest per-run measurement (the low end of the spread). */
readonly min: number;
/** The largest per-run measurement (the high end of the spread). */
readonly max: number;
/** The population standard deviation of the per-run measurements (0 for a single-run cell). */
readonly stdev: number;
}
/** Summarize a fixed-order list of per-run measurements as a {@link Dist}. PURE — folds in the caller's
* (deterministic) order, so the same values always yield byte-identical floats. `values` is never empty (a
* cell always holds ≥ 1 run). Exported so other measurement layers (e.g. the percept-lab score log,
* kestrel-m9i.12) fold their replicate probes with the SAME semantics and their rows port into this grid. */
export function distOf(values: readonly number[]): Dist {
const n = values.length;
let sum = 0;
let min = values[0]!;
let max = values[0]!;
for (const v of values) {
sum += v;
if (v < min) min = v;
if (v > max) max = v;
}
const mean = sum / n;
let sq = 0;
for (const v of values) sq += (v - mean) * (v - mean);
const stdev = Math.sqrt(sq / n);
return { n, mean, min, max, stdev };
}
// ─────────────────────────────────────────────────────────────────────────────
// UnderlierHold + buyAndHoldBaseline — the per-cell buy-and-hold BASELINE (kestrel-m9i.19)
// ─────────────────────────────────────────────────────────────────────────────
/**
* The cell underlier's price series over the horizon plus the units held — the source of the buy-and-hold
* BASELINE (kestrel-m9i.19). The driver extracts the open → close SPOT series of the cell underlier from the
* cell tape and hands it in (grid never reaches into the bus): `prices[0]` is the OPEN, `prices[last]` the
* CLOSE, and `qty` the units held across the horizon. `symbol` is the underlier (SPY for an equity basket, BTC
* for crypto — §4.1 public benchmark vocabulary), surfaced onto the {@link Benchmark} for provenance.
*/
export interface UnderlierHold {
/** The underlier symbol held over the horizon (SPY for an equity basket, BTC for crypto — §4.1). */
readonly symbol: string;
/** The underlier price series over the cell horizon: `[0]` = open, `[last]` = close (read off the cell tape). */
readonly prices: readonly number[];
/** The units of the underlier held across the horizon. */
readonly qty: number;
}
/**
* The buy-and-hold P&L of an underlier over the horizon (kestrel-m9i.19): `qty × (close − open)`, where
* `open = prices[0]` and `close = prices[last]`. PURE + byte-stable (no wall clock, no RNG — RUNTIME §0): the
* interior prints don't matter, the hold is open → close and `qty` scales it linearly. FAIL-CLOSED — an
* underlier price the baseline needs but cannot resolve (an empty series, or a non-finite open / close / qty)
* is a typed refusal, NEVER a silent 0 (a silent 0-alpha would launder market direction as judgment).
*/
export function buyAndHoldBaseline(hold: UnderlierHold): number {
const { symbol, prices, qty } = hold;
if (prices.length === 0) {
throw new Error(
`grid: buyAndHoldBaseline cannot resolve an empty underlier price series for ${symbol} (fail-closed — never a silent 0-alpha)`,
);
}
const open = prices[0]!;
const close = prices[prices.length - 1]!;
if (!Number.isFinite(open) || !Number.isFinite(close)) {
throw new Error(
`grid: buyAndHoldBaseline cannot resolve a non-finite underlier price for ${symbol} (open=${open}, close=${close}) (fail-closed — never a silent 0-alpha)`,
);
}
if (!Number.isFinite(qty)) {
throw new Error(
`grid: buyAndHoldBaseline cannot resolve a non-finite qty for the ${symbol} hold (qty=${qty}) (fail-closed — never a silent 0-alpha)`,
);
}
return qty * (close - open);
}
// ─────────────────────────────────────────────────────────────────────────────
// The honest axes — each a PURE read of the finalized Blotter (no schema field added)
// ─────────────────────────────────────────────────────────────────────────────
/** The benchmark sub-axis of the financial axis (kestrel-m9i.19): the per-cell buy-and-hold BASELINE plus the
* benchmark-relative ALPHA, reported ALONGSIDE the absolute `expected` EV (never replacing it). Position /
* swing / crypto cells are BETA-dominated — absolute P&L there measures market DIRECTION, not judgment — so
* this rides the cell EV against a buy-and-hold of the cell underlier (SPY for an equity basket, BTC for
* crypto — §4.1) over the cell horizon. Present ONLY when a per-cell hold is supplied to {@link buildGrid};
* ABSENT (the field is not stamped) otherwise, so an un-baselined cell is byte-identical to today's grid. */
export interface Benchmark {
/** The underlier held over the horizon for the baseline (SPY for equity, BTC for crypto — §4.1). */
readonly symbol: string;
/** The buy-and-hold P&L of that underlier over the horizon ({@link buyAndHoldBaseline}) — a scalar $ amount,
* the provenance `alpha` is measured against (mirrors 5zl.16.8 `InstanceFinancial.baseline`). */
readonly baseline: number;
/** The benchmark-relative alpha as a {@link Dist} over the replicates: `alpha_i = expected_i − baseline`, so
* `alpha.mean = cell EV − baseline` (the 5zl.16.8 identity), reported WITH grid-consistent uncertainty. */
readonly alpha: Dist;
}
/** The financial (EV / P&L) axis — the conservative definite-fill floor and the expected total, plus the
* expected-$ partitioned by fill support (the `calibrated` slice a grader banks vs the `extrapolated` slice it
* refuses to bank, 9gu.3). Each a distribution over the cell's replicate runs. */
export interface FinancialAxis {
/** `totals.floor` — realized / definite-fill $ — across the replicates. */
readonly floor: Dist;
/** `totals.expected` — E[$] — across the replicates. */
readonly expected: Dist;
/** The `calibrated`-support slice of `totals.expected` (a grader banks this). */
readonly calibrated: Dist;
/** The `extrapolated`-support slice of `totals.expected` (a grader refuses to bank this). */
readonly extrapolated: Dist;
/** The per-cell benchmark baseline + benchmark-relative alpha (kestrel-m9i.19), reported ALONGSIDE the
* absolute `expected` EV. Present ONLY when a per-cell hold is supplied to {@link buildGrid}; the field is
* OMITTED otherwise (no grid churn — an un-baselined cell is byte-identical to today's). */
readonly benchmark?: Benchmark;
}
/** The risk axis — the headline taint and the far-OTM directional-cap disposition (9gu.6). Reported as honest
* counts / a union of reasons across the cell's replicate runs (categorical, not a numeric distribution). */
export interface RiskAxis {
/** How many replicate runs carried a TAINTED headline (`totals.headline.tainted`). */
readonly tainted_runs: number;
/** The union of `totals.headline.reasons` across the replicates, de-duplicated and sorted (deterministic). */
readonly headline_reasons: readonly string[];
/** The far-OTM directional-cap disposition tallied over every order in every replicate run (9gu.6). */
readonly directional_cap: {
readonly applied: number;
readonly exempt: number;
readonly na: number;
};
}
/** The process axis — the shape of the run's activity (orders, fills, reprices, plans), each a distribution
* over the cell's replicate runs. */
export interface ProcessAxis {
/** The placed-order count per run. */
readonly orders: Dist;
/** The definite-fill count per run (`orders[].filled`). */
readonly fills: Dist;
/** Σ `orders[].reprice_count` per run — the cancel/replace churn. */
readonly reprices: Dist;
/** The plan-record count per run. */
readonly plans: Dist;
}
/** The fidelity axis — the realism `level` breakdown plus the judge's self-limitation (what the fill model
* structurally could NOT observe). The self-limitation is invariant across a cell (the fill model is a CellKey
* axis), so it is reported once. */
export interface FidelityAxis {
/** The count of replicate runs at each fidelity level (e.g. `{ modeled: 3 }`) — the level breakdown. */
readonly levels: Readonly<Record<string, number>>;
/** The judge's self-limitation (fill model + what it could not observe) — invariant across the cell. */
readonly self_limitation: FidelitySelfLimitation;
}
/** The cost axis — the INJECTED token accounting (`session.envelope.token_accounting`), each a distribution
* over the cell's replicate runs. Config-level in the current schema (constant across a cell's replicates), so
* the spread is `0` — reported honestly as a Dist all the same. */
export interface CostAxis {
readonly input: Dist;
readonly output: Dist;
readonly thinking: Dist;
}
/** The certification breakdown of a cell — the projector's grade, surfaced, never laundered: the
* certified/provisional replicate COUNTS plus a WORST-CASE verdict (provisional if ANY replicate is
* provisional). A provisional run is thus never rendered as if certified. */
export interface CertificationBreakdown {
/** How many replicate runs the projector graded `certified`. */
readonly certified: number;
/** How many replicate runs the projector graded `provisional`. */
readonly provisional: number;
/** The cell's worst-case verdict: `provisional` if any replicate is provisional, else `certified`. */
readonly verdict: Verdict;
}
/** The rankable breakdown of a cell (kestrel-m9i.25) — the DERIVED leaderboard-ELIGIBILITY axis, surfaced
* DISTINCTLY from {@link CertificationBreakdown}. The two are ORTHOGONAL: a cell can be
* `certification.verdict === "certified"` yet `rankable.verdict === false` (a certified PRE-cutoff cell).
* The rankable/not-rankable replicate COUNTS plus a WORST-CASE verdict — the cell is rankable ONLY if EVERY
* replicate is rankable (one not-rankable replicate makes the cell not rankable), mirroring the
* worst-case discipline of the certification breakdown. */
export interface RankableBreakdown {
/** How many replicate runs the projector derived `rankable`. */
readonly rankable: number;
/** How many replicate runs the projector derived NOT rankable. */
readonly not_rankable: number;
/** The cell's worst-case verdict: `true` only if every replicate is rankable, else `false`. */
readonly verdict: boolean;
}
// ─────────────────────────────────────────────────────────────────────────────
// CellAggregate + Grid
// ─────────────────────────────────────────────────────────────────────────────
/**
* The aggregate of ONE comparison cell — the honest, multi-axis measurements over the cell's replicate runs,
* each reported WITH uncertainty (a {@link Dist} for every numeric axis) and keyed to the cell's 5zl.7
* CellKey. Deliberately carries NO blended / omnibus score: the axes stand side by side (the m9i principle).
*/
export interface CellAggregate {
/** The 5zl.7 CellKey (tape × fill_model × config-ConfigId) this cell aggregates — its stable identity. */
readonly cell: string;
/** The number of replicate runs (Session/day evidence units) in this cell. */
readonly runs: number;
/** The DISTINCT SimRunIds of the replicates (= `session.bus.sha256`), sorted for a byte-stable order. */
readonly runIds: readonly string[];
/** The financial (EV / P&L) axis. */
readonly financial: FinancialAxis;
/** The risk axis (headline taint + directional-cap disposition). */
readonly risk: RiskAxis;
/** The process axis (orders / fills / reprices / plans). */
readonly process: ProcessAxis;
/** The fidelity axis (level breakdown + judge self-limitation). */
readonly fidelity: FidelityAxis;
/** The provider axis — the a57.14 experimental envelope (the author's identity), invariant across the cell,
* or `null` for a no-envelope cell (an honest gap, never a fabricated identity). */
readonly provider: ExperimentalEnvelope | null;
/** The cost axis (injected token accounting), or `null` when the cell declares no envelope. */
readonly cost: CostAxis | null;
/** The certification breakdown — the projector's grade, surfaced. */
readonly certification: CertificationBreakdown;
/** The rankable breakdown (kestrel-m9i.25) — the DERIVED leaderboard-eligibility axis, surfaced DISTINCTLY
* from {@link certification} (a certified cell can be not rankable). */
readonly rankable: RankableBreakdown;
}
/** The config-comparison GRID: the deterministically ordered set of {@link CellAggregate}s, one per distinct
* CellKey. Cells are sorted by CellKey so the grid is byte-stable regardless of input order. */
export interface Grid {
readonly cells: readonly CellAggregate[];
}
// ─────────────────────────────────────────────────────────────────────────────
// buildGrid — group finalized Blotters by CellKey, aggregate each cell's replicates
// ─────────────────────────────────────────────────────────────────────────────
/** The SimRunId of a finalized run, read straight off the record (`session.bus.sha256`, which the projector
* already computed and equals `SimRunId(gradedBus)` — the 5zl.7 pure-read reconciliation). */
function runIdOf(blotter: Blotter): string {
return blotter.session.bus.sha256;
}
/** Aggregate one cell's already-sorted replicate runs into a {@link CellAggregate}. PURE. `runs` is
* non-empty and in a FIXED (SimRunId-sorted) order, so every reduction is deterministic. When a per-cell
* `hold` is supplied (kestrel-m9i.19), the financial axis additionally carries the buy-and-hold BASELINE +
* benchmark-relative ALPHA; without one the axis is byte-identical to today's (the field is omitted). */
function aggregateCell(cell: string, runs: readonly Blotter[], hold?: UnderlierHold): CellAggregate {
// Financial — the floor / expected totals and the support-partitioned expected, per run.
const claimExpected = (b: Blotter, support: string): number => {
const c = b.fill_claim.find((f) => f.support === support);
return c === undefined ? 0 : c.expected;
};
const financialBase = {
floor: distOf(runs.map((b) => b.totals.floor)),
expected: distOf(runs.map((b) => b.totals.expected)),
calibrated: distOf(runs.map((b) => claimExpected(b, "calibrated"))),
extrapolated: distOf(runs.map((b) => claimExpected(b, "extrapolated"))),
};
// The benchmark rides ALONGSIDE the absolute EV, computed ONLY when a per-cell hold is supplied. baseline =
// the buy-and-hold P&L of the cell underlier; alpha_i = expected_i − baseline (a Dist WITH uncertainty, so
// alpha.mean = cell EV − baseline, the 5zl.16.8 identity, over the SAME fixed replicate order as `expected`).
// FAIL-CLOSED: an unresolvable underlier price propagates buyAndHoldBaseline's typed refusal — never a
// silent 0-alpha. When absent, the `benchmark` field is OMITTED (not `undefined`), so an un-baselined cell
// is byte-identical to today's grid (no churn — existing goldens/e2e intact).
const financial: FinancialAxis =
hold === undefined
? financialBase
: (() => {
const baseline = buyAndHoldBaseline(hold);
return {
...financialBase,
benchmark: {
symbol: hold.symbol,
baseline,
alpha: distOf(runs.map((b) => b.totals.expected - baseline)),
},
};
})();
// Risk — headline taint + the directional-cap disposition tallied over every order in every run.
let taintedRuns = 0;
const reasons = new Set<string>();
const cap = { applied: 0, exempt: 0, na: 0 };
for (const b of runs) {
if (b.totals.headline.tainted) taintedRuns += 1;
for (const r of b.totals.headline.reasons) reasons.add(r);
for (const o of b.orders) {
if (o.directional_cap === "applied") cap.applied += 1;
else if (o.directional_cap === "exempt") cap.exempt += 1;
else if (o.directional_cap === "na") cap.na += 1;
}
}
const risk: RiskAxis = {
tainted_runs: taintedRuns,
headline_reasons: [...reasons].sort(),
directional_cap: cap,
};
// Process — orders / fills / reprices / plans, per run.
const process: ProcessAxis = {
orders: distOf(runs.map((b) => b.orders.length)),
fills: distOf(runs.map((b) => b.orders.filter((o) => o.filled).length)),
reprices: distOf(runs.map((b) => b.orders.reduce((a, o) => a + o.reprice_count, 0))),
plans: distOf(runs.map((b) => b.plans.length)),
};
// Fidelity — the level breakdown + the (cell-invariant) judge self-limitation.
const levels: Record<string, number> = {};
for (const b of runs) {
const lvl = b.session.fidelity.level;
levels[lvl] = (levels[lvl] ?? 0) + 1;
}
const fidelity: FidelityAxis = {
levels,
self_limitation: runs[0]!.session.fidelity.self_limitation,
};
// Provider / cost — the a57.14 envelope, invariant across the cell (the config axis of the CellKey). A
// no-envelope cell (a provisional / NO-LLM run) has neither — surfaced honestly as `null`, never fabricated.
const envelope = runs[0]!.session.envelope ?? null;
const provider: ExperimentalEnvelope | null = envelope;
const cost: CostAxis | null =
envelope === null
? null
: {
input: distOf(runs.map((b) => b.session.envelope!.token_accounting.input)),
output: distOf(runs.map((b) => b.session.envelope!.token_accounting.output)),
thinking: distOf(runs.map((b) => b.session.envelope!.token_accounting.thinking)),
};
// Certification — the projector's grade, surfaced with counts + a worst-case verdict.
let certified = 0;
let provisional = 0;
for (const b of runs) {
if (b.certification.verdict === "certified") certified += 1;
else provisional += 1;
}
const certification: CertificationBreakdown = {
certified,
provisional,
verdict: provisional > 0 ? "provisional" : "certified",
};
// Rankability — the DERIVED leaderboard-eligibility axis (kestrel-m9i.25), surfaced DISTINCTLY from
// certification: the rankable/not-rankable replicate counts + a WORST-CASE verdict (the cell is rankable
// ONLY if every replicate is rankable — one not-rankable replicate makes the cell not rankable).
let rankableRuns = 0;
let notRankableRuns = 0;
for (const b of runs) {
if (b.rankability.rankable) rankableRuns += 1;
else notRankableRuns += 1;
}
const rankable: RankableBreakdown = {
rankable: rankableRuns,
not_rankable: notRankableRuns,
verdict: notRankableRuns === 0,
};
return {
cell,
runs: runs.length,
runIds: runs.map(runIdOf),
financial,
risk,
process,
fidelity,
provider,
cost,
certification,
rankable,
};
}
/**
* The ADDITIVE options of {@link buildGrid} (kestrel-m9i.19): a per-cell buy-and-hold BASELINE source, keyed by
* the 5zl.7 {@link cellOf} CellKey (the tape axis already identifies the cell underlier). A cell WITH a supplied
* hold gets a `financial.benchmark` (baseline + alpha); a cell WITHOUT one is byte-identical to today's grid.
* The whole options object is OPTIONAL — `buildGrid(runs)` and `buildGrid(runs, {})` are byte-identical to each
* other and to today's grid (no churn).
*/
export interface BuildGridOptions {
/** Per-cell (CellKey → {@link UnderlierHold}) — the buy-and-hold source for that cell's benchmark baseline. */
readonly baselines?: ReadonlyMap<string, UnderlierHold>;
}
/**
* Group a set of FINALIZED Blotters into the config-comparison {@link Grid} (kestrel-5zl.8). Each run is keyed
* by its 5zl.7 {@link cellOf} CellKey (tape × fill_model × config-ConfigId); runs sharing a key are REPLICATES
* aggregated into one {@link CellAggregate} (each keeping a distinct SimRunId). PURE and
* INPUT-ORDER-INSENSITIVE: cells are sorted by CellKey and each cell's replicates by SimRunId before any
* aggregation, so the same runs in ANY order yield a byte-identical grid (RUNTIME §0). Reads finalized
* Blotters only — writes no bus/Blotter byte, stamps no schema field.
*
* ADDITIVE (kestrel-m9i.19): the optional 2nd arg supplies a per-cell buy-and-hold BASELINE. A cell whose
* CellKey appears in `opts.baselines` gets a `financial.benchmark` (the baseline + benchmark-relative alpha,
* ALONGSIDE absolute EV); every other cell — and the whole grid when no options are supplied — is BYTE-IDENTICAL
* to today's. The baseline touches neither CellKey nor SimRunId nor the leaderboard row shape. FAIL-CLOSED: an
* unresolvable supplied underlier price propagates {@link buyAndHoldBaseline}'s typed refusal (never a silent 0).
*/
export function buildGrid(runs: readonly Blotter[], opts?: BuildGridOptions): Grid {
const baselines = opts?.baselines;
const byCell = new Map<string, Blotter[]>();
for (const b of runs) {
const key = cellOf(b);
const bucket = byCell.get(key);
if (bucket === undefined) byCell.set(key, [b]);
else bucket.push(b);
}
const cells: CellAggregate[] = [];
for (const key of [...byCell.keys()].sort()) {
const bucket = byCell.get(key)!;
// Sort replicates by SimRunId so every downstream reduction folds over a FIXED order (determinism).
const sorted = [...bucket].sort((a, b) => (runIdOf(a) < runIdOf(b) ? -1 : runIdOf(a) > runIdOf(b) ? 1 : 0));
cells.push(aggregateCell(key, sorted, baselines?.get(key)));
}
return { cells };
}
// ─────────────────────────────────────────────────────────────────────────────
// leaderboard — one row per cell, the axes side by side, NO opaque omnibus score
// ─────────────────────────────────────────────────────────────────────────────
/** One row of the {@link leaderboard} — one comparison cell's honest measurements laid out SIDE BY SIDE with
* per-axis uncertainty. Deliberately carries NO omnibus / composite / rank field (the m9i principle). */
export interface LeaderboardRow {
/** The 5zl.7 CellKey this row reports on. */
readonly cell: string;
/** The replicate (Session/day evidence-unit) count behind this row's measurements. */
readonly runs: number;
/** The distinct SimRunIds of the replicates (provenance for the row's numbers). */
readonly runIds: readonly string[];
/** The financial axis, WITH uncertainty. */
readonly financial: FinancialAxis;
/** The risk axis. */
readonly risk: RiskAxis;
/** The process axis, WITH uncertainty. */
readonly process: ProcessAxis;
/** The fidelity axis. */
readonly fidelity: FidelityAxis;
/** The provider (envelope identity) axis, or `null`. */
readonly provider: ExperimentalEnvelope | null;
/** The cost axis, WITH uncertainty, or `null`. */
readonly cost: CostAxis | null;
/** The certification breakdown — a provisional cell is surfaced as provisional, never as certified. */
readonly certification: CertificationBreakdown;
/** The rankable breakdown (kestrel-m9i.25) — leaderboard ELIGIBILITY, a DISTINCT axis from
* {@link certification}: a row can be `certification.verdict === "certified"` yet `rankable.verdict === false`. */
readonly rankable: RankableBreakdown;
}
/**
* Project a {@link Grid} into the comparison LEADERBOARD (kestrel-5zl.8): ONE ROW PER CELL, the honest
* multi-axis measurements reported SIDE BY SIDE with per-axis uncertainty, and NO opaque omnibus score — the
* axes are laid out, never blended into one number and never sorted into a rank (the m9i principle). PURE and
* deterministic: the row order mirrors the grid's (CellKey-sorted), so the same grid always yields a
* byte-identical table.
*/
export function leaderboard(grid: Grid): readonly LeaderboardRow[] {
return grid.cells.map((c) => ({
cell: c.cell,
runs: c.runs,
runIds: c.runIds,
financial: c.financial,
risk: c.risk,
process: c.process,
fidelity: c.fidelity,
provider: c.provider,
cost: c.cost,
certification: c.certification,
rankable: c.rankable,
}));
}
// ─────────────────────────────────────────────────────────────────────────────
// InstanceBlotter — fold an ORDERED SEQUENCE of a multi-session Instance's Blotters into ONE chained record
// (kestrel-5zl.16.8). Where buildGrid folds order-INSENSITIVE REPLICATES of one cell into a distribution,
// this folds an order-SENSITIVE SEQUENCE of sessions into one chained ledger — the ordering IS the semantics.
// ─────────────────────────────────────────────────────────────────────────────
/**
* One link of the Instance's chained-cash ledger — session `ordinal`'s place in the running P&L chain, a PURE
* read of the finalized per-session Blotter. `openingCash` is the cash session N+1 OPENS from (= session N's
* `closingCash`), `realized` is the session's definite-fill floor (`totals.floor`), and `closingCash =
* openingCash + realized`. This re-derives, purely from the ordered Blotters, the exact chain `carry.ts`
* threads (`carry.cash = prior.cash + settle.floorTotal`, and `settle.floorTotal === totals.floor`).
*/
export interface SessionLink {
/** The session ordinal `k` (its position in the ordered sequence). */
readonly ordinal: number;
/** The session's {@link SimRunId} (`session.bus.sha256`) — provenance for this link of the chain. */
readonly runId: string;
/** The cash this session OPENS from — session `k-1`'s `closingCash` (`0` at the sequence origin). */
readonly openingCash: number;
/** The session's realized / definite-fill P&L (`totals.floor`) — the chain increment. */
readonly realized: number;
/** The cash this session CLOSES at — `openingCash + realized`, carried into session `k+1`'s open. */
readonly closingCash: number;
}
/**
* The Instance's financial axis — the chained cash of the whole horizon plus the benchmark-relative ALPHA
* hook. `cash` is the final running cash of the chain (`Σ per-session realized floor`); `instanceEV` is the
* chained expected value (`Σ totals.expected`); `alpha = instanceEV − baseline` is the honest judgment axis
* (ONE measure alongside `cash`/`instanceEV`, never a top-level collapse), and `baseline` is surfaced as the
* provenance of that alpha. A missing / non-finite baseline is a typed refusal, never a silent 0-alpha default.
*/
export interface InstanceFinancial {
/** The chained cash of the whole horizon (`Σ per-session realized floor` = the final `closingCash`). */
readonly cash: number;
/** The chained expected value of the whole horizon (`Σ totals.expected`). */
readonly instanceEV: number;
/** The benchmark-relative alpha: `instanceEV − baseline` (one honest axis, never raw EV, never raw beta). */
readonly alpha: number;
/** The supplied benchmark expected value (in $) alpha is measured against — the provenance of `alpha`. */
readonly baseline: number;
}
/**
* The sequence-level GRADE of an Instance — the honest multi-axis measurements over the ordered sequence,
* each an axis reported SIDE BY SIDE, with NO opaque omnibus score (the m9i principle). The `financial` axis
* is the CHAINED, benchmark-relative {@link InstanceFinancial}; the non-financial axes
* (risk/process/fidelity/provider/cost/certification) reuse the EXISTING grid axes ({@link aggregateCell})
* over the sessions-as-replicate-runs, so a length-1 Instance's non-financial axes are BYTE-IDENTICAL to
* today's single-Session grid cell.
*/
export interface InstanceGrade {
/** The chained + benchmark-relative financial axis. */
readonly financial: InstanceFinancial;
/** The risk axis (headline taint + directional-cap disposition) over the sessions. */
readonly risk: RiskAxis;
/** The process axis (orders / fills / reprices / plans) over the sessions. */
readonly process: ProcessAxis;
/** The fidelity axis (level breakdown + judge self-limitation) over the sessions. */
readonly fidelity: FidelityAxis;
/** The provider axis — the a57.14 experimental envelope, or `null` for a no-envelope Instance. */
readonly provider: ExperimentalEnvelope | null;
/** The cost axis (injected token accounting), or `null` when no envelope is declared. */
readonly cost: CostAxis | null;
/** The certification breakdown — the projector's grade over the sessions, surfaced. */
readonly certification: CertificationBreakdown;
}
/**
* The aggregate of ONE multi-session **Instance** (kestrel-5zl.16.8): the ORDERED sequence of per-session
* {@link SessionLink}s (the chained-cash ledger) plus the sequence-level {@link InstanceGrade}, keyed by the
* Instance's 5zl.16.5 identity. Deliberately carries NO blended / omnibus score — the axes stand side by side
* (the m9i principle); alpha is ONE honest axis inside `financial`, never a collapse.
*/
export interface InstanceBlotter {
/** The whole Instance's identity — `sha256` of the ORDERED per-session SimRunIds (kestrel-5zl.16.5). */
readonly instanceRunId: string;
/** The Instance's sequence CELL key — the ordered per-session {@link cellOf} keys (length-1 unchanged). */
readonly sequenceCellKey: string;
/** The ordered chained-cash ledger — one {@link SessionLink} per session, in SEQUENCE order. */
readonly sessions: readonly SessionLink[];
/** The sequence-level grade — honest multi-axis, chained financial + benchmark-relative alpha, NO omnibus. */
readonly grade: InstanceGrade;
}
/** Build options for {@link buildInstanceBlotter} — the supplied benchmark against which alpha is measured. */
export interface BuildInstanceBlotterOptions {
/** The benchmark expected value (in $) alpha subtracts from `instanceEV`. Required + finite (fail-closed). */
readonly baseline: number;
}
/**
* Fold the ORDERED per-session Blotters of a multi-session Instance into ONE {@link InstanceBlotter}
* (kestrel-5zl.16.8) — a PURE read of the finalized sequence (no wall clock, no RNG — RUNTIME §0; no bus /
* Blotter byte written, no schema field stamped). Where {@link buildGrid} folds order-INSENSITIVE REPLICATES
* into a distribution, this folds an order-SENSITIVE SEQUENCE into one CHAINED record — the ordering IS the
* semantics. Three things:
*
* 1. CHAINS per-session cash across the sequence: session N+1 OPENS from session N's carried cash
* (`openingCash[k] = closingCash[k-1] = Σ_{j<k} realized_j`, `realized_k = totals.floor`), exactly
* re-deriving the chain `carry.ts` threads (`carry.cash = prior.cash + settle.floorTotal`). Deterministic
* + byte-stable: the ledger folds in SEQUENCE order (no clock, no RNG). `grade.financial.cash` is the
* final running cash.
* 2. a sequence-level GRADE — the financial axis chained; the non-financial axes reuse the EXISTING grid
* axes ({@link aggregateCell}) over the sessions-as-replicate-runs, so a LENGTH-1 Instance's non-financial
* axes are BYTE-IDENTICAL to `buildGrid([bl]).cells[0]` (NO existing-grid churn). NO opaque omnibus score.
* 3. a benchmark-relative ALPHA hook — `alpha = instanceEV − baseline`, `instanceEV = Σ totals.expected`.
* alpha lives INSIDE the financial axis, one honest measure — never a collapsed single score.
*
* FAIL-CLOSED: an empty (unstitchable) sequence, or a missing / non-finite baseline, is a typed refusal —
* never a silent default (no silent 0-alpha).
*/
export function buildInstanceBlotter(
orderedBlotters: readonly Blotter[],
opts: BuildInstanceBlotterOptions,
): InstanceBlotter {
// Fail-closed — an empty sequence has no chain to open.
if (orderedBlotters.length === 0) {
throw new Error("grid: buildInstanceBlotter needs a non-empty session sequence (fail-closed)");
}
// Fail-closed — a missing / non-finite baseline is a typed refusal, never a silent 0-alpha default.
const baseline = opts?.baseline;
if (typeof baseline !== "number" || !Number.isFinite(baseline)) {
throw new Error(
"grid: buildInstanceBlotter needs a finite `baseline` benchmark (fail-closed — never a silent 0-alpha default)",
);
}
// (1) The chained-cash ledger — order-SENSITIVE, folded in SEQUENCE order (each session opens from the
// prior's carried cash). This re-derives, purely, the exact chain carry.ts threads (cash = prior.cash +
// settle.floorTotal, and settle.floorTotal === totals.floor).
const sessions: SessionLink[] = [];
let running = 0;
for (let k = 0; k < orderedBlotters.length; k++) {
const b = orderedBlotters[k]!;
const openingCash = running;
const realized = b.totals.floor;
const closingCash = openingCash + realized;
sessions.push({ ordinal: k, runId: runIdOf(b), openingCash, realized, closingCash });
running = closingCash;
}
// (3) The financial axis — chained cash + the benchmark-relative alpha hook.
const instanceEV = orderedBlotters.reduce((acc, b) => acc + b.totals.expected, 0);
const financial: InstanceFinancial = {
cash: running, // the final running cash of the chain (Σ per-session realized floor)
instanceEV,
alpha: instanceEV - baseline,
baseline,
};
// (2) The non-financial axes — reuse the EXISTING grid aggregation over the sessions-as-replicate-runs, in
// the SAME SimRunId-sorted FIXED order buildGrid folds a cell in (so a length-1 Instance's non-financial
// axes are byte-identical to buildGrid([bl]).cells[0], and every reduction is deterministic — RUNTIME §0).
const sorted = [...orderedBlotters].sort((a, b) => (runIdOf(a) < runIdOf(b) ? -1 : runIdOf(a) > runIdOf(b) ? 1 : 0));
const sequenceCellKey = SequenceCellKey(orderedBlotters.map((b) => cellOf(b)));
const agg = aggregateCell(sequenceCellKey, sorted);
const grade: InstanceGrade = {
financial,
risk: agg.risk,
process: agg.process,
fidelity: agg.fidelity,
provider: agg.provider,
cost: agg.cost,
certification: agg.certification,
};
return {
// The 5zl.16.5 identity chain, read straight off the ordered finalized sequence.
instanceRunId: InstanceRunId(orderedBlotters.map((b) => runIdOf(b))),
sequenceCellKey,
sessions,
grade,
};
}