/**
 * # 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 { Module } from "../lang/index.ts";
import type { NewBusEvent, BusEvent, Right } from "../bus/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 { type Moneyness } from "../fill/index.ts";
import { TriggerEvaluator, type SeriesProvider, type SeriesRegistry, type OrgFacts } from "../series/index.ts";
import { type RepriceTokenBucket } from "./pricing.ts";
import type { ArmRefusalCode, ArmRefusal, ArmReport } 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 declare class ArmError extends Error {
    readonly code: ArmRefusalCode;
    readonly refusals?: readonly ArmRefusal[];
    constructor(code: ArmRefusalCode, message: string, refusals?: readonly ArmRefusal[]);
}
/**
 * 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 declare 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 declare function armRefusalError(refusals: readonly ArmRefusal[]): ArmError;
/** 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 declare function deriveOrderGuard(side: "buy" | "sell", spot: number | undefined, strike: number, right: Right): OrderGuardEvidence;
/** 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[];
}
export declare class PlanEngine {
    #private;
    constructor(opts: PlanEngineOptions);
    /** 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;
    /**
     * 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;
    /** 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;
    /**
     * 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;
    /**
     * **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;
    /**
     * 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;
    /**
     * 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;
    };
    /**
     * 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;
    /**
     * 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;
    /**
     * 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;
    /**
     * 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;
    /**
     * 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;
}
export {};
//# sourceMappingURL=plans.d.ts.map