fix(feed): stop the update stream from replaying items the list already holds - #133
Conversation
…dy holds The list is ordered by arrival, not by publish date: a live update is prepended as it comes in and a batch of new articles is broadcast newest-first, so the item left on top is the batch's oldest. Reconnecting asked the server to replay from that item's timestamp, so every other article of the batch came back and was prepended a second time — duplicate rows and duplicate React keys, an inflated unread badge, and the replayed copy showing as unread even when the original had been read (the catch-up projection reports no read/like/bookmark state by design). Ask the server to replay from the newest timestamp in the list instead of from the top item, and drop an already-known id in prependItem so any other double delivery cannot duplicate a row either. Also retry the stream when it ends cleanly, not only when it errors: a server restart can close it without surfacing an error, which left the page with no live updates until a reload, contradicting the effect's own comment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hnoh6YfJwp63szeP8ZFqVg
Adds a regression test for the duplicate-row fix in 90d45c4: - feedStore.test.ts: prependItem is a no-op for an id already in the list, and still prepends a genuinely new id afterwards. - FeedPage.test.tsx: drives the stream's reconnect path end-to-end (a newest-first batch, a clean stream end, the 5s backoff retry) and asserts the reconnect replays from the newest publishedAt in the list rather than items[0] — the batch's oldest article, left on top by arrival order. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hnoh6YfJwp63szeP8ZFqVg
|
Warning Review limit reachedNext included review available in 37 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Team Run ID: 📒 Files selected for processing (5)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hnoh6YfJwp63szeP8ZFqVg
… type npm run build (tsc -b, project references) type-checks test files strictly against the real generated protobuf message type, unlike npm run typecheck's plain tsc --noEmit — a gap this reconnect test fell into by yielding plain object literals instead of casting them, unlike every other mocked streamFeedUpdates implementation already in this file. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hnoh6YfJwp63szeP8ZFqVg
Gitar review finding on this PR: newestPublishedAt compared publishedAt values with the string `>` operator. Items loaded via getFeed and items delivered over the live stream aren't guaranteed identical formatting (a value can be missing its trailing Z depending on DateTimeKind, independent of this fix) — a longer string sharing the same prefix sorts as "greater" lexicographically even when it represents the same or an earlier instant. Parsing both sides as dates removes that dependency on wire-format stability entirely. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hnoh6YfJwp63szeP8ZFqVg
|
Code Review ✅ Approved 1 resolved / 1 findingsFixes the feed's live-update stream replaying duplicate items on reconnect by using the newest ✅ 1 resolved✅ Edge Case: Replay point relies on lexicographic == chronological order
OptionsAuto-apply is off → Gitar will not commit updates to this branch. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |



Summary
items[0])items[0].publishedAt, which replayed the rest of that same batch on every reconnect — duplicate rows (and duplicate React keys), an inflated unread badge, and the replayed copy showing as unread even when the original had been readpublishedAtcurrently in the list instead ofitems[0], andprependItemno-ops for an id already present as a second line of defense against any other double deliveryRelated issue
Found by
architectduring this cycle's/maintainrefactor analysis while re-verifying repo-scout's signals; not tied to a pre-existing issue.Type of change
Checklist
dotnet build --configuration Releasepasses with 0 errors (unaffected by this branch)cd src/pressmark-web && npm run buildpasses with 0 TypeScript errorsfeedStore.test.ts(new:prependItemdedup) andFeedPage.test.tsx(extended: reconnect replay point end-to-end, including the clean-end retry) — 42/42 frontend tests passt('ns:key')— no strings touchedNotes for reviewers
Independently verified (build, full test suite, typecheck, lint, format) before opening this PR, separately from the
architect/testeragents that authored the fix and tests.🤖 Generated with Claude Code
https://claude.ai/code/session_01Hnoh6YfJwp63szeP8ZFqVg
Generated by Claude Code