When merge fuses two sources' copies, the loser's id leaves the calendar, and it may already hold a reader's ticks from a day it was published alone. Until now that needed a hand-edited RETIRED_IDS entry per fusion.
The survivor now lists what it absorbed in an optional mergedIds field. When a feed arrives the client turns those into carry-forward pairs, skipping any id the feed still publishes, and an import takes the same table. Optional, so an older cached feed still validates and an older bundle drops it unread. RETIRED_IDS stays for parser changes, whose old ids no feed will name again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Game8 heads Genshin's pass "Battle Pass - Moontrace"; Fandom lists "Moontrace" and files the category in a column. The label minted a second id for one event, so every battle pass since the Fandom source landed has been on the calendar twice, and a reader reported it.
Both sources now mint the same id and merge fuses them by id. The two Game8 ids that moved, Frostfarer and Moontrace (the only ones git history shows ever shipped), go in RETIRED_IDS so readers' marks follow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An event id is a localStorage key, so a fix that changes one orphans every tick, note and ignore filed under it. retiredIds.ts holds an explicit table of old id -> new id, and the progress, daily and ignored stores move entries across on load and on import.
It moves rather than copies: a copy left under the old id would be carried forward again on every load and bring back a mark the reader had cleared. When both ids hold an entry, each store's own import merge decides, except that progress keeps any field only the older row held. The table starts empty; the change that moves ids adds its entries.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>