forge back up sync 성공 success! 333 저그 백업

# Blueprint: MemoSquare data backup, survival, and recovery

**Purpose:** Hand this document to another Cursor agent (or human architect) so they can **design a similar system** in another app. It describes **how this Flutter app keeps memo data recoverable** across crashes, bad saves, many in-place upgrades, and **some** reinstall scenarios — and where recovery **cannot** be automatic.

**Canonical implementation (this repo):** Dart under `lib/data/`, `lib/services/`, orchestration in `lib/main.dart`.

---

## 1. Why it feels like “rebuild / override still allows backup”

The app does **not** rely on a single store. It uses **layered persistence**:

| Layer | Typical location | Survives in-place app upgrade? | Survives “Delete app” on iOS? |
|--------|------------------|-------------------------------|-------------------------------|
| A. Working copy | `SharedPreferences` (per-app container) | Yes | **No** |
| B. Local safety net | `getApplicationSupportDirectory()` → `memo_data/` | Yes | **No** |
| C. Survival / folder backup | User-visible path (e.g. `Documents/…`, or **iCloud Drive** folder picked in Files) | N/A (not inside app sandbox) | **Often yes** if folder is outside sandbox / synced |
| D. iCloud board file | App’s **iCloud Drive** container (native channel) | Yes (synced) | **Often yes** if iCloud account + container intact |
| E. Google Drive (optional) | User’s Drive file | Yes | Yes (account + file) |
| F. Backup code | User copy/paste (email, Notes, etc.) | N/A | **Yes** (user-owned) |

**Key idea:** An **in-place** install from Xcode / TestFlight / App Store **keeps the same app container**, so layers **A + B** (and prefs-backed trash) remain. That is why frequent **rebuilds that replace the binary without deleting the app** still show the same data.

If the user **deletes the app** on iPhone, **Apple removes the sandbox** — **A and B are gone** unless something already copied data to **C, D, E, or F**.

---

## 2. Architecture snapshot (mental model)

```mermaid
flowchart TB
  subgraph runtime [Runtime UI state]
    UI[Board + editors]
  end

  subgraph primary [Primary persistence]
    Prefs[SharedPreferences: memos, settings, windows, trash JSON]
  end

  subgraph safety [Local safety net - same machine, app container]
    Auto[autosave_latest.json]
    Snap[snapshots / rotating JSON]
    Log[backup_recovery.log + data_guardian_audit.log]
  end

  subgraph escape [Outside sandbox or user-owned]
    Surv[current.json in chosen folder]
    iCloud[iCloud Drive board JSON - native]
    Drive[Google Drive file - optional]
    Code[Base64 backup code - clipboard / text]
  end

  UI -->|debounced persist| Prefs
  UI -->|same payload| Auto
  Auto --> Snap
  UI --> Surv
  UI --> iCloud
  UI --> Drive
  UI --> Code

Single canonical payload shape: A JSON object (and the same structure inside MemoBackupPack) carrying at least: schema, updatedAt, memos[], trash[], settings, windowStates, windowOrder, plus optional appVersion / appBuild. The backup code is that JSON + checksum, Base64-encoded (lib/data/memo_backup_code.dart).


3. Layer A — Primary store (MemoRepository)


4. Layer B — MemoSafetyNet (autosave + rotating snapshots)


5. Layer C — MemoSurvivalBackupService (“folder backup”)


6. Layer D — Apple iCloud board sync


7. Layer E — Google Drive (optional user sync)


8. Layer F — Backup code (portable, human-storable)


9. Trash as soft-delete safety


10. Observability — MemoDataGuardian


11. Orchestration — boot and persist (main.dart)

Bootstrap order (simplified):

  1. MemoRepository.runStartupMigrations()
  2. MemoSurvivalBackupService.ensureBootstrapped() (validate/clear broken folder prefs)
  3. Load memos/settings/windows/order/trash from prefs
  4. If survival pack applies → write back into prefs + trash
  5. If memos still empty or load was corrupted → tryFindRecoveryOffer() (safety net), then _tryRecoveryOfferFromICloud()
  6. setState with loaded data; _finishDeferredStartup may show “Local backup found” dialog for RecoveryOffer
  7. MemoSurvivalBackupService.markReadyForSync() and stage payload after first frame (deferred startup)

Every successful persist path should:

Guards against “empty board” wiping real data:


12. Spec for another agent (“build this pattern elsewhere”)

Minimum viable multi-layer design:

  1. Primary store — fast reads/writes (prefs, SQLite, Hive — team choice).
  2. Same-device safety net — full export JSON under getApplicationSupportDirectory() (or equivalent), atomic writes, rotating snapshots, non-empty clobber protection.
  3. Optional off-sandbox mirror — user-chosen directory or cloud doc; debounced; tmp+rename; auto-restore only when local is provably empty to avoid overwriting intentional clears (this app still allows nuanced cases — study tryAutoRestoreIfLocalEmpty + memoV2KeyPresent).
  4. Optional platform cloud — container-based file (iOS/macOS) or backend sync; recovery read when local + safety net fail.
  5. Human-portable export — signed or checksummed serialized blob (like backup code) for air-gapped recovery.
  6. Soft delete — time-bounded trash in the same backup payload so restores are complete.
  7. Audit log — append-only, build-tagged, for support and Data Sentinel-style investigations.
  8. Single JSON schema — one toJson / fromJson “pack” used for file export, safety net, folder backup, and optional cloud — avoids incompatible parallel formats.

Non-goals / caveats to document for users:


13. File index (implementation map)

Concern Primary file(s)
Prefs + migrations + save guards lib/data/memo_repository.dart
Autosave, snapshots, recovery scan, recovery log lib/data/memo_safety_net.dart
Folder survival backup + auto-restore lib/services/memo_survival_backup_service.dart
iCloud board file bridge lib/services/apple_icloud_board_sync.dart
Backup code codec + pack schema lib/data/memo_backup_code.dart
Trash lib/data/memo_trash_store.dart
Audit trail lib/services/memo_data_guardian.dart
Boot, dialogs, persist pipeline, merge lib/main.dart
Tests (examples) test/memo_safety_net_test.dart, test/memo_backup_code_test.dart

14. Related product rules (this repo)


Document generated for cross-agent handoff. Implementation truth remains the Dart sources cited above.