Found by the domain:ui execution seat while implementing objectui#6813 (both providers' context VALUE). Not fixed there — #6813 is scope-locked to the context value, and this is the other memo in the same file, feeding an effect rather than the context.
The shape
packages/permissions/src/MePermissionsProvider.tsx builds its fetch driver in a useCallback and names it as the fetch effect's dependency:
const fetchPermissions = useCallback(async (token) => { ... },
[endpoint, fetcher, maxRetries, retryBaseDelayMs]);
const retry = useCallback(() => { void fetchPermissions(); }, [fetchPermissions]);
useEffect(() => {
if (initialPermissions) return;
const token = { cancelled: false };
void fetchPermissions(token);
return () => { token.cancelled = true; };
}, [fetchPermissions, initialPermissions]);
useCallback carries no semantic guarantee: React may discard the cache and rebuild even when the four dependencies compare equal. The rebuilt function is a NEW identity, so the effect tears down and re-runs with nothing an author or a caller controls having changed. The observable is a redundant /api/v1/auth/me/permissions round trip — one network request per discard, on the provider that gates the whole console.
It also moves the context value, legitimately: fetchPermissions opens with setLoading(true), which flips isLoaded to false and then back, so consumers see two genuine context changes for a discard nobody asked for. After the first successful load data is non-null, so loading && !data is false and children are NOT unmounted — the fallback does not flash.
Why #6813 does not close it
#6813 makes both providers' context VALUE discard-proof, keyed on the inputs it is derived from. That is the right guarantee for the value and it does not reach here: this defect is at the EFFECT end, and the context change it produces is honest (isLoaded really did flip). Fixing it means the effect must stop keying on a droppable identity, which is a different shape from the one #6813 lands — and a riskier one, since the four dependencies include a fetcher that may be undefined and three primitives, so the input-keyed WeakMap idiom #6813 introduces does not transfer unchanged.
Measured, not inferred — this is LATENT
Same standing as the rest of the family. On React 19.2.8 (this repo's pinned version) the cache is not discarded spontaneously: measured while objectui#6724 landed, 51 re-renders with no provider, 51 with one and 42 under StrictMode each returned ONE identity, and there is no Activity/Offscreen subtree in this repo — the documented case where React does throw memo caches away. So this is a correctness dependency resting on a licence React has not exercised here, NOT a reproduction. Type Task, not Bug.
The instrument exists: the forced-discard proxy in packages/permissions/src/__tests__/providerCtxIdentity.discarded.test.tsx (added by #6813) already patches useCallback as well as useMemo, so a pin here would reuse it directly. A pin that does not force a discard proves nothing, because React will not discard on its own.
Severity
Low, same as the family — a redundant round trip on a discard, not incorrect data.
Family: objectui#6018 · #5976 · #6591 · #6592 · #6697 · #6724 · #6813 — same hazard, the effect end of the provider #6813 hardens.
Dedupe
/search/issues is unreliable for this seat, so this went through the repo-scoped REST list endpoint plus a local grep: 250 open issues collected, zero hits for fetchPermissions, MePermissionsProvider or /me/permissions other than an unrelated CEL-predicate card (#4421), and the only discard hit is #6813 itself. Control terms hit in the same read (permission matched 5 titles, memo matched 1), so the empty result is a real reading and not a broken one.
Unassigned — filed as a finding for triage to route, not claimed.
Generated by Claude Code
Generated by Claude Code
Found by the
domain:uiexecution seat while implementing objectui#6813 (both providers' context VALUE). Not fixed there — #6813 is scope-locked to the context value, and this is the other memo in the same file, feeding an effect rather than the context.The shape
packages/permissions/src/MePermissionsProvider.tsxbuilds its fetch driver in auseCallbackand names it as the fetch effect's dependency:useCallbackcarries no semantic guarantee: React may discard the cache and rebuild even when the four dependencies compare equal. The rebuilt function is a NEW identity, so the effect tears down and re-runs with nothing an author or a caller controls having changed. The observable is a redundant/api/v1/auth/me/permissionsround trip — one network request per discard, on the provider that gates the whole console.It also moves the context value, legitimately:
fetchPermissionsopens withsetLoading(true), which flipsisLoadedto false and then back, so consumers see two genuine context changes for a discard nobody asked for. After the first successful loaddatais non-null, soloading && !datais false and children are NOT unmounted — the fallback does not flash.Why #6813 does not close it
#6813 makes both providers' context VALUE discard-proof, keyed on the inputs it is derived from. That is the right guarantee for the value and it does not reach here: this defect is at the EFFECT end, and the context change it produces is honest (
isLoadedreally did flip). Fixing it means the effect must stop keying on a droppable identity, which is a different shape from the one #6813 lands — and a riskier one, since the four dependencies include afetcherthat may beundefinedand three primitives, so the input-keyedWeakMapidiom #6813 introduces does not transfer unchanged.Measured, not inferred — this is LATENT
Same standing as the rest of the family. On React 19.2.8 (this repo's pinned version) the cache is not discarded spontaneously: measured while objectui#6724 landed, 51 re-renders with no provider, 51 with one and 42 under
StrictModeeach returned ONE identity, and there is noActivity/Offscreen subtree in this repo — the documented case where React does throw memo caches away. So this is a correctness dependency resting on a licence React has not exercised here, NOT a reproduction. TypeTask, notBug.The instrument exists: the forced-discard proxy in
packages/permissions/src/__tests__/providerCtxIdentity.discarded.test.tsx(added by #6813) already patchesuseCallbackas well asuseMemo, so a pin here would reuse it directly. A pin that does not force a discard proves nothing, because React will not discard on its own.Severity
Low, same as the family — a redundant round trip on a discard, not incorrect data.
Family: objectui#6018 · #5976 · #6591 · #6592 · #6697 · #6724 · #6813 — same hazard, the effect end of the provider #6813 hardens.
Dedupe
/search/issuesis unreliable for this seat, so this went through the repo-scoped REST list endpoint plus a local grep: 250 open issues collected, zero hits forfetchPermissions,MePermissionsProvideror/me/permissionsother than an unrelated CEL-predicate card (#4421), and the onlydiscardhit is #6813 itself. Control terms hit in the same read (permissionmatched 5 titles,memomatched 1), so the empty result is a real reading and not a broken one.Unassigned — filed as a finding for triage to route, not claimed.
Generated by Claude Code
Generated by Claude Code