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
JavaScript
/**
* # 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;
}