From b8ebfe388b69793c5c857006161a7b2e2d8a7db2 Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 15 Oct 2026 22:22:31 -0700 Subject: [PATCH 1/5] feat(scaling): snapshot schema v2 with ladder and work evidence Co-authored-by: Toshi --- app/scaling/fixtures/synthetic.v2.json | 108 +++++++++++++++++++++++ app/scaling/page.tsx | 32 +++++-- app/scaling/snapshot.mjs | 116 ++++++++++++++++++++++--- app/scaling/snapshot.test.ts | 100 +++++++++++++-------- scripts/scaling-snapshot.mjs | 2 +- 5 files changed, 299 insertions(+), 59 deletions(-) create mode 100644 app/scaling/fixtures/synthetic.v2.json diff --git a/app/scaling/fixtures/synthetic.v2.json b/app/scaling/fixtures/synthetic.v2.json new file mode 100644 index 0000000..ab3f615 --- /dev/null +++ b/app/scaling/fixtures/synthetic.v2.json @@ -0,0 +1,108 @@ +{ + "schemaVersion": 2, + "network": "synthetic-testnet", + "collectedAt": "2026-01-02T00:00:00Z", + "context": "Synthetic fixture: every number is invented to exercise the schema. Not a measurement of any network.", + "windows": { + "day": { "label": "24 h", "start": "2026-01-01T00:00:00Z", "end": "2026-01-02T00:00:00Z" }, + "week": { "label": "7 d", "start": "2025-12-26T00:00:00Z", "end": "2026-01-02T00:00:00Z" } + }, + "ladder": { + "observed": { + "p50": { + "value": 12.5, + "unit": "Mgas/s", + "tag": "measured", + "window": "day", + "source": { "label": "Synthetic dashboard", "url": "https://example.com/synthetic/gas-per-second" }, + "help": "Median gas executed per second over the window." + }, + "p99": { + "value": null, + "unit": "Mgas/s", + "tag": "not-measured", + "window": null, + "help": "Peak gas per second over the window; needs a p99 query." + } + }, + "operatingPoint": { + "value": 40, + "unit": "Mgas/s", + "tag": "measured", + "window": null, + "help": "Fee-market target: gas target divided by elasticity. Where fees start rising." + }, + "configuredLimit": { + "value": 200, + "unit": "Mgas/s", + "tag": "measured", + "window": null, + "help": "Block gas limit divided by block time. A setting, not demonstrated capacity." + }, + "qualified": { + "value": null, + "unit": "Mgas/s", + "tag": "not-established", + "window": null, + "help": "Highest rate a full-block run on mainnet-sized state has sustained. No run exists yet." + } + }, + "translations": { + "gasPerPayment": { + "value": 52000, + "unit": "gas", + "derivation": "Median gas of a synthetic reference token transfer over the window.", + "window": "week" + }, + "gasPerSwap": null + }, + "sections": [ + { + "id": "da", + "title": "Data availability", + "summary": "Synthetic DA observations.", + "metrics": [ + { "label": "Blob utilization", "status": "observed", "value": 42, "unit": "%", "window": "day", "note": "Invented.", "query": "synthetic.blob_utilization" }, + { "label": "Confirmed drain", "status": "not-measured", "value": null, "unit": "KB/s", "window": null, "note": "No query yet." } + ], + "gaps": ["Confirmed drain"] + } + ], + "work": [ + { + "area": "execution", + "title": "Synthetic unified binary", + "status": "In progress", + "owner": "Synthetic owner", + "hypothesis": "Fewer process hops per block speed up block processing.", + "change": "Run the consensus and execution clients in one process.", + "expected": "about 20% faster block processing", + "realized": "18% faster on a synthetic before/after", + "evidence": { "label": "Synthetic run", "url": "https://example.com/synthetic/runs/1" }, + "linear": { + "projectId": "synthetic-project", + "state": "started", + "progress": 0.4, + "scopeDone": 4, + "scopeTotal": 10, + "targetDate": "2026-03-01", + "updatedAt": "2026-01-01T12:00:00Z", + "url": "https://example.com/synthetic/linear/project" + }, + "links": [] + }, + { + "area": "da", + "title": "Synthetic blob batching change", + "status": "Proposed", + "owner": null, + "hypothesis": "Denser batches post fewer bytes per gas.", + "change": "Not yet specified.", + "expected": null, + "realized": null, + "evidence": null, + "linear": null, + "links": [{ "label": "Synthetic doc", "url": "https://example.com/synthetic/doc" }] + } + ] +} diff --git a/app/scaling/page.tsx b/app/scaling/page.tsx index f4b6456..7d21055 100644 --- a/app/scaling/page.tsx +++ b/app/scaling/page.tsx @@ -6,7 +6,7 @@ import { Card } from '../components/ui/Card'; import { EmptyState } from '../components/ui/EmptyState'; import { Text } from '../components/ui/Text'; -import { parseSnapshot, type Metric, type Snapshot, type WorkItem } from './snapshot.mjs'; +import { parseSnapshot, type Ladder, type Metric, type Rung, type Snapshot, type WorkItem } from './snapshot.mjs'; // The snapshot is a runtime file, so render per request rather than at build. export const dynamic = 'force-dynamic'; @@ -66,7 +66,7 @@ export default async function ScalingPage() {
- {snapshot.headline.map((m) => ( + {headline(snapshot.ladder).map((m) => ( {m.label} @@ -122,9 +122,26 @@ export default async function ScalingPage() { ); } -const STATUS_LABEL = { observed: '', partial: 'Partial', unavailable: 'Unavailable', 'not-measured': 'Not measured' }; +type Shown = Omit & { status: string }; -function Value({ metric, large = false }: { metric: Metric; large?: boolean }) { +function headline(ladder: Ladder): Shown[] { + const rungs: [string, Rung][] = [ + ['Observed gas/s (p50)', ladder.observed.p50], + ['Operating point', ladder.operatingPoint], + ['Configured limit', ladder.configuredLimit], + ['Qualified sustained', ladder.qualified], + ]; + return rungs.map(([label, r]) => ({ + label, status: r.value === null ? r.tag : 'observed', value: r.value, unit: r.unit, window: r.window, note: r.help, source: r.source, + })); +} + +const STATUS_LABEL: Record = { + observed: '', partial: 'Partial', unavailable: 'Unavailable', 'not-measured': 'Not measured', + 'not-modeled': 'Not modeled', 'not-established': 'Not established', +}; + +function Value({ metric, large = false }: { metric: Shown; large?: boolean }) { if (metric.value === null) { return ( @@ -143,7 +160,7 @@ function Value({ metric, large = false }: { metric: Metric; large?: boolean }) { ); } -function MetricDetails({ metric, snapshot }: { metric: Metric; snapshot: Snapshot }) { +function MetricDetails({ metric, snapshot }: { metric: Shown; snapshot: Snapshot }) { const window = metric.window ? snapshot.windows[metric.window] : null; return (
@@ -162,8 +179,8 @@ function WorkCard({ item }: { item: WorkItem }) { const rows = [ ['Hypothesis', item.hypothesis], ['Change', item.change], - ['Expected impact', item.impact], - ['Evidence needed', item.evidence], + ['Expected impact', item.expected ?? 'Not estimated'], + ['Realized impact', item.realized ?? 'Not measured'], ]; return ( @@ -173,6 +190,7 @@ function WorkCard({ item }: { item: WorkItem }) {

{term}: {text}

))}
+ {item.evidence ? : null} {item.links.map((l) => )}
diff --git a/app/scaling/snapshot.mjs b/app/scaling/snapshot.mjs index 24f2ba5..9c24ffb 100644 --- a/app/scaling/snapshot.mjs +++ b/app/scaling/snapshot.mjs @@ -3,9 +3,12 @@ // // A snapshot is a reviewed, dated set of observations, not live data. Real // snapshots hold internal measurements, so they stay outside this public repo. +// app/scaling/fixtures/synthetic.v2.json is an invented example of every field. /** * @typedef {'observed' | 'partial' | 'unavailable' | 'not-measured'} MetricStatus + * @typedef {'measured' | 'modeled' | 'estimated' | 'not-measured' | 'not-modeled' | 'not-established'} Tag + * @typedef {'da' | 'execution' | 'state' | 'networking'} Area * @typedef {{ label: string, url: string }} Link * @typedef {{ label: string, start: string, end: string }} Window * @typedef {{ @@ -13,21 +16,37 @@ * window: string | null, note: string, query?: string, source?: Link * }} Metric * @typedef {{ id: string, title: string, summary: string, metrics: Metric[], gaps: string[] }} Section + * @typedef {{ value: number | null, unit: 'Mgas/s', tag: Tag, window: string | null, source?: Link, help: string }} Rung + * @typedef {{ observed: { p50: Rung, p99: Rung }, operatingPoint: Rung, configuredLimit: Rung, qualified: Rung }} Ladder + * @typedef {{ value: number, unit: 'gas', derivation: string, window: string | null }} Constant * @typedef {{ - * title: string, status: string, owner: string | null, hypothesis: string, - * change: string, impact: string, evidence: string, links: Link[] + * projectId: string, issueId?: string, state: string, progress: number, scopeDone: number, + * scopeTotal: number, targetDate?: string, updatedAt: string, url: string + * }} LinearStatus + * @typedef {{ + * area: Area, title: string, status: string, owner: string | null, hypothesis: string, change: string, + * expected: string | null, realized: string | null, evidence: Link | null, linear: LinearStatus | null, + * links: Link[] * }} WorkItem * @typedef {{ - * schemaVersion: 1, network: string, collectedAt: string, context: string, - * windows: Record, headline: Metric[], sections: Section[], work: WorkItem[] + * schemaVersion: 2, network: string, collectedAt: string, context: string, windows: Record, + * ladder: Ladder, translations: { gasPerPayment: Constant | null, gasPerSwap: Constant | null }, + * sections: Section[], work: WorkItem[] * }} Snapshot */ const STATUSES = ['observed', 'partial', 'unavailable', 'not-measured']; +const TAGS = ['measured', 'modeled', 'estimated', 'not-measured', 'not-modeled', 'not-established']; +const VALUED_TAGS = ['measured', 'modeled', 'estimated']; +const AREAS = ['da', 'execution', 'state', 'networking']; +const DAY_MS = 86_400_000; +const V1_MESSAGE = + 'schemaVersion: 1 is no longer read; migrate to 2: replace headline with ladder, add translations, ' + + 'and give each work entry area, expected, realized, evidence and linear'; /** - * Missing values must be explicit nulls with a non-observed status, never - * zeros, so the page shows them as unknown. + * Missing values must be explicit nulls with a non-measured status or tag, + * never zeros, so the page shows them as unknown. * @param {unknown} data * @returns {{ ok: true, snapshot: Snapshot } | { ok: false, errors: string[] }} */ @@ -38,9 +57,21 @@ export function parseSnapshot(data) { const str = (o, key, path) => { if (typeof o[key] !== 'string' || o[key] === '') fail(`${path}.${key}`, 'expected non-empty string'); }; + const nullableStr = (o, key, path) => { + if (o[key] !== null && typeof o[key] !== 'string') fail(`${path}.${key}`, 'expected string or null'); + }; const date = (o, key, path) => { if (typeof o[key] !== 'string' || Number.isNaN(Date.parse(o[key]))) fail(`${path}.${key}`, 'expected ISO timestamp'); }; + const nullableDate = (o, key, path) => o[key] !== null && date(o, key, path); + const oneOf = (o, key, path, values) => { + if (!values.includes(o[key])) fail(`${path}.${key}`, `expected one of ${values.join(', ')}`); + }; + const obj = (o, key, path) => { + if (isObj(o[key])) return o[key]; + fail(`${path}.${key}`, 'expected object'); + return {}; + }; const list = (o, key, path) => { if (Array.isArray(o[key])) return o[key]; fail(`${path}.${key}`, 'expected array'); @@ -51,15 +82,25 @@ export function parseSnapshot(data) { str(l, 'label', path); if (typeof l.url !== 'string' || !l.url.startsWith('https://')) fail(`${path}.url`, 'expected https URL'); }; + const windowRef = (o, key, path) => { + if (o[key] !== null && !Object.hasOwn(windows, o[key])) fail(`${path}.${key}`, 'expected a key of windows'); + }; + const tagged = (o, path) => { + if (o.unit !== 'Mgas/s') fail(`${path}.unit`, 'expected Mgas/s'); + if (!TAGS.includes(o.tag)) fail(`${path}.tag`, `expected one of ${TAGS.join(', ')}`); + else if (VALUED_TAGS.includes(o.tag)) { + if (!Number.isFinite(o.value)) fail(`${path}.value`, `required for ${o.tag}`); + } else if (o.value !== null) fail(`${path}.value`, `must be null when ${o.tag}`); + }; if (!isObj(data)) return { ok: false, errors: ['snapshot: expected object'] }; - if (data.schemaVersion !== 1) fail('schemaVersion', 'expected 1'); + if (data.schemaVersion === 1) return { ok: false, errors: [V1_MESSAGE] }; + if (data.schemaVersion !== 2) fail('schemaVersion', 'expected 2'); str(data, 'network', 'snapshot'); date(data, 'collectedAt', 'snapshot'); str(data, 'context', 'snapshot'); - const windows = isObj(data.windows) ? data.windows : {}; - if (!isObj(data.windows)) fail('windows', 'expected object'); + const windows = obj(data, 'windows', 'snapshot'); for (const [id, w] of Object.entries(windows)) { if (!isObj(w)) { fail(`windows.${id}`, 'expected object'); continue; } str(w, 'label', `windows.${id}`); @@ -68,6 +109,31 @@ export function parseSnapshot(data) { if (Date.parse(w.start) >= Date.parse(w.end)) fail(`windows.${id}`, 'start must precede end'); } + const rung = (r, path) => { + if (!isObj(r)) return fail(path, 'expected object'); + tagged(r, path); + windowRef(r, 'window', path); + str(r, 'help', path); + if (r.source !== undefined) link(r.source, `${path}.source`); + }; + const ladder = obj(data, 'ladder', 'snapshot'); + const observed = obj(ladder, 'observed', 'ladder'); + rung(observed.p50, 'ladder.observed.p50'); + rung(observed.p99, 'ladder.observed.p99'); + for (const key of ['operatingPoint', 'configuredLimit', 'qualified']) rung(ladder[key], `ladder.${key}`); + + const translations = obj(data, 'translations', 'snapshot'); + for (const key of ['gasPerPayment', 'gasPerSwap']) { + const c = translations[key]; + const path = `translations.${key}`; + if (c === null) continue; + if (!isObj(c)) { fail(path, 'expected object or null'); continue; } + if (!Number.isFinite(c.value)) fail(`${path}.value`, 'expected number'); + if (c.unit !== 'gas') fail(`${path}.unit`, 'expected gas'); + str(c, 'derivation', path); + windowRef(c, 'window', path); + } + const metric = (m, path) => { if (!isObj(m)) return fail(path, 'expected object'); str(m, 'label', path); @@ -81,8 +147,6 @@ export function parseSnapshot(data) { if (m.query !== undefined && typeof m.query !== 'string') fail(`${path}.query`, 'expected string'); if (m.source !== undefined) link(m.source, `${path}.source`); }; - - list(data, 'headline', 'snapshot').forEach((m, i) => metric(m, `headline[${i}]`)); const ids = new Set(); list(data, 'sections', 'snapshot').forEach((s, i) => { const path = `sections[${i}]`; @@ -93,13 +157,39 @@ export function parseSnapshot(data) { list(s, 'metrics', path).forEach((m, j) => metric(m, `${path}.metrics[${j}]`)); list(s, 'gaps', path).forEach((g, j) => typeof g !== 'string' && fail(`${path}.gaps[${j}]`, 'expected string')); }); + + const linear = (l, path) => { + if (!isObj(l)) return fail(path, 'expected object or null'); + for (const key of ['projectId', 'state']) str(l, key, path); + if (l.issueId !== undefined) str(l, 'issueId', path); + if (!(typeof l.progress === 'number' && l.progress >= 0 && l.progress <= 1)) fail(`${path}.progress`, 'expected a number in [0, 1]'); + for (const key of ['scopeDone', 'scopeTotal']) { + if (!Number.isInteger(l[key]) || l[key] < 0) fail(`${path}.${key}`, 'expected non-negative integer'); + } + if (l.targetDate !== undefined) date(l, 'targetDate', path); + date(l, 'updatedAt', path); + if (typeof l.url !== 'string' || !l.url.startsWith('https://')) fail(`${path}.url`, 'expected https URL'); + }; list(data, 'work', 'snapshot').forEach((w, i) => { const path = `work[${i}]`; if (!isObj(w)) return fail(path, 'expected object'); - for (const key of ['title', 'status', 'hypothesis', 'change', 'impact', 'evidence']) str(w, key, path); - if (w.owner !== null && typeof w.owner !== 'string') fail(`${path}.owner`, 'expected string or null'); + oneOf(w, 'area', path, AREAS); + for (const key of ['title', 'status', 'hypothesis', 'change']) str(w, key, path); + for (const key of ['owner', 'expected', 'realized']) nullableStr(w, key, path); + if (w.evidence !== null) link(w.evidence, `${path}.evidence`); + else if (w.realized !== null) fail(`${path}.evidence`, 'required when realized is set'); + if (w.linear !== null) linear(w.linear, `${path}.linear`); list(w, 'links', path).forEach((l, j) => link(l, `${path}.links[${j}]`)); }); return errors.length ? { ok: false, errors } : { ok: true, snapshot: data }; } + +/** + * @param {string} collectedAt + * @param {Date} now + * @param {number} [days] + */ +export function isStale(collectedAt, now, days = 10) { + return now.getTime() - Date.parse(collectedAt) > days * DAY_MS; +} diff --git a/app/scaling/snapshot.test.ts b/app/scaling/snapshot.test.ts index e086552..f8491c9 100644 --- a/app/scaling/snapshot.test.ts +++ b/app/scaling/snapshot.test.ts @@ -1,62 +1,86 @@ +import { readFileSync } from 'node:fs'; + import { describe, expect, it } from 'vitest'; -import { parseSnapshot } from './snapshot.mjs'; +import { isStale, parseSnapshot } from './snapshot.mjs'; // Synthetic data only: real snapshots are internal and never committed. function fixture() { - return { - schemaVersion: 1, - network: 'testnet', - collectedAt: '2026-01-02T00:00:00Z', - context: 'Synthetic fixture.', - windows: { day: { label: '24 h', start: '2026-01-01T00:00:00Z', end: '2026-01-02T00:00:00Z' } }, - headline: [{ label: 'Observed gas/s', status: 'observed', value: 12.5, unit: 'Mgas/s', window: 'day', note: 'n' }], - sections: [ - { - id: 'da', - title: 'DA', - summary: 's', - metrics: [{ label: 'Confirmed drain', status: 'not-measured', value: null, unit: 'KB/s', window: null, note: 'n' }], - gaps: ['Confirmed drain'], - }, - ], - work: [ - { - title: 'w', status: 'Proposed', owner: null, hypothesis: 'h', change: 'c', impact: 'i', evidence: 'e', - links: [{ label: 'Doc', url: 'https://example.com' }], - }, - ], - }; + return JSON.parse(readFileSync(new URL('./fixtures/synthetic.v2.json', import.meta.url), 'utf8')); +} + +function errorsOf(data: unknown) { + const result = parseSnapshot(data); + return result.ok ? [] : result.errors; } describe('parseSnapshot', () => { - it('accepts a valid snapshot and keeps missing values null', () => { + it('accepts the synthetic fixture and keeps missing values null', () => { const result = parseSnapshot(fixture()); expect(result.ok).toBe(true); - if (result.ok) expect(result.snapshot.sections[0].metrics[0].value).toBeNull(); + if (!result.ok) return; + expect(result.snapshot.ladder.observed.p99.value).toBeNull(); + expect(result.snapshot.translations.gasPerSwap).toBeNull(); + expect(result.snapshot.work[1].linear).toBeNull(); + }); + + it('rejects schema version 1 and names the migration', () => { + const errors = errorsOf({ ...fixture(), schemaVersion: 1 }); + expect(errors).toHaveLength(1); + expect(errors[0]).toMatch(/^schemaVersion: 1 is no longer read; migrate to 2: replace headline with ladder/); }); it('rejects a zero standing in for an unmeasured value', () => { const data = fixture(); - (data.sections[0].metrics[0] as { value: number | null }).value = 0; - expect(parseSnapshot(data)).toEqual({ - ok: false, - errors: ['sections[0].metrics[0].value: must be null when not-measured'], - }); + data.ladder.qualified.value = 0; + data.sections[0].metrics[1].value = 0; + expect(errorsOf(data)).toEqual([ + 'ladder.qualified.value: must be null when not-established', + 'sections[0].metrics[1].value: must be null when not-measured', + ]); + }); + + it('rejects measured entries without a value', () => { + const data = fixture(); + data.ladder.observed.p50.value = null; + expect(errorsOf(data)).toEqual(['ladder.observed.p50.value: required for measured']); + }); + + it('rejects unknown areas and tags', () => { + const data = fixture(); + data.ladder.operatingPoint.tag = 'guessed'; + data.work[0].area = 'ops'; + expect(errorsOf(data)).toEqual([ + 'ladder.operatingPoint.tag: expected one of measured, modeled, estimated, not-measured, not-modeled, not-established', + 'work[0].area: expected one of da, execution, state, networking', + ]); + }); + + it('rejects a realized effect without evidence', () => { + const data = fixture(); + data.work[0].evidence = null; + expect(errorsOf(data)).toEqual(['work[0].evidence: required when realized is set']); }); - it('rejects observations without a value or a known window', () => { + it('rejects Linear progress outside [0, 1]', () => { const data = fixture(); - Object.assign(data.headline[0], { value: null, window: '7d' }); - expect(parseSnapshot(data)).toEqual({ - ok: false, - errors: ['headline[0].value: required for observed', 'headline[0].window: expected a key of windows'], - }); + data.work[0].linear.progress = 1.5; + expect(errorsOf(data)).toEqual(['work[0].linear.progress: expected a number in [0, 1]']); }); it('rejects windows that end before they start', () => { const data = fixture(); data.windows.day.end = '2025-12-31T00:00:00Z'; - expect(parseSnapshot(data)).toEqual({ ok: false, errors: ['windows.day: start must precede end'] }); + expect(errorsOf(data)).toEqual(['windows.day: start must precede end']); + }); +}); + +describe('isStale', () => { + const collectedAt = fixture().collectedAt; + + it('flags snapshots older than the threshold', () => { + expect(isStale(collectedAt, new Date('2026-01-13T00:00:00Z'))).toBe(true); + expect(isStale(collectedAt, new Date('2026-01-11T00:00:00Z'))).toBe(false); + expect(isStale(collectedAt, new Date('2026-01-13T00:00:00Z'), 20)).toBe(false); }); }); diff --git a/scripts/scaling-snapshot.mjs b/scripts/scaling-snapshot.mjs index 8ae277e..e203907 100644 --- a/scripts/scaling-snapshot.mjs +++ b/scripts/scaling-snapshot.mjs @@ -18,7 +18,7 @@ if (!result.ok) { } const { snapshot } = result; -const metrics = [...snapshot.headline, ...snapshot.sections.flatMap((s) => s.metrics)]; +const metrics = snapshot.sections.flatMap((s) => s.metrics); const missing = metrics.filter((m) => m.value === null).length; console.log( `${file}: valid — ${snapshot.network}, collected ${snapshot.collectedAt}, ` + From fb577182faf749a95917f0ddda9602fde5a33b08 Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 15 Oct 2026 22:26:15 -0700 Subject: [PATCH 2/5] Use isStale for the page banner and drop an unused validator helper Co-authored-by: Toshi --- app/scaling/page.tsx | 15 ++++++++------- app/scaling/snapshot.mjs | 1 - 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/scaling/page.tsx b/app/scaling/page.tsx index 7d21055..fa7dd1d 100644 --- a/app/scaling/page.tsx +++ b/app/scaling/page.tsx @@ -6,18 +6,18 @@ import { Card } from '../components/ui/Card'; import { EmptyState } from '../components/ui/EmptyState'; import { Text } from '../components/ui/Text'; -import { parseSnapshot, type Ladder, type Metric, type Rung, type Snapshot, type WorkItem } from './snapshot.mjs'; +import { isStale, parseSnapshot, type Ladder, type Metric, type Rung, type Snapshot, type WorkItem } from './snapshot.mjs'; // The snapshot is a runtime file, so render per request rather than at build. export const dynamic = 'force-dynamic'; -const STALE_AFTER_DAYS = 7; +const STALE_AFTER_DAYS = 10; const DAY_MS = 86_400_000; type Loaded = | { kind: 'unconfigured' } | { kind: 'invalid'; errors: string[] } - | { kind: 'ok'; snapshot: Snapshot; ageDays: number }; + | { kind: 'ok'; snapshot: Snapshot; ageDays: number; stale: boolean }; async function loadSnapshot(): Promise { const path = process.env.SCALING_SNAPSHOT_PATH; @@ -25,8 +25,9 @@ async function loadSnapshot(): Promise { try { const result = parseSnapshot(JSON.parse(await readFile(path, 'utf8'))); if (!result.ok) return { kind: 'invalid', errors: result.errors }; - const ageDays = Math.floor((Date.now() - Date.parse(result.snapshot.collectedAt)) / DAY_MS); - return { kind: 'ok', snapshot: result.snapshot, ageDays }; + const { collectedAt } = result.snapshot; + const ageDays = Math.floor((Date.now() - Date.parse(collectedAt)) / DAY_MS); + return { kind: 'ok', snapshot: result.snapshot, ageDays, stale: isStale(collectedAt, new Date(), STALE_AFTER_DAYS) }; } catch (error) { return { kind: 'invalid', errors: [error instanceof Error ? error.message : String(error)] }; } @@ -51,7 +52,7 @@ export default async function ScalingPage() { ); } - const { snapshot, ageDays } = loaded; + const { snapshot, ageDays, stale } = loaded; return (
@@ -60,7 +61,7 @@ export default async function ScalingPage() { As of {formatTime(snapshot.collectedAt)} ({ageDays === 0 ? 'today' : `${ageDays}d old`}) · manually refreshed snapshot, not live data. {snapshot.context} - {ageDays > STALE_AFTER_DAYS ? ( + {stale ? ( Stale: this snapshot is older than {STALE_AFTER_DAYS} days. Refresh it before using these numbers. ) : null}
diff --git a/app/scaling/snapshot.mjs b/app/scaling/snapshot.mjs index 9c24ffb..0bba404 100644 --- a/app/scaling/snapshot.mjs +++ b/app/scaling/snapshot.mjs @@ -63,7 +63,6 @@ export function parseSnapshot(data) { const date = (o, key, path) => { if (typeof o[key] !== 'string' || Number.isNaN(Date.parse(o[key]))) fail(`${path}.${key}`, 'expected ISO timestamp'); }; - const nullableDate = (o, key, path) => o[key] !== null && date(o, key, path); const oneOf = (o, key, path, values) => { if (!values.includes(o[key])) fail(`${path}.${key}`, `expected one of ${values.join(', ')}`); }; From 73bea6f33a31374b1b279f4dc0189e891100023f Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 15 Oct 2026 22:30:54 -0700 Subject: [PATCH 3/5] Label details of rungs that have a value but no window Co-authored-by: Toshi --- app/scaling/page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/scaling/page.tsx b/app/scaling/page.tsx index fa7dd1d..c087435 100644 --- a/app/scaling/page.tsx +++ b/app/scaling/page.tsx @@ -165,7 +165,9 @@ function MetricDetails({ metric, snapshot }: { metric: Shown; snapshot: Snapshot const window = metric.window ? snapshot.windows[metric.window] : null; return (
- {window ? window.label : 'No measurement yet'} · details + + {window ? `${window.label} · details` : metric.value === null ? 'No measurement yet · details' : 'Details'} +
{window ?

{formatTime(window.start)} → {formatTime(window.end)}

: null}

{metric.note}

From 0e2efb198c3a5e7e8be569ffb206838555df829c Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 15 Oct 2026 22:31:43 -0700 Subject: [PATCH 4/5] feat(scaling): add ceilings and guardrails to the snapshot Co-authored-by: Toshi --- app/scaling/fixtures/synthetic.v2.json | 113 +++++++++++++++++++++++++ app/scaling/snapshot.mjs | 77 ++++++++++++++++- app/scaling/snapshot.test.ts | 32 ++++++- scripts/scaling-snapshot.mjs | 6 +- 4 files changed, 220 insertions(+), 8 deletions(-) diff --git a/app/scaling/fixtures/synthetic.v2.json b/app/scaling/fixtures/synthetic.v2.json index ab3f615..7cae772 100644 --- a/app/scaling/fixtures/synthetic.v2.json +++ b/app/scaling/fixtures/synthetic.v2.json @@ -47,6 +47,119 @@ "help": "Highest rate a full-block run on mainnet-sized state has sustained. No run exists yet." } }, + "ceilings": [ + { + "area": "da", + "value": 95, + "unit": "Mgas/s", + "tag": "modeled", + "formula": "(blob_target × blob_bytes ÷ 12 s) × 0.8 ÷ da_bytes_per_gas", + "inputs": [ + { "name": "blob_target", "value": 6, "unit": "blobs/block", "asOf": "2026-01-01T00:00:00Z", "status": "configured" }, + { "name": "blob_bytes", "value": 131072, "unit": "bytes", "asOf": "2026-01-01T00:00:00Z", "status": "configured" }, + { + "name": "da_bytes_per_gas", + "value": 0.0055, + "unit": "bytes/gas", + "asOf": "2026-01-02T00:00:00Z", + "source": { "label": "Synthetic query", "url": "https://example.com/synthetic/da-bytes-per-gas" }, + "status": "observed" + } + ], + "help": "Gas per second the data-availability layer absorbs at 80% blob utilization." + }, + { + "area": "execution", + "value": 150, + "unit": "Mgas/s", + "tag": "estimated", + "formula": "sequencer gas/s of the newest passing bench run", + "inputs": [ + { "name": "bench_run", "value": "synthetic-run-1", "unit": "", "asOf": "2025-12-20T00:00:00Z", "status": "observed" }, + { "name": "state_size", "value": null, "unit": "GB", "asOf": null, "status": "pending" } + ], + "reason": "state size not recorded for the run", + "help": "Highest rate at which block building fits the slot and validators keep up, on mature state." + }, + { + "area": "state", + "value": null, + "unit": "Mgas/s", + "tag": "not-modeled", + "formula": "state_growth_budget ÷ state_bytes_per_gas", + "inputs": [ + { "name": "single_node_max", "value": 45, "unit": "TB", "asOf": "2026-01-01T00:00:00Z", "status": "configured" }, + { "name": "runway_years", "value": 4, "unit": "years", "asOf": "2026-01-01T00:00:00Z", "status": "provisional" }, + { "name": "state_bytes_per_gas", "value": null, "unit": "bytes/gas", "asOf": null, "status": "pending" } + ], + "reason": "live-state growth not measured", + "help": "Gas per second that keeps state within what one node can hold over the runway." + }, + { + "area": "networking", + "value": null, + "unit": "Mgas/s", + "tag": "not-modeled", + "formula": "none yet", + "inputs": [], + "reason": "gossip and peer limits unknown", + "help": "Gas per second at which blocks still propagate within the slot." + } + ], + "guardrails": [ + { + "id": "da-utilization", + "label": "DA utilization", + "value": 42, + "unit": "%", + "threshold": 80, + "comparator": "<=", + "thresholdTag": "set", + "owner": null, + "asOf": "2026-01-02T00:00:00Z", + "status": "ok", + "help": "Share of blob capacity used at the current rate." + }, + { + "id": "rpc-availability", + "label": "RPC availability", + "value": 99.5, + "unit": "%", + "threshold": 99.9, + "comparator": ">=", + "thresholdTag": "set", + "owner": null, + "asOf": "2026-01-02T00:00:00Z", + "status": "breached", + "help": "Successful RPC responses over the window." + }, + { + "id": "validator-keep-up", + "label": "Validator keep-up", + "value": 3, + "unit": "blocks", + "threshold": 5, + "comparator": "<=", + "thresholdTag": "provisional", + "owner": "Synthetic owner", + "asOf": "2026-01-01T00:00:00Z", + "status": "ok", + "help": "p90 distance of validators behind the tip. Threshold is a placeholder until the pod sets one." + }, + { + "id": "rpc-p99", + "label": "RPC p99 latency", + "value": 180, + "unit": "ms", + "threshold": null, + "comparator": "<=", + "thresholdTag": "unset", + "owner": null, + "asOf": "2026-01-02T00:00:00Z", + "status": "unknown", + "help": "Slowest 1% of RPC responses. No operational rule yet, so no status." + } + ], "translations": { "gasPerPayment": { "value": 52000, diff --git a/app/scaling/snapshot.mjs b/app/scaling/snapshot.mjs index 0bba404..be07747 100644 --- a/app/scaling/snapshot.mjs +++ b/app/scaling/snapshot.mjs @@ -18,6 +18,19 @@ * @typedef {{ id: string, title: string, summary: string, metrics: Metric[], gaps: string[] }} Section * @typedef {{ value: number | null, unit: 'Mgas/s', tag: Tag, window: string | null, source?: Link, help: string }} Rung * @typedef {{ observed: { p50: Rung, p99: Rung }, operatingPoint: Rung, configuredLimit: Rung, qualified: Rung }} Ladder + * @typedef {{ + * name: string, value: number | string | null, unit: string, asOf: string | null, source?: Link, + * status: 'observed' | 'configured' | 'provisional' | 'pending' + * }} CeilingInput + * @typedef {{ + * area: Area, value: number | null, unit: 'Mgas/s', tag: Tag, formula: string, + * inputs: CeilingInput[], reason?: string, help: string + * }} Ceiling + * @typedef {{ + * id: string, label: string, value: number | null, unit: string, threshold: number | null, + * comparator: '<=' | '>=', thresholdTag: 'set' | 'provisional' | 'unset', owner: string | null, + * asOf: string | null, status: 'ok' | 'breached' | 'unknown', help: string + * }} Guardrail * @typedef {{ value: number, unit: 'gas', derivation: string, window: string | null }} Constant * @typedef {{ * projectId: string, issueId?: string, state: string, progress: number, scopeDone: number, @@ -30,7 +43,8 @@ * }} WorkItem * @typedef {{ * schemaVersion: 2, network: string, collectedAt: string, context: string, windows: Record, - * ladder: Ladder, translations: { gasPerPayment: Constant | null, gasPerSwap: Constant | null }, + * ladder: Ladder, ceilings: Ceiling[], guardrails: Guardrail[], + * translations: { gasPerPayment: Constant | null, gasPerSwap: Constant | null }, * sections: Section[], work: WorkItem[] * }} Snapshot */ @@ -39,10 +53,11 @@ const STATUSES = ['observed', 'partial', 'unavailable', 'not-measured']; const TAGS = ['measured', 'modeled', 'estimated', 'not-measured', 'not-modeled', 'not-established']; const VALUED_TAGS = ['measured', 'modeled', 'estimated']; const AREAS = ['da', 'execution', 'state', 'networking']; +const INPUT_STATUSES = ['observed', 'configured', 'provisional', 'pending']; const DAY_MS = 86_400_000; const V1_MESSAGE = - 'schemaVersion: 1 is no longer read; migrate to 2: replace headline with ladder, add translations, ' + - 'and give each work entry area, expected, realized, evidence and linear'; + 'schemaVersion: 1 is no longer read; migrate to 2: replace headline with ladder, add ceilings, guardrails and ' + + 'translations, and give each work entry area, expected, realized, evidence and linear'; /** * Missing values must be explicit nulls with a non-measured status or tag, @@ -63,6 +78,7 @@ export function parseSnapshot(data) { const date = (o, key, path) => { if (typeof o[key] !== 'string' || Number.isNaN(Date.parse(o[key]))) fail(`${path}.${key}`, 'expected ISO timestamp'); }; + const nullableDate = (o, key, path) => o[key] !== null && date(o, key, path); const oneOf = (o, key, path, values) => { if (!values.includes(o[key])) fail(`${path}.${key}`, `expected one of ${values.join(', ')}`); }; @@ -121,6 +137,47 @@ export function parseSnapshot(data) { rung(observed.p99, 'ladder.observed.p99'); for (const key of ['operatingPoint', 'configuredLimit', 'qualified']) rung(ladder[key], `ladder.${key}`); + const areas = new Set(); + list(data, 'ceilings', 'snapshot').forEach((c, i) => { + const path = `ceilings[${i}]`; + if (!isObj(c)) return fail(path, 'expected object'); + oneOf(c, 'area', path, AREAS); + if (areas.has(c.area)) fail(`${path}.area`, 'duplicate'); + areas.add(c.area); + tagged(c, path); + str(c, 'formula', path); + str(c, 'help', path); + if (c.reason !== undefined) str(c, 'reason', path); + list(c, 'inputs', path).forEach((input, j) => { + const p = `${path}.inputs[${j}]`; + if (!isObj(input)) return fail(p, 'expected object'); + str(input, 'name', p); + if (typeof input.unit !== 'string') fail(`${p}.unit`, 'expected string'); + oneOf(input, 'status', p, INPUT_STATUSES); + if ((input.value === null) !== (input.status === 'pending')) fail(`${p}.value`, 'null exactly when status is pending'); + else if (input.value !== null && !['number', 'string'].includes(typeof input.value)) fail(`${p}.value`, 'expected number or string'); + nullableDate(input, 'asOf', p); + if (input.source !== undefined) link(input.source, `${p}.source`); + }); + }); + + list(data, 'guardrails', 'snapshot').forEach((g, i) => { + const path = `guardrails[${i}]`; + if (!isObj(g)) return fail(path, 'expected object'); + for (const key of ['id', 'label', 'help']) str(g, key, path); + if (typeof g.unit !== 'string') fail(`${path}.unit`, 'expected string'); + for (const key of ['value', 'threshold']) { + if (g[key] !== null && !Number.isFinite(g[key])) fail(`${path}.${key}`, 'expected number or null'); + } + oneOf(g, 'comparator', path, ['<=', '>=']); + oneOf(g, 'thresholdTag', path, ['set', 'provisional', 'unset']); + if ((g.threshold === null) !== (g.thresholdTag === 'unset')) fail(`${path}.thresholdTag`, 'unset exactly when threshold is null'); + nullableStr(g, 'owner', path); + nullableDate(g, 'asOf', path); + oneOf(g, 'status', path, ['ok', 'breached', 'unknown']); + if ((g.value === null || g.threshold === null) && g.status !== 'unknown') fail(`${path}.status`, 'must be unknown without a value and threshold'); + }); + const translations = obj(data, 'translations', 'snapshot'); for (const key of ['gasPerPayment', 'gasPerSwap']) { const c = translations[key]; @@ -184,6 +241,20 @@ export function parseSnapshot(data) { return errors.length ? { ok: false, errors } : { ok: true, snapshot: data }; } +/** + * The lowest ceiling with evidence behind it; estimates and unmodeled areas + * never bind. Null when no area is measured or modeled. + * @param {Ceiling[]} ceilings + * @returns {Ceiling | null} + */ +export function deriveBinding(ceilings) { + let binding = null; + for (const c of ceilings) { + if ((c.tag === 'measured' || c.tag === 'modeled') && (binding === null || c.value < binding.value)) binding = c; + } + return binding; +} + /** * @param {string} collectedAt * @param {Date} now diff --git a/app/scaling/snapshot.test.ts b/app/scaling/snapshot.test.ts index f8491c9..8f74b0d 100644 --- a/app/scaling/snapshot.test.ts +++ b/app/scaling/snapshot.test.ts @@ -2,7 +2,7 @@ import { readFileSync } from 'node:fs'; import { describe, expect, it } from 'vitest'; -import { isStale, parseSnapshot } from './snapshot.mjs'; +import { deriveBinding, isStale, parseSnapshot } from './snapshot.mjs'; // Synthetic data only: real snapshots are internal and never committed. function fixture() { @@ -40,22 +40,31 @@ describe('parseSnapshot', () => { ]); }); - it('rejects measured entries without a value', () => { + it('rejects measured or modeled entries without a value', () => { const data = fixture(); + data.ceilings[0].value = null; data.ladder.observed.p50.value = null; - expect(errorsOf(data)).toEqual(['ladder.observed.p50.value: required for measured']); + expect(errorsOf(data)).toEqual(['ladder.observed.p50.value: required for measured', 'ceilings[0].value: required for modeled']); }); it('rejects unknown areas and tags', () => { const data = fixture(); + data.ceilings[1].area = 'l1'; data.ladder.operatingPoint.tag = 'guessed'; data.work[0].area = 'ops'; expect(errorsOf(data)).toEqual([ 'ladder.operatingPoint.tag: expected one of measured, modeled, estimated, not-measured, not-modeled, not-established', + 'ceilings[1].area: expected one of da, execution, state, networking', 'work[0].area: expected one of da, execution, state, networking', ]); }); + it('rejects a guardrail status without both a value and a threshold', () => { + const data = fixture(); + data.guardrails[3].status = 'ok'; + expect(errorsOf(data)).toEqual(['guardrails[3].status: must be unknown without a value and threshold']); + }); + it('rejects a realized effect without evidence', () => { const data = fixture(); data.work[0].evidence = null; @@ -75,6 +84,23 @@ describe('parseSnapshot', () => { }); }); +describe('deriveBinding', () => { + it('picks the lowest measured or modeled ceiling and ignores estimates', () => { + const { ceilings } = fixture(); + ceilings[1].value = 60; + expect(deriveBinding(ceilings)?.area).toBe('da'); + ceilings[1].tag = 'measured'; + expect(deriveBinding(ceilings)?.area).toBe('execution'); + }); + + it('returns null when no ceiling is measured or modeled', () => { + const { ceilings } = fixture(); + Object.assign(ceilings[0], { value: null, tag: 'not-modeled' }); + ceilings[1].tag = 'estimated'; + expect(deriveBinding(ceilings)).toBeNull(); + }); +}); + describe('isStale', () => { const collectedAt = fixture().collectedAt; diff --git a/scripts/scaling-snapshot.mjs b/scripts/scaling-snapshot.mjs index e203907..df1fdce 100644 --- a/scripts/scaling-snapshot.mjs +++ b/scripts/scaling-snapshot.mjs @@ -3,7 +3,7 @@ // Usage: npm run scaling:check -- /private/path/mainnet.json import { readFileSync } from 'node:fs'; -import { parseSnapshot } from '../app/scaling/snapshot.mjs'; +import { deriveBinding, parseSnapshot } from '../app/scaling/snapshot.mjs'; const file = process.argv[2]; if (!file) { @@ -20,7 +20,9 @@ if (!result.ok) { const { snapshot } = result; const metrics = snapshot.sections.flatMap((s) => s.metrics); const missing = metrics.filter((m) => m.value === null).length; +const binding = deriveBinding(snapshot.ceilings); console.log( `${file}: valid — ${snapshot.network}, collected ${snapshot.collectedAt}, ` + - `${metrics.length} metrics (${missing} without a value), ${snapshot.work.length} work items`, + `binding ceiling ${binding ? `${binding.area} ${binding.value} ${binding.unit}` : 'none'}, ` + + `${snapshot.guardrails.length} guardrails, ${metrics.length} metrics (${missing} without a value), ${snapshot.work.length} work items`, ); From 286308330200d5aad89f0f9719a072de79ab74b0 Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 15 Oct 2026 22:35:08 -0700 Subject: [PATCH 5/5] Require every ceiling area, unique guardrail ids and a status matching the numbers Co-authored-by: Toshi --- app/scaling/snapshot.mjs | 11 ++++++++++- app/scaling/snapshot.test.ts | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/scaling/snapshot.mjs b/app/scaling/snapshot.mjs index be07747..465e9e3 100644 --- a/app/scaling/snapshot.mjs +++ b/app/scaling/snapshot.mjs @@ -160,11 +160,15 @@ export function parseSnapshot(data) { if (input.source !== undefined) link(input.source, `${p}.source`); }); }); + for (const area of AREAS) if (!areas.has(area)) fail('snapshot.ceilings', `missing ${area}`); + const guardrailIds = new Set(); list(data, 'guardrails', 'snapshot').forEach((g, i) => { const path = `guardrails[${i}]`; if (!isObj(g)) return fail(path, 'expected object'); for (const key of ['id', 'label', 'help']) str(g, key, path); + if (guardrailIds.has(g.id)) fail(`${path}.id`, 'duplicate'); + guardrailIds.add(g.id); if (typeof g.unit !== 'string') fail(`${path}.unit`, 'expected string'); for (const key of ['value', 'threshold']) { if (g[key] !== null && !Number.isFinite(g[key])) fail(`${path}.${key}`, 'expected number or null'); @@ -175,7 +179,12 @@ export function parseSnapshot(data) { nullableStr(g, 'owner', path); nullableDate(g, 'asOf', path); oneOf(g, 'status', path, ['ok', 'breached', 'unknown']); - if ((g.value === null || g.threshold === null) && g.status !== 'unknown') fail(`${path}.status`, 'must be unknown without a value and threshold'); + if (g.value === null || g.threshold === null) { + if (g.status !== 'unknown') fail(`${path}.status`, 'must be unknown without a value and threshold'); + } else if (Number.isFinite(g.value) && Number.isFinite(g.threshold)) { + const ok = g.comparator === '<=' ? g.value <= g.threshold : g.value >= g.threshold; + if (g.status !== (ok ? 'ok' : 'breached')) fail(`${path}.status`, `expected ${ok ? 'ok' : 'breached'} for ${g.value} ${g.comparator} ${g.threshold}`); + } }); const translations = obj(data, 'translations', 'snapshot'); diff --git a/app/scaling/snapshot.test.ts b/app/scaling/snapshot.test.ts index 8f74b0d..86d344e 100644 --- a/app/scaling/snapshot.test.ts +++ b/app/scaling/snapshot.test.ts @@ -55,14 +55,28 @@ describe('parseSnapshot', () => { expect(errorsOf(data)).toEqual([ 'ladder.operatingPoint.tag: expected one of measured, modeled, estimated, not-measured, not-modeled, not-established', 'ceilings[1].area: expected one of da, execution, state, networking', + 'snapshot.ceilings: missing execution', 'work[0].area: expected one of da, execution, state, networking', ]); }); - it('rejects a guardrail status without both a value and a threshold', () => { + it('rejects a missing ceiling area and a duplicate guardrail id', () => { const data = fixture(); + data.ceilings.pop(); + data.guardrails[1].id = data.guardrails[0].id; + expect(errorsOf(data)).toEqual(['snapshot.ceilings: missing networking', 'guardrails[1].id: duplicate']); + }); + + it('rejects a guardrail status that disagrees with its value and threshold', () => { + const data = fixture(); + data.guardrails[0].status = 'breached'; + data.guardrails[1].status = 'unknown'; data.guardrails[3].status = 'ok'; - expect(errorsOf(data)).toEqual(['guardrails[3].status: must be unknown without a value and threshold']); + expect(errorsOf(data)).toEqual([ + 'guardrails[0].status: expected ok for 42 <= 80', + 'guardrails[1].status: expected breached for 99.5 >= 99.9', + 'guardrails[3].status: must be unknown without a value and threshold', + ]); }); it('rejects a realized effect without evidence', () => {