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.
59 lines (58 loc) • 3.88 kB
JavaScript
/**
* # frame/founder-registry — the (seat × frameKind) → founder View mapping (ADR-0041 §2 / A1, kestrel-wa0j.26)
*
* A **founder View** is the graded SEED a pod seat reads at a given frame kind — a hypothesis entering
* the ADR-0029 tournament, NEVER a blessed default (ADR-0041 §2: the null is that a pane HURTS until a
* both-poles matched set says otherwise). This module is the small, semantic map from a {@link SeatId}
* at a frame kind to the NAME of its founder View; the View DATA itself comes from
* {@link ./founder-views.generated.ts FOUNDER_VIEWS} (parsed from the golden corpus source, never a
* hand-duplicated pane list). Two layers, cleanly split:
* - the **mapping** here is the seat-doctrine knowledge (which seat reads which named screen when);
* - the **panes** are single-sourced from `tests/golden/accept/founder-seat-views.kestrel`.
*
* ## The seed mapping (docs/views/founder-seat-views.md)
* - `strategist` at `OPEN` → `strategist-open` — the frontier framer orients at the open.
* - `watcher` at `WAKE` → `watcher-wake` — the fast reflex reads WHAT MOVED (delta leads).
* - `pm` at `WAKE` → `scan-fire` — the PM's discovery half reads one name deep on a scan.
*
* Both `watcher-wake` and `scan-fire` are WAKE-kind Views distinguished by SEAT, not frame kind — which
* is exactly why the cell key carries a role axis (a pane's sign is role-dependent, ADR-0041 §2).
*
* ## Honest absence (never a silent default)
* An (seat × frameKind) pair with no founder seed — a strategist at WAKE, a watcher at OPEN, a PM at
* OPEN — resolves to `undefined` HERE, and {@link ./pane-catalog.ts resolveView} falls through to the
* phase default for it. Absence is a first-class value (ADR-0041 §3 absent-not-hidden), never a
* fabricated seed. This is a pure lookup — no clock, no RNG, fail-closed on an unknown View name.
*/
import { FOUNDER_VIEWS } from "./founder-views.generated.js";
/** The seed mapping: `${seat}/${frameKind}` → the founder View NAME. An absent key = honest absence
* (falls through to the phase default at resolution). The View DATA is looked up in
* {@link FOUNDER_VIEWS} — this table holds only names, so it can never duplicate a pane list. */
const FOUNDER_VIEW_NAME_BY_SEAT_FRAME = {
"strategist/OPEN": "strategist-open",
"watcher/WAKE": "watcher-wake",
"pm/WAKE": "scan-fire",
};
/**
* The founder {@link ViewSelection} a seat reads at a frame kind, or `undefined` when the seat has no
* founder seed for that frame (honest absence → the caller falls through to the phase default). Pure.
*
* FAIL-CLOSED: a mapping that names a View absent from the generated {@link FOUNDER_VIEWS} throws
* rather than returning `undefined` — that is a generator/mapping drift (a founder View was renamed or
* dropped from the golden without updating this table), not honest absence, and it must never be
* silently swallowed into a phase-default fallback.
*/
export function founderViewFor(seat, frameKind) {
const name = FOUNDER_VIEW_NAME_BY_SEAT_FRAME[`${seat}/${frameKind}`];
if (name === undefined)
return undefined; // honest absence — no founder seed for this (seat × frameKind)
const view = FOUNDER_VIEWS[name];
if (view === undefined) {
throw new Error(`founder registry maps ${seat}/${frameKind} → "${name}", but that View is absent from the generated FOUNDER_VIEWS ` +
`(regenerate: bun scripts/gen-founder-view-registry.ts). This is registry/golden drift, never honest absence.`);
}
return view;
}
/** The full set of (seat × frameKind) pairs that carry a founder seed — the mapping's key set, exposed
* so tests (and any census) enumerate the seeds without re-hardcoding them. */
export const FOUNDER_SEED_KEYS = Object.keys(FOUNDER_VIEW_NAME_BY_SEAT_FRAME);