UNPKG

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.

545 lines (506 loc) 36.4 kB
/** * # blotter/types — the Blotter v1 typed record (kestrel-a57.1, ADR-0011; epic a57 schema) * * The **Blotter** is the deterministic projection of one Session {@link ../bus Bus} into the canonical * typed record of that Session (CONTEXT: Blotter). The Bus is the truth; the Blotter is its projection — * regenerable, byte-stable, never a second writer (ADR-0010). This file is the SHAPE only; the fold that * produces it is {@link ./project.ts}, its canonical bytes are {@link ./serialize.ts}, and the * determinism leg that proves the fold is pure is {@link ./certify.ts}. * * Two orthogonal stamps ride a Blotter and must never be conflated (ADR-0011): * **Certification** — is this record faithful and reproducible? (a set of pass/fail/na legs; the * determinism leg is a57.1's, the paper/live legs land in a57.3) — and **Fidelity** — how real are its * fills? (the `modeled|realized` level from the graded META, plus the judge's self-limitation stamp — what * it could NOT observe — derived from the fill model in a57.4). Certified never means good. */ import type { DirectionalCap, ExperimentalEnvelope, Fidelity, FillModelStamp, FillSupport, InstanceIdentity, MoneynessBucket, PlanOutcome, PlanState, RankableReason, Right, SessionInstrument, } from "../bus/index.ts"; import type { PnlChannel, SampledQualification } from "../grade/headline.ts"; export type { ExperimentalEnvelope, TokenAccounting } from "../bus/index.ts"; // ───────────────────────────────────────────────────────────────────────────── // session — identity, judge, fidelity, and the bus receipt this record projects from // ───────────────────────────────────────────────────────────────────────────── /** The bus receipt: how many events the graded bus held, the sha256 over its canonical JSONL * serialization (the truth this Blotter projects from), and the bus schema it was written at. */ export interface BusReceipt { readonly events: number; readonly sha256: string; readonly schema: number; } // ───────────────────────────────────────────────────────────────────────────── // fidelity — the level (modeled|realized) + a SELF-LIMITATION the judge stamps (a57.4) // ───────────────────────────────────────────────────────────────────────────── /** * One thing the judge (the fill model that produced this graded bus's fills) **structurally could NOT * observe** — a stable machine `code` plus an honest human `note` (a57.4). These are not fabricated: each * is a documented structural fact of the named fill model (e.g. a strict-cross judge credits a fill only * on a tape cross; no judge in this build sees queue position), so a consumer cannot OVER-CLAIM realism the * judge never had. */ export interface FidelityLimit { /** A stable, lexical machine token for the limit (e.g. `no-queue-position`). */ readonly code: string; /** The honest human phrasing of what the judge could not see. */ readonly note: string; } /** * The judge's self-limitation declaration (a57.4): the fill model that produced the fills, and the ordered * list of what it structurally could NOT observe. Sourced deterministically from the graded META's * `fill_model` — never fabricated — so it re-projects byte-identically. An unrecognized fill model * fails closed to a single `observability-unstated` limit rather than inventing observability the judge may * not have (a consumer then treats the record as maximally limited). */ export interface FidelitySelfLimitation { /** The fill model these limits belong to (`fill_model.name` on the graded META — the judge). */ readonly fill_model: string; /** What the judge could not observe, in a stable order (byte-stable across re-projection). */ readonly limits: readonly FidelityLimit[]; } /** * The Blotter's Fidelity stamp (CONTEXT: Fidelity; a57.4): the realism `level` a session may CLAIM * (`modeled` in sim/paper, `realized` in live — a META pass-through), PLUS a `self_limitation` declaring * what the judge could not see. The two together are how a consumer reads fill realism WITHOUT over-claiming * it: the level says how real the fills are, the self-limitation bounds what "real" even means for this judge. * Orthogonal to Certification (a sim Blotter can be certified yet low-fidelity). */ export interface BlotterFidelity { /** The fill realism level (`modeled` | `realized`) — the graded META pass-through. */ readonly level: Fidelity; /** What the judge structurally could not observe — sourced from the fill model, never fabricated. */ readonly self_limitation: FidelitySelfLimitation; } /** The Session header of a Blotter — its Instance identity + judge + fidelity + the bus it projects * from. Every field is a pure read of the graded bus's opening META (ADR-0011 part B). */ export interface BlotterSession { /** Instance identity (lineage/version/mode, pod when a Pod document ran) — ADR-0011 recommendation 1. */ readonly instance: InstanceIdentity; /** The session's instruments (from the input META, carried through the graded META). */ readonly instruments: readonly SessionInstrument[]; /** The bus this Blotter projects from — the recorded truth (ADR-0010). */ readonly bus: BusReceipt; /** sha256 over the graded bus's ENGINE-OUTPUT events (everything after the opening META) — pins the * engine's decisions, mirroring {@link ../session EpisodeReport}'s `determinism_hash`. A pure function * of the bus. (It re-pins the output from the graded bus, whose seq are re-stamped from the META, so it * need not byte-equal the report's hash — see the report→Blotter unification note in project.ts.) */ readonly determinism_hash: string; /** The fill model that produced this graded bus's fills (the judge). */ readonly fill_model: FillModelStamp; /** The fill realism this session may claim — the `modeled|realized` level PLUS the judge's * self-limitation (what it could not observe). Orthogonal to Certification (a57.4). */ readonly fidelity: BlotterFidelity; /** * The author's experimental envelope (kestrel-a57.14): the agent/model identity that produced this * session, projected VERBATIM from the graded META's injected `envelope` (symmetric with `fill_model`, * the judge). Present ONLY when the graded bus declared a COMPLETE envelope; a bus that declared none * (a pre-a57.14 / input-derived graded bus) carries no envelope, and a declared-but-INCOMPLETE envelope * is NOT recorded here — a broken identity is never stamped as identity; it fails the record closed to a * PROVISIONAL {@link Certification} with the missing field named in `certification.reasons` instead. */ readonly envelope?: ExperimentalEnvelope; /** * The FULL `ConfigId` of the run's config descriptor (`sha256(canonical AgentConfig)`, kestrel-5zl.6), * projected VERBATIM from the graded META's `config_id` (m9i.2 owner tweak). A SUPERSET identity of the * envelope — it additionally folds the sampling axis (`temperature`/`thinkingLevel`), `format`, and * `label` — so temperature-only config variants carry DISTINCT ids: the config axis the grid CellKey * keys on (`cellOf`, run-identity.ts). Additive/OPTIONAL: absent on a config-less graded bus and on a * pre-tweak graded bus (whose cell falls back to the envelope identity — a legacy cell, never pooled * with a ConfigId-keyed cell). */ readonly config_id?: string; /** * The **cell config axis** — the full ConfigId with the `fillSeed` REPLICATE axis STRIPPED (kestrel-9gu.8.1 / * ADR-0016 §3), projected VERBATIM from the graded META's `cell_config_id`. This is the config axis `cellOf` * keys the cell on: seed-only variants share it (replicates in one cell, each a distinct `SimRunId`), while * temperature/behavioral variants still split. Present ONLY when it DIVERGES from `config_id` (the config * carried a `fillSeed`); a seedless run's cell axis EQUALS `config_id`, so `cellOf` reads `config_id` * directly. Additive/OPTIONAL: absent on a config-less, pre-tweak, or seedless graded bus. */ readonly cell_config_id?: string; } // ───────────────────────────────────────────────────────────────────────────── // certification — record honesty (a set of legs → a verdict) // ───────────────────────────────────────────────────────────────────────────── /** One certification leg's state (CONTEXT: Certification). `na` = not applicable to this mode. */ export type CertLeg = "pass" | "fail" | "na"; /** The certification verdict: `certified` only when every APPLICABLE leg passes, else `provisional` * (CONTEXT: Certification). Numbers always render with the verdict attached. */ export type Verdict = "certified" | "provisional"; /** * Certification — the verdict on whether this Blotter is a faithful, reproducible record of its Session. * a57.1 owns the SHAPE of all four legs and implements the **determinism** leg (re-project + byte-compare, * {@link ./certify.ts}); the paper/live legs (`detectors_bit_for_bit`, `fills_subset_of_live`, * `lifecycle_divergence_explained`) are `na` in sim and land as real legs with paper/live in a57.3. */ export interface Certification { readonly legs: { /** Re-project the Bus → byte-identical Blotter (the projector's core invariant, sim-applicable). */ readonly determinism: CertLeg; /** Detectors reproduce bit-for-bit under replay (paper/live; `na` in sim — a57.3). */ readonly detectors_bit_for_bit: CertLeg; /** Simulated fills are a subset of live fills (paper/live; `na` in sim — a57.3). */ readonly fills_subset_of_live: CertLeg; /** Lifecycle divergence from live is explained (paper/live; `na` in sim — a57.3). */ readonly lifecycle_divergence_explained: CertLeg; }; readonly verdict: Verdict; /** * The recorded reasons a verdict is `provisional` — fail-closed, never silent (kestrel-a57.14). Parallel * to {@link HeadlineFlag.reasons}: each reason NAMES the cause (e.g. an experimental-envelope identity * field that was declared-missing), so a `provisional` number never renders without its cause attached. * Present ONLY when there ARE reasons (a byte-minimal `certified` record carries none). Byte-stable: * derived deterministically, in a fixed order, from the bus. */ readonly reasons?: readonly string[]; } // ───────────────────────────────────────────────────────────────────────────── // rankability — leaderboard ELIGIBILITY, a DISTINCT axis from certification (kestrel-m9i.25) // ───────────────────────────────────────────────────────────────────────────── /** One rankable leg's state (kestrel-m9i.25). `pass`/`fail` mirror {@link CertLeg}; `unknown` is the * fail-closed state — an ABSENT or malformed leg input is UNKNOWN, never silently passed. There is no `na`: * every leg is applicable to leaderboard eligibility. */ export type RankableLeg = "pass" | "fail" | "unknown"; /** * Rankability — the DERIVED leaderboard-ELIGIBILITY axis (kestrel-m9i.25), a sibling of {@link Certification} * but an ORTHOGONAL question. Certification asks "is this record honest + reproducible?"; rankability asks * "is this row COMPARABLE on the leaderboard?". It is a CONJUNCTION of five legs — * * rankable = CERTIFIED ∧ POST-CUTOFF ∧ DATE-BLIND ∧ (SEASON) FROZEN ∧ CLOCK-HONEST * * — DERIVED at projection from already-stamped fields (the certification verdict, the kestrel-m9i.23 cutoff * cross-check on the a57.14 envelope, and the three ATT/Layer-3 attestations `date_blind`/`season_frozen`/ * `clock_honest`), NEVER a field a producer stamps (a producer-stamped `rankable` on the bus is ignored — * the projector recomputes the conjunction from the legs). It ENDS "certified" doing double duty: a * CERTIFIED run on a PRE-CUTOFF tape (the model already knew the outcome) is * `certification.verdict === "certified"` yet `rankable === false`. FAIL-CLOSED: an UNKNOWN leg input ⇒ not * rankable + a NAMED reason, never a silent `rankable: true`. * * The CLOCK-HONEST leg (ADR-0040 / kestrel-w7la.3, appended LAST so every pre-existing reason ordering is * byte-stable) is "no governed season runs latency-blind" (AGENTS.md) made mechanical: a latency-blind * session stays VALID — certified, gradable, comparable in exploratory grids — but is never * governed-leaderboard-rankable and can never ground a latency claim ({@link ./claims.ts latencyClaimGate}). */ export interface Rankability { readonly legs: { /** The certification verdict is `certified` (not `provisional`). */ readonly certified: RankableLeg; /** The tape date is STRICTLY after the model's knowledge cutoff (m9i.23 `isPostCutoff`); `unknown` when * the cutoff is unverifiable (no complete envelope / unknown model id / declared-registry mismatch). */ readonly post_cutoff: RankableLeg; /** The run is attested date-blind (`meta.date_blind`); `unknown` when the attestation is absent/malformed. */ readonly date_blind: RankableLeg; /** The benchmark season is frozen (`meta.season_frozen`); `unknown` when the attestation is absent/malformed. */ readonly season_frozen: RankableLeg; /** The session is attested clock-honest (`meta.clock_honest`, ADR-0040 / kestrel-w7la.3): deliberation * consumed tape time on eligible (sub-minute) ground truth. The driver stamps `true` or NOTHING — so * `unknown` (absent/malformed) is the latency-blind/unattested case, and `fail` is reachable only via a * platform-side revocation (`false`). This is the leg the platform's FORBIDDEN-CLAIMS lint reads * ({@link ./claims.ts latencyClaimGate}): a latency claim may cite only a `pass` session. */ readonly clock_honest: RankableLeg; }; /** The headline conjunction: `true` iff EVERY leg passes. */ readonly rankable: boolean; /** * The recorded reasons `rankable` is false — fail-closed, never silent, one per non-passing leg, in a * FIXED leg order (certified → post_cutoff → date_blind → season_frozen → clock_honest; the fifth leg is * appended LAST so every pre-w7la.3 ordering is byte-stable) for byte-stability. Each NAMES which leg * failed (and whether it was a hard-false or a fail-closed UNKNOWN). Present ONLY when `rankable` is * false (a rankable row is byte-minimal, carrying none). */ readonly reasons?: readonly RankableReason[]; } // ───────────────────────────────────────────────────────────────────────────── // journals — the author's reasoning, inline (folded straight from the JOURNAL stream) // ───────────────────────────────────────────────────────────────────────────── /** One JOURNAL record projected into the Blotter — full markdown `body` inline (text is truth, * ADR-0010). `seq` is the source of truth a plan's {@link ReasoningRef} references. */ export interface JournalEntry { readonly seq: number; readonly kind: string; readonly ts: number; readonly body: string; } // ───────────────────────────────────────────────────────────────────────────── // disarms — reason-carrying stand-down / de-arm audit records (CONTROL `disarm`) // ───────────────────────────────────────────────────────────────────────────── /** One reason-carrying de-arm on the Blotter, projected from a CONTROL `disarm` record (kestrel-2pl). * The author's `standDown` (inventory rides its TP/EXIT, never liquidates) and a fail-closed * supersede-parse-escape de-arm both emit this — so the WHY of a stand-down is an explicit audit record, * not merely INFERABLE from plans going `expired(superseded)`. `reason` is the note the CONTROL carried. * The de-arm discriminant + reason vocabulary this projects from live in `src/engine/disarm.ts` * (`DISARM_CONTROL_TYPE`, kestrel-z473.5). */ export interface DisarmRecord { readonly seq: number; readonly ts: number; readonly reason?: string; } // ───────────────────────────────────────────────────────────────────────────── // plans — lifecycle + final state + a reference (never a copy) to the author's reasoning // ───────────────────────────────────────────────────────────────────────────── /** One transition in a plan's lifecycle, reconstructed from the PLAN stream — carrying the `seq` * so the trace is anchored to the bus. */ export interface BlotterLifecycleStep { readonly seq: number; readonly ts: number; readonly state: PlanState; readonly outcome?: PlanOutcome; readonly reason?: string; } /** * A reference (never a copy) to the author's reasoning for a plan (ADR-0011 recommendation 2): the * `seq` into {@link Blotter.journals} is the source of truth, and `excerpt` is a mechanical, * deterministic preview — the body's first non-empty line, trimmed, capped ~120 chars with an ellipsis. * No markdown interpretation. */ export interface ReasoningRef { readonly seq: number; readonly excerpt: string; } /** A plan's lifecycle trace + terminal state, plus a reference to its pre-hoc author reasoning when a * JOURNAL precedes its first lifecycle transition. One record is one exact Plan **instance** (kestrel-22j.15): * a legal same-name replacement projects as its OWN record beside its predecessor, never collapsed into it. */ export interface PlanRecord { /** The authored plan NAME — the Lineage aggregation key (a consumer rolls instances up BY this; a name may * legally recur, so it is NOT the instance identity). */ readonly plan: string; /** The exact Plan-instance identity this record traces (kestrel-22j.15): the `plan_instance` minted by the * engine on a v5 bus, or a deterministic sessionized key migrated from a pre-v5 bus. Distinct per instance, * so two same-name instances are two records. */ readonly plan_instance: string; readonly lifecycle: readonly BlotterLifecycleStep[]; /** The terminal outcome when `done`, else the last lifecycle state. */ readonly final_state: PlanState | PlanOutcome; /** The pre-hoc author JOURNAL this plan references (absent when none precedes it). */ readonly reasoning_ref?: ReasoningRef; } // ───────────────────────────────────────────────────────────────────────────── // orders — one record per placed order, joined across ORDER + TELEMETRY (never engine state) // ───────────────────────────────────────────────────────────────────────────── /** An order's terminal disposition, read from the ORDER stream. */ export type OrderFinalState = "filled" | "cancelled" | "expired" | "rejected" | "working"; /** * One placed order, projected by joining the ORDER stream (place/fill/cancel/reject) with the engine's * per-order TELEMETRY (`order` → esc rung + reprice tally; `settle` → the probabilistic accounting + * the 9gu.3 support flag). Every field is re-derived from bus bytes — never scraped from engine state * (ADR-0011). Per-order `support` is here; its session-level rollup is {@link Blotter.fill_claim} (a57.4). */ export interface OrderRecord { readonly order_id: string; readonly plan?: string; /** The exact Plan-instance identity that authored this order (kestrel-22j.15), when it came from a plan on a * v5 bus — so an order joins its exact instance, not merely its recurring name. */ readonly plan_instance?: string; readonly instrument: string; readonly side: "buy" | "sell"; readonly qty: number; /** The option strike, present IFF this order carries an option leg. ABSENT for an ADR-0017 equity/spot * order (which has NEITHER a strike NOR a right) — never a fictional zero-strike. The machine surface * mirrors the frame's spot/option boundary ({@link ../frame/format.ts}:isSpotLeg): both absent ⇒ spot. * A prior projection substituted `strike: 0` here, planting a phantom zero-strike CALL contract in the * recomputability substrate (report.json/ledger) that an agent parsing an equity order would misread * (kestrel-ldqt; same failure kestrel-1kp fixed on the frame surface). */ readonly strike?: number; /** The option right, present IFF this order carries an option leg. ABSENT for an ADR-0017 equity/spot * order — never a fictional `"C"`. Rides in lockstep with {@link strike} (both present ⇒ option; both * absent ⇒ spot); a mixed presence is never emitted. */ readonly right?: Right; /** Limit price at placement. */ readonly px: number; /** The esc-ladder rung this order last occupied (last `esc_stage` on its TELEMETRY `order` records). */ readonly esc_stage: number; /** Count of cancel/replace reprices for this order (sum of `reprice` on its TELEMETRY `order` records). */ readonly reprice_count: number; readonly final_state: OrderFinalState; /** Did the strict-cross floor fire (a definite fill)? */ readonly filled: boolean; /** Accrued expected fill probability `1 − survival` (`1` on a floor fill). */ readonly pFill: number; /** The order-level fill-support flag (9gu.3): a grader refuses to bank `expected` from `extrapolated`. */ readonly support: FillSupport; /** Realized $ under the strict-cross floor (0 unless floor-filled). */ readonly floor_pnl: number; /** E[$] under `pFill`. */ readonly expected_pnl: number; /** A cancel/reject reason, when the terminal ORDER event carried one. */ readonly reason?: string; /** The coarse moneyness bucket the fill-guard assessed (kestrel-9gu.6), when this order was * submitted in the far-OTM wing (the only wing the directional guard acts on) — from the `guard` * TELEMETRY record. Absent for a near-the-money / unclassified order (no guard record). */ readonly moneyness?: MoneynessBucket; /** The covered-state the guard threaded (kestrel-9gu.6): `true` = a covering wing (exempt from the * SELL far-OTM cap), `false` = a standalone offer. Present iff a `guard` record rode for this order. */ readonly covered?: boolean; /** The directional far-OTM cap decision (kestrel-9gu.6): `applied` (standalone SELL floored to the * no-fill cap), `exempt` (covering wing left uncapped), or `na` (far-OTM BUY, unlocked not capped). * Present iff a `guard` record rode for this order. */ readonly directional_cap?: DirectionalCap; } // ───────────────────────────────────────────────────────────────────────────── // totals — the floor / expected accounting, re-derived from the settle telemetry // ───────────────────────────────────────────────────────────────────────────── /** * The headline record on the totals (kestrel-9gu.12): WHICH channel is the headline (`channel`/`usd`, * selected through the ONE gate policy in {@link ../grade/headline.ts} — `floor` until the sampled * channel's 9gu.8.6 qualification is on the record, never an extrapolated-support sampled result), the * `gate` verdict that made the selection (its reasons on the record when floor holds), and the honesty * taint: `tainted` is `true` when any non-zero `expected` contribution rode on `extrapolated` support * (a cell a grader refuses to bank, CONTEXT: Support flag) or the settle mark itself was untrustworthy * (kestrel-xwf) — a taint rides EVERY channel (all three settle against the same mark), so the headline * number always renders with its taint attached, never silently. */ export interface HeadlineFlag { /** The selected headline channel: `floor` (fail-closed default) or `sampled` (once qualified). */ readonly channel: PnlChannel; /** The selected channel's $ value (the other channels stay beside it as honest bounds). */ readonly usd: number; /** The qualification-gate verdict (kestrel-9gu.8.6 seam) that selected the channel. */ readonly gate: SampledQualification; readonly tainted: boolean; readonly reasons: readonly string[]; } /** * The settle mark's provenance + fail-closed staleness verdict (kestrel-xwf), re-derived from the * `settle_mark` TELEMETRY record: WHAT spot the book cash-settled against, WHEN that value was last * established on the input tape (`as_of_ts` — a frozen feed re-printing the same px does not refresh * it), and whether it was `stale` at the settle instant under the declared threshold. * `stale: true` taints {@link Totals.headline}: the settled P&L is graded against a mark the tape * stopped supporting (the 2025-04-09 frozen-underlier phantom loss), so it is NON-BANKABLE — honest * provenance, never an invented "true close". Explicit `null` = unstated (itself a stale verdict). * * `source`/`mark_uncertain`/`note` carry the ANNOTATED-RECOVERY layer (kestrel-xwf unification): a * STALE mark that could be recovered from closing put-call parity settles against the recovered spot * (`source: "parity"`, `px` = the recovered value) — but recovery ANNOTATES, it never PARDONS: the * staleness taint stands (`stale: true` still taints the headline, kestrel-9gu.12). One that could not * be recovered is retained (`source: "stale"`, `mark_uncertain: true` — the loudest taint). A live mark * is `source: "market"`. */ export interface SettleMarkRecord { readonly px: number; readonly as_of_ts: number | null; readonly last_observed_ts: number | null; readonly age_ms: number | null; readonly stale_after_ms: number; readonly stale: boolean; readonly source: "market" | "parity" | "stale"; readonly mark_uncertain: boolean; readonly note: string | null; } /** The session totals — the THREE-channel P&L contract (kestrel-9gu.12), re-derived from the * settle-outcome TELEMETRY (the sum of the unrounded per-order P&L, rounded once on the 1e-8 grid — * byte-matching {@link ../session EpisodeReport}'s totals). Floor and expected are always present; * `sampled` only on a seeded run — the headline is SELECTED among the realizations by the gate. */ export interface Totals { /** Definite-fill $ only — the conservative floor (`realized_floor_usd` in the report). */ readonly floor: number; /** E[$] weighted by `1 − survival` (`expected_usd` in the report). */ readonly expected: number; /** The seeded causal realization (`sampled_usd` in the report). ABSENT — not `0` — when the sampled * channel is off (an un-run channel is never conflated with a zero outcome, kestrel-9gu.12). */ readonly sampled?: number; readonly headline: HeadlineFlag; /** The settle mark's provenance + staleness verdict (kestrel-xwf), when the graded bus carries the * `settle_mark` TELEMETRY record. ABSENT on a pre-xwf graded bus (provenance unrecorded — the * additive-optional convention, like `guard`); a stale record taints {@link headline}. */ readonly settle_mark?: SettleMarkRecord; } // ───────────────────────────────────────────────────────────────────────────── // fill_claim — the expected-$ partitioned by fill support (a57.4) // ───────────────────────────────────────────────────────────────────────────── /** * One support class's slice of the Blotter's expected-$ (a57.4) — the aggregate rollup deferred from a57.1 * (`OrderRecord.support` is the per-order flag; this is its session-level partition). Derived PURELY from * the per-order `support` on the settle telemetry: it splits `totals.expected` into the portion resting on * `calibrated` support (which a grader banks) and the portion resting on `extrapolated` support (which a * grader **refuses to bank** — the bankableEv doctrine, 9gu.3). The two claims partition * the whole: `Σ fill_claim[i].expected` reconstructs `totals.expected`. * * SIGNED SUB-CELLS (kestrel-dpy, ADR-0014): each slice carries the partition WRITTEN ONCE — a `gain` * (Σ non-negative expected-$) and a `loss` (Σ negative), with `expected === gain + loss`. This closes * a fail-open gap: a consumer that re-banks by SUBTRACTING the extrapolated slice must subtract only its * `gain`, so extrapolated LOSSES survive (they are kept by the `bankableEv` doctrine, 9gu.3) and can * never be zeroed. Subtracting the unsigned `expected` (the old shape) would have erased extrapolated * gains and losses together — flattering the loss-parking strategy the doctrine exists to punish. Same * bus ⇒ byte-identical claims. */ export interface FillClaim { /** The support class this slice aggregates (CONTEXT: Support flag). */ readonly support: FillSupport; /** How many settled orders rest on this support class. */ readonly orders: number; /** Σ `expected_pnl` over those orders (rounded once on the 1e-8 grid, like {@link Totals}). * Equals `gain + loss` — the net of this slice's signed sub-cells. */ readonly expected: number; /** The slice's GAINS: Σ non-negative `expected_pnl` (≥ 0, on the 1e-8 grid). A subtracting consumer * removes ONLY this to re-bank, so extrapolated losses are never erased (ADR-0014). */ readonly gain: number; /** The slice's LOSSES: Σ negative `expected_pnl` (≤ 0, on the 1e-8 grid). KEPT for extrapolated cells * — `bankableEv` refuses extrapolated gains but never forgives a loss. */ readonly loss: number; } // ───────────────────────────────────────────────────────────────────────────── // wake_trace — the clock-honest deliberation record, projected (ADR-0040 / kestrel-w7la.1) // ───────────────────────────────────────────────────────────────────────────── /** One `WAKE`/`deliberation` record projected verbatim (bus_schema v6, CONTEXT "Deliberation record"): * the cost of one clocked Seat turn. `ts` IS the derived return time (`checkpoint.ts + measured_ms + * buffer_ms` — the reader enforces the identity); `wake_seq` names the WAKE checkpoint it prices. */ export interface DeliberationTraceEntry { /** The record's own `seq` on the graded bus. */ readonly seq: number; /** The `seq` of the WAKE checkpoint this cost belongs to (the vantage the Seat answered). */ readonly wake_seq: number; /** The derived return time — when the Seat's control landed back on the tape. */ readonly ts: number; /** Measured wall time of the Seat's whole turn (bounded authoring loop + repairs included). */ readonly measured_ms: number; /** The configured latency buffer added to every measured turn. */ readonly buffer_ms: number; } /** The Blotter's wake trace (clock-honest wakes §6): the session's deliberation records — per-wake * cost and the session total. RECORD COMPLETENESS, not a score (ADR-0011: the Blotter is the whole * record) — latency is already priced inside alpha by the fills landing at return-time prices; there * is no separate latency score to game. Present ONLY when the graded bus carries deliberation records * (a clock-honest session); a latency-blind blotter omits it, byte-identical to before. */ export interface WakeTrace { readonly deliberations: readonly DeliberationTraceEntry[]; /** Σ (`measured_ms + buffer_ms`) over the session — the total tape time deliberation consumed. */ readonly deliberation_ms_total: number; } // ───────────────────────────────────────────────────────────────────────────── // The Blotter // ───────────────────────────────────────────────────────────────────────────── /** Blotter v1 — the complete, replayable record of one Session, projected deterministically from its * Bus (CONTEXT: Blotter; a57 epic schema; ADR-0011). */ export interface Blotter { readonly session: BlotterSession; readonly certification: Certification; /** The DERIVED leaderboard-ELIGIBILITY axis (kestrel-m9i.25) — `certified ∧ post-cutoff ∧ date-blind ∧ * (season) frozen`, computed at projection from stamped fields, a DISTINCT axis from `certification` (a * row can be `certification.verdict === "certified"` yet `rankability.rankable === false`). */ readonly rankability: Rankability; readonly journals: readonly JournalEntry[]; /** Reason-carrying stand-down / de-arm audit records (kestrel-2pl), in bus order. Empty when the * session never de-armed. Projected from CONTROL `disarm` — a pure bus read (ADR-0011). */ readonly disarms: readonly DisarmRecord[]; readonly plans: readonly PlanRecord[]; readonly orders: readonly OrderRecord[]; readonly totals: Totals; /** The session's expected-$ partitioned by fill support (a57.4): the `calibrated` slice a grader banks * and the `extrapolated` slice it refuses to bank (9gu.3). Always two entries, in the fixed order * `[calibrated, extrapolated]` — deterministic and byte-stable. Partitions {@link Totals.expected}. */ readonly fill_claim: readonly FillClaim[]; /** The clock-honest deliberation trace (ADR-0040 / kestrel-w7la.1) — per-wake cost + session total, * a pure fold of the bus's `WAKE`/`deliberation` records. ABSENT on a latency-blind bus (which * carries none), so every existing projected Blotter is byte-identical. */ readonly wake_trace?: WakeTrace; }