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.
86 lines • 7.03 kB
TypeScript
/**
* # validate — the single home for the six doctrine invariants (ADR-0004 / ADR-0005)
*
* The typed object model IS the language (ADR-0004), authored three ways: `parse(text)`, the
* `builders`, and hand-built literals. For the round-trip to hold, an invariant that any one
* surface enforces must be enforced by ALL of them — otherwise a builder-authored object can
* exist that the parser would reject, so `parse(print(x))` throws on something only a builder
* could make. This module is where the six invariants live, once, so every surface reads the
* same rule and the same words:
*
* 1. `atomic` is reserved (never faked) — ADR-0005
* 2. a cross may not be `held` (edge, not level) — sfg8 / xrearm-1
* 3. a cross re-arm band must be a positive width — sfg8 / xrearm-1
* 4. a risk budget must be a positive risk fraction — ARCHITECTURE §6.1
* 5. an EXIT may not condition on a mark (marks lie) — ADR-0005 / ARCHITECTURE §6.3
* 6. provenance may only narrow downward (never elevate) — ARCHITECTURE §6
*
* `parse` throws a positioned {@link KestrelParseError} at the offending token, so it reads
* the message text and predicates here but keeps its own `line`/`col` throw. `builders` call
* the `refuse*` guards at construction (fail closed before a bad object exists); `print` calls
* them before emitting (never print text `parse` would reject). Invariants 1–3 carry two
* voices — a rich parse-time message that names the remedy, and a terse `*_RESERVED` message
* the builder/printer share — because the parser can afford prose the object surfaces cannot.
*/
import type { Citation, Operand, ProvenanceTier, Quantity, SeriesRef, Statement, Trigger } from "./ast.ts";
/** The rich parse-time refusal: names why atomicity cannot be faked and what to do instead. */
export declare const ATOMIC_MESSAGE: string;
/** The terse refusal the builder and printer share byte-for-byte (object surfaces, no prose). */
export declare const ATOMIC_RESERVED = "atomic is reserved: refused until whole-structure preflight + atomic execution adapter exist";
/** Refuse to CONSTRUCT/EMIT an atomic multi-leg. Atomicity is never faked (ADR-0005). */
export declare function refuseAtomic(atomic: boolean | undefined): void;
/** The rich parse-time refusal: a cross is an edge, not a level; holding it is the anti-pattern
* that phantom-fired in graded sim. Names both remedies (re-arm band, or hold a level). */
export declare const HELD_CROSS_MESSAGE: string;
/** The terse refusal the builder and printer share byte-for-byte. */
export declare const HELD_CROSS_RESERVED = "a cross cannot be held: a cross is an edge event, not a level \u2014 use a re-arm band instead of holding the edge (xrearm-1)";
/** Refuse to CONSTRUCT/EMIT held-over-cross. Robustness comes from a re-arm band, not from
* sustaining an edge; `within` over a cross stays legal. */
export declare function refuseHeldOverCross(inner: Trigger): void;
/** The terse refusal the builder and printer share byte-for-byte. */
export declare const BAND_POSITIVE_RESERVED = "a cross re-arm band must be a positive width: a zero/negative band can never re-arm \u2014 the cross would never clear its band to fire again (xrearm-1)";
/** The rich parse-time refusal, which also names the offending width. A zero/negative band's
* clear-and-re-arm distance would be ≤ 0, so the cross could never clear its band to fire
* again — a no-op guard, refused rather than armed. */
export declare function bandMessage(widthText: string): string;
/** Refuse to CONSTRUCT/EMIT a non-positive band (a no-op re-arm guard). */
export declare function refuseNonPositiveBand(band: Quantity | undefined): void;
/** `size × max_loss ≤ budget` (ARCHITECTURE §6.1): a non-positive risk budget can never admit
* any real position, so it is always wrong — fail closed rather than arm a dead plan. Shared
* on every surface: a builder-authored `budget(-0.5)` must throw exactly where the parser does,
* or its printed text (`budget -0.5R`) would not round-trip. */
export declare function budgetMessage(value: number): string;
/** Refuse to CONSTRUCT/EMIT a non-positive risk budget. */
export declare function refuseNonPositiveBudget(value: number): void;
/** A single-segment, un-windowed, un-selected series — text cannot distinguish it from a bare
* identifier operand (a plan-state literal), and ADR-0004 forbids un-round-trippable
* distinctions. Shared with the parser's structural-event path. */
export declare function isSimpleSeries(op: Operand): op is SeriesRef;
/** The name of the first mark operand a trigger conditions on, or undefined. */
export declare function findMarkInTrigger(t: Trigger): string | undefined;
export declare function markMessage(mark: string): string;
/** Refuse to CONSTRUCT/EMIT an EXIT whose trigger conditions on a mark. */
export declare function refuseExitOnMark(when: Trigger): void;
export declare function provenanceMessage(planTier: ProvenanceTier, moduleTier: ProvenanceTier): string;
/** Does statement `s` (at index) elevate above the module tier? Returns the offending plan tier
* or undefined. Shared so parse (positioned) and builder/print (plain throw) apply one rule. */
export declare function elevatesProvenance(s: Statement, moduleRank: number): ProvenanceTier | undefined;
/** Refuse to CONSTRUCT/EMIT a module whose statement elevates above its declared provenance. */
export declare function refuseProvenanceElevation(moduleTier: ProvenanceTier | undefined, statements: readonly Statement[]): void;
/** Is `hash` a well-formed Thesis digest (64 lowercase hex chars)? Shared by parse (positional
* throw), and the builder/printer `refuse*` guards, so one rule governs every surface. */
export declare function isCitationHash(hash: string): boolean;
/** The rich parse-time refusal for a malformed/absent content hash — names the fixed shape and
* the offending token. */
export declare function citationBadHashMessage(got: string): string;
/** The rich parse-time refusal for an inline thesis body — the citation is content-hash ONLY. */
export declare const CITATION_INLINE_BODY_MESSAGE: string;
/** The rich parse-time refusal for `because` on a View or Grade — a citation pre-registers a
* Plan or Wake only (never a fifth statement kind, ADR-0001). */
export declare function citationOnMessage(kind: "View" | "Grade"): string;
/** The terse refusal the builder and printer share byte-for-byte (object surfaces, no prose). */
export declare const CITATION_BAD_HASH_RESERVED = "a `because` citation needs a content hash of the fixed shape `sha256:<64 hex>`: a Thesis digest is 64 lowercase hex chars";
/** Refuse to CONSTRUCT/EMIT a malformed citation (bad algo or a non-64-hex digest), so a hand-
* built citation throws exactly where the parser does and its printed text always round-trips. */
export declare function refuseBadCitation(c: Citation): void;
//# sourceMappingURL=validate.d.ts.map