Web Performance
Loading-path architecture, budgets, and verification for the Clapilot web app.
Clapilot prioritizes useful persisted data before remote synchronization and keeps optional surfaces off the critical navigation path.
Loading-path rules
- The root language provider starts from the server cookie, owns one dictionary/listener set, and prevents a German-to-selected-language hydration refetch.
- The global chat rail loads the full floating chat only when expanded or after an idle window for users who left it expanded.
- Email, Calendar, and Documents paint persisted database/cache state first. Provider synchronization is explicit or scheduled after browser idle and must not block navigation.
- Dashboard and task lists use compact task payloads and SQL aggregates; task details and attachments load from the dedicated detail route.
- Module inventory uses a short process cache and direct slug lookup. Module pages request one manifest; static module assets use ETags and private browser caching.
- Long email, task, and chat rows use
content-visibility: autoso off-screen rows do not consume initial layout/paint work. - Chat inline link previews fetch only when the preview card is near the viewport (IntersectionObserver), cap concurrent
/api/chat/link-previewrequests at four, and cache results — including failures — insessionStoragefor six hours, so a link-heavy chat history does not refire dozens of preview requests on every app start. - In the Clapilot Tab Layout, the always-visible chat dock still mounts on browser idle rather than eagerly, so page startup wins the race for network and main-thread time.
- The Next.js client router keeps recently visited pages reusable for 30 seconds (
experimental.staleTimes.dynamic), so switching between workspace tabs or using back/forward does not refetch the full RSC payload each time; pages fetch their data client-side on mount, so this does not serve stale content. - The tab layout additionally keeps the three most recently used tab views mounted (hidden) with frozen router contexts, flipping visibility optimistically on tab activation — switch-back is instant with full client state preserved, at the cost of the hidden pages' background effects staying active (bounded by the three-view cap).
Regression gates
npm run perf:bundle:gate reports raw, gzip, and Brotli JavaScript totals and gates on the largest raw chunk, the largest Brotli chunk, and the Brotli JS total. Environment overrides are PERF_MAX_CHUNK_KB, PERF_MAX_CHUNK_BROTLI_KB, and PERF_MAX_TOTAL_JS_BROTLI_MB. The raw JS total is intentionally report-only: it grows with every feature even under healthy code-splitting, so gating it only produced recurring threshold-bump commits, while the compressed transfer weight and per-chunk caps catch the regressions that actually affect users (a giant chunk from broken splitting, or a heavy dependency landing in the client bundle).
npm run e2e:local:performance covers Dashboard, Chat, Documents, Email, Tasks, Calendar, Canvas, and Agent Orchestrator. Route checks record interactive time and include request-count, transferred-byte, compact-payload, provider-sync, and overlapping-poll assertions where applicable.
Use a production build for bundle comparisons. Browser development-server measurements are useful for request fan-out and behavior, but they are not production latency SLAs.
