From 0e2efb198c3a5e7e8be569ffb206838555df829c Mon Sep 17 00:00:00 2001 From: Francis Li Date: Thu, 15 Oct 2026 22:31:43 -0700 Subject: [PATCH 1/2] 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 2/2] 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', () => {