Skip to content

chore(size): measure the minified allowance against the minified recorded with each cap - #3822

Merged
ryansolid merged 5 commits into
nextfrom
size-gate-recorded-minified
Oct 6, 2026
Merged

ryansolid merged 5 commits into
nextfrom
size-gate-recorded-minified

Conversation

@ryansolid

@ryansolid ryansolid commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Follow-up to #3821, implementing the maintainer's ruling on its open question.

Problem

#3821 measured the 20 B minified allowance per PR, against each PR's base. A scenario that merged over its brotli cap on noise stays over it, because the ratchet only lowers caps. From then on, every PR was held only to +20 B over its own base, so minified growth could pile up across PRs without limit. The #3821 warning also promised "cap will be re-based at the next ratchet", which the lower-only ratchet can't do for an over-cap scenario.

The rule

Every cap now records the minified size measured when the cap was set: capMinified next to an inline limit in scenarios.js, and { "cap", "minified" } entries in floor-caps.json.

brotli vs cap head minified verdict
at or under anything pass
over ≤ recorded + MINIFIED_ALLOWANCE (20 B) pass with a warning
over more fail
over no recorded minified: ≤ 20 B over the PR's base pass with a warning (fail-safe; the summary says so)
over no recorded minified: more, or no base fail
  • Warning text (it no longer promises a re-base): over brotli cap by N B; minified M B vs R B recorded with the cap (+D B) — H B of the 20 B minified allowance left; +P B minified over this PR's base. A failure carries the same numbers, so a small PR can see that earlier growth used up the allowance.
  • Size-Exception: stays the override. check-floor-caps.mjs now freezes each floor's recorded minified the same way as its cap: raising or removing it needs the exception. It accepts the old string entries at the base, so this PR's own format change passes.
  • Ratchet (lower-only, per RC):
    • When it lowers a cap, it records the minified measured with it. That is one measurement; if the minified is higher, the ratchet lists it explicitly, and brotli still shrank.
    • For a cap it doesn't lower, it only lowers the recorded minified, or records one that is missing. It never raises one.
    • --minified-only leaves every cap alone; this PR seeds with it.
    • It lists scenarios still over their cap, or above their recorded minified, and leaves them alone.
  • Report: the PR comment gains a "minified vs recorded" column and a fail-safe note.

Seed

