You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
web: set and enforce a precache bundle budget for the MUI migration #825
Component plan: build this from the components named in docs/designs/822-mui-revamp.md. The rule is "reused, never rebuilt" — composing MUI primitives into a screen wrapper is expected; reimplementing behaviour a shared component already has is not. Say in the PR which row of that table covers this screen.
Measured on 2026-09-13 (docs/decisions/674-ui-component-library.md):
precache
JS gzip
baseline, hand-rolled
1312.45 KiB
85.27
MUI provider only, zero components
1397.79 KiB
115.45
MUI + a realistic component kit
1632.68 KiB
186.98
+320 KiB precache (+24%) for a full kit. This is a PWA for phones in sheds.
Scope
Set an explicit precache ceiling.
Enforce it in CI so it fails a PR rather than being discovered at screen 13.
Decide how a slice that adds weight without retiring hand-built code is justified.
Runtime cost is a separate question and currently looks fine: at 6x CPU throttling the converted
Dashboard rendered in 1168 ms median vs 1213 ms hand-rolled — inside the noise. Re-measure when a
screen uses Autocomplete or a data grid.
Runtime baseline measured (2026-09-14) — the slope, not the value, is what this slice must watch
Three production builds, 9 runs each, 6x CPU throttling, phone viewport (390x844), timed to the
last Dashboard panel visible. Static build served locally with /api proxied to a dev API, so the
server and seed data are identical across all three and only the client differs.
build
wall
script
style recalc
layout
precache
base, no MUI
917 ms
107 ms
15 ms
26 ms
1312.45 KiB
+ theme provider only
921 ms
119 ms
15 ms
26 ms
1397.79 KiB
+ MUI Dashboard (11 components)
919 ms
136 ms
15 ms
25 ms
1436.90 KiB
What this settles
Wall time is flat — 4 ms spread across 27 runs. ~900 ms is network and API, so MUI is
invisible end to end at this scale.
Style recalculation does not move: 15 ms in all three. This retires the concern that
Emotion's runtime CSS injection would be expensive on low-end phones. It is not.
What this slice should therefore budget
Script time is the cost that scales: +12 ms for the provider alone, +29 ms with eleven
components, at 6x throttle — about +5 ms on the device. That is +27% script time for a light
dose of MUI.
So a bundle-only budget is the wrong instrument on its own. Recommend this slice sets two
ceilings:
Precache bytes — the number this slice already names.
Script duration on a reference screen, because it is what actually grows per component and a
byte budget cannot see it.
Re-measure when a screen adopts Autocomplete or a data grid (#826 is the first): the slope above
is measured on Paper/Typography/Chip/Divider only, and must not be assumed flat.
Method is reproducible — CDP Performance.getMetrics deltas around each navigation, median of 9.
Full numbers in docs/decisions/674-ui-component-library.md.
Projection after #826, #827 and #828 (option (a)): about 1,590 KiB before their CSS is deleted. #835's opsz adds 118.9 on top, so about 1,710. A 1,600 ceiling goes red at #835 and is marginal at #827. 1,800 leaves roughly 90 KiB for the seven remaining screens.
Not yet chosen. The owner asked what the ceiling is for and whether it applies to one control or the whole shell; answered in session, recorded here once decided.
Decision (owner, 2026-09-16): measure and report, no gate
The precache total is printed in CI on every web PR (extend web/scripts/verify-sw.mjs, which already extracts the manifest, to sum the listed files from dist/ and print the KiB), with no threshold. Same shape as backend coverage (#776): a floor picked before enough slices have been measured is a guess, and a wrong guard reads as safety. Revisit a ceiling once the control slices (#826, #827, #828) and #835 have landed and the number has a trend.
What this changes in the design doc: D9 ("Ceiling 1,800 KiB, enforced in CI") and the §7 owner-review row are amended when this slice lands. The "a slice that adds more than 10 KiB without deleting code names the reason in its PR body" rule stays as a convention. The script-duration ceiling from the comment above is likewise report-only for now.
Activity
mforce commentedon Sep 14, 2026
Runtime baseline measured (2026-09-14) — the slope, not the value, is what this slice must watch
Three production builds, 9 runs each, 6x CPU throttling, phone viewport (390x844), timed to the
last Dashboard panel visible. Static build served locally with
/apiproxied to a dev API, so theserver and seed data are identical across all three and only the client differs.
What this settles
invisible end to end at this scale.
Emotion's runtime CSS injection would be expensive on low-end phones. It is not.
What this slice should therefore budget
Script time is the cost that scales: +12 ms for the provider alone, +29 ms with eleven
components, at 6x throttle — about +5 ms on the device. That is +27% script time for a light
dose of MUI.
So a bundle-only budget is the wrong instrument on its own. Recommend this slice sets two
ceilings:
byte budget cannot see it.
Re-measure when a screen adopts
Autocompleteor a data grid (#826 is the first): the slope aboveis measured on
Paper/Typography/Chip/Divideronly, and must not be assumed flat.Method is reproducible — CDP
Performance.getMetricsdeltas around each navigation, median of 9.Full numbers in
docs/decisions/674-ui-component-library.md.feat(web): adopt MUI, themed from the farm palette tokens (#674) (#860)
feat(web): visual language theme overrides for the MUI revamp (#864) (#…
feat(web): convert the Dashboard and app shell to MUI (#829) (#883)
feat(web): retire the Slack-blue link colour for ink + a rule underli…
mforce commentedon Sep 16, 2026
Numbers for the ceiling choice (2026-09-16, head
f502222)Measured on a throwaway worktree with live imports (an upper bound, since some of these components may already be in the bundle after #829/#830):
Dialogfamily,Autocomplete,TextField,Tooltip,IconButtonDialog+useConfirm+NamedEntityPicker, isolated as one chunk (what #826/#827 retire)NumberField, isolated (what #828 retires)Projection after #826, #827 and #828 (option (a)): about 1,590 KiB before their CSS is deleted. #835's
opszadds 118.9 on top, so about 1,710. A 1,600 ceiling goes red at #835 and is marginal at #827. 1,800 leaves roughly 90 KiB for the seven remaining screens.Not yet chosen. The owner asked what the ceiling is for and whether it applies to one control or the whole shell; answered in session, recorded here once decided.
mforce commentedon Sep 16, 2026
Decision (owner, 2026-09-16): measure and report, no gate
The precache total is printed in CI on every web PR (extend
web/scripts/verify-sw.mjs, which already extracts the manifest, to sum the listed files fromdist/and print the KiB), with no threshold. Same shape as backend coverage (#776): a floor picked before enough slices have been measured is a guess, and a wrong guard reads as safety. Revisit a ceiling once the control slices (#826, #827, #828) and #835 have landed and the number has a trend.What this changes in the design doc: D9 ("Ceiling 1,800 KiB, enforced in CI") and the §7 owner-review row are amended when this slice lands. The "a slice that adds more than 10 KiB without deleting code names the reason in its PR body" rule stays as a convention. The script-duration ceiling from the comment above is likewise report-only for now.
fix(web): restore offline caching for the five dropped Inter subsets
fix(web): restrict the font runtime-cache to same-origin /assets/ fon…
docs(help): tighten the MCP Help strings to fit the precache ceiling