Skip to content

perf(platform): lazy-load route setup groups - #29757

Open
hulh122 wants to merge 22 commits into
mainfrom
perf/route-boundary-lazy-loading
Open

perf(platform): lazy-load route setup groups#29757
hulh122 wants to merge 22 commits into
mainfrom
perf/route-boundary-lazy-loading

Conversation

@hulh122

@hulh122 hulh122 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Keep the bootstrap shell, router primitives, locale, authentication, redirects, not-found handling, and initial app skeleton eager while loading activity, browser, chat, home, miscellaneous, onboarding, settings, and workflow setup groups only after route matching.
  • Prepare a cold target's route and layout modules under a navigation-only signal while the current page keeps its active lifecycle. After preparation succeeds, clear the old page before resetting its route signal, then run the existing auth/layout/setup handoff.
  • Treat the global app skeleton as bootstrap-only: the initial route still hides it only when safe, but client navigation keeps current content visible while target modules load and never re-shows the global skeleton.
  • Add internal-link intent prefetch on pointer enter, focus, and touch start. It resolves only the matched route/layout loader, composes consumer handlers, performs no auth/registration/setup/navigation/render work, retries rejection, and has no idle or broad-import path.
  • Keep loader and activation state scoped to the active app root. Setup attempts are identity guarded, activation is recorded only after setup succeeds under the same root, and rejected or aborted attempts remain cold for retry.

Refs #29576

Route lifecycle

  1. Match the target route and cancel only any older pending route preparation.
  2. Resolve the matched route setup group, keyboard-shortcut module, and selected layout module while the current route signal remains active.
  3. Reject stale navigation or root ownership before any route setup runs.
  4. On the target route's first activation in this root, clear the outgoing page before resetting its route signal. This prevents a mounted page from observing a torn-down lifecycle.
  5. Register the already-resolved layout and run the existing authentication and page setup with the new route signal.
  6. Reuse a live or successfully resolved same-root boundary without another import or cold clear, preserving same-route page/container identity.

Each in-flight setup has an identity-scoped failure and completion path. A stale abort, rejection, or continuation cannot clear or overwrite a newer attempt's pending/successful marker. A setup is marked activated only after its command resolves, its signal remains live, and root ownership is revalidated. Failed first setup attempts therefore remain cold; after another route becomes active, retry clears the outgoing page before its lifecycle is aborted.

On initial bootstrap, the existing skeleton remains visible through preparation and setup until the selected page calls hideAppSkeleton$. On client navigation, no showAppSkeleton$ call is made, so module fetch/parse time is not presented as a second bootstrap.

Intent prefetch

  • Link invokes consumer pointer/focus/touch handlers first and respects preventDefault().
  • Only the route matched by the internal pathname is prepared.
  • Auth, layout registration, route setup, page rendering, analytics, and navigation do not run during prefetch.
  • Failed loads are evicted and retry on later intent/navigation.
  • Root changes invalidate pending/resolved loader ownership.
  • There is no requestIdleCallback, timer, or all-route warmup.

Build evidence

Method: production vite build --manifest; eager graph is the recursive static-import closure of index.html plus the critical src/main.ts entry. Transfer proxies sum independently compressed JS assets using gzip level 9 and Brotli quality 4.

graph JS chunks raw bytes gzip-9 bytes Brotli-q4 bytes change vs. base
exact base 9a0a6cf813 eager 37 12,442,050 3,029,044 2,932,394 baseline
prior head ddd6342a81 eager 44 5,780,893 1,591,275 1,542,622 -53.54% raw / -47.39% Brotli
transition head d63d4744db eager 45 5,782,906 1,592,116 1,543,830 -53.52% raw / -47.35% Brotli
this head 1ffc139b86 eager 45 5,783,797 1,592,316 1,543,987 -53.51% raw / -47.35% Brotli
this head selected chat 142 11,231,976 2,830,223 2,809,273 -9.73% raw / -4.20% Brotli

The attempt-identity correction costs 891 raw bytes, 200 gzip bytes, and 157 Brotli-q4 bytes versus d63d4744db, with no eager chunk-count change. Total emitted JS assets are 456; 45 are in the eager closure.

All coarse route groups remain isDynamicEntry=true and outside the eager closure:

route entry asset raw gzip-9 Brotli-q4
activity activity-D6-t78Xi.js 172,924 32,091 32,786
browser browser-BTQ4uacX.js 31,473 6,166 6,532
chat chat-BSo97GNH.js 60,547 15,728 16,055
home home-DpfLG_lc.js 94,956 21,814 22,249
miscellaneous misc-BvBod969.js 20,895 5,301 5,514
onboarding onboarding-DNptKKVZ.js 99,400 24,305 24,633
settings settings-DogsmjXo.js 284,672 49,510 50,389
workflows workflows-CqMb079t.js 256,129 43,326 42,601

The sidebar layout is also a non-eager dynamic entry (sidebar-layout-Ca9CS5rN.js, 102,432 raw / 20,902 gzip-9 / 21,592 Brotli-q4). The minimal layout stays in a non-eager shared chunk.

Largest remaining eager chunks are unchanged in kind: realtime (2,299,181 raw), main (1,147,253), runners (857,171), clipboard (578,290), and Clerk client (389,330). No minification, manual chunking, Clerk/PostHog entry point, service worker, or cache-policy changes are included.

Exact eager JS assets at 1ffc139

alert-CnkZWFVT.js, app-skeleton-ScyQDx5T.js, arrow-up-right-tF4dEQw7.js, attachment-url-CCB_8Q-0.js, avatar-svg-preview-D5rpKCZs.js, billing-M5bX22Fc.js, chat-threads-Cg3EzWJj.js, chevron-right-BwzJZwwR.js, chunk-C_YJxL4P.js, client-BNIv8351.js, clipboard-CpFPzUFC.js, components-DhGyjOOI.js, constants-CMrrKKh_.js, constants-ijs49fxA.js, document-title-BSpBcMPb.js, emotion-react-jsx-runtime.browser.esm-sywq2dAg.js, error-BZ5yOgGa.js, error-uYOdvTDm-CDgY7MAQ.js, eye-LC_8sh8H.js, fetch-D5yY4_VF.js, floating-ui.react-dom-ptEAfu16.js, image-annotation-CieYHe6f.js, image-annotation-marks-BFSs1rFP.js, index-Df17u1jY.js, link-DSvv81Sj.js, mail-xswUZYTH.js, main-uPpp599P.js, org-member-routes-C5GxCN2U.js, pencil-CVbZljwS.js, platform-assets-D4fkvcAE.js, providers-DhrAmi5t.js, pwa-install-CxXaxkHB.js, queryParams-CPRBc80S.js, queryStateParams-CqgTDpqh.js, react-52BX-0De.js, react-E3W10DUO.js, react-dom-BqSYs1o-.js, realtime-BF7g2Fia.js, run-routes-DDJoPda3.js, runners-CY98t2iu.js, slack-redirect-DJhPCufL.js, standalone-mode-p8lAs8IT.js, trash-2-C0ioeiwV.js, useClerkModalStateParams-oGL1cSNr.js, warnAboutCustomizationWithoutPinning-Dpwb_Rfk.js.

Validation

  • Prettier on the final changed files
  • standard and type-aware Oxlint on the final changed files
  • ESLint on the final changed files with zero warnings
  • pnpm -F @okouai/app check-types (production and tests)
  • route-lazy-loading.test.ts: 12/12
  • link-navigation.test.tsx: 9/9 (21/21 loader/Link matrix)
  • exact chat thread-container identity regression: 1/1
  • fresh pnpm -F @okouai/app exec vite build --manifest (7,541 modules, 22.65s)
  • manifest eager/dynamic closure audit and git diff --check

No full local Vitest suite, dev server, staging-browser, pr-auto, enqueue, or merge was used.

Co-authored-by: Okou <okou@vm0.ai>
@hulh122 hulh122 self-assigned this Aug 27, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in VM0 Kanban Aug 27, 2026
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 04:20 Destroyed
@codecov

codecov Bot commented Aug 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.18269% with 45 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
...src/signals/okou-page/global-keyboard-shortcuts.ts 77.77% 16 Missing ⚠️
...s/platform/src/signals/bootstrap/slack-redirect.ts 29.41% 12 Missing ⚠️
turbo/apps/platform/src/signals/route.ts 90.99% 10 Missing ⚠️
turbo/apps/platform/src/signals/bootstrap.ts 96.79% 5 Missing ⚠️
...latform/src/signals/bootstrap/inspect-log-input.ts 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown
Contributor

API preview: https://pr-29757-api.vm6.ai

Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 04:58 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 05:24 Destroyed
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 05:39 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 05:59 Destroyed
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 06:37 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 06:58 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 07:19 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 07:36 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 07:57 Destroyed
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 08:16 Destroyed
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 08:36 Destroyed
@hulh122

hulh122 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Parent Mode C preview gate — exact head ddd6342a81760a217443afa39d19650bd7535ac1

Classification: PASS for PR-specific regression risk. Connectors checkpoint is baseline-blocked/non-attributable and does not block this PR.

Environment:

Measured anonymous mobile startup against an equivalent production capture:

  • Cold skeleton hidden: 1,719 ms vs 3,132.5 ms (45.1% faster)
  • Warm skeleton hidden: 2,532 ms vs 3,067 ms (17.4% faster)
  • All startup JS decoded: 6,741,162 vs 13,411,033 bytes (49.7% lower)
  • All startup JS encoded: 1,906,126 vs 3,319,259 bytes (42.6% lower)

Functional/regression coverage:

  • Anonymous root → sign-in redirect, sign-up/password/OTP flow, and onboarding: PASS
  • Representative Agents/Workflows navigation and mobile sidebar: PASS
  • Settings lazy handoff: PASS; no skeleton regression and CDP remained responsive
  • Same-group chat → chat navigation: PASS; the original chat region and <main> DOM nodes remained connected and identical, with skeletonSeen=false and zero skeleton mutations
  • Screenshot: https://cdn.vm0.io/artifacts/f7guj9q087.png
  • No 5xx observed. Clerk development warnings and optional chat browser/event-snapshot 404s were present. One synthetic second-chat creation briefly showed Chat composer requires an active agent; reload recovered, and focused/full chat navigation tests plus the exact DOM-identity invariant passed. Treating this as an unattributed residual, not a demonstrated PR regression.

Connectors attribution gate:

  • Re-ran the identical chat → mobile sidebar → Connectors flow on immutable pre-lazy SHA 9a0a6cf813f85f977d615d9f6043170e91990c5f (https://7ffa5807.okou-app.pages.dev) while routing its API calls to the same PR API and using the same authenticated org/agent fixture.
  • The baseline reproduced the freeze exactly. Before click the renderer was 0% CPU / 309,124 KiB RSS. The next three 1-second samples were 100%, 83%, and 92% CPU (91.7% average), with RSS 631,580 → 647,044 → 672,168 KiB; later it reached 101% CPU / 1,144,172 KiB RSS. CDP get url and eval both timed out at 6 seconds.
  • Code attribution also found the relevant Connectors/sidebar/category blobs byte-identical on base, PR head, and current main; the real grouped-category mount test passed 3/3 on both exact base and exact head.

Therefore the Connectors loop is a pre-existing baseline issue to track separately. No correction or head change is warranted for #29757 from the current evidence.

hulh122 and others added 5 commits August 27, 2026 18:23
Co-authored-by: Okou <okou@vm0.ai>
Co-authored-by: Okou <okou@vm0.ai>
Co-authored-by: Okou <okou@vm0.ai>
Co-authored-by: Okou <okou@vm0.ai>
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 12:12 Destroyed
hulh122 and others added 2 commits August 27, 2026 20:32
Co-authored-by: Okou <okou@vm0.ai>
Preserve lazy target preparation and active-route ownership while integrating history-state synchronization from #29898.

Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 12:50 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 13:13 Destroyed
@github-actions
github-actions Bot temporarily deployed to api/preview/perf/route-boundary-lazy-loading August 27, 2026 13:30 Destroyed
Co-authored-by: Okou <okou@vm0.ai>
@hulh122

hulh122 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Exact-head staging-browser QA passed for 3500569.

Deployment under test

Desktop cold route-boundary checks (1440x900)

  • Chat -> Workflows: workflows chunk 251.2 ms; navigation committed 18.4 ms after completion; no bootstrap/global skeleton.
  • Workflows -> Artifacts: activity chunk 207.1 ms; navigation committed after target resources completed; no skeleton and no long task.
  • Artifacts -> Connectors: settings chunk 219.9 ms; navigation committed about 19 ms after completion; no skeleton.
  • Hot Artifacts <-> Connectors: no route JS requests and no skeleton.
  • Stable Connectors renderer: 0.2% average CPU and 300,731 KiB average RSS over 5 seconds; CDP remained responsive.

Desktop evidence: https://cdn.vm0.io/artifacts/0rd4rl3vh5.png

iPhone 17 Chat -> mobile sidebar -> Connectors

  • Device emulation: 402x874 CSS px, DPR 3, iPhone OS 19 UA.
  • Cold: settings chunk 44.2 ms; navigation committed 38.6 ms after chunk completion; old main remained until commit; no skeleton. Long tasks were 87 ms and 198 ms during the first mount.
  • Hot: no JS resources and no skeleton; CDP remained responsive. One 265 ms Connectors mount task remains visible.
  • Stable renderer after hot navigation: 0.4% average CPU and 284,612 KiB average RSS over 5 seconds.
  • The residual Connectors mount cost is consistent with the separately reproduced non-PR baseline issue; current evidence does not attribute it to this PR.
  • Only console output was the expected Clerk development-key warning.

Mobile evidence: https://cdn.vm0.io/artifacts/9w5ypmgh5z.png

CI is fully green, including all 8 app shards, all 8 API shards, lint/type/Knip, CodeQL, Semgrep, deploy, e2e, and ci-gate-turbo. GitHub currently reports OPEN, MERGEABLE, CLEAN.

No merge or pr-auto action was taken.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

1 participant