perf + attestation· chapter 28 · 4 gates · 106 pages graded
budgets are declared, not assumed
four gates run in ci:all immediately after the build. bundle weight, prerendered HTML weight, web-vitals contract runtime parity, build attestation + SBOM. every budget lives in a single contract; tightening it requires an ADR. the report you are reading is regenerated on every build.
as of
00 · liveLCP · INP · CLS · FCP · TTFB · graded against the budget the moment the browser reports themthis page reports its own vitals · live
- lcplargest contentful paint— · pending sample
- inpinteraction to next paint— · pending sample
- clscumulative layout shift— · pending sample
- fcpfirst contentful paint— · pending sample
- ttfbtime to first byte— · pending sample
sampled on this page · pageId · — · the same registrar is mounted at app/layout.tsx for telemetry-spare console reporting · CI gate scripts/check-web-vitals.mjs enforces parity between this runtime and the budget contract.
as of
01 · budgetssingle source of truthevery budget is declared in one file
| category | field | value |
|---|---|---|
| bundle | perRouteFirstLoadKb | 1450 kB |
| bundle | largestChunkKb | 490 kB |
| ssr | perRouteHtmlKb | 840 kB |
| ssr | perRouteChunkCount | 24 |
| web-vitals | LCP | ≤ 2500 ms |
| web-vitals | INP | ≤ 200 ms |
| web-vitals | CLS | ≤ 0.1 |
| web-vitals | FCP | ≤ 1800 ms |
| web-vitals | TTFB | ≤ 800 ms |
why budgets, not benchmarks
a benchmark is a number you publish. a budget is a number you defend. these four gates defend the budget on every PR; the build hard-fails if the system regresses past the line. tightening a budget is an ADR; loosening one needs brand-veto sign-off. the audit identified "deleted in cleanup, no replacement" as the failure mode this ADR corrects.as of
02 · bundletop 5 of 106per-route first-load JS · ranked
| route | chunks | first-load JS | largest chunk |
|---|---|---|---|
/charts | 14 | 1421.5 kB | 481.1 kB |
/theme | 14 | 1420.6 kB | 481.1 kB |
/components | 12 | 1380.9 kB | 481.1 kB |
/receipts | 12 | 1354.7 kB | 481.1 kB |
/motion | 12 | 1354.4 kB | 481.1 kB |
as of
03 · ssrtop 5 of 91per-route prerendered HTML · ranked
| route | HTML | chunks |
|---|---|---|
/charts | 812.9 kB | 19 |
/components | 703.1 kB | 17 |
/v1 | 577.5 kB | 16 |
/tokens | 387.3 kB | 17 |
/changelog | 236.4 kB | 17 |
as of
04 · web vitalscontract ↔ runtimethree-way parity check, no browser needed
| step | artefact | verified by |
|---|---|---|
| 1 · contract | governance/perf/budgets.contract.json | schema=zero.perf.budgets.v1 |
| 2 · runtime | brand-runtime/src/web-vitals.ts · WEB_VITALS_BUDGET | field-for-field parse + compare |
| 3 · mount | book/app/layout.tsx mounts <WebVitalsBoot /> | regex on the layout file |
how the runtime ships
registerWebVitals() dynamically imports web-vitals only in the browser; SSR is a no-op. each sample is graded against the contract; failing samples surface via console.warn by default so the discipline is visible in DevTools without a backend. consumers can pass a sink to forward samples to a real telemetry pipeline.as of
05 · attestation6 inputs · 353 sbom componentsevery primary build output · hashed and committed
| input | bytes | sha256 |
|---|---|---|
packages/tokens/tokens.json | 11,989 | 179052bd8369 |
packages/tokens/tokens.css | 8,288 | 4d296c29222b |
packages/icons/src/icons.json | 7,157 | 527f67368194 |
packages/ui/dist/index.js | 56,638 | b7a87e512087 |
brand-runtime/dist/index.js | 2,809 | 3e20ed23f9b6 |
book/.next/BUILD_ID | 21 | fe0ade8e165c |
reproducibility now, deterministic build later
v1 commits the attestation so PR diffs reveal what the build changed. it does not yet enforce equality across runs (Next's BUILD_ID is timestamp-derived). a future ADR may promote the gate to require deterministic hashes once the build pipeline supports it.