# Web request audit ## Initial request topology ### App shell and auth | Area | Request(s) | Current behavior | Waste / risk | | --- | --- | --- | --- | | `AppShell` | `refreshAccessToken()`, `getUserProfile()` | Runs on authenticated shell mount. Profile is stored in `UserSettingsContext`. | This should be the primary profile/settings source for child pages. | | `LoginForm` existing auth check | `refreshAccessToken()`, `getUserProfile()` | Valid existing session fetches profile to choose locale before redirect. | Acceptable, but could reuse stored auth language if persisted in future. | | `LoginForm` submit | `loginWithEmail()`, `getUserProfile()` | Login response lacks settings, so profile is fetched to choose locale. | Acceptable until backend returns language/settings in auth response. | ### Duplicate profile/settings reads | Page | Current request | Better behavior | | --- | --- | --- | | `SettingsPage` | `getUserProfile()` + `getPointsBalance()` | Reuse `UserSettingsContext.userProfile`; fetch only points. | | `GeneralSettingsPage` | `getUserProfile()` | Seed from `UserSettingsContext.userProfile`; fetch only if context missing. Update context after `updateUserSettings()`. | | `ProfileDetailPage` | `getUserProfile()` | Seed from `UserSettingsContext.userProfile`; fetch only if context missing. Update context after `updateUserProfile()` / avatar upload. | | `ManualDivinationPage` / `AutoDivinationPage` | import `getUserProfile` but rely on context for profile; `getUserProfile` import appears unused | Remove unused import and keep using context. | ### Points and store reads | Page | Current request | Better behavior | | --- | --- | --- | | `Dashboard` | `getPointsBalance()`, `getUnreadNotificationCount()`, `getAgentHistory()` | Points has TTL cache, but should dedupe in-flight. History can be shared with history list via short TTL cache. | | `SettingsPage` | `getPointsBalance()` | Keep cache, add in-flight dedupe and stale-while-revalidate option. | | `StorePage` | `getPointsBalance()`, `getPackages()` | Cache packages for a longer TTL because packages are stable configuration. Keep explicit invalidation after purchase/payment flows. | | `ManualDivinationPage` / `AutoDivinationPage` | `getPointsBalance()` | Reuse points cache/in-flight dedupe; consider prefetch when entering divination nav group. | ### History and notifications | Area | Current request | Better behavior | | --- | --- | --- | | `Dashboard` | `getAgentHistory()` for latest four | Add short TTL/in-flight cache for history summary; history list can reuse if still fresh. | | `HistoryListPage` | `getAgentHistory()` full list | Reuse cache populated by dashboard, then refresh in background. | | `NotificationPage` | `getNotifications(locale)` | Cache list briefly per locale. Invalidate/update locally after mark-read actions. | | Dashboard unread badge | `getUnreadNotificationCount()` | Cache briefly; invalidate/update after mark-read or mark-all-read. | ## Priority plan 1. Add a small typed cache helper in `web/src/lib/api.ts` or adjacent `web/src/lib/api-cache.ts` for: - TTL cache - in-flight promise dedupe - explicit invalidation - optional stale return with background refresh where UI can support it 2. Apply first to profile/settings, points, packages, history summary/list, notification list/count. 3. Refactor pages to use `UserSettingsContext` for profile reads. 4. Add prefetch hooks at AppShell/nav boundaries where it is cheap and safe. 5. Browser-verify request count reductions on: - login -> dashboard - dashboard -> settings -> profile -> general - dashboard -> manual/auto divination - dashboard -> history - dashboard -> store ## Open implementation questions * Whether to implement a minimal local cache helper or introduce a query library. Current repo has no query dependency, so default recommendation is a minimal helper first. * Whether auth login response should eventually include profile language/settings to avoid immediate post-login profile fetch. This would be a backend contract change and is out of scope for the first frontend-only optimization pass.