The values come from CI, not macOS: the size-head artifact of the Size run on the push of next @ fff1615ee (#3811; run 37443080193). They were written by ratchet.mjs --minified-only with a dated Ratchet (2026-10-06, seed, …) ledger line on each scenario, plus header and README ledger notes. Every cap is unchanged.

A first seed from c54fb10eb was replaced. #3812, #3814 and #3811 moved minified by +30 to +69 B after it, and keeping it would have failed the 7 over-cap scenarios on arrival.

scenario cap brotli @ fff1615 recorded minified
signals: core floor (floor) 7.35 KB 7,361 (+11 over) 20,133
signals: + createStore 14.56 KB 14,540 44,396
signals: + isPending/latest 9.49 KB 9,513 (+23 over) 26,828
app: render + one signal (floor) 9.86 KB 9,856 27,687
app: hydrating (no stores) (floor) 17.71 KB 17,672 52,567
app: hydrating + every store primitive family 28.87 KB 28,888 (+18 over) 91,625
app: CSR 12.86 KB 12,905 (+45 over) 36,568
app: CSR, observe tier 14.46 KB 14,449 41,091
app: CSR, observe tier + attribution 28.66 KB 28,639 86,419
app: compiled floor 10.05 KB 10,031 28,218
app: compiled CSR 25.13 KB 25,129 78,898
app: compiled hydrating 30.93 KB 30,957 (+27 over) 99,198
frames: eager client consumer 13.78 KB 13,770 43,310
page: base server components (floor) 44.84 KB 44,864 (+24 over) 145,599
page: live server components (floor) 48.51 KB 48,527 (+17 over) 157,562
server: floor (floor) 1.34 KB 1,331 3,324
server: renderToString (floor) 20.42 KB 20,412 71,813

Seven scenarios sit over their brotli cap on next; they landed with noise warnings under #3821's gate. Each now has 20 B of minified headroom against its recorded size and passes with a warning. It fails only once minified grows more than 20 B past what is recorded now. The lower-only ratchet cannot re-base these 7; they stay over until code shrinks or a PR raises the cap with a reason.

Creep dry run

These runs go through gate.mjs on the tip's CI measurement with the seeded values. Each simulated PR adds +15 B minified to every over-cap scenario, with brotli unchanged:

step minified vs recorded vs its base verdict (7 over-cap scenarios)
next @ fff1615 as is +0 +0 7 ⚠️ warn: "over brotli cap by 11 B; minified 20,133 B vs 20,133 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left"
PR 1: +15 B +15 +15 7 ⚠️ warn: "over brotli cap by 45 B; minified 36,583 B vs 36,568 B recorded with the cap (+15 B) — 5 B of the 20 B minified allowance left; +15 B minified over this PR's base"
PR 2: +15 B on PR 1 +30 +15 7 ❌ fail: "… (+30 B) — 10 B past the 20 B minified allowance; +15 B minified over this PR's base. real growth: …"
PR 2 under #3821's per-PR rule — +15 7 ⚠️ warn: the creep this PR closes

Tests

gate.test.mjs has 12 tests, which CI runs in check. The new ones cover:

  • Creep: two successive +15 B PRs on an over-cap scenario. The first warns with 5 B of headroom left. The second fails at +30 B over the recorded size, even though it is only +15 B over its own base, and the test asserts that the per-PR fallback would have let it through.
  • Exactly at the allowance warns; one byte past fails.
  • The fail-safe path is labelled as such.
  • A recorded value works without any base measurement.

Public API changes

None. This is tooling only (scripts/size/), with no changes under packages/*/src, so there is no changeset. Internal tooling surface touched:

  • the capMinified scenario field;
  • the floor-caps.json entry format ({ cap, minified }; the string form is still read);
  • ratchet.mjs --minified-only;
  • decide() verdicts gain against, headroom and baseDelta.

🤖 Generated with Claude via Cursor

ryansolid and others added 2 commits October 5, 2026 23:50
…rded with each cap

Each cap records the minified size measured when it was set (capMinified
inline, { cap, minified } in floor-caps.json). Over the brotli cap, a scenario
fails when head minified exceeds the recorded size + 20 B, so growth cannot
creep across PRs; the base comparison stays as the fail-safe for a cap without
one. The warning states the headroom left. The ratchet pairs the recorded
minified with each lowered cap and otherwise only lowers it (--minified-only
records missing ones); check-floor-caps freezes a floor's recorded minified
like its cap.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@changeset-bot

changeset-bot Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 18939cc

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Size (brotli, eager entry chunk)

scenario head vs base minified vs base minified vs recorded cap lazy chunks (not counted)
signals: core floor (createSignal/Memo/Effect/Root/flush) 7.36 KB 0 B 0 B 0 B 7.35 KB ⚠️ over by 11 B, 20 B minified headroom
signals: + createStore 14.54 KB 0 B 0 B 0 B 14.56 KB ✅
signals: + isPending/latest 9.51 KB 0 B 0 B 0 B 9.49 KB ⚠️ over by 23 B, 20 B minified headroom
app: render + one signal (the simple-app floor) 9.86 KB 0 B 0 B 0 B 9.86 KB ✅
app: hydrating (no stores) with Show/For/Loading/Errored/lazy 17.67 KB 0 B 0 B 0 B 17.71 KB ✅ lazy-page.js 0.04 KB
app: hydrating + every store primitive family 28.89 KB 0 B 0 B 0 B 28.87 KB ⚠️ over by 18 B, 20 B minified headroom lazy-page.js 0.04 KB
app: CSR with Show/For/Loading/Errored/lazy 12.90 KB 0 B 0 B 0 B 12.86 KB ⚠️ over by 45 B, 20 B minified headroom lazy-page.js 0.04 KB
app: CSR, observe tier (same app on the observe artifacts) 14.45 KB 0 B 0 B 0 B 14.46 KB ✅ lazy-page.js 0.04 KB
app: CSR, observe tier + attribution engine enabled 28.64 KB 0 B 0 B 0 B 28.66 KB ✅ lazy-page.js 0.04 KB
app: compiled floor (one template, one text hole, one delegated click) 10.03 KB 0 B 0 B 0 B 10.05 KB ✅
app: compiled CSR (JSX todo app: spread/merge/omit, events, class/style, keyed For, Show, Loading + lazy, store) 25.13 KB 0 B 0 B 0 B 25.13 KB ✅ stats.js 0.18 KB
app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable) 30.96 KB 0 B 0 B 0 B 30.93 KB ⚠️ over by 27 B, 20 B minified headroom stats.js 0.20 KB
frames: eager client consumer (frames client + transport, lazy codec) 13.77 KB 0 B 0 B 0 B 13.78 KB ✅
page: base server components (hydrating + dynamic + frames + sf reference) 44.86 KB 0 B 0 B 0 B 44.84 KB ⚠️ over by 24 B, 20 B minified headroom decode.js 6.07 KB, lazy-page.js 0.04 KB
page: live server components (base + live/GET + action + isPending/latest) 48.53 KB 0 B 0 B 0 B 48.51 KB ⚠️ over by 17 B, 20 B minified headroom decode.js 6.07 KB, lazy-page.js 0.04 KB
server: floor (getRequestEvent + isServer) 1.33 KB 0 B 0 B 0 B 1.34 KB ✅
server: renderToString (the server-render floor) 20.41 KB 0 B 0 B 0 B 20.42 KB ✅

⚠️ Over the brotli cap within the minified allowance (passes)

  • signals: core floor (createSignal/Memo/Effect/Root/flush): over brotli cap by 11 B; minified 20,133 B vs 20,133 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • signals: + isPending/latest: over brotli cap by 23 B; minified 26,828 B vs 26,828 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: hydrating + every store primitive family: over brotli cap by 18 B; minified 91,625 B vs 91,625 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: CSR with Show/For/Loading/Errored/lazy: over brotli cap by 45 B; minified 36,568 B vs 36,568 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • app: compiled hydrating (the same JSX todo app through hydrate(), compiled hydratable): over brotli cap by 27 B; minified 99,198 B vs 99,198 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: base server components (hydrating + dynamic + frames + sf reference): over brotli cap by 24 B; minified 145,599 B vs 145,599 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base
  • page: live server components (base + live/GET + action + isPending/latest): over brotli cap by 17 B; minified 157,562 B vs 157,562 B recorded with the cap (+0 B) — 20 B of the 20 B minified allowance left; +0 B minified over this PR's base

Bundled with Rolldown (what Vite ships), brotli q11, decimal KB. A scenario fails only when it is over its brotli cap and its minified size is more than 20 B over the minified recorded with the cap; over the cap within that allowance is brotli layout noise and passes with a warning. Caps and their recorded minified in scripts/size/scenarios.js; the floor and page caps in floor-caps.json are frozen (lower only, or Size-Exception: in the PR body). npm run ratchet lowers caps per RC; it never raises one (scripts/size/README.md).

@coveralls

coveralls commented Oct 6, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 37443526665

Coverage remained the same at 76.058%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 1196
Covered Lines: 963
Line Coverage: 80.52%
Relevant Branches: 930
Covered Branches: 654
Branch Coverage: 70.32%
Branches in Coverage %: Yes
Coverage Strength: 28.35 hits per line

💛 - Coveralls

@codspeed

codspeed Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 188 untouched benchmarks


Comparing size-gate-recorded-minified (18939cc) with next (fff1615)

Open in CodSpeed

@nickshiro

Copy link
Copy Markdown

The PR description still has SEED_SECTION and SEED_CREEP placeholders. Could you add the seed measurement and creep dry-run results? Those would make the two key claims of this change easy to verify during review.

ryansolid and others added 2 commits October 6, 2026 02:27
#3812, #3814 and #3811 moved minified +30 to +69 B after the first seed
(c54fb10); re-seeded from scratch at the tip, every cap unchanged.

Co-authored-by: Claude via Cursor <noreply@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@ryansolid
ryansolid merged commit b987c41 into next Oct 6, 2026
8 checks passed
ryansolid added a commit that referenced this pull request Oct 6, 2026
…3822 format)

Size-Exception (approved): hydrating + stores 28.85 KB, CSR observe 14.48 KB,
compiled CSR 25.23 KB, compiled hydrating 30.95 KB, base page 44.86 KB,
live page 48.56 KB, each CI brotli (Size run 37450070886) + 10 B with the
minified from the same run.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants