Observation, filed unassigned while implementing #5198 (PR #5242). Pre-existing on main, untouched by that PR, and not a disclosure — the orphaned entry belongs to the same user who wrote it. Filing it because it silently costs the seed cache its whole benefit on a browser's first login, and because it is invisible: every symptom looks like a normal cache miss.
What I measured
While writing the counter-pin for #5198 ("a same-principal remount still seeds instantly"), the test read an empty app list until I stamped ActiveOrganizationStoragebefore the first render. Tracing why:
MetadataProvider derives its cache scope from ActiveOrganizationStorage.get() || NO_ORG_SCOPE (activeOrgScope, objectui#4486). On a browser that has never signed in before, that storage is empty when the console mounts, and AuthProvider only resolves the active organization aftergetSession returns, in refreshOrganizations. The eager app fetch starts at mount and typically lands first, so:
- first login: apps fetched, written under the no-org scope (
objectui:metadata:app:@none:...), then AuthProvider stamps the real org id; - every later boot: the key now resolves to the real org, the no-org entry is never looked at again, and the seed misses;
- the miss is repaired by the fetch that follows, which writes the correctly scoped entry — so it self-heals after one boot, and the stale no-org entry stays in
sessionStorage until the tab closes.
Net cost: the seed optimization does nothing on the boot right after a first login, plus one orphaned entry per tab. Nothing renders wrong, which is exactly why nothing reports it.
primeCacheFor in MetadataProvider.orgScopedCache.test.tsx sets the org id before rendering, which models a returning browser correctly — so the existing pins never see this window, and neither do the ones I added (they follow the same modelling deliberately).
Adjacent, same window, worth a look at the same time
That first-login GET /api/v1/meta/* also goes out with noX-Tenant-ID header, since createAuthenticatedFetch reads the same empty storage. Whatever the server does with a missing tenant on an authenticated session is presumably fine, but the cached answer is then labelled @none while being whatever the server chose — worth confirming those two agree before deciding what the right key is.
Not attempted here
Out of scope for #5198 and deliberately left alone: the fix is a design choice (wait for the org before seeding? re-key the entry when the org resolves? treat the no-org entry as adoptable by the first org that resolves?), and each option trades a request against a render. No code was touched for this.
Observation, filed unassigned while implementing #5198 (PR #5242). Pre-existing on
main, untouched by that PR, and not a disclosure — the orphaned entry belongs to the same user who wrote it. Filing it because it silently costs the seed cache its whole benefit on a browser's first login, and because it is invisible: every symptom looks like a normal cache miss.What I measured
While writing the counter-pin for #5198 ("a same-principal remount still seeds instantly"), the test read an empty app list until I stamped
ActiveOrganizationStoragebefore the first render. Tracing why:MetadataProviderderives its cache scope fromActiveOrganizationStorage.get() || NO_ORG_SCOPE(activeOrgScope, objectui#4486). On a browser that has never signed in before, that storage is empty when the console mounts, andAuthProvideronly resolves the active organization aftergetSessionreturns, inrefreshOrganizations. The eagerappfetch starts at mount and typically lands first, so:objectui:metadata:app:@none:...), thenAuthProviderstamps the real org id;sessionStorageuntil the tab closes.Net cost: the seed optimization does nothing on the boot right after a first login, plus one orphaned entry per tab. Nothing renders wrong, which is exactly why nothing reports it.
primeCacheForinMetadataProvider.orgScopedCache.test.tsxsets the org id before rendering, which models a returning browser correctly — so the existing pins never see this window, and neither do the ones I added (they follow the same modelling deliberately).Adjacent, same window, worth a look at the same time
That first-login
GET /api/v1/meta/*also goes out with noX-Tenant-IDheader, sincecreateAuthenticatedFetchreads the same empty storage. Whatever the server does with a missing tenant on an authenticated session is presumably fine, but the cached answer is then labelled@nonewhile being whatever the server chose — worth confirming those two agree before deciding what the right key is.Not attempted here
Out of scope for #5198 and deliberately left alone: the fix is a design choice (wait for the org before seeding? re-key the entry when the org resolves? treat the no-org entry as adoptable by the first org that resolves?), and each option trades a request against a render. No code was touched for this.