Skip to content

perf(ui): drop typebox from Control UI startup by lazy-loading the approval page#110528

Merged
steipete merged 2 commits into
mainfrom
claude/web-ui-startup-lcp
Jul 18, 2026
Merged

perf(ui): drop typebox from Control UI startup by lazy-loading the approval page#110528
steipete merged 2 commits into
mainfrom
claude/web-ui-startup-lcp

Conversation

@steipete

Copy link
Copy Markdown
Contributor

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 /approve document 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 imports approval-result-validators, which compiles protocol schemas with typebox at module init.

The fix uses the repo's existing OptionalCustomElement seam (already used for the terminal panel, browser panel, and command palette): openclaw-approval-page now 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 /approve approval flow is unchanged: the page lazy-loads on approval URLs and renders identically.

Evidence

  • Startup attribution (sourcemap byte accounting across startup chunks): typebox 130.4 KiB raw was the largest npm group, present only via the approval-page chain; frame-guards and 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.
  • Live verification on a real-data dev gateway: /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 /approve and pass through the lazy path.
  • Focused units: ui/src/app (273 tests), approval-page.test.ts (12), test/scripts/control-ui-performance.test.ts — all pass.
  • Full local ui/src sweep: 6 unrelated files fail identically on clean main (Node 26 experimental localStorage artifact; CI truth is Node 24).
  • Structured autoreview (gpt-5.6-sol): clean, no findings.

…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.
@openclaw-barnacle openclaw-barnacle Bot added app: web-ui App: web-ui scripts Repository scripts size: XS maintainer Maintainer-authored PR labels Jul 18, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. labels Jul 18, 2026
@clawsweeper

clawsweeper Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 18, 2026, 3:52 AM ET / 07:52 UTC.

Summary
The PR makes approval-page registration lazy for approval-document URLs and lowers Control UI startup JavaScript/request budgets to the measured baseline.

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: 🐚 platinum hermit
Proof: 🐚 platinum hermit
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Rebase onto current main and capture a fresh first-load /approve verification if the base changes the Control UI boot path.

Risk before merge

  • [P2] The approval document moves from eagerly defined to asynchronously registered; despite the supplied route proof, a merge should confirm the first /approve render and retry/error behavior on the rebased head so an approval URL cannot remain on its booting fallback if the dynamic chunk fails or loads late.
  • [P1] The branch is behind current main; refresh the approval-route verification after rebasing rather than inferring a regression from base drift alone.

Maintainer options:

  1. Refresh approval-route proof after rebase (recommended)
    Rebase onto current main and rerun the real /approve first-load check so the asynchronous custom-element boundary is proven on the actual merge head.
  2. Accept the lazy-load boundary
    Maintain the smaller normal-startup graph while accepting the dynamic import as the approval document’s explicit availability boundary.

Next step before merge

  • [P2] Direct maintainer review is needed to accept the asynchronous approval-document registration boundary; no narrow mechanical repair is currently identified.

Maintainer decision needed

  • Question: Should the approval document’s registration move behind the optional-element loader after a latest-main approval-route verification confirms first-load availability?
  • Rationale: The patch intentionally changes an availability-sensitive document boot path, and the protected maintainer label requires a human merge decision even though no discrete code defect is established.
  • Likely owner: steipete — They are the available recent contributor with directly related merged Control UI performance history and authored the supplied loading analysis.
  • Options:
    • Land after refreshed route proof (recommended): Rebase the branch and confirm /approve/<id> replaces its fallback with the approval element on the current head before merging.
    • Keep eager registration: Retain the existing startup dependency if maintainers prioritize eliminating the approval document’s asynchronous-definition boundary over the measured startup reduction.

Security
Cleared: The shown diff changes local Control UI module loading and performance thresholds without adding dependencies, permissions, downloads, secret handling, or external code execution.

Review details

Best possible solution:

Keep the existing optional-element approach, rebase onto current main, and retain a browser-level first-load approval-route check that proves the lazy definition replaces the fallback and preserves the approval document’s unavailable/error state.

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 changes

