Add selective per-dynasty sidecar merge (C8 phase 2) - #88
Conversation
Sidecar archive import is now a per-dynasty merge instead of a whole-store replace: players pick which archive dynasties to import, each with an added/overwritten conflict hint computed against live local stores. Unselected dynasties — local or archive — stay byte-identical, so imports can never surprise-wipe local history. Rivalry heat is league-scoped and stays untouched in selective mode (wholesale import remains that path); career totals recompute on write. Adds 6 lib merge tests (subset, conflicts, legacy no-rivalry archive, unknown ids, empty selection, dry-run plan) and 2 preview render contracts.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7e05af51fc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const result = mergeDynastySidecarArchiveJson( | ||
| exportDynastySidecarArchiveJson(pendingSidecarImport.payload), | ||
| { dynastyIds: sidecarSelection }, | ||
| ); |
There was a problem hiding this comment.
Restore rivalry data on standalone archive import
When a user restores a standalone Complete Dynasty Sidecars archive, confirmation now always calls mergeDynastySidecarArchiveJson, which deliberately never writes the archive's rivalry payload. The panel still exports and advertises rivalry heat, but a repo-wide caller search shows the wholesale importer is otherwise reachable only through the combined-backup journal and tests; there is no standalone “full-archive import” control despite the preview directing users to one. The import therefore reports success while silently retaining unrelated local rivalry records, so this flow needs either a full-import option or an explicit way to apply the archived rivalry store.
Useful? React with 👍 / 👎.
Understanding
C8 phase 2 (Fable board #6): sidecar archive import used to replace whole stores, which could surprise-wipe local dynasties absent from the archive. Phase 1 (preview + never-replace-absent-stores) shipped 2026-07-06; this completes the selective per-dynasty import/merge with conflict summary.
What changed
Lib (
apps/web/src/lib/dynasty-sidecar-archive.ts)planDynastySidecarMerge(payload, selection, current?)— pure dry-run: per-dynasty, per-storeadded/overwrittenoutcomes with detail strings, plus notes (unknown selection ids ignored, empty selection, rivalry policy).mergeDynastySidecarPayloads(imported, current, selection)— pure per-dynasty merge across all five dynasty-scoped stores; career totals recompute on write via existingreplaceCareerMetanormalization.mergeDynastySidecarArchiveJson(raw, { dynastyIds })— parse → plan → merge → write → fresh summary + plan.importDynastySidecarArchiveJsonremains the rivalry path and is unchanged.UI (
DynastyCartridge.tsx)Verification
pnpm --filter @mfd/web test -- src/lib/dynasty-sidecar-archive.test.ts src/features/dynasty-cartridgetsc --noEmittestcheckWhat did not change
Rollback
Revert this commit; sidecar import returns to whole-store replace with preview.