UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

51 lines 3.6 kB
/** * # bus/plan-instances — group a bus's PLAN stream into exact Plan INSTANCES (kestrel-22j.15) * * A pure, deterministic fold of a bus's {@link PlanEvent}s into the distinct Plan **instances** they trace — * the ONE place both report projections (`project(bus)` in {@link ../blotter/project.ts} and `buildReport` * in {@link ../session/sim.ts}) agree on "what is one instance", so their groupings can never drift. * * ## Why a name is not an instance (the P0 this fixes) * A plan NAME is a **Lineage** key (names-are-data, ADR-0006): `fade-ladder` authored fresh on 40 days is one * strategy with 40 instances, and a name may legally RECUR after an earlier instance is `done` (the F4 * same-name-supersession guard in {@link ../engine/plans.ts} only forbids re-declaring a name still owned by a * NOT-done plan). Grouping the PLAN stream by name ALONE therefore merges a `done` instance and its same-name * replacement into one trace whose terminal outcome is the last one seen — silently erasing the predecessor's * (kestrel-22j.15). Grouping by INSTANCE keeps them apart; Lineage aggregation (by name) is a SEPARATE roll-up * a consumer does over `group.plan`. * * ## The instance key (v5 explicit, else an explicit pre-v5 migration) * - **v5 bus** — every PLAN event carries {@link PlanEvent.plan_instance}, the id the engine minted * deterministically at registration. The key IS that id: exact, no inference. * - **pre-v5 bus** (no `plan_instance`) — MIGRATE EXPLICITLY by **lifecycle sessionization**: a name's next * instance opens at the structural boundary where its currently-open instance has already reached a terminal * `done`. This is exact — never a timestamp heuristic, never fuzzy name-matching — because F4 guarantees a * name only recurs after its predecessor is `done`, so a `done`→next-event boundary is precisely one * instance boundary. Fail-closed doctrine (RUNTIME §8): a pre-v5 bus is migrated, never silently * mis-identified. * * A single bus is all-or-nothing (an engine either stamps every plan event with an id or, pre-v5, none), so the * two paths never interleave within one bus. * * Determinism (RUNTIME §0): pure function of the bus bytes — no wall clock, no RNG. Output order is * `(name asc, first-seq asc)`, which reproduces the prior name-sorted order exactly when every name has a * single instance, and orders same-name instances by first appearance. */ import type { BusEvent, PlanEvent } from "./types.ts"; /** One exact Plan instance traced off the bus: its Lineage name, its instance identity (the explicit * `plan_instance` on a v5 bus, else a deterministic sessionized key), and its PLAN events in seq order. */ export interface PlanInstanceGroup { /** The authored plan NAME — the Lineage aggregation key (a consumer rolls instances up BY this). */ readonly plan: string; /** The exact instance identity: {@link PlanEvent.plan_instance} (v5) or a sessionized migration key (pre-v5). */ readonly instance: string; /** This instance's PLAN events, in bus seq order (lifecycle transitions + any reject notices). */ readonly events: readonly PlanEvent[]; } /** * Group a bus's PLAN events into the distinct Plan instances they trace (see the module header). Pure and * deterministic. Non-PLAN events are ignored. The bus is expected in seq order (as every well-formed bus is); * events are folded in the order given. */ export declare function groupPlanInstances(bus: readonly BusEvent[]): PlanInstanceGroup[]; //# sourceMappingURL=plan-instances.d.ts.map