Label justifications:

  • P3: This is a measured startup-performance improvement with no demonstrated functional regression in the supplied evidence.
  • merge-risk: 🚨 availability: Approval-document registration changes from eager startup evaluation to an asynchronous module load, so first-load availability remains a merge-specific concern.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The PR body provides after-fix real-gateway approval-route and normal-chat observations, supported by a browser E2E route check and a successful dedicated real-behavior-proof job.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body provides after-fix real-gateway approval-route and normal-chat observations, supported by a browser E2E route check and a successful dedicated real-behavior-proof job.
Evidence reviewed

PR surface:

Source +18, Other 0. Total +18 across 4 files.

View PR surface stats
Area Files Added Removed Net
Source 3 19 1 +18
Tests 0 0 0 0
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 1 2 2 0
Total 4 21 3 +18

What I checked:

  • Lazy registration boundary: At the PR head, eager approval-page registration is removed from the app host and replaced with an existing optional-custom-element definition that dynamically imports the approval registration module. (ui/src/app/lazy-custom-element.ts:59, ed444f980953)
  • Approval-mode preload: The app host preloads the optional approval-page element only when the resolved document mode is approval, preserving a targeted path for the standalone approval document rather than adding a normal-chat startup dependency. (ui/src/app/app-host.ts:244, ed444f980953)
  • Budget ratchet: The performance script changes startup budgets from 28 to 20 JavaScript requests and from 370 KiB to 340 KiB gzip, so the claimed loading decision is protected by a tighter regression threshold. (scripts/check-control-ui-performance.mjs:10, ed444f980953)
  • After-fix behavior evidence: The PR body reports a real-data gateway check of /approve/<id> through the lazy path, normal-chat boot without the approval module in its network log, and six browser E2E tests for the approval route; the provided checks also show the dedicated real-behavior-proof job succeeded. (ed444f980953)
  • Adjacent performance history: The linked merged performance PR addressed forced reflows in the chat render path, which is related Control UI performance work but a distinct root cause from approval-page bundle loading. (cad4e395d21f)

Likely related people:

  • steipete: Authored the adjacent merged Control UI performance work and the current focused loading optimization, making them the strongest available routing candidate for the performance rationale and approval-route proof. (role: recent UI performance contributor; confidence: medium; commits: cad4e395d21f, df3959b62a66; files: ui/src/app/app-host.ts, ui/src/app/lazy-custom-element.ts, scripts/check-control-ui-performance.mjs)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (2 earlier review cycles)
  • reviewed 2026-07-18T07:32:46.850Z sha df3959b :: needs maintainer review before merge. :: none
  • reviewed 2026-07-18T07:45:48.374Z sha df3959b :: needs maintainer review before merge. :: none

@steipete steipete closed this Jul 18, 2026
@steipete steipete reopened this Jul 18, 2026
@steipete steipete self-assigned this Jul 18, 2026
@steipete
steipete merged commit e77140a into main Jul 18, 2026
98 checks passed
@steipete
steipete deleted the claude/web-ui-startup-lcp branch July 18, 2026 08:02
@steipete

Copy link
Copy Markdown
Contributor Author

Merged via squash.

steipete added a commit to wanyongstar/openclaw that referenced this pull request Jul 18, 2026
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.
steipete added a commit that referenced this pull request Jul 18, 2026
…#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]>
steipete added a commit that referenced this pull request Jul 18, 2026
…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.
coder-master-0915 pushed a commit to coder-master-0915/openclaw that referenced this pull request Jul 18, 2026
…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.
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
…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
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
…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]>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 19, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: web-ui App: web-ui maintainer Maintainer-authored PR merge-risk: 🚨 availability 🚨 May cause crashes, hangs, restart loops, stalls, or process outages. P3 Low-priority cleanup, docs, polish, ergonomics, or speculative work. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. scripts Repository scripts size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant