From 4dbba559efaa5f25485d4c440fc1ab907c148b77 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:53:15 +0100 Subject: [PATCH] a panel view read an unasked provider as a refusal, so bench 311 was built around it The ledger picked a row's value with `activePanel.values[r.slug] ?? 0`. A metric panel holds one figure per provider and a provider we could not ask has none, so that zero is not a missing reading. On a higher-is-better board it is the worst possible one, printed as a number, ranked last, with a data bar beside it. Bench 311 hit this and routed around it rather than through it. Its capability figures are published as columns, never as selectable views, and its methodology says why in its own words: "In a chart view the board renders one metric for every row, and a provider with no reading for it is shown as a zero rather than left out, which would state a refusal we never received." The columns were the workaround. Earlier today the same zero reached a page anyway, through `?view=` on panels declared `tab: false`, and that was closed by shutting the route rather than fixing what it exposed. The second half is quieter and was never noticed. The same `?? 0` fed the field mean, which is the denominator of every row's delta, so one unmeasured provider moved the percentage printed next to all the others. Four providers reading 100 with one unasked produced a mean of 75 and gave the other three a delta of +33 percent against a field that does not exist. Columns already had the convention right: a custom cell renders "-" when its value is null. This brings the panel path to the same rule. A missing value is null, it prints as a dash, it carries no bar, it is excluded from the mean, and it sorts last in BOTH directions, which one sentinel cannot do: the worst place on a latency board is the largest number and on a coverage board it is zero. Using zero for both is what put an unmeasured provider at the top of a latency table. The rules are in src/lib/ledger-value.ts rather than inline, because a rule nobody can call is a rule nobody can test, and this one has three cases that each fail differently. Eleven tests, mutation-checked twice: collapsing the sort sentinel to zero fails one, and counting absences in the mean fails four. A measured zero still passes through untouched, which matters now that a getLogs window of 0 blocks is a real reading. What this unlocks is the point. A capability, a window or any other per-provider figure can now live in the view selector on any board without lying about the rows it could not measure, which is where a reader choosing a provider would actually look for it. tsc clean, 827 tests pass. --- src/components/ledger-table.tsx | 48 ++++++++++----- src/lib/ledger-value.test.ts | 103 ++++++++++++++++++++++++++++++++ src/lib/ledger-value.ts | 58 ++++++++++++++++++ 3 files changed, 194 insertions(+), 15 deletions(-) create mode 100644 src/lib/ledger-value.test.ts create mode 100644 src/lib/ledger-value.ts diff --git a/src/components/ledger-table.tsx b/src/components/ledger-table.tsx index ef7729fd3..390cb715d 100644 --- a/src/components/ledger-table.tsx +++ b/src/components/ledger-table.tsx @@ -3,6 +3,7 @@ import { useMemo, useState } from "react"; import { rateDecimals, successTitle } from "@/lib/sample-precision"; import { rowHref } from "@/lib/row-link"; +import { barScale, fieldMean, sortableValue } from "@/lib/ledger-value"; import Link from "next/link"; import { ChevronDown, ChevronUp } from "lucide-react"; @@ -230,11 +231,25 @@ export function LedgerTable({ // window's first custom column, or `panel.values[slug]` when a panel // tab is active. Used for sort, filter, the inline data bar, and the // displayed value in the headline column. - const pickValue = (r: ProviderResult): number => { - if (activePanel) return activePanel.values[r.slug] ?? 0; - if (customCols) return colValueW(r, customCols[0]) ?? 0; + // The honest value, which can be absent. A panel holds one figure per + // provider and a provider we could not ask has none; coercing that to 0 + // publishes a refusal nobody gave. Bench 311 renders its capability + // figures as columns for exactly this reason, and said so in its + // methodology rather than fixing it here. + // + // Columns already distinguish the two: a custom cell renders "-" when its + // value is null. This brings the panel path to the same convention. + const pickValueOrNull = (r: ProviderResult): number | null => { + if (activePanel) return activePanel.values[r.slug] ?? null; + if (customCols) return colValueW(r, customCols[0]) ?? null; return r.ms.p50; }; + // Numeric form for the places that need a scalar and have no way to + // express absence: sort position, the data-bar scale. Missing sorts as + // the worst value rather than the best, which is what 0 was doing on a + // higher-is-better board. + const pickValue = (r: ProviderResult): number => + sortableValue(pickValueOrNull(r), higherIsBetter); // Detected from the first provider's results — if ANY provider declares // slot_p50/slot_p99 in its YAML queries, every row gets the column (with // "-" for providers that don't declare it). Used by Solana-native benches @@ -263,9 +278,9 @@ export function LedgerTable({ // Field-mean Δ — duplicated here so the sort comparator can rank by it // without waiting on per-Row computation. Kept aligned with the same // formula the Row uses for the displayed Δ% (see deltaPct below). - const fieldMeanRaw = - results.reduce((s, r) => s + pickValue(r), 0) / - Math.max(1, results.length); + // Rows with no value are excluded rather than counted as zero: a mean + // dragged down by absences makes every other row's delta wrong too. + const fieldMeanRaw = fieldMean(results.map((r) => pickValueOrNull(r))); const deltaForSort = (r: ProviderResult): number => { if (fieldMeanRaw === 0) return 0; return (pickValue(r) - fieldMeanRaw) / fieldMeanRaw; @@ -416,7 +431,7 @@ export function LedgerTable({ // Max + field-mean of the active column. Computed off `sorted` (the // scored set) so the field mean isn't pulled to zero by augmented // unavailable rows, which would flatten every delta to +999%. - const maxValue = Math.max(...sorted.map((r) => pickValue(r))) || 1; + const maxValue = barScale(sorted.map((r) => pickValueOrNull(r))); const fieldValue = sorted.reduce((s, r) => s + pickValue(r), 0) / Math.max(1, sorted.length); @@ -698,7 +713,7 @@ export function LedgerTable({ r={r} i={i} unit={unit} - value={pickValue(r)} + value={pickValueOrNull(r)} fieldValue={fieldValue} maxValue={maxValue} panelActive={panelActive} @@ -739,7 +754,7 @@ export function LedgerTable({ r={r} i={-1} unit={unit} - value={pickValue(r)} + value={pickValueOrNull(r)} fieldValue={fieldValue} maxValue={maxValue} panelActive={panelActive} @@ -825,7 +840,7 @@ function Row({ r: ProviderResult; i: number; unit: string; - value: number; + value: number | null; fieldValue: number; maxValue: number; panelActive: boolean; @@ -858,9 +873,10 @@ function Row({ const isUnranked = !isUnresponsive && !!r.unrankedLabel; const isOffline = !isUnresponsive && !isUnranked && r.availability === "unavailable"; const isMuted = isOffline || isUnresponsive; - const deltaPct = fieldValue > 0 ? ((value - fieldValue) / fieldValue) * 100 : 0; + const deltaPct = + value != null && fieldValue > 0 ? ((value - fieldValue) / fieldValue) * 100 : 0; const deltaSign = deltaPct > 0 ? "+" : deltaPct < 0 ? "−" : "±"; - const barPct = Math.max(2, (value / maxValue) * 100); + const barPct = value == null ? 0 : Math.max(2, (value / maxValue) * 100); return ( @@ -1134,9 +1150,11 @@ function Row({ ? customCells[0].v != null ? fmtUnit(customCells[0].v, customCells[0].unit) : "-" - : panelActive - ? fmtUnit(value, unit) - : fmtHeadline(value, { ...benchmark, unit }, r.slug)} + : value == null + ? "-" + : panelActive + ? fmtUnit(value, unit) + : fmtHeadline(value, { ...benchmark, unit }, r.slug)} diff --git a/src/lib/ledger-value.test.ts b/src/lib/ledger-value.test.ts new file mode 100644 index 000000000..3e189eef9 --- /dev/null +++ b/src/lib/ledger-value.test.ts @@ -0,0 +1,103 @@ +import { describe, expect, it } from "bun:test"; + +import { barScale, fieldMean, sortableValue } from "@/lib/ledger-value"; + +/** + * The case these exist for, in full, because it is the reason bench 311 was + * built around the problem instead of through it. + * + * A metric panel holds one figure per provider. A provider we could not ask + * has none, and the table read that as `?? 0`. On a higher-is-better board a + * zero is not an absence, it is the worst possible reading, printed as a + * number with a bar beside it. Bench 311's methodology states the + * consequence plainly: "a provider with no reading for it is shown as a zero + * rather than left out, which would state a refusal we never received", and + * its capability figures were published as columns to avoid it. + * + * The same `?? 0` was also in the field mean, which is the denominator of + * every row's delta, so one unmeasured provider moved the percentage printed + * next to all the others. + */ +describe("sortableValue", () => { + it("sorts a missing value last on a lower-is-better board", () => { + // Latency. The worst place is the largest number, and 0 would have put an + // unmeasured provider at the top of the table. + expect(sortableValue(null, false)).toBe(Number.POSITIVE_INFINITY); + expect(sortableValue(null, false)).toBeGreaterThan(sortableValue(99999, false)); + }); + + it("sorts a missing value last on a higher-is-better board", () => { + // Capability, in percent. Here the worst place IS zero, which is why one + // sentinel cannot serve both directions. + expect(sortableValue(null, true)).toBe(0); + expect(sortableValue(null, true)).toBeLessThan(sortableValue(0.1, true)); + }); + + it("passes a real zero through unchanged", () => { + // A measured 0 is a refusal we did receive, and it must keep its place: + // a getLogs window of 0 blocks means the narrowest span was refused, + // which is a fact about the provider rather than about our access. + expect(sortableValue(0, true)).toBe(0); + expect(sortableValue(0, false)).toBe(0); + }); +}); + +describe("fieldMean", () => { + it("excludes missing rows instead of counting them as zero", () => { + // Four providers, one unmeasured. The mean of what was measured is 100. + // Counting the absence drags it to 75 and every other row's delta with it. + expect(fieldMean([100, 100, 100, null])).toBe(100); + }); + + it("is the plain mean when nothing is missing", () => { + expect(fieldMean([10, 20, 30])).toBe(20); + }); + + it("returns zero when nothing was measured", () => { + // The caller reads 0 as "no delta to show" rather than dividing by it. + expect(fieldMean([null, null])).toBe(0); + expect(fieldMean([])).toBe(0); + }); + + it("ignores the infinite sentinel if one ever reaches it", () => { + // sortableValue's lower-is-better sentinel is Infinity. If the two paths + // were ever crossed, a single Infinity would make the mean Infinity and + // every delta NaN, which renders as nothing at all and looks like a data + // outage rather than a bug. + expect(fieldMean([10, 20, Number.POSITIVE_INFINITY])).toBe(15); + }); +}); + +describe("barScale", () => { + it("scales to the largest measured value", () => { + expect(barScale([10, 40, null])).toBe(40); + }); + + it("never returns zero, so dividing by it stays safe", () => { + expect(barScale([])).toBe(1); + expect(barScale([null, null])).toBe(1); + expect(barScale([0, 0])).toBe(1); + }); + + it("refuses an infinite sentinel, which would flatten every real bar", () => { + // This is the failure that would be hardest to read on the page: every + // bar becomes a sliver and nothing errors. + expect(barScale([10, 40, Number.POSITIVE_INFINITY])).toBe(40); + }); +}); + +describe("the three together, on the shape that caused this", () => { + it("one unasked provider changes neither the mean nor the scale", () => { + // Bench 311's robinhood tab: five providers, one of which we could not + // ask on that chain. Measured values are the getLogs windows. + const measured = [500, 10_000, 10_000, 1_000_000]; + const withGap: (number | null)[] = [...measured, null]; + expect(fieldMean(withGap)).toBe(fieldMean(measured)); + expect(barScale(withGap)).toBe(barScale(measured)); + // And the unasked row is last rather than first on a board where more + // is better. + const keys = withGap.map((v) => sortableValue(v, true)); + expect(Math.min(...keys)).toBe(0); + expect(keys[keys.length - 1]).toBe(0); + }); +}); diff --git a/src/lib/ledger-value.ts b/src/lib/ledger-value.ts new file mode 100644 index 000000000..c820dc593 --- /dev/null +++ b/src/lib/ledger-value.ts @@ -0,0 +1,58 @@ +/** + * What the ledger does with a row that has no value. + * + * A metric panel holds one figure per provider, and a provider we could not + * ask has none. The table read that absence as `?? 0`, which on a + * higher-is-better board is not a missing reading, it is the worst possible + * one: a refusal nobody gave, ranked last, with a data bar of zero width and + * a number printed next to it. + * + * Bench 311 ran into this and routed around it rather than through it. Its + * capability figures are published as columns, and its methodology says why: + * "In a chart view the board renders one metric for every row, and a provider + * with no reading for it is shown as a zero rather than left out, which would + * state a refusal we never received." The columns were the workaround. This is + * the fix, and with it a capability or a window can live in the view selector + * on any board without lying about the rows it could not measure. + * + * Columns already had the convention right: a custom cell renders "-" when its + * value is null. Everything here brings the panel path to that same rule. + */ + +/** Sort key for a possibly-absent value. + * + * Absent must sort LAST in both directions, which a single sentinel cannot + * do: on a lower-is-better board the worst value is the largest, and on a + * higher-is-better board it is the smallest. Using 0 for both is what put an + * unmeasured provider at the top of a latency board. */ +export function sortableValue( + value: number | null, + higherIsBetter: boolean, +): number { + if (value != null) return value; + return higherIsBetter ? 0 : Number.POSITIVE_INFINITY; +} + +/** Mean over the rows that have a value. + * + * Counting an absence as zero does not only misplace that row: the field + * mean is the denominator of every other row's delta, so one unmeasured + * provider moves the percentage printed beside all the others. Returns 0 + * when nothing was measured, which the caller already treats as "no delta + * to show". */ +export function fieldMean(values: (number | null)[]): number { + const present = values.filter((v): v is number => v != null && Number.isFinite(v)); + if (present.length === 0) return 0; + return present.reduce((s, v) => s + v, 0) / present.length; +} + +/** Scale for the inline data bars: the largest value actually measured. + * + * Infinite sentinels from `sortableValue` must never reach this, or every + * real bar collapses to nothing. Returns 1 when there is nothing to scale + * against, so a division by it is still safe. */ +export function barScale(values: (number | null)[]): number { + const present = values.filter((v): v is number => v != null && Number.isFinite(v)); + if (present.length === 0) return 1; + return Math.max(...present, 0) || 1; +}