feat(engine): Phase M — sync transparency + local data backup #189

Merged
funman300 merged 1 commits from feat/sync-transparency into master 2026-07-17 01:11:26 +00:00
Owner

The last remaining item of the 13-phase menu redesign.

1. Post-sync merge summary

The pull poller keeps the ConflictReports from solitaire_sync::merge (previously discarded at sync_plugin.rs) in a new SyncConflictLog resource, and toasts Synced — N conflicts, kept newer values when a merge wasn't clean. No §8 territory touchedSyncResponse.conflicts already existed on the wire; no solitaire_sync type or merge-strategy changes.

2. Account tab detail

Under the sync row: a clean-merge caption or a per-field conflict list (win_streak_current — this device: 3 / server: 5), timestamped. SyncStatus::LastSynced remains live-updated as before.

3. Local data export/import

New solitaire_data::transfer: one versioned JSON bundle (settings, stats, achievements, progress) written atomically (.tmp → rename) next to the other saves. Loader is version-gated and error-surfacing (never default-restores). Account tab gains Export/Import pill buttons (hidden on wasm) with the backup path shown in a caption. Handler runs in Last — off the annotated Update spine, so the ambiguity gate is untouched.

Tests

  • solitaire_data: bundle round-trip, newer-schema rejection, missing-file error
  • engine: ECS export→import round trip; failed import warns and leaves data untouched

Gate: cargo test --workspace green, clippy --all-targets -D warnings clean, rustfmt applied.

🤖 Generated with Claude Code

The last remaining item of the 13-phase menu redesign. ## 1. Post-sync merge summary The pull poller keeps the `ConflictReport`s from `solitaire_sync::merge` (previously discarded at `sync_plugin.rs`) in a new `SyncConflictLog` resource, and toasts `Synced — N conflicts, kept newer values` when a merge wasn't clean. **No §8 territory touched** — `SyncResponse.conflicts` already existed on the wire; no solitaire_sync type or merge-strategy changes. ## 2. Account tab detail Under the sync row: a clean-merge caption or a per-field conflict list (`win_streak_current — this device: 3 / server: 5`), timestamped. `SyncStatus::LastSynced` remains live-updated as before. ## 3. Local data export/import New `solitaire_data::transfer`: one versioned JSON bundle (settings, stats, achievements, progress) written atomically (`.tmp` → rename) next to the other saves. Loader is version-gated and error-surfacing (never default-restores). Account tab gains Export/Import pill buttons (hidden on wasm) with the backup path shown in a caption. Handler runs in `Last` — off the annotated Update spine, so the ambiguity gate is untouched. ## Tests - solitaire_data: bundle round-trip, newer-schema rejection, missing-file error - engine: ECS export→import round trip; failed import warns and leaves data untouched Gate: `cargo test --workspace` green, `clippy --all-targets -D warnings` clean, rustfmt applied. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
funman300 added 1 commit 2026-07-17 01:06:20 +00:00
feat(engine): Phase M — sync transparency + local data backup
Test / fmt (pull_request) Successful in 5s
Test / test (pull_request) Successful in 4m22s
2f373784bf
Closes out the 13-phase menu redesign. Three parts:

1. Post-sync merge summary: the pull poller now keeps the
   `ConflictReport`s the merge produces (previously discarded) in a new
   `SyncConflictLog` resource and toasts "Synced — N conflicts, kept
   newer values" when a merge wasn't clean. No wire changes — the
   server-side `SyncResponse.conflicts` field already existed.

2. Account tab detail: a per-field conflict list under the sync row
   ("win_streak_current — this device: 3 / server: 5") plus a
   clean-merge caption, so the last merge is always inspectable.

3. Local data export/import: new `solitaire_data::transfer` module —
   one versioned JSON bundle (settings, stats, achievements, progress)
   written atomically next to the other saves, with a version-gated
   loader that surfaces every failure instead of defaulting. Account
   tab grows an Export/Import button pair (desktop+Android only); the
   handler runs in `Last`, off the annotated Update spine. Import
   rewrites the live resources, persists via the existing save fns,
   and fires SettingsChangedEvent so appliers react normally.

Tests: bundle round-trip/version-gate/missing-file in solitaire_data;
ECS-level export→import round trip and failed-import-warns in
settings_plugin. Gate: workspace tests green, clippy -D warnings clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
funman300 merged commit 020c860cc5 into master 2026-07-17 01:11:26 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: funman300/Ferrous-Solitaire#189