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: auto so 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-preview requests at four, and cache results — including failures — in sessionStorage for 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.