10 KiB
Content Classification Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [x]) syntax for tracking.
Goal: Separate Miliastra, represent sourced passes as cycles, and let readers disable Genshin TCG tracking without losing state.
Architecture: Preserve source/identity game and IDs in the feed; optional classification metadata supplies the destination lane, battle-pass family and TCG tag. A shared resolver handles old feeds and saved snapshots. Reader normalization routes identified Miliastra state atomically and preserves account clocks; pass presentation reuses event progress rather than creating another task store.
Tech Stack: Bun, TypeScript, React 19, Zod; existing dependencies only.
Spec: docs/superpowers/specs/2026-11-01-content-classification-design.md
Global Constraints
- Work on main in this checkout per AGENTS.md. No commits or pushes without request.
- Keep every event ID, date, source ID and source URL unchanged.
- Preserve old feed validation: classification is additive; wire game stays the identity game.
- Miliastra arrives off; TCG defaults on across Genshin accounts.
- No guessed recurrence, dates, classification or completion; reader-created content is excluded.
Review Focus
- Legacy imports/late writes after a move must honor destination deletion timestamps.
- Multiple accounts with equal regions must retain distinct ownership and clock history.
- Old cached feeds and remembered snapshots must classify consistently without causing startup failure.
- Pass-only tracking and independent pins must not create duplicate rows or unrelated cycle follows.
- Hidden TCG must leave history intact and disappear from all live instructions, including event dailies.
Task 1: Shared classification and parser evidence
Files: src/shared/schema.ts, src/shared/classification.ts (new), src/shared/games.ts, src/ingest/parsers/{fandom,fandomTable,versionTable}.ts, src/ingest/merge.ts, test/classification.test.ts (new), affected adapter expectations.
Interfaces: classificationFor(event): EventClassification, displayGameFor(event): string, passFamilyFor(event): string | null, isTcgEvent(event): boolean. Metadata is optional { lane?: "miliastra", activity?: "battle-pass", family?: string, tags?: string[] }. Recognize established article families and source registrations for cached feeds; parser reads stated type labels. Register miliastra for current clients but keep existing wire game fields unchanged.
- Write tests for source categories, cached source paths, custom exclusion, source winner lacking metadata, and unknown fallback; run and observe failure.
- Add schema/resolver, parser labels and merge retention; preserve fixture IDs and dates. Cite reviewed TCG family evidence.
- Run
bun test test/classification.test.ts test/merge.test.ts test/adapters/game8.test.ts test/adapters/fandomTable.test.ts test/adapters/versionTable.test.ts; regenerate only intentionally changed expectation fields.
Task 2: Atomic routing and compatibility
Files: src/client/state/reader/contentRouting.ts (new), store.ts, reducer.ts, export.ts, schema.ts as needed; test/reader-content-routing.test.ts (new).
Interfaces: routeReaderContent(state: ReaderState): ReaderState is pure and idempotent, driven by the shared classifier. A persisted contentRoutes record maps original accounts to Miliastra counterparts. Counterpart identity is derived deterministically from the existing account ID; it is not a reminted identity. Copy original clock revisions and unknown account fields, preserve retired status, and append Miliastra to known games while hiding it on first introduction.
Rebase account-scoped fieldTimes paths for classified IDs, preserving the latest timestamp at either path and retaining origin tombstones. Route incoming old-account actions before conflict checks. Destination values win timestamp ties; progress fields merge by latest field time, day logs by union subject to day tombstones, amounts by period timestamp, ignores by saved timestamps. Archive/seen records keep original clock/resolved end and event ID but use logical game Miliastra. Do not reuse an unrelated same-region account. Normalize startup, dispatch and imports/restores so old-tab/backup deltas route consistently. No source-state transfer is repeated after clearing a mark.
- Add failing tests for two same-region origins, clock revisions, all record collections, idempotence, source/destination clears, late actions and import/restore.
- Implement pure normalization and wire locked save/import paths; create empty destination data only for actual moved state or explicit account use.
- Run
bun test test/reader-content-routing.test.tsand existing reader store/export/migration tests; expect no state validation failures.
Task 3: Classified reader rows and Miliastra settings
Files: src/client/state/reader/{eventRows,routines,migration,onboarding}.ts, src/client/ReaderApp.tsx, src/client/components/redesign/{GameSettings,SetupWizard}.tsx, src/shared/feed.ts, tests for reader rows and views.
Interfaces: Reader event rows use logical destination game and existing IDs. Shared category filtering excludes TCG when prefs.genshinTcg === false. Retain classified snapshots for migration and source-backed pass rows even when ordinary Events is off. Freshness/source lookup maps logical Miliastra to supplying Genshin sources.
- Add failing tests for lane independence, hidden-lane policy, cached snapshots, source attribution and unchanged Genshin rows.
- Route row rendering and lane discovery; offer Miliastra independently without synthetic dailies; point to its settings from Genshin Setup.
- Run focused reader/view tests and typecheck.
Task 4: Feed-backed battle-pass cycles
Files: src/client/state/reader/{routines,contentRouting}.ts, src/client/components/redesign/{RoutinesPage,RoutineDrawer,RoutineProgress,TimelinePage,GameSettings,SetupWizard}.tsx, src/client/ReaderApp.tsx, test/reader-pass-cycles.test.ts (new).
Interfaces: Feed cycle rows carry event and a stable family placement key while retaining per-window event ID for progress. Family placements are persisted in account.routines; next window inherits pins, not completion. Existing event followers gain pass-family placements once, without following unrelated routines. New accounts derive pass following from Cycles intent. Event-backed routine controls dispatch existing event outcome/progress actions.
- Add failing tests for old-reader transition, pass-only tracking, next-window completion isolation, independent pins, Miliastra pass off until lane enabled, and no second ordinary-event row.
- Implement family placements and dynamic rows using sourced event clocks; extend library, progress controls, checklist guidance and timeline windows. Never render a predicted recurrence/reset.
- Run
bun test test/reader-pass-cycles.test.tsplus affected routine/timeline tests and typecheck.
Task 5: Genshin TCG switch
Files: src/client/components/redesign/GameSettings.tsx, src/client/state/reader/eventRows.ts, routines.ts, test/reader-tcg.test.tsx (new).
Interfaces: prefs.genshinTcg absent means true. Genshin Setup renders Track TCG events, supporting copy from spec, immediate save and failure rollback. All live surfaces use the shared reader classification predicate; history remains readable.
- Add failing tests for default/on/off/cached snapshot behavior, multiple accounts, saved-history preservation and rejected save.
- Implement existing-style accessible toggle and shared filtering; no ignored/completed writes.
- Run focused TCG and reader row tests.
Task 6: Verification and documentation
Files: PRD, DATA-MODEL, INGESTION, DESIGN, ARCHITECTURE where changed; prior pass spec and audit; focused expiration regressions; browser scratch files only under /tmp.
- Verify audited ended trials cannot re-enter live rows through remembered state; verify regional day-precision ends without changing stored timestamps. Test existing behavior before proposing repairs.
- Compare parsed/built before/after IDs and dates offline. Update docs for implemented classification, routing and cycle behavior; record any plan rulings in the ledger.
- Run
bun run typecheck, fullbun test,git diff --check. Build only css/js/static shell; use a separately generated temporary feed if needed for deterministic browser data. - Browser-check desktop/mobile, both themes, ordinary HTTP hostname startup/writes, old-state migration, TCG off/reload/on, pass-only and Miliastra isolation. Run Impeccable detector once over changed UI files.
- Fresh-context final review; fix material findings with regression tests; report checks and any remaining limitations. Leave work uncommitted per AGENTS.md.
Verification record
Completed 2026-11-01. Full suite: 1748 pass, 0 fail; typecheck and diff check pass. The baseline required HTTP test binding outside the sandbox. Browser checks used Chrome on an ordinary HTTP hostname at fresh build origins: migration, TCG off/reload/failure/on, pass completion/undo, Miliastra enabling and retained pass following, new-reader startup, and desktop/mobile dark/light layouts with no page errors or horizontal overflow. Impeccable detector returned no findings. Offline comparison against HEAD covered 25 sources / 197 parsed rows and every merged game: IDs, dates, titles and source identities unchanged; only category metadata changed. The old feed schema accepts the new parsed output. Fresh code review produced five material findings, all fixed with failing-then- passing regressions (backup tombstones, legacy ignores, late-created pass following, inventory precedence and unrelated imported origins). Additional regressions cover the new-reader placeholder and already-routed import mapping. Implemented on main. The follow-up changelog commit records the implementation range under the repository convention.