/** * # engine/plans — the plan lifecycle runtime (RUNTIME §5) * * The heart of the system: a **pure, injected-time** {@link PlanEngine} that arms parsed * Kestrel documents, sweeps their triggers over a bus event stream, and drives every plan * through its lifecycle `authored → armed → fired → managing → done(filled|expired|invalidated)` * (RUNTIME §5) with **fire-then-inform** semantics — on a definite trigger it submits children * through the Gate *and* emits a `WAKE` in the same step (ARCHITECTURE §1: slow judgment * compiled into a fast reflex). * * It sits **above** the shipped substrate and owns none of it: * - price resolution is delegated to {@link resolvePrice} (`./pricing.ts`, RUNTIME §4) — the one * place that knows `fair`/`mid`/`peg`/`esc`/`cap`/`floor`; * - triggers are evaluated by the injected {@link TriggerEvaluator} factory over a * {@link SeriesProvider} (`src/series`, tri-state + causal, RUNTIME §3) — one evaluator per * trigger per plan instance so each carries its own edge memory; * - orders go out through the injected {@link Gate} (`submit`/`cancel`) and fills come *back* as * `ORDER fill` bus events observed in {@link PlanEngine.onEvent} — the engine never simulates a * fill itself (the fill engine behind the gate is the judge, RUNTIME §6); * - `pnl` / `fills.*` / `plan(x).*` org facts are served from the engine's {@link BookLedger} * through {@link EngineOrgFacts} (`./orgfacts.ts`). * * All time is injected (RUNTIME §0): every method takes `now` from the event; the engine holds * no clock and no RNG. **Same bus + same armed documents ⇒ byte-identical emitted stream** — the * determinism invariant, certified by comparing {@link PlanEngine.dumpState} across two replays. * * ## Sweep order (one pass per event — never a fixpoint) * Each trigger is evaluated **exactly once per event** so its causal edge memory (crosses, * held, within, nth) advances correctly — re-evaluating a `crosses` twice in one tick would * consume the transition. Consequence: plan **chaining** (`plan(a).fired` gating plan `b`) * resolves on the **next** event, not the same one (the fired latch is on the ledger, read on * the following sweep — RUNTIME §5 "plan-state triggers read the bus lifecycle"). * * The per-event pass: * 1. **ingest** — fold TICK into spot/book, REGIME into the tag store, correlate `ORDER` * fill/cancel/reject into inventory + the ledger. * 2. **reconcile arming** — an armed plan whose regime gate goes UNKNOWN de-arms; an authored * plan whose gate is (re)satisfied arms; a past-TTL armed plan expires (RUNTIME §3/§5). * 3. **fire** — collect armed plans whose `WHEN` is definitely true, admit by envelope + * priority arbitration (higher wins, ties → earlier armed), fire the admitted (submit + * inform). * 4. **manage** — for fired plans: line/plan `CANCEL-IF`, `INVALIDATE`, `EXIT` (+ esc/STAND), * `RELOAD` rungs, `TP` maintenance, `TTL`, and peg/esc reprice of working orders. */ import type { ArmClause, CancelIfClause, ExitClause, ExpirySelector, InvalidateClause, Leg, Module, OrderPolicy, PlanStatement, PriceExpr, ReloadClause, RiskLine, StrikeSpec, TpClause, Trigger, } from "../lang/index.ts"; import type { NewBusEvent, BusEvent, OptionQuote, PlanOutcome, PlanState, Right } from "../bus/index.ts"; import { foldBook, type BookState } from "../bus/index.ts"; import { intrinsic, buildSurface, impliedForward, interpIv } from "../fair/index.ts"; import { greeks } from "../fair/black76.ts"; import type { ExecutionFairInput } from "../fair/index.ts"; /** The τ provider `@fair` is built through — `(now, expiry) => years | null`, `null` = unknowable * (fail closed, kestrel-wcnd). Structurally identical to `src/session/clock.ts`'s `FairTauProvider` * and REDECLARED here on purpose: `src/session` imports `src/engine`, so importing the type back * would invert the layering (the same leaf-purity reason `src/bus` redeclares `FillSupport`). The * one shipped implementation is `expiryTauYears`; the CLI's `--tau-hours` constant is the other. */ type FairTauProvider = (now: number, expiry?: string) => number | null; import { guardIntent, type GuardIntentInput, type Moneyness } from "../fill/index.ts"; import { durationMs, isUnknown, UNKNOWN, TriggerEvaluator, defaultSeriesRegistry, type SeriesProvider, type SeriesRegistry, type Resolved, type OrgFacts, type TriState, type Unknown, } from "../series/index.ts"; import type { Baseline, FillEvent, SeriesRef, StructuralEvent } from "../lang/index.ts"; import { assertNever } from "../lang/index.ts"; import { unknownSeriesRejection } from "../validate/index.ts"; import type { SessionPhase } from "../bus/index.ts"; import { isUnresolvable, resolvePrice, type PriceCtx, type PriceLine, type RepriceTokenBucket } from "./pricing.ts"; import { BookLedger, EngineOrgFacts } from "./orgfacts.ts"; import { perLegFillQualifierReason } from "./disarm.ts"; import { sha256 } from "../crypto/sha256.ts"; import type { ArmRefusalCode, ArmRefusal, ArmReport, ArmNotice } from "../protocol/index.ts"; /** * A whole-document arm refusal that MUST halt arming (OSS-ADR-0045). It carries a * wire-stable `.code` from {@link ArmRefusalCode} so a consumer routes on the code, * NEVER on the message string. The one arm throw today is `plan-name-in-use` (the F4 * live-name collision): arming would clobber the by-name ledger, so — unlike the * per-statement `unknown-series` refusal, which is returned as DATA — it fails the * whole document. `refusals`, when present, carries the underlying coded refusals a * driver aggregated into this throw (so nothing is silently dropped at the boundary). */ export class ArmError extends Error { readonly code: ArmRefusalCode; readonly refusals?: readonly ArmRefusal[]; constructor(code: ArmRefusalCode, message: string, refusals?: readonly ArmRefusal[]) { super(message); this.name = "ArmError"; this.code = code; if (refusals !== undefined) this.refusals = refusals; } } /** * The stable lead marker of the `adoption-bound-nothing` notice (kestrel-c25w) — the byte prefix of the * message {@link PlanEngine.armDocument} emits as a PLAN-lifecycle reason (and mirrors onto * {@link ArmReport.notices} / `validateArm.warnings`) when a valid `ARM … foreach held leg` adopter binds * ZERO held legs. A consumer that has only the projected lifecycle reason (the day report carries no * separate code on a lifecycle step) matches on THIS constant instead of hardcoding the prose, so the * default `day` terminal can surface the notice on the plan's own line (kestrel-gjx5 residual). The * message begins with this exact string; do not reword it without updating the constant. */ export const ARM_BOUND_NOTHING_MARKER = "ARM bound nothing"; /** * Aggregate one-or-more per-statement {@link ArmRefusal}s into a single throwable {@link ArmError} * for a session-driver boundary that refuses the WHOLE document (sim/paper): the message enumerates * EVERY refusal (nothing silently dropped) and the error carries the structured `refusals` + the * first refusal's `code` so a consumer routes on the code, never on the message. Callers pass a * non-empty list. */ export function armRefusalError(refusals: readonly ArmRefusal[]): ArmError { const code: ArmRefusalCode = refusals[0]?.code ?? "unknown-series"; const message = `arm: ${refusals.length} statement(s) refused — ` + refusals.map((r) => `[${r.code}] ${r.message}`).join(" | "); return new ArmError(code, message, refusals); } // ───────────────────────────────────────────────────────────────────────────── // Public seams // ───────────────────────────────────────────────────────────────────────────── /** Which clause authored an order — carried onto the intent for grading/lineage. `adopted` is * NOT a gate order: it is the synthetic already-filled long child the cross-Plan inventory * handshake attaches to an explicitly ARM-bound replacement (kestrel-22j.14) so its covered sell * reads as covered — bookkeeping only, never submitted to the Gate. */ export type OrderRole = "entry" | "reload" | "tp" | "exit" | "adopted"; /** A concrete, resolved order the engine hands the {@link Gate}. A single defined-risk option * leg — or a spot/equity leg (ADR-0017) — at a resting price, with its price-resolution * `sourceAnnotation` (a silent mid is forbidden, RUNTIME §4). `ref` is the engine's * deterministic id; the gate returns the ref the engine correlates later `ORDER` fills against. * `strike`/`right` are BOTH present for an option leg and BOTH absent for a spot leg — a spot * instrument has neither, and a fictional strike is never written (ADR-0017); the gate routes * on their absence. */ export interface OrderIntent { readonly ref: string; /** The authoring plan's NAME (Lineage key). */ readonly plan: string; /** The authoring plan's exact INSTANCE identity (kestrel-22j.15) — the id minted at registration, carried * onto the order's bus evidence so an order joins its exact instance, not merely its recurring name. */ readonly plan_instance: string; readonly role: OrderRole; readonly instrument: string; readonly side: "buy" | "sell"; readonly qty: number; /** The option strike; ABSENT for a spot/equity leg (ADR-0017). */ readonly strike?: number; /** The option right; ABSENT for a spot/equity leg (ADR-0017). */ readonly right?: Right; readonly px: number; readonly sourceAnnotation: string; /** Coarse moneyness vs the decision-time underlier (strike vs spot), derived at submission and * threaded into the FillOrder so the directional far-OTM maker-fair guard runs end to end * (kestrel-9gu.6). Absent ⇒ spot was unresolvable for a BUY (the symmetric path; a BUY carries no * bounded-risk leak). A SELL never carries an absent moneyness — an unresolvable one fails closed * to the conservative far-OTM cap (see {@link deriveOrderGuard}). */ readonly moneyness?: Moneyness; /** Covered-state threaded into the FillOrder: `true` iff a covering wing of a defined-risk package * (exempt from the SELL far-OTM cap). The engine authors only single-leg closes of a held long, so * every SELL is a STANDALONE offer (`covered: false`) — the conservative default. Absent for a BUY. */ readonly covered?: boolean; } /** The moneyness + covered-state the engine derives for a submitted leg (kestrel-9gu.6) — the * directional-guard evidence threaded onto the {@link OrderIntent}. `unresolved` is `true` only on a * SELL whose spot was unclassifiable, where the conservative far-OTM cap was applied fail-closed (so * the caller LOGS the fallback rather than letting the sell slip through the guard uncapped). */ export interface OrderGuardEvidence { readonly moneyness?: Moneyness; readonly covered?: boolean; readonly unresolved: boolean; } /** * Derive the directional far-OTM guard evidence for a leg at submission — the engine-side * {@link OrderGuardEvidence} projection of the {@link import("../fill/index.ts").GuardedIntent} * verdict authored by the ONE owner (`guardIntent`, fill/guarded-intent, kestrel-vav2). The * classification, decision-time spot resolution, and the fail-closed fork all live in that owner — * this function is the thin adapter that strips the intent's `side` for the engine's evidence shape, * so the far-OTM standalone SELL cap, the covered-wing exemption, and the BUY crossing unlock all run * END TO END through the fill model. Pure (no clock/RNG). * * - **BUY** — the guard only ever UNLOCKS a far-OTM bid (crossing-print), never a bounded-risk leak, * so an unresolvable spot is safe: classify when we can, else take the symmetric path (no moneyness). * `covered` is not meaningful for a buy. * - **SELL** — the engine's never-naked boundary authors only single-leg closes of a held long, which * are STANDALONE offers in the fill-model sense (`covered: false`) — a genuine multi-leg * combo-anchored short is not authored here (a FORK; if one is ever authored it must set * `covered: true` explicitly). If spot is unresolvable we cannot classify the wing, so we **fail * closed** to the conservative far-OTM cap (`deep_otm`, standalone) and flag it — NEVER a silent * default and never a silent bypass (BOUNDED-RISK / never-naked, RUNTIME §8). */ export function deriveOrderGuard( side: "buy" | "sell", spot: number | undefined, strike: number, right: Right, ): OrderGuardEvidence { const g = guardIntent({ side, strike, right, resolveSpot: () => spot }); return { ...(g.moneyness !== undefined ? { moneyness: g.moneyness } : {}), ...(g.covered !== undefined ? { covered: g.covered } : {}), unresolved: g.unresolved, }; } /** The execution seam (RUNTIME §7): `submit` rests an order (returning the ref to correlate * fills against) and `cancel` pulls one. In `sim` the gate wraps the fill engine; in `live` it * wraps the broker adapter — one engine path, different gate. */ export interface Gate { submit(intent: OrderIntent): string; cancel(ref: string): void; } /** Where the engine writes its own lifecycle output (PLAN transitions + WAKE informs). The * real {@link import("../bus/write.ts").BusWriter} adapts to this; a recording fake backs the * tests. The engine never emits `ORDER` events — those come from the gate/fill engine. */ export interface BusSink { emit(ev: NewBusEvent): void; } /** The per-instrument facts the engine needs to turn a leg into a concrete order: tick size * (snap + peg hysteresis), strike grid step (relative/ATM strikes), contract multiplier * (premium/P&L in dollars), and the plan-side role. */ export interface InstrumentSpec { readonly symbol: string; readonly tickSize: number; readonly strikeStep: number; readonly multiplier: number; readonly role?: "signal" | "exec"; } /** Everything a {@link PlanEngine} is constructed with. All time is injected (`now` seeds the * clock; every subsequent time comes from the swept event). */ export interface PlanEngineOptions { readonly instruments: readonly InstrumentSpec[]; /** The market-fact provider (`src/series`). Org facts (`pnl`, `plan(x).state`, …) are served * by the engine's own {@link BookLedger}; this provider supplies `spot`/`vwap`/windows/phase * and the optional detector/fill/calendar hooks. */ readonly seriesProvider: SeriesProvider; /** The shared series phonebook (cza.1) the engine's market-vs-org routing dials — the same table * the injected {@link seriesProvider} routes through, so their org/market split cannot skew. The * market side is platform-declared and static, so the process-wide {@link defaultSeriesRegistry} * is the deterministic default; inject an isolated one only to observe per-session org registrations. */ readonly registry?: SeriesRegistry; /** A fresh {@link TriggerEvaluator} per trigger per plan (each owns its causal edge memory). * Defaults to `() => new TriggerEvaluator()`. */ readonly newEvaluator?: () => TriggerEvaluator; readonly gate: Gate; readonly busWriter: BusSink; /** The global reprice token bucket for `peg` maintenance (RUNTIME §4). Absent ⇒ unlimited. */ readonly repriceBucket?: RepriceTokenBucket; /** Dollar value of one risk unit `R` — the budget clamp scale (`budget_r × rUsd`). */ readonly rUsd: number; /** The initial injected time (epoch ms). */ readonly now: number; /** Optional injected time-to-expiry (years) for building `@fair` (RUNTIME §4) — resolved against * the valued CONTRACT's own expiry (kestrel-wcnd), which {@link PlanEngine} threads from the book * it is pricing off. Absent (or `null` for an unresolvable expiry) ⇒ `@fair` resolves through its * annotated book fallback, which ALWAYS names itself (never a silent mid). */ readonly fairTauYears?: FairTauProvider; /** The COMPLETE set of regime scopes this tape will ever write (kestrel-ocyf) — available only when * the caller holds the whole bus up front (sim / replay / day; `runSimSession` pre-scans REGIME * events). When present, {@link PlanEngine.armDocument} can tell a PROVABLY-dead regime gate (scope * not in this set ⇒ it can never arm on this tape) from one legitimately waiting for a later REGIME * write. Absent (live / stepped-unknown) ⇒ the arm notice stays conditional, never definitive. */ readonly tapeRegimeScopes?: readonly string[]; } // ───────────────────────────────────────────────────────────────────────────── // Internal runtime // ───────────────────────────────────────────────────────────────────────────── /** A nesting risk envelope (a Book, or a Pod with an `R` risk-budget). A fire must fit every * ancestor (RUNTIME §5). `firedCount` (concurrency) aggregates the subtree via increments up the * parent chain; the DOLLAR reservation is derived on demand from the subtree's OPEN exposure * ({@link PlanEngine.#envelopeExposureUsd}, bd 1a8) — a round-trip to flat frees it. */ interface Envelope { readonly name: string; /** kestrel-eywk: MUTABLE on the engine-minted `default` envelope ONLY — the standing authored document * RESTATES its own bound on every authored arm (a supersede that raises `budget` raises the envelope; a * synthesized one-shot inherits and can never widen it). Author-minted POD/BOOK envelopes are never * restated: their bound is the one their author wrote. */ totalBudgetUsd: number | null; readonly maxConcurrent: number | null; readonly parent: Envelope | null; /** Concurrency count — the number of open (fired, not-done) positions in the subtree. Incremental * (bumped on fire/reload, dropped on finish). The DOLLAR reservation is NOT stored here — it is * derived on demand by {@link PlanEngine} from the subtree's open exposure (bd 1a8). */ firedCount: number; } /** A HELD leg's identity (kestrel-h5nx): the `(strike, right)` never-naked coverage key plus the * instrument it trades. A spot/equity leg carries `undefined` for both (ADR-0017 — no fictional * strike), so spot legs net against each other and never against an option leg. Every multi-leg sell * surface (TP, EXIT) allocates across these and names each sell for the leg that covers it. */ interface HeldLeg { readonly strike: number | undefined; readonly right: Right | undefined; readonly instrument: string; } /** The resolved, priced form of one leg — what {@link PlanEngine.resolveLeg} hands back when the leg's * strike and price line both resolve against the current book (`null` when they do not: fail-closed). */ interface BuiltLeg { readonly instrument: string; readonly strike?: number; readonly right?: Right; readonly qty: number; readonly px: number; readonly ann: string; readonly multiplier: number; readonly priceLine?: PriceLine; } interface ChildOrder { ref: string; readonly role: OrderRole; readonly side: "buy" | "sell"; readonly qty: number; readonly instrument: string; /** The option strike; ABSENT for a spot/equity child (ADR-0017 — no fictional strike). All * leg-keyed netting compares `strike`+`right` directly, so spot children (both `undefined`) * net against each other and never against an option leg. */ readonly strike?: number; /** The option right; ABSENT for a spot/equity child (ADR-0017). */ readonly right?: Right; px: number; ann: string; filled: boolean; /** Cumulative filled contracts against this child (supports partial fills, F/Bug-2). `filled` * latches true once `filledQty >= qty`; the target-qty cap sizes reprice remainders off this. */ filledQty: number; /** Cumulative filled COST of this child (`Σ thisFill × fillPx`) — the per-LEG basis source * (kestrel-m9i.41). The plan-total {@link PlanRuntime.buyCostSum} blends every leg together, which * is meaningless the moment a plan holds MORE THAN ONE leg (a straddle's call and put have * different premiums, so a plan-total `TP +50%` would target 1.5× the *blended* basis on both). * Summing this over a leg's filled BUY children yields that leg's own average entry premium. */ filledCost: number; cancelled: boolean; readonly placedAt: number; escStage: number; readonly priceLine?: PriceLine; readonly lineCancelIf?: Trigger; readonly tpTier?: number; /** Management authority for this filled leg has been HANDED OFF to an explicitly ARM-bound * replacement (the cross-Plan inventory handshake, kestrel-22j.14). A transferred child is * excluded from this plan's {@link PlanEngine.netHeldForLeg} / {@link PlanEngine.restingSellForLeg} * so the source can no longer rest a sell against inventory it no longer manages — the moved claim * lives as a fresh `adopted` long on the replacement. The Book {@link BookLedger} fill log is never * touched (quantity conserved: only the per-plan management claim moved, never the trade record). */ transferred?: boolean; } interface EntrySpec { readonly legs: readonly Leg[]; readonly price: PriceExpr; readonly policy?: OrderPolicy; } interface PlanRuntime { readonly plan: PlanStatement; readonly name: string; /** The exact Plan-instance identity (kestrel-22j.15), minted DETERMINISTICALLY at registration and * carried onto every PLAN/WAKE transition + authored ORDER this instance emits, so a legal same-name * replacement never collapses into this instance's lifecycle trace at report projection. */ readonly instanceId: string; readonly envelope: Envelope; state: PlanState; fired: boolean; done: boolean; armedAtSeq: number | null; armedAtTs: number | null; filledBuyQty: number; filledSellQty: number; buyCostSum: number; basisPx: number | null; /** The injected event time of this plan's FIRST acquiring (buy) fill — the "held since" anchor a * 0DTE `EXIT held ` time-held stop measures from (docs/results/fomc-options-axis). Null until * the position is first acquired; set once (first-write-wins) and never cleared, so a momentarily- * flat-then-re-acquired plan keeps its ORIGINAL entry clock. Pure (from the injected `now`, no wall * clock/RNG, RUNTIME §0), so two replays agree byte-for-byte. */ firstAcquiredAt: number | null; reloadsHalted: boolean; acquisitionHalted: boolean; /** The agent has FLATTENED this plan's position (bd if0 / 75n): acquisition is halted, its resting * orders for the instrument were cancelled, and a covered close crosses to settle. The latch stops * {@link PlanEngine.#maintainTP} from re-posting the TP (the re-post that defeated a bare cancel) and * suppresses the authored EXIT loop, so the flatten actually neutralizes management — the plan finishes * `done(expired, "flattened")` the instant it goes flat with nothing working. Internal-only (never in * dumpState): the visible signal is the graded-bus `flatten` WAKE + the `flattened` done reason. */ flattened: boolean; /** This plan took on ADOPTED inventory via the ARM inventory-binding handshake (kestrel-22j.14 / * kestrel-r866). Set the instant {@link PlanEngine.#transferLeg} attaches an adopted long. A PURE * adopter (`entries.length === 0` — an `ARM … foreach held leg` binding with no fireable `DO`) never * reaches the fire path, so it would sit `armed` forever with its EXIT/TP inert (the tracer-7 "escape * hatch is INERT on the day handshake" defect). This flag drives {@link PlanEngine.#beginAdoptedManagement} * to promote such a plan armed→managing so its authored management clauses actually run. Internal-only. */ adoptedInventory: boolean; exitStood: boolean; reloadRungs: number; /** The org-path de-arm reason currently latched on this plan (an armed WHEN naming a genuinely * unresolvable org path). Non-null ⇒ the loud de-arm WAKE has already been informed for this * episode; cleared the instant the WHEN resolves again, so the notice fires once per edge, not * every sweep (RUNTIME §8). */ orgDeArmReason: string | null; /** The fire-time bounded-risk BUDGET refuse currently latched on this plan: an armed plan whose * WHEN is true and whose order builds, but whose premium won't fit an ancestor risk-envelope's * budget, so it is refused at admission and stays armed. Non-null ⇒ the loud refuse has already * been emitted for this episode; cleared the instant the plan fits (or fires), so the notice fires * ONCE per edge, not every sweep — otherwise an authored plan whose WHEN stays true would storm the * stream. Mirrors {@link orgDeArmReason}'s edge-latch idiom (kestrel-m9i.32.1: the refuse was * previously SILENT — lifecycle stayed armed, orders=[], no journal — invisible to the agent). */ envelopeRefuseReason: string | null; /** The ARM-TIME gate-block reason currently latched on this plan (kestrel-50w): a plan whose regime * gate is UNSATISFIABLE — the gated tag is UNKNOWN (no feed) or holds a different value — so it can * never leave `authored` to arm. Non-null ⇒ the frame renders `authored (blocked: )` so the * bare-`authored` state (a live plan awaiting its WHEN) is DISTINGUISHABLE from an authored plan stuck * on an unsatisfiable gate (the phantom-position trap: an agent read `authored` as armed-and-live and * traded a position that never existed). PURE — derived from {@link #regimeSatisfied} over the injected * tag store, no wall clock/RNG; cleared the instant the gate is satisfied and the plan arms. This is the * ARM-TIME counterpart to {@link envelopeRefuseReason}'s FIRE-TIME budget refuse (kestrel-m9i.32.1): * both surface a reason the engine already knows but the frame previously hid ("engine knows, frame * hides"). It rides dumpState (the frame's projection source) only — NEVER the graded bus. */ armBlockReason: string | null; /** Has this plan's entry WHEN ever evaluated to a DEFINITE verdict (`true`/`false`, i.e. NOT UNKNOWN) * since it armed? (kestrel-y6vo). An armed plan whose WHEN references a market series the tape never * writes a value for — `velocity(5m)`, a percentile baseline, … — reads UNKNOWN every sweep (fail- * closed), so the trigger silently never confirms and the plan expires at TTL having placed zero * orders with NO reason surfaced (the ungated hero-example silent-0, kestrel-hk9u regression). This * latch lets {@link PlanEngine.#ttlExpiryReason} tell that DOA class ("trigger never RESOLVED — an * operand series never wrote a value") apart from a healthy untriggered plan ("trigger never * CROSSED", `whenEverDefinite === true`) at the ttl-expiry drop site. Set the instant the WHEN * resolves definite; never cleared (a once-resolved trigger is not the unwritten-series class). PURE — * derived from the injected bus (the same evaluate a replay re-runs), no wall clock/RNG (RUNTIME §0), * so it reconstructs identically on replay and need not ride dumpState. */ whenEverDefinite: boolean; /** Non-null once an AUTHORED plan's fire was fully clamped by its own plan budget — every entry leg is * larger than the budget, so {@link PlanEngine.#resolveEntries} admits nothing and the plan stays armed * with `orders=[]` (kestrel-kglw / kestrel-markets-a4ya). Before this latch the computed * `order refused: exceeds plan budget` reason was DROPPED for authored plans (only a SYNTHESIZED * one-shot surfaced it), so a budget-DOA plan was the exact silent never-fired class. The latch (a) * edge-gates the loud PLAN reject so it emits ONCE, not once per sweep, and (b) lets * {@link PlanEngine.#neverFiredExpiryReason} name the budget cause at the ttl-expiry drop site instead * of a bare `ttl`. Cleared the instant a fire admits a leg (the clamp lifted). PURE — derived from the * injected `rUsd` × budget and the resolved leg premium, no wall clock/RNG (RUNTIME §0). */ budgetRefusedReason: string | null; /** Non-null once a `Nd` DELTA-targeted leg (`buy 1 16d P`, kestrel-d6nk) fired but its band could NOT * be resolved to a concrete quotable strike — a degenerate/empty vol surface near the 0DTE close, an * absent book, or no strike quoted for the leg's right. Delta selection is UPSTREAM of the fill: once * the band resolves it becomes a NORMAL single-leg order against a real book quote (identical to * atm/+N), so a resolvable band fires and this stays null. UNRESOLVABLE is the surface.ts-documented * empty-surface case — and it must be LOUD, never the pre-d6nk silent parse+arm+meter+0-fill (the * one strike language options traders actually use, billing but never filling). Mirrors * {@link budgetRefusedReason}'s edge-latch: (a) the loud PLAN reject emits ONCE per distinct reason, * not per sweep, and (b) {@link PlanEngine.#neverFiredExpiryReason} / the never-fired digest name the * delta cause at the drop site instead of a bare `ttl`. Cleared the instant a delta leg resolves to a * strike. PURE — buildSurface+greeks are pure and the tie-break is pinned (lower strike), no wall * clock/RNG (RUNTIME §0), so it reconstructs identically on replay. */ deltaRefusedReason: string | null; /** This plan was SYNTHESIZED from an agent `placeOrder` (kestrel-5zl.5), not authored in a document. * Two behaviors follow (both scoped to this surface — authored plans are untouched, preserving the * plan-scoped never-naked decision of kestrel-22j.14): (1) a SELL leg covers against the whole BOOK's * net-held-minus-resting-sells (the doctrine formula, SURFACES "never-naked SELL boundary"), because * a one-shot placeOrder has no authored plan structure to scope to and no ARM-held handshake to invoke; * (2) an admit-nothing fire is TERMINAL (de-arm loudly ONCE), never a per-sweep refusal storm — a * WHEN-less one-shot has no future sweep that will make an uncovered/unbuildable leg fire. */ readonly synthesizedOrder: boolean; readonly children: ChildOrder[]; // clause partitions readonly entries: readonly EntrySpec[]; readonly reloads: readonly ReloadClause[]; readonly tps: readonly TpClause[]; readonly exits: readonly ExitClause[]; readonly invalidates: readonly InvalidateClause[]; readonly cancelifs: readonly CancelIfClause[]; readonly armClause: ArmClause | null; // per-clause causal memory whenEval: TriggerEvaluator | null; readonly reloadEvals: (TriggerEvaluator | null)[]; readonly reloadPrevTrue: boolean[]; readonly exitEvals: (TriggerEvaluator | null)[]; /** The exit trigger has latched (edge) — the exit INTENT is armed and stays armed until the * held qty is fully worked (F1: protect later-acquired inventory even while momentarily flat). */ readonly exitIntent: boolean[]; /** The exit sell has been submitted for the held qty ⇒ the intent is satisfied (esc staging of * that child then proceeds via {@link PlanEngine.onEvent} → maintainWorking). */ readonly exitWorked: boolean[]; readonly invalidateEvals: (TriggerEvaluator | null)[]; readonly cancelifEvals: (TriggerEvaluator | null)[]; } const EPS = 1e-9; const DEFAULT_SPEC: Omit = { tickSize: 0.01, strikeStep: 1, multiplier: 1 }; /** The bare `spot` market-fact ref — resolved through the engine's composed provider to feed the * `spot` PRICE anchor (ADR-0030 / kestrel-ipc) from EXACTLY the value the `spot` SERIES reads * (`CanonicalState.spot`), so the two readings of `spot` can never diverge (no two-truths). */ const SPOT_SERIES_REF: SeriesRef = { kind: "series", segments: [{ name: "spot" }] }; /** * A {@link SeriesProvider} that routes **org** paths to the engine's ledger-backed * {@link OrgFacts} and everything else (market scalars, windowed metrics, baselines, phase, the * detector/fill/calendar hooks) to the injected market provider. This lets the engine own the * org side (so `plan(x).state`, `pnl`, `fills.*` are always current as of the swept event) * without the caller having to wire the engine's org facts into the provider before the engine * exists (a construction-order cycle). */ class ComposedProvider implements SeriesProvider { constructor( private readonly market: SeriesProvider, private readonly org: OrgFacts, /** The ONE shared phonebook (cza.1) the market-vs-org decision dials — the same table * `series/provider` routes through. Routing through it (instead of an engine-private mirror of * the market-name set) means the engine's org/market split can never skew from the provider's. */ private readonly registry: SeriesRegistry, /** Stash a fail-closed reason for the sweep in progress — the SAME latch * {@link EngineOrgFacts} feeds for an unresolvable org path, so an UNKNOWN raised HERE also * turns a silent non-fire into a loud, edge-latched de-arm (RUNTIME §3/§8). Optional so a * bare ComposedProvider (tests) still constructs. */ private readonly onUnresolved: (reason: string) => void = () => {}, ) {} private isMarket(ref: SeriesRef): boolean { if (ref.window !== undefined) return true; // windowed ⇒ a market metric (windowed org not v1) const head = ref.segments[0]; if (ref.segments.length !== 1 || head === undefined || head.selector !== undefined) return false; // Single source of truth: a bare name is a market fact iff the phonebook says so (cza.1) — no // second market-name list to drift from `series/provider`. return this.registry.lookup(head.name)?.kind === "market"; } resolve(ref: SeriesRef, now: number): Resolved { return this.isMarket(ref) ? this.market.resolve(ref, now) : this.org.resolve(ref.segments); } /** A quantified operand (`children(any|all).`) is always an org path (a leading `sel-quant` * selector ⇒ not a bare market name) — resolve it to the per-member vector through the org * backing so the trigger folds the comparator per member (∃/∀). UNKNOWN (fail-closed) if the org * backing does not implement quantification. */ quantify(ref: SeriesRef, _now: number): readonly number[] | Unknown { return this.org.quantify?.(ref.segments) ?? UNKNOWN; } baseline(ref: SeriesRef, stat: Baseline): number | Unknown { return this.market.baseline(ref, stat); } phase(): SessionPhase | Unknown { return this.market.phase(); } structural(ev: StructuralEvent, now: number): TriState { return this.market.structural ? this.market.structural(ev, now) : UNKNOWN; } /** * Fill-lifecycle telemetry (`WHEN filled`, `rejected`, `cancelled`). * * A LEG QUALIFIER (`filled leg 1`) is REFUSED here, before the provider hook is consulted * (kestrel-s3h8). Every fill-telemetry provider that ships today — the sim's session-scoped * `FillTelemetry`, the IBKR feed's — answers from a session-wide latch with no per-plan / per-leg * scoping, so passing the qualifier through would silently WIDEN the author's intent: a plan armed * on "my own leg 1 filled" would fire on a STRANGER's fill elsewhere in the session (cross-plan * bleed). That is the silent default doctrine forbids, and SURFACES.md already states these read * UNKNOWN. So: UNKNOWN **with a logged reason**, which de-arms the plan loudly instead of leaving * it armed on a trigger that means something other than what it says. The real capability * (per-plan/per-leg scoping) lands with kestrel-qbwo.2; when a provider can honour the qualifier * it is admitted here, not by deleting the refusal. */ fillEvent(ev: FillEvent): TriState { if (ev.leg !== undefined) { this.onUnresolved(perLegFillQualifierReason(ev.event, ev.leg)); return UNKNOWN; } return this.market.fillEvent ? this.market.fillEvent(ev) : UNKNOWN; } timeOfDayMinutes(now: number): number | Unknown { return this.market.timeOfDayMinutes ? this.market.timeOfDayMinutes(now) : UNKNOWN; } } /** A short, human display name for a {@link SeriesRef} operand — the dotted segment path, with a `(…)` * window suffix when the ref is windowed (`velocity` + a 5-minute window ⇒ `velocity(5m)`). Used to NAME * the unwritten operands in a `trigger never resolved` ttl-expiry reason (kestrel-y6vo). Deliberately a * compact label (not the full canonical printer): it names WHICH series the tape never wrote. */ function seriesDisplayName(ref: SeriesRef): string { const path = ref.segments.map((s) => s.name).join("."); const w = ref.window; return w === undefined ? path : `${path}(${w.value}${w.unit})`; } /** Every bare {@link SeriesRef} operand a trigger references, collected recursively — the arm-time * input to the unknown-series integrity check (kestrel-mte). Mirrors {@link findMarkInTrigger}'s * structural walk: only `cmp`/`cross`/`event` carry operands; the compound/decorator nodes recurse * into their sub-triggers; `phase`/`time-window`/`fill` reference no named series. */ function collectSeriesOperands(t: Trigger, out: SeriesRef[]): void { switch (t.kind) { case "cmp": case "cross": if (t.left.kind === "series") out.push(t.left); if (t.right.kind === "series") out.push(t.right); return; case "event": { const of = t.of; if (of?.kind === "series") out.push(of); return; } case "break-hold": case "within": case "until": case "at": collectSeriesOperands(t.inner, out); return; case "nth": collectSeriesOperands(t.event, out); return; case "not": collectSeriesOperands(t.term, out); return; case "and": case "or": for (const term of t.terms) collectSeriesOperands(term, out); return; case "phase": case "time-window": case "fill": // A 0DTE time-stop references the clock / hold-duration, never a named series — no operand to collect. case "held-stop": case "clock-stop": return; default: return assertNever(t, "collectSeriesOperands"); } } // ───────────────────────────────────────────────────────────────────────────── // PlanEngine // ───────────────────────────────────────────────────────────────────────────── /** The execution instrument as the AUTHOR bound it: a `(symbol, expiry)` pair (kestrel-ih5h seam 1). * The expiry is the SELECTOR the author wrote (`0dte`, `2026-07-17`, `weekly`) — not a resolved date; * resolving a relative selector needs a session calendar, which this module deliberately does not * have (no ambient clock is ever consulted here). */ interface ExecRef { readonly symbol: string; readonly expiry?: ExpirySelector; } /** Normalize a date selector's RAW digit text for comparison — `2026-1-5` and `2026-01-05` name the * same day and must not read as a contradiction. The raw text is preserved in the AST (the printer's * round-trip depends on it), so the padding is applied HERE, at the comparison, and nowhere else. */ function normalizeExpiryDate(d: string): string { const parts = d.split("-"); if (parts.length !== 3) return d; return `${parts[0]!.padStart(4, "0")}-${parts[1]!.padStart(2, "0")}-${parts[2]!.padStart(2, "0")}`; } /** Does a book whose tenor is `have` satisfy an authored `want` selector? A pure predicate — no clock, * no calendar, no ambient date (kestrel-ih5h seam 1). * * Three cases, each fail-closed in the sense that matters — the function never GUESSES: * - `want === undefined` — the author named no tenor, so nothing can contradict it. Admit. * - `have === undefined` — the book does not state its tenor, so it cannot contradict the author * either. Admit: inventing a tenor for it would be exactly the silent default this forbids. * - `want` is an absolute DATE — comparable to the book's date with no calendar at all. This is the * one case that can be DECIDED, so it is the one case that is enforced. * * A RELATIVE selector (`0dte`) or a TAG (`weekly`) cannot be decided here: `0dte` means "expiring on * the session's date", and the session's date lives behind the clock seam this module must not reach * across. Admitting them preserves today's behavior exactly (every single-tenor plan we grade prices * as it always has) and leaves the resolution to the half of this seam that owns the calendar. That * residue is real and is named in the handoff — it is not a claim that relative tenors are checked. */ function expiryAdmits(want: ExpirySelector | undefined, have: string | undefined): boolean { if (want === undefined || have === undefined) return true; if (want.kind === "expiry-date") return normalizeExpiryDate(want.date) === normalizeExpiryDate(have); return true; } export class PlanEngine { readonly #instruments = new Map(); readonly #execDefault: string | undefined; readonly #signalDefault: string | undefined; readonly #provider: SeriesProvider; readonly #newEvaluator: () => TriggerEvaluator; readonly #gate: Gate; readonly #bus: BusSink; readonly #bucket: RepriceTokenBucket | undefined; readonly #rUsd: number; readonly #fairTau: FairTauProvider | undefined; /** The tape's complete regime-scope vocabulary when the caller pre-scanned the full bus * ({@link PlanEngineOptions.tapeRegimeScopes}), else undefined (live: the future is unknown). */ readonly #tapeRegimeScopes: ReadonlySet | undefined; readonly #ledger: BookLedger; readonly #registry: SeriesRegistry; readonly #orgFacts: EngineOrgFacts; /** The path-naming de-arm reason stashed by {@link EngineOrgFacts} the last time a WHEN sweep hit * a structurally-unresolvable org path — read (and cleared) around each plan's WHEN evaluation in * {@link #fire} to turn a SILENT non-fire into a de-arm with a logged reason. First-write-wins per * sweep, so the reason is deterministic. */ #lastOrgUnresolved: string | null = null; readonly #plans: PlanRuntime[] = []; readonly #childIndex = new Map(); readonly #childEvals = new Map(); readonly #envelopes: Envelope[] = []; /** The engine-MINTED fallthrough envelope (kestrel-eywk) — created only when the first-armed document * declares no POD and no BOOK. It is the ONLY envelope the engine may restate, precisely because it is * the only one the AUTHOR did not write. Identity-tracked (not matched by name) so a document that * happens to name a Book "default" can never be mistaken for it. */ #defaultEnvelope: Envelope | null = null; /** Option books re-keyed by `symbol → expiryKey → book` (kestrel-ih5h.2), so two tenors of one * underlier COEXIST (a calendar) instead of latest-wins clobbering under a symbol-only key. The * inner key is the BOOK event's own `expiry` string (`""` when the tape pins none); {@link foldBook} * stays latest-wins WITHIN one expiry — its shape is deliberately unchanged (the collision fence * with kestrel-snq0). A leg's authored tenor selects its OWN expiry's book in {@link #bookFor}. */ readonly #books = new Map>(); /** The last observed spot NBBO per instrument (ADR-0017): the additive `bid`/`ask` riding a * SPOT tick, plus the print itself as `last`. The quote a spot leg's price ctx reads — an * option tape sets neither side, so its entries stay dark and no spot leg can price. */ readonly #spotQuotes = new Map(); readonly #tags = new Map(); #now: number; #orderSeq = 0; #armSeq = 0; /** Monotonic registration counter — the deterministic ordinal that mints each Plan-instance identity * (kestrel-22j.15). Increments once per {@link #registerPlan}, so the SAME documents armed in the SAME * order yield byte-identical instance ids across replays (no wall clock / no RNG, RUNTIME §0). It is the * only thing that distinguishes a legal same-name replacement from its predecessor. */ #planInstanceSeq = 0; /** Count of actual cancel/replace REPRICE submissions (peg drift + esc-stage boundary) — the * honest reprice tally the report exposes (F6). Distinct from `escStage`, which is the ladder * rung an order occupies, not a count of reprices. */ #repriceCount = 0; constructor(opts: PlanEngineOptions) { for (const spec of opts.instruments) this.#instruments.set(spec.symbol, spec); this.#execDefault = opts.instruments.find((s) => s.role === "exec")?.symbol ?? opts.instruments[0]?.symbol; this.#signalDefault = opts.instruments.find((s) => s.role === "signal")?.symbol ?? opts.instruments[0]?.symbol; this.#newEvaluator = opts.newEvaluator ?? (() => new TriggerEvaluator()); this.#gate = opts.gate; this.#bus = opts.busWriter; this.#bucket = opts.repriceBucket; this.#rUsd = opts.rUsd; this.#fairTau = opts.fairTauYears; this.#tapeRegimeScopes = opts.tapeRegimeScopes !== undefined ? new Set(opts.tapeRegimeScopes) : undefined; this.#now = opts.now; this.#ledger = new BookLedger((instrument) => this.#specOf(instrument).multiplier); this.#registry = opts.registry ?? defaultSeriesRegistry; // The engine owns the mutable de-arm stash; the org read view stays pure and only reports the // reason (fail-closed loud de-arm, RUNTIME §8). First unresolvable path per sweep wins. this.#orgFacts = new EngineOrgFacts(this.#ledger, (reason) => { this.#lastOrgUnresolved ??= reason; }); this.#provider = new ComposedProvider(opts.seriesProvider, this.#orgFacts, this.#registry, (reason) => { this.#lastOrgUnresolved ??= reason; }); } /** The engine's ledger-backed org facts (RUNTIME §2): `pnl`, `fills.*`, `plan(x).*`, * `children(any|all).*`. Exposed for panes/grade columns and inspection; the engine's own trigger * sweep reads it internally. */ get orgFacts(): OrgFacts { return new EngineOrgFacts(this.#ledger); } /** * The PM-node write seam for a child's published aggregate — the write side of * `children(any|all).` (RUNTIME §5 / CONTEXT: Pod). A parent Pod receives each child's own * org aggregate (its `drawdown`, its `pnl`, …) and folds it into the book ledger so a PM wake can * quantify across the pod (`children(any).drawdown > 0.3R`). Registers the child-scoped org path * in the shared phonebook on first write, source-stamped to the child (implicit registration, * cza.1). Deterministic (a pure upsert); the value is the child's own already-computed aggregate, * so this seam is taste-free — it does not recompute or judge it. */ noteChildFact(child: string, fact: string, value: number): void { this.#ledger.setChildFact(child, fact, value); this.#registry.noteOrgWrite( [{ name: "children", selector: { kind: "sel-quant", q: "any" } }, { name: fact }], child, ); } /** The engine's composed {@link SeriesProvider} — the seam its trigger sweep reads through, * routing org paths to the ledger and market facts to the injected provider via the shared * phonebook. Exposed read-only for inspection (e.g. certifying that the engine and the provider * agree on market-vs-org for every declared series — the anti-skew guarantee, cza.2). */ get seriesProvider(): SeriesProvider { return this.#provider; } // ── arming ──────────────────────────────────────────────────────────────── /** * Arm a parsed Kestrel document (RUNTIME §5). Builds the document's nesting envelope chain * (Pod `R`-risk-budgets → Book budget/concurrency; a single-book document is a chain of one), * registers each plan `authored`, then immediately reconciles arming at the current time: a * plan whose regime gate is satisfied (or `regime {any}`/absent) arms; one gated on a tag not * yet written stays authored until a `REGIME` write (UNKNOWN de-arm, RUNTIME §3). */ armDocument(mod: Module, opts?: { synthesizedOrder?: boolean }): ArmReport { // Two arm-time integrity checks, one per shape (OSS-ADR-0045): // - F4 (same-name supersession shadowing): a revision re-declaring a name still owned by a LIVE // (not-done) prior record would clobber the by-name ledger and garble the merged lifecycle // trace (`plan(x).state`, the frame's plan lines). This THROWS an {@link ArmError} (code // `plan-name-in-use`) — a whole-document refusal, because arming would corrupt LIVE state; the // thrown error carries `.code`, never matched by message. A name whose prior record is already // `done` (fully expired/settled) is free to reuse. // - unknown-series (mte): a trigger names a case-variant of a known market fact. This is a // per-statement DATA refusal collected below — the offending statement is skipped (never armed: // arming it would read UNKNOWN forever, the silent de-arm this reverts), its healthy siblings // arm. Nothing healthy is aborted by a bad sibling; nothing is silently dropped. const refusals: ArmRefusal[] = []; for (const st of mod.statements) { if (st.kind !== "plan") continue; const clash = this.#plans.find((pr) => pr.name === st.name && !pr.done); if (clash !== undefined) { throw new ArmError( "plan-name-in-use", `arm: plan name ${JSON.stringify(st.name)} still owned by a managing plan — names are lineage; author a new name (fail-closed, RUNTIME §8)`, ); } const rejection = this.#unknownSeriesRejection(st); if (rejection !== null) { refusals.push({ statement: st.name, code: "unknown-series", message: rejection.message, repair: rejection.repair }); } // Manages-nothing (kestrel-b4wx/kestrel-hcnj): a covered sell with no way to ever hold inventory is // refused AS DATA — never registered, healthy siblings still arm — so it can never silently arm inert. const manages = this.#managesNothingRejection(st); if (manages !== null) { refusals.push({ statement: st.name, code: "manages-nothing", message: manages.message }); } // Opening-short (kestrel-53gw): an AUTHORED plan whose `DO`/`ALSO` entry opens a SELL leg it can never // cover at fire is refused AS DATA — so a debit spread whose short leg would silently drop (minting a // proof byte-identical to a naked long) fails closed LOUD at arm instead. A synthesized one-shot // placeOrder is EXEMPT (it has its own terminal de-arm at fire, tests/simulate.order-actions.test.ts). if (!(opts?.synthesizedOrder ?? false)) { const openShort = this.#openingShortRejection(st); if (openShort !== null) { refusals.push({ statement: st.name, code: "opening-short-uncovered", message: openShort.message }); } } } // The healthy view: refused statements are filtered out so their name/budget never enter the // standing book (envelope construction, registration, and `plan(x).state` all read `healthy`). // With no refusals `mod` passes through UNCHANGED — the healthy arm path stays byte-identical. const refused = new Set(refusals.map((r) => r.statement)); const healthy: Module = refused.size === 0 ? mod : { ...mod, statements: mod.statements.filter((st) => st.kind !== "plan" || !refused.has(st.name)) }; const planEnvelope = this.#buildEnvelopes(healthy, opts?.synthesizedOrder ?? false); const registered: PlanRuntime[] = []; for (const st of healthy.statements) { if (st.kind !== "plan") continue; registered.push(this.#registerPlan(st, planEnvelope, opts?.synthesizedOrder ?? false)); } // Cross-Plan inventory-ownership handshake (kestrel-22j.14): a replacement plan carrying an // explicit ARM inventory binding adopts management authority for the exact held Book legs from // superseded, acquisition-halted, inventory-holding siblings — HERE, at the tail of arming // (after #registerPlan, before #reconcileArming), inside the guaranteed supersede-then-arm // window so the adopted long is in place before any onEvent can fire a covered sell. this.#adoptInventory(registered, this.#now); this.#reconcileArming(this.#now); // Adoption-management promotion (kestrel-r866). A PURE adopter — an `ARM … foreach held leg` binding // with no fireable entry — has now been armed by #reconcileArming, but it will never reach the fire // path, so its EXIT/TP would sit inert while it holds the adopted leg (the tracer-7 "escape hatch is // INERT on the day handshake" defect). Promote it armed→managing so its authored management clauses // run. Scoped to `entries.length === 0` (a plan WITH entries reaches managing through the fire path // and is untouched) and to plans that actually took inventory (`adoptedInventory`) and armed cleanly. for (const pr of registered) { if (!pr.adoptedInventory || pr.done || pr.fired || pr.state !== "armed" || pr.entries.length > 0) continue; this.#beginAdoptedManagement(pr, this.#now); } // Accept-path activation-gate visibility (kestrel-ocyf). A plan gated on a regime SCOPE with no // writer on this tape reconciles to `authored (blocked: regime UNKNOWN)` and stays there // forever — it can never arm, never fire. Before this the block was SILENT at submit: no reject, // no bus notice, visible only a full wake later in the frame's plans section, so an agent read // `authored` as a live position (the phantom-position trap; tracer-6 lost its best entry to it). // // Two grades of signal, and the notice must never claim more than the engine can prove: // - With {@link #tapeRegimeScopes} (the caller pre-scanned the full bus — sim/replay/day): a gate // scope OUTSIDE the tape's regime vocabulary is PROVABLY dead — the definitive verdict. A scope // the tape WILL write later is legitimately waiting and arms when the write replays — no notice // at all (a warning there would cry wolf on every regime-gated plan, worse than silence). // - Without it (live: the future is unknown): a CONDITIONAL notice — the scope is not yet // written, the plan stays blocked until a REGIME event writes it. Never the word "never". // // A WARNING, not a throw, even for the provably-dead grade — three reasons over the mte precedent // (#refuseUnknownSeries, which THROWS): (1) mte refuses typos of the FROZEN market vocabulary // (zero legitimate authorings); a regime gate on a regime-less tape is a legal cross-mode pattern // (the shipped e2e corpus deliberately co-arms `gated-idle` beside four live plans — a standing // gated plan whose feed exists live but not on this sim tape). (2) an armDocument throw is // WHOLE-document: it would take down live sibling plans for a per-plan condition. (3) every // arm-time throw must also be previewed by {@link supersedeArmRejection} or a doomed revision // tears down the standing book before the arm detonates (the p48s day-swallow bug) — a notice // has no such blast radius. // // The notice is surfaced TWO ways from the SAME string, byte-for-byte (kestrel-hk9u): emitted on // the bus (the run-time plan-lifecycle trace — what a real batch run's blotter/frame shows) AND // collected onto {@link ArmReport.notices}, so a bus-less arm preview (`validateArm`, whose Bus is // a no-op sink) can surface the identical verdict. This closes the validate-gap the persona probe // hit (kestrel-hk9u): a regime-gated flagship example that `validate --arm` called "arms clean" // while every free cell placed 0 orders — now the dead-on-arrival gate is legible AT ARM. const notices: ArmNotice[] = []; for (const pr of registered) { if (pr.state !== "authored") continue; const gate = pr.plan.regime; if (gate === undefined) continue; const unknown = gate.tags .filter((b) => b.scope !== "any" && b.value !== "any" && this.#tags.get(b.scope) === undefined) .map((b) => b.scope); if (unknown.length === 0) continue; let message: string; if (this.#tapeRegimeScopes !== undefined) { const dead = unknown.filter((s) => !this.#tapeRegimeScopes!.has(s)); if (dead.length === 0) continue; // every gated scope IS written later — legitimately waiting message = `regime ${dead.join(", ")} is never written on this tape — this plan can never arm ` + `(dead on arrival; it stays authored (blocked) for the whole session)`; } else { message = `regime ${unknown.join(", ")} not yet written — plan stays authored (blocked) until a ` + `REGIME event writes it; on a tape with no ${unknown.join("/")} writer it can never arm`; } this.#emitReject(this.#now, pr, message); notices.push({ statement: pr.name, code: "regime-unsatisfiable", message }); } // Adoption-bound-nothing visibility (kestrel-c25w, the run-tier sibling of the b4wx footgun). A VALID // `ARM [basis ] foreach|any held leg` adopter (its `armClause.over` is defined, so it PASSES the // parse-tier manages-nothing refusal — it genuinely CAN hold inventory) that, at THIS arm, resolved to // ZERO held legs bound nothing: no superseded, acquisition-halted, inventory-holding sibling had a leg // to hand off, so #adoptInventory left `adoptedInventory` false. Its whole job was adopt-and-exit, so // binding nothing means its exit protection is INERT — yet the plan arms, expires quietly, and grades // GREEN (`plans_armed=1 … orders_placed=0`), indistinguishable on the run report from a working // adoption. This is the SAME silent one-way-door class b4wx targeted, surviving at the RUN/grade tier // for a plan the PARSER cannot refuse — it is a legitimate adopter that simply found nothing to adopt in // this session (a DIFFERENT session, with a superseded holder in place, would bind), so this is a // NOTICE, never a refusal (the document is integrity-clean; it no-op'd here). // // Surfaced TWO ways from the SAME string, byte-for-byte (the kestrel-hk9u pattern the regime notice // above uses): emitted on the bus as a PLAN-lifecycle reason (so the frame / human report shows it on // the plan's OWN line — the bus-only notice is what a real batch run's blotter/frame carries) AND // collected onto {@link ArmReport.notices} (so a bus-less arm preview — `validateArm`, `parse --arm`, // the hosted `/validate` — surfaces the identical verdict). Before this the zero-leg bind was pure // silence: not even a bus reason, so an author could not tell adoption succeeded from silently no-op'd. for (const pr of registered) { if (pr.done || pr.armClause?.over === undefined || pr.adoptedInventory) continue; const message = `${ARM_BOUND_NOTHING_MARKER} — this \`ARM … foreach held leg\` adopter found no superseded, inventory-holding ` + `plan to adopt from, so it bound 0 held legs and its exit protection is INERT (it stays armed, then ` + `expires without ever covering). Adoption is a one-shot at arm: supersede the inventory-holding ` + `plan in the SAME step so its held leg is in place to bind (the day handshake, RUNTIME §4).`; this.#emitReject(this.#now, pr, message); notices.push({ statement: pr.name, code: "adoption-bound-nothing", message }); } return { armed: registered.map((pr) => pr.name), refusals, ...(notices.length > 0 ? { notices } : {}) }; } /** * Arm-time unknown-series integrity (kestrel-mte / OSS-ADR-0045) as a PURE preview (no mutation, no * throw): the coded refusal a case-variant market-series trigger raises at arm — its DISPLAY * `message` and a concrete did-you-mean `repair` (the canonical lowercase series name) — or `null` * when every trigger operand resolves. * * The failure it catches: a trigger operand names a CASE-VARIANT of a registered market fact * (`VELOCITY` for the windowed `velocity`, `VWAP` for the scalar `vwap`). Because * {@link SeriesRegistry.lookup} is now EXACT-MATCH (OSS-ADR-0045; the 0.4.5 case-insensitive * forgiveness is reverted), that name has NO phonebook record, so the org/market split routes it to * the ORG side where it reads UNKNOWN forever: the plan would arm but never fire, its only runtime * signal a MISLEADING "org path unresolvable — no such org fact". The platform market vocabulary is * FROZEN and known at arm time (the shared phonebook, `this.#registry`), so this is STATICALLY * knowable: {@link armDocument} refuses the offending statement AS DATA (naming the offending token * AND the intended market series), its healthy siblings still arm — never arming a plan that can * never fire, never silently de-arming it at runtime (RUNTIME §8). * * Scoped narrowly to a case-variant near-miss of a KNOWN market name (zero false positives — a * genuine org fact like `pnl` never case-matches a market name); a truly unknown bare name with no * market near-match stays an org path (org facts register implicitly / delegate to the book). Split * out so {@link supersedeArmRejection} can preview it — a preview that misses it lets a doomed * revision tear down the standing book before the arm (the p48s day-swallow bug). * * DELEGATES to the ONE shared LIGHT implementation ({@link unknownSeriesRejection}, `src/validate`), * so this arm path and the hosted `/validate` route run the SAME semantic check — never a second * drifting copy (kestrel-h0ax; the mei parse ≠ arm bug came from that duplication). */ #unknownSeriesRejection(st: PlanStatement): { message: string; repair: string } | null { return unknownSeriesRejection(st, this.#registry); } /** * Arm-time MANAGES-NOTHING integrity (kestrel-b4wx / kestrel-hcnj) as a PURE preview (no mutation, no * throw): the coded refusal a plan that carries a covered sell it can never cover raises at arm — its * DISPLAY `message` — or `null` when the plan can hold inventory. * * The failure it catches: a plan carries an `EXIT`/`TP` (a covered sell — both can ONLY ever sell * inventory the plan HOLDS) but has NO way to ever hold inventory. Such a plan arms and sits inert * forever: an entryless plan never fires, so its EXIT is never even evaluated — the never-naked * refusal never gets to speak, and the held leg an EXIT-only revision meant to protect stays silently * unreachable. This is the SAME silent one-way door r866 fixed, reached one dropped keyword away (a * revision that wrote `EXIT …` but forgot the `ARM … foreach held leg` adoption binding). b4wx surfaced * it as a bus-only arm-time NOTICE, which is invisible on the author path (`parse --arm` still returned * armed=true) and never blocked arming — the footgun re-locked. Now it is refused AS DATA (like the mte * unknown-series refusal): the offending statement never registers, its healthy siblings still arm, and * `parse --arm`, `/validate`, and a real run all fail closed identically. * * A plan CAN hold/manage inventory iff ANY of: (1) an entry clause (`DO`/`ALSO`/`RELOAD`) acquires it; * (2) an `ARM … foreach|any held leg` binding adopts a superseded plan's held leg; (3) a covered sell * ITSELF carries a `foreach|any held leg` quantifier and so binds the held legs it iterates (the * `manage-inventory` golden: `ARM WHEN …` + `TP … foreach held leg` + `EXIT … foreach held leg`). Only a * covered sell with NONE of these three can never cover — that, and only that, is refused. A bare * `ARM WHEN` chain (no held quantifier) does NOT hold inventory on its own (RUNTIME §4), so it does not * rescue a bare `EXIT`. Kept as a SEMANTIC (cross-clause) arm-tier check, NOT a parse-tier one: the * grammar round-trip property legitimately generates inert-but-well-formed ASTs, and a covered sell IS * grammatically valid — the defect is that it can never cover, which is an arm-integrity fact. * * Split out (like {@link #unknownSeriesRejection}) so {@link supersedeArmRejection} can preview it — a * preview that misses it lets a doomed EXIT-only revision supersede the standing book, then get refused * at arm, tearing the book down (the p48s day-swallow bug). PURE: reads only `st.clauses`. */ #managesNothingRejection(st: PlanStatement): { message: string } | null { const covered = st.clauses.find((c) => c.kind === "exit" || c.kind === "tp"); if (covered === undefined) return null; // no covered sell ⇒ nothing to cover, nothing to refuse const hasEntry = st.clauses.some((c) => c.kind === "do" || c.kind === "also" || c.kind === "reload"); const armAdopts = st.clauses.some((c) => c.kind === "arm" && c.over !== undefined); const coverAdopts = st.clauses.some((c) => (c.kind === "exit" || c.kind === "tp") && c.over !== undefined); if (hasEntry || armAdopts || coverAdopts) return null; const clause = covered.kind === "exit" ? "EXIT" : "TP"; return { message: `arm: plan ${JSON.stringify(st.name)} carries a covered ${clause} but can never hold inventory — no ` + `\`DO\`/\`ALSO\`/\`RELOAD\` entry to acquire it and no \`ARM … foreach held leg\` to adopt a superseded ` + `plan's held leg — so it manages NOTHING and can never cover (fail-closed at arm, RUNTIME §4/§8; a ` + `plan that can never cover would arm and sit inert forever). Did you mean \`DO buy …\` to acquire the ` + `leg, or \`ARM [basis ] foreach held leg\` to adopt the held leg this ${clause} would exit?`, }; } /** * Arm-time OPENING-SHORT-STRUCTURE integrity (kestrel-53gw) as a PURE preview (no mutation, no throw): the * coded refusal a MULTI-LEG opening structure raises at arm when its uncoverable SELL leg would silently * collapse into a misleading long-only proof — its DISPLAY `message` — or `null` when the structure is safe. * * The failure it catches (the debit-spread silent-collapse): an author writes a defined-risk vertical — * `DO buy 2 +1 C @ ask` / `ALSO sell 2 +3 C @ bid`. The short leg's ONLY coverage at the plan's single * entry fire is inventory the plan already HOLDS, and it holds none: a same-fire entry BUY has not filled * yet (fills return as later bus events, {@link #netHeldForLeg} counts `filledQty`), and cross-strike * STRUCTURAL coverage — the long +1 C bounding the short +3 C to a defined debit — is a genuine combo * concept that needs ATOMIC multi-leg execution the engine does not yet do (the AST `atomic` keyword is * reserved-but-refused on every surface; the whole-structure preflight + atomic adapter is future work). * So {@link #sellCovered} refuses the short at every fire ("uncovered sell refused: never naked") while the * plan's BUY leg FIRES — silently degrading the spread to a naked long whose graded proof is BYTE-IDENTICAL * to the single long leg (order_count/fill_count/pnl all equal), with the refusal buried on an `armed` * lifecycle line the headline proof never surfaces. That is the zero-diagnostic misleading proof * kestrel-53gw reports. * * SCOPED PRECISELY to that byte-identical-proof defect: the refusal fires only when an uncoverable opening * SELL leg CO-EXISTS with an opening BUY leg (`DO buy …`, incl. a spot leg) — the exact shape where the buy * fires alone and mints a proof indistinguishable from a valid long (debit spread → naked long; credit * spread → naked long wing; single-entry covered call → naked long shares). A PURE naked opening sell (a * `DO sell …` with NO opening buy anywhere) is deliberately LEFT to the fire-time never-naked boundary * ({@link #sellCovered}): its proof is `order_count = 0` — already DISTINGUISHABLE from a long and already * carrying a per-fire `uncovered sell refused: never naked` lifecycle diagnostic (ADR-0017; the equity * naked-short in tests/e2e.equity.test.ts). It is not the misleading-proof bug, so we do not move its * refusal earlier and change that established surface. * * A plan CAN cover an entry sell at fire iff it ADOPTS pre-existing inventory: an `ARM … foreach|any held * leg` binding, or an `EXIT`/`TP … foreach|any held leg` cover that itself binds the held legs. A structure * with NEITHER (and no held inventory) is dead on arrival — refused AS DATA (like * {@link #managesNothingRejection}), so `parse --arm`, `/validate`, and a real run all fail closed * identically and the misleading proof can never mint. An adopting plan is left to the fire-time * {@link #sellCovered} boundary (unchanged). * * SCOPED to authored plans (the caller skips it for a synthesized one-shot `placeOrder`, which has its own * LOUD terminal de-arm at fire, tested by tests/simulate.order-actions.test.ts). Reads only `st.clauses`; * pure. RELOAD sells are NOT caught — a RELOAD fires during MANAGEMENT, after the entry buy can fill, so it * is legitimately deferred-coverable. */ #openingShortRejection(st: PlanStatement): { message: string } | null { const entry = st.clauses.find((c) => (c.kind === "do" || c.kind === "also") && c.legs.some((l) => l.side === "sell")); if (entry === undefined) return null; // no entry sell ⇒ nothing that could open uncovered // The misleading-proof defect requires an opening BUY that fires ALONE and passes for a long. Without one, // a pure naked opening sell mints order_count=0 (distinguishable) and keeps its fire-time never-naked // diagnostic — not this bug, left to #sellCovered (ADR-0017; e2e.equity naked-short). const opensLong = st.clauses.some((c) => (c.kind === "do" || c.kind === "also") && c.legs.some((l) => l.side === "buy")); if (!opensLong) return null; const armAdopts = st.clauses.some((c) => c.kind === "arm" && c.over !== undefined); const coverAdopts = st.clauses.some((c) => (c.kind === "exit" || c.kind === "tp") && c.over !== undefined); if (armAdopts || coverAdopts) return null; // may hold covering inventory at fire — leave to #sellCovered const clause = entry.kind === "do" ? "DO" : "ALSO"; return { message: `arm: plan ${JSON.stringify(st.name)} opens a multi-leg structure whose \`${clause}\` SELL leg it can ` + `never cover — no \`ARM … foreach|any held leg\` and no \`EXIT\`/\`TP … foreach|any held leg\` to adopt ` + `inventory. An entry sell can only ever CLOSE inventory the plan already holds, and at the entry fire ` + `it holds none (a same-fire entry BUY has not filled yet). A defined-risk vertical whose long leg would ` + `structurally cover the short needs ATOMIC multi-leg execution the engine does not yet support ` + `(kestrel-psn5) — without it the short is refused never-naked at every fire while the long fires alone, ` + `minting a proof byte-identical to a naked long (fail-closed at arm, RUNTIME §8; kestrel-53gw). Did you ` + `mean a long-only structure, or \`ARM [basis ] foreach held leg\` to adopt the leg this sell closes?`, }; } /** * **Document supersession** (RUNTIME §5): a newly-authored document replaces the standing one. * Every currently-standing plan is de-armed cleanly at `now` — acquisition is halted and all * **unfilled** entry/reload children are cancelled — but **filled inventory and its resting * TP/EXIT management PERSIST** under the superseded plan record (never liquidated). A plan that * holds no inventory and has nothing working is finished (`done(expired)`, reason `superseded`); * a plan still holding inventory keeps managing it (TP/EXIT/INVALIDATE) under its old record. The * caller then {@link armDocument}s the replacement. Ordering with the caller is: supersede first * (at `now`), then arm — the two documents never both acquire the same tick. */ supersede(now: number): void { this.#now = now; for (const pr of this.#plans) { if (pr.done) continue; // Halt acquisition + cancel unfilled entry/reload children only (inventory + TP/EXIT ride). pr.acquisitionHalted = true; pr.reloadsHalted = true; for (const child of pr.children) { if (child.filled || child.cancelled) continue; if (child.role === "entry" || child.role === "reload") this.#cancelChild(child); } // No inventory and nothing working ⇒ cleanly de-armed (the common case: an un-fired or // fully-worked plan). Inventory-holding plans ride under their superseded record. Evaluated // via the shared survivor predicate (after the entry/reload cancellation above) so it can never // drift from {@link supersedeArmRejection}'s preview. if (!this.#survivesSupersede(pr)) { this.#finish(pr, now, "expired", "superseded"); } } } /** * Cancel a resting order by its real Gate ref on behalf of an agent `cancelOrder` (kestrel-5zl.5 / #3c). * Routes through {@link #cancelChild} so BOTH the engine's child state AND the emitted bus are updated * in the SAME step: the engine dump (which the wake snapshot scrapes `resting[]` from) drops the order * and the Gate emits the ORDER `cancel` — so `resting[]` and `engineLog` agree within one snapshot, * never one wake late (the anomaly the capstone agent flagged). Returns whether a resting order was * actually removed; an unknown / already-filled / already-cancelled ref returns `false` (the driver's * proportionate fail-closed refuse — never a crash, never a wrongful cancel). */ cancelOrderByRef(ref: string, now: number): boolean { this.#now = now; for (const pr of this.#plans) { for (const c of pr.children) { if (c.ref === ref && !c.filled && !c.cancelled) { this.#cancelChild(c); // A SYNTHESIZED one-shot placeOrder (kestrel-5zl.5) has no authored lifecycle to fall back on: // its ONLY child was this now-cancelled order, so it sits `managing` with zero live orders and // no inventory until some UNRELATED event happens to finish it (kestrel-6bd). Finish it in the // SAME operation so {@link #finish} releases the envelope's concurrency slot THIS wake snapshot // (the dollar reservation already dropped the instant the resting buy was cancelled — it is derived // from live children, bd 1a8 — so `budget.used` never lands one-plus wakes late). GUARDED (never // over-eager): only a synthesized plan (authored // plans keep their existing lifecycle — a cancelled child there rides on, kestrel-22j.14), and // only when it holds NO inventory (`#netHeldTotal`, transferred-aware) and has NO other working // order (`#workingCount`) — a synthesized plan that filled earlier and still holds inventory // keeps managing (its held portion is never released by a cancel). PURE reuse of the same // spent-plan predicate {@link #doPlanCancelIf} / {@link #adoptInventory} apply ⇒ determinism holds. if (pr.synthesizedOrder && !pr.done && this.#netHeldTotal(pr) <= EPS && this.#workingCount(pr) === 0) { this.#finish(pr, now, "expired", "cancelled by author"); } return true; } } } return false; } /** * FLATTEN an instrument on behalf of an agent `flatten` action (bd if0 / the 75n flatten portion) — the * first-class "get me out now" primitive. For EVERY managing plan holding net-long inventory in * `instrument`, in ONE synchronous step (routed through the SAME gate a fired Plan uses): * (1) HALT acquisition and CANCEL all its unfilled children for the instrument — this neutralizes the * resting TP/EXIT (freeing the lot reservation) AND, via the {@link PlanRuntime.flattened} latch, * stops {@link #maintainTP} re-posting it every sweep (the rolling re-post that defeated a bare * `cancelOrder`, the un-exitable-position trap); * (2) cross to CLOSE each held leg with a COVERED sell priced marketable `@bid` — never-naked HOLDS: the * close is covered by the exact held leg (the TP was cancelled in the same step, so * {@link #sellCovered} sees the full lot free), and a SELL is floored at intrinsic downstream in * pricing. A flatten can therefore NEVER create a naked position. * Once the close fills the plan goes flat and finishes `done(expired, "flattened")` on the next sweep * (per bd 1a8 its R is freed the moment it is flat). A FLAT book is a clean no-op. PURE — a function of * current positions + the injected `now`, no wall clock / RNG — so two replays agree byte-for-byte * (RUNTIME §0). Returns the closed contracts and how many plans were flattened (the driver's audit). */ flatten(instrument: string, now: number): { closedQty: number; plansFlattened: number } { this.#now = now; let closedQty = 0; let plansFlattened = 0; for (const pr of this.#plans) { if (pr.done || !pr.fired) continue; // Act when the plan has EITHER a held position OR a LIVE resting order for the instrument (Finding 3): // a fully-unfilled resting ENTRY must also be cancelled + acquisition halted, or "get me out now" would // leave the entry live to fill on a later tick, re-longing the agent against intent. A truly flat plan // with no resting order in the instrument stays a clean no-op (skipped). if (this.#netHeldInInstrument(pr, instrument) <= EPS && !this.#hasLiveOrderInInstrument(pr, instrument)) continue; closedQty += this.#flattenPlan(pr, instrument, now); plansFlattened += 1; } return { closedQty, plansFlattened }; } /** Close one managing plan's held position in `instrument` (bd if0). Halt acquisition, cancel every * unfilled child for the instrument (neutralizing the TP + its reservation), latch `flattened` (so the * TP never re-posts and the authored EXIT is suppressed), then cross to close each held leg with a * COVERED marketable `@bid` sell. Returns the contracts crossed. An unresolvable close price (no live * bid) leaves the held lot resting-order-free but still held — fail-closed: the position rides untouched * until a quote arrives and a later flatten can cross it (never abandons inventory, never sells naked). */ #flattenPlan(pr: PlanRuntime, instrument: string, now: number): number { pr.flattened = true; pr.acquisitionHalted = true; pr.reloadsHalted = true; // (1) Cancel every unfilled child for the instrument — TP/EXIT/entry/reload. Risk-reducing (only ever // cancels a resting order), so `Σ resting_sells(leg) ≤ net_held(leg)` holds at every step. for (const c of pr.children) { if (c.filled || c.cancelled || c.instrument !== instrument) continue; this.#cancelChild(c); } // (2) Cross to close each held leg with a covered marketable sell. let closed = 0; for (const leg of this.#heldLegsInInstrument(pr, instrument)) { const held = this.#netHeldForLeg(pr, leg.strike, leg.right); if (held <= EPS) continue; // Never-naked guard (belt-and-suspenders — the TP was just cancelled, so this always holds): the // close is covered iff `held − resting_sells ≥ held`, i.e. no other sell still reserves the lot. if (!this.#sellCovered(pr, leg.strike, leg.right, held)) continue; const closeLeg: Leg = leg.strike === undefined ? { kind: "equity-leg", side: "sell", qty: held } : { kind: "leg", side: "sell", qty: held, strike: { kind: "strike-abs", strike: leg.strike }, right: leg.right ?? "C" }; const line: PriceLine = { price: { kind: "anchor", name: "bid" } }; // marketable — crosses to close const built = this.#resolveLeg(pr, closeLeg, line, now, now, undefined); if (built === null) continue; // no live bid ⇒ can't cross this tick; the lot rides (fail-closed) this.#submit( pr, { role: "exit", side: "sell", qty: held, instrument: built.instrument, ...(built.strike !== undefined ? { strike: built.strike } : {}), ...(built.right !== undefined ? { right: built.right } : {}), px: built.px, ann: built.ann, placedAt: now, ...(built.priceLine !== undefined ? { priceLine: built.priceLine } : {}), }, now, ); closed += held; } this.#emitWake(now, pr.name, pr.instanceId, "flatten"); // If the plan is ALREADY flat with nothing working (e.g. every leg's price was unresolvable and no // inventory remained), finish it now; otherwise it finishes when the close fills (see {@link #manage}). if (this.#netHeldTotal(pr) <= EPS && this.#workingCount(pr) === 0) this.#finish(pr, now, "expired", "flattened"); return closed; } /** Net-held contracts of `pr` in `instrument`, across all its legs (transferred-aware). Used to decide * whether a plan has a position to flatten. */ #netHeldInInstrument(pr: PlanRuntime, instrument: string): number { let q = 0; for (const c of pr.children) { if (c.transferred || c.instrument !== instrument) continue; q += (c.side === "buy" ? 1 : -1) * c.filledQty; } return q; } /** Does `pr` have a LIVE (unfilled, non-cancelled, non-transferred) resting order for `instrument`? A * flatten must act on such a plan even with zero net-held (Finding 3): the resting entry would otherwise * fill later and re-long the agent against a "get me out now". */ #hasLiveOrderInInstrument(pr: PlanRuntime, instrument: string): boolean { for (const c of pr.children) { if (c.instrument === instrument && !c.filled && !c.cancelled && !c.transferred) return true; } return false; } /** The distinct held legs of `pr` in `instrument` (filled, non-cancelled, non-transferred BUY children), * in deterministic order — the enumeration {@link #flattenPlan} closes. Includes SPOT legs * (strike/right `undefined`, ADR-0017) so an equity position flattens too. */ #heldLegsInInstrument(pr: PlanRuntime, instrument: string): { strike: number | undefined; right: Right | undefined }[] { const seen = new Set(); const out: { strike: number | undefined; right: Right | undefined }[] = []; for (const c of pr.children) { if (c.transferred || c.cancelled || !c.filled || c.side !== "buy" || c.instrument !== instrument) continue; const k = `${c.strike ?? "spot"}|${c.right ?? "-"}`; if (seen.has(k)) continue; seen.add(k); out.push({ strike: c.strike, right: c.right }); } out.sort((a, b) => (a.strike ?? -1) - (b.strike ?? -1) || (String(a.right) < String(b.right) ? -1 : String(a.right) > String(b.right) ? 1 : 0)); return out; } /** * Would `pr` keep OWNING its name after a supersession at the current instant? A managing / * inventory-holding plan (or one with a working TP/EXIT) rides under its superseded record and keeps * the name reserved; an un-fired or fully-worked plan releases it (its unfilled entry/reload children * are cancelled by supersede, then it finishes `done(expired, "superseded")`). PURE — reads state * only. Shared by {@link supersede} (the actual finish decision, evaluated after cancellation) and * {@link supersedeArmRejection} (the pre-mutation preview: entry/reload children are treated as if * already cancelled, matching what supersede will do). */ #survivesSupersede(pr: PlanRuntime): boolean { if (pr.done) return false; if (pr.filledBuyQty - pr.filledSellQty > EPS) return true; // holds inventory ⇒ rides for (const c of pr.children) { if (c.filled || c.cancelled) continue; if (c.role === "entry" || c.role === "reload") continue; // supersede cancels these return true; // a working TP/EXIT keeps the plan managing } return false; } /** * The rejection a supersede-then-arm of `mod` would raise, or `null` when it would arm cleanly — * PURE (no mutation). Previews BOTH of {@link armDocument}'s statically-knowable throws: * * 1. The F4 same-name collision guard, evaluated against the POST-supersede survivor set * ({@link #survivesSupersede}): a name still owned by a managing/inventory-holding plan * collides; a name whose only holder is an un-fired or fully-worked plan (which supersede * finishes) is free to reuse. * 2. The mte unknown-series guard ({@link #unknownSeriesRejection}): a case-variant of a known * market series (`VWAP` for `vwap`) refuses the whole document with the repair-guiding message. * * Covering both is load-bearing (bead kestrel-p48s): a preview that misses a throw lets the caller * supersede first and detonate on the arm — tearing down the standing book for a doomed revision. * A `null` here guarantees the subsequent supersede+arm cannot reject, so a stepped runner can * reject-and-reprompt WITHOUT touching the standing book (fail-closed blast-radius, RUNTIME §8). */ supersedeArmRejection(mod: Module): string | null { for (const st of mod.statements) { if (st.kind !== "plan") continue; const clash = this.#plans.find((pr) => pr.name === st.name && this.#survivesSupersede(pr)); if (clash !== undefined) { return `arm: plan name ${JSON.stringify(st.name)} still owned by a managing plan — names are lineage; author a new name (fail-closed, RUNTIME §8)`; } const unknownSeries = this.#unknownSeriesRejection(st); if (unknownSeries !== null) return unknownSeries.message; // Manages-nothing (kestrel-b4wx/kestrel-hcnj): previewed HERE so an EXIT-only revision is rejected // BEFORE the supersede runs — the standing book is never torn down for a doomed arm (the p48s bug). const manages = this.#managesNothingRejection(st); if (manages !== null) return manages.message; // Opening-short (kestrel-53gw): previewed HERE for the same reason — a doomed debit-spread revision is // rejected before supersede, never tearing down the standing book. (Supersession is always authored, so // no synthesized-order exemption is needed on this path.) const openShort = this.#openingShortRejection(st); if (openShort !== null) return openShort.message; } return null; } #registerPlan(plan: PlanStatement, envelope: Envelope, synthesizedOrder = false): PlanRuntime { const entries: EntrySpec[] = []; const reloads: ReloadClause[] = []; const tps: TpClause[] = []; const exits: ExitClause[] = []; const invalidates: InvalidateClause[] = []; const cancelifs: CancelIfClause[] = []; let armClause: ArmClause | null = null; for (const c of plan.clauses) { switch (c.kind) { case "do": case "also": entries.push({ legs: c.legs, price: c.price, ...(c.policy !== undefined ? { policy: c.policy } : {}) }); break; case "reload": reloads.push(c); break; case "tp": tps.push(c); break; case "exit": exits.push(c); break; case "invalidate": invalidates.push(c); break; case "cancel-if": cancelifs.push(c); break; case "arm": armClause = c; break; default: break; } } const pr: PlanRuntime = { plan, name: plan.name, instanceId: mintPlanInstanceId(plan.name, this.#planInstanceSeq++), envelope, state: "authored", fired: false, done: false, armedAtSeq: null, armedAtTs: null, filledBuyQty: 0, filledSellQty: 0, buyCostSum: 0, basisPx: null, firstAcquiredAt: null, reloadsHalted: false, acquisitionHalted: false, flattened: false, adoptedInventory: false, exitStood: false, reloadRungs: 0, orgDeArmReason: null, envelopeRefuseReason: null, armBlockReason: null, whenEverDefinite: false, budgetRefusedReason: null, deltaRefusedReason: null, synthesizedOrder, children: [], entries, reloads, tps, exits, invalidates, cancelifs, armClause, whenEval: null, reloadEvals: reloads.map(() => null), reloadPrevTrue: reloads.map(() => false), exitEvals: exits.map(() => null), exitIntent: exits.map(() => false), exitWorked: exits.map(() => false), invalidateEvals: invalidates.map(() => null), cancelifEvals: cancelifs.map(() => null), }; this.#plans.push(pr); this.#ledger.setPlanState(pr.name, "authored"); this.#emitPlan(this.#now, pr.name, pr.instanceId, "authored"); return pr; } // ── cross-Plan inventory-ownership handshake (kestrel-22j.14) ──────────────── /** * The ownership handshake. A Plan never *owns* inventory — it holds **management authority** (the * right to rest sells) over a slice of Book inventory. After document supersession the filled * long + its resting TP/EXIT ride under the superseded plan's own record; a legally-authored * replacement therefore has zero children and {@link #sellCovered} would refuse its covered exit * as naked even though the Book is long the exact leg. This transfers the management claim from the * superseded instance to an explicitly-bound replacement — WITHOUT weakening {@link #sellCovered} * globally and WITHOUT inferring attachment from a matching strike. * * TRIGGER — authority transfers ONLY when the replacement carries an explicit ARM inventory-binding * clause (`ARM … foreach|any held leg [basis ]`). A plain `DO buy`/`DO sell` naming a matching * strike NEVER adopts: acquisition and adoption are different verbs. * * CONSERVING, SINGLE-OWNER TRANSFER — for each held leg of each qualifying source, in one * synchronous step: (1) mark the source's filled lots `transferred` (they leave its net-held / * resting-sell authority); (2) cancel the source's resting protective TP/EXIT for the moved leg * (risk-reducing — never rests a NEW sell, so `Σ resting_sells(leg) ≤ Book_net_held(leg)` holds at * every intermediate step); (3) attach a synthetic already-filled `adopted` long to the * replacement (basis-carried; NO Gate buy). Quantity is conserved: the Book fill log is untouched; * `netHeld(source) + netHeld(replacement) == Book_net_held` before and after. * * FORKS — conservative defaults taken for the away owner (kestrel-22j.14 DESIGN): * • Fork A (which legs `foreach|any held leg` adopts): the WHOLE unclaimed held book of the * superseded siblings — wholesale authority transfer, no strike inference (the covered sell must * still name a genuinely-held leg). A per-leg named binding is deferred (forward-compatible). * • Fork B (source ordering): registration-ordinal order (oldest first = `#plans` order), legs in * `(strike, right)` order. * • Fork C (live siblings): NOT a source — only superseded / acquisition-halted siblings, so an * actively-acquiring plan can never have inventory yanked mid-management (single-owner preserved). */ #adoptInventory(registered: readonly PlanRuntime[], now: number): void { for (const rep of registered) { // TRIGGER: explicit ARM inventory binding, never strike inference. if (rep.armClause?.over === undefined) continue; // Source authority: superseded / acquisition-halted, inventory-holding siblings, oldest-first // (`#plans` is registration-ordinal). A live (not-halted) acquirer is EXCLUDED (Fork C). for (const src of this.#plans) { if (src === rep || src.done || !src.acquisitionHalted) continue; for (const leg of this.#heldLegsOf(src)) { const q = this.#netHeldForLeg(src, leg.strike, leg.right); if (q <= EPS) continue; // no (net) held qty ⇒ nothing to adopt (no phantom inventory) this.#transferLeg(src, rep, leg.strike, leg.right, q, now); } // Whole-holding wholesale transfer (Fork A): a now-flat source with nothing working is // finished done(expired, "transferred") — its authority has fully moved. if (this.#netHeldTotal(src) <= EPS && this.#workingCount(src) === 0) { this.#finish(src, now, "expired", "transferred"); } } } } /** Move one held leg's management authority from `src` to `rep` (kestrel-22j.14). Atomic + * conserving: mark the source lots handed-off, cancel the source's protective sells for the leg, * attach a basis-carried `adopted` long to the replacement. Emits a first-class transfer record * attributed to the replacement instance, naming the source instance as origin. */ #transferLeg(src: PlanRuntime, rep: PlanRuntime, strike: number, right: Right, q: number, now: number): void { // Basis carry: explicit `ARM basis ` overrides; else the source's realized VWAP for the // moved lots (a pure function of recorded fills + authored expr — no clock/RNG). Computed BEFORE // the source lots are marked transferred. const basis = this.#adoptBasis(src, rep, strike, right, now); // (1) Decrement source authority: its filled lots for this leg leave #netHeldForLeg / // #restingSellForLeg (the source can no longer rest a sell against inventory it handed off). for (const c of src.children) { if (c.transferred || c.cancelled || !c.filled) continue; if (c.strike !== strike || c.right !== right) continue; c.transferred = true; } // (2) Cancel the source's resting protective TP/EXIT covering the moved leg — risk-reducing // (only ever cancels a sell, never rests one), so the never-naked bound never grows. for (const c of src.children) { if (c.filled || c.cancelled) continue; if (c.side === "sell" && c.strike === strike && c.right === right) this.#cancelChild(c); } // (3) Attach the synthetic already-filled long to the replacement — bookkeeping only, NO Gate // buy. This IS the native long child #sellCovered reads; it exists ONLY via the explicit adopt. const ref = `a${++this.#orderSeq}`; // monotonic #orderSeq ⇒ deterministic attribution (RUNTIME §0) const child: ChildOrder = { ref, role: "adopted", side: "buy", qty: q, instrument: this.#execFor(rep).symbol, strike, right, px: basis, ann: `adopted from ${src.instanceId}`, filled: true, filledQty: q, // A synthetic already-filled long adopted at `basis` (kestrel-22j.14): its filled COST is the // basis it was adopted at, so the replacement plan's per-leg TP basis reads the handed-off premium. filledCost: q * basis, cancelled: false, placedAt: now, escStage: 0, }; rep.children.push(child); this.#childIndex.set(ref, { pr: rep, child }); // Keep the replacement's OWN net-held accounting self-consistent (basis + TP/EXIT sizing + the // `held = filledBuy − filledSell` EXIT reads): the adopted long is a filled buy of the replacement. rep.filledBuyQty += q; rep.buyCostSum += q * basis; if (rep.basisPx === null) rep.basisPx = basis; // The replacement now HOLDS an adopted position it must manage (kestrel-r866). A pure adopter (no // fireable entry) never reaches the fire path, so record the adoption so {@link armDocument} can // promote it armed→managing after arming — otherwise its EXIT/TP are inert (the tracer-7 defect). rep.adoptedInventory = true; // First-class transfer record: attributed to the REPLACEMENT instance, naming the SOURCE instance // as origin, so Grade/Blotter show the replacement's covered sell realizing against inventory // acquired under the source. Informational (not a lifecycle change); rides the deterministic stream. this.#bus.emit({ ts: now, stream: "PLAN", type: "lifecycle", plan: rep.name, plan_instance: rep.instanceId, state: rep.state, reason: `adopted ${q}×${strike}${right} held inventory from ${src.instanceId} (kestrel-22j.14 inventory handshake)`, }); } /** The basis to carry onto an adopted lot: explicit `ARM basis ` if the replacement authored * one, else the source's realized VWAP (`Σ filledQty·px / Σ filledQty`) for that leg's still-held * buys. A pure function of recorded fills + the authored expr (no clock/RNG). */ #adoptBasis(src: PlanRuntime, rep: PlanRuntime, strike: number, right: Right, now: number): number { if (rep.armClause?.basis !== undefined) { const r = this.#resolveExprPx(rep, rep.armClause.basis, "buy", this.#execFor(rep).symbol, strike, right, now); if (r !== null) return r; } let q = 0; let cost = 0; for (const c of src.children) { if (c.transferred || c.cancelled || !c.filled || c.side !== "buy") continue; if (c.strike !== strike || c.right !== right) continue; q += c.filledQty; cost += c.filledQty * c.px; } return q > EPS ? cost / q : 0; } /** The distinct held legs of a plan (filled, non-cancelled, non-transferred BUY children), in * deterministic `(strike, right)` order — the enumeration `#adoptInventory` moves oldest-first. * OPTION legs only: the ARM inventory handshake (kestrel-22j.14) is strike-keyed, and spot * inventory adoption is not in the first equity slice (ADR-0017 long-only intraday) — a spot * lot is simply never enumerated as adoptable (conservative, fail-closed). */ #heldLegsOf(pr: PlanRuntime): { strike: number; right: Right }[] { const seen = new Set(); const out: { strike: number; right: Right }[] = []; for (const c of pr.children) { if (c.transferred || c.cancelled || !c.filled || c.side !== "buy") continue; if (c.strike === undefined || c.right === undefined) continue; // spot lot: not adoptable in this slice const k = `${c.strike}|${c.right}`; if (seen.has(k)) continue; seen.add(k); out.push({ strike: c.strike, right: c.right }); } out.sort((a, b) => a.strike - b.strike || (a.right < b.right ? -1 : a.right > b.right ? 1 : 0)); return out; } /** Net long inventory of a plan across ALL legs (transferred lots excluded) — used to decide when * a source has been fully handed off and can finish `done(expired, "transferred")`. */ #netHeldTotal(pr: PlanRuntime): number { let q = 0; for (const c of pr.children) { if (c.transferred) continue; q += (c.side === "buy" ? 1 : -1) * c.filledQty; } return q; } // ── the tick ──────────────────────────────────────────────────────────────── /** * Advance the engine by one bus event (RUNTIME §5/§7). Pure and total: state at event *N* is * a function of events `≤ N` (no look-ahead). Returns nothing — all output is emitted onto * the {@link BusSink} (PLAN + WAKE) and through the {@link Gate} (order submit/cancel). */ onEvent(ev: BusEvent): void { this.#now = ev.ts; this.#ingest(ev); this.#reconcileArming(ev.ts); this.#fire(ev.ts); for (const pr of this.#plans) if (pr.fired && !pr.done) this.#manage(pr, ev.ts); } #ingest(ev: BusEvent): void { switch (ev.stream) { case "TICK": if (ev.type === "SPOT") { this.#ledger.setSpot(ev.instrument, ev.px); // The spot instrument's own NBBO rides the SPOT tick (additive bid/ask, ADR-0017); // fold it so a spot leg's price ctx reads a live quote. Option tapes set neither. this.#spotQuotes.set(ev.instrument, { bid: ev.bid ?? null, ask: ev.ask ?? null, last: ev.px }); } else if (ev.type === "BOOK") { // Merge the incoming leg(s) into the prior book (per-leg upsert): a single-leg delta // stream accumulates a full chain instead of collapsing to the last-quoted leg. // Fold into the (symbol, expiry) slot, not a symbol-only slot (kestrel-ih5h.2): a second // tenor on the same underlier no longer clobbers the first. `foldBook` stays latest-wins // per-leg WITHIN one expiry — but now that fold accumulates into ITS OWN expiry's book, so a // calendar's near and far books coexist and a leg fetches the tenor it authored (#bookFor). { const byExpiry = this.#books.get(ev.instrument) ?? new Map(); const eKey = ev.expiry ?? ""; byExpiry.set(eKey, foldBook(ev, byExpiry.get(eKey))); this.#books.set(ev.instrument, byExpiry); } this.#ledger.setSpot(ev.instrument, ev.underlier_px); } break; case "REGIME": this.#tags.set(ev.scope, ev.value); break; case "ORDER": this.#onOrder(ev.type, ev.order_id, ev.px, ev.qty); break; default: break; } } #onOrder(action: string, orderId: string, px: number | undefined, qty?: number): void { const hit = this.#childIndex.get(orderId); if (hit === undefined) return; const { pr, child } = hit; if (child.filled || child.cancelled) return; if (action === "fill") { // Partial-fill aware (Bug 2): honor the fill event's `qty` (capped at the child's remainder), // defaulting to the whole remainder when absent (the single-leg all-or-nothing common case). // `filled` latches only once the cumulative fill reaches the child qty; a partially-filled // child still rests its remainder, and the target-qty cap sizes any reprice to it. const remaining = child.qty - child.filledQty; const thisFill = qty === undefined ? remaining : Math.min(qty, remaining); if (thisFill <= 0) return; const fillPx = px ?? child.px; child.px = fillPx; child.filledQty += thisFill; child.filledCost += thisFill * fillPx; // per-LEG basis source (kestrel-m9i.41) if (child.filledQty >= child.qty - EPS) child.filled = true; this.#ledger.recordFill({ plan: pr.name, ref: child.ref, instrument: child.instrument, side: child.side, qty: thisFill, px: fillPx, ...(child.strike !== undefined ? { strike: child.strike } : {}), ...(child.right !== undefined ? { right: child.right } : {}), }); // A fill is the book's first authorized write of its `pnl`/`fills.*` org facts — register them // implicitly in the shared phonebook (cza.1 seam), source-stamped to the authoring plan. Idempotent // (first-write-wins) and fail-closed (never over a platform name), so this is a cheap per-fill call. this.#registry.noteOrgWrite([{ name: "pnl" }], pr.name); this.#registry.noteOrgWrite([{ name: "fills" }, { name: "avg_px" }], pr.name); if (child.side === "buy") { // First-write-wins "held since" anchor for a 0DTE `EXIT held ` (this.#now is the // current event's injected ts, RUNTIME §0) — set on the first acquiring fill, never reset. if (pr.firstAcquiredAt === null) pr.firstAcquiredAt = this.#now; pr.filledBuyQty += thisFill; pr.buyCostSum += thisFill * fillPx; } else { pr.filledSellQty += thisFill; } } else if (action === "cancel" || action === "reject") { child.cancelled = true; } } // ── arming reconciliation ───────────────────────────────────────────────── #reconcileArming(now: number): void { for (const pr of this.#plans) { if (pr.done || pr.fired) continue; const expired = this.#ttlExpired(pr, now); // TTL expiry for a still-armed plan: it stops arming/firing (RUNTIME §5). A plan reaching THIS site // armed and never fired (a FIRED plan expires through #doTtlExpire/#finish, not here — the loop's // `pr.fired` guard above skips it) is the highest-value teaching moment of a 0-order run, and it used // to expire with a bare `ttl` that could not tell a healthy untriggered plan from a dead-on-arrival // one (kestrel-y6vo / kestrel-kglw). Classify WHY it never fired onto the terminal reason so the // outcome self-explains (budget-clamped / trigger never resolved / trigger never crossed). if (pr.state === "armed" && expired) { this.#transition(pr, now, "done", { outcome: "expired", reason: this.#neverFiredExpiryReason(pr, now) }); pr.done = true; continue; } const gate = this.#regimeSatisfied(pr); if (pr.state === "authored" && gate.ok) { // TTL arming race (F3): an authored plan whose deadline has already passed (e.g. a `ttl-at` // that arms late in the same event its gate is satisfied) must NOT arm and fire past its // deadline — expire it directly instead. if (expired) { this.#transition(pr, now, "done", { outcome: "expired", reason: "ttl" }); pr.done = true; continue; } if (pr.armedAtSeq === null) { pr.armedAtSeq = this.#armSeq++; pr.armedAtTs = now; } pr.armBlockReason = null; // the gate is satisfied and the plan arms ⇒ clear the arm-block latch this.#transition(pr, now, "armed"); } else if (pr.state === "armed" && !gate.ok) { // The gate flipped UNSATISFIED under an armed plan ⇒ de-arm back to authored, and latch WHY so // the frame renders `authored (blocked: )`, not a bare `authored` (kestrel-50w). pr.armBlockReason = gate.reason ?? "regime gate unsatisfied"; this.#transition(pr, now, "authored", { reason: gate.reason ?? "regime gate unsatisfied" }); } else if (pr.state === "authored" && !gate.ok) { // Still authored and UNABLE to arm (an UNKNOWN/mismatched regime gate — commonly a `regime {…}` // gate on a tape with no regime feed, which fail-closes to UNKNOWN forever). No lifecycle // transition fires (it was never armed to de-arm), so latch the engine's own reason HERE — this is // the arm-time gate-block the frame previously hid, letting an agent misread `authored` as a live, // filled position (the phantom-position trap). PURE (derived from the injected tags), bus untouched. pr.armBlockReason = gate.reason ?? "regime gate unsatisfied"; } } } /** * WHY an ARMED plan that placed ZERO orders is expiring at its ttl — the classified terminal reason * that replaces the bare `ttl` at the armed-expiry drop site (kestrel-y6vo / kestrel-kglw). Called ONLY * for a plan that armed and never fired (the caller's `pr.fired` guard has already excluded fired plans, * which expire through {@link #doTtlExpire}), so every branch here describes a never-fired outcome: * * 1. **budget-clamped** — every fire this session was refused because each entry leg is larger than the * plan budget ({@link #fire} latched {@link PlanRuntime.budgetRefusedReason}). * 2. **unwritten-series trigger** — the entry WHEN references a market series this tape never wrote a * value for (`hod`, `velocity(5m)`, a percentile baseline, …), so that operand reads UNKNOWN every * sweep and the trigger could NEVER have confirmed — dead on arrival. The unwritten operands are * NAMED (kestrel-y6vo). Checked BEFORE `whenEverDefinite` because a compound `A AND B` can resolve to * a definite `false` through a WRITTEN term (`spot crosses above hod`) while a second, UNWRITTEN term * (`velocity(5m) >= p95`) is the real reason it can never fire — the ungated hero example exactly * (kestrel-hk9u). Naming the unwritten operand is the actionable root cause; "never crossed" would * mask it. * 3. **trigger never CROSSED** — every operand IS written and the WHEN was evaluated to a definite * verdict (`whenEverDefinite === true`) but the condition was simply never met. A healthy untriggered * plan. (`whenEverDefinite === false` with no unwritten market operand ⇒ some other fail-closed * UNKNOWN, e.g. a baseline never warm — reported as `never resolved` rather than a false "crossed".) * * PURE: reads the injected provider + the plan's own latches, no wall clock/RNG (RUNTIME §0), so it * reconstructs identically on replay. The reason still carries the `ttl` token so a reader (and the * plans_expired digest, which keys off the `expired` OUTCOME, not the reason string) sees it expired. */ #neverFiredExpiryReason(pr: PlanRuntime, now: number, prefix = "ttl"): string { if (pr.budgetRefusedReason !== null) { return `${prefix} — never fired: every entry leg is larger than the plan budget (size down)`; } const unwritten = this.#unwrittenTriggerSeries(pr, now); if (unwritten.length > 0) { return `${prefix} — never fired: trigger references series ${unwritten.join(", ")} which this tape never writes — it could never confirm (dead on arrival)`; } if (!pr.whenEverDefinite) { return `${prefix} — never fired: trigger never resolved — an operand read UNKNOWN every sweep (fail-closed)`; } return `${prefix} — never fired: trigger never crossed (armed all session; the condition was evaluated but never met)`; } /** * De-arm every still-armed, never-fired plan whose entry WHEN spent the WHOLE session UNRESOLVABLE, * carrying the classified reason (kestrel-j43g). Called ONCE by the session driver at settle * (`SessionCore.settle`), with the injected settle instant as `now` — never a wall clock (RUNTIME §0), * so a replay reconstructs the identical terminal step. * * ## The hole this closes * {@link #neverFiredExpiryReason} — the y6vo/kglw classifier that names WHY a 0-order plan never fired * — had exactly ONE drop site: the ttl-expiry transition in {@link #reconcileArming}. But * {@link #ttlExpired} returns `false` outright when the plan authored no `ttl` (and for a `ttl-at` * whose calendar hook is absent), so a plan with no deadline could never reach it. Such a plan armed on * a series the tape never writes, read UNKNOWN on every sweep, placed zero orders, and ended the * session `armed` with NO reason on any lifecycle step and not one byte on any CLI surface — silent on * the text digest, the human report and `--json` alike. Fail-closed-SILENT: safe (it never fired) but * doctrine-violating, because the author cannot distinguish "waiting" from "impossible" (AGENTS.md: * unknown series ⇒ UNKNOWN ⇒ de-arm WITH A LOGGED REASON, never a silent default). * * ## Scope — the UNRESOLVABLE class only, never every armed plan * Only a plan whose WHEN could not have confirmed is de-armed: * - it names market series this tape never wrote (NAMED in the reason — the absent-series class), or * - it never once reached a definite verdict (`whenEverDefinite === false`) — UNKNOWN every sweep. * * A plan whose trigger WAS evaluated definite and simply never crossed is legitimately *waiting*: the * tape ended, not the plan's chance. De-arming it would be over-scope — it would rewrite the terminal * state of every ordinary idle plan (and every golden that carries one) to say something the engine * cannot prove. It is left exactly as it is today: `armed`, silent, byte-identical. * * The reason reuses the y6vo classifier verbatim under a `session end` prefix (never the misleading * `ttl` token — this plan has no ttl), so all three CLI faces surface it through the SAME per-plan * reason projection they already read, from this ONE emission. Pure w.r.t. the tape: it reads the * injected provider and the plan's own latches. */ deArmUnresolvedAtClose(now: number): void { for (const pr of this.#plans) { if (pr.done || pr.fired || pr.state !== "armed") continue; // NEVER-SWEPT guard: `whenEval` is minted on the plan's first fire-sweep, so a null evaluator means // the tape ended before this plan was ever evaluated (it armed on the final event). Nothing about // its trigger is knowable, so it stays armed and silent rather than being told it "never resolved" // — the notice must never claim more than the engine can prove. if (pr.whenEval === null) continue; const unresolvable = this.#unwrittenTriggerSeries(pr, now).length > 0 || !pr.whenEverDefinite; if (!unresolvable) continue; // definite-but-never-crossed ⇒ legitimately waiting, left untouched this.#finish(pr, now, "expired", this.#neverFiredExpiryReason(pr, now, "session end")); } } /** The distinct market-series operands of a plan's entry WHEN that this tape never wrote a value for — * they resolve UNKNOWN from the provider at settle (kestrel-y6vo). NAMES the dead-on-arrival cause so an * unwritten-series reason is concrete, not opaque. PURE (reads the injected provider). */ #unwrittenTriggerSeries(pr: PlanRuntime, now: number): string[] { const when = pr.plan.when; if (when === undefined) return []; const refs: SeriesRef[] = []; collectSeriesOperands(when, refs); const names: string[] = []; const seen = new Set(); for (const ref of refs) { if (!isUnknown(this.#provider.resolve(ref, now))) continue; // wrote a value ⇒ not the culprit const name = seriesDisplayName(ref); if (seen.has(name)) continue; seen.add(name); names.push(name); } return names; } #regimeSatisfied(pr: PlanRuntime): { ok: boolean; reason?: string } { const gate = pr.plan.regime; if (gate === undefined || gate.tags.length === 0) return { ok: true }; for (const b of gate.tags) { if (b.scope === "any" || b.value === "any") continue; // wildcard const cur = this.#tags.get(b.scope); if (cur === undefined) return { ok: false, reason: `regime ${b.scope} UNKNOWN` }; if (cur !== b.value) return { ok: false, reason: `regime ${b.scope}=${cur} != ${b.value}` }; } return { ok: true }; } // ── fire (with envelope + priority arbitration) ──────────────────────────── #fire(now: number): void { interface Candidate { readonly pr: PlanRuntime; readonly admitted: readonly { child: Omit; }[]; readonly premium: number; } const candidates: Candidate[] = []; for (const pr of this.#plans) { if (pr.state !== "armed" || pr.done || pr.fired) continue; if (pr.whenEval === null) pr.whenEval = this.#newEvaluator(); const when = pr.plan.when; this.#lastOrgUnresolved = null; const t = when === undefined ? true : pr.whenEval.evaluate(when, this.#provider, now); if (isUnknown(t) && this.#lastOrgUnresolved !== null) { // Fail closed LOUDLY (RUNTIME §8 / CONTEXT): an armed plan whose WHEN names a genuinely // unresolvable org path must NOT sit silently un-firing — it de-arms with a logged reason. // Surfaced as a WAKE inform (the PM channel) on the rising edge so it neither fires (closed) // nor oscillates, and it re-arms automatically once the org path resolves. if (pr.orgDeArmReason !== this.#lastOrgUnresolved) { pr.orgDeArmReason = this.#lastOrgUnresolved; this.#emitWake(now, pr.name, pr.instanceId, this.#lastOrgUnresolved); } continue; } pr.orgDeArmReason = null; // WHEN resolved (or decided true/false) ⇒ clear the de-arm latch // The entry WHEN reached a DEFINITE verdict this sweep (true OR false, not UNKNOWN) — latch it so the // ttl-expiry drop site can tell a healthy untriggered plan ("never CROSSED", whenEverDefinite=true) // apart from a plan whose trigger read UNKNOWN every sweep because an operand series this tape never // wrote ("never RESOLVED — dead on arrival", whenEverDefinite=false; kestrel-y6vo). Never cleared. if (!isUnknown(t)) pr.whenEverDefinite = true; if (t !== true) continue; const resolved = this.#resolveEntries(pr, now); if (resolved.admitted.length === 0) { // A SYNTHESIZED one-shot placeOrder that admits nothing is TERMINAL: de-arm loudly ONCE with the // reason (never a per-sweep storm — a WHEN-less one-shot has no future sweep that will make an // uncovered/unbuildable leg fire; kestrel-5zl.5 hygiene). Authored plans stay armed and retry — // an unbuildable leg (an option strike that may resolve later, or a spot quote still dark) can // become buildable on a later sweep now that the engine executes spot legs too (kestrel-20f.14; // the ADR-0017 terminal equity refusal this branch used to carry is gone). if (pr.synthesizedOrder && !pr.done) { this.#finish(pr, now, "expired", resolved.refusal ?? "order unfillable"); } else if (!pr.synthesizedOrder && resolved.refusal !== null && resolved.refusal.includes("exceeds plan budget")) { // An AUTHORED plan whose WHEN went true but whose every entry leg is larger than the plan budget // admits nothing and stays armed — the exact silent never-fired class the engine used to DROP // here (kestrel-kglw / kestrel-markets-a4ya: `#resolveEntries` computed `exceeds plan budget` // then this branch just `continue`d, so a budget-DOA authored plan sat armed with orders=[] and // no journal). Surface it LOUDLY ONCE (edge-latched via `budgetRefusedReason` so a WHEN that // stays true does not storm the log every sweep) as a PLAN reject carrying the `exceeds plan // budget` marker the engine-log allow-list already recognizes, and latch it so the ttl-expiry // drop site names the budget cause rather than a bare `ttl` (RUNTIME §8 — de-arms/refuses with a // logged reason, never a silent default). const reason = "order refused: exceeds plan budget — every entry leg is larger than the plan budget (size down)"; if (pr.budgetRefusedReason !== reason) { pr.budgetRefusedReason = reason; this.#emitReject(now, pr, reason); } } continue; // nothing buildable ⇒ stay armed (authored, retryable) / done (synthesized) } pr.budgetRefusedReason = null; // a leg admitted ⇒ the plan-budget clamp has lifted; clear the latch candidates.push({ pr, admitted: resolved.admitted, premium: resolved.premium }); } if (candidates.length === 0) return; // Arbitrate: higher priority wins the envelope; ties → earlier armed. candidates.sort((a, b) => { const pa = a.pr.plan.priority ?? 0; const pb = b.pr.plan.priority ?? 0; if (pa !== pb) return pb - pa; return (a.pr.armedAtSeq ?? 0) - (b.pr.armedAtSeq ?? 0); }); for (const c of candidates) { if (!this.#envelopeFits(c.pr.envelope, c.premium)) { // The bounded-risk envelope can't take this fire. When it's the BUDGET dimension that's // exhausted, surface it ONCE (edge-latched) as a PLAN refuse carrying the `exceeds plan // budget` marker — so the acting agent's engine log shows "fired order budget-clamped, size // down" instead of a plan silently stuck armed with orders=[] and no journal (kestrel-m9i.32.1; // the reason marker is what promotes it to a `rejected` engine action, simulate REFUSAL_MARKERS). // A pure concurrency-cap refusal (budget fits, maxConcurrent doesn't) stays silent here — out of // this bug's scope. The latch clears the instant the plan fits or fires, so an authored plan // whose WHEN stays true emits the notice once per edge, never a per-sweep storm. if (!this.#envelopeBudgetFits(c.pr.envelope, c.premium)) { const reason = "fired order refused: exceeds plan budget (bounded-risk envelope) — size down"; if (c.pr.envelopeRefuseReason !== reason) { c.pr.envelopeRefuseReason = reason; this.#emitReject(now, c.pr, reason); } } continue; // refused this tick; stays armed } c.pr.envelopeRefuseReason = null; // fits now ⇒ clear the refuse latch (re-emit if it recurs) this.#doFire(c.pr, c.admitted, c.premium, now); } } #resolveEntries( pr: PlanRuntime, now: number, ): { admitted: { child: Omit }[]; premium: number; refusal: string | null } { const budgetUsd = pr.plan.budget ? pr.plan.budget.value * this.#rUsd : Infinity; const admitted: { child: Omit }[] = []; let premium = 0; // The reason the LAST leg was turned away (uncovered / unbuildable / over-budget) — carried out so a // SYNTHESIZED one-shot that admits nothing can de-arm loudly ONCE with it (no per-sweep storm). let refusal: string | null = null; for (const entry of pr.entries) { const line: PriceLine = { price: entry.price, ...(entry.policy !== undefined ? { policy: entry.policy } : {}) }; for (const leg of entry.legs) { const built = this.#resolveLeg(pr, leg, line, now, undefined, undefined); if (built === null) { refusal = "order leg unresolvable (fail-closed)"; // RUNTIME §8 — a synthesized one-shot cannot build continue; // unresolvable leg ⇒ skip } // Never-naked boundary (F2 / ARCHITECTURE §6.1): a SELL entry/ALSO leg is legal ONLY as a COVERED // sell. A SYNTHESIZED placeOrder covers against the whole BOOK (the doctrine formula — it has no // authored plan structure to scope to); an AUTHORED plan keeps the plan-scoped slice (kestrel-22j.14, // must NOT be weakened globally). Uncovered ⇒ refuse; other legs in this DO still proceed. if (leg.side === "sell") { const covered = pr.synthesizedOrder ? this.#bookSellCovered(built.strike, built.right, built.qty) : this.#sellCovered(pr, built.strike, built.right, built.qty); if (!covered) { refusal = "uncovered sell refused: never naked"; // Authored plans: the loud per-sweep PLAN notice (unchanged; coverage may materialize later). // Synthesized one-shots: NO per-sweep notice — the caller de-arms terminally with this reason, // so the refusal is ONE loud record, not thousands (kestrel-5zl.5 hygiene / storm fix). if (!pr.synthesizedOrder) this.#emitReject(now, pr, refusal); continue; } } const legPremium = leg.side === "buy" ? built.qty * built.px * built.multiplier : 0; if (premium + legPremium > budgetUsd + EPS) { refusal = "order refused: exceeds plan budget"; continue; // plan budget clamp (RUNTIME §5) } premium += legPremium; admitted.push({ child: { role: "entry", side: leg.side, qty: built.qty, instrument: built.instrument, ...(built.strike !== undefined ? { strike: built.strike } : {}), ...(built.right !== undefined ? { right: built.right } : {}), px: built.px, ann: built.ann, placedAt: now, ...(built.priceLine !== undefined ? { priceLine: built.priceLine } : {}), ...(entry.policy?.cancelIf !== undefined ? { lineCancelIf: entry.policy.cancelIf } : {}), }, }); } } return { admitted, premium, refusal }; } #doFire( pr: PlanRuntime, admitted: readonly { child: Omit }[], premium: number, now: number, ): void { // Resolve the ARM basis (if any) once at fire, for basis-anchored TP/EXIT pricing. if (pr.armClause?.basis !== undefined && admitted[0] !== undefined) { const a = admitted[0].child; const r = this.#resolveExprPx(pr, pr.armClause.basis, a.side, a.instrument, a.strike, a.right, now); if (r !== null) pr.basisPx = r; } pr.fired = true; this.#transition(pr, now, "fired"); this.#commitEnvelope(pr.envelope); for (const a of admitted) this.#submit(pr, a.child, now); // Fire-then-inform: the WAKE lands in the SAME step as the submits (RUNTIME §5). this.#emitWake(now, pr.name, pr.instanceId, "fired"); this.#transition(pr, now, "managing"); } /** * Promote a PURE ADOPTER — an `ARM … foreach held leg` binding with NO fireable entry * (`entries.length === 0`) that took on adopted inventory — from `armed` to `managing` (kestrel-r866). * * WHY THIS EXISTS. The manage sweep runs only for `pr.fired` plans ({@link onEvent}: `if (pr.fired …)`), * and a plan with no `DO` entry never reaches the fire path ({@link #fire}/{@link #resolveEntries} find * nothing to admit). So an adopter whose whole job is to MANAGE a held leg (`ARM basis foreach held * leg` + `EXIT …`) armed and then sat inert forever: it adopted the leg, but its EXIT/TP were never * evaluated — no covered sell, no error (tracer-7 s7-run01: "the escape hatch is INERT on the day * handshake"). Promoting it here makes the adopted position actually manageable end-to-end. * * Mirrors {@link #doFire}'s tail (commit the concurrency slot the away owner released on its * `done(transferred)`, fire-then-inform WAKE, transition) MINUS the entry submits — there is nothing to * acquire, only inventory to manage. NOT gated on {@link #envelopeFits}: the adopted position already * exists (it was committed under the source), so refusing to manage it would orphan a live leg — the * envelope cap governs NEW acquisition, never management of inventory already on the book. * * A plan that DOES have entries reaches `managing` through the normal fire path and is untouched here. * Pure: injected `now`, no wall clock/RNG (RUNTIME §0). */ #beginAdoptedManagement(pr: PlanRuntime, now: number): void { pr.fired = true; this.#commitEnvelope(pr.envelope); this.#emitWake(now, pr.name, pr.instanceId, "adopted"); this.#transition(pr, now, "managing"); } // ── manage (fired/managing plans) ────────────────────────────────────────── #manage(pr: PlanRuntime, now: number): void { if (pr.done) return; // A FLATTENED plan (bd if0) has had its acquisition halted, its resting orders cancelled, and a // covered close crossed. It manages ONLY that close: once it is flat with nothing working (the close // filled), finish `done(expired, "flattened")` — its R is already freed (bd 1a8). Skip the authored // manage triggers (EXIT/RELOAD/TP) entirely so nothing re-posts against the position being closed. if (pr.flattened) { this.#maintainWorking(pr, now); // still esc/peg the close if it carries a policy if (!pr.done && this.#netHeldTotal(pr) <= EPS && this.#workingCount(pr) === 0) { this.#finish(pr, now, "expired", "flattened"); } return; } // RUNTIME §3: an unresolvable org path in ANY trigger — not just the entry WHEN — de-arms with a // logged reason, never silently. Reset the per-sweep stash here so every manage trigger below // (line/plan CANCEL-IF, INVALIDATE, EXIT, RELOAD) accumulates into it, then reconcile ONCE after // the trigger phase (#reconcileOrgDeArm) so the loud de-arm is edge-latched per plan, not // re-emitted per trigger or per sweep. (Manage triggers already fail CLOSED on UNKNOWN — they // only fire on `=== true` — so this adds the missing LOUDNESS, not a behavior change on fire.) this.#lastOrgUnresolved = null; // Line-level CANCEL-IF: cancel just the guarded unfilled child (RUNTIME §5). for (const child of pr.children) { if (child.filled || child.cancelled || child.lineCancelIf === undefined) continue; let ev = this.#childEvals.get(child.ref); if (ev === undefined) { ev = this.#newEvaluator(); this.#childEvals.set(child.ref, ev); } if (ev.evaluate(child.lineCancelIf, this.#provider, now) === true) this.#cancelChild(child); } // INVALIDATE: halt reloads + cancel unfilled children; inventory rides; NEVER sells. for (let i = 0; i < pr.invalidates.length; i++) { const inv = pr.invalidates[i]!; let ev = pr.invalidateEvals[i]; if (ev == null) { ev = this.#newEvaluator(); pr.invalidateEvals[i] = ev; } if (ev.evaluate(inv.when, this.#provider, now) === true) { this.#doInvalidate(pr, now); return; } } // CANCEL-IF (plan-level): halt acquisition + cancel unfilled entry/reload orders only. for (let i = 0; i < pr.cancelifs.length; i++) { const cif = pr.cancelifs[i]!; let ev = pr.cancelifEvals[i]; if (ev == null) { ev = this.#newEvaluator(); pr.cancelifEvals[i] = ev; } if (ev.evaluate(cif.when, this.#provider, now) === true) this.#doPlanCancelIf(pr, now); } // EXIT: latch an exit-INTENT on the trigger edge, then work it each tick until the held qty // is fully worked. Ordering is load-bearing (F1): build the sell price FIRST; only on a // successful build cancel the covering TP and submit — an unresolvable price retries next tick // with the TP still resting, so inventory is never left unprotected. for (let i = 0; i < pr.exits.length; i++) { if (pr.exitWorked[i]) continue; // intent already satisfied for this exit clause const exit = pr.exits[i]!; if (!pr.exitIntent[i]) { if (!this.#exitFired(pr, i, exit.when, now)) continue; // not triggered yet pr.exitIntent[i] = true; // latch on the edge; the intent now persists until worked } this.#workExit(pr, i, exit, now); } // Exit esc/STAND + working-order peg maintenance. this.#maintainWorking(pr, now); if (pr.done) return; // RE-ARM THE EXIT ON THE FLAT EDGE (kestrel-xxu2 — bounded-risk hole). An EXIT does NOT end the Plan // and does NOT halt RELOADs (there is no "flat ⇒ done" rule), so a Plan whose EXIT fired and filled // stays `managing` with live RELOAD clauses — and `exitWorked[i]`, once set in #workExit (:1827), used // to persist for the whole life of the plan instance, permanently short-circuiting the EXIT loop above // (`if (pr.exitWorked[i]) continue`). The re-entered inventory a later RELOAD acquired was therefore // UNSTOPPED: the very exit that FIRED and SAVED you went dead, while an exit that triggered while FLAT // (returns early at exitQty <= 0, never sets exitWorked) correctly kept protecting later inventory. The // strategist authored BOTH the EXIT and the RELOAD; the engine honors both for the WHOLE life of the // Plan, not just the first round trip. // // The flat edge — no inventory (`#netHeldTotal`, transferred-aware) and nothing working // (`#workingCount`) — is where a round trip is COMPLETE: the exit sell has filled and no TP/entry/reload // child is still resting. A STOOD exit leaves inventory HELD, so `netHeldTotal > 0` and this guard does // not trip — the intent stays satisfied for the qty it already worked, and no second sell is ever posted // against a lot the first one reserves. The per-clause rising-edge `exitEvals[i]` are DELIBERATELY NOT // reset: the clause re-triggers when its WHEN rises again, exactly as it did the first time — re-arming // without a fresh rising edge would re-fire the exit against inventory the next RELOAD has not acquired. if (this.#netHeldTotal(pr) <= EPS && this.#workingCount(pr) === 0) { for (let i = 0; i < pr.exits.length; i++) { if (!pr.exitWorked[i]) continue; pr.exitWorked[i] = false; pr.exitIntent[i] = false; } } // RELOAD rungs on adverse movement (rising edge), budget-checked, halted by INVALIDATE. for (let i = 0; i < pr.reloads.length; i++) { this.#maybeReload(pr, i, now); } // Every manage trigger has now been swept: if any named a genuinely-unresolvable org path, // surface the loud, edge-latched de-arm (RUNTIME §3/§8) so a non-entry trigger can never swallow // an org-UNKNOWN silently. this.#reconcileOrgDeArm(pr, now); // TP maintenance against filled inventory (never oversell; intrinsic-floored). this.#maintainTP(pr, now); // TTL for a fired plan: pull unfilled, inventory rides, done(expired). if (this.#ttlExpired(pr, now)) this.#doTtlExpire(pr, now); } #doInvalidate(pr: PlanRuntime, now: number): void { pr.reloadsHalted = true; pr.acquisitionHalted = true; for (const child of pr.children) if (!child.filled && !child.cancelled) this.#cancelChild(child); this.#finish(pr, now, "invalidated", "invalidate"); } #doPlanCancelIf(pr: PlanRuntime, now: number): void { if (pr.acquisitionHalted) return; // idempotent pr.acquisitionHalted = true; pr.reloadsHalted = true; for (const child of pr.children) { if (child.filled || child.cancelled) continue; if (child.role === "entry" || child.role === "reload") this.#cancelChild(child); } // No acquisition, no inventory, nothing working ⇒ the plan is spent. if (pr.filledBuyQty - pr.filledSellQty <= 0 && this.#workingCount(pr) === 0) { this.#finish(pr, now, "expired", "cancel-if"); } } /** * Has EXIT clause `i`'s condition fired at `now`? Three EXIT surfaces, one intent latch: * * - **`held-stop`** (`EXIT held 90m`) — a 0DTE TIME-HELD stop: fires once the position has been * held for the duration, measured from {@link PlanRuntime.firstAcquiredAt} (the first acquiring * fill). Nothing acquired yet ⇒ no hold-clock ⇒ never fires. This is BOOK state the generic * causal evaluator (tape-only) cannot see, so it is decided here (the evaluator reads UNKNOWN). * - **`clock-stop`** (`EXIT clockET 15:40`) — a 0DTE WALL-CLOCK stop: fires at/after the ET time, * read from the session calendar (`timeOfDayMinutes`); an absent calendar fails closed (never). * - any other trigger — the ordinary causal {@link TriggerEvaluator} (crosses/held/within/…). * * All three fire at the leg's own basis through the SAME {@link #workExit} path (never-naked / * covered-close preserved). Pure over the injected `now` — no wall clock / RNG (RUNTIME §0). */ #exitFired(pr: PlanRuntime, i: number, when: Trigger, now: number): boolean { if (when.kind === "held-stop") { if (pr.firstAcquiredAt === null) return false; // no position acquired ⇒ no hold-clock started return now - pr.firstAcquiredAt >= durationMs(when.dur.value, when.dur.unit) - EPS; } if (when.kind === "clock-stop") { const m = this.#provider.timeOfDayMinutes?.(now); if (m === undefined || isUnknown(m)) return false; // calendar absent ⇒ fail-closed, never fire return m >= when.at.hour * 60 + when.at.minute; // at-or-after the wall-clock ET time } let ev = pr.exitEvals[i]; if (ev == null) { ev = this.#newEvaluator(); pr.exitEvals[i] = ev; } return ev.evaluate(when, this.#provider, now) === true; } /** * Work an armed exit intent for one tick (F1). Correct ordering + retry semantics: * (a) compute the exit qty from the CURRENT net held (minus what is already being worked as an * exit sell); if `<= 0` keep the intent alive without acting — a momentarily-flat plan still * protects inventory it acquires later; * (b) BUILD the sell price FIRST — before touching the protective TP; an unresolvable price * returns here and retries next tick with the TP untouched (never abandon inventory); * (c) only on a successful build cancel the resting TP covering the exit qty and submit the * sell in the SAME step. The held qty is then fully worked ⇒ the intent is satisfied and esc * staging of the child proceeds through {@link maintainWorking}. */ #workExit(pr: PlanRuntime, i: number, exit: ExitClause, now: number): void { // (a) exit qty from CURRENT net held. Route through the transferred-aware net-held so a lot whose // management authority was handed off (kestrel-22j.14) never backs this protective/exit sell — // never-naked (kestrel-22j.16). Byte-identical to `filledBuyQty − filledSellQty` when nothing is // transferred; only subtracts a handed-off lot. Fully-transferred sources are `done` (unmanaged). const held = this.#netHeldTotal(pr); const exitQty = held - this.#exitingQty(pr); if (exitQty <= 0) return; // flat / already fully worked ⇒ hold the intent, protect later fills // (b) build the sell price FIRST — the TP stays resting until we know the sell is buildable. const price: PriceExpr = exit.price ?? { kind: "anchor", name: "fair" }; const line: PriceLine = { price, ...(exit.policy !== undefined ? { policy: exit.policy } : {}) }; // PER-LEG ALLOCATION (kestrel-h5nx) — the EXIT surface carried the SAME never-naked hole as // #maintainTP: it sized off the PLAN-TOTAL net-held while binding the sell to `children.find(c => // c.filled)` — the first filled child of ANY side. A multi-leg plan therefore emitted an EXIT sell // for the whole position NAMED FOR ONE LEG: a long straddle with an `EXIT` sold 2 calls against 1 // held → NET SHORT AN UNCOVERED 0DTE CALL. (No shipped multi-leg plan has an EXIT — the FOMC // straddle uses only TP, dip-fade uses INVALIDATE, breakout-chase is single-leg — so no fixture or // pinned floor was contaminated. But this is precisely the class of bug that silently pins into a // golden, which is why this PR exists.) The exit now allocates its quantity across the HELD legs, // each sell CAPPED BY and NAMED FOR the leg that covers it, and routed through #sellCovered. // // Resting TP sells do NOT reduce a leg's exit capacity — they are about to be cancelled to free // exactly that inventory (step (c), unchanged: the EXIT outranks the TP). Resting EXIT sells do. const legKeys = this.#heldLegKeys(pr); const alloc: { lk: HeldLeg; qty: number; built: BuiltLeg }[] = []; let remaining = exitQty; for (const lk of legKeys) { if (remaining <= EPS) break; const legAvail = this.#netHeldForLeg(pr, lk.strike, lk.right) - this.#restingNonTpSellForLeg(pr, lk.strike, lk.right); const take = Math.min(remaining, legAvail); if (take <= EPS) continue; // The exit sell mirrors the leg it covers: an option leg carries ITS OWN strike+right; a spot // leg (ADR-0017) carries neither, so the exit is an equity sell of the held shares. const leg: Leg = lk.strike === undefined ? { kind: "equity-leg", side: "sell", qty: take } : { kind: "leg", side: "sell", qty: take, strike: { kind: "strike-abs", strike: lk.strike }, right: lk.right ?? "C", }; const built = this.#resolveLeg(pr, leg, line, now, now, undefined); // Unresolvable ⇒ hold the WHOLE intent and touch NOTHING (the TPs stay resting): fail-closed, // retry next tick. Bailing here (rather than part-working) preserves the original contract. if (built === null) return; alloc.push({ lk, qty: take, built }); remaining -= take; } if (alloc.length === 0) return; // nothing coverable ⇒ hold the intent (never an uncovered sell) // (c) success: cancel the resting TPs covering the exit qty, then submit the sells — same step. let toFree = exitQty; for (const child of pr.children) { if (toFree <= 0) break; if (child.role === "tp" && !child.filled && !child.cancelled) { this.#cancelChild(child); toFree -= child.qty; } } for (const a of alloc) { // The never-naked boundary, now enforced on the EXIT surface too. `legAvail` already bounds // `qty`, so this can only trip on a future regression — fail-closed and logged, never silent. if (!this.#sellCovered(pr, a.lk.strike, a.lk.right, a.qty)) { this.#emitReject( now, pr, `EXIT sell of ${a.qty} at ${a.lk.strike ?? "spot"}${a.lk.right ?? ""} refused: uncovered (never naked, ADR-0017)`, ); continue; } this.#submit( pr, { role: "exit", side: "sell", qty: a.qty, instrument: a.built.instrument, ...(a.built.strike !== undefined ? { strike: a.built.strike } : {}), ...(a.built.right !== undefined ? { right: a.built.right } : {}), px: a.built.px, ann: a.built.ann, placedAt: now, ...(a.built.priceLine !== undefined ? { priceLine: a.built.priceLine } : {}), }, now, ); } this.#emitWake(now, pr.name, pr.instanceId, "exit"); // Only latch the intent as WORKED when the FULL held qty was covered. A partially-coverable exit // keeps the intent armed so the remainder is worked on a later tick (F1: protect later fills). if (remaining <= EPS) pr.exitWorked[i] = true; } #maintainWorking(pr: PlanRuntime, now: number): void { for (const child of [...pr.children]) { if (child.filled || child.cancelled || child.priceLine === undefined) continue; const policy = child.priceLine.policy; const escLen = policy?.esc?.length ?? 0; const hasEsc = escLen > 0; const isPeg = policy?.pricing === "peg"; if (!hasEsc && !isPeg) continue; // EXIT STAND: the order already worked the FINAL esc stage on a prior pass and is still // unfilled ⇒ ride to settle (RUNTIME §5). `escStage === escLen` is the terminal rung; it // is only set once the ladder has repriced INTO it, so the final price is genuinely worked // for at least one tick before we STAND. if (child.role === "exit" && hasEsc && child.escStage === escLen) { this.#cancelChild(child); pr.exitStood = true; this.#emitWake(now, pr.name, pr.instanceId, "exit-stand"); continue; } const res = this.#resolveChild(pr, child, now, { px: child.px, escStage: child.escStage }, isPeg); if (res === null || isUnresolvable(res)) continue; // hold on unresolvable if (res.repriceOf !== undefined) { // Bug 2 (reprice-after-fill): a child that FILLED or was cancelled between resolve and here // must never be replaced — the acquisition intent is already satisfied. Fills-before-sweep // (RUNTIME §7) makes a same-BOOK-event fill possible, so re-check live state right here. if (child.filled || child.cancelled) continue; // Bug 2 (target-qty cap): an entry/reload intent has a target qty. Once cumulative fills for // this exact leg reach it, place no more — pull the now-redundant resting child and STOP. // A partial fill sizes the replacement to the REMAINDER only (never re-buy the filled part). let repriceQty = child.qty; if (child.role === "entry" || child.role === "reload") { const remaining = child.qty - this.#filledQtyForLeg(pr, child.role, child.strike, child.right); if (remaining <= EPS) { this.#cancelChild(child); this.#emitReject(now, pr, "reprice dropped: entry intent already filled"); continue; } repriceQty = Math.min(child.qty, remaining); } // Bug 1 (cumulative per-plan budget at EVERY placement): a peg/esc reprice to a higher price // must re-satisfy `committed + new ≤ budget` — filled premium + open resting exposure, the // BOOK envelope + every ancestor re-checked the same way. A replacement that would breach is // NOT placed: keep the OLD resting child at its prior price/stage (never trade up into a // breach). A risk-reducing (down) reprice is always allowed. Pegging stops once budget is // fully consumed by fills. const mult = this.#specOf(child.instrument).multiplier; const oldExposure = child.side === "buy" ? child.qty * child.px * mult : 0; const newExposure = child.side === "buy" ? repriceQty * res.px * mult : 0; const delta = newExposure - oldExposure; if (newExposure > oldExposure + EPS) { const budgetUsd = pr.plan.budget ? pr.plan.budget.value * this.#rUsd : Infinity; const prospective = this.#committedPremium(pr) - oldExposure + newExposure; if (prospective > budgetUsd + EPS || !this.#envelopeBudgetFits(pr.envelope, delta)) { this.#emitReject(now, pr, "peg capped by budget"); continue; // keep the old resting child; do not advance the stage } } // Cancel/replace: pull the old resting order, rest a fresh one at the new price + stage, and // re-account the exposure delta against the plan + every ancestor envelope (Bug 1). this.#repriceCount += 1; // an actual reprice (peg drift or esc-stage boundary) — F6 this.#cancelChild(child); // The dollar reservation is DERIVED from the resting/held children (bd 1a8): re-resting the // child at `res.px` below updates it automatically — no envelope counter to adjust. this.#submit( pr, { role: child.role, side: child.side, qty: repriceQty, instrument: child.instrument, ...(child.strike !== undefined ? { strike: child.strike } : {}), ...(child.right !== undefined ? { right: child.right } : {}), px: res.px, ann: res.sourceAnnotation, placedAt: child.placedAt, // esc is absolute-from-placement (RUNTIME §4) priceLine: child.priceLine, ...(child.tpTier !== undefined ? { tpTier: child.tpTier } : {}), }, now, res.escStage, true, // reprice: the submission #repriceCount just tallied (peg drift / esc boundary) ); } else { // No cancel/replace — the resting order keeps its ref. Advance its esc rung in place and, // only on an actual change, emit a telemetry record so a report can recover the final rung // per order without a reprice being implied (esc advances are not reprices). if (res.escStage !== child.escStage) { child.escStage = res.escStage; this.#emitTelemetry(now, child.ref, res.escStage, false); } } } } #maybeReload(pr: PlanRuntime, i: number, now: number): void { const reload = pr.reloads[i]!; let cur: boolean; if (reload.when === undefined) { cur = pr.reloadRungs === 0; // no trigger ⇒ a single rung at first management pass } else { let ev = pr.reloadEvals[i]; if (ev == null) { ev = this.#newEvaluator(); pr.reloadEvals[i] = ev; } cur = ev.evaluate(reload.when, this.#provider, now) === true; } const rising = cur && !pr.reloadPrevTrue[i]; pr.reloadPrevTrue[i] = cur; if (!rising) return; if (pr.reloadsHalted || pr.acquisitionHalted) return; // halted by INVALIDATE / CANCEL-IF const budgetUsd = pr.plan.budget ? pr.plan.budget.value * this.#rUsd : Infinity; const line: PriceLine = { price: reload.price, ...(reload.policy !== undefined ? { policy: reload.policy } : {}), }; const specs: { child: Omit; premium: number }[] = []; let premium = 0; for (const leg of reload.legs) { const built = this.#resolveLeg(pr, leg, line, now, undefined, undefined); if (built === null) continue; // Never-naked boundary (F2): a reload SELL leg follows the same covered-sell rule as entries. if (leg.side === "sell" && !this.#sellCovered(pr, built.strike, built.right, built.qty)) { this.#emitReject(now, pr, "uncovered sell refused: never naked"); continue; } const legPremium = leg.side === "buy" ? built.qty * built.px * built.multiplier : 0; // Cumulative-with-fills budget stop (Bug 1): committed = filled premium + open resting exposure // of every live child, recomputed — a rung that would breach is clamped (RUNTIME §5/§8). if (this.#committedPremium(pr) + premium + legPremium > budgetUsd + EPS) continue; premium += legPremium; specs.push({ premium: legPremium, child: { role: "reload", side: leg.side, qty: built.qty, instrument: built.instrument, ...(built.strike !== undefined ? { strike: built.strike } : {}), ...(built.right !== undefined ? { right: built.right } : {}), px: built.px, ann: built.ann, placedAt: now, ...(built.priceLine !== undefined ? { priceLine: built.priceLine } : {}), ...(reload.policy?.cancelIf !== undefined ? { lineCancelIf: reload.policy.cancelIf } : {}), }, }); } if (specs.length === 0) return; // budget exhausted ⇒ no rung (clamped, RUNTIME §8) if (!this.#envelopeFits(pr.envelope, premium)) return; // ancestor envelope full this.#commitEnvelope(pr.envelope); pr.reloadRungs += 1; this.#ledger.setPlanRungs(pr.name, pr.reloadRungs); for (const s of specs) this.#submit(pr, s.child, now); // Fire-then-inform (F4): a reload submits rungs like an entry fires — emit a WAKE in the SAME // step it submits, so the reload's acquisition is never silent (RUNTIME §5). this.#emitWake(now, pr.name, pr.instanceId, "reload"); } /** * Maintain the TP ladder — **PER HELD LEG** (kestrel-m9i.41, the never-naked options hole). * * ## The bug this closes * The TP used to size off the PLAN-TOTAL net-held (`#netHeldTotal` / `pr.filledBuyQty`) while binding * the sell to a SINGLE leg — `children.find(c => c.filled && c.side === "buy")`, i.e. the FIRST filled * buy. On a one-leg plan those coincide, so it looked right for years. On a MULTI-leg plan they do not: * a long straddle (`buy 1 atm C, buy 1 atm P`) holds 2 contracts across 2 DISTINCT legs, so a `TP +50%` * computed `desired = 2` and sold **2 CALLS** while the plan held only 1 — driving it **NET SHORT an * uncovered 0DTE call** (unbounded upside risk) — and the PUT never got a TP at all. The plan-scoped * never-naked boundary ({@link PlanEngine.sellCovered}, F2/ARCHITECTURE §6.1) already existed and would * have refused exactly this, but the TP path never consulted it: ADR-0017's never-naked was enforced on * entry/reload/ALSO sells and on EQUITY legs, and silently skipped for TP-generated OPTION sells. * * ## The fix * Everything is now keyed to the leg: iterate the distinct HELD legs; per leg, size `desired` off THAT * leg's own filled BUY qty, clamp by THAT leg's own coverage (`netHeldForLeg − restingSellForLeg`), and * target off THAT leg's own basis (a straddle's call and put have different premiums — a blended * plan-total basis would aim `TP +50%` at 1.5× the *average* premium on both legs, which is neither). * Every TP sell is finally routed through {@link PlanEngine.sellCovered}. A refused oversell is logged, * never silent (F2/RUNTIME §8). {@link PlanEngine.workExit} carried the STRUCTURALLY IDENTICAL hole and * is fixed the same way — between them, every sell surface (entry / ALSO / RELOAD / flatten / TP / EXIT) * is now coverage-guarded. * * Single-leg plans are byte-identical (per-leg ≡ plan-total when there is one leg) — the whole existing * corpus of pinned floors is unchanged; only genuinely multi-leg plans move, and they move from * "naked short" to "covered". */ #maintainTP(pr: PlanRuntime, now: number): void { if (pr.tps.length === 0 || pr.flattened) return; // a flattened plan never re-posts its TP (bd if0) const legKeys = this.#heldLegKeys(pr); // The TP LADDER keeps its PLAN-TOTAL quantity semantics: `TP 2x frac 0.5` means "take profit on half // of MY POSITION", not "half of each leg" (a 2-leg ladder of 1 contract each would then floor to 0 and // never take profit at all). What changes is that the resulting quantity is ALLOCATED across the held // legs — each sell capped by, and named for, the leg that actually covers it. let available = this.#netHeldTotal(pr) - this.#restingSellQty(pr); if (available <= EPS) return; for (let tier = 0; tier < pr.tps.length; tier++) { if (available <= EPS) break; const tp = pr.tps[tier]!; const frac = tp.frac ?? 1; const desired = Math.floor(frac * pr.filledBuyQty + EPS); const placed = this.#tpPlacedForTier(pr, tier); let remaining = Math.min(desired - placed, available); if (remaining <= 0) continue; // Allocate this tier's quantity across the held legs, each capped by its OWN coverage // (net-held-of-leg − that leg's resting sells). A leg with no free inventory is skipped, so the // ladder walks onto the next held leg instead of overselling the first one. for (const lk of legKeys) { if (remaining <= EPS) break; const legAvail = this.#netHeldForLeg(pr, lk.strike, lk.right) - this.#restingSellForLeg(pr, lk.strike, lk.right); const take = Math.min(remaining, legAvail); if (take <= EPS) continue; // The never-naked boundary — now enforced on the TP surface too (the hole this fix closes). // Belt-and-braces: `legAvail` already bounds `take`, so this can only trip on a future regression. if (!this.#sellCovered(pr, lk.strike, lk.right, take)) { this.#emitReject( now, pr, `TP sell of ${take} at ${lk.strike ?? "spot"}${lk.right ?? ""} refused: uncovered (never naked, ADR-0017)`, ); continue; } const thresholdPx = this.#tpTargetPxForLeg(pr, tp, this.#legBasisOf(pr, lk.strike, lk.right), lk, now); if (thresholdPx === null) continue; let restPx = thresholdPx; if (tp.price !== undefined) { // Two-axis TP (kestrel-lic3). The public contract (`plan-tp`, docs/public/card.md / // status.md) reads "bank a fraction AT A MULTIPLE, RESTING AT a value anchor" — BOTH // operative: the target is the tier THRESHOLD (has the leg's value reached the multiple?) // and the authored `@` is the EXEC anchor the banked fraction rests at once it has (the // fade-ladder golden's "0.5 @ 2x resting fair"). Before this fix the `@` was parsed, // printed, and round-tripped but never read — the sell rested at mult×basis and the // authored exec price silently never reached the book (the AX charter's cardinal sin). // The threshold observable is `fair` — the value-anchor family's own definition of worth // (a quote is never a value); a dark/unbuildable fair holds the tier fail-closed and // retries next event, exactly like an unresolvable target (the `continue`s above/below). const valueNow = this.#resolveExprPx( pr, { kind: "anchor", name: "fair" }, "sell", lk.instrument, lk.strike, lk.right ?? "C", now, ); if (valueNow === null || valueNow < thresholdPx - EPS) continue; // tier not reached — hold const execPx = this.#resolveExprPx(pr, tp.price, "sell", lk.instrument, lk.strike, lk.right ?? "C", now); if (execPx === null) continue; restPx = execPx; } const line: PriceLine = { price: { kind: "price-abs", value: restPx }, ...(tp.policy !== undefined ? { policy: tp.policy } : {}), }; // The TP sell mirrors the leg it covers: a spot leg (no strike/right, ADR-0017) sells shares; an // option leg keeps its OWN strike/right — never another leg's. const sellLeg: Leg = lk.strike === undefined ? { kind: "equity-leg", side: "sell", qty: take } : { kind: "leg", side: "sell", qty: take, strike: { kind: "strike-abs", strike: lk.strike }, right: lk.right ?? "C", }; const built = this.#resolveLeg(pr, sellLeg, line, now, now, undefined); if (built === null) continue; this.#submit( pr, { role: "tp", side: "sell", qty: take, instrument: lk.instrument, ...(built.strike !== undefined ? { strike: built.strike } : {}), ...(built.right !== undefined ? { right: built.right } : {}), px: built.px, ann: built.ann, placedAt: now, tpTier: tier, ...(built.priceLine !== undefined ? { priceLine: built.priceLine } : {}), }, now, ); remaining -= take; available -= take; } } } /** * TTL expiry for a FIRED plan (RUNTIME §5: "at expiry the plan stops arming/firing and pulls * unfilled children; inventory rides"). TTL ends the plan's **acquisition** authority, NOT its * **management** — mirroring document supersession's explicit continued-management guarantee * exactly (§5: "a plan still holding inventory keeps managing it (TP/EXIT/INVALIDATE) under its old * record"). So: halt acquisition + reloads and cancel unfilled entry/reload children (resting * TP/EXIT ride), but a plan STILL HOLDING inventory keeps managing it under its own record — its * authored EXIT can still fire, and it stays a valid cross-plan adoption source (ARM-foreach-held- * leg, kestrel-22j.14). It finishes `done(expired, "ttl")` ONLY once flat with nothing working, * decided by the SAME survivor predicate supersede uses. Re-checked every tick — {@link #manage} * re-invokes this while ttl stays expired — so a later EXIT fill (or an inventory adoption that * hands the leg off) promotes it to done. Was: `#finish` unconditionally, which set a still-holding * plan `done` and orphaned its held inventory — permanently unmanageable, since #adoptInventory * excludes `done` sources (kestrel-22j.18). Flat/un-fired cases are byte-identical to before. */ #doTtlExpire(pr: PlanRuntime, now: number): void { pr.acquisitionHalted = true; pr.reloadsHalted = true; for (const child of pr.children) { if (child.filled || child.cancelled) continue; if (child.role === "entry" || child.role === "reload") this.#cancelChild(child); } if (!this.#survivesSupersede(pr)) { this.#finish(pr, now, "expired", "ttl"); } } // ── shared helpers ────────────────────────────────────────────────────────── #finish(pr: PlanRuntime, now: number, outcome: PlanOutcome, reason: string): void { if (pr.done) return; pr.done = true; if (pr.fired) this.#releaseEnvelope(pr.envelope); this.#transition(pr, now, "done", { outcome, reason }); } #cancelChild(child: ChildOrder): void { if (child.filled || child.cancelled) return; child.cancelled = true; this.#gate.cancel(child.ref); } #submit( pr: PlanRuntime, spec: Omit, now: number, escStage = 0, reprice = false, ): void { const ref = `o${++this.#orderSeq}`; // Directional-guard evidence (kestrel-9gu.6 / kestrel-vav2): author the leg's GuardedIntent HERE, // at submission, through the ONE owner (`guardIntent`, fill/guarded-intent) — passing the // decision-time exec-spot resolver as a thunk, so the owner resolves spot only when an option leg // actually needs classifying and the never-naked cap is decided behind the enforcing seam. The // resulting moneyness + covered-state thread onto the intent so the Session's FillOrder carries // them into the maker-fair directional guard (far-OTM standalone SELL cap, covered-wing exemption, // BUY crossing unlock — end to end). A SPOT leg (no strike/right, ADR-0017) has no moneyness — the // far-OTM guard is an option concept with no spot analogue, so the owner returns the symmetric // path (never a cap) and `#spotFor` is never dialed. const guardInput: GuardIntentInput = { side: spec.side, ...(spec.strike !== undefined ? { strike: spec.strike } : {}), ...(spec.right !== undefined ? { right: spec.right } : {}), resolveSpot: () => this.#spotFor(pr), }; const guard = guardIntent(guardInput); const intent: OrderIntent = { ref, plan: pr.name, plan_instance: pr.instanceId, role: spec.role, instrument: spec.instrument, side: spec.side, qty: spec.qty, ...(spec.strike !== undefined ? { strike: spec.strike } : {}), ...(spec.right !== undefined ? { right: spec.right } : {}), px: spec.px, sourceAnnotation: spec.ann, ...(guard.moneyness !== undefined ? { moneyness: guard.moneyness } : {}), ...(guard.covered !== undefined ? { covered: guard.covered } : {}), }; const returned = this.#gate.submit(intent); const child: ChildOrder = { ...spec, ref: returned, filled: false, filledQty: 0, filledCost: 0, cancelled: false, escStage }; pr.children.push(child); this.#childIndex.set(returned, { pr, child }); // Per-order telemetry (a57.9): record the esc-ladder rung this order rests at, and mark the // submission that a reprice produced. Observational only — the engine never reads it back — so // this emission joins the determinism hash but changes no decision. Emitted AFTER the gate // submit so it trails the order's `place` on the bus. this.#emitTelemetry(now, returned, escStage, reprice); // FAIL CLOSED (kestrel-9gu.6 / RUNTIME §8): an unresolvable-moneyness SELL had the conservative // far-OTM cap applied rather than silently bypassing the directional guard — surface it LOUDLY so // the owner sees the fallback (AUTONOMOUS MODE: apply the cap + log the fork). if (guard.unresolved) { this.#emitReject( now, pr, `fill-guard: moneyness unresolved for ${spec.side} ${spec.strike}${spec.right} — conservative far-OTM cap applied (fail-closed, kestrel-9gu.6)`, ); } // Guard evidence for the far-OTM wing (the ONLY wing the directional guard acts on): record the // GENERIC assessment inputs + cap decision on the bus so a Blotter documents the capped/exempt call // without any strategy-specific data (kestrel-9gu.6). Observational only (determinism-stable). if (guard.moneyness === "deep_otm") { this.#emitGuardTelemetry(now, returned, spec.side, guard.covered === true); } } #restingSellQty(pr: PlanRuntime): number { let q = 0; // Exclude handed-off lots' resting sells — symmetric with #restingSellForLeg (kestrel-22j.16): // a transferred lot is not this plan's resting authority, so it must not inflate `available`. for (const c of pr.children) if (c.side === "sell" && !c.filled && !c.cancelled && !c.transferred) q += c.qty; return q; } #exitingQty(pr: PlanRuntime): number { let q = 0; for (const c of pr.children) if (c.role === "exit" && !c.filled && !c.cancelled) q += c.qty; return q; } // ── never-naked coverage (F2 / ARCHITECTURE §6.1) ────────────────────────── /** Net long inventory of the EXACT leg (strike+right), in contracts, from filled quantity * (partial-fill aware — reads `filledQty`, not the all-or-nothing `filled` latch). A SPOT leg * passes `undefined` for both (ADR-0017): `undefined === undefined` nets spot children against * each other and never against an option leg — the instrument-keyed coverage that makes an * uncovered equity SELL (a naked short) refuse through the SAME never-naked boundary. */ #netHeldForLeg(pr: PlanRuntime, strike: number | undefined, right: Right | undefined): number { let q = 0; for (const c of pr.children) { // A `transferred` lot's management authority has been handed off to an ARM-bound replacement // (kestrel-22j.14): it no longer counts toward THIS plan's covered-sell authority. if (c.transferred) continue; if (c.strike !== strike || c.right !== right) continue; q += (c.side === "buy" ? 1 : -1) * c.filledQty; } return q; } /** Cumulative filled contracts for the EXACT leg + role — the target-qty cap that stops an entry/ * reload intent from acquiring past its target (Bug 2: a same-event fill can satisfy the intent * while a peg-chain sibling still rests). Partial-fill aware. */ #filledQtyForLeg(pr: PlanRuntime, role: OrderRole, strike: number | undefined, right: Right | undefined): number { let q = 0; for (const c of pr.children) { if (c.role === role && c.strike === strike && c.right === right) q += c.filledQty; } return q; } /** * The distinct HELD legs of a plan, in deterministic ACQUISITION order (the first filled BUY child * claims the slot) — the allocation order every multi-leg sell surface walks (kestrel-h5nx). * * NOTE (semantics worth knowing): the allocators that consume this are GREEDY in this order, so a * `frac < 1` TP on a symmetric multi-leg plan takes profit on the FIRST-ACQUIRED leg rather than * pro-rata across legs. That is harmless for everything shipped today (`TP +50%` carries no `frac`, * so `frac ?? 1` = the whole position, which allocates across every leg) and is strictly better than * the plan-total behavior it replaces — but a future pro-rata `frac` policy would slice here. */ #heldLegKeys(pr: PlanRuntime): HeldLeg[] { const out: HeldLeg[] = []; const seen = new Set(); for (const c of pr.children) { if (!c.filled || c.side !== "buy" || c.transferred) continue; const k = `${c.strike ?? ""}|${c.right ?? ""}`; if (seen.has(k)) continue; seen.add(k); out.push({ strike: c.strike, right: c.right, instrument: c.instrument }); } return out; } /** Contracts of the EXACT leg committed to resting sells that are NOT take-profits — i.e. the sells * an EXIT cannot free (kestrel-h5nx). A resting TP does not reduce a leg's exit capacity because the * EXIT outranks it and cancels it to free exactly that inventory; a resting EXIT sell does. */ #restingNonTpSellForLeg(pr: PlanRuntime, strike: number | undefined, right: Right | undefined): number { let q = 0; for (const c of pr.children) { if (c.transferred || c.role === "tp") continue; if (c.side === "sell" && !c.filled && !c.cancelled && c.strike === strike && c.right === right) q += c.qty; } return q; } /** Contracts of the EXACT leg already committed to resting (unfilled) sells. */ #restingSellForLeg(pr: PlanRuntime, strike: number | undefined, right: Right | undefined): number { let q = 0; for (const c of pr.children) { if (c.transferred) continue; // handed-off lot ⇒ not this plan's resting authority (kestrel-22j.14) if (c.side === "sell" && !c.filled && !c.cancelled && c.strike === strike && c.right === right) q += c.qty; } return q; } /** The never-naked boundary: an entry/reload/ALSO SELL leg is legal ONLY as a COVERED sell — * covered = net held of the exact leg minus sells already resting. `sellQty` within that is a * risk-reducing close (consumes $0 budget); anything beyond it is an uncovered short ⇒ refused. */ #sellCovered(pr: PlanRuntime, strike: number | undefined, right: Right | undefined, sellQty: number): boolean { const available = this.#netHeldForLeg(pr, strike, right) - this.#restingSellForLeg(pr, strike, right); return sellQty <= available + EPS; } // ── BOOK-level coverage — the synthesized-order (placeOrder) surface ONLY (kestrel-5zl.5) ────── // The doctrine formula (SURFACES "never-naked SELL boundary") reads `net held of that exact leg` — a // Book fact (positions are Book facts; management authority is Plan-scoped, kestrel-22j.14). Authored // plans keep the plan-scoped #sellCovered (that decision must NOT be weakened globally). A SYNTHESIZED // one-shot placeOrder, though, has no authored plan structure to scope to and no ARM-held handshake to // invoke, so its covered-sell authority is the Book's: net held of the exact leg across ALL plans minus // sells already committed to rest across ALL plans (RESERVATION — an authored TP/EXIT sell or an earlier // synthesized sell already reserves the lot, so two sells can never double-cover the same inventory). /** Book-wide net long inventory of the EXACT leg (strike+right), summed across every plan's filled * children (partial-fill aware; a transferred lot excluded — its authority moved, kestrel-22j.14). */ #bookNetHeldForLeg(strike: number | undefined, right: Right | undefined): number { let q = 0; for (const pr of this.#plans) { for (const c of pr.children) { if (c.transferred) continue; if (c.strike !== strike || c.right !== right) continue; q += (c.side === "buy" ? 1 : -1) * c.filledQty; } } return q; } /** Book-wide contracts of the EXACT leg already committed to unfilled (resting) sells across every * plan — the reservation that stops two sells double-covering the same held lot. */ #bookRestingSellForLeg(strike: number | undefined, right: Right | undefined): number { let q = 0; for (const pr of this.#plans) { for (const c of pr.children) { if (c.transferred) continue; if (c.side === "sell" && !c.filled && !c.cancelled && c.strike === strike && c.right === right) q += c.qty; } } return q; } /** The book-level never-naked boundary for a synthesized placeOrder SELL: covered = book net-held of * the exact leg minus book resting sells (unreserved inventory). `sellQty` within that is a * risk-reducing close; anything beyond it is an uncovered short ⇒ refused (SELL still floored at * intrinsic downstream in pricing.ts — this gate is quantity, not price). */ #bookSellCovered(strike: number | undefined, right: Right | undefined, sellQty: number): boolean { const available = this.#bookNetHeldForLeg(strike, right) - this.#bookRestingSellForLeg(strike, right); return sellQty <= available + EPS; } /** A loud PLAN reject notice — NOT a lifecycle transition (the plan keeps its current state), * but the refusal is on the audit trail so a never-naked skip is never silent (F2/RUNTIME §8). */ #emitReject(now: number, pr: PlanRuntime, reason: string): void { this.#bus.emit({ ts: now, stream: "PLAN", type: "lifecycle", plan: pr.name, plan_instance: pr.instanceId, state: pr.state, reason }); } #tpPlacedForTier(pr: PlanRuntime, tier: number): number { let q = 0; for (const c of pr.children) if (c.role === "tp" && c.tpTier === tier && !c.cancelled) q += c.qty; return q; } /** The average entry premium of the EXACT leg (`Σ filledCost / Σ filledQty` over its filled BUY * children) — the per-leg TP basis (kestrel-m9i.41). An explicit `basisPx` on the plan still wins (it * is an authored override). Falls back to the plan-total basis when the leg has no filled buy. */ #legBasisOf(pr: PlanRuntime, strike: number | undefined, right: Right | undefined): number | null { if (pr.basisPx !== null) return pr.basisPx; let cost = 0; let qty = 0; for (const c of pr.children) { if (c.side !== "buy" || c.transferred || c.strike !== strike || c.right !== right) continue; cost += c.filledCost; qty += c.filledQty; } return qty > EPS ? cost / qty : this.#basisOf(pr); } /** {@link PlanEngine.tpTargetPx}, resolved against a SPECIFIC held leg (kestrel-m9i.41) — a `tp-price` * expression on a multi-leg plan must price off the leg it will actually sell, not the first filled buy. */ #tpTargetPxForLeg( pr: PlanRuntime, tp: TpClause, basis: number | null, lk: { strike: number | undefined; right: Right | undefined; instrument: string }, now: number, ): number | null { switch (tp.target.kind) { case "tp-price": return this.#resolveExprPx(pr, tp.target.price, "sell", lk.instrument, lk.strike, lk.right ?? "C", now); case "tp-pct": return basis === null ? null : basis * (1 + tp.target.pct / 100); case "tp-mult": return basis === null ? null : basis * tp.target.mult; default: return null; } } #workingCount(pr: PlanRuntime): number { let n = 0; for (const c of pr.children) if (!c.filled && !c.cancelled) n += 1; return n; } #basisOf(pr: PlanRuntime): number | null { if (pr.basisPx !== null) return pr.basisPx; if (pr.filledBuyQty > 0) return pr.buyCostSum / pr.filledBuyQty; return null; } #tpTargetPx(pr: PlanRuntime, tp: TpClause, basis: number | null, now: number): number | null { switch (tp.target.kind) { case "tp-price": { const leg = pr.children.find((c) => c.filled && c.side === "buy"); // A held SPOT leg keeps strike/right undefined so the expression resolves through the // spot price ctx (ADR-0017); no filled leg yet keeps the option defaults (as before). return this.#resolveExprPx( pr, tp.target.price, "sell", leg?.instrument ?? this.#execFor(pr).symbol, leg === undefined ? 0 : leg.strike, leg === undefined ? "C" : leg.right, now, ); } case "tp-pct": return basis === null ? null : basis * (1 + tp.target.pct / 100); case "tp-mult": return basis === null ? null : basis * tp.target.mult; default: return null; } } // ── price resolution glue ───────────────────────────────────────────────── #resolveLeg( pr: PlanRuntime, leg: Leg, line: PriceLine, now: number, placedAt: number | undefined, prior: { px: number; escStage: number } | undefined, ): { instrument: string; strike?: number; right?: Right; qty: number; px: number; ann: string; multiplier: number; priceLine?: PriceLine } | null { // The leg's OWN `(symbol, expiry)`: a per-leg `exp` overrides the ambient `USING exec` tenor, // absent one it inherits it (kestrel-ih5h seam 1). Only `.symbol` names the instrument (bytes // unchanged); the expiry rides along to the book fetch inside the price ctx. const exec = this.#execForLeg(pr, leg); const instrument = exec.symbol; const spec = this.#specOf(instrument); const spot = this.#spotFor(pr); // ADR-0017 / kestrel-20f.14: an equity/spot leg (`buy 100 shares`) has no strike/right and // prices off the instrument's OWN two-sided quote (the SPOT tick's additive bid/ask): the spot // price ctx routes `@fair` to the quote-mid resolver (exec-fair-quote-v1) and refuses // `@intrinsic`/`@basis`; a dark/one-sided quote makes the line unresolvable (fail-closed — // the plan stays armed and retries, exactly like an unresolvable option strike). No fictional // strike is ever attached (ADR-0017 "considered and rejected"). if (leg.kind === "equity-leg") { const ctx = this.#spotPriceCtx(pr, leg.side, instrument, now, placedAt, prior, line); const res = resolvePrice(line, ctx); if (isUnresolvable(res)) return null; return { instrument, qty: leg.qty, px: res.px, ann: res.sourceAnnotation, multiplier: spec.multiplier, ...(line.policy?.pricing !== undefined || line.policy?.esc !== undefined ? { priceLine: line } : {}), }; } // A `Nd` DELTA-targeted strike (`buy 1 16d P`, kestrel-d6nk) is resolved to a CONCRETE quotable // strike HERE, where the leg's OWN book is reachable ({@link #bookFor}) — the vol surface that // {@link #resolveStrike} was never handed (it only receives spot + InstrumentSpec, which is exactly // why the arm was punted to a silent `return null`). Selection is UPSTREAM of the fill: the resolved // strike becomes a NORMAL single-leg order priced against that strike's real book quote, byte-for-byte // the atm/+N path. An UNRESOLVABLE band (degenerate/empty surface, absent book, no strike quoted for // the right) is surfaced LOUDLY (edge-latched reject), NEVER the pre-d6nk silent parse+arm+meter+0-fill. let strike: number; if (leg.strike.kind === "strike-delta") { const d = this.#resolveDeltaStrike(pr, instrument, leg.right, leg.strike.delta, spot, exec.expiry, now); if ("refused" in d) { if (pr.deltaRefusedReason !== d.refused) { pr.deltaRefusedReason = d.refused; this.#emitReject(now, pr, d.refused); } return null; // fail-closed AND loud: stays armed to retry a surface that may fill in later } pr.deltaRefusedReason = null; // the band resolved ⇒ clear the latch (re-emit if it recurs) strike = d.strike; } else { const s = this.#resolveStrike(leg.strike, spec, spot); if (s === null) return null; strike = s; } const ctx = this.#priceCtx(pr, leg.side, instrument, strike, leg.right, now, placedAt, prior, line, undefined, exec.expiry); const res = resolvePrice(line, ctx); if (isUnresolvable(res)) return null; return { instrument, strike, right: leg.right, qty: leg.qty, px: res.px, ann: res.sourceAnnotation, multiplier: spec.multiplier, ...(line.policy?.pricing !== undefined || line.policy?.esc !== undefined ? { priceLine: line } : {}), }; } #resolveChild( pr: PlanRuntime, child: ChildOrder, now: number, prior: { px: number; escStage: number }, isPeg: boolean, ) { if (child.priceLine === undefined) return null; // A spot child (no strike/right, ADR-0017) re-resolves through the spot price ctx — same // peg/esc machinery, quote-anchored anchors. const ctx = child.strike === undefined || child.right === undefined ? this.#spotPriceCtx(pr, child.side, child.instrument, now, child.placedAt, prior, child.priceLine, isPeg) : this.#priceCtx( pr, child.side, child.instrument, child.strike, child.right, now, child.placedAt, prior, child.priceLine, isPeg, ); return resolvePrice(child.priceLine, ctx); } #resolveExprPx( pr: PlanRuntime, expr: PriceExpr, side: "buy" | "sell", instrument: string, strike: number | undefined, right: Right | undefined, now: number, ): number | null { // No strike/right ⇒ a spot leg's expression (ADR-0017): resolve through the spot price ctx. const ctx = strike === undefined || right === undefined ? this.#spotPriceCtx(pr, side, instrument, now, now, undefined, { price: expr }) : this.#priceCtx(pr, side, instrument, strike, right, now, now, undefined, { price: expr }); const res = resolvePrice({ price: expr }, ctx); return isUnresolvable(res) ? null : res.px; } #priceCtx( pr: PlanRuntime, side: "buy" | "sell", instrument: string, strike: number, right: Right, now: number, placedAt: number | undefined, prior: { px: number; escStage: number } | undefined, line: PriceLine, isPeg?: boolean, /** The LEG's own expiry when it authored one (`buy 2 +1 C exp 0dte`); absent, the book is checked * against the plan's ambient `USING exec` tenor (kestrel-ih5h seam 1). */ legExpiry?: ExpirySelector, ): PriceCtx { const spec = this.#specOf(instrument); const spot = this.#spotFor(pr); // Through #bookFor: a book whose tenor CONTRADICTS the authored expiry is refused, and a refused // book quotes dark (bid/ask null) ⇒ the line is unresolvable ⇒ no fill. The authored expiry is a // gate on pricing now, not a dropped field. const book = this.#bookFor(pr, instrument, legExpiry); const quote = this.#quoteFor(book, strike, right); const intr = spot !== undefined ? intrinsic(spot, strike, right) : 0; const fairInput = this.#fairInputFor(book, spot, strike, right, now); const wantBucket = isPeg ?? line.policy?.pricing === "peg"; return { side, leg: { instrument, strike, right }, quote, fairInput, basis: pr.basisPx ?? this.#basisOf(pr), intrinsic: intr, tickSize: spec.tickSize, now, ...(placedAt !== undefined ? { placedAt } : {}), ...(prior !== undefined ? { prior } : {}), ...(wantBucket && this.#bucket !== undefined ? { tokenBucket: this.#bucket } : {}), }; } /** The price ctx for a SPOT/equity leg (ADR-0017, kestrel-20f.14): the quote is the * instrument's OWN NBBO (folded off the SPOT tick's additive bid/ask; dark when the tape * carries none), `instrumentKind: "spot"` routes `@fair` to the quote-mid resolver * (exec-fair-quote-v1) and refuses `@intrinsic`/`@basis`, and the SELL intrinsic floor * degenerates to 0 (a spot leg has no intrinsic — the floor is vacuous, never fictional). */ #spotPriceCtx( pr: PlanRuntime, side: "buy" | "sell", instrument: string, now: number, placedAt: number | undefined, prior: { px: number; escStage: number } | undefined, line: PriceLine, isPeg?: boolean, ): PriceCtx { const spec = this.#specOf(instrument); const q = this.#spotQuotes.get(instrument); const wantBucket = isPeg ?? line.policy?.pricing === "peg"; return { side, leg: { instrument }, instrumentKind: "spot", quote: q === undefined ? { bid: null, ask: null } : { bid: q.bid, ask: q.ask, last: q.last }, fairInput: null, // never Black-76 for a spot leg (ADR-0017); @fair routes via instrumentKind basis: pr.basisPx ?? this.#basisOf(pr), intrinsic: 0, // no strike ⇒ no intrinsic; the mandatory SELL floor degenerates to 0 (vacuous) // The `spot` PRICE anchor (ADR-0030 / kestrel-ipc) reads the canonical underlying — the SAME // value the `spot` SERIES reads; UNKNOWN ⇒ null ⇒ the line de-arms fail-closed (ven.4). spot: this.#canonicalSpot(now), tickSize: spec.tickSize, now, ...(placedAt !== undefined ? { placedAt } : {}), ...(prior !== undefined ? { prior } : {}), ...(wantBucket && this.#bucket !== undefined ? { tokenBucket: this.#bucket } : {}), }; } #quoteFor(book: BookState | undefined, strike: number, right: Right): OptionQuote { const found = book?.legs.find((l) => l.strike === strike && l.right === right); if (found !== undefined) return found; return { strike, right, bid: null, ask: null, bsz: null, asz: null }; } #fairInputFor( book: BookState | undefined, spot: number | undefined, strike: number, right: Right, now: number, ): ExecutionFairInput | null { if (this.#fairTau === undefined || spot === undefined || book === undefined) return null; // kestrel-wcnd: τ is resolved against the BOOK's own expiry — the contract being priced, never // this session's close. `null` (absent / unparseable / unresolvable tag) ⇒ `@fair` is // UNBUILDABLE ⇒ the caller's annotated book fallback names itself; never a same-day assumption. const tau = this.#fairTau(now, book.expiry); if (tau === null) return null; return { underlyingSpot: spot, strike, right, tauYears: tau, liquidQuotes: book.legs, asof: book.asof_ts }; } #resolveStrike(spec: StrikeSpec, inst: InstrumentSpec, spot: number | undefined): number | null { switch (spec.kind) { case "strike-abs": return spec.strike; case "strike-atm": return spot === undefined ? null : Math.round(spot / inst.strikeStep) * inst.strikeStep; case "strike-rel": { if (spot === undefined) return null; const atm = Math.round(spot / inst.strikeStep) * inst.strikeStep; return atm + spec.steps * inst.strikeStep; } case "strike-delta": // Handled UPSTREAM in {@link #resolveLeg} via {@link #resolveDeltaStrike}, which has the vol // surface this resolver was never handed (kestrel-d6nk). Defensive only — a delta spec never // reaches here; if one ever did, fail-closed rather than pick a fictional strike. return null; default: return null; } } /** * Resolve a `Nd` DELTA-targeted strike (`buy 1 16d P`, kestrel-d6nk) to a CONCRETE, quotable strike. * * `16d` means "the strike whose |Black-76 delta| is nearest 0.16" — the ONLY strike language options * traders actually reach for. It parsed, armed, printed, and METERED budget, but the runtime resolver * had a silent `return null` (delta-targeting was punted for want of the vol surface), so the DO leg * never became an order and every delta fire was a silent, billed 0-fill. This wires it end-to-end. * * Selection is UPSTREAM of the fill and fakes NO liquidity (not an ADR-0005 class fake): once the band * resolves to a strike, a NORMAL single-leg order prices against that strike's real book quote, exactly * as atm/+N does. The two machinery pieces are already in-repo, pure, and deterministic — * {@link greeks} (per-strike delta at r = 0) and {@link buildSurface} (the sparse liquid-strike IV * surface, read at each candidate by {@link interpIv}); the forward is DERIVED by parity * ({@link impliedForward}, ADR-0037 §2), never assumed equal to spot. * * DETERMINISM: buildSurface/greeks/interpIv are pure and the candidate strikes are iterated in ascending * order, so the ONLY tie-break that can move a byte is an EXACT-equal delta score — pinned to the LOWER * strike (kept by the strict `<` on ascending iteration), preserving byte-identical replay. * * FAIL-CLOSED AND LOUD: an unresolvable band (no book, τ unbuildable, an EMPTY surface — surface.ts's own * documented sparse/degenerate case near a 0DTE close — or no strike quoted for the leg's right) returns * a machine-nameable `refused` reason. The caller emits it as a loud, edge-latched PLAN reject; it is * NEVER a silent null. */ #resolveDeltaStrike( pr: PlanRuntime, instrument: string, right: Right, deltaPoints: number, spot: number | undefined, legExpiry: ExpirySelector | undefined, now: number, ): { strike: number } | { refused: string } { // `16d` ⇒ target |delta| = 0.16 (delta quoted in points 0..100, the trader convention the parser reads). const target = Math.abs(deltaPoints) / 100; if (spot === undefined) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — underlying spot unknown (fail-closed)` }; } const book = this.#bookFor(pr, instrument, legExpiry); if (book === undefined) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — no option book at the authored tenor (fail-closed)` }; } if (this.#fairTau === undefined) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — no τ provider wired for the vol surface (fail-closed)` }; } const tau = this.#fairTau(now, book.expiry); if (tau === null) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — τ to expiry unbuildable (fail-closed)` }; } // The forward is DERIVED from the option market by parity (ADR-0037 §2), used for BOTH the IV inversion // and the per-strike delta — never assumed equal to spot. const fwd = impliedForward(book.legs, spot); const surface = buildSurface(book.legs, fwd.forward, tau); if (surface.length === 0) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — the vol surface is empty (no liquid two-sided strikes); the ${deltaPoints}Δ band has no admissible strike (fail-closed)` }; } // Candidate strikes: those QUOTED for this leg's right in the book — a delta selection must land on a // real, quotable strike so the follow-on order fills against a real quote, never a synthesized one. // Deduped and sorted ascending so the tie-break (lower strike) is a deterministic function of the book. const strikes = [...new Set(book.legs.filter((l) => l.right === right).map((l) => l.strike))].sort((a, b) => a - b); if (strikes.length === 0) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — no ${right} strike is quoted in the book; the ${deltaPoints}Δ band has no admissible strike (fail-closed)` }; } let best: number | null = null; let bestScore = Infinity; for (const k of strikes) { const iv = interpIv(surface, k); if (iv === null) continue; // defensive: surface non-empty ⇒ interpIv is total, but keep fail-closed const d = greeks({ forward: fwd.forward, strike: k, tauYears: tau, sigma: iv, right }).delta; const score = Math.abs(Math.abs(d) - target); // Strict `<` on ASCENDING iteration ⇒ an exact-equal score keeps the earlier (LOWER) strike: the // pinned tie-break the replay invariant needs. if (score < bestScore) { bestScore = score; best = k; } } if (best === null) { return { refused: `delta strike unresolvable: ${deltaPoints}Δ ${right} on ${instrument} — no candidate strike produced a delta; the ${deltaPoints}Δ band has no admissible strike (fail-closed)` }; } return { strike: best }; } /** The execution instrument a plan trades: the `(symbol, expiry)` PAIR the author bound with * `USING exec SPY 0dte`, not the bare symbol (kestrel-ih5h seam 1). It used to return the symbol * alone, which silently discarded `using.exec.expiry` — a plan that named its tenor and a plan that * did not were indistinguishable downstream, so a book of the WRONG tenor priced the plan with no * one the wiser. The expiry now travels with the symbol to {@link #bookFor}, the single place a book * is fetched, which refuses a contradiction instead of pricing through it. * * `.symbol` is byte-identical to the old return value, so every event field keyed off it (a * `ChildOrder.instrument`, an {@link InstrumentSpec} lookup) is unchanged. */ #execFor(pr: PlanRuntime): ExecRef { const exec = pr.plan.using?.exec; const symbol = exec?.symbol ?? this.#execDefault ?? ""; return exec?.expiry === undefined ? { symbol } : { symbol, expiry: exec.expiry }; } /** The execution instrument for ONE leg: a per-leg `exp` (`buy 2 +1 C exp 0dte`, kestrel-ih5h seam 1) * OVERRIDES the ambient `USING exec` tenor; a leg with no `exp` inherits it. This is what keeps the * per-leg syntax from being inert — an authored leg expiry reaches the book fetch and is checked. */ #execForLeg(pr: PlanRuntime, leg: Leg): ExecRef { const exec = this.#execFor(pr); if (leg.kind === "equity-leg" || leg.expiry === undefined) return exec; return { symbol: exec.symbol, expiry: leg.expiry }; } /** Fetch the option book for an execution instrument, refusing one whose tenor CONTRADICTS the * authored expiry (fail-closed: a refused book reads as "no quote" ⇒ the price line is unresolvable * ⇒ the plan stays armed and retries, exactly as it does for a dark quote — never a fill against the * wrong tenor). * * The `#books` map is keyed by `(symbol, expiry)` (kestrel-ih5h.2), so two tenors of one underlier * COEXIST — a calendar's near and far books both survive rather than latest-wins clobbering. Among * the books held under `instrument`, this returns the one whose OWN tenor the authored selector * ADMITS: an absolute-date `want` admits exactly its date's book, so a composite-key MISS (the * authored date has no book) returns `undefined` — fail-closed, the price line reads dark and the * plan stays armed, NEVER a fill against a wrong-tenor book. An undefined/relative `want` admits * every tenor (the calendar residue {@link expiryAdmits} leaves open, decided by the clock-owning * seam); ties break to the LATEST fold by `asof_seq`, reproducing the pre-composite latest-wins * behavior byte-for-byte on a single-tenor tape. */ #bookFor(pr: PlanRuntime, instrument: string, legExpiry?: ExpirySelector): BookState | undefined { const byExpiry = this.#books.get(instrument); if (byExpiry === undefined) return undefined; const want = legExpiry ?? this.#execFor(pr).expiry; let best: BookState | undefined; for (const b of byExpiry.values()) { if (!expiryAdmits(want, b.expiry)) continue; if (best === undefined || b.asof_seq > best.asof_seq) best = b; } return best; } /** The canonical underlying spot as the `spot` SERIES sees it (`CanonicalState.spot`), resolved * through the SAME composed-provider seam the trigger sweep reads — so the `spot` PRICE anchor * and the `spot` SERIES can never report two truths. UNKNOWN (a gapped/dead feed) or a * non-numeric resolution ⇒ `null` ⇒ the `spot` anchor is unresolvable and the line de-arms * fail-closed (ven.4 / ADR-0030 / kestrel-ipc). */ #canonicalSpot(now: number): number | null { const r = this.#provider.resolve(SPOT_SERIES_REF, now); return isUnknown(r) || typeof r !== "number" ? null : r; } #spotFor(pr: PlanRuntime): number | undefined { const exec = this.#execFor(pr).symbol; // Through #bookFor, not #books directly: a book whose tenor contradicts the authored expiry must // not supply the underlier context either — it falls through to the ledger's spot, exactly as an // absent book does. const book = this.#bookFor(pr, exec); if (book !== undefined) return book.underlier_px; const s = this.#ledger.spotOf(exec); if (s !== undefined) return s; return this.#signalDefault !== undefined ? this.#ledger.spotOf(this.#signalDefault) : undefined; } #specOf(symbol: string): InstrumentSpec { return this.#instruments.get(symbol) ?? { symbol, ...DEFAULT_SPEC }; } // ── TTL ───────────────────────────────────────────────────────────────────── #ttlExpired(pr: PlanRuntime, now: number): boolean { const ttl = pr.plan.ttl; if (ttl === undefined) return false; if (ttl.kind === "ttl-rel") { if (pr.armedAtTs === null) return false; return now - pr.armedAtTs >= durationMs(ttl.dur.value, ttl.dur.unit) - EPS; } // ttl-at: compare against the calendar's minute-of-day (provider hook); absent ⇒ never. const mod = this.#provider.timeOfDayMinutes?.(now); if (mod === undefined || isUnknown(mod)) return false; return mod >= ttl.at.hour * 60 + ttl.at.minute; } // ── cumulative committed premium (Bug 1: the never-trade-up-into-a-breach invariant) ─────── /** * The plan's cumulative committed premium (RUNTIME §5): **filled premium + open resting exposure** * of every LIVE (not-cancelled) BUY child, in dollars (`qty × px × multiplier`). SELLs are covered * / risk-reducing and consume no premium budget (consistent with the fire/reload legPremium rule). * Recomputed from the children so a peg/esc reprice can never desync it — the budget check at every * placement reads THIS, not a counter that only the first resolution updated (the v1 stress bug). */ #committedPremium(pr: PlanRuntime): number { // OPEN capital-at-risk (bd 1a8 — open-exposure reservation, NOT gross turnover). Two terms: // • every RESTING (unfilled) BUY child's notional — acquisition capital already committed to rest; // • the cost-basis value of the NET-HELD long (`netHeld × basis × mult`). // A SELL that CLOSES the position drops `netHeld`, so a round-trip TO FLAT drives this to $0 — the // reservation is FREED and the day-trader may re-enter with zero open exposure. SELLs consume no // premium (covered / risk-reducing), and a handed-off (`transferred`) lot belongs to its adopter, not // here (symmetric with the never-naked accounting, kestrel-22j.14). PURE — a function of current // children + recorded fills, no clock/RNG — so the derived reservation cannot drift and two replays // agree byte-for-byte (RUNTIME §0). Equals the prior gross-buy sum whenever nothing has been sold. let usd = 0; for (const c of pr.children) { if (c.side !== "buy" || c.cancelled || c.transferred || c.filled) continue; // Only the STILL-RESTING remainder is fresh acquisition capital: a PARTIALLY-filled child // (0 < filledQty < qty, so `filled` has not yet latched) has its filled portion ALREADY counted // in `#netHeldTotal` below — net it out here or the filled slice is reserved twice (Finding 1). usd += (c.qty - c.filledQty) * c.px * this.#specOf(c.instrument).multiplier; // resting remainder } const netHeld = this.#netHeldTotal(pr); if (netHeld > EPS) { const basis = this.#basisOf(pr) ?? 0; const held0 = pr.children.find((c) => c.filled && c.side === "buy"); usd += netHeld * basis * this.#specOf(held0?.instrument ?? this.#execFor(pr).symbol).multiplier; } return usd; } // ── envelope arithmetic ─────────────────────────────────────────────────── /** The OPEN-EXPOSURE dollars committed against an envelope (bd 1a8): `Σ #committedPremium(pr)` over * every LIVE (not-done) plan whose envelope chain passes through `env` (self-or-ancestor). DERIVED — * recomputed on demand from current positions/resting orders, never an incrementally-maintained counter * — so a round-trip TO FLAT releases the reservation with nothing to drift, and two replays of the same * bus agree byte-for-byte (RUNTIME §0). A `done` plan has released (its inventory, if any, rode off under * a superseded/ttl/invalidated record — matching the prior release-on-finish behavior). */ #envelopeExposureUsd(env: Envelope): number { let usd = 0; for (const pr of this.#plans) { if (pr.done) continue; if (!this.#envelopeInSubtree(pr.envelope, env)) continue; usd += this.#committedPremium(pr); } return usd; } /** Is `target` a self-or-ancestor of the leaf envelope `leaf`? (Walk the parent chain.) */ #envelopeInSubtree(leaf: Envelope, target: Envelope): boolean { for (let e: Envelope | null = leaf; e !== null; e = e.parent) if (e === target) return true; return false; } #envelopeFits(env: Envelope, premium: number): boolean { for (let e: Envelope | null = env; e !== null; e = e.parent) { if (e.totalBudgetUsd !== null && this.#envelopeExposureUsd(e) + premium > e.totalBudgetUsd + EPS) return false; if (e.maxConcurrent !== null && e.firedCount + 1 > e.maxConcurrent) return false; } return true; } /** Does an EXPOSURE delta (a peg/esc reprice, not a new fire) fit every ancestor budget? Unlike * {@link #envelopeFits} this checks only the budget dimension — a reprice adds no new concurrent * position, so `maxConcurrent` is not re-tested (Bug 1: BOOK envelope re-checked at every placement). */ #envelopeBudgetFits(env: Envelope, delta: number): boolean { for (let e: Envelope | null = env; e !== null; e = e.parent) { if (e.totalBudgetUsd !== null && this.#envelopeExposureUsd(e) + delta > e.totalBudgetUsd + EPS) return false; } return true; } /** Fire/reload commit: bump the CONCURRENCY count up the chain (the dollar dimension is DERIVED by * {@link #envelopeExposureUsd} from the plan's own resting/held children — no counter to increment). */ #commitEnvelope(env: Envelope): void { for (let e: Envelope | null = env; e !== null; e = e.parent) e.firedCount += 1; } /** Finish release: drop the CONCURRENCY count up the chain. The dollar reservation is released * automatically the moment the plan goes flat (or `done`) because it is derived, not stored. */ #releaseEnvelope(env: Envelope): void { for (let e: Envelope | null = env; e !== null; e = e.parent) e.firedCount -= 1; } #buildEnvelopes(mod: Module, synthesized = false): Envelope { const books: { book: import("../lang/index.ts").BookStatement; chainTop: Envelope | null }[] = []; const mkEnv = (name: string, totalBudgetUsd: number | null, maxConcurrent: number | null, parent: Envelope | null): Envelope => { const e: Envelope = { name, totalBudgetUsd, maxConcurrent, parent, firedCount: 0 }; this.#envelopes.push(e); return e; }; const rBudget = (risk: readonly RiskLine[]): number | null => { const line = risk.find((r) => r.threshold.unit === "R"); return line !== undefined ? line.threshold.value * this.#rUsd : null; }; const walkPod = (pod: import("../lang/index.ts").PodStatement, parent: Envelope | null): void => { const env = mkEnv(pod.name, rBudget(pod.risk), null, parent); for (const child of pod.children) { if (child.kind === "book") { const bEnv = mkEnv(child.name, child.budget ? child.budget.value * this.#rUsd : null, child.arbitration?.concurrency ?? null, env); books.push({ book: child, chainTop: bEnv }); } else { walkPod(child, env); } } }; for (const st of mod.statements) { if (st.kind === "pod") walkPod(st, null); else if (st.kind === "book") { const bEnv = mkEnv(st.name, st.budget ? st.budget.value * this.#rUsd : null, st.arbitration?.concurrency ?? null, null); books.push({ book: st, chainTop: bEnv }); } } if (books.length > 0 && books[0] !== undefined) return books[0].chainTop!; // No book: attach plans to the first pod envelope (its author-stated RISK bound), else the engine-minted // `default` (chain of one). kestrel-eywk — THE AUTHOR-STATED BOUND: the old fallthrough minted // `mkEnv("default", null, null, null)` — null budget, null concurrency, an envelope that bounds NOTHING. // A synthesized agent order (`placeOrder`) carries no POD/BOOK of its own and inherits this first // envelope, so each order was capped at its own `budget 1R` but the COUNT was capped by nothing — five // re-entries against a bare PLAN, all five admitted, exposure unbounded. The engine invents no bound; it // READS the one the author already wrote: Σ of the standing document's own `budget NR` lines. `null` // (still unbounded) survives ONLY when a plan states no budget at all — declining to state a bound is the // author's choice, and that is the one honest case the synthesis site fail-closes on // (`synthesisEnvelopeBounded`). The envelope is minted once at first arm and reused across arms by // IDENTITY: an AUTHORED arm RESTATES the bound (a supersede that raises `budget` raises the envelope); // a SYNTHESIZED one-shot INHERITS it and can never widen it — its `budget 1R` is a per-order cap, not // a grant of new authority. An author-minted POD envelope is never restated: its RISK bound is the one // its author wrote, and clobbering it with a plan-budget sum would be cross-document. const firstPod = this.#envelopes[0]; if (firstPod !== undefined && firstPod !== this.#defaultEnvelope) return firstPod; if (this.#defaultEnvelope !== null) { if (!synthesized) this.#defaultEnvelope.totalBudgetUsd = this.#authoredBudgetUsd(mod); // the standing document restates its own bound return this.#defaultEnvelope; } this.#defaultEnvelope = mkEnv("default", this.#authoredBudgetUsd(mod), null, null); return this.#defaultEnvelope; } /** Σ of the document's OWN `budget NR` lines, in dollars — the total authority the author WROTE. `null` * (unbounded) when any plan declines to state a budget, or the document states none at all: the engine * reports what the author said and NEVER fabricates a bound where none was stated (kestrel-eywk). */ #authoredBudgetUsd(mod: Module): number | null { let sum = 0; for (const st of mod.statements) { if (st.kind !== "plan") continue; if (st.budget === undefined) return null; // an unbudgeted plan ⇒ the author stated no total sum += st.budget.value * this.#rUsd; } return sum > 0 ? sum : null; } /** * FAIL-CLOSED SYNTHESIS GUARD (kestrel-eywk). Would a synthesized one-shot — an agent `placeOrder` — * inherit a BOUNDED risk envelope? A synthesized module carries no POD/BOOK, so {@link #buildEnvelopes} * hands it the first envelope this engine ever minted (the standing document's), which now carries the * author's OWN stated budget — so the answer is normally YES and the order is admitted, bounded by the * author's number, not one the engine made up. It is NO in exactly one case: the standing document states * no budget ANYWHERE (no POD `RISK`, no BOOK `budget`, at least one plan with no `budget`) — there is then * genuinely no authority to read, and the engine will not manufacture one; the synthesis site refuses that * one order, logged, book intact. With NOTHING standing yet the order's OWN `budget` becomes the bound, so * the production agent-order path (an agent that never arms at OPEN) stays alive AND bounded. */ synthesisEnvelopeBounded(): boolean { const inherited = this.#envelopes[0]; if (inherited === undefined) return true; // nothing standing — the order's own `budget NR` is the bound for (let e: Envelope | null = inherited; e !== null; e = e.parent) { if (e.totalBudgetUsd !== null || e.maxConcurrent !== null) return true; } return false; } // ── emit ─────────────────────────────────────────────────────────────────── #transition(pr: PlanRuntime, now: number, state: PlanState, opts: { outcome?: PlanOutcome; reason?: string } = {}): void { pr.state = state; // The org fact `plan(x).state` reflects the current lifecycle; on `done` it exposes the // terminal outcome (`filled`/`expired`/`invalidated`) so a sibling can gate on it. this.#ledger.setPlanState(pr.name, state === "done" && opts.outcome !== undefined ? opts.outcome : state); // Stamping a plan's lifecycle is the authorized write that makes `plan(x).state` a live org fact — // register it implicitly (cza.1 seam), source-stamped to the plan. Idempotent, fail-closed. this.#registry.noteOrgWrite([{ name: "plan", selector: { kind: "sel-name", name: pr.name } }, { name: "state" }], pr.name); this.#emitPlan(now, pr.name, pr.instanceId, state, opts); } #emitPlan(now: number, plan: string, planInstance: string, state: PlanState, opts: { outcome?: PlanOutcome; reason?: string } = {}): void { this.#bus.emit({ ts: now, stream: "PLAN", type: "lifecycle", plan, plan_instance: planInstance, state, ...(opts.outcome !== undefined ? { outcome: opts.outcome } : {}), ...(opts.reason !== undefined ? { reason: opts.reason } : {}), }); } #emitWake(now: number, wake: string, planInstance: string, reason: string): void { this.#bus.emit({ ts: now, stream: "WAKE", type: "wake", wake, plan_instance: planInstance, reason }); } /** Reconcile a fired plan's org-path de-arm after its manage triggers have been swept for the * tick. Symmetric with the entry-WHEN de-arm in {@link #fire}: if some manage trigger named a * genuinely-unresolvable org path this sweep (`#lastOrgUnresolved` set by the org read view), * inform it once on the rising edge (edge-latched via `pr.orgDeArmReason`) so risk is never left on * with a silently-non-firing exit/cancel/invalidate/reload; clear the latch the sweep it resolves * again so it re-arms. Deterministic (first unresolvable path per sweep wins). */ #reconcileOrgDeArm(pr: PlanRuntime, now: number): void { if (this.#lastOrgUnresolved !== null) { if (pr.orgDeArmReason !== this.#lastOrgUnresolved) { pr.orgDeArmReason = this.#lastOrgUnresolved; this.#emitWake(now, pr.name, pr.instanceId, this.#lastOrgUnresolved); } } else { pr.orgDeArmReason = null; } } /** Emit one per-order TELEMETRY record (a57.9) — the esc-ladder rung an order rests at, plus a * `reprice` marker on the cancel/replace submission that produced it. Observational only: the * engine never reads telemetry back, so this rides the emitted stream (joining the determinism * hash, stable on replay) without touching any order/fill/plan decision. A report folds this * stream into `orders[].esc_stages` (last rung per ref) and `session.reprice_count` (count of * `reprice`), so reports consume the bus instead of scraping engine internals. */ #emitTelemetry(now: number, orderId: string, escStage: number, reprice: boolean): void { this.#bus.emit({ ts: now, stream: "TELEMETRY", type: "order", order_id: orderId, esc_stage: escStage, reprice }); } /** Emit one per-order fill-guard TELEMETRY record (kestrel-9gu.6) for a leg the engine submitted in * the far-OTM wing — the GENERIC assessment inputs (side / moneyness bucket = `deep_otm` / covered) * and the directional cap decision they imply. `applied` = a standalone far-OTM SELL floored to the * no-fill cap; `exempt` = a covering wing left uncapped; `na` = a far-OTM BUY (unlocked, not capped). * Derived PURELY from side × covered (moneyness is `deep_otm` by construction here), so it matches the * fill model's own `directionalPfill` branch exactly (the engine threads the identical inputs). * Observational only — the engine never reads telemetry back — and it leaks no strategy-specific data * (no calibration constants / private thresholds), so it rides the emitted stream provenance-clean. */ #emitGuardTelemetry(now: number, orderId: string, side: "buy" | "sell", covered: boolean): void { const directional_cap = side === "sell" ? (covered ? "exempt" : "applied") : "na"; this.#bus.emit({ ts: now, stream: "TELEMETRY", type: "guard", order_id: orderId, side, moneyness: "deep_otm", covered, directional_cap, }); } // ── determinism dump ────────────────────────────────────────────────────── /** * A deterministic, JSON-serializable snapshot of the whole engine (RUNTIME §7): plans (sorted * by name, with their children sorted by ref), envelopes, the ledger, the tag store, and the * monotonic counters. Two replays of the same bus + same armed documents produce equal dumps — * the certification the determinism test asserts. */ dumpState(): unknown { const plans = [...this.#plans] .map((pr) => ({ name: pr.name, state: pr.state, // kestrel-50w: the arm-time gate-block reason (null unless a plan is stuck `authored` on an // unsatisfiable regime gate). Rides dumpState — the frame's projection source — never the graded bus. armBlockReason: pr.armBlockReason, fired: pr.fired, done: pr.done, // Open-exposure reservation (bd 1a8): the plan's CURRENT capital-at-risk (open long basis + // resting BUY notional), DERIVED — so it drops to $0 on a round-trip to flat. The frame reads // this (via the envelope max) as `budget.used`, so freeing it restores the agent's sizing headroom. committedUsd: round(this.#committedPremium(pr)), filledBuyQty: pr.filledBuyQty, filledSellQty: pr.filledSellQty, buyCostSum: round(pr.buyCostSum), basisPx: pr.basisPx === null ? null : round(pr.basisPx), reloadRungs: pr.reloadRungs, reloadsHalted: pr.reloadsHalted, acquisitionHalted: pr.acquisitionHalted, exitStood: pr.exitStood, armedAtSeq: pr.armedAtSeq, exitIntent: [...pr.exitIntent], exitWorked: [...pr.exitWorked], reloadPrevTrue: [...pr.reloadPrevTrue], children: [...pr.children] .map((c) => ({ ref: c.ref, role: c.role, side: c.side, qty: c.qty, // A spot child has no strike/right (ADR-0017): dump `null`, never a fictional strike. // Option children keep their exact prior bytes (defined values serialize identically). strike: c.strike ?? null, right: c.right ?? null, px: round(c.px), filled: c.filled, filledQty: c.filledQty, cancelled: c.cancelled, escStage: c.escStage, tpTier: c.tpTier ?? null, transferred: c.transferred ?? false, })) .sort((a, b) => (a.ref < b.ref ? -1 : a.ref > b.ref ? 1 : 0)), })) .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); const envelopes = [...this.#envelopes] .map((e) => ({ name: e.name, committedUsd: round(this.#envelopeExposureUsd(e)), firedCount: e.firedCount })) .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0)); const tags = [...this.#tags.entries()].sort((a, b) => (a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)); return { plans, envelopes, ledger: this.#ledger.dump(), tags, orderSeq: this.#orderSeq, armSeq: this.#armSeq, repriceCount: this.#repriceCount, }; } } /** Deterministic 1e-8-grid rounding so dumped prices/dollars are byte-stable (RUNTIME §0). */ function round(x: number): number { return Math.round(x * 1e8) / 1e8; } /** * Mint a DETERMINISTIC Plan-instance identity (kestrel-22j.15). Derived PURELY from the authored NAME (the * Lineage key) and the registration `ordinal` (a monotonic per-engine counter = the registration/bus * position) — **no wall clock, no unseeded RNG** (RUNTIME §0) — so the same documents armed in the same * order yield byte-identical ids across replays. The name ALONE cannot distinguish a legal same-name * replacement from its predecessor (that IS the 22j.15 defect); the ordinal is what makes two same-name * instances distinct. `sha256(name ⊕ ordinal)` truncated to 16 hex (64 bits) — opaque, fixed-width, and * collision-free within a session's handful of instances. The `pi_` prefix marks it a Plan-instance id * (never to be confused with the session-level {@link import("../bus/index.ts").InstanceIdentity}). */ function mintPlanInstanceId(name: string, ordinal: number): string { // Portable sha256 (bead alw.14 / kestrel-markets-770.15): the DIRECT `new Bun.CryptoHasher` // threw `ReferenceError: Bun is not defined` when the engine runs in a Cloudflare Worker // isolate (workerd has no `Bun` global), 500-ing the deployed simulate arm path. `sha256` is // the runtime's one portable, SYNCHRONOUS hash — Bun's native hasher under `bun`, the // pure-JS `sha256Pure` in a Worker — so this id mints byte-identically on both paths. const digest = sha256(`${name}${ordinal}`); return `pi_${digest.slice(0, 16)}`; }