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.
741 lines • 71.4 kB
TypeScript
/**
* # bus/types — the typed append-only event bus envelope + payloads (RUNTIME §1)
*
* The bus is the single substrate underneath every mode: it is the **audit trail**, the
* **replay corpus**, and the **grading corpus** at once (RUNTIME §1). Every event is one
* flat JSONL record with the envelope `{ seq, ts, stream, type, ...payload }` and a
* monotonic `seq`. The engine writes everything it does back onto the bus; a Session in
* `sim` mode reads a recorded/synthetic bus *as its clock* — `now` is the current event's
* `ts` (RUNTIME §0), so nothing on the runtime path reads a wall clock.
*
* This file is the **typed object model of the bus**. Other modules import these types;
* they never redefine an envelope or a payload. The model is a discriminated union keyed on
* `stream` (and, within `TICK`, on `type`) — narrow on `event.stream` first, then on
* `event.type`.
*
* ## Streams (RUNTIME §1)
* `META` (session header) · `TICK` (`SPOT` | `BOOK` | `HEARTBEAT`) · `DETECTOR` ·
* `PLAN` (lifecycle) · `ORDER` (place/cancel/fill/reject) · `WAKE` · `CONTROL` (author
* actions) · `REGIME` (tag writes) · `JOURNAL` (author reasoning, full markdown inline) ·
* `TELEMETRY` (engine-emitted per-order observability). Schema-versioned via {@link BUS_SCHEMA},
* carried on the `META` header and validated by the reader.
*
* `JOURNAL` is the one stream that is **author metadata, not an engine input** (a57.11): the
* per-event pass never folds it, so adding JOURNAL records leaves the emitted engine event
* stream — and the determinism hash — byte-identical. It is also the one stream discriminated
* on `kind` rather than an engine `type` (there is no `(stream,type)` pairing for it); the
* reader tolerates a forward/unknown `kind` by a logged skip instead of a throw.
*
* `TELEMETRY` (a57.9) is the mirror-image case: it IS engine output (emitted onto the same
* stream as PLAN/WAKE, so it joins the determinism hash — same input ⇒ same telemetry ⇒ stable
* hash) but it is **observational only** — no engine DECISION reads it back, so the per-event
* pass skips it exactly like JOURNAL and its presence never changes an order, fill, or plan
* outcome. It carries an **open** `type` vocabulary (like DETECTOR/CONTROL) so per-order
* observability can grow without a schema bump; a reader tolerates an unrecognized telemetry
* `type` by simply ignoring it, and a report over a bus that carries no telemetry degrades to a
* documented default (esc stage 0, reprice count 0) rather than crashing (RUNTIME §8).
*
* ## Equity-only sessions
* An equity-only session is representable two ways, both first-class: emit no `BOOK` events
* at all (SPOT-only tape), or emit `BOOK` events whose `legs` array is empty. Options are a
* per-instrument overlay on the underlier tape, never a structural requirement.
*/
/** The bus schema version new buses are written at. Bumped on any breaking envelope/payload
* change — including the addition of a new stream tag, since a reader that predates the tag
* hard-throws on it (fail-closed, but mid-stream): v2 adds the {@link JournalEvent JOURNAL} stream,
* v3 adds the {@link TelemetryEvent TELEMETRY} stream. (The TELEMETRY `type` vocabulary is open and
* may grow WITHOUT a bump — that is a within-stream extension a telemetry-aware reader tolerates;
* introducing the STREAM tag itself is the breaking change, exactly as JOURNAL was.)
*
* v4 (kestrel-a57.1 slice 1, ADR-0011) records the **probabilistic accounting on the bus** so the
* Blotter projector re-derives `totals{floor,expected}` + `orders[].support` with no engine scrape:
* a settle-outcome TELEMETRY record ({@link TelemetrySettlePayload}, `type: "settle"`) per order, and
* the **judge self-description** on a GRADED bus's META ({@link MetaPayload.fill_model}/`instance`/
* `fidelity`). Both are strictly additive & OPTIONAL — the settle `type` rides the already-open
* TELEMETRY vocabulary (no reader gate to widen) and the META judge fields are absent on a fill-model-
* agnostic INPUT tape (the two-bus rule, ADR-0011) — so a v1..v3 bus reads clean here and the bump is
* really only about advertising the new self-describing shape. The reader accepts every schema in
* {@link SUPPORTED_BUS_SCHEMAS} and refuses anything outside it loudly (fail-closed, RUNTIME §8).
*
* v5 (kestrel-22j.15) mints a per-Plan-**instance** identity — {@link PlanPayload.plan_instance}, carried
* onto the authored {@link OrderPayload}/{@link WakePayload}/{@link ControlPayload} evidence — so a legal
* same-NAME replacement (a Lineage key recurs after its predecessor is `done`, CONTEXT: Lineage) no longer
* collapses into the prior instance's lifecycle trace at report projection. The id is minted DETERMINISTICALLY
* at registration (the authored name + a registration ordinal; NO wall clock, NO unseeded RNG, RUNTIME §0) —
* same bus + documents ⇒ byte-identical ids. It is additive & OPTIONAL (a v1..v4 bus simply carries none), but
* the bump is DELIBERATE: it advertises that a graded bus now self-identifies its Plan instances, and it draws
* the fail-closed line for the migration. A projector reads the explicit `plan_instance` when present and, for a
* pre-v5 bus that lacks it, MIGRATES EXPLICITLY by sessionizing each name's lifecycle at its structural
* `done`→re-authored boundary ({@link ../blotter/project.ts}) — never inferring identity from timestamps or
* fuzzy name-matching (which the F4 same-name-supersession guard makes exact: a name only recurs after its
* predecessor is `done`). The reader accepts every schema in {@link SUPPORTED_BUS_SCHEMAS} and refuses anything
* outside it loudly (fail-closed, RUNTIME §8).
*
* v6 (kestrel-w7la.1, ADR-0040 — clock-honest wakes) records **deliberation cost on the Bus**: the
* within-stream WAKE type `"deliberation"` ({@link DeliberationEvent}) prices one clocked Seat turn —
* `ts` IS the derived return time `checkpoint.ts + measured_ms + buffer_ms`, `wake_seq` names the WAKE
* checkpoint it belongs to — so replay/certification re-project byte-identically from recorded time
* (the determinism invariant's recorded-time clause, RUNTIME §0). The type is within-stream-ADDITIVE,
* yet the bump is DELIBERATE (exactly the v5 precedent): a v5 build that *ignored* deliberation records
* would re-project a clocked bus into a *different* byte stream — silent wrongness — so stamping v6
* makes every pre-0040 build refuse a clocked bus up front at the META line. Only session-OUTPUT (graded)
* buses stamp v6; synthetic INPUT tapes keep their pinned literal schema (an input tape can never carry a
* deliberation record, and re-stamping the corpus would force a corpus re-mint — ADR-0018). A `deliberation`
* record on a bus whose META stamps `bus_schema < 6` is refused loudly by the reader (a clocked record on a
* pre-clocked bus is a corrupt bus). See docs/design/clock-honest-wakes.md §1. */
export declare const BUS_SCHEMA: 6;
export type BusSchema = typeof BUS_SCHEMA;
/** The schemas a bus META may legally carry under this build — {@link SUPPORTED_BUS_SCHEMAS} as a type.
* {@link MetaPayload.bus_schema} is typed on THIS union (not the literal {@link BusSchema}) because input-tape
* generators deliberately keep writing their pinned literal schema (v5 today — the input-tape-literal rule,
* clock-honest wakes §1) and must typecheck; the reader still refuses anything outside the set at runtime. */
export type SupportedBusSchema = 1 | 2 | 3 | 4 | 5 | 6;
/** The bus schemas this build can read. JOURNAL landed in v2, TELEMETRY in v3, the settle-outcome
* telemetry + META judge self-description in v4, and the per-Plan-instance identity
* ({@link PlanPayload.plan_instance}) in v5 — every one an append-only / additive change that
* moves no engine semantics, so older buses are forward-compatible: a v1 bus simply has no JOURNAL/
* TELEMETRY records, a v2 bus no TELEMETRY, a v3 bus no settle-outcome record and a bare (judge-less)
* META, a v4 bus no `plan_instance` (a projector migrates it explicitly by lifecycle sessionization,
* never a silent mis-identify). The reader tolerates any schema listed here and rejects the rest
* loudly — a build that predates a stream refuses a bus carrying it up front at the META line rather
* than throwing mid-stream (fail-closed, §8). v6 (clock-honest wakes, ADR-0040) adds the WAKE
* `deliberation` record; a v1..v5 bus simply carries none and replays byte-identically. */
export declare const SUPPORTED_BUS_SCHEMAS: ReadonlySet<number>;
/** The streams (RUNTIME §1). `JOURNAL` (v2) is author metadata, never an engine input;
* `TELEMETRY` (a57.9) is engine output but observational only — never an engine input. */
export type Stream = "META" | "TICK" | "DETECTOR" | "PLAN" | "ORDER" | "WAKE" | "CONTROL" | "REGIME" | "JOURNAL" | "TELEMETRY";
/** The set of legal stream tags, for reader validation (fail-closed on an unknown stream). */
export declare const STREAMS: ReadonlySet<Stream>;
/** What an order means when it fires (CONTEXT: Mode). One engine path; only the gate differs. */
export type Mode = "sim" | "paper" | "live";
/** Session-calendar phase (RUNTIME §2 `session phase`), carried on heartbeats. */
export type SessionPhase = "pre" | "open" | "regular" | "close" | "post";
/** Option right — the two contract kinds (shared vocabulary with the language `Leg`). */
export type Right = "C" | "P";
/** The fields every bus event carries. `stream`/`type` are added per event as literal
* discriminants; the payload fields are spread flat alongside (RUNTIME §1: the envelope IS
* `{ seq, ts, stream, type, ...payload }`, not a nested payload object). */
export interface EnvelopeBase {
/** Monotonic sequence number, assigned by the single writer. Determinism key. */
readonly seq: number;
/** Injected timestamp (epoch milliseconds). In `sim` this is the clock (RUNTIME §0). */
readonly ts: number;
}
/** One instrument the session carries. An equity-only session lists only equity/index
* instruments and never emits BOOK legs against them. */
export interface SessionInstrument {
readonly symbol: string;
/** The asset class of the underlier. `option-underlier` signals that BOOK legs may appear
* for this symbol; the others are quoted by SPOT alone. */
readonly assetClass: "equity" | "index" | "future" | "option-underlier";
/** The plan-side role, when the session pins one (a signal-space vs execution-space
* instrument, ARCHITECTURE §2 `USING`). Absent when unroled. */
readonly role?: "signal" | "exec";
}
/**
* The judge that produced a GRADED bus's fills — the fill model's identity + calibration receipt
* (CONTEXT: Fidelity; ADR-0006 "every grade stamps its judge"). EVs across fill-model versions do
* NOT naively compare, so the version + the exact calibration bytes are pinned here. `calibration_sha`
* is the sha256 over the calibration descriptor (a stable digest of `{version, calibrated}` for a
* hazard model, or of a `"none"` sentinel for a pure floor model with no calibration) — two grades
* under byte-identical calibration share it; a re-fit produces a new one.
*/
export interface FillModelStamp {
readonly name: string;
readonly version: string;
readonly calibration_sha: string;
/**
* What the judge structurally could NOT observe (a57.4) — DECLARED by the fill model itself and stamped
* here, in a stable order, so a projector reads the judge's self-limitation off the graded META rather than
* guessing it from `name` (a lookup that silently mislabelled the calibrated `maker-fair-v1+<version>`
* judge — kestrel-o32). OPTIONAL/additive exactly like the other graded-only fields: a legacy graded bus
* (or an input tape) carries none, and a projector fails closed to "unstated"/maximally-limited on its
* absence — never a silent default to unlimited observability.
*/
readonly self_limitation?: readonly {
readonly code: string;
readonly note: string;
}[];
}
/**
* The Instance identity of a GRADED bus (CONTEXT: Instance; ADR-0011 recommendation 1 — reuse the
* Ledger model). `lineage` is the **authored NAME** (names-are-data, ADR-0006 — the Ledger's
* aggregation key: `fade-ladder` authored on 40 days is one lineage with 40 instances); `version` is
* the `plans_sha256` (the canonical parse-print hash of the armed document set); `mode` is the session
* mode; `pod` is present ONLY when the Session runs an actual Pod document (a plans library is itself a
* versioned module, ADR-0003 — so a bare-plans grade carries no `pod`).
*/
export interface InstanceIdentity {
readonly lineage: string;
readonly version: string;
readonly mode: Mode;
readonly pod?: string;
}
/**
* Fidelity — how *real* a Session's fills are (CONTEXT: Fidelity): the axis from **modeled** to
* **realized**. `sim` and `paper` are both `modeled` (the same probabilistic FillModel yields a
* per-fill `pFill` scored over the survival product `Π(1 − pFillᵢ)`); `live` is `realized` (the fills
* are actual). Carried on a GRADED bus's META so the Blotter can *claim* its fill realism — orthogonal
* to Certification (a sim Blotter can be certified yet low-fidelity). Richer per-fill fill-claims land
* with a57.4; slice 1 carries only this level.
*/
export type Fidelity = "modeled" | "realized";
/**
* The token accounting the {@link ExperimentalEnvelope} carries (a57.14) — the INJECTED spend the author
* incurred (`input`/`output`/`thinking`), sourced off the deterministic record path from an authenticated
* or explicitly injected input (the owner-away conservative default), NEVER re-derived on the record path.
* The natural shape of the provider's usage (`LlmUsage`, `src/session/agent.ts`).
*/
export interface TokenAccounting {
readonly input: number;
readonly output: number;
readonly thinking: number;
}
/**
* The interface **face** an author reached the model through (kestrel-m9i.2 owner tweak, ENV·CFG): the
* closed vocabulary the m9i.7 face tournament pivots on — `http` (raw provider HTTP), `sdk` (a
* provider/AI SDK client), `cli` (a terminal harness), `mcp` (a Model Context Protocol server). A CLOSED
* set: the projector fail-closes a declared envelope whose `face` is outside it (a typed vocabulary,
* never an open string that drifts).
*
* CANONICAL in the protocol leaf ({@link import("../protocol/index.ts").Face}, ADR-0004) — the bus
* re-exports it so the a57.14 envelope-identity axis validates against the SAME closed vocabulary the
* surfaces project, one source of truth across the protocol/bus seam (arch-review A6). Protocol does
* not import bus, so this cannot cycle.
*/
export { FACES } from "../protocol/index.ts";
export type { Face } from "../protocol/index.ts";
/**
* The **corpus tier** a benchmark run draws on (kestrel-m9i.26, platform ADR-0018 three-tier addendum): the
* closed vocabulary that gates the practice-vs-holdback wall. Exactly like {@link Face} — a CLOSED set, not
* an open string that drifts — so the projector fail-closes a declared envelope whose `corpus_tier` is
* outside it (a typo or the retired `public-baseline` never silently certifies):
*
* - `public` — a freely-trainable, NON-ranking PRACTICE corpus (well-known events; prior knowledge OK).
* - `semi-private` — a sealed forward-window HOLDBACK corpus (blinded holdback; event-date > training_cutoff).
* - `private` — a custom HOLDBACK corpus (owner-authored; event-date > training_cutoff), ranking-eligible.
*
* NOTE — distinct from the catalog's opaque `CorpusTier` (src/protocol/catalog.ts, deliberately open, a
* DIFFERENT axis, b83-owned): this is the a57.14 envelope-identity axis, mirrored on the FACES pattern.
*/
export type CorpusTier = "public" | "semi-private" | "private";
/** The closed {@link CorpusTier} vocabulary in a FIXED order (deterministic validation reasons; mirrors
* {@link FACES}). */
export declare const CORPUS_TIERS: readonly CorpusTier[];
/**
* The practice-vs-holdback **benchmark-tiering wall** (kestrel-m9i.26; benchmark-matrix-design §3; ADR-0018
* season) mapped explicitly onto the {@link CorpusTier} vocabulary. A pure, TOTAL classifier over the closed
* set: `public` is the freely-trainable, non-ranking PRACTICE tier; `semi-private`/`private` are the sealed
* forward-window HOLDBACK tiers (event-date > training_cutoff, ranking-eligible). No wall clock/RNG.
*/
export declare function corpusTierWall(tier: CorpusTier): "practice" | "holdback";
/**
* The complete agent/model **experimental envelope** (kestrel-a57.14): the identity of the author that
* produced this session, captured from authenticated or explicitly INJECTED inputs and stamped on the
* GRADED bus META at open — exactly where the judge self-description (`fill_model`/`instance`/`fidelity`,
* a57.4) already rides — so a projector reads it as a PURE input and stamps it on the finalized Blotter.
* Its input half is the seam's `AgentConfig`/`ModelIdentity` descriptor; every value has traceable
* provenance and is never fabricated.
*
* NOTE — distinct from the bus event {@link EnvelopeBase} (the `{seq,ts,stream,type}` record envelope):
* this is the *experimental* envelope (the author's identity), a payload field, not the wire envelope.
*
* FAIL-CLOSED (a57.14; FOURTEEN fields since the m9i.2 owner tweaks — `face` + `adapter` +
* `adapter_version` joined the original eleven, mandatory-for-certified BEFORE the m9i.7 face/harness
* tournament): the projector treats all fourteen fields as REQUIRED identity. A declared envelope
* missing/empty on ANY one finalizes the Blotter as an explicit PROVISIONAL result with a recorded reason
* naming the field — no field is ever silently defaulted to `""`/`"unknown"`. The `prompt_hash` carries the
* prompt HASH, never the prompt content (the provenance fence).
*/
export interface ExperimentalEnvelope {
/** The model provider/family (generic identity — e.g. a vendor label). NEVER a credential. */
readonly provider: string;
/** The pinned model identity (family/snapshot). */
readonly model: string;
/** The model version/snapshot token. */
readonly version: string;
/** The model's training cutoff (the basis for model-relative holdback gating). */
readonly training_cutoff: string;
/** The sha256 HASH of the author prompt/profile — a hash, NEVER the prompt content (`AuthorPolicy`). */
readonly prompt_hash: string;
/** The tokenizer identity token costs are measured under (`RenderingIdentity`). */
readonly tokenizer: string;
/** The tool policy the author ran under (e.g. `no-tools`). */
readonly tool_policy: string;
/** The Rendering identity — how the Frame was rendered to tokens (`RenderingIdentity`). */
readonly rendering_identity: string;
/** What woke the author (e.g. `structural-cadence`). */
readonly wake_source: string;
/** The INJECTED token accounting (off the deterministic record path — the owner-away default). */
readonly token_accounting: TokenAccounting;
/** The corpus tier the author draws on (`public`|`semi-private`|`private`) — the practice-vs-holdback
* wall (kestrel-m9i.26). Typed loosely here (injected bytes, exactly like `face`); the projector enforces
* the closed {@link CorpusTier} vocabulary fail-closed (a typo / the retired `public-baseline` refuses). */
readonly corpus_tier: string;
/** The interface {@link Face} the author reached the model through (`http`|`sdk`|`cli`|`mcp`) — the
* m9i.7 tournament axis. Typed loosely here (injected bytes); the projector enforces the closed
* {@link FACES} vocabulary fail-closed (m9i.2 owner tweak). */
readonly face: string;
/** The adapter/harness identity that drove the author (e.g. `kestrel-live-agent` vs an external coding
* harness) — the field the our-harness-vs-others comparison pivots on (m9i.2 owner tweak). */
readonly adapter: string;
/** The adapter/harness version/revision token (paired with `adapter`). */
readonly adapter_version: string;
}
/**
* The typed fail-closed reason the {@link ExperimentalEnvelope}'s `training_cutoff` cross-check emits
* (kestrel-m9i.23), alongside the a57.14 "missing identity field" reasons. The declared `training_cutoff`
* is only a basis for model-relative holdback if it AGREES with the platform model registry keyed by
* `model` id (the `MODEL_KNOWLEDGE_CUTOFF` fence mirrored in `src/blotter/model-registry.ts`). Two ways it
* fails closed — each NAMES the disagreement so the projector can render `detail` onto the certification
* reasons and flip the Blotter to PROVISIONAL, never a silent default and never a silent correction of the
* declared value (an attestation-side VERIFICATION, never a rewrite):
*
* - `unknown-model-id` — no registry entry for `model`: the cutoff cannot be verified, so the row
* cannot certify as post-cutoff (NOT treated as post- or pre-cutoff).
* - `declared-registry-mismatch`— the declared `training_cutoff` disagrees with the registry's cutoff for
* `model` (`registry` present): the declared value is RECORDED, not rewritten.
*/
export interface TrainingCutoffReason {
readonly kind: "unknown-model-id" | "declared-registry-mismatch";
/** The declared model id the cross-check keyed on. */
readonly model: string;
/** The declared `training_cutoff` (recorded verbatim — never corrected to the registry's). */
readonly declared: string;
/** The registry's cutoff for `model` — present ONLY on a `declared-registry-mismatch`. */
readonly registry?: string;
/** The stable, NAMED message projected onto `certification.reasons` (fail-closed, deterministic). */
readonly detail: string;
}
/**
* The closed vocabulary of a per-leg failure the DERIVED `rankable` axis NAMES (kestrel-m9i.25), a sibling
* of the a57.14 certification reasons and the m9i.23 {@link TrainingCutoffReason}. `rankable` is a
* CONJUNCTION of five legs — CERTIFIED ∧ POST-CUTOFF ∧ DATE-BLIND ∧ (SEASON) FROZEN ∧ CLOCK-HONEST
* (the fifth leg is ADR-0040 / kestrel-w7la.3, appended LAST so every pre-existing reason ordering stays
* byte-stable) — DERIVED at projection from already-stamped fields, NEVER a stamped verdict a producer
* sets. Each `kind` names WHICH leg did not pass, and whether it was a hard-false (`not-certified` /
* `pre-cutoff` / `not-date-blind` / `season-not-frozen` / `not-clock-honest`) OR a fail-closed UNKNOWN
* (`cutoff-unknown` / `date-blind-unknown` / `season-unknown` / `clock-honest-unknown` — an ABSENT or
* malformed leg input is UNKNOWN, never a silent pass). `not-clock-honest` is reachable ONLY via a
* platform-side revocation (`meta.clock_honest: false`) — the driver stamps `true` or nothing, so an
* unattested (latency-blind) session is `clock-honest-unknown`, never `not-clock-honest`.
*/
export type RankableReasonKind = "not-certified" | "pre-cutoff" | "cutoff-unknown" | "not-date-blind" | "date-blind-unknown" | "season-not-frozen" | "season-unknown" | "not-clock-honest" | "clock-honest-unknown";
/**
* A typed, NAMED per-leg reason the DERIVED `rankable` axis (kestrel-m9i.25) records — mirroring the shape
* of {@link TrainingCutoffReason}: a closed {@link RankableReasonKind} plus the stable human `detail`. A
* not-rankable row can never be silently non-comparable — every non-passing leg NAMES itself. Present on the
* derived rankability ONLY when the row is not rankable (a rankable row carries none).
*/
export interface RankableReason {
readonly kind: RankableReasonKind;
/** The stable, NAMED message the derived rankable axis records (fail-closed, deterministic). */
readonly detail: string;
}
/**
* `{session_date, instruments, mode, bus_schema}` — the session header (plus, on a GRADED bus, the
* judge self-description). Exactly one META event opens a well-formed bus; the reader validates its
* `bus_schema`.
*
* **The two-bus rule (ADR-0011).** The market-data **INPUT tape** is fill-model-*agnostic* — the same
* recorded tape is graded under many models — so `fill_model`/`instance`/`fidelity` are **absent** on
* it. The **GRADED bus** (the input plus the engine's reactions, produced under one fill model)
* self-describes its judge: the session driver stamps these three at open (they are session INPUTS,
* known before the first event). They are therefore **OPTIONAL** here — present on a graded bus, absent
* on an input tape — and a future projector fails closed on a graded bus that lacks them.
*/
export interface MetaPayload {
readonly session_date: string;
readonly instruments: readonly SessionInstrument[];
readonly mode: Mode;
/** The schema this bus is written at — any {@link SupportedBusSchema}, deliberately NOT the literal
* {@link BusSchema}: graded OUTPUT buses stamp the current {@link BUS_SCHEMA}, while synthetic INPUT
* tapes keep their pinned literal (v5 — the input-tape-literal rule, clock-honest wakes §1) so the
* frozen corpus is never re-minted by a schema bump. */
readonly bus_schema: SupportedBusSchema;
/** The fill model that produced this graded bus's fills (a GRADED bus only; absent on an input tape). */
readonly fill_model?: FillModelStamp;
/** The Instance identity of this graded session (a GRADED bus only; absent on an input tape). */
readonly instance?: InstanceIdentity;
/** The fill realism this session may claim (a GRADED bus only; absent on an input tape). */
readonly fidelity?: Fidelity;
/** The author's experimental envelope (kestrel-a57.14) — the INJECTED agent/model identity, stamped at
* open alongside the judge fields. Additive/OPTIONAL exactly like `fill_model`/`instance`/`fidelity`: an
* input tape (and a pre-a57.14 graded bus) carries none. When present, the projector requires it COMPLETE
* or fails the Blotter closed to PROVISIONAL (a57.14). */
readonly envelope?: ExperimentalEnvelope;
/** The FULL `ConfigId` of the run's config descriptor (`sha256(canonical AgentConfig)` — kestrel-5zl.6),
* stamped at open alongside `envelope` whenever the config declares one (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 identity the grid
* CellKey keys on (run-identity.ts). Additive/OPTIONAL like `envelope`: absent on an input tape, a
* config-less graded bus, and a pre-tweak graded bus. */
readonly config_id?: string;
/** The **cell config axis** — the full ConfigId with the `fillSeed` REPLICATE axis STRIPPED
* (`deriveConfigId(cellCanonicalConfig(config))`, kestrel-9gu.8.1 / ADR-0016 §3). This is the config axis
* the grid CellKey/`cellOf` keys on: seed-only variants share it (replicates in one cell), while
* temperature/behavioral variants still split. The driver stamps it ONLY when it DIVERGES from `config_id`
* (i.e. the config carried a `fillSeed`) — a seedless run's cell axis EQUALS `config_id`, so no separate
* stamp and the graded bus is byte-identical to before. `config_id` keeps the FULL, seeded identity (the
* run identity feeding `SimRunId`). Additive/OPTIONAL: absent on an input tape, a config-less bus, a
* pre-tweak bus, and every seedless run. */
readonly cell_config_id?: string;
/** The sampled-channel qualification claim ({@link SampledQualificationClaim}, kestrel-9gu.8.6/9gu.12),
* stamped at open ONLY when the run's config records an owner-approved qualification AND the run is
* structurally consistent with it (a seeded run under a calibrated judge — the driver refuses to stamp a
* contradiction, RUNTIME §8). Additive/OPTIONAL: absent on an input tape and on every unqualified graded
* bus, whose headline therefore FAILS CLOSED to the strict-cross floor (9gu.8 AC#7). */
readonly sampled_qualification?: SampledQualificationClaim;
/** The **date-blind ATT attestation** (kestrel-m9i.25): the run is attested date-blind — the agent could
* not read the calendar date off the tape and the date-blinding held (the T-5 date-blind briefing,
* SYSTEM-PROFILE.md). A Layer-3 GOVERNANCE input stamped on the graded META beside the judge fields — NOT
* an a57.14 {@link ExperimentalEnvelope} field (the 14-field identity envelope stays frozen), and OFF the
* ConfigId hash (like `config_id`/`sampled_qualification`, it is a stamped result, never a config input).
* One of the DERIVED `rankable` legs the projector reads (never producer-trusted): `true` passes the
* date-blind leg, `false` fails it (`not-date-blind`), ABSENT/malformed is UNKNOWN ⇒ fail-closed
* (`date-blind-unknown`), never a silent rankable. Additive/OPTIONAL: absent on an input tape and on every
* un-attested graded bus. */
readonly date_blind?: boolean;
/** The **season-frozen ATT attestation** (kestrel-m9i.25): the benchmark season / submission is
* frozen/sealed — the content-addressed frozen submission before the window opens (platform ADR-0018). A
* Layer-3 GOVERNANCE input stamped on the graded META — NOT an a57.14 envelope field, and OFF the ConfigId
* hash. One of the DERIVED `rankable` legs (never producer-trusted): `true` passes the season leg, `false`
* fails it (`season-not-frozen`), ABSENT/malformed is UNKNOWN ⇒ fail-closed (`season-unknown`), never a
* silent rankable. Additive/OPTIONAL exactly like `date_blind`. */
readonly season_frozen?: boolean;
/** The **clock-honest attestation** (ADR-0040 / kestrel-w7la.1): the session ran clocked semantics —
* deliberation consumed tape time, priced by {@link DeliberationEvent} records — AND the §4 data-floor
* verification held (sub-minute ground truth). A Layer-3 stamped RESULT beside `date_blind`/
* `season_frozen`: NOT an envelope field, OFF the ConfigId hash. The driver stamps `true` or nothing —
* it NEVER stamps `false` (decided): `false` is reserved as a platform-side *revocation* value
* (governance marking a discovered clock-honesty defect hard-bad post-hoc); an OSS run is either
* attested-true or unattested. Absent ⇒ latency-blind ⇒ the run can never ground a latency claim
* (the m9i.25-style rankability leg that READS this is kestrel-w7la.3's). Additive/OPTIONAL: absent
* on an input tape and on every latency-blind graded bus — those stay byte-identical. */
readonly clock_honest?: boolean;
}
export interface MetaEvent extends EnvelopeBase, MetaPayload {
readonly stream: "META";
readonly type: "session";
}
/**
* The owner-recorded qualification claim for the SAMPLED fill channel (kestrel-9gu.8.6; 9gu.8 AC#7):
* the sampled (seeded-Bernoulli) channel may become the HEADLINE P&L only after its qualification study
* passes all three legs — **calibration** (the hazard judge is calibrated against live ground truth),
* **causality** (sampled fills enter positions/Plan management causally, not as terminal EV), and
* **fidelity** (realized distributions reconcile with live fill behaviour). `reference` NAMES where the
* approval is recorded (a bead / ADR) — provenance, never blank. Stamped on a GRADED bus's META
* ({@link MetaPayload.sampled_qualification}) so the headline selection re-derives from bus bytes
* (ADR-0011). No claim on record ⇒ the gate FAILS CLOSED and strict-cross floor stays the headline
* (kestrel-9gu.12).
*/
export interface SampledQualificationClaim {
/** Leg 1 — the hazard calibration passed qualification (9gu.8.6). */
readonly calibration: boolean;
/** Leg 2 — causal integration passed qualification (sampled fills drive management, 9gu.8). */
readonly causality: boolean;
/** Leg 3 — realized-fill fidelity passed qualification (reconciles with live ground truth). */
readonly fidelity: boolean;
/** Where the approval is recorded (bead/ADR id) — non-empty provenance. */
readonly reference: string;
}
/** `{instrument, px}` — one underlier print. For an **equity/spot instrument** the SPOT tick
* may also carry a two-sided **NBBO quote** (`bid`/`ask`, ADR-0017) so a spot leg fills against
* the instrument's own book (instrument-keyed strict-cross) and `@fair` resolves to the quote mid
* (`exec-fair-quote-v1`). Both are OPTIONAL and additive: an option-underlier SPOT sets neither, so
* every existing tape is byte-identical; a missing side is `null` (dark), never omitted or zero. */
export interface SpotPayload {
readonly instrument: string;
readonly px: number;
/** The equity NBBO bid (spot instruments); `null` when dark, absent on a plain underlier print. */
readonly bid?: number | null;
/** The equity NBBO ask (spot instruments); `null` when dark, absent on a plain underlier print. */
readonly ask?: number | null;
}
export interface SpotEvent extends EnvelopeBase, SpotPayload {
readonly stream: "TICK";
readonly type: "SPOT";
}
/**
* One option-leg quote inside a BOOK event. A missing side is `null` — not zero and not
* omitted — so the reducer can tell a genuinely one-sided or dark book (the MM-pull
* fingerprint of a real move; the observed mid is then a health signal, never a price,
* ARCHITECTURE §4) from a two-sided quote. `bid`/`ask` `null` ⇒ that side is dark; both
* `null` ⇒ the whole leg is dark.
*/
export interface OptionQuote {
readonly strike: number;
readonly right: Right;
readonly bid: number | null;
readonly ask: number | null;
/** Resting size on the bid (contracts); `null` when the bid is dark. */
readonly bsz: number | null;
/** Resting size on the ask (contracts); `null` when the ask is dark. */
readonly asz: number | null;
/** Last trade price, when one has printed for this leg in-session. */
readonly last?: number;
}
/** `{instrument, underlier_px, expiry?, legs}` — an option chain slice around the underlier
* at one moment, plus the underlying context (`underlier_px`) it was quoted against. `legs`
* is empty for an equity-only BOOK. */
export interface BookPayload {
/** The option underlier symbol the legs are written against. */
readonly instrument: string;
/** The underlier price at the moment this book was observed (the underlying context). */
readonly underlier_px: number;
/** The chain's expiry (a date `2026-07-17` or a tag `0dte`), when the session pins one. */
readonly expiry?: string;
/** The option legs. Empty for an equity-only session. */
readonly legs: readonly OptionQuote[];
}
export interface BookEvent extends EnvelopeBase, BookPayload {
readonly stream: "TICK";
readonly type: "BOOK";
}
/** A cadence event — proof of life on a quiet tape (RUNTIME §1). Carries the session phase
* when the calendar advances it. Used by the engine's staleness backstop. */
export interface HeartbeatPayload {
readonly phase?: SessionPhase;
}
export interface HeartbeatEvent extends EnvelopeBase, HeartbeatPayload {
readonly stream: "TICK";
readonly type: "HEARTBEAT";
}
/** The three TICK events. */
export type TickEvent = SpotEvent | BookEvent | HeartbeatEvent;
/** A detector emission. `detector`/`version` name the reducer (same input sequence ⇒ same
* detector event sequence); `type` is the detector's own event vocabulary (open). */
export interface DetectorPayload {
readonly detector: string;
readonly version: string;
readonly instrument?: string;
readonly value?: number | string | boolean | null;
}
export interface DetectorEvent extends EnvelopeBase, DetectorPayload {
readonly stream: "DETECTOR";
/** The detector's own event name (open vocabulary). */
readonly type: string;
}
/** Plan lifecycle state (RUNTIME §5: authored → armed → fired → managing → done). */
export type PlanState = "authored" | "armed" | "fired" | "managing" | "done";
/** Terminal outcome, present only on the `done` transition. */
export type PlanOutcome = "filled" | "expired" | "invalidated";
export interface PlanPayload {
/** The human-authored plan NAME — the **Lineage** key (names-are-data, ADR-0006). A name may legally
* RECUR after an earlier instance is `done` (CONTEXT: Lineage), so it is NOT an instance identity. */
readonly plan: string;
/** The exact per-Plan-**instance** identity (bus_schema v5, kestrel-22j.15): minted DETERMINISTICALLY at
* registration (the name + a registration ordinal; no wall clock / no unseeded RNG, RUNTIME §0), stable
* across replay. Distinguishes a legal same-name replacement from its predecessor so a report projection
* keeps them as TWO instances instead of collapsing one terminal outcome onto the other. ABSENT on a
* pre-v5 bus (a projector then migrates by lifecycle sessionization — never a silent mis-identify). */
readonly plan_instance?: string;
readonly state: PlanState;
readonly outcome?: PlanOutcome;
/** A logged reason for a de-arm / invalidation (fail-closed transitions are never silent). */
readonly reason?: string;
}
export interface PlanEvent extends EnvelopeBase, PlanPayload {
readonly stream: "PLAN";
readonly type: "lifecycle";
}
export type OrderAction = "place" | "cancel" | "fill" | "reject";
export interface OrderPayload {
readonly order_id: string;
/** The plan NAME (Lineage key) that authored the order, when it came from one. */
readonly plan?: string;
/** The exact Plan-**instance** identity that authored the order (bus_schema v5, kestrel-22j.15) — the same
* minted id carried on the plan's {@link PlanPayload.plan_instance}, so an order joins its exact instance,
* not merely its recurring name. Present when the order came from a plan on a v5 bus. */
readonly plan_instance?: string;
readonly instrument: string;
readonly side: "buy" | "sell";
readonly qty: number;
/** Limit price on `place`, fill price on `fill`. */
readonly px?: number;
readonly strike?: number;
readonly right?: Right;
/** Price-resolution annotation (e.g. `fair=fallback(mid)`) or reject/cancel reason. A
* silent mid is forbidden (RUNTIME §4); the annotation rides here. */
readonly reason?: string;
}
export interface OrderEvent extends EnvelopeBase, OrderPayload {
readonly stream: "ORDER";
readonly type: OrderAction;
}
/** A wake delivery, a coalesced/downgraded squelch record (visible in the kernel, never a
* silent drop), or a fire-then-inform lifecycle wake. */
export type WakeKind = "wake" | "coalesced" | "downgraded";
export interface WakePayload {
readonly wake: string;
/** The exact Plan-**instance** identity this wake belongs to (bus_schema v5, kestrel-22j.15) when the wake
* is a plan's fire-then-inform / de-arm inform — the same minted id on the plan's
* {@link PlanPayload.plan_instance}. Present for a plan-authored wake on a v5 bus. */
readonly plan_instance?: string;
/** The View the wake delivers, when it names one. */
readonly view?: string;
readonly priority?: number;
readonly reason?: string;
}
export interface WakeEvent extends EnvelopeBase, WakePayload {
readonly stream: "WAKE";
readonly type: WakeKind;
}
/**
* The **Deliberation record** (bus_schema v6 — ADR-0040 / kestrel-w7la.1, CONTEXT "Deliberation
* record"): the cost of ONE clocked Seat turn, recorded on the Bus so replay and certification
* re-project byte-identically from recorded time (the determinism invariant's recorded-time clause).
* One record per delivered vantage ANSWERED BY A SEAT (OPEN and every wake), in a clock-honest
* session only — a latency-blind session emits none, and an engine-synthesized turn (a replay
* divergence stand-down / inserted-vantage pass) is not Seat deliberation and records nothing.
*
* The envelope `ts` **is** the derived return time — `ts = checkpoint(wake_seq).ts + measured_ms +
* buffer_ms` — deliberately carrying no redundant `cost_ms`/`return_ts` field. The identity is scoped
* **record-vs-checkpoint, never record-vs-landing**: the OPEN turn's actions legitimately land at
* `max(firstTs, returnTs)`, later than this record's `ts` (clock-honest wakes §1/§2.6); the reader
* verifies the record against the checkpoint it names and the DRIVER owns landing semantics. All
* arithmetic is integer-ms addition — cross-arch bit-stable, never a hardware-minted NaN
* (kestrel-gdih). Distinct from {@link WakePayload} on purpose: `WakeKind`/`WakePayload` are NOT
* widened (`WakePayload` requires `wake: string`, which a deliberation record does not carry).
*/
export interface DeliberationPayload {
/** The `seq` of the WAKE checkpoint this cost belongs to (the vantage the Seat answered). */
readonly wake_seq: number;
/** Measured wall time of the Seat's WHOLE turn (bounded authoring loop + repair retries included,
* §2.7), minted ONCE at the driver's injectable `now` seam (§7). Integer ms ≥ 0. */
readonly measured_ms: number;
/** The configured latency buffer ({@link import("../session/agent.ts").AgentConfig.latencyBufferMs})
* added to every measured Seat turn. Integer ms ≥ 0; `0` declares a colocated seat. */
readonly buffer_ms: number;
}
export interface DeliberationEvent extends EnvelopeBase, DeliberationPayload {
readonly stream: "WAKE";
readonly type: "deliberation";
}
export interface ControlPayload {
/** The statement or pod node the action targets (by NAME — the Lineage key an author-action addresses). */
readonly target?: string;
/** The exact Plan-**instance** identity a control action targets (bus_schema v5, kestrel-22j.15), when the
* action addresses one specific instance rather than the recurring name. Author-actions still address by
* `target` (the name), so this is present only where a control resolves to a single minted instance. */
readonly plan_instance?: string;
/** The authenticated channel provenance tier, when the action carries one. */
readonly provenance?: string;
readonly note?: string;
}
export interface ControlEvent extends EnvelopeBase, ControlPayload {
readonly stream: "CONTROL";
/** The author action (`arm` | `disarm` | `author` | `supersede` | `allocate` | …). Open
* vocabulary — control is the L2→L1 intent channel, extended as the org grows. The de-arm
* discriminant (`disarm`) and its reason wording are owned by `src/engine/disarm.ts`
* (`DISARM_CONTROL_TYPE`, kestrel-z473.5) — the disarm EVENT itself is intrinsic and lives here. */
readonly type: string;
}
export interface RegimePayload {
/** The tag scope (`intraday`, `session`, …). */
readonly scope: string;
/** The tag value — an opaque, open-vocabulary token; the platform defines no taxonomy. */
readonly value: string;
/** Who wrote it (trader-agent / PM / app detector) — writes are always attributed. */
readonly writer: string;
readonly reason?: string;
}
export interface RegimeEvent extends EnvelopeBase, RegimePayload {
readonly stream: "REGIME";
readonly type: "tag";
}
/**
* What a JOURNAL record is (CONTEXT: Bus — "reasoning rides the bus too"):
* - `author` — reasoning written **pre-hoc**, BEFORE arming. Provable pre-hoc because its
* `seq` is strictly less than the first armed `PLAN`'s `seq` — the ordering is a fact of the
* log, never a claim of convention.
* - `debrief` — reasoning written **post-hoc**, AFTER the position CLOSEs, before finalize.
* Provable post-hoc because its `seq` is strictly greater than the CLOSE `seq`.
* - `note` — a free author note anywhere in the session.
*
* The reader tolerates an unknown/forward `kind` (a v3 journal read by a v2 build) by a logged
* skip, never a throw — so this vocabulary may grow without breaking older readers.
*/
export type JournalKind = "author" | "debrief" | "note";
/** The known JOURNAL kinds. A JOURNAL whose `kind` is outside this set is well-formed but
* unknown — the reader drops it with a logged reason (crash-tolerant forward-compat, RUNTIME §8),
* never widening it onto the union. */
export declare const JOURNAL_KINDS: ReadonlySet<string>;
/** `{kind, body}` — the author's reasoning with the **full markdown `body` inline**. Text is
* truth (ADR-0010): the reasoning is not a reference to some external doc, it rides the bus. */
export interface JournalPayload {
readonly kind: JournalKind;
readonly body: string;
}
/**
* A JOURNAL record. Unlike every other stream it carries **no engine `type`** — its discriminant
* is `kind` — because the engine never reacts to it (it is author metadata, so it has no
* `(stream,type)` pairing and does not participate in {@link isValidStreamType}). The reader
* validates it on `kind`/`body` directly and never feeds it to the per-event pass, which is what
* keeps the determinism invariant intact when JOURNAL records are added to a bus.
*/
export interface JournalEvent extends EnvelopeBase, JournalPayload {
readonly stream: "JOURNAL";
}
/**
* The telemetry event vocabulary. `order` is the per-order **working-life** record (a57.9) — the
* escalation stage a working order occupies plus a marker for the reprice (cancel/replace) that
* produced it. `settle` is the per-order **outcome** record (a57.1 slice 1) — the accrued
* probabilistic accounting the engine commits to the bus AT settle. `guard` is the per-order
* **fill-guard** record (kestrel-9gu.6) — the generic assessment inputs (side / moneyness bucket /
* covered-state) and the directional far-OTM cap decision the engine applied when it submitted a
* leg in the far-OTM wing; it carries NO strategy-specific data (no calibration constants, no
* private thresholds). The vocabulary is deliberately **open** (like DETECTOR/CONTROL) so per-order
* observability can grow without a schema bump; {@link isValidStreamType} accepts any non-empty
* telemetry `type` and a consumer ignores the ones it does not recognize (crash-tolerant, §8) —
* which is why adding `settle` / `guard` needs no reader-gate change.
*/
export type TelemetryType = "order" | "settle" | "guard" | "hazard" | "settle_mark" | "theta_bleed";
/** The telemetry `type`s this build emits/recognizes. Not a closed reader gate (telemetry is
* open-vocabulary) — it is the set a consumer folds; an unrecognized `type` is skipped, not an
* error. */
export declare const TELEMETRY_TYPES: ReadonlySet<string>;
/** Coarse moneyness bucket of a leg vs the decision-time underlier (the axis the directional fill
* guard keys on). Redeclared here so `src/bus` stays a leaf with no dependency on `src/fill`
* (mirrors {@link FillSupport}); it is the same vocabulary as the fill model's `Moneyness`. */
export type MoneynessBucket = "itm" | "atm" | "otm" | "deep_otm";
/** The directional far-OTM cap decision recorded on a {@link TelemetryGuardPayload} (kestrel-9gu.6):
* `applied` = a standalone far-OTM SELL floored to the no-fill cap (the fantasy the guard closes);
* `exempt` = a covering wing of a defined-risk package left uncapped; `na` = not a capped SELL (a
* far-OTM BUY, which is UNLOCKED not capped, or a leg outside the far-OTM wing). Fully derivable from
* `side` × `moneyness` × `covered`, carried explicitly so a projector never re-derives the policy. */
export type DirectionalCap = "applied" | "exempt" | "na";
/**
* `{order_id, esc_stage, reprice}` — one per-order **working-life** observability record (a57.9). The
* engine emits one at each order submission (carrying the esc-ladder rung the order rests at) and again
* whenever a working order's esc stage advances in place; `reprice` is `true` exactly on the
* cancel/replace submission that a peg-drift or esc-boundary reprice produced, which is what lets
* a report recover the honest session reprice tally by counting `reprice === true` records.
*
* This is **observational**: the engine never reads a telemetry record back, so emitting it
* cannot move any order/fill/plan decision. A report derives `orders[].esc_stages` (last
* `esc_stage` per `order_id`) and `session.reprice_count` (count of `reprice`) from this stream
* instead of scraping engine internals.
*/
export interface TelemetryOrderPayload {
/** The order ref this record is about (the engine's deterministic `oN` id / the gate ref). */
readonly order_id: string;
/** The esc-ladder rung this order occupied at emission (RUNTIME §4) — NOT a count of reprices. */
readonly esc_stage: number;
/** `true` on the submission a reprice (cancel/replace) produced; `false` on an initial submit or
* an in-place esc-stage advance. Summed over the stream, this is the session reprice tally (F6). */
readonly reprice: boolean;
}
/**
* `{order_id, expected_fill_prob, support, floor_filled, floor_pnl, expected_pnl}` — one per-order
* **settle-outcome** record (kestrel-a57.1 slice 1, ADR-0011). The engine (the single bus writer)
* emits exactly one per submitted order AT settle, committing the probabilistic accounting that used
* to live only in the engine's settle state + the report ONTO the bus — so the Blotter projector
* re-derives `totals{floor,expected}` and `orders[].support` from bus bytes alone, with **no engine-
* state scrape** ("the Bus is the truth", ADR-0010).
*
* This is what finally lands the 9gu.3 `calibrated | extrapolated` **support** flag on the bus (it was
* computed per-assessment but never persisted): a grader **refuses to bank expected-$ from an
* extrapolated cell**, so a projector needs the flag to honour that fail-closed default. An order's
* `support` is `extrapolated` iff ANY assessment folded into its survival product was extrapolated (or
* unstated) — a definite floor fill is a structural price-priority fact and is al