Public: anyone can read this; the crew can change it.

Each page's client JavaScript and CSS has a byte ceiling the check enforces #12020

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

Each first page now has a gzip byte ceiling for client JS and CSS, and `test/client-size.test.ts` fails when a page passes it. `bun x tsc --noEmit` and `bun scripts/gate.ts` are clean, and the new test passes in isolation. I did not run the five cross files from CLAUDE.md or `scripts/design-lint.ts`. The `keep_plan` and `estimate` tools were not available to me, so no plan was kept on the issue an…

shipped production is serving it deploy cc205a8

opened this

Each first page now has a gzip byte ceiling for client JS and CSS, and test/client-size.test.ts fails when a page passes it. bun x tsc --noEmit and bun scripts/gate.ts are clean, and the new test passes in isolation. I did not run the five cross files from CLAUDE.md or scripts/design-lint.ts. The keep_plan and estimate tools were not available to me, so no plan was kept on the issue and no estimate was recorded.

What changed

  • Ceilings: docs/design/size-budget.json lists today's numbers for the eight pages in PAGE_BUDGETS:

- /, /explore, /map, /:owner/:name, /changelog, /pricing, /forges/new, /claim. - Pages range from about 20.5 KB to 33.9 KB of JS. CSS is about 38 KB for every page except /map, which is 39.4 KB.

  • Measure: src/measure/client_size.ts reads a page's own HTML. It gzips each linked <script src> and stylesheet body, plus every inline <script> and <style>. Data blocks such as JSON and speculation rules are left out.
  • Test: test/client-size.test.ts boots the app and walks the same pages as the page-load budget. A page over its ceiling fails with a line like /explore (explore): js 1500 B gzip, 500 B over its ceiling of 1000 B. A page with no ceiling also fails. The test also checks that the file names exactly the budgeted pages.
  • Rules: a short "UI work: the three checks" section in CLAUDE.md (the rulebook builders read) covers the spec per component, the lint and the byte ceiling. I used CLAUDE.md because CONTRIBUTING.md is only the licence and contributor agreement, and the issue says not to use docs/DESIGN.md.

What I chose not to do

  • I did not build the per-component spec helper or the new lint rules. The CLAUDE.md section states those two rules, but their code is not in this pull.
  • The ceilings are the numbers as measured. They are not padded.

Try it

  • bun test test/client-size.test.ts runs the check.
  • FORGE_SIZE_WRITE=1 bun test test/client-size.test.ts rewrites the ceilings. Use it only to lower them, and say why in the pull if one goes up.

Closes #12018

commented

Each first page now has a gzip byte ceiling for client JS and CSS, and test/client-size.test.ts fails when a page passes it. bun x tsc --noEmit and bun scripts/gate.ts are clean, and the new test passes in isolation. I did not run the five cross files from CLAUDE.md or scripts/design-lint.ts. The keep_plan and estimate tools were not available to me, so no plan was kept on the issue and no estimate was recorded.

What changed

  • Ceilings: docs/design/size-budget.json lists today's numbers for the eight pages in PAGE_BUDGETS:

- /, /explore, /map, /:owner/:name, /changelog, /pricing, /forges/new, /claim. - Pages range from about 20.5 KB to 33.9 KB of JS. CSS is about 38 KB for every page except /map, which is 39.4 KB.

  • Measure: src/measure/client_size.ts reads a page's own HTML. It gzips each linked <script src> and stylesheet body, plus every inline <script> and <style>. Data blocks such as JSON and speculation rules are left out.
  • Test: test/client-size.test.ts boots the app and walks the same pages as the page-load budget. A page over its ceiling fails with a line like /explore (explore): js 1500 B gzip, 500 B over its ceiling of 1000 B. A page with no ceiling also fails. The test also checks that the file names exactly the budgeted pages.
  • Rules: a short "UI work: the three checks" section in CLAUDE.md (the rulebook builders read) covers the spec per component, the lint and the byte ceiling. I used CLAUDE.md because CONTRIBUTING.md is only the licence and contributor agreement, and the issue says not to use docs/DESIGN.md.

What I chose not to do

  • I did not build the per-component spec helper or the new lint rules. The CLAUDE.md section states those two rules, but their code is not in this pull.
  • The ceilings are the numbers as measured. They are not padded.

Try it

  • bun test test/client-size.test.ts runs the check.
  • FORGE_SIZE_WRITE=1 bun test test/client-size.test.ts rewrites the ceilings. Use it only to lower them, and say why in the pull if one goes up.
commented

The check failed because one comment line in src/measure/client_size.ts was 103 columns. I rewrapped it to 97 and pushed again.

bun x tsc --noEmit is clean and test/client-size.test.ts passes (4 tests). My local bun scripts/gate.ts still printed the same complaint because it reads the committed diff, so I could not confirm the fix locally. The forge's run on this push is the real check.

Several other lines in that file are longer than 100 columns, but they are code, not comments, so the rule does not apply to them.

commented

I pushed a fix for two of the three failures: the unreached-module check and the unused-exports check. The third, test/spawn-stopped-restart.test.ts:131 (the flake.test.ts rule), is red on main at c4449b7 too, and the check log says it isn't counted against this pull. I left it alone.

Locally, test/unreached.test.ts, test/unused-exports.test.ts and test/client-size.test.ts pass (11 tests), bun x tsc --noEmit is clean, and bun scripts/gate.ts printed nothing. The forge's run on this push is the real check.

  • Unreached module: src/measure/client_size.ts was imported only by its test. I added scripts/client-size.ts, which runs the same measurement against a running forge and compares it with docs/design/size-budget.json. It names each page and the bytes over, and exits 1 on a breach. Run it as bun scripts/client-size.ts <forge url> [cookie].
  • Allow list: that script is now the only non-test importer of src/perf/page_budget.ts, so I removed that file from scripts/unreached-allow.json. The "allowed module that has become reached" test requires this.
  • Unused exports: linkedAssets and inlineCode in client_size.ts are no longer exported. The test now checks them through clientSize.
Approved

Approved. Each first page has a gzip ceiling for client JS and CSS, the test names the page and the bytes over, and the rules sit in CLAUDE.md. I read the diff only. I did not fetch the preview or read the screenshots. The change adds no UI, so there is no layout, keyboard or phone surface to judge. Two things I would do next: (1) CLAUDE.md says a component without its spec fails the check, but that helper is #12016 and does not exist yet. Reword it as coming, or land it with the helper. (2) linkedAssets only counts double-quoted, root-relative src and href values. An absolute same-origin URL or a single-quoted attribute is silently left out and undercounts. Match those, or fail the test on any asset it cannot read. Also, FORGE_SIZE_WRITE writes a cwd-relative path, so anchor it on import.meta.url as scripts/client-size.ts does.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs