/**
 * # 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 <dur>` 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: <reason>)` 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<InstrumentSpec, "symbol"> = { 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).<fact>`) 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<string, InstrumentSpec>();
  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<string> | 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<string, { pr: PlanRuntime; child: ChildOrder }>();
  readonly #childEvals = new Map<string, TriggerEvaluator>();
  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<string, Map<string, BookState>>();
  /** 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<string, { bid: number | null; ask: number | null; last: number }>();
  readonly #tags = new Map<string, string>();

  #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).<fact>` (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 <scope> 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 <px>] 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 <px>] 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 <px>] 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<string>();
    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 <px>]`). 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 <px>` 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 <px>` 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<string>();
    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<string, BookState>();
            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 <dur>` (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: <reason>)`, 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<string>();
    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<ChildOrder, "ref" | "filled" | "filledQty" | "filledCost" | "cancelled" | "escStage">; }[];
      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<ChildOrder, "ref" | "filled" | "filledQty" | "filledCost" | "cancelled" | "escStage"> }[]; premium: number; refusal: string | null } {
    const budgetUsd = pr.plan.budget ? pr.plan.budget.value * this.#rUsd : Infinity;
    const admitted: { child: Omit<ChildOrder, "ref" | "filled" | "filledQty" | "filledCost" | "cancelled" | "escStage"> }[] = [];
    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<ChildOrder, "ref" | "filled" | "filledQty" | "filledCost" | "cancelled" | "escStage"> }[],
    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 <px> 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<ChildOrder, "ref" | "filled" | "filledQty" | "filledCost" | "cancelled" | "escStage">; 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<ChildOrder, "ref" | "filled" | "filledQty" | "filledCost" | "cancelled" | "escStage">,
    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<string>();
    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)}`;
}
