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>
3.5 KiB
3.5 KiB
Genshin battle-pass duplicate Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: One row per Genshin battle pass, with every reader's marks carried to the surviving ID.
Architecture: The Game8 parser drops a leading Battle Pass - label so both sources mint the
same ID; titleExtends learns leading qualifiers; a pure remap table (src/client/state/retiredIds.ts)
moves entries in the three event-keyed stores on load and on import.
Spec: docs/superpowers/specs/2026-10-14-genshin-duplicates-design.md
Global Constraints
- Commits wait to be asked (AGENTS.md § Conventions); each task ends green.
- No change to
slugify,eventIdor the feed schema. - The remap never drops data: moved entries merge under the store's own import rule.
Tasks
- 1. Parser. Failing test in
test/adapters/game8.test.ts: the Genshin fixture yieldsFrostfarerwith idgenshin:frostfarer:2026-08-12, and no title startsBattle Pass. Strip intoEvent(src/ingest/parsers/game8.ts). Regeneratefixtures/genshin/game8-events-2026-08-14.expected.jsonwithbun run parse … --jsonand diff it: only the Frostfarer row may move. - 2. Merge. Failing tests in
test/merge.test.ts:titleExtends("Frozen Wilds", "Anniversary Frozen Wilds")is true; a one-word base is still refused;Gold Clash/Gold Rush Clashstill refused. Then amergeEventstest fusing the two pinned Genshin fixtures' Frostfarer rows into one. Implement. - 3. Remap.
src/client/state/retiredIds.ts:RETIRED_IDStable andcarryForward(map, combine). Tests intest/retired-ids.test.ts: move, both-present merge per store, idempotence, unrelated ids untouched. Wire intouseProgressload,useDailyLoginit,useMarkSetinit, andparseImportData. - 4. Verify.
bun run typecheck,bun test, rebuild the feed and diff Genshin IDs against the baseline: exactly one fewer row, only the battle-pass IDs moved. schema-guardian review. - 6. Selectors (added once the owner established the pairing).
src/ingest/aliases.tsrecords each pair by event ID with its evidence;isSameEventaccepts it; Fandom's two IDs joinRETIRED_IDS. Tests: fuses the recorded pair, not next year's run, not across pairs. - 5. Docs. AGENTS.md § Fandom (Genshin): the prefix, the corrected net-new count;
docs/DATA-MODEL.md§ ID stability: the retired-ID table;docs/INGESTION.mdmerge rules if they describetitleExtends.
Part 2 tasks
- 7. Summary rule. Tests in
test/merge.test.ts: fuses a pair whose summary names the other title; refuses when ends differ, when the named title is one word, when no summary names it. EmptySAME_EVENT;recordedSametakes a table so its test injects one. - 8. mergedIds. Merge test: survivor lists the absorbed id, chained fusions accumulate,
same-id repeats add nothing.
GachaEvent.mergedIdsoptional; feed parse accepts with and without. - 9. Client carry from feed.
aliasesFrom(events)(skips published ids and self-maps);carryForward(map, combine, table); acarry(table)on the three stores; one effect inApp. Drop Fandom's selector ids fromRETIRED_IDS. - 10. Verify. typecheck, suite, feed diff (214, survivors carry
mergedIds), browser check, schema-guardian. - 11. Docs. AGENTS.md selector bullet, INGESTION.md rule 2, DATA-MODEL.md
mergedIdsand the feed-carried move.