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.

555 lines (520 loc) 31.8 kB
/** * # cli/render/tables — three render branches for the registry rows (Kestrel CLI v1 §5) * * Pure functions of `(data, ctx)`. The data is IDENTICAL across the three branches; only the skin * changes: * - **human**: fixed-width glyph tables (`─` separators, right-aligned signed `usd()`, `✓` marks, * `shortId` truncation), ANSI color gated on `ctx.color`, cells truncated to `ctx.width`. * - **text**: one record per line, TAB-separated, no glyphs, no color, full precision (grep/LLM). * - **json**: one deterministic object `{schema, rows:[…]}`, keys sorted, numbers as JSON numbers. * * The Unicode/glyph printers live ONLY in the human branch (the machine branches are plain). */ import type { OutputCtx } from "../context.ts"; import type { LeaderboardRow, LineageInstance, PlanInstanceRow, RunRow, RunWithPlans } from "../heavy.ts"; import { isPlanDigest, type HostedRunRow } from "../../ledger/hosted.ts"; // ───────────────────────────────────────────────────────────────────────────── // Formatting primitives (human money glyph + padding) — human branch only // ───────────────────────────────────────────────────────────────────────────── const GREEN = "[32m"; const RED = "[31m"; const DIM = "[2m"; const BOLD = "[1m"; const RESET = "[0m"; function pad(s: string, w: number): string { return s.length >= w ? s : s + " ".repeat(w - s.length); } function padL(s: string, w: number): string { return s.length >= w ? s : " ".repeat(w - s.length) + s; } /** Signed dollars, 2dp (`+25.00`, `-270.00`) — the human money glyph. */ function usd(n: number): string { return (n >= 0 ? "+" : "") + n.toFixed(2); } function shortId(id: string): string { return id.slice(0, 12); } /** * A compact, legible plan/strategy LABEL for the runs table (kestrel-etyu): the `--plans` file's * BASENAME or the stand-down/bundled default's short head (its trailing "— supply --plans …" hint * dropped). An absent field (an old receipt written before the field existed) renders `—` — UNKNOWN, * fail-soft on the display path, never a fault. The label is for READABILITY; two runs of one path * edited in place share it — the {@link shortDigest} column is what tells them apart. The FULL label * is preserved verbatim in `runs show` and every json payload; this only trims the one-line list cell. */ function compactStrategy(strategy: string | undefined): string { if (strategy === undefined || strategy === "") return "—"; const head = strategy.split(" — ")[0]!; // drop the demo label's trailing "— supply --plans <file> …" return head.slice(head.lastIndexOf("/") + 1); // basename of a --plans path (no-op when there is no `/`) } /** The plan CONTENT identity, trimmed to a legible 8-hex prefix for the list cell (kestrel-etyu). This * is the field that distinguishes two runs of ONE `--plans` path edited in place. `—` for anything * {@link isPlanDigest} calls UNKNOWN (absent, empty, malformed) — the SAME predicate `samePlan` reads, * so the list cell and the compare verdict can never disagree about what is known. */ function shortDigest(digest: string | undefined): string { return isPlanDigest(digest) ? digest.slice(0, 8) : "—"; } /** * A DISPLAY-only column: header + alignment + a human cell string. Used by the human/text branches * ({@link glyphTable}/{@link tsv}), which need nothing else. `json` is optional HERE and serves one * purpose — a `money` column's colorizer reads it for the numeric value. * * This is the weaker of the two column types on purpose. {@link Col} (below) is what a json-rendered * table must use, and it REQUIRES the json pair — so `tsc`, not a docstring, keeps every column of a * machine schema in that schema. */ interface DisplayCol<R> { readonly header: string; readonly right: boolean; readonly cell: (r: R) => string; // shared by human + text /** When true, positive→green / negative→red in the human branch (money columns). Needs {@link json}. */ readonly money?: boolean; /** The numeric/raw value — read ONLY by the money colorizer on a display column. */ readonly json?: (r: R) => unknown; } /** * A JSON-PROJECTED column: a {@link DisplayCol} that additionally names its wire (key,value) pair. * `jsonKey`/`json` are REQUIRED — a table rendered through {@link jsonRowObjs} takes `Col<R>[]`, so a * column added without a json projection is a COMPILE error, never a field that silently vanishes from * `kestrel.runs/v1` / `kestrel.lineage/v1` / `kestrel.leaderboard/v1`. */ interface Col<R> extends DisplayCol<R> { readonly jsonKey: string; readonly json: (r: R) => unknown; } /** Render a header + rows as a fixed-width glyph table with optional color (human branch). */ function glyphTable<R>(cols: readonly DisplayCol<R>[], rows: readonly R[], ctx: OutputCtx): string { const headers = cols.map((c) => c.header); const body = rows.map((r) => cols.map((c) => c.cell(r))); const widths = headers.map((h, c) => Math.max(h.length, ...body.map((row) => (row[c] ?? "").length))); const colorCell = (raw: string, c: number, r: R): string => { if (!ctx.color) return raw; const col = cols[c]!; if (col.money && col.json !== undefined) { const v = col.json(r); if (typeof v === "number") return v >= 0 ? `${GREEN}${raw}${RESET}` : `${RED}${raw}${RESET}`; } return raw; }; const fmtHeader = headers.map((h, c) => (cols[c]!.right ? padL(h, widths[c]!) : pad(h, widths[c]!))); const headerLine = ctx.color ? fmtHeader.map((h) => `${BOLD}${h}${RESET}`).join(" ") : fmtHeader.join(" "); const sepRaw = widths.map((w) => "─".repeat(w)).join(" "); const sep = ctx.color ? `${DIM}${sepRaw}${RESET}` : sepRaw; const bodyLines = rows.map((r, ri) => cols .map((col, c) => { const raw = body[ri]![c]!; const padded = col.right ? padL(raw, widths[c]!) : pad(raw, widths[c]!); return colorCell(padded, c, r); }) .join(" "), ); return [headerLine, sep, ...bodyLines].join("\n"); } /** Text branch: TSV of the shared cell() strings, no header, no color. */ function tsv<R>(cols: readonly DisplayCol<R>[], rows: readonly R[]): string { return rows.map((r) => cols.map((c) => c.cell(r)).join("\t")).join("\n"); } /** The per-row `{key:value}` object for the json branch (keys sorted for determinism). */ function jsonRowObjs<R>(cols: readonly Col<R>[], rows: readonly R[]): Record<string, unknown>[] { return rows.map((r) => { const o: Record<string, unknown> = {}; for (const c of cols) o[c.jsonKey] = c.json(r); return sortKeys(o); }); } /** Json branch: `{schema, rows:[{key:value}]}`, keys sorted for determinism. */ function jsonRows<R>(schema: string, cols: readonly Col<R>[], rows: readonly R[]): string { return JSON.stringify({ schema, rows: jsonRowObjs(cols, rows) }); } /** Deterministic key ordering for machine JSON. */ function sortKeys<T extends Record<string, unknown>>(o: T): Record<string, unknown> { const out: Record<string, unknown> = {}; for (const k of Object.keys(o).sort()) out[k] = o[k]; return out; } // ───────────────────────────────────────────────────────────────────────────── // runs list // ───────────────────────────────────────────────────────────────────────────── const RUN_COLS: readonly Col<RunRow>[] = [ // `source` labels the run's lineage (`local` = a `run`/`day` session recorded here; `hosted` = a // `sim` receipt, rendered from HOSTED_COLS below) so a merged `runs list` is legible (kestrel-pvlc). { header: "source", right: false, cell: () => "local", jsonKey: "source", json: () => "local" }, { header: "run_id", right: false, cell: (r) => shortId(r.run_id), jsonKey: "run_id", json: (r) => r.run_id }, { header: "date", right: false, cell: (r) => r.session_date, jsonKey: "session_date", json: (r) => r.session_date }, { header: "mode", right: false, cell: (r) => r.mode, jsonKey: "mode", json: (r) => r.mode }, { header: "fill_model", right: false, cell: (r) => r.fill_model, jsonKey: "fill_model", json: (r) => r.fill_model }, { header: "cal", right: false, cell: (r) => (r.calibrated ? "✓" : ""), jsonKey: "calibrated", json: (r) => Boolean(r.calibrated) }, { header: "realized", right: true, money: true, cell: (r) => usd(r.realized_floor_usd), jsonKey: "realized_floor_usd", json: (r) => r.realized_floor_usd }, { header: "expected", right: true, money: true, cell: (r) => usd(r.expected_usd), jsonKey: "expected_usd", json: (r) => r.expected_usd }, { header: "premium", right: true, cell: (r) => r.premium_spent.toFixed(2), jsonKey: "premium_spent", json: (r) => r.premium_spent }, { header: "events", right: true, cell: (r) => String(r.bus_events), jsonKey: "bus_events", json: (r) => r.bus_events }, ]; /** The hosted-lineage columns (`sim` receipts, kestrel-pvlc). Same three skins as the local runs; * the shared `source` column ties the two lineages into one `runs list`. The human/text branches show * a readable subset; the json branch emits the FULL {@link HostedRunRow} (every artifact id + the * signed timestamp) so an agent can re-resolve the whole receipt, not just what fits a glyph table. * * Hence {@link DisplayCol}, not {@link Col}: these columns are the human/text SUBSET and never project * the wire schema (the spread in {@link renderRuns} does), so they carry no `jsonKey`/`json` pair — and * the type makes passing them to {@link jsonRowObjs} a compile error rather than a silent half-schema. */ const HOSTED_COLS: readonly DisplayCol<HostedRunRow>[] = [ { header: "source", right: false, cell: (h) => h.source }, { header: "scenario", right: false, cell: (h) => h.slug }, // The plan that produced the run (kestrel-etyu) — the human LABEL for readability + the CONTENT // digest that actually distinguishes two runs of ONE path edited in place ("did my tweak help?"). { header: "strategy", right: false, cell: (h) => compactStrategy(h.strategy) }, { header: "digest", right: false, cell: (h) => shortDigest(h.strategy_digest) }, { header: "operation", right: false, cell: (h) => h.operation_id }, { header: "orders", right: true, cell: (h) => String(h.order_count) }, { header: "fills", right: true, cell: (h) => String(h.fill_count) }, // `json` here is NOT a wire projection — it is the money colorizer's numeric accessor (see DisplayCol). { header: "realized", right: true, money: true, cell: (h) => usd(h.realized_pnl), json: (h) => h.realized_pnl }, { header: "proof", right: false, cell: (h) => h.proof_url }, ]; /** * Render the local runs and the hosted-lineage receipts as ONE `runs list` (kestrel-pvlc). The two * lineages carry different columns, so human/text keep them as two labeled sections; json merges them * into a single self-describing `rows` array (each row's `source` names its lineage). An all-empty * result stays the prior `(no runs)` / empty-payload sentinel byte-for-byte. */ export function renderRuns(rows: readonly RunRow[], ctx: OutputCtx, hosted: readonly HostedRunRow[] = []): string { if (ctx.mode === "json") { const local = jsonRowObjs(RUN_COLS, rows); // The full receipt, not the glyph subset — sorted keys for determinism, like every json row. const hostedObjs = hosted.map((h) => sortKeys({ ...h })); return JSON.stringify({ schema: "kestrel.runs/v1", rows: [...local, ...hostedObjs] }) + "\n"; } if (ctx.mode === "text") { const parts: string[] = []; if (rows.length > 0) parts.push(tsv(RUN_COLS, rows)); if (hosted.length > 0) parts.push(tsv(HOSTED_COLS, hosted)); return parts.length === 0 ? "" : parts.join("\n") + "\n"; } if (rows.length === 0 && hosted.length === 0) return "(no runs)\n"; const parts: string[] = []; if (rows.length > 0) parts.push(glyphTable(RUN_COLS, rows, ctx) + `\n\n${rows.length} run(s)`); if (hosted.length > 0) parts.push(glyphTable(HOSTED_COLS, hosted, ctx) + `\n\n${hosted.length} hosted run(s)`); return parts.join("\n\n") + "\n"; } // ───────────────────────────────────────────────────────────────────────────── // runs show (a run header block + its plans table) // ───────────────────────────────────────────────────────────────────────────── const PLAN_COLS: readonly Col<PlanInstanceRow>[] = [ { header: "plan", right: false, cell: (p) => p.name, jsonKey: "name", json: (p) => p.name }, { header: "final_state", right: false, cell: (p) => p.final_state, jsonKey: "final_state", json: (p) => p.final_state }, { header: "outcome", right: false, cell: (p) => p.outcome ?? "—", jsonKey: "outcome", json: (p) => p.outcome }, { header: "fired", right: true, cell: (p) => (p.fired ? "yes" : "no"), jsonKey: "fired", json: (p) => Boolean(p.fired) }, { header: "orders", right: true, cell: (p) => String(p.orders), jsonKey: "orders", json: (p) => p.orders }, { header: "fills", right: true, cell: (p) => String(p.fills), jsonKey: "fills", json: (p) => p.fills }, { header: "realized", right: true, money: true, cell: (p) => usd(p.realized_usd), jsonKey: "realized_usd", json: (p) => p.realized_usd }, { header: "expected", right: true, money: true, cell: (p) => usd(p.expected_usd), jsonKey: "expected_usd", json: (p) => p.expected_usd }, ]; export function renderRunShow(rec: RunWithPlans, ctx: OutputCtx): string { const r = rec.run; if (ctx.mode === "json") { const run: Record<string, unknown> = { run_id: r.run_id, session_date: r.session_date, mode: r.mode, instruments: r.instruments, fill_model: r.fill_model, calibrated: Boolean(r.calibrated), recorded_at: r.recorded_at, r_usd: r.r_usd, realized_floor_usd: r.realized_floor_usd, expected_usd: r.expected_usd, premium_spent: r.premium_spent, bus_events: r.bus_events, bus_sha256: r.bus_sha256, plans_sha256: r.plans_sha256, determinism_hash: r.determinism_hash, report_path: r.report_path, }; const plans = rec.plans.map((p) => sortKeys({ name: p.name, final_state: p.final_state, outcome: p.outcome, fired: Boolean(p.fired), orders: p.orders, fills: p.fills, realized_usd: p.realized_usd, expected_usd: p.expected_usd, }), ); return JSON.stringify({ schema: "kestrel.run/v1", run: sortKeys(run), plans }) + "\n"; } if (ctx.mode === "text") { const lines = [ `run_id\t${r.run_id}`, `session_date\t${r.session_date}`, `mode\t${r.mode}`, `instruments\t${r.instruments}`, `fill_model\t${r.fill_model}`, `calibrated\t${Boolean(r.calibrated)}`, `recorded_at\t${r.recorded_at}`, `r_usd\t${r.r_usd}`, `realized_floor_usd\t${r.realized_floor_usd}`, `expected_usd\t${r.expected_usd}`, `premium_spent\t${r.premium_spent}`, `bus_events\t${r.bus_events}`, `bus_sha256\t${r.bus_sha256}`, `determinism_hash\t${r.determinism_hash}`, `report_path\t${r.report_path ?? ""}`, ]; const plansTsv = rec.plans.length === 0 ? "" : "\n" + tsv(PLAN_COLS, rec.plans); return lines.join("\n") + plansTsv + "\n"; } // human const head = [ `run_id ${r.run_id}`, `session ${r.session_date} ${r.mode} [${r.instruments}]`, `fill_model ${r.fill_model}${r.calibrated ? " (calibrated)" : ""}`, `recorded_at ${r.recorded_at} (settle ts, injected)`, `r_usd ${r.r_usd.toFixed(2)}`, `totals realized ${usd(r.realized_floor_usd)} expected ${usd(r.expected_usd)} premium ${r.premium_spent.toFixed(2)}`, `bus ${r.bus_events} events sha ${shortId(r.bus_sha256)} det ${shortId(r.determinism_hash)}`, r.report_path !== null ? `report_path ${r.report_path}` : "report_path (none)", "", ].join("\n"); const plansTable = rec.plans.length === 0 ? "(no plan instances)\n" : glyphTable(PLAN_COLS, rec.plans, ctx) + "\n"; return head + "\n" + plansTable; } /** * Re-print a HOSTED `sim` receipt (kestrel-pvlc) — the full print-once story the sim showed exactly once * on stdout, resolved back from the local `hosted-runs.jsonl` lineage: the scenario, the grade digest, * every durable evidence artifact id, and the shareable proof URL (the last human line, mirroring the sim * story). A distinct `kestrel.hosted-run/v1` json schema so a hosted receipt is never mistaken for a local * run's shape. Pure text — this render path is node-light, like the store it reads. */ export function renderHostedRunShow(h: HostedRunRow, ctx: OutputCtx): string { if (ctx.mode === "json") { return JSON.stringify({ schema: "kestrel.hosted-run/v1", hosted_run: sortKeys({ ...h }) }) + "\n"; } if (ctx.mode === "text") { return ( [ `source\t${h.source}`, `operation_id\t${h.operation_id}`, `slug\t${h.slug}`, `requested_slug\t${h.requested_slug}`, `strategy\t${h.strategy ?? ""}`, `strategy_digest\t${isPlanDigest(h.strategy_digest) ? h.strategy_digest : ""}`, `order_count\t${h.order_count}`, `fill_count\t${h.fill_count}`, `realized_pnl\t${h.realized_pnl}`, // The grader's never-fired reason (kestrel-kglw) — present ONLY on a re-shown 0-order run that // carried one, so a returning user re-reads WHY nothing fired instead of a bare order_count=0. ...(h.never_fired_reason !== undefined ? [`never_fired_reason\t${h.never_fired_reason}`] : []), `grade_artifact_id\t${h.grade_artifact_id}`, `bus_artifact_id\t${h.bus_artifact_id}`, `manifest_artifact_id\t${h.manifest_artifact_id}`, `issued_at\t${h.issued_at}`, ...(h.copy_token !== undefined ? [`continues_copy_token\t${h.copy_token}`] : []), `proof_url\t${h.proof_url}`, ].join("\n") + "\n" ); } // human — the proof URL is the LAST line, mirroring the sim story's ordering. const aliasLine = h.requested_slug !== h.slug ? [`alias ${h.requested_slug}${h.slug}`] : []; // The recorded continuation lineage (kestrel-gu9h): the opaque copy-token this run continued from. const lineageLine = h.copy_token !== undefined ? [`continues copy-token ${h.copy_token}`] : []; return ( [ `operation ${h.operation_id} (source: hosted)`, `scenario ${h.slug}`, ...aliasLine, `strategy ${h.strategy ?? "(unknown — receipt predates the plan field)"}`, `plan digest ${isPlanDigest(h.strategy_digest) ? h.strategy_digest : "(unknown — predates the plan digest, or unusable)"}`, `grade order_count=${h.order_count} fill_count=${h.fill_count} realized_pnl=${usd(h.realized_pnl)}`, // The never-fired reason (kestrel-kglw): a re-shown 0-order run states WHY nothing fired, exactly // as the sim story did once — so `runs show` is never the bare `order_count=0` the receipt used to // re-print (the silent-gate class, one surface downstream). Only when the receipt carried a reason. ...(h.never_fired_reason !== undefined ? [`why no orders fired — ${h.never_fired_reason}`] : []), `evidence manifest=${h.manifest_artifact_id} bus=${h.bus_artifact_id} grade=${h.grade_artifact_id}`, ...lineageLine, `issued_at ${h.issued_at === "" ? "(unstamped)" : h.issued_at}`, `proof ${h.proof_url}`, "", ].join("\n") ); } // ───────────────────────────────────────────────────────────────────────────── // runs compare (kestrel-etyu) — diff two hosted `sim` receipts, "did my tweak help?" // ───────────────────────────────────────────────────────────────────────────── /** Signed integer delta glyph (`+3`, `-1`, `0`) — deterministic, no locale. */ function signedInt(n: number): string { return (n > 0 ? "+" : "") + String(n); } /** Signed 2dp money delta (`+12.00`, `-12.00`) reusing {@link usd}. */ function signedUsd(n: number): string { return usd(n); } /** * The plan-identity relationship between two receipts (kestrel-etyu): do they carry the SAME plan * CONTENT? `true`/`false` only when BOTH digests are KNOWN per {@link isPlanDigest}; `null` whenever * either is unknown (absent, empty, or malformed) — never a guessed verdict in EITHER direction. This * is the signal that makes "did my tweak help?" answerable even when both runs used the SAME `--plans` * path (an in-place edit moves the digest). * * It reads the shared predicate rather than an `=== undefined` check on purpose: a bare undefined-check * let an empty-string digest count as known, so two UNKNOWN digests compared EQUAL and this returned * `true` — "A and B ran the SAME plan content" — for runs the list was rendering `—`. Unknown ≠ match. */ function samePlan(a: HostedRunRow, b: HostedRunRow): boolean | null { if (!isPlanDigest(a.strategy_digest) || !isPlanDigest(b.strategy_digest)) return null; return a.strategy_digest === b.strategy_digest; } /** * Diff two HOSTED `sim` receipts (kestrel-etyu) — the returning user's "did my tweak help?" job. `a` * is the baseline, `b` the tweak; every delta is `b − a` (how B moved from A). It shows WHICH plan * each run used — its LABEL *and* its CONTENT digest, plus whether the two are the same plan content * — and diffs the graded activity metrics (orders, fills, realized P&L). The digest is what makes the * in-place-edit workflow work: two runs of ONE `--plans` path share a label but differ by digest. * * Pure + deterministic: integer/`toFixed` arithmetic + a hex compare over the two rows only — no wall * clock, no RNG, so the same pair renders byte-identically. A missing `strategy`/`strategy_digest` (a * pre-field receipt) reads `(unknown)` and yields `same_plan: null`; comparing two DIFFERENT scenarios * is not blocked but is flagged, since cross-scenario P&L is not directly comparable. */ export function renderRunsCompare(a: HostedRunRow, b: HostedRunRow, ctx: OutputCtx): string { const dOrders = b.order_count - a.order_count; const dFills = b.fill_count - a.fill_count; const dPnl = b.realized_pnl - a.realized_pnl; const stratA = a.strategy ?? "(unknown)"; const stratB = b.strategy ?? "(unknown)"; const sameScenario = a.slug === b.slug; const same = samePlan(a, b); if (ctx.mode === "json") { return ( JSON.stringify({ schema: "kestrel.runs-compare/v1", same_scenario: sameScenario, // Whether A and B ran the SAME plan CONTENT (by digest); null when either digest is unknown. same_plan: same, a: sortKeys({ ...a }), b: sortKeys({ ...b }), // Deltas are B − A (the tweak relative to the baseline) — the activity family the receipt carries. delta: sortKeys({ order_count: dOrders, fill_count: dFills, realized_pnl: dPnl }), }) + "\n" ); } if (ctx.mode === "text") { const lines = [ `a_operation_id\t${a.operation_id}`, `b_operation_id\t${b.operation_id}`, `a_scenario\t${a.slug}`, `b_scenario\t${b.slug}`, `same_scenario\t${sameScenario}`, `a_strategy\t${a.strategy ?? ""}`, `b_strategy\t${b.strategy ?? ""}`, `a_strategy_digest\t${isPlanDigest(a.strategy_digest) ? a.strategy_digest : ""}`, `b_strategy_digest\t${isPlanDigest(b.strategy_digest) ? b.strategy_digest : ""}`, `same_plan\t${same === null ? "unknown" : same}`, `metric\ta\tb\tdelta`, `order_count\t${a.order_count}\t${b.order_count}\t${dOrders}`, `fill_count\t${a.fill_count}\t${b.fill_count}\t${dFills}`, `realized_pnl\t${a.realized_pnl}\t${b.realized_pnl}\t${dPnl}`, ]; return lines.join("\n") + "\n"; } // human — a small metric table (A | B | Δ) under a header naming each run's scenario + plan (label // AND content digest). The plan line states whether A and B ran the same plan content. const metrics: readonly { label: string; a: string; b: string; d: string }[] = [ { label: "orders", a: String(a.order_count), b: String(b.order_count), d: signedInt(dOrders) }, { label: "fills", a: String(a.fill_count), b: String(b.fill_count), d: signedInt(dFills) }, { label: "realized", a: usd(a.realized_pnl), b: usd(b.realized_pnl), d: signedUsd(dPnl) }, ]; const wLabel = Math.max(6, ...metrics.map((m) => m.label.length)); const wA = Math.max(1, "A".length, ...metrics.map((m) => m.a.length)); const wB = Math.max(1, "B".length, ...metrics.map((m) => m.b.length)); const wD = Math.max(1, "Δ".length, ...metrics.map((m) => m.d.length)); const planNote = same === null ? "plan content identity UNKNOWN (a receipt predates the plan digest, or carries an unusable one)" : same ? "plan A and B ran the SAME plan content (digests match)" : "plan A and B ran DIFFERENT plan content (digests differ) — a real tweak"; const head = [ `compare · ${sameScenario ? a.slug : `${a.slug}${b.slug}`}`, `A ${shortId(a.operation_id)} ${a.slug}`, ` plan ${stratA} #${shortDigest(a.strategy_digest)}`, `B ${shortId(b.operation_id)} ${b.slug}`, ` plan ${stratB} #${shortDigest(b.strategy_digest)}`, planNote, ...(sameScenario ? [] : ["note different scenarios — P&L is not directly comparable"]), "", `${pad("metric", wLabel)} ${padL("A", wA)} ${padL("B", wB)} ${padL("Δ", wD)}`, `${"─".repeat(wLabel)} ${"─".repeat(wA)} ${"─".repeat(wB)} ${"─".repeat(wD)}`, ]; const rows = metrics.map((m) => `${pad(m.label, wLabel)} ${padL(m.a, wA)} ${padL(m.b, wB)} ${padL(m.d, wD)}`); return [...head, ...rows, ""].join("\n"); } // ───────────────────────────────────────────────────────────────────────────── // lineage // ───────────────────────────────────────────────────────────────────────────── const LINEAGE_COLS: readonly Col<LineageInstance>[] = [ { header: "date", right: false, cell: (i) => i.session_date, jsonKey: "session_date", json: (i) => i.session_date }, { header: "mode", right: false, cell: (i) => i.mode, jsonKey: "mode", json: (i) => i.mode }, { header: "fill_model", right: false, cell: (i) => i.fill_model, jsonKey: "fill_model", json: (i) => i.fill_model }, { header: "final_state", right: false, cell: (i) => i.final_state, jsonKey: "final_state", json: (i) => i.final_state }, { header: "outcome", right: false, cell: (i) => i.outcome ?? "—", jsonKey: "outcome", json: (i) => i.outcome }, { header: "fired", right: true, cell: (i) => (i.fired ? "yes" : "no"), jsonKey: "fired", json: (i) => Boolean(i.fired) }, { header: "fills", right: true, cell: (i) => String(i.fills), jsonKey: "fills", json: (i) => i.fills }, { header: "realized", right: true, money: true, cell: (i) => usd(i.realized_usd), jsonKey: "realized_usd", json: (i) => i.realized_usd }, { header: "expected", right: true, money: true, cell: (i) => usd(i.expected_usd), jsonKey: "expected_usd", json: (i) => i.expected_usd }, ]; /** * Render a plan lineage. `coverage`, when supplied (the command passes it on an EMPTY result, * kestrel-qozv), names what lineage DOES track so an empty result is never SILENT: json carries it * as a structured `coverage` field (machine-legible, not advisory noise), human appends it under the * `(no instances)` sentinel. text stays a pure TSV payload — the command mirrors the notice to stderr. */ export function renderLineage( name: string, rows: readonly LineageInstance[], ctx: OutputCtx, coverage?: string, ): string { if (ctx.mode === "json") { const body = JSON.parse(jsonRows("kestrel.lineage/v1", LINEAGE_COLS, rows)) as { schema: string; rows: unknown[] }; return ( JSON.stringify({ schema: body.schema, name, rows: body.rows, ...(coverage !== undefined ? { coverage } : {}), }) + "\n" ); } if (ctx.mode === "text") return rows.length === 0 ? "" : tsv(LINEAGE_COLS, rows) + "\n"; if (rows.length === 0) return `lineage: ${name}\n\n(no instances)\n${coverage !== undefined ? `\n${coverage}\n` : ""}`; const realizedSum = rows.reduce((s, i) => s + i.realized_usd, 0); const expectedSum = rows.reduce((s, i) => s + i.expected_usd, 0); return ( `lineage: ${name}\n\n` + glyphTable(LINEAGE_COLS, rows, ctx) + `\n\n${rows.length} instance(s) realized ${usd(realizedSum)} expected ${usd(expectedSum)}\n` ); } // ───────────────────────────────────────────────────────────────────────────── // leaderboard // ───────────────────────────────────────────────────────────────────────────── const LEADER_COLS: readonly Col<LeaderboardRow>[] = [ { header: "lineage", right: false, cell: (r) => r.name, jsonKey: "name", json: (r) => r.name }, { header: "runs", right: true, cell: (r) => String(r.runs), jsonKey: "runs", json: (r) => r.runs }, { header: "fired", right: true, cell: (r) => String(r.fired), jsonKey: "fired", json: (r) => r.fired }, { header: "fills", right: true, cell: (r) => String(r.fills), jsonKey: "fills", json: (r) => r.fills }, { header: "realized_sum", right: true, money: true, cell: (r) => usd(r.realized_sum), jsonKey: "realized_sum", json: (r) => r.realized_sum }, { header: "expected_sum", right: true, money: true, cell: (r) => usd(r.expected_sum), jsonKey: "expected_sum", json: (r) => r.expected_sum }, { header: "realized_avg", right: true, money: true, cell: (r) => usd(r.realized_avg), jsonKey: "realized_avg", json: (r) => r.realized_avg }, { header: "expected_avg", right: true, money: true, cell: (r) => usd(r.expected_avg), jsonKey: "expected_avg", json: (r) => r.expected_avg }, ]; export function renderLeaderboard(rows: readonly LeaderboardRow[], ctx: OutputCtx): string { if (ctx.mode === "json") return jsonRows("kestrel.leaderboard/v1", LEADER_COLS, rows) + "\n"; if (ctx.mode === "text") return rows.length === 0 ? "" : tsv(LEADER_COLS, rows) + "\n"; if (rows.length === 0) return "(no lineages)\n"; return glyphTable(LEADER_COLS, rows, ctx) + `\n\n${rows.length} lineage(s)\n`; }