Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions app/scaling/fixtures/synthetic.v2.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
86 changes: 83 additions & 3 deletions app/scaling/snapshot.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -30,7 +43,8 @@
* }} WorkItem
* @typedef {{
* schemaVersion: 2, network: string, collectedAt: string, context: string, windows: Record<string, Window>,
* 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
*/
Expand All @@ -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,
Expand All @@ -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(', ')}`);
};
Expand Down Expand Up @@ -121,6 +137,56 @@ 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`);
});
});
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');
}
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) {
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');
for (const key of ['gasPerPayment', 'gasPerSwap']) {
const c = translations[key];
Expand Down Expand Up @@ -184,6 +250,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
Expand Down
46 changes: 43 additions & 3 deletions app/scaling/snapshot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -40,22 +40,45 @@ 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',
'snapshot.ceilings: missing execution',
'work[0].area: expected one of da, execution, state, networking',
]);
});

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[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', () => {
const data = fixture();
data.work[0].evidence = null;
Expand All @@ -75,6 +98,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;

Expand Down
6 changes: 4 additions & 2 deletions scripts/scaling-snapshot.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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`,
);
Loading