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.

288 lines (266 loc) 11.1 kB
/** * # src/oversight/project.ts — the TOTAL Kernel projector (the conformance point) * * `projectKernel(k: Kernel): OversightKernel` is the ONE place the runtime's acting * {@link ../frame/types.ts Kernel} (14 sections, `src/frame/types.ts`) is mapped onto the protocol's * structural mirror {@link ../protocol/oversight.ts OversightKernel}. The protocol imports NOTHING * (it is a dependency-free leaf), so drift between the two shapes can be caught in EXACTLY one * place — here — and it is caught **AT COMPILE TIME**. * * ## The key-witness (the compile-time fence) * {@link SECTION_PROJECTORS} is typed `{ readonly [K in keyof Kernel]: (k: Kernel) => OversightKernel[K] }`. * Its keys are EXACTLY `keyof Kernel`, and each value must return the matching `OversightKernel` * field. Adding a section to `Kernel` therefore breaks the build in two independent ways: * 1. the {@link SECTION_PROJECTORS} literal is now MISSING a key → red until a projector is written; * 2. `OversightKernel[newKey]` does not resolve until the protocol mirror grows the section too. * The paired {@link PROJECTED_KERNEL_SECTIONS} carries the bead's named `Record<keyof Kernel, true>` * roster. A new Kernel section thus becomes a **compile error, never a silent drop** — the exact * failure the golden `OversightFrame` fixture (tests/golden/oversight-frame.golden.json) also reds on * once the section reaches the wire. A projector that merely spread `{...k}` would let a new section * drop silently (red-on-inert); this one cannot. * * ## Total, pure, absent-not-hidden * PURE — no clock, no I/O, no RNG. TOTAL — an absent source section (the cockpit sections are optional * on `Kernel` so the pre-cockpit construction stays valid) projects to an explicit `null`/`[]`, NEVER * a dropped key (contract invariant 6). So the wire shape carries no optionality to be silent with. * * ## Deliberate mirror exclusions (recorded, never silent) * - {@link ../frame/types.ts FillRecord}`.clock` is DROPPED (a wall clock is permitted only on * `SpectatorFrame.asof`; fill order rides the Bus `seq`). * - {@link ../frame/types.ts BudgetEnvelope}`.sizing` (`SizingHeadroom`) is DROPPED (an agent-authoring * aid, not PM oversight state — a PM never authors tickets). * Both are documented at the protocol shapes (`KernelFill` / `RiskEnvelope`) and here. */ import type { Brief, Budget, BudgetEnvelope, Claim, EngineAction, Field, FillRecord, Kernel, Mandate, OwnerAct, PlanStateEntry, Position, RestingOrder, VehicleHealth, WakeRef, } from "../frame/types.ts"; import type { KernelBrief, KernelBudget, KernelClaim, KernelEngineAction, KernelField, KernelFill, KernelLeg, KernelMandate, KernelOwnerAct, KernelPlanState, KernelPosition, KernelRestingOrder, KernelVehicleHealth, KernelWake, OversightKernel, RiskEnvelope, } from "../protocol/oversight.ts"; /* ── Per-shape projections ─────────────────────────────────────────────────── */ /** A DATE-BLIND leg (`symbol` + optional `strike`/`right`). Builds without ever assigning `undefined` * to an optional key (exactOptionalPropertyTypes). Shared by position/resting/fill projection. */ function projectLeg(src: { readonly instrument: string; readonly strike?: number; readonly right?: "C" | "P"; }): KernelLeg { const leg: { symbol: string; strike?: number; right?: "C" | "P" } = { symbol: src.instrument }; if (src.strike !== undefined) leg.strike = src.strike; if (src.right !== undefined) leg.right = src.right; return leg; } /** A {@link Field} → {@link KernelField}: same shape, but built key-by-key so an absent optional is * never materialized as `undefined` (exactOptionalPropertyTypes). */ function projectField(f: Field): KernelField { const out: { value: number; attribution: "OBS" | "CALC" | "MODEL"; source?: string; modelVer?: string; confidence?: number; asOfSeq?: number; } = { value: f.value, attribution: f.attribution }; if (f.source !== undefined) out.source = f.source; if (f.modelVer !== undefined) out.modelVer = f.modelVer; if (f.confidence !== undefined) out.confidence = f.confidence; if (f.asOfSeq !== undefined) out.asOfSeq = f.asOfSeq; return out; } function projectMandate(m: Mandate | null | undefined): KernelMandate | null { if (m === undefined || m === null) return null; return { objective: m.objective, rUsd: m.rUsd, successCriterion: m.successCriterion, riskRule: m.riskRule, }; } function projectBrief(b: Brief | null | undefined): KernelBrief | null { if (b === undefined || b === null) return null; return { text: b.text, hash: b.hash, version: b.version ?? null }; } function projectWake(w: WakeRef | null | undefined): KernelWake | null { if (w === undefined || w === null) return null; return { reason: w.reason, severity: w.severity, deadlineMin: w.deadline }; } function projectVehicleHealth(v: VehicleHealth): KernelVehicleHealth { return { symbol: v.instrument, bidPresentRate: v.bidPresentRate, twoSided: v.twoSided, staleS: v.staleS, dark: v.dark, }; } /** DELIBERATE MIRROR EXCLUSION: {@link BudgetEnvelope}`.sizing` (`SizingHeadroom`) is dropped — an * agent-authoring aid, not PM oversight state (a PM never authors tickets). */ function projectBudgetEnvelope(b: BudgetEnvelope | null | undefined): RiskEnvelope | null { if (b === undefined || b === null) return null; return { remainingR: b.remainingR, planEnvelope: b.planEnvelope, bookEnvelope: b.bookEnvelope, ownerEnvelope: b.ownerEnvelope, }; } function projectOwnerAct(a: OwnerAct): KernelOwnerAct { return { id: a.id, kind: a.kind, asofSeq: a.asofSeq }; } function projectEngineAction(a: EngineAction): KernelEngineAction { return { id: a.id, kind: a.kind, asofSeq: a.asofSeq, reason: a.reason ?? null }; } function projectClaim(c: Claim): KernelClaim { return { field: projectField(c.field), claimType: c.claimType }; } function projectPosition(p: Position): KernelPosition { return { leg: projectLeg(p), qty: p.qty, basis: p.basis, fair: p.fair ?? null, unrealUsd: p.unrealUsd ?? null, plan: p.plan ?? null, claimOwner: p.claimOwner ?? null, structure: p.structure ?? null, }; } function projectResting(o: RestingOrder): KernelRestingOrder { return { ref: o.ref, leg: projectLeg(o), side: o.side, qty: o.qty, px: o.px, live: o.live ?? false, clamped: o.clamped ?? false, note: o.note ?? null, plan: o.plan ?? null, }; } /** DELIBERATE MIRROR EXCLUSION: {@link FillRecord}`.clock` is dropped — a wall clock is permitted only * on `SpectatorFrame.asof`; fill order rides the Bus `seq`, never a clock token on the deterministic * path. */ function projectFill(f: FillRecord): KernelFill { return { leg: projectLeg(f), side: f.side, qty: f.qty, px: f.px, plan: f.plan ?? null }; } function projectBudget(b: Budget | null | undefined): KernelBudget | null { if (b === undefined || b === null) return null; return { used: b.used, remaining: b.remaining, total: b.total ?? null, maxConcurrentR: b.maxConcurrentR ?? null, }; } function projectPlan(p: PlanStateEntry): KernelPlanState { return { name: p.name, state: p.state, outcome: p.outcome ?? null, note: p.note ?? null, blockedReason: p.blockedReason ?? null, }; } /* ── The key-witness + the projector map (the compile-time fence) ──────────────── */ /** * THE KEY-WITNESS. Its type `{ readonly [K in keyof Kernel]-?: (k: Kernel) => OversightKernel[K] }` * makes this object BOTH the projector and the fence (the `-?` strips the optionality Kernel's * cockpit sections carry, so every section demands a projector — not just the required ones): * - keys are EXACTLY `keyof Kernel` — a section added to `Kernel` makes this literal miss a key and * fails the build until a projector is written for it; * - each value's return type is pinned to `OversightKernel[K]` — so the section also cannot be added * to `Kernel` alone: `OversightKernel[K]` fails to resolve until the protocol mirror grows it too. * A new section therefore CANNOT ship unprojected: it is a compile error, never a silent drop. */ const SECTION_PROJECTORS: { readonly [K in keyof Kernel]-?: (k: Kernel) => OversightKernel[K] } = { mandate: (k) => projectMandate(k.mandate), brief: (k) => projectBrief(k.brief), wake: (k) => projectWake(k.wake), dataHealth: (k) => (k.dataHealth ?? []).map(projectVehicleHealth), unavailable: (k) => [...(k.unavailable ?? [])], budgetEnvelope: (k) => projectBudgetEnvelope(k.budgetEnvelope), ownerActs: (k) => (k.ownerActs ?? []).map(projectOwnerAct), engineLog: (k) => (k.engineLog ?? []).map(projectEngineAction), claims: (k) => (k.claims ?? []).map(projectClaim), positions: (k) => k.positions.map(projectPosition), resting: (k) => k.resting.map(projectResting), fillsSinceLast: (k) => k.fillsSinceLast.map(projectFill), budget: (k) => projectBudget(k.budget), plans: (k) => k.plans.map(projectPlan), }; /** * The bead's named `Record<keyof Kernel, true>` view of {@link SECTION_PROJECTORS} — the section * roster, enumerated once. `satisfies Record<keyof Kernel, true>` makes a MISSING or EXTRA section a * compile error here as well: a redundant second fence, and the human-readable list of exactly which * Kernel sections the oversight seam projects. */ export const PROJECTED_KERNEL_SECTIONS = { mandate: true, brief: true, wake: true, dataHealth: true, unavailable: true, budgetEnvelope: true, ownerActs: true, engineLog: true, claims: true, positions: true, resting: true, fillsSinceLast: true, budget: true, plans: true, } as const satisfies Record<keyof Kernel, true>; /** * Project the runtime {@link Kernel} onto the protocol {@link OversightKernel}. TOTAL and PURE: every * section is emitted (absent ⇒ explicit `null`/`[]`, never a dropped key), and there is no clock, no * I/O, no RNG. The `satisfies OversightKernel` on the literal is the third fence — the output is * compile-checked TOTAL against the protocol shape. */ export function projectKernel(k: Kernel): OversightKernel { return { mandate: SECTION_PROJECTORS.mandate(k), brief: SECTION_PROJECTORS.brief(k), wake: SECTION_PROJECTORS.wake(k), dataHealth: SECTION_PROJECTORS.dataHealth(k), unavailable: SECTION_PROJECTORS.unavailable(k), budgetEnvelope: SECTION_PROJECTORS.budgetEnvelope(k), ownerActs: SECTION_PROJECTORS.ownerActs(k), engineLog: SECTION_PROJECTORS.engineLog(k), claims: SECTION_PROJECTORS.claims(k), positions: SECTION_PROJECTORS.positions(k), resting: SECTION_PROJECTORS.resting(k), fillsSinceLast: SECTION_PROJECTORS.fillsSinceLast(k), budget: SECTION_PROJECTORS.budget(k), plans: SECTION_PROJECTORS.plans(k), } satisfies OversightKernel; }