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.
72 lines • 5.53 kB
TypeScript
/**
* # series/author-lint — the unknown-series trigger operand, made legible (kestrel unknown-series-legible)
*
* A registry-aware, author-time semantic check that turns a SILENT never-fire into a LOUD,
* repair-guiding rejection. The failure it catches: a trigger operand names a series that PARSES
* as a well-formed {@link SeriesRef} but has **no phonebook record** because the authored name is a
* case-clash of a known key (`VELOCITY` against the lowercase key `velocity`). Such an operand resolves UNKNOWN, so a
* `crosses` never observes its transition and the plan arms then silently expires — 0 orders, and
* the plan reads `armed`, not blocked. That VIOLATES the registry's own fail-closed charter ("the
* phonebook never guesses … a logged reason … never a silent default", `registry.ts` header): here
* the unknown-NAME is a *silent* never-fire the author cannot see.
*
* This check restores legibility at author-time (Option A / iq5-shaped): a document whose trigger
* references an unknown series NAME that is a case-variant of a known market series is rejected
* with a message that names the offending series, the known series it clashes with ("did you mean 'velocity'?"),
* and the whole known vocabulary — a repairable `invalid` outcome the harness feeds back so the
* agent self-corrects next wake (the gxz reason-feedback loop). It is pure (no clock/RNG/IO) and
* off the graded path: a validation rejection is not graded-bus content.
*
* ## Case-variants are ALL flagged (OSS-ADR-0045) — the 0.4.5 forgiveness is reverted
* `registry.lookup` is now EXACT-MATCH: a case-variant of ANY known market series — the seven scalars
* (`VWAP`/`HOD`) OR a windowed metric (`VELOCITY`) — has no phonebook record, so this check's
* `registry.lookup(name) !== undefined` gate does NOT skip it and `nearestKnown` finds the canonical
* lowercase name it clashes with. A scalar case-variant is thus a flaggable author error exactly like
* a windowed one — the same coded `unknown-series` refusal the arm-time guard raises. This reverts the
* kestrel-7tbi case-insensitive forgiveness: forgiving a case-clash at resolution armed a plan on a
* name the author never registered and hid a de-arm they could not see (a silent default, RUNTIME §8).
*
* ## What it flags — and, deliberately, what it does NOT
* The hard part is telling a **market-fact case-clash** (`VELOCITY`) apart from a **legitimate org
* fact** (`pnl`, `plan(chase).state`), a **state literal** (`done`), or a **regime tag** (`chop`) —
* all are `registry.lookup(name) === undefined`, because org facts register implicitly / delegate to
* the book (`series/provider`) and literals/tags resolve symbolically. Structure alone can't
* distinguish them (`VELOCITY` and `pnl` are both bare single-segment identifiers). The discriminator
* is **legibility-only / the phonebook never guesses**: we flag a bare, unregistered operand ONLY when
* it is a case-INSENSITIVE-EXACT match to a known series (a pure case-clash). We do NOT guess at
* arbitrary edit-distance typos — that arm false-positived on first-class operands (`done`, `chop`,
* `gap`, `long`, …). So:
*
* - `VELOCITY` → flagged (case-clash of `velocity`, a windowed metric) — author error
* - `VWAP` / `HOD` → flagged (a scalar case-clash of `vwap`/`hod`; exact-match, OSS-ADR-0045) — author error
* - `vwpa` → NOT flagged (a genuine typo, but no case-exact known match; never guessed at)
* - `vwap` → NOT flagged (it IS registered)
* - `pnl` → NOT flagged (no case-exact market series; a real org fact routed to the book)
* - `plan(x).state` → NOT flagged (has a selector ⇒ never a market read)
* - `armed` / `done`/ `chop` → NOT flagged (a symbolic literal / regime tag, not a known series name)
*
* A series that IS in the phonebook but is currently **dark** (resolves UNKNOWN as a transient data
* state) is never touched here — that stays silently-armed by design (fail-closed on a value, not a
* name). We only ever read the phonebook, never a live value.
*/
import type { Module, Statement } from "../lang/ast.ts";
import type { SeriesRegistry } from "./registry.ts";
/** A repair-guiding diagnostic for a trigger operand naming a series with no phonebook record but a
* near-match to a known market series (a case-clash or typo). Off the graded path. */
export interface UnknownSeriesDiagnostic {
/** The offending series NAME exactly as authored (e.g. `"VWAP"`). */
readonly name: string;
/** The nearest known market series NAME (e.g. `"vwap"`) — the repair target. */
readonly suggestion: string;
/** The full repair-guiding message: the unknown name, the did-you-mean, and the known vocabulary. */
readonly message: string;
}
/**
* The FIRST unknown-series trigger operand in a parsed document that near-matches the known market
* vocabulary — a repairable author error — or `undefined` when every trigger operand is either a
* registered series, a legitimate org path, or a symbolic literal too far from any market series to
* be a typo. Reads only the phonebook (never a live value), so a currently-dark registered series is
* never flagged. Pure and deterministic (document-order traversal; deterministic suggestion).
*/
export declare function unknownSeriesDiagnostic(node: Module | Statement, registry: SeriesRegistry): UnknownSeriesDiagnostic | undefined;
//# sourceMappingURL=author-lint.d.ts.map