Mail: a complete refreshed page keeps deleted rows from its warm snapshot #847

Open
opened 2026-10-02 13:28:34 +00:00 by kayg · 0 comments
Owner

Independent round 7b data-integrity review for #427. Branch job/maillayouts, head f9f360e68f. Non-blocking: obsolete rows and bodies remain visible; no accepted write to deleted data was shown.

Context: the Mail layout warms a list snapshot. A message is then removed from that folder by the provider or another client. On a warm return after 15 seconds, the app fetches the current list page.

Evidence:

  • apps/web/src/lib/mail/MailView.svelte:623-631: the fresh page replaces matching rows, then appends every cached row whose ID is absent from the fresh page. It does this even when next is null and the fresh page is the entire remaining list, including an empty list. storeListSnapshot saves the obsolete rows again.
  • MailView.svelte:463-467 and 547-550: a warmed message body is read from mailDetailCache without revalidation, so its obsolete row can reopen the retained body.
  • apps/web/src/lib/mail/readerCache.ts:67-70: getOrLoad returns a retained body directly; the body cache has bounds but no freshness check.

Proof: an isolated local test executes the exact merge expression from the reviewed MailView head. Start with one cached removed-message row and return an authoritative empty page with next=null. The old row remains. The route, provider sync and DOM were not run.

Expected: reconcile the authoritative refreshed window, including deletions. If next=null, replace the complete list and invalidate removed bodies. If more pages remain, preserve only rows that are known to belong outside the refreshed window, or use deletion deltas.

Regression test idea: warm a one-message folder and its body; remove that message; return after the stale window; respond with items=[] and next=null. Assert an empty list and no cached deleted-message reader. Also cover a short complete refreshed page with one removed row and one surviving row.

Duplicate check: searched all issue titles for Mail stale/deleted/cache findings. No separate issue for the completed-page merge was found.

Independent round 7b data-integrity review for #427. Branch job/maillayouts, head f9f360e68f4e9ca106ddd7fea24d1f4363881a2b. Non-blocking: obsolete rows and bodies remain visible; no accepted write to deleted data was shown. Context: the Mail layout warms a list snapshot. A message is then removed from that folder by the provider or another client. On a warm return after 15 seconds, the app fetches the current list page. Evidence: - apps/web/src/lib/mail/MailView.svelte:623-631: the fresh page replaces matching rows, then appends every cached row whose ID is absent from the fresh page. It does this even when next is null and the fresh page is the entire remaining list, including an empty list. storeListSnapshot saves the obsolete rows again. - MailView.svelte:463-467 and 547-550: a warmed message body is read from mailDetailCache without revalidation, so its obsolete row can reopen the retained body. - apps/web/src/lib/mail/readerCache.ts:67-70: getOrLoad returns a retained body directly; the body cache has bounds but no freshness check. Proof: an isolated local test executes the exact merge expression from the reviewed MailView head. Start with one cached removed-message row and return an authoritative empty page with next=null. The old row remains. The route, provider sync and DOM were not run. Expected: reconcile the authoritative refreshed window, including deletions. If next=null, replace the complete list and invalidate removed bodies. If more pages remain, preserve only rows that are known to belong outside the refreshed window, or use deletion deltas. Regression test idea: warm a one-message folder and its body; remove that message; return after the stale window; respond with items=[] and next=null. Assert an empty list and no cached deleted-message reader. Also cover a short complete refreshed page with one removed row and one surviving row. Duplicate check: searched all issue titles for Mail stale/deleted/cache findings. No separate issue for the completed-page merge was found.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#847
No description provided.