Files
pl_mail/docs/internals
Paul LütznerandClaude Opus 5 6125cc3a32 fix(gmail): a relabelled message reaches the tab it was moved to
Gmail classifies after delivery, and re-classifies every time the user drags
mail between tabs, so CATEGORY_* comes and goes on rows plMail has held for
months. Re-fetching exactly those rows is what the relabel sync exists for —
and enrichExisting() overwrote gmailLabelIds, the only signal the categorizer
consults on a Gmail row, without asking what those new labels now mean.

So the category kept whatever ingest computed on the day the mail arrived. The
drift was visible from two directions at once: the reading pane explains the
category live, from the labels, while the inbox tabs filter on the thread's
stored column. A relabelled promotion said "Gmail said so: CATEGORY_PROMOTIONS"
in the reading pane and sat in Primary in the list, until the next
`app:backfill category` run happened to agree with it.

The row is recategorised in the same breath as the labels that decide it, and
then re-adopted onto the thread — the tabs read the thread, not the message, so
the row alone never reaches them. An empty correspondent map is passed on
purpose: gmailLabelIds is non-null by then, so the categorizer takes its Gmail
branch and never reaches the correspondence override that map feeds.

MessageThreader::refreshCategory() is the other half of recordActivity(), which
could not do this job: it adopts only while moving lastMessageAt forward, and a
message being re-read is already counted in that key, so its gate never opens.
Most-recent-wins instead — adopt when this message IS the newest the thread
has. Ties adopt, matching the backfill's DISTINCT ON, which picks between two
rows of equal received_at without ceremony either.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 13:08:25 +02:00
..