fix(frontend): /me history goes stale under Activity (audit finding 1) - #61
Merged
Merged
Conversation
items seeded from the analyses prop via useState, which used to be safe: navigating away unmounted the route, so returning remounted and reseeded it. Under cacheComponents the App Router hides /me behind React's <Activity> and keeps it alive for up to 3 routes, so the seed stuck and an analysis saved elsewhere never appeared in the history. Resync on the server list's content rather than its identity -- a fresh server render hands over a new array every time, and resetting on that would wipe an in-flight undo. Uses React's documented adjusting-state- when-a-prop-changes pattern, so there is no effect and no extra paint. The pending row stays optimistically removed because its DELETE has not been sent yet and the server still legitimately lists it. orderRef becomes state: the resync rewrites it, and writing a ref during render is not allowed (react-hooks/refs).
This was referenced Jul 31, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Finding 1 from the Activity audit in #59. Same root cause as that PR, different component and a different symptom — this one is data correctness rather than cosmetics.
The bug
HistoryGridseedsitemsfrom theanalysesprop withuseState. That used to be safe: navigating away unmounted/me, so returning remounted the component and reseeded it from the fresh server payload.Under
cacheComponents: truethe App Router hides/mebehind React's<Activity>and keeps it alive for up to 3 routes. The seed sticks. So: open/me, go analyse someone, save them, come back — the new analysis is not in your history. The server sent it; the component ignored it.Reproduced before fixing: render with one row, hide, re-show with two rows, and the second row never appears.
The fix
Resync on the server list's content, not its identity. A fresh server render hands over a new array on every pass, so resetting on identity would wipe an in-flight undo for no reason — there is a regression test for exactly that.
This is React's documented "adjusting state when a prop changes" pattern: a plain render-time computation, no
useEffectand no extra paint.Two details worth calling out:
DELETEhas not been sent yet, so the server still legitimately lists it; naively resyncing would make a deleted card flicker back while its own undo toast is still on screen. Covered by a test.orderRefbecame state. The resync rewrites it, and writing a ref during render is not allowed —react-hooks/refscaught this, which is how it was found rather than shipped.Tests
Four new cases alongside the existing delete/undo pair, all driving a real
<Activity>hide→show cycle:Verification
vitest run77 passed / 23 files,tsc --noEmitclean,eslint srcclean.Merge order
Independent of #59 and #60 — no shared files. CI here will stay red until #60 merges:
mainis currently red from a fixture time bomb unrelated to any of this.Findings 2 and 3 from the audit (badge popover escaping the Activity boundary; badge trigger not exposed as a button) are still unfixed and documented in
docs/PROGRESS_LOG.md.