perf(ui): drop typebox from Control UI startup by lazy-loading the approval page#110528
Conversation
…proval page The approval document page was statically registered in app-host, pulling the protocol validators and the full typebox runtime (schemas, value checks, formats, locales) into every Control UI boot even though the page only renders for /approve document URLs. - register openclaw-approval-page through the existing OptionalCustomElement seam, preloaded only when bootstrap resolves an approval document mode; the approval document's booting splash covers the load gap - ratchet startup budgets to the new baseline (340 KiB gzip, 20 requests) so the win cannot silently regress Startup JS: 369.1 -> 321.8 KiB gzip (-13%), 22 -> 13 requests.
|
Codex review: needs maintainer review before merge. Reviewed July 18, 2026, 3:52 AM ET / 07:52 UTC. Summary PR surface: Source +18, Other 0. Total +18 across 4 files. Reproducibility: not applicable. as a reported bug reproduction: this PR is a proactive startup-performance optimization, and its supplied browser evidence exercises the changed approval-document route. Review metrics: none identified. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Maintainer decision needed
Security Review detailsBest possible solution: Keep the existing optional-element approach, rebase onto current Do we have a high-confidence way to reproduce the issue? Not applicable as a reported bug reproduction: this PR is a proactive startup-performance optimization, and its supplied browser evidence exercises the changed approval-document route. Is this the best way to solve the issue? Unclear pending latest-main confirmation: reusing the existing optional-custom-element seam is the narrowest design shown, but the approval route must remain demonstrably available after the branch is rebased. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 6def1a9c04d6. Label changesLabel justifications:
Evidence reviewedPR surface: Source +18, Other 0. Total +18 across 4 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Merged via squash.
|
Current main now carries the smaller 340 KiB startup ratchet from openclaw#110528, so remove the temporary 372 KiB adjustment while keeping the PR head conflict-free.
…#109176) * fix(ci): bound all openclaw-npm-release git fetch operations with timeout Wrap all six git fetch commands with a 120-second deadline and 10-second termination grace period, matching the pattern established in #108940. Fixes #109176 * test(ci): cover npm release fetch deadlines Co-authored-by: wanyongstar <[email protected]> * ci: raise Control UI startup budget Unblock exact-head builds after the compressed startup bundle reached the previous 370 KiB ratchet. * ci: drop superseded Control UI budget bump Current main now carries the smaller 340 KiB startup ratchet from #110528, so remove the temporary 372 KiB adjustment while keeping the PR head conflict-free. --------- Co-authored-by: Peter Steinberger <[email protected]>
…110687) The entry stylesheet aggregated approval, config, config-quick, and lobster-pet styles even though every consumer is a lazy route chunk or lazily defined element, and two small UI landings tipped startup CSS to exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run 29643357786). Move each stylesheet to its owning lazy module — pages already own their CSS this way (chat, agents, cron) — and keep only the shell's pre-hydration approval booting subset in the entry via the new approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS precedent from #110528.
…penclaw#110687) The entry stylesheet aggregated approval, config, config-quick, and lobster-pet styles even though every consumer is a lazy route chunk or lazily defined element, and two small UI landings tipped startup CSS to exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run 29643357786). Move each stylesheet to its owning lazy module — pages already own their CSS this way (chat, agents, cron) — and keep only the shell's pre-hydration approval booting subset in the entry via the new approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS precedent from openclaw#110528.
…proval page (openclaw#110528) * perf(ui): drop typebox from Control UI startup by lazy-loading the approval page The approval document page was statically registered in app-host, pulling the protocol validators and the full typebox runtime (schemas, value checks, formats, locales) into every Control UI boot even though the page only renders for /approve document URLs. - register openclaw-approval-page through the existing OptionalCustomElement seam, preloaded only when bootstrap resolves an approval document mode; the approval document's booting splash covers the load gap - ratchet startup budgets to the new baseline (340 KiB gzip, 20 requests) so the win cannot silently regress Startup JS: 369.1 -> 321.8 KiB gzip (-13%), 22 -> 13 requests. * chore(ui): record approval-page label in i18n raw-copy baseline
…openclaw#109176) * fix(ci): bound all openclaw-npm-release git fetch operations with timeout Wrap all six git fetch commands with a 120-second deadline and 10-second termination grace period, matching the pattern established in openclaw#108940. Fixes openclaw#109176 * test(ci): cover npm release fetch deadlines Co-authored-by: wanyongstar <[email protected]> * ci: raise Control UI startup budget Unblock exact-head builds after the compressed startup bundle reached the previous 370 KiB ratchet. * ci: drop superseded Control UI budget bump Current main now carries the smaller 340 KiB startup ratchet from openclaw#110528, so remove the temporary 372 KiB adjustment while keeping the PR head conflict-free. --------- Co-authored-by: Peter Steinberger <[email protected]>
…penclaw#110687) The entry stylesheet aggregated approval, config, config-quick, and lobster-pet styles even though every consumer is a lazy route chunk or lazily defined element, and two small UI landings tipped startup CSS to exactly the 42.0 KiB budget, failing all QA Smoke profiles on main (run 29643357786). Move each stylesheet to its owning lazy module — pages already own their CSS this way (chat, agents, cron) — and keep only the shell's pre-hydration approval booting subset in the entry via the new approval-boot.css. Startup CSS drops 42.0 -> 36.0 KiB gzip; tighten the budget 42 -> 38 KiB to lock in the recovery, matching the startup-JS precedent from openclaw#110528.
What Problem This Solves
Control UI initial loads carry more JavaScript than the page needs: every boot downloads and evaluates the full typebox runtime (schema builders, value checks, format validators, and its locale tables) even though only the standalone
/approvedocument page uses it. That inflates cold loads, updates (new hashed assets), and remote/Tailscale sessions where the startup payload dominates time-to-interactive.Why This Change Was Made
Follow-up to #110472. Startup-bundle attribution through the build sourcemaps showed typebox as the single largest startup module group (130 KiB raw), reachable only via
app-host.ts's static import of the approval page registration — the one page not behind a lazy route. The approval page importsapproval-result-validators, which compiles protocol schemas with typebox at module init.The fix uses the repo's existing
OptionalCustomElementseam (already used for the terminal panel, browser panel, and command palette):openclaw-approval-pagenow registers through a lazy module load, preloaded only when bootstrap resolves an approval document mode from the URL. The approval document template already renders a booting splash as light-DOM fallback, so the load gap is covered by design. Startup budgets are ratcheted to the new baseline (340 KiB gzip, 20 requests) per the budget script's "budget changes accompany an intentional loading decision" contract, so the win cannot silently erode.User Impact
Every Control UI load ships 47 KiB gzip (~250 KiB raw) less JavaScript and makes 9 fewer startup requests: startup JS drops from 369.1 KiB gzip / 22 requests to 321.8 KiB / 13 requests (−13%). Cold loads, post-update loads, and slow links benefit most. The
/approveapproval flow is unchanged: the page lazy-loads on approval URLs and renders identically.Evidence
frame-guardsand the other boot-reachable protocol modules are type-only imports and stay startup-safe.node scripts/check-control-ui-performance.mjs: startup JS 321.8 KiB gzip / 13 requests (was 369.1 KiB / 22 on main); all budgets green including the new tighter limits./approve/<id>lazy-defines and renders the approval page (correct "Approval unavailable" state for an unknown id); normal chat boot renders with no approval module in the network log.node scripts/run-vitest.mjs ui/src/e2e/approval-page.e2e.test.ts— 6 e2e tests drive a real browser against the built UI at/approveand pass through the lazy path.ui/src/app(273 tests),approval-page.test.ts(12),test/scripts/control-ui-performance.test.ts— all pass.ui/srcsweep: 6 unrelated files fail identically on cleanmain(Node 26 experimentallocalStorageartifact; CI truth is Node 24).