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
text/typescript
/**
* # 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;
}