Context
useApiResource (apps/loopover-ui/src/lib/api/use-api-resource.ts:37-70) has no cancellation guard on its path dependency changing:
const load = useCallback(async () => { ...; setState(...) }, [enabled, label, path, token]);
useEffect(() => { ...; void load(); }, [enabled, load]);
Two concretely reachable sites:
apps/loopover-ui/src/components/site/dead-letter-queue-panel.tsx:44-46 - path = useMemo(() => buildDeadLetterQueuePath(offset), [offset]), and offset changes on every Next/Previous pagination click. Rapid clicking can let an older page's response resolve after a newer one, silently showing the wrong page's rows while the "Showing X-Y of Z" text and Prev/Next button states reflect the new offset.
apps/loopover-ui/src/components/site/app-panels/owner-panel.tsx:44-54 - repo is a free-text <input> (value={repo} / onChange={(e) => setRepo(e.target.value)} at lines 94-95) feeding directly into useApiResource's path, same keystroke-race class as the companion 4-panel issue.
apps/loopover-ui/src/routes/app.analytics.tsx:177-180 - operatorDashboardPath(selectedWindow) changes on time-window selection; lower risk (discrete clicks, not keystrokes) but same root cause.
Since this is the shared hook (also used by notification-readiness-card.tsx, digest-panel.tsx, maintainer-panel.tsx, miner-panel.tsx, commands-panel.tsx, app.operator.tsx, app.index.tsx, app.runs.tsx), fixing it once resolves this bug class for every current and future consumer of this hook.
Requirements
⚠️ Required pattern - apply the same cancelled-flag idiom already used in apps/loopover-miner-ui/src/lib/use-polled-fetch.ts:44,53,64 (set in the effect cleanup, checked before every setState) to useApiResource's load/effect. This is a distinct fix location from the companion "4 repo-picker panels" issue - those components don't use this hook, they have their own inline load functions.
Deliverables
Test Coverage Requirements
apps/loopover-ui is not covered by the src/** 99% patch gate - the new regression test is this issue's own coverage deliverable.
Expected Outcome
Rapid pagination clicks or path changes on any useApiResource consumer never result in a stale, out-of-order response silently overwriting the current state.
Links & Resources
apps/loopover-ui/src/lib/api/use-api-resource.ts:37-70, apps/loopover-miner-ui/src/lib/use-polled-fetch.ts:44,53,64 (the pattern to reuse), dead-letter-queue-panel.tsx:44-46 and owner-panel.tsx:44-54 (concrete reachable sites)
Context
useApiResource(apps/loopover-ui/src/lib/api/use-api-resource.ts:37-70) has no cancellation guard on itspathdependency changing:Two concretely reachable sites:
apps/loopover-ui/src/components/site/dead-letter-queue-panel.tsx:44-46-path = useMemo(() => buildDeadLetterQueuePath(offset), [offset]), andoffsetchanges on every Next/Previous pagination click. Rapid clicking can let an older page's response resolve after a newer one, silently showing the wrong page's rows while the "Showing X-Y of Z" text and Prev/Next button states reflect the new offset.apps/loopover-ui/src/components/site/app-panels/owner-panel.tsx:44-54-repois a free-text<input>(value={repo}/onChange={(e) => setRepo(e.target.value)}at lines 94-95) feeding directly intouseApiResource'spath, same keystroke-race class as the companion 4-panel issue.apps/loopover-ui/src/routes/app.analytics.tsx:177-180-operatorDashboardPath(selectedWindow)changes on time-window selection; lower risk (discrete clicks, not keystrokes) but same root cause.Since this is the shared hook (also used by
notification-readiness-card.tsx,digest-panel.tsx,maintainer-panel.tsx,miner-panel.tsx,commands-panel.tsx,app.operator.tsx,app.index.tsx,app.runs.tsx), fixing it once resolves this bug class for every current and future consumer of this hook.Requirements
Deliverables
useApiResource(apps/loopover-ui/src/lib/api/use-api-resource.ts) ignores a response that resolves afterpath(orenabled/token) changed again, using the same guard idiom asuse-polled-fetch.ts.pathchange) with out-of-order response resolution, asserting the final state matches the latest request, not an earlier one.Test Coverage Requirements
apps/loopover-ui is not covered by the src/** 99% patch gate - the new regression test is this issue's own coverage deliverable.
Expected Outcome
Rapid pagination clicks or path changes on any
useApiResourceconsumer never result in a stale, out-of-order response silently overwriting the current state.Links & Resources
apps/loopover-ui/src/lib/api/use-api-resource.ts:37-70,apps/loopover-miner-ui/src/lib/use-polled-fetch.ts:44,53,64(the pattern to reuse),dead-letter-queue-panel.tsx:44-46andowner-panel.tsx:44-54(concrete reachable sites)