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.

1,025 lines (940 loc) 54.9 kB
/** * kestrel.markets/protocol — the CANONICAL wire-protocol types (v0.1). * * This module is the open, versioned contract that the managed backend * implements (ADR-0010): the platform is the SERVER and imports these SHAPES. * * HARD CONSTRAINTS (do not violate): * - ZERO runtime dependencies. This file imports NOTHING. It must typecheck * with `chdb` uninstalled and pull in no engine / language / series / fill * code. Keep it that way — it is the core side of the core-vs-local split. * - SHAPES ONLY. No signing keys, no Stripe/commerce logic, no corpus/presence. * Every signed, replayable artifact carries an opaque `*Ref` HANDLE, never a * key or a signing routine (ADR-0010 open/closed seam). * - GENERIC INSTRUMENTS ONLY. Order/Fill/Position/Blotter model instruments * generically (opaque `symbol: string`). No founding-app tickers or strategy * names appear in types, doc comments, or examples. Product tickers ride * INSIDE these fields at runtime; they are not part of the published contract. * * Scope: v0.1 covered the trial→conversion (M1) surface — authority-continuation, * purchase, and grade receipts. v0.2 ADDS, non-breakingly, the content-addressed * catalog contract (./catalog.ts), the incremental Session transcript * (./session.ts) — both re-exported below — and the M2–M4 receipt roots * (input-admission, resource-value, promotion, live-activation), which the * CertifiedReceipt umbrella admits as non-breaking additions to AnyReceipt. No * existing v0.1 shape changed, so 0.2 stays a purely additive minor. v0.3 ADDS, * non-breakingly, the oversight contract (./oversight.ts) — the cross-repo seam * between the CLI and Web Renderings of the human PM/Pod oversight surface * (ADR-0035), re-exported below. No existing shape changed, so 0.3 stays a purely * additive minor. * * Under 0.3, further ADDITIVE-OPTIONAL shapes have landed without a minor bump — * the same rule `inputPin` (kestrel-m9i.27), the OfferTerms vocabulary * (PLAT-ADR-0028), the OSS-ADR-0046 wire shapes, and the h314 certified-evidence * fields all rode under. The ex-ante catalog PRICING block (kestrel-adge — * {@link import("./catalog.ts").CatalogPricing} on the optional * {@link import("./catalog.ts").CatalogPage}) is another: it adds an OPTIONAL field * to a wrapped catalog body (absent ⇒ today's bare listing) and touches no existing * shape, so it rides additive under 0.3. The MINOR moves only for a deliberate * milestone surface (as at 0.2 and 0.3), never for each additive-optional field. * * Version: `major.minor`. Bump the MAJOR on any breaking shape change; bump the * MINOR for a purely additive milestone (v0.2 = catalog + Session, additive to * every v0.1 shape — no existing field changed). */ import type { OfferTerms } from "./offer.ts"; import type { FillSupport } from "./support.ts"; /* ────────────────────────────── version ────────────────────────────────── */ /** * Canonical protocol version (`major.minor`). Bump the major on any breaking * shape change; bump the minor for a purely additive milestone. v0.2 adds the * catalog + incremental Session surface without touching any v0.1 shape. */ export const PROTOCOL_VERSION = "0.4" as const; /** * @deprecated Alias of {@link PROTOCOL_VERSION} kept so existing consumers that * import `CONTRACT_VERSION` swap module specifiers without a rename. Remove once * all consumers migrate to PROTOCOL_VERSION. */ export const CONTRACT_VERSION = PROTOCOL_VERSION; /* ─────────────────────────────── faces ─────────────────────────────────── */ /** The four faces every operation must project identically (ADR-0004). */ export const FACES = ["http", "sdk", "cli", "mcp"] as const; export type Face = (typeof FACES)[number]; /* ──────────────────────────── scope + signers ──────────────────────────── */ /** * Authorization scopes (ADR-0003 two-signer rule). Ordered least→most * privileged. Treat as extensible: new scopes may be added, so consumers must * not exhaustively switch without a default. (Attestation is modeled separately * as {@link AttestationRole}, not as a Scope.) * * The trailing three are the FLEET/PM-tier scopes (OSS-ADR-0035 / PLAT-ADR-0031 §2), * neither wallet- nor per-act human-signable but granted as a whole PM/trader * ENVELOPE via the delegation handshake (PLAT-ADR-0031 §1): * - `fleet-evidence:self` — a trader reads its OWN blotter/journal/mandate/brief * (PLAT-ADR-0026 §5's own-scope `fleet-evidence read`, adopted here by name). * - `fleet-evidence:subtree` — a PM reads every child trader's evidence across its * workspace subtree (the new PM reach; supersedes 0026's own-only term, split into * `:self` == own and `:subtree` == subtree). * - `fleet:write` — a PM writes the population (hire/assign/allocate/arm on * the paper path; live-arming still suspends to a human per PLAT-ADR-0003). Widening * stays inexpressible in the algebra — a PM cannot mint authority it does not hold. */ export type Scope = | "data" | "sim" | "grade" | "paper" | "broker" | "live" | "fleet-evidence:self" | "fleet-evidence:subtree" | "fleet:write"; /** * Every authorization scope as a runtime tuple — the closed wire vocabulary of * {@link Scope}, ordered least→most privileged to match the type. The `satisfies` * proves the tuple stays a SUBSET of Scope; the paired exhaustiveness guard proves * it names EVERY member. Together they fail closed in BOTH directions: a scope * added to (or removed from) the union without editing this tuple breaks the * build, so the runtime vocabulary can never silently drift from the type. */ export const SCOPES = [ "data", "sim", "grade", "paper", "broker", "live", "fleet-evidence:self", "fleet-evidence:subtree", "fleet:write", ] as const satisfies readonly Scope[]; /** Reverse-exhaustiveness proof: any `Scope` omitted from {@link SCOPES} makes `_Missing`'s default non-`never`, which violates its `never` constraint and fails to compile. */ type _ScopesExhaustive<_Missing extends never = Exclude<Scope, (typeof SCOPES)[number]>> = true; /** Scopes a wallet may sign (reversible, commerce-only). The rest require a human. */ export const WALLET_SIGNABLE_SCOPES = ["data", "sim", "grade", "paper"] as const satisfies readonly Scope[]; /** True iff `scope` may be authorized by a wallet signature alone. */ export function isWalletSignable(scope: Scope): boolean { return (WALLET_SIGNABLE_SCOPES as readonly Scope[]).includes(scope); } /** Authority classes that can hold/sign a grant (ADR-0003). */ export type SignerClass = "wallet" | "human"; /** Runtime tuple of every {@link SignerClass}. Same both-ways closed-vocabulary guard as {@link SCOPES}. */ export const SIGNER_CLASSES = ["wallet", "human"] as const satisfies readonly SignerClass[]; type _SignerClassesExhaustive<_Missing extends never = Exclude<SignerClass, (typeof SIGNER_CLASSES)[number]>> = true; /** * Conjunctive attestation roles layered ON TOP of an authority signature * (ADR-0003 refinement). A grade/executor attestation NEVER substitutes for a * human authority signature — it is required IN ADDITION. */ export type AttestationRole = "compute" | "executor"; /** Runtime tuple of every {@link AttestationRole}. Same both-ways closed-vocabulary guard as {@link SCOPES}. */ export const ATTESTATION_ROLES = ["compute", "executor"] as const satisfies readonly AttestationRole[]; type _AttestationRolesExhaustive<_Missing extends never = Exclude<AttestationRole, (typeof ATTESTATION_ROLES)[number]>> = true; /** * Versioned result of resolving a requested scope set into the signatures and * attestations required to mint an Envelope. Conjunctive across roles. */ export interface SignerRequirementSet { readonly version: typeof PROTOCOL_VERSION; readonly scope: readonly Scope[]; /** Authority signatures required (all must be present). */ readonly requiredSigners: readonly SignerClass[]; /** Attestation roles required in addition to the authority signatures. */ readonly requiredAttestations: readonly AttestationRole[]; } /** * Revocation / currentness coordinate for one Root subtree (ADR-0003). Distinct * from the live fencing epoch. Monotonic within a subtree; a higher epoch * supersedes lower ones. */ export type AuthorityEpoch = number; /** Opaque identifier of a node in a workspace's pod/authority tree. */ export type NodeId = string; /** Opaque handle to a revocation record; resolution is server-side. */ export type RevocationRef = string; /** * Immutable, versioned top of a workspace authority tree (ADR-0003). Existing * children never widen; supersession re-derives a NARROWER grant. */ export interface RootGrant { readonly node: NodeId; readonly generation: number; readonly signerClass: SignerClass; readonly scope: readonly Scope[]; readonly authorityEpoch: AuthorityEpoch; } /** * A signed grant bound to one node of the pod tree (ADR-0003). Narrowing-only * downward: a derived child's scope/budget/ceiling never exceed its parent's. * This is an invariant on how Envelopes relate, not a field. */ export interface Envelope { readonly node: NodeId; readonly scope: readonly Scope[]; /** Cumulative spend cap granted (settlement asset minor units or abstract). */ readonly budget?: number; /** Per-operation cap. */ readonly ceiling?: number; /** ISO-8601 expiry; absent = no time bound. */ readonly expiry?: string; /** Opaque handle to this Envelope's revocation record, if revocable. */ readonly revocation?: RevocationRef; } /** Opaque handle to a minted/derived Envelope (used in receipts). */ export type EnvelopeRef = string; /* ──────────────────────────────── offer ────────────────────────────────── */ /** How an Offer may be settled (ADR-0002/0003). Discriminated on `kind`. */ export type SettlementMethod = | { readonly kind: "stripe-mpp" } | { readonly kind: "x402"; readonly network?: string; readonly asset?: string } | { readonly kind: "human-claim"; readonly url: string }; /** * Platform-signed commercial proposal bound to exactly one Operation (ADR-0002). * Accepting an Offer can mint ONLY its named Envelope's scope; price never * supplies broker/live authority — those still require a human signature. */ export interface Offer { readonly offerId: string; readonly operationId: string; /** Hash of the canonical agent intent this Offer prices. */ readonly intentHash: string; readonly scope: readonly Scope[]; /** Spend ceiling of the Envelope this Offer would mint. */ readonly ceiling: number; /** Amount to be settled to accept. */ readonly amount: number; /** Settlement asset code (e.g. "usd"); opaque to the protocol. */ readonly asset: string; /** Digest of the human-readable terms; full terms fetched by ref, not inlined. */ readonly termsDigest: string; /** ISO-8601 expiry after which the Offer is void. */ readonly expiry: string; /** Anti-replay nonce; one Offer accepts once. */ readonly nonce: string; readonly settlementMethods: readonly SettlementMethod[]; /** * The commercial terms this Offer prices ({@link OfferTerms}). Additive vocabulary * (PLAT-ADR-0028 A1): absent = the legacy implicit `one_shot` shape. Nouns only — no * billing/renewal/credit-burn SEMANTICS live on the protocol. */ readonly terms?: OfferTerms; } /* ─────────────────────────── proof + the 402 body ──────────────────────── */ /** * Shareable evidence of a certified result earned under a trial capability — * the conversion-moment artifact (ADR-0002). */ export interface Proof { readonly proofUrl: string; /** The capability under which the result was earned. */ readonly earnedUnder: string; /** Opaque handle to the certified result the proof points at. */ readonly resultRef: string; } /** * The structured HTTP 402 body (ADR-0002/0004) — part of the CONTRACT, not a * transport error. Every face (http/sdk/cli/mcp) exposes it identically. Carries * the free result already earned plus BOTH continuations, scope-gated. This * wraps an {@link Offer}; it is not an Offer itself. */ export interface PaymentRequired { readonly operationId: string; /** The free result already earned, as shareable proof. */ readonly proof: Proof; readonly offer: Offer; /** * The plural commercial alternatives on this 402 boundary (PLAT-ADR-0028 A1): e.g. a * `one_shot` beside a `recurring` or `committed_use_discount`, each a fully-formed * {@link Offer} carrying its own {@link OfferTerms}. Additive vocabulary — no * selection SEMANTICS here. INVARIANT (unenforced at this layer): when present it * includes the singular `offer` as its default/primary member. */ readonly offers?: readonly Offer[]; /** * Machine-settleable continuations. INVARIANT: a subset of the wrapped * `offer.settlementMethods` containing only the non-`human-claim` variants. */ readonly machineSettlement: readonly SettlementMethod[]; /** * The contextual human claim-and-fund continuation. INVARIANT: its `url` * equals the wrapped Offer's `human-claim` SettlementMethod url when present. */ readonly humanAction: { readonly url: string }; } /* ───────────────────────────── capabilities ────────────────────────────── */ /** * Ephemeral binding of an anonymous Operation's subject (ADR-0002/0009). Opaque * commitment; the platform resolves the real principal server-side. */ export type SubjectCommitment = string; /** Base grant-token shape common to all capabilities. */ export interface Capability { readonly capabilityId: string; readonly scopes: readonly Scope[]; /** ISO-8601 expiry. */ readonly expiry: string; } /** * Anonymous, ephemeral, rate-limited capability (ADR-0002). Permits catalog * discovery, validation, free sim instances, certified grades/blotters, * temporary artifacts, and proof URLs. EXCLUDES commerce, paper, broker, wallet, * and live — its `scopes` are a subset of WALLET_SIGNABLE_SCOPES. May PRESENT * external settlement evidence but never controls a wallet until a new Envelope * is minted. */ export interface TrialCapability extends Capability { readonly kind: "trial"; readonly subjectCommitment: SubjectCommitment; /** Requests-per-window budget; shape is server-defined, referenced opaquely. */ readonly rateLimit: { readonly limit: number; readonly windowSeconds: number }; } /* ───────────────────────────── operation ───────────────────────────────── */ /** Durable state of an Operation across the free→settle→complete lifecycle. */ export type OperationState = | "open" | "awaiting-settlement" | "awaiting-human" | "resumed" | "completed" | "failed"; /** Runtime tuple of every {@link OperationState}. Same both-ways closed-vocabulary guard as {@link SCOPES}. */ export const OPERATION_STATES = [ "open", "awaiting-settlement", "awaiting-human", "resumed", "completed", "failed", ] as const satisfies readonly OperationState[]; type _OperationStatesExhaustive<_Missing extends never = Exclude<OperationState, (typeof OPERATION_STATES)[number]>> = true; /** Resumable SSE event position (ADR-0004: resumable event cursors are canonical). */ export interface EventCursor { /** Monotonic event sequence number last delivered. */ readonly sequence: number; /** Opaque resume token the server maps back to a stream offset. */ readonly token: string; } /** * Durable, resumable control-plane unit for ONE canonical agent intent * (ADR-0004). Survives the free boundary, machine settlement, human completion, * callbacks, and retries WITHOUT changing `operationId`. */ export interface Operation { readonly operationId: string; readonly intentHash: string; readonly state: OperationState; readonly cursor: EventCursor; /** Opaque handle to the latest durable checkpoint, if any. */ readonly checkpoint?: string; readonly authorityEpoch: AuthorityEpoch; /** Incremented each time the Operation is resumed after a boundary. */ readonly resumeGeneration: number; } /* ───────────────────────────── receipts ────────────────────────────────── */ /** * Umbrella base for every platform-signed, replayable receipt (ADR-0004). The * signature is referenced by opaque handle — never the key or routine. * Concrete receipts compose this base. */ export interface CertifiedReceipt { readonly kind: string; /** Opaque handle to the platform's signature over this receipt. */ readonly signatureRef: string; /** Root nodes whose authority this receipt is bound under. */ readonly boundRoots: readonly NodeId[]; /** ISO-8601 issuance time. */ readonly issuedAt: string; } /** * Immutable record binding an Operation to its canonical intent, proposal, * qualification, checkpoint, successor Root, child-Envelope derivation, authority * epoch, resume generation, and first resumed event sequence (ADR-0004). * Idempotent on duplicate callbacks; cannot repeat a completed semantic-effect * slot. */ export interface AuthorityContinuationReceipt extends CertifiedReceipt { readonly kind: "authority-continuation"; readonly operationId: string; readonly intentHash: string; readonly successorRoot: NodeId; readonly derivedEnvelope: EnvelopeRef; readonly authorityEpoch: AuthorityEpoch; readonly resumeGeneration: number; readonly firstResumedSequence: number; } /** * Settlement evidence on a {@link PurchaseReceipt}: either a provider payment id * or an on-chain transaction. Discriminated by presence. */ export type ProviderPayment = | { readonly provider: string; readonly paymentId: string } | { readonly chainTx: string }; /** * Record that an Offer was settled and an Envelope granted (ADR-0003). Each * provider payment id satisfies one Offer ONCE; duplicate callbacks are * idempotent; cross-Operation replay is refused (fail-closed). */ export interface PurchaseReceipt extends CertifiedReceipt { readonly kind: "purchase"; readonly offerId: string; /** Opaque handle to the paying principal; resolution is server-side. */ readonly payerPrincipal: string; readonly providerPayment: ProviderPayment; readonly acceptedTerms: { readonly termsDigest: string }; readonly grantedEntitlement: EnvelopeRef; /** Opaque handle to the delivered artifact/stream. */ readonly delivery: string; } /* ──────────────────────── generic instruments ──────────────────────────── */ /** * Generic instrument descriptor. Opaque `symbol` — NO founding-app tickers in * the contract; product symbols ride here at runtime. Covers equity/ETF and a * generic option leg; extend via `kind` without encoding any strategy. */ export type Instrument = | { readonly kind: "equity"; readonly symbol: string } | { readonly kind: "option"; readonly symbol: string; readonly right: "call" | "put"; readonly strike: number; /** ISO-8601 (date or datetime) expiry. */ readonly expiry: string; }; export type Side = "buy" | "sell"; /** Runtime tuple of every {@link Side}. Same both-ways closed-vocabulary guard as {@link SCOPES}. */ export const SIDES = ["buy", "sell"] as const satisfies readonly Side[]; type _SidesExhaustive<_Missing extends never = Exclude<Side, (typeof SIDES)[number]>> = true; /** A single order as recorded in a Blotter. Generic instrument fields only. */ export interface Order { readonly orderId: string; readonly instrument: Instrument; readonly side: Side; readonly qty: number; /** Limit price in the instrument's quote units. */ readonly price: number; /** ISO-8601 time the order was placed. */ readonly placedAt: string; } /** A single fill against an order. Generic instrument fields only. */ export interface Fill { readonly fillId: string; readonly orderId: string; readonly instrument: Instrument; readonly side: Side; readonly qty: number; readonly price: number; /** ISO-8601 time the fill occurred. */ readonly filledAt: string; } /** A held position snapshot. Generic instrument fields only. */ export interface Position { readonly instrument: Instrument; /** Signed quantity (positive = long, negative = short). */ readonly qty: number; /** Average entry price in quote units. */ readonly avgPrice: number; } /** Terminal settlement summary of a Session run. */ export interface Settlement { /** Realized cash result in the settlement asset's minor units or abstract. */ readonly realized: number; readonly asset: string; /** ISO-8601 settlement time. */ readonly settledAt: string; } /* ─────────────────── certified evidence (kestrel-h314, additive) ─────────────────── */ /** * Which P&L channel a Blotter's headline number came from (CONTEXT "Headline"): the conservative * definite-fill `floor`, the probability-weighted `expected`, or a seeded causal `sampled` * realization. Mirrors the engine's channel vocabulary — the wire copy so an outsider recomputing a * grade reads the same channel identity without the runtime. */ export type PnlChannel = "floor" | "expected" | "sampled"; /** * The sampled-channel qualification-gate verdict carried on the headline (CONTEXT "Certified evidence"): * whether the sampled channel earned headline status and, when refused, the fail-closed `reasons`. A * verdict-only wire shape — no study internals, just the pass/fail + why. */ export interface SampledQualification { readonly qualified: boolean; readonly reasons: readonly string[]; } /** * The headline record on a Blotter's totals (CONTEXT "Certified evidence"): WHICH `channel` is the * headline and its `usd` value, the qualification `gate` verdict that selected it, and the honesty * `tainted` bit (with `reasons`) — `true` when any non-zero expected contribution rode on extrapolated * support or the settle mark itself was untrustworthy. The number always travels with its taint attached, * never silently. */ export interface HeadlineFlag { readonly channel: PnlChannel; readonly usd: number; readonly gate: SampledQualification; readonly tainted: boolean; readonly reasons: readonly string[]; } /** * The verdict-only projection of the engine's settle mark that crosses onto the public Blotter * (CONTEXT "SettleMarkVerdict"; kestrel-h314 D3, DESIGN §1.3 exposure boundary). The engine's rich * `SettleMarkRecord` — the raw vendor settle spot (`px`) plus its `as_of_ts` / `last_observed_ts` / * `age_ms` observation timestamps — is LICENSED data and stays private; only the derived staleness * VERDICT ships, so a Blotter can carry settle-mark honesty without redistributing vendor data. */ export interface SettleMarkVerdict { /** Was the mark stale at the settle instant (taints {@link HeadlineFlag})? */ readonly stale: boolean; /** The mark's provenance: a live `market` print, a `parity`-recovered spot, or a retained `stale` value. */ readonly source: "market" | "parity" | "stale"; /** `true` when the mark could not be recovered (the loudest taint) — a non-bankable settle. */ readonly mark_uncertain: boolean; /** The honest human annotation, when the recovery/staleness layer recorded one. */ readonly note: string | null; } /** * The session totals the certified risk-honest metrics recompute from (CONTEXT "Certified evidence"; * kestrel-h314 D2). Un-dropped from the rich engine Blotter — byte-stable and deterministic, NOT invented * arithmetic. `floor` and `expected` are always present; `sampled` only on a seeded run (ABSENT — never * `0` — when the channel is off). `settle_mark` is the verdict-only {@link SettleMarkVerdict}, absent on a * pre-staleness-provenance bus. */ export interface Totals { /** Definite-fill $ only — the conservative floor (`realized_floor_usd`). */ readonly floor: number; /** E[$] weighted by `1 − survival` (`expected_usd`). */ readonly expected: number; /** The seeded causal realization; ABSENT (not `0`) when the sampled channel is off. */ readonly sampled?: number; readonly headline: HeadlineFlag; /** The settle mark's verdict-only staleness projection, when the bus recorded settle-mark provenance. */ readonly settle_mark?: SettleMarkVerdict; } /** * One support class's slice of a Blotter's expected-$ (CONTEXT "Certified evidence"; kestrel-h314 D2, * ADR-0014). The certified `fill_claim` partitions `totals.expected` into the `calibrated` slice a grader * banks and the `extrapolated` slice it refuses to bank. SIGNED sub-cells (`gain ≥ 0`, `loss ≤ 0`, * `expected === gain + loss`) so a consumer that re-banks by SUBTRACTING the extrapolated slice removes * only its `gain` — extrapolated LOSSES survive (the `bankableEv` doctrine) and can never be zeroed. The * wire slice drops the engine record's per-slice `orders` count (not needed to recompute the metric). */ export interface FillClaim { /** The support class this slice aggregates (CONTEXT "Support flag"). */ readonly support: FillSupport; /** Σ expected-$ over this slice's orders (on the grader's 1e-8 grid). Equals `gain + loss`. */ readonly expected: number; /** The slice's GAINS: Σ non-negative expected-$ (≥ 0). A subtracting consumer removes ONLY this. */ readonly gain: number; /** The slice's LOSSES: Σ negative expected-$ (≤ 0) — KEPT for extrapolated cells (never forgiven). */ readonly loss: number; } /** * Certified execution record of a Session run (CONTEXT "Blotter") — the WHAT * HAPPENED, distinct from a Grade's judgment. Generic instruments only. * * CERTIFIED EVIDENCE (kestrel-h314, D2 — additive-optional): `totals` and `fill_claim` un-drop the * risk-honest accounting the rich engine Blotter already carries, so the risk-honest metric family * (bankable EV, realized floor, expected-$) is OUTSIDER-RECOMPUTABLE from the published artifact — not * only inside the private engine. Their ABSENCE on a pre-widening Blotter reads as UNKNOWN and fails the * risk-honest family CLOSED (never a silent zero — D6); the {@link ./grade.ts grade} Judge refuses to * certify those metrics for a Blotter that lacks the evidence. */ export interface Blotter { readonly sessionId: string; readonly orders: readonly Order[]; readonly fills: readonly Fill[]; readonly positions: readonly Position[]; readonly settlement: Settlement; /** * The session totals — the certified risk-honest accounting (kestrel-h314 D2). ADDITIVE-OPTIONAL: * ABSENT on a pre-widening Blotter, where its absence reads as UNKNOWN and fails the risk-honest * metrics closed (D6) — never a silent zero. */ readonly totals?: Totals; /** * The session's expected-$ partitioned by fill support (kestrel-h314 D2): the `calibrated` slice a * grader banks and the `extrapolated` slice it refuses to bank. When present, always two entries in the * fixed order `[calibrated, extrapolated]` (deterministic, byte-stable). ADDITIVE-OPTIONAL: ABSENT on a * pre-widening Blotter (UNKNOWN, fails the risk-honest family closed — D6). Part of the certified * evidence alongside {@link totals} (the totals alone are not enough — CONTEXT "Certified evidence"). */ readonly fill_claim?: readonly FillClaim[]; } /* ───────────────────────────── grade ───────────────────────────────────── */ /** * The judged verdict over one or more Blotters (CONTEXT "Certified grade"). The * COMPUTED numbers — portable: a self-hosted Kestrel produces the same values; * only the platform mints the trusted, signed receipt form ({@link CertifiedGrade}). * Metric keys are open (a string→number map) so new dimensions need no shape bump. */ export interface GradeResult { readonly subjectSessionId: string; /** Open metric map, e.g. { "bankable_ev": ..., "fill_support": ... }. */ readonly metrics: Readonly<Record<string, number>>; /** Optional discrete verdict labels, e.g. flags an honest-fill judge sets. */ readonly labels?: Readonly<Record<string, string>>; } /** * The signed, replayable receipt form of a {@link GradeResult}. Pins the data and * fill-model versions so anyone can replay to the identical numbers. */ export interface CertifiedGrade extends CertifiedReceipt { readonly kind: "grade"; readonly result: GradeResult; /** Opaque handle to the pinned data snapshot the grade ran over. */ readonly pinnedDataRef: string; /** Version tag of the fill model used. */ readonly pinnedFillModelVersion: string; /** Opaque handle to the judge's signature. */ readonly judgeSignatureRef: string; readonly replayable: true; } /* ─────────────────────── M2–M4 receipt roots (additive) ────────────────────── */ /** * Admission verdict for a purchased/delivered input (M2) — the fail-closed * trichotomy on the wire (18-operation-promotion-protocols.md): * - `known` a verified, complete delivery within scope — the ONLY admittable state. * - `unknown` qualified, but delivery/schema/watermark/freshness/content/use could * not be established — NOT presented as a known zero or neutral default. * - `omitted` not acquired under any entitlement — NOT silently substituted. * This is the Kestrel KNOWN | UNKNOWN | OMITTED doctrine (fail closed) on the wire. */ export type AdmissionState = "known" | "unknown" | "omitted"; /** Runtime tuple of every {@link AdmissionState}. Same both-ways closed-vocabulary guard as {@link SCOPES}. */ export const ADMISSION_STATES = ["known", "unknown", "omitted"] as const satisfies readonly AdmissionState[]; type _AdmissionStatesExhaustive<_Missing extends never = Exclude<AdmissionState, (typeof ADMISSION_STATES)[number]>> = true; /** * Receipt-gated admission of a purchased/delivered input into an Operation (M2). * Verifies the qualification receipt (entitlement + accepted terms) AND the * delivery receipt (what bytes/blocks/result were delivered), then binds the exact * Field identity, content root, source watermark, and consumer use scope. The * {@link AdmissionState} trichotomy fails closed: only a verified complete delivery * within scope is `known`. Source: 18-operation-promotion-protocols.md * `InputAdmissionReceipt`; the schema_version / receipt_id / canonical_digest / * signature patent fields fold into the {@link CertifiedReceipt} base. */ export interface InputAdmissionReceipt extends CertifiedReceipt { readonly kind: "input-admission"; /** Opaque root of the purchased-input manifest. */ readonly inputManifestRoot: string; /** Opaque root of the qualification receipt proving entitlement + accepted terms. */ readonly qualificationReceiptRoot: string; /** Opaque root of the delivery receipt proving what was delivered. */ readonly deliveryReceiptRoot: string; /** Opaque identity of the admitted Field. */ readonly admittedFieldId: string; /** Opaque handle to the admitted Field's declared type/schema. */ readonly admittedType: string; /** Opaque content root of the admitted value/content. */ readonly admittedContentRoot: string; /** Opaque source-watermark handle bounding freshness/provenance. */ readonly admittedSourceWatermark: string; /** Opaque handle to the consumer use scope the admission authorizes. */ readonly admittedUseScope: string; /** Opaque root of the consumer artifact the input is admitted into. */ readonly consumerArtifactRoot: string; /** The fail-closed admission verdict (KNOWN | UNKNOWN | OMITTED). */ readonly resultingState: AdmissionState; /** Stated reason when `unknown`/`omitted`; absent on `known`. */ readonly reason?: string; /** Opaque coordinate of the entitlement consumed, when one was. */ readonly consumedEntitlementAt?: string; } /** * Metered marginal technical value of an admitted input (M3), measured by a * one-intervention baseline↔variant fork under a SHARED environment: the same * runtime/binding roots and named Judge grade both branches, isolating the single * admitted Field as the only change. The open `technicalEffectVector` carries the * named machine measures (decision agreement, indeterminate-decision count, token * consumption, latency, …) as a GradeResult-style string→number map, so a new * metric needs no shape bump. Binds acquisition cost WITHOUT claiming that a * favorable financial return proves technical validity. Source: * 18-operation-promotion-protocols.md `ResourceValueReceipt`. */ export interface ResourceValueReceipt extends CertifiedReceipt { readonly kind: "resource-value"; /** Opaque root of the purchased-input manifest under valuation. */ readonly purchasedInputManifestRoot: string; /** Opaque root of the qualification receipt for the purchased input. */ readonly qualificationReceiptRoot: string; /** Opaque root of the {@link InputAdmissionReceipt} that admitted the input. */ readonly inputAdmissionReceiptRoot: string; /** Opaque root of the one-intervention branch descriptor. */ readonly branchDescriptorRoot: string; /** Opaque root of the baseline branch's output. */ readonly baselineOutputRoot: string; /** Opaque root of the variant branch's output. */ readonly variantOutputRoot: string; /** Opaque root of the baseline branch's grade receipt. */ readonly baselineGradeReceiptRoot: string; /** Opaque root of the variant branch's grade receipt. */ readonly variantGradeReceiptRoot: string; /** Opaque root of the environment inputs held identical across both branches. */ readonly sharedEnvironmentInputRoot: string; /** Opaque root of the conserved semantic artifact under test. */ readonly semanticArtifactRoot: string; /** Opaque roots pinning the runtime + binding held constant across branches. */ readonly runtimeAndBindingRoots: readonly string[]; /** Opaque identity of the named Judge that graded both branches. */ readonly namedJudgeIdentity: string; /** Opaque handle to the acquisition amount / compute-cost measure. */ readonly acquisitionMeasure: string; /** Open named machine-measure map (GradeResult-style); a new metric needs no shape bump. */ readonly technicalEffectVector: Readonly<Record<string, number>>; /** The computed baseline↔variant comparison result. */ readonly comparisonResult: string; /** Opaque root of the confidence / repetition manifest, when present. */ readonly confidenceManifestRoot?: string; } /** * A candidate promoted per the promotion policy (M3/M4): the conserved semantic * artifact is carried unchanged while ONLY enumerated deployment dimensions change. * The policy verifies engine/Judge identities, input/output roots, receipt classes, * evidence freshness, semantic-root EQUALITY, thresholds, and approvals; it refuses * a rebuilt artifact lacking a semantic-equivalence proof or a report produced under * another Judge. Source: 18-operation-promotion-protocols.md `PromotionReceipt`. */ export interface PromotionReceipt extends CertifiedReceipt { readonly kind: "promotion"; /** Opaque root of the promotion policy the promotion was verified against. */ readonly promotionPolicyRoot: string; /** Opaque root of the evidence set the policy verified. */ readonly promotionEvidenceSetRoot: string; /** Opaque root of the semantic artifact conserved across the promotion. */ readonly conservedSemanticArtifactRoot: string; /** Opaque root of the prior deployment, when superseding one. */ readonly priorDeploymentRoot?: string; /** Opaque root of the successor deployment. */ readonly successorDeploymentRoot: string; /** The enumerated deployment dimensions permitted to change (mode/envelope/fence/…). */ readonly changedDeploymentDimensions: readonly string[]; /** Opaque handle to the supersession barrier, when one gates the promotion. */ readonly supersessionBarrierRef?: string; /** Preconditions that must hold before live activation (e.g. human approval). */ readonly activationPreconditions: readonly string[]; /** The policy verification result. */ readonly verificationResult: string; } /** * Live-activation state under the staged fencing protocol (M4). No live initiation * occurs before `active`: * - `preparing` a next fencing epoch is reserved. * - `fence-confirmed` the epoch is installed and confirmed at the live Gate. * - `active` the candidate actuates; the prior holder is fenced out. * - `refused` activation was refused; the candidate stays non-actuating. * A failed fence leaves the candidate non-actuating — fail closed. */ export type ActivationState = "preparing" | "fence-confirmed" | "active" | "refused"; /** Runtime tuple of every {@link ActivationState}, in staged-fencing order. Same both-ways closed-vocabulary guard as {@link SCOPES}. */ export const ACTIVATION_STATES = ["preparing", "fence-confirmed", "active", "refused"] as const satisfies readonly ActivationState[]; type _ActivationStatesExhaustive<_Missing extends never = Exclude<ActivationState, (typeof ACTIVATION_STATES)[number]>> = true; /** * A config/agent activated live (M4). The live execution module verifies the * control-plane signature, capsule digest, target-executor binding, Authority Head, * narrowing certificate, Envelope, approvals, expiry, and fencing epoch, then * follows the staged fencing protocol via {@link ActivationState}. Fencing epochs * are monotonic: the reserved epoch supersedes the prior. Possession of an old * capsule/artifact/credential reference is insufficient. Source: * 18-operation-promotion-protocols.md `LiveActivationReceipt`. */ export interface LiveActivationReceipt extends CertifiedReceipt { readonly kind: "live-activation"; /** Opaque root of the Deployment Capsule being activated. */ readonly deploymentCapsuleRoot: string; /** Opaque identity of the target executor bound to the capsule. */ readonly targetExecutorIdentity: string; /** Opaque root of the ownership record for the activation. */ readonly ownershipRecordRoot: string; /** The prior holder's fencing epoch (monotonic; superseded by the reserved one). */ readonly priorFencingEpoch: number; /** The reserved successor fencing epoch. */ readonly reservedFencingEpoch: number; /** Opaque root of the fence confirmation from the live Gate. */ readonly fenceConfirmationRoot: string; /** The staged-fencing activation state (no live initiation before `active`). */ readonly activationState: ActivationState; /** Opaque digest of the current Authority Head at activation. */ readonly activeAuthorityHeadDigest: string; /** Event sequence at which the activation took effect. */ readonly activatedAtSequence: number; } /** * Discriminated union of every concrete certified receipt. Switch on `.kind` for * exhaustive server-side dispatch. v0.1 minted the M1 trial→conversion roots * (authority-continuation, purchase, grade); v0.2 additively admits the M2–M4 roots * (input-admission, resource-value, promotion, live-activation) — the * CertifiedReceipt umbrella makes each a non-breaking addition. */ export type AnyReceipt = | AuthorityContinuationReceipt | PurchaseReceipt | CertifiedGrade | InputAdmissionReceipt | ResourceValueReceipt | PromotionReceipt | LiveActivationReceipt; /** * Every concrete receipt discriminant as a runtime tuple — the closed `.kind` * wire vocabulary of {@link AnyReceipt}. Driven by the union itself (`satisfies * readonly AnyReceipt["kind"][]`) so extending {@link AnyReceipt} with a new * receipt root (M2–M4) forces this tuple to grow in lockstep. Same both-ways * closed-vocabulary guard as {@link SCOPES}: a `.kind` added to (or removed from) * the union without editing this tuple breaks the build. Fail-closed, no drift. */ export const RECEIPT_KINDS = [ "authority-continuation", "purchase", "grade", "input-admission", "resource-value", "promotion", "live-activation", ] as const satisfies readonly AnyReceipt["kind"][]; type _ReceiptKindsExhaustive<_Missing extends never = Exclude<AnyReceipt["kind"], (typeof RECEIPT_KINDS)[number]>> = true; /* ─────────────────────────── arm refusals (OSS-ADR-0045) ─────────────────────────── */ /** * The closed, wire-stable vocabulary of reasons the plan engine REFUSES to arm a * statement (OSS-ADR-0045). A refusal is DATA, never a matched message string: a * consumer routes on `code`, and the human-readable `message`/`repair` are DISPLAY * only (a wording change is never a breaking change). Extensible — add a code here * and to {@link ARM_REFUSAL_CODES} in lockstep; consumers must not exhaustively * switch without a default. Two codes today: * - `unknown-series` — a trigger names a series the FROZEN phonebook does not know: * a case-variant of a known market fact (`VWAP` for `vwap`, `VELOCITY` for the * windowed `velocity`), refused with a did-you-mean {@link ArmRefusal.repair}. The * offending statement is refused AS DATA; its healthy siblings still arm. This * reverts the 0.4.5 case-insensitive forgiveness AND the silent runtime de-arm (a * plan that would have armed-then-never-fired): an unknown series is a LOUD, * repairable refusal at arm, never a silent default (RUNTIME §8). * - `plan-name-in-use` — a revision re-declares a plan NAME still owned by a LIVE * (managing/inventory-holding) prior record. This one REMAINS a throw (an * `ArmError`, whole-document): arming would clobber the by-name ledger and garble * the merged lifecycle — names are lineage; author a new name. * - `manages-nothing` — a plan carries a covered sell (`EXIT`/`TP`, both of which can * only ever sell inventory the plan HOLDS) but NO way to ever hold inventory: no * `DO`/`ALSO`/`RELOAD` entry to acquire, no `ARM … foreach|any held leg` to adopt a * superseded plan's held leg, and no `foreach|any held leg` on the cover itself. Its * exit can never cover, so the plan would arm and sit inert forever — the silent * one-way door r866 fixed, one dropped keyword away (an EXIT-only revision that forgot * the adoption binding). Refused AS DATA at arm — the offending statement never * registers, its healthy siblings still arm — so `parse --arm`, `/validate`, and a * real run all fail closed identically (kestrel-b4wx/kestrel-hcnj, RUNTIME §4/§8). * - `opening-short-uncovered` — a MULTI-LEG opening structure: an ENTRY (`DO`/`ALSO`) SELL * leg CO-EXISTS with an opening BUY leg on a plan that adopts NO inventory (no `ARM … * foreach|any held leg`, no `EXIT`/`TP … foreach|any held leg`). An entry sell is covered * ONLY by inventory the plan HOLDS at fire — and at the one entry fire nothing has filled * yet (a same-fire entry BUY is still resting) and cross-strike structural coverage (a * debit spread's long leg covering its short) needs ATOMIC combo execution the engine does * not yet do (the AST `atomic` keyword is reserved-but-refused). So such a sell is DEAD ON * ARRIVAL: never-naked refuses it at every fire while the plan's BUY leg fires ALONE, * silently degrading a debit spread to a naked long whose proof is BYTE-IDENTICAL to the * single long leg (kestrel-53gw). Refused AS DATA at arm — LOUD, never a silent per-leg * drop — until atomic multi-leg structures land (kestrel-psn5). Scoped to that misleading- * proof shape: a PURE naked opening sell (no opening buy) is NOT caught here — its proof is * order_count=0 (distinguishable) and it keeps its fire-time never-naked diagnostic (ADR-0017). */ export type ArmRefusalCode = "unknown-series" | "plan-name-in-use" | "manages-nothing" | "opening-short-uncovered"; /** * Runtime tuple of every {@link ArmRefusalCode} — the closed wire vocabulary. Same * both-ways closed-vocabulary guard as {@link SCOPES}: a code added to (or removed * from) the union without editing this tuple breaks the build, so the runtime * vocabulary can never silently drift from the type. */ export const ARM_REFUSAL_CODES = ["unknown-series", "plan-name-in-use", "manages-nothing", "opening-short-uncovered"] as const satisfies readonly ArmRefusalCode[]; /** Reverse-exhaustiveness proof: any `ArmRefusalCode` omitted from {@link ARM_REFUSAL_CODES} makes `_Missing`'s default non-`never`, which violates its `never` constraint and fails to compile. */ type _ArmRefusalCodesExhaustive<_Missing extends never = Exclude<ArmRefusalCode, (typeof ARM_REFUSAL_CODES)[number]>> = true; /** * One coded arm-time refusal (OSS-ADR-0045). `statement` is the offending plan's * NAME; `code` is the wire-stable reason ({@link ArmRefusalCode}); `message` is the * human-readable diagnostic (DISPLAY only — NEVER matched as an interface); `repair`, * when present, is a concrete did-you-mean fix (e.g. the canonical lowercase series * name a case-variant meant). */ export interface ArmRefusal { readonly statement: string; readonly code: ArmRefusalCode; readonly message: string; readonly repair?: string; } /** * Every arm-time NOTICE code (kestrel-hk9u). A notice is NON-FATAL: unlike an * {@link ArmRefusal}, the statement still arms (it is a legal, integrity-clean * document) — the notice reports a condition that will keep the plan from ever * FIRING on the arming context, so an author is not silently handed a plan that * no-ops. Keep this union and {@link ARM_NOTICE_CODES} in lockstep. One code today: * - `regime-unsatisfiable` — the plan carries a `regime {scope: value}` gate whose * scope no REGIME writer on the arming context's tape will ever write, so the plan * stays `authored (blocked)` for the whole session and never fires. This is the * silent-gate the docs mistaught as an opaque author tag (the plan parses, arms, * and then no-ops on every regime-less tape); the notice makes the dead-on-arrival * verdict LEGIBLE at arm, identically across `parse --arm`, the hosted /validate * route, and a real batch run (RUNTIME §8). * - `adoption-bound-nothing` — the plan carries a valid `ARM … foreach|any held leg` * adoption binding, but at arm it resolved to ZERO held legs (no superseded, * inventory-holding sibling to adopt from). It arms and expires without ever covering, * so its exit protection is inert — indistinguishable on the run report from a working * adoption until this notice names it (kestrel-c25w; the run-tier sibling of the b4wx * parse-tier manages-nothing refusal). */ export type ArmNoticeCode = "regime-unsatisfiable" | "adoption-bound-nothing"; /** * Runtime tuple of every {@link ArmNoticeCode} — the closed wire vocabulary, guarded * both ways exactly like {@link ARM_REFUSAL_CODES}: adding a code to (or removing one * from) the union without editing this tuple breaks the build. */ export const ARM_NOTICE_CODES = ["regime-unsatisfiable", "adoption-bound-nothing"] as const satisfies readonly ArmNoticeCode[]; /** Reverse-exhaustiveness proof: any `ArmNoticeCode` omitted from {@link ARM_NOTICE_CODES} makes `_Missing`'s default non-`never`, which violates its `never` constraint and fails to compile. */ type _ArmNoticeCodesExhaustive<_Missing extends never = Exclude<ArmNoticeCode, (typeof ARM_NOTICE_CODES)[number]>> = true; /** * One coded arm-time NOTICE (kestrel-hk9u). `statement` is the plan's NAME; `code` is * the wire-stable reason ({@link ArmNoticeCode}); `message` is the human-readable * diagnostic (DISPLAY only — NEVER matched as an interface). A notice never sets the * plan un-armed: it is surfaced ALONGSIDE a successful arm. */ export interface ArmNotice { readonly statement: string; readonly code: ArmNoticeCode; readonly message: string; } /** * The result of arming a document (OSS-ADR-0045): the NAMES of the plans that armed, * the coded {@link ArmRefusal}s for the statements that did not — nothing healthy * is aborted by a bad sibling, and nothing is silently dropped — and the non-fatal * {@link ArmNotice}s for plans that armed but can never FIRE on this tape * (kestrel-hk9u). A refusal that MUST halt the WHOLE document (a live-name collision, * `plan-name-in-use`) is thrown as an `ArmError` carrying its `.code` instead of being * returned here. `notices` is additive and optional — a report without regime notices * omits it, so existing consumers are unaffected. */ export interface ArmReport { readonly armed: readonly string[]; readonly refusals: readonly ArmRefusal[]; readonly notices?: readonly ArmNotice[]; } /* ─────────────────────────── v0.2 additive surface ─────────────────────────── */ /** * The v0.2 additions, re-exported so the published `kestrel.markets/protocol` * entrypoint carries the whole contract. Both modules are intra-protocol and * dependency-free (asserted by tests/protocol.boundary.test.ts): * - ./catalog.ts — the content-addressed {@link import("./catalog.ts").CatalogEntry}. * - ./session.ts — the incremental Session transcript + Operation/Session * identity split + the idempotency/fail-closed reconciler. */ export * from "./catalog.ts"; export * from "./session.ts"; /* ─────────────────────────── v0.3 additive surface (oversight) ─────────────────────────── */ /** * The v0.3 addition: the OVERSIGHT contract — the cross-repo seam between the CLI * (`ascii`/Ink) and Web (`html`) Renderings of the human PM/Pod oversight surface * (ADR-0035). Re-exported so the published `kestrel.markets/protocol` entrypoint * carries the whole contract. Intra-protocol and dependency-free (asserted by * tests/protocol.boundary.test.ts): the acting Kernel is a STRUCTURAL MIRROR of * `src/frame/types.ts`, never an import. PURELY ADDITIVE — 0.2 → 0.3 touches no * existing shape (same rule catalog.ts and session.ts landed under). * - ./oversight.ts — Caller / OversightIdentity, the acting Kernel as a protocol * shape, the Book/Pod org, OversightFrame, the turn-stream, typed owner acts, * the human-message channel, input resolution, the closed OversightEvent enum, * and the OversightBackend seam. */ export * from "./oversight.ts"; /* ─────────────────────────── offer terms (PLAT-ADR-0028 A1) ────────