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.

550 lines (549 loc) 30.6 kB
/** * # frame/render-arm — the token-optimal percept renderer ARM (kestrel-4gl.9 / wa0j.27 / 5ine) * * A SECOND, explicit rendering of the SAME typed Frame inputs, restructured to spend fewer * tokens under a BPE tokenizer while carrying the IDENTICAL fact set. This module NEVER touches * the canonical renderer ({@link ./render.ts}) — canonical stays the frozen leaderboard control * (wa0j.27). The arm is selectable as a HARNESS PROPERTY ({@link RenderArm} / {@link renderPercept}) * so a run picks its renderer the way it picks its model — the renderer rides the harness bundle, * it is never a leaderboard facet (kestrel-5ine). * * ## Why an arm (the hypothesis, wa0j.27) * The percept is dense numeric/tabular data — exactly where BPE fragmentation bites. Three * measured hotspots (see `docs/results/gemini-token-optimal/report.md`, measured on the real * `src/frame` output under the o200k_base + cl100k_base BPE tables): * 1. **Alignment padding** — the chain table's `pad(...)` runs and the kernel's fixed gutters * emit runs of spaces; a run of 2+ spaces is often its own BPE token, spent per row per column. * 2. **Non-ASCII glyphs** — `·` `→` `–` `—` `█` `─` are multi-byte and frequently 1+ token each; * the rotated-candlestick tape is a WALL of `█`/`─` glyphs (dozens of tokens per row) that * encodes price only approximately (column position), so it is both the costliest pane AND * lossy vs the numbers it is drawn from. * 3. **Repeated skeleton** — the verbose banners (`==== SAFETY / CONTROL KERNEL … ====`) and the * re-spent column labels cost tokens on every frame. * * The arm's fixes are UNIVERSAL to BPE tokenizers (Gemini's SentencePiece, GPT's o200k, Claude's * proprietary BPE all fragment padded decimals + rare glyphs): collapse alignment to a single * delimiter, render ASCII, render the tape as compact numeric OHLC (cheaper AND exact), and shorten * the skeleton. See the report for per-family caveats (only the GPT-family o200k/cl100k counts are * measured precisely here; the Gemini/Claude hosted count endpoints were unreachable in-env). * * ## Information parity is a GATE wired into the RUNTIME emit path (review F1) * The arm carries the SAME facts as canonical — a compaction that DROPS a level, a strike, a P&L, or * a wake reason is a safety regression, not a token win. {@link assertInformationParity} extracts the * numeric fact MULTISET from BOTH renders and asserts the arm carries every occurrence canonical * does, plus every free-text fact collected from the typed INPUT. The gate runs INLINE inside * {@link renderTokenOptimal} — the production entry point — so a lossy arm REFUSES to emit at * runtime, not only under test (the house guard-wiring rule: a guard present but unwired is inert). * `tests/render-arm.parity.test.ts` ships DELIBERATELY-LOSSY fixtures that the gate must catch * through that runtime path — unwire the inline assert and those tests go red. */ import { KERNEL_DELTA_SENTINEL, KERNEL_LEAD_SENTINEL, KERNEL_SECTION_LABELS } from "./types.js"; import { DASH, isSpotLeg, num, px, signedInt, toClose, usd } from "./format.js"; import { renderBriefing, renderWakeDelta } from "./render.js"; import { actionToken, bucketEngineLog, collapseActionTokens, restingLabel, walkKernel, } from "./kernel-walk.js"; import { candleLine, tapeBounds } from "./tape-geometry.js"; /** The arm ids this module materializes (canonical is served by {@link ./render.ts}, not here). */ export const TOKEN_OPTIMAL_ARMS = ["token-optimal-gemini", "token-optimal-fable"]; /** Is `arm` one of the token-optimal arms this module renders? */ export function isTokenOptimalArm(arm) { return TOKEN_OPTIMAL_ARMS.includes(arm); } /** The measured tape style per token-optimal arm (native-count driven, never taste). */ export const ARM_TAPE_STYLE = { "token-optimal-gemini": "candlestick", "token-optimal-fable": "ohlc", }; // ───────────────────────────────────────────────────────────────────────────── // Compact formatting primitives (ASCII-only; single-delimiter; shares format.ts value fns) // ───────────────────────────────────────────────────────────────────────────── /** The arm's ASCII unknown token. Canonical uses `—` (U+2014, a multi-byte rare glyph); the arm * uses `na`. {@link normalizeFact} maps BOTH to one canonical UNKNOWN fact so the parity guard * never mistakes a re-encoding of "unknown" for a dropped fact. */ const ARM_DASH = "na"; /** px/num but with the ASCII unknown token — a value renders identically to canonical (same digits, * same precision, same integer/2dp rule) so numeric facts match byte-for-byte across arms. */ const apx = (x) => { const s = px(x); return s === DASH ? ARM_DASH : s; }; const anum = (x, prec = 2) => { const s = num(x, prec); return s === DASH ? ARM_DASH : s; }; const ausd = (x) => { const s = usd(x); return s === DASH ? ARM_DASH : s; }; /** Compact tape/deadline reused from canonical (relative, date-blind). */ const armToClose = (min) => toClose(min).replace(" to close", ""); // ───────────────────────────────────────────────────────────────────────────── // Kernel (compact) — the SAME 8 fixed-order sections + optional mandate/brief, absent-not-hidden // ───────────────────────────────────────────────────────────────────────────── /** The engine-log bucket set + classification, the resting-state label, and the action id token are * single-source in {@link ./kernel-walk.ts} (shared with the canonical adapter). The arm's action * token is `id@N(reason)` (no `seq` prefix, no space before the reason); its resting-label vocabulary * is the lowercase `live`/`live+clamped` form. */ const ARM_ACTION_STYLE = { seqPrefix: "", reasonSpace: false }; const ARM_RESTING_LABELS = { live: "live", liveClamped: "live+clamped", clamped: "clamped", off: "off" }; function sizingLine(s) { if (s === undefined || s === null) return "sizing=na"; const unit = s.unit === "shares" ? "sh" : "ct"; if (s.maxUnits !== null && s.basisPerUnit !== null) { return `sizing=max~${s.maxUnits}${unit} basis=${anum(s.basisPerUnit)}/${unit} rem1R=${anum(s.remainingUsd)}`; } const note = s.note !== undefined ? ` ${s.note}` : ""; return `sizing=max~na${unit} rem1R=${anum(s.remainingUsd)}${note}`; } /** * The token-optimal {@link KernelSink} — {@link walkKernel}'s ARM adapter. SAME 8 fixed-order * sections, SAME facts, compacted to ASCII + single-space delimiting. Leads with a short sentinel * that still marks the safety block unambiguously; the `frame=` line is folded into that sentinel * (so {@link frameLine} is a no-op). This module NEVER touches the canonical adapter — it only * shares the ONE walk and the parameterized kernel helpers. */ class ArmKernelSink { L = []; sentinel(frameKind) { this.L.push(`#KERNEL ${frameKind} (safety/control; leads every frame)`); } frameLine() { // The arm folds the frame kind into its sentinel; no separate `frame=` line. } mandate(mandate) { this.L.push(`#mandate obj=${mandate.objective} 1R=$${apx(mandate.rUsd)} success=${mandate.successCriterion} risk=${mandate.riskRule}`); } brief(brief) { const ver = brief.version !== undefined ? ` v${brief.version}` : ""; this.L.push(`#brief(directional; NOT a constraint) ${brief.text.replace(/\n+/g, " ")} [${brief.hash}${ver}]`); } // 1. WAKE wake(w) { this.L.push(w == null ? `#wake na` : `#wake reason=${w.reason} sev=${w.severity} ${armToClose(w.deadline ?? null)}`); } // 2. DATA-HEALTH dataHealth(dh, unav) { if (dh.length === 0) { this.L.push(`#health na`); } else { for (const h of dh) { this.L.push(`#health ${h.instrument} bid=${anum(h.bidPresentRate, 3)} 2sided=${h.twoSided} stale=${anum(h.staleS, 1)} dark=${h.dark}`); } } this.L.push(`#unavail ${unav.length > 0 ? unav.join(", ") : "none"}`); } // 3. POSITIONS positions(positions) { if (positions.length === 0) { this.L.push(`#pos flat`); return; } for (const p of positions) { const unreal = p.unrealUsd === undefined ? "" : ` unreal=${ausd(p.unrealUsd)}`; const leg = isSpotLeg(p) ? `${p.instrument}sh` : `${p.right}${apx(p.strike)}`; this.L.push(`#pos ${signedInt(p.qty)} ${leg} basis=${apx(p.basis)} ${p.structure ?? "?"} claim=${p.claimOwner ?? "?"}${unreal}`); } } // 4. RESTING resting(resting) { if (resting.length === 0) { this.L.push(`#rest none`); return; } for (const o of resting) { const leg = isSpotLeg(o) ? `${o.instrument}sh` : `${o.right}${apx(o.strike)}`; this.L.push(`#rest ref=${o.ref} ${o.side} ${leg}@${apx(o.px)} ${restingLabel(o, ARM_RESTING_LABELS)} qty=${o.qty}`); } } // 5. BUDGET + sizing budget(b) { if (b == null) { this.L.push(`#budget na`); this.L.push(`sizing=na`); } else { this.L.push(`#budget remR=${anum(b.remainingR)} plan=${anum(b.planEnvelope)} book=${anum(b.bookEnvelope)} owner=${anum(b.ownerEnvelope)}`); this.L.push(sizingLine(b.sizing)); } } // 6. OWNER envelope + acts ownerActs(b, oacts) { const owner = b != null ? anum(b.ownerEnvelope) : ARM_DASH; const acts = oacts.length === 0 ? "none" : oacts.map((a) => `${a.kind}(id=${a.id},@${a.asofSeq})`).join(" "); this.L.push(`#owner envelope=${owner} acts=${acts}`); } // 7. ENGINE LOG engineLog(elog) { if (elog.length === 0) { this.L.push(`#engine none`); return; } const { buckets, other } = bucketEngineLog(elog); const parts = []; for (const { kind, items } of buckets) { // De-dup a per-tick refusal flood: a run of identical (id, reason) entries collapses to // `<count>x <exemplar>` (kestrel-7bip). The bucket count still counts EVERY action. const ids = collapseActionTokens(items, ARM_ACTION_STYLE).join(","); parts.push(`${kind}=${items.length}${ids !== "" ? `[${ids}]` : ""}`); } if (other.length > 0) parts.push(`other=${other.length}[${other.map((e) => actionToken(e, ARM_ACTION_STYLE)).join(",")}]`); this.L.push(`#engine ${parts.join(" ")}`); } // 8. CLAIMS claims(claims) { if (claims.length === 0) { this.L.push(`#claims none`); return; } for (const c of claims) { const f = c.field; const val = typeof f.value === "number" ? anum(f.value) : String(f.value); this.L.push(`#claims ${c.claimType}=${val} (src=${f.source},ver=${f.modelVer},conf=${anum(f.confidence)})`); } } } /** Render the compact cockpit kernel — the ONE walk ({@link walkKernel}) driving {@link ArmKernelSink}. * SAME sections, SAME facts, ASCII, single-space delimited. */ function armKernel(kernel, frameKind) { const sink = new ArmKernelSink(); walkKernel(kernel, frameKind, sink); return sink.L.join("\n"); } // ───────────────────────────────────────────────────────────────────────────── // Body panes (compact) // ───────────────────────────────────────────────────────────────────────────── function armInstruments(instruments) { if (instruments.length === 0) return "instruments: none"; const rows = instruments.map((i) => { const role = i.role !== undefined ? ` ${i.role}` : ""; return ` ${i.symbol} ${i.assetClass}${role} mult=${apx(i.multiplier)} tick=${apx(i.tick)}`; }); return ["instruments:", ...rows].join("\n"); } function armLevels(instrument, lv) { // Range separator is `/`, never `-`: a `-` between two numbers reads as a negative sign (to a BPE // reader AND to the numeric-parity extractor), so `5112.50-5125` would mis-say "-5125". return `levels ${instrument}: spot=${apx(lv.spot)} pc=${apx(lv.priorClose)} hod=${apx(lv.hod)} lod=${apx(lv.lod)} vwap=${apx(lv.vwap)} or=${apx(lv.orLow)}/${apx(lv.orHigh)}`; } /** * The tape under the arm — TWO measured styles ({@link ArmTapeStyle}): * * - `ohlc` (the FABLE arm): exact numeric OHLC per bucket. The comprehension winner (readers * preferred it 9–1; +2.7pp quiz accuracy) — MORE information than the glyph picture — but it * costs MORE tokens on every tokenizer measured (decimals fragment; glyph runs compress). * - `candlestick` (the GEMINI arm): the canonical rotated-candlestick GLYPH rows under the arm's * compact ASCII header. Native Gemini counts price a candlestick bucket at 18 tokens vs 39 as * OHLC, and the OHLC-style arm LOST 26.9% on the tape-heavy percept — so the Gemini arm keeps * the glyph tape (measured, not designed). The glyph geometry is the SHARED * {@link ./tape-geometry.ts candleLine} (the ONE 40-column geometry the canonical pane draws too, * ADR-0052 §1) — the arm owns only its own compact header + row prefix, never a second geometry. * * Both carry the same facts the canonical tape does (axis lo/hi + per-row clocks); `volume` when * present is carried on the ohlc style (parity superset). */ function armTape(rows, bucketMin, style) { if (rows.length === 0) return `tape ${bucketMin}m: no prints`; const { lo, hi } = tapeBounds(rows); const header = `tape ${bucketMin}m axis=${apx(lo)}/${apx(hi)} anchor=${rows[0].clock}`; if (style === "ohlc") { const body = rows.map((r) => { const vol = r.volume !== null && r.volume !== undefined && Number.isFinite(r.volume) ? ` v=${apx(r.volume)}` : ""; return ` ${r.clock} o=${apx(r.open)} h=${apx(r.high)} l=${apx(r.low)} c=${apx(r.close)}${vol}`; }); return [header, ...body].join("\n"); } // candlestick: the canonical glyph rows via the SHARED geometry (wick `─` low→high, body `█` // open→close, price = column) — {@link ./tape-geometry.ts candleLine}, drawn identically to the // canonical pane; the arm owns only its compact ASCII header + ` <clock> ` row prefix. const body = rows.map((r) => ` ${r.clock} ${candleLine(r, lo, hi)}`); return [header, ...body].join("\n"); } function armDarkFlag(row) { const bidDark = row.bid === null; const askDark = row.ask === null; if (bidDark && askDark) return "dark"; if (bidDark) return "bid-dark"; if (askDark) return "ask-dark"; return ""; } function armChain(underlier, chain, dte) { const dteTag = dte !== undefined && dte !== null && Number.isFinite(dte) ? ` ${dte}dte` : ""; if (chain.length === 0) return `chain ${underlier}${dteTag}: no legs`; const rows = chain.map((r) => { const fair = r.fairNote !== undefined && r.fair !== null && r.fair !== undefined ? `${apx(r.fair)} ${r.fairNote}` : apx(r.fair); const flag = armDarkFlag(r); return ` ${apx(r.strike)}${r.right} bid=${apx(r.bid)} ask=${apx(r.ask)} fair=${fair}${flag !== "" ? ` ${flag}` : ""}`; }); return [`chain ${underlier}${dteTag} (strike/right bid ask fair):`, ...rows].join("\n"); } const ARM_MACRO = "macro: unavailable (v1 harness)"; function planTag(plan) { return plan === undefined ? "" : ` (${plan})`; } function armPositionsDetail(positions) { if (positions.length === 0) return "positions: none"; const rows = positions.map((p) => { const unreal = p.unrealUsd === undefined ? "" : ` unreal=${ausd(p.unrealUsd)}`; return isSpotLeg(p) ? ` ${signedInt(p.qty)} ${p.instrument}sh basis=${apx(p.basis)}${unreal}${planTag(p.plan)}` : ` ${signedInt(p.qty)} ${p.instrument} ${apx(p.strike)}${p.right} basis=${apx(p.basis)} fair=${apx(p.fair)}${unreal}${planTag(p.plan)}`; }); return ["positions:", ...rows].join("\n"); } function armResting(resting) { if (resting.length === 0) return "resting: none"; const rows = resting.map((o) => { const note = o.note !== undefined ? ` [${o.note}]` : ""; const leg = isSpotLeg(o) ? "sh" : `${apx(o.strike)}${o.right}`; return ` ${o.side.toUpperCase()} ${o.qty} ${o.instrument} ${leg} @${apx(o.px)}${note}${planTag(o.plan)}`; }); return ["resting:", ...rows].join("\n"); } function armFills(fills) { if (fills.length === 0) return "fills since last: none"; const rows = fills.map((f) => { const at = f.clock !== undefined ? ` @${f.clock}` : ""; const leg = isSpotLeg(f) ? "sh" : `${apx(f.strike)}${f.right}`; return ` ${f.side.toUpperCase()} ${f.qty} ${f.instrument} ${leg} @${apx(f.px)}${at}${planTag(f.plan)}`; }); return ["fills since last:", ...rows].join("\n"); } function armBudget(budget) { if (budget == null) return `budget: ${ARM_DASH}`; const total = budget.total !== undefined ? ` total=${apx(budget.total)}` : ""; const maxR = budget.maxConcurrentR !== undefined ? ` maxR=${apx(budget.maxConcurrentR)}` : ""; return `budget: used=${anum(budget.used)} rem=${anum(budget.remaining)}${total}${maxR}`; } function armPlans(kernel) { if (kernel.plans.length === 0) return "plans: none"; const rows = kernel.plans.map((p) => { const outcome = p.outcome !== undefined ? ` (${p.outcome})` : ""; const blocked = p.blockedReason !== undefined ? ` (blocked: ${p.blockedReason})` : ""; const note = p.note !== undefined ? ` - ${p.note}` : ""; return ` ${p.name}: ${p.state}${outcome}${blocked}${note}`; }); return ["plans:", ...rows].join("\n"); } function armActingDetail(kernel) { return [ "#ACTING", armPositionsDetail(kernel.positions), armResting(kernel.resting), armFills(kernel.fillsSinceLast), armBudget(kernel.budget), armPlans(kernel), ].join("\n"); } // ───────────────────────────────────────────────────────────────────────────── // Public API — the arm's OPEN keyframe + WAKE delta (default View; mirrors ./render.ts entry points) // ───────────────────────────────────────────────────────────────────────────── const joinPanes = (...blocks) => blocks.join("\n\n"); /** Render the OPEN keyframe under a token-optimal arm (default View). Same facts as * {@link ./render.ts renderBriefing}, compacted; the tape style is the arm's ONE measured * divergence ({@link ArmTapeStyle}, default `ohlc` = the Fable arm). Kernel leads. PURE. */ export function renderBriefingArm(input, tape = "ohlc") { const clock = input.clockET !== undefined ? ` ${input.clockET}ET` : ""; const phase = input.phase != null ? ` ${input.phase}` : ""; const header = `#OPEN ${armToClose(input.timeToCloseMin)}${phase}${clock}`; return joinPanes(armKernel(input.kernel, "OPEN"), header, armInstruments(input.instruments), armLevels(input.market.instrument, input.market.levels), armTape(input.market.tape, input.market.tapeBucketMin, tape), armChain(input.market.instrument, input.market.chain, input.market.chainDte), ARM_MACRO, armActingDetail(input.kernel)); } /** Render the WAKE delta under a token-optimal arm (default View). Same facts as * {@link ./render.ts renderWakeDelta}, compacted; tape style per the arm ({@link ArmTapeStyle}, * default `ohlc` = the Fable arm). Kernel leads. PURE + deterministic. */ export function renderWakeDeltaArm(input, tape = "ohlc") { const clock = input.clockET !== undefined ? ` ${input.clockET}ET` : ""; const phase = input.phase != null ? ` ${input.phase}` : ""; const since = `${input.minutesSinceLast === null ? ARM_DASH : String(Math.round(input.minutesSinceLast))}m-since-last`; const reason = input.wakeReason !== undefined ? ` reason=${input.wakeReason}` : ""; const header = `#WAKE ${input.wakeIndex} ${since} ${armToClose(input.timeToCloseMin)}${phase}${clock}${reason}`; return joinPanes(armKernel(input.kernel, "WAKE"), header, armTape(input.market.tape, input.market.tapeBucketMin, tape), armLevels(input.market.instrument, input.market.levels), armChain(input.market.instrument, input.market.chain, input.market.chainDte), armActingDetail(input.kernel)); } // ───────────────────────────────────────────────────────────────────────────── // Information-parity GATE (kestrel-4gl.9 — no guard without a failing fixture) // ───────────────────────────────────────────────────────────────────────────── /** Thrown when a token-optimal arm render DROPS a fact the canonical render carried — a safety * regression masquerading as a token win. The gate fails CLOSED (the arm is refused, never shipped). */ export class InformationParityError extends Error { constructor(message) { super(message); this.name = "InformationParityError"; } } /** Canonical SKELETON strings that carry DIGITS but are NOT facts (e.g. `-- L0/L1 ENGINE LOG --`) — * removed before numeric extraction so a fixed label's digits are never mistaken for a market value. * The arm's own skeleton is digit-free, so this only needs to neutralize canonical's labels. */ const SKELETON_WITH_DIGITS = [ KERNEL_LEAD_SENTINEL, KERNEL_DELTA_SENTINEL, ...KERNEL_SECTION_LABELS, ]; function stripSkeleton(text) { let out = text; for (const label of SKELETON_WITH_DIGITS) out = out.split(label).join(" "); return out; } /** The numeric literals in a rendered percept as a MULTISET (token → occurrence count) — a strike, * a level, a budget, a P&L. Signs are kept (`-25.97` ≠ `25.97`). A true multiset (review F4): the * arm must carry every occurrence canonical does, so a dropped duplicate can never hide behind * another rendering of the same number (the Set version's blind spot — e.g. a strike shown in both * the kernel and the acting detail). Fixed skeleton labels are stripped first ({@link stripSkeleton}) * so a digit-bearing label (`-- L0/L1 ENGINE LOG --`) is never mistaken for a market value. */ export function numericFacts(text) { const m = new Map(); for (const tok of stripSkeleton(text).match(/-?\d+(?:\.\d+)?/g) ?? []) { m.set(tok, (m.get(tok) ?? 0) + 1); } return m; } /** Collect the FREE-TEXT (non-numeric) facts from a typed percept input — the values a numeric-only * check cannot see (wake reasons, plan names/states, order refs, notes, unavailable capabilities, * claim sources, instrument symbols). Driven from the INPUT (not re-parsed from text) so it is a * precise, non-brittle list of exactly what the arm MUST still contain. */ export function freeTextFacts(p) { const facts = []; const k = p.input.kernel; const push = (s) => { if (s !== null && s !== undefined && s !== "" && !/^-?\d+(?:\.\d+)?$/.test(s)) facts.push(s); }; if (p.kind === "WAKE") push(p.input.wakeReason); push(p.input.market.instrument); if (k.wake != null) { push(k.wake.reason); push(k.wake.severity); } for (const h of k.dataHealth ?? []) push(h.instrument); for (const u of k.unavailable ?? []) push(u); for (const o of k.resting ?? []) { push(o.ref); push(o.side); push(o.instrument); push(o.note); push(o.plan); } for (const pos of k.positions ?? []) { push(pos.instrument); push(pos.plan); } for (const f of k.fillsSinceLast ?? []) { push(f.side); push(f.instrument); push(f.plan); } for (const e of k.engineLog ?? []) { push(e.id); push(e.reason); } for (const c of k.claims ?? []) { push(c.claimType); push(c.field.source); push(c.field.modelVer); } for (const pl of k.plans ?? []) { push(pl.name); push(pl.state); push(pl.blockedReason); push(pl.note); } for (const inst of p.kind === "OPEN" ? p.input.instruments : []) { push(inst.symbol); push(inst.assetClass); } return facts; } /** * The information-parity GATE. Given the typed input, the canonical render, and the arm render, * assert the arm carries EVERY fact canonical does: * (1) numeric multiset: every numeric literal in canonical appears in the arm at least as often; * (2) free-text: every free-text fact from the INPUT appears as a substring of the arm. * Fails CLOSED ({@link InformationParityError}) on the FIRST dropped fact. PURE. This is the driver * a deliberately-lossy arm fixture must trip (the house rule: a guard ships with a failing fixture). */ export function assertInformationParity(p, canonicalText, armText) { const canon = numericFacts(canonicalText); const arm = numericFacts(armText); for (const [tok, need] of canon) { const have = arm.get(tok) ?? 0; if (have < need) { throw new InformationParityError(`token-optimal arm DROPPED a numeric fact: canonical shows "${tok}" ×${need}, the arm shows it ×${have}. ` + "A compaction that loses a level/strike/budget/P&L occurrence is a safety regression, not a token win (fail-closed)."); } } // Case-insensitive: a side ("buy") renders uppercased ("BUY") in BOTH renderers — the FACT is // present, only the case differs. A dropped fact is absent in any case. const armLower = armText.toLowerCase(); for (const fact of freeTextFacts(p)) { if (!armLower.includes(fact.toLowerCase())) { throw new InformationParityError(`token-optimal arm DROPPED a free-text fact from the input: ${JSON.stringify(fact)} is absent from the arm render ` + "(fail-closed — the arm must carry the same facts as canonical)."); } } } /** The UNGATED arm render (frame-kind dispatch + the arm's measured tape style). INTERNAL BUILDING * BLOCK: production callers MUST go through {@link renderTokenOptimal}, which runs the fail-closed * information-parity gate on every emit — this function exists as the emitter's default impl and as * the base a parity-fixture wraps. Exported for those two uses only. */ export function renderTokenOptimalUnchecked(p, arm = "token-optimal-fable") { const tape = ARM_TAPE_STYLE[arm]; return p.kind === "OPEN" ? renderBriefingArm(p.input, tape) : renderWakeDeltaArm(p.input, tape); } /** * Classify each blank-line-separated pane of a rendered percept as POPULATED (carries at least one * numeric fact) or DEAD (skeleton + placeholders only — `—`/`na`/`none`/`no prints`/`no legs`). * Works on BOTH the canonical and the arm render (the pane separator is the same `\n\n`). A frame * header pane (clock digits) counts as populated — the detector's target is FACT panes that show no * fact. Pure. A monitoring consumer flags a pane that is dead across ALL frames of a run (the * failed-breaks failure mode); a single degraded frame's dead pane is honest rendering, not a defect. */ export function panePopulation(rendered) { // A pane FACT is a STANDALONE number — not a digit embedded in a label token (`5m` bucket width, // `v1` version, `b76` receipt, `T-385m`): those are skeleton, and counting them was exactly how an // all-dash pane could masquerade as populated. Lookarounds exclude letter-adjacent digits. const FACT = /(?<![A-Za-z0-9])-?\d+(?:\.\d+)?(?![A-Za-z0-9])/; return rendered.split("\n\n").map((block) => { const firstLine = block.split("\n", 1)[0] ?? ""; return { pane: firstLine.slice(0, 60), dead: !FACT.test(stripSkeleton(block)) }; }); } /** The dead panes of a rendered percept (see {@link panePopulation}) — convenience projection. */ export function deadPanes(rendered) { return panePopulation(rendered) .filter((p) => p.dead) .map((p) => p.pane); } /** * Render a percept under a token-optimal arm — the PRODUCTION entry point, with the * information-parity gate WIRED INLINE (review F1; the house guard-wiring pattern): every emit * renders the canonical control from the SAME typed input and runs {@link assertInformationParity} * before the arm text is returned. A parity failure THROWS — the arm REFUSES to emit a percept that * dropped a fact, in production, not only under test. The `impl` parameter is the injectable seam a * failing fixture uses to prove this gate is wired through THIS path (unwire the assert below and * `tests/render-arm.parity.test.ts`'s runtime-gate fixture goes red). PURE + deterministic. */ export function renderTokenOptimal(p, arm = "token-optimal-fable", impl = renderTokenOptimalUnchecked) { const canonical = p.kind === "OPEN" ? renderBriefing(p.input) : renderWakeDelta(p.input); const armText = impl(p, arm); assertInformationParity(p, canonical, armText); // fail-closed: refuse a lossy emit (F1) return armText; }