Back to Super Productivity

SuperSync Sync Flow — Mermaid Chart

docs/sync-and-op-log/supersync-scenarios-flowchart.md

18.4.45.4 KB
Original Source

SuperSync Sync Flow — Mermaid Chart

Visual overview of the main sync decision tree. For full details see supersync-scenarios.md.

mermaid
flowchart TD
    START([Sync Triggered]) --> DL[Download remote ops]
    DL --> HAS_OPS{Remote ops found?}

    %% No remote ops path
    HAS_OPS -->|No| EMPTY_SVR{Empty server
+ fresh client
+ has local data?}
    EMPTY_SVR -->|Yes| SILENT_MIG[Silent server migration
creates SYNC_IMPORT]
    EMPTY_SVR -->|No| UPLOAD
    SILENT_MIG --> UPLOAD

    %% Has remote ops path
    HAS_OPS -->|Yes| DECRYPT{Encrypted?}

    %% Decryption path (two distinct error dialogs)
    DECRYPT -->|Yes| DECRYPT_OK{Decryption succeeds?}
    DECRYPT -->|No| IS_FRESH
    DECRYPT_OK -->|Yes| IS_FRESH
    DECRYPT_OK -->|No password configured| NO_PWD_DLG[Enter Password dialog:
Save & Sync / Force Overwrite / Cancel]
    DECRYPT_OK -->|Wrong password| WRONG_PWD_DLG[Decrypt Error dialog:
Save & Sync / Use Local Data / Cancel]
    NO_PWD_DLG -->|Save & Sync| START
    NO_PWD_DLG -->|Force Overwrite| FORCE_UP[Force upload local state
SYNC_IMPORT]
    WRONG_PWD_DLG -->|Save & Sync| START
    WRONG_PWD_DLG -->|Use Local| FORCE_UP

    %% Fresh client check (under "has remote ops" branch)
    IS_FRESH{Fresh client?}
    IS_FRESH -->|No| IS_IMPORT
    IS_FRESH -->|Yes + has local data| CONFLICT_DLG[SyncConflictDialog:
USE_LOCAL / USE_REMOTE / CANCEL]
    IS_FRESH -->|Yes + no local data| CONFIRM[Confirm dialog:
Download remote?]
    CONFIRM -->|OK| APPLY
    CONFIRM -->|Cancel| CANCELLED([Sync Cancelled])
    CONFLICT_DLG -->|Use Local| FORCE_UP
    CONFLICT_DLG -->|Use Remote| FORCE_DL[Force download
from seq 0]
    CONFLICT_DLG -->|Cancel| CANCELLED

    %% SYNC_IMPORT handling
    IS_IMPORT{Contains SYNC_IMPORT?}
    IS_IMPORT -->|No| CONFLICT_CHK
    IS_IMPORT -->|Yes| IMPORT_CONFLICT{Meaningful
pending ops?}
    IMPORT_CONFLICT -->|Yes| IMPORT_DLG[ImportConflictDialog:
import reason shown,
Use Server Data recommended]
    IMPORT_CONFLICT -->|No| APPLY_IMPORT[Apply full state replacement
silently — already-synced
store data is not a conflict
PASSWORD_CHANGED falls through here]
    IMPORT_DLG -->|Use Server| FORCE_DL
    IMPORT_DLG -->|Use Local| FORCE_UP
    IMPORT_DLG -->|Cancel| CANCELLED

    %% Conflict detection
    CONFLICT_CHK{Vector clock conflict?} -->|CONCURRENT| LWW[Auto-resolve LWW
later timestamp wins
ties → remote wins
archive ops always win]
    CONFLICT_CHK -->|No conflict| APPLY

    LWW --> APPLY[Apply ops to NgRx store]
    APPLY_IMPORT --> UPLOAD

    %% Upload phase
    APPLY --> UPLOAD[Upload pending local ops]
    UPLOAD --> REJECTED{Server rejects any?}

    REJECTED -->|No| PIGGYBACK[Process piggybacked ops]
    REJECTED -->|CONFLICT_CONCURRENT| REDOWNLOAD[Re-download & resolve
max 3 retries per entity]
    REJECTED -->|VALIDATION_ERROR| PERM_REJECT[Op permanently rejected]
    REJECTED -->|Payload too large| ALERT[Alert dialog, sync stops]

    REDOWNLOAD --> CONFLICT_CHK
    PIGGYBACK --> ENCRYPT_CHK

    %% Post-sync encryption check
    ENCRYPT_CHK{SuperSync without
encryption?}
    ENCRYPT_CHK -->|Yes| ENC_PROMPT[Encryption prompt:
Set password or disable sync]
    ENCRYPT_CHK -->|No| IN_SYNC([IN_SYNC ✓])
    ENC_PROMPT -->|Password set| ENABLE_ENC[Enable encryption:
delete server → upload encrypted]
    ENC_PROMPT -->|Disable SuperSync| DISABLE([Sync Disabled])
    ENABLE_ENC --> IN_SYNC

    FORCE_UP --> IN_SYNC
    FORCE_DL --> IN_SYNC
    PERM_REJECT --> ERROR([ERROR])
    ALERT --> ERROR

    %% Styling
    classDef success fill:#2d6,stroke:#1a4,color:#fff
    classDef error fill:#d33,stroke:#a11,color:#fff
    classDef cancel fill:#888,stroke:#555,color:#fff
    classDef dialog fill:#48f,stroke:#26d,color:#fff
    classDef action fill:#e90,stroke:#b60,color:#fff,stroke-width:3px

    class IN_SYNC success
    class ERROR error
    class CANCELLED,DISABLE cancel
    class CONFIRM,CONFLICT_DLG,IMPORT_DLG,NO_PWD_DLG,WRONG_PWD_DLG,ENC_PROMPT dialog
    class APPLY,APPLY_IMPORT,FORCE_UP,FORCE_DL,ENABLE_ENC,UPLOAD,SILENT_MIG action

Legend:

  • 🟢 Green = success states
  • 🔴 Red = error states
  • 🔵 Blue = user-facing dialogs
  • 🟠 Orange = key actions (state changes, uploads, downloads)
  • ⚫ Gray = cancelled/disabled

Notes:

  • The Enter Password and Decrypt Error dialogs correspond to DecryptNoPasswordError and DecryptError respectively — they are distinct components with different options.
  • IMPORT_CONFLICT gate uses pending ops only, not store contents (_hasMeaningfulPendingOps()). PASSWORD_CHANGED SYNC_IMPORTs without pending ops fall through this gate naturally to silent acceptance — the data is identical, only the encryption changed. "Meaningful" = TASK/PROJECT/TAG/NOTE create/update/delete or full-state ops — config-only ops don't count. Already-synced store data is not a conflict with the incoming SYNC_IMPORT — the user-facing warning happens on the originating device. Including store contents in the gate would let an old client pick USE_LOCAL and force-upload its stale pre-import state, rolling back the remote import for everyone.
  • LWW tie-breaking: on equal timestamps, remote wins (server-authoritative). moveToArchive operations always win regardless of timestamp.
  • Re-download retry limit: max 3 resolution attempts per entity (MAX_CONCURRENT_RESOLUTION_ATTEMPTS); if exceeded, ops are permanently rejected.