Skip to content

fix(frontend): /me history goes stale under Activity (audit finding 1) - #61

Merged
Shaan-alpha merged 2 commits into
mainfrom
fix/history-grid-stale-under-activity
Jul 31, 2026
Merged

fix(frontend): /me history goes stale under Activity (audit finding 1)#61
Shaan-alpha merged 2 commits into
mainfrom
fix/history-grid-stale-under-activity

Conversation

@Shaan-alpha

Copy link
Copy Markdown
Owner

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

HistoryGrid seeds items from the analyses prop with useState. That used to be safe: navigating away unmounted /me, so returning remounted the component and reseeded it from the fresh server payload.

Under cacheComponents: true the App Router hides /me behind 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 useEffect and no extra paint.

Two details worth calling out:

  • The pending row stays optimistically removed across a resync. Its DELETE has 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.
  • orderRef became state. The resync rewrites it, and writing a ref during render is not allowed — react-hooks/refs caught 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:

  • picks up an analysis saved elsewhere (failed before the fix)
  • drops an analysis deleted elsewhere (failed before the fix)
  • a resync does not resurrect a card inside its undo window (failed before the fix)
  • does not reset the list when the prop identity changes but content does not (passed before, guards the fix from over-correcting)

Verification

vitest run 77 passed / 23 files, tsc --noEmit clean, eslint src clean.

Merge order

Independent of #59 and #60 — no shared files. CI here will stay red until #60 merges: main is 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.

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).
Copilot AI review requested due to automatic review settings July 31, 2026 02:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Shaan-alpha
Shaan-alpha merged commit b694214 into main Jul 31, 2026
5 checks passed
@Shaan-alpha
Shaan-alpha deleted the fix/history-grid-stale-under-activity branch July 31, 2026 03:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants