mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 13:01:00 +00:00
A server-prefetching page dehydrates a health record into the HTML and seeds it into the client's query cache under the key the client cell reads. That copy is only right when the account the page read is the account the client's route would answer with, and under an account switch it never is: `getSession()` answers who is CALLING, deliberately and permanently, so all five prefetching pages seeded the delegate's own rows into a document opened on somebody else's record. The dashboard was the worst of it — snapshot, digest and widget layout ride routes that refuse the refetch, so the delegate's numbers stayed on the owner's dashboard, while the chart row refetched to the owner and left the page showing two people at once under a banner naming one. Resolve the session through `getUnswitchedSession()` instead, which answers null while the browser is acting on another record. The prefetch steps aside and every cell fetches through its route, which is where the grant check lives. Resolving the owner here instead was the other candidate and is worse: the grant check has exactly one home, half of what these pages prefetch rides routes that declared themselves not delegable, and re-deriving it in an RSC would be a second program deciding whose record this is. Close the two shell-level doors beside it. The Coach launcher, its drawer mount and the module tour hang off `<AuthShell>` rather than off a route, so dropping Insights and Coach from the navigation under a switch left them painting on every page in the record and on the unavailable panel too — one tap from a drawer whose every action refuses, and a `POST /api/insights/coach/seen` fired by opening it at all. All three bind `useRecordCapabilities()`, the same resolved grant the banner and the switcher read. Absent, not disabled. The prefetch rule is structural: the guard walks the app tree, so a sixth prefetching page is covered the moment it lands rather than the moment somebody remembers. The pages stay on the session-surface allowlist with their reasons rewritten, because a wrapper one directory deeper is the easiest way for a surface to walk out of a guard.