super-productivity/docs/plans
Johannes Millan c41c18f247
fix(sync): guard fresh-client first sync against data-loss trap & decrypt-race (#7980)
* fix(sync): guard fresh-client first sync against data-loss trap & decrypt-race

A genuinely-fresh client seeds example tasks before its first sync, so on first
connect to a populated, encrypted SuperSync dataset it hit a SYNC_IMPORT conflict
dialog whose USE_LOCAL would overwrite the real remote, plus a red
DecryptNoPasswordError.

- A1: SyncImportConflictGateService flags isNeverSynced (\!hasSyncedOps) on the
  incoming-import dialog; the dialog guards the destructive USE_LOCAL with a
  confirm and focuses the scenario-appropriate safe button via cdkFocusInitial.
- B: OperationLogDownloadService logs 'encrypted ops, no key' quietly only for a
  genuinely-fresh client (never synced AND no local encryption flag); it stays
  loud (dropped-credential signature) for already-synced clients and for a wiped
  op store whose config still flags encryption on, via the new optional provider
  method SuperSync.isEncryptionEnabled().
- D: SuperSync.isReady() returns false while isEncryptionEnabled && \!encryptKey,
  keeping a self-inconsistent encrypted config out of auto-sync; shares the
  _isEncryptionHalfConfigured predicate with getEncryptKey().

Tests: super-sync isReady/isEncryptionEnabled, conflict-gate isNeverSynced,
dialog confirm-guard + focus-by-scenario, download severity branches, and a
sync-service end-to-end first-sync-trap guard.

Plan/rationale: docs/plans/2026-06-03-fresh-client-first-sync-data-loss-trap.md

* docs(sync): add fresh-client first-sync data-loss-trap plan

Documents the shared root cause (example tasks seeded pre-first-sync), the A1/B/D
fixes, and the accepted Fix C residual (example-task pollution onto non-encrypted
accounts) with the rationale for deferring an identity-based cleanup.

* fix(sync): capture never-synced guard pre-download for piggyback SYNC_IMPORT

The SYNC_IMPORT conflict gate's never-synced guard (which blocks a
fresh client from force-overwriting a populated remote via USE_LOCAL)
was computed from a live hasSyncedOps() read on the piggyback-upload
path. By the time that gate runs, the same sync has already persisted
downloaded ops with syncedAt and marked accepted uploads synced, so the
flag flips to "has synced" mid-cycle and the guard disarms itself —
re-opening the data-loss trap on the piggyback path.

Capture the never-synced snapshot once at sync-cycle start, before
download, and thread it through downloadRemoteOps()/uploadPendingOps()
into the gate. The gate falls back to a live read only for standalone
callers (download gate is safe live: nothing is persisted until
processRemoteOps).

Also stop the sync-wrapper catch from re-logging the expected
DecryptNoPasswordError at error level — the download/upload service
already logs it at the right severity, so re-logging undid the
fresh-client onboarding-prompt quieting.

Tests: gate honors a caller-provided isNeverSynced without consulting
live history; piggyback path flags isNeverSynced=true even when the
upload marks ops synced; wrapper threads the pre-download snapshot.
Refresh the plan doc's stale test counts and document the fix (§11).
2026-06-03 16:01:16 +02:00
..
2026-03-22-advanced-onboarding-design.md feat(onboarding): add lightweight onboarding hints 2026-03-22 19:50:24 +01:00
2026-04-21-schedule-week-range-header.md feat(schedule): add week number and date range to nav header 2026-04-21 21:50:52 +02:00
2026-04-29-focus-mode-time-tracking-sync.md New focus mode rework (#7411) 2026-05-06 21:11:02 +02:00
2026-05-12-pr5-dropbox-slice.md refactor(sync-providers): move provider error classes 2026-05-12 20:32:08 +02:00
2026-05-12-pr5-webdav-slice.md docs(sync-core-plan): fold Gemini review dissent into consensus 2026-05-12 22:22:41 +02:00
2026-05-12-pr7-super-sync-slice.md refactor(sync-providers): move SuperSync provider into package 2026-05-13 00:30:19 +02:00
2026-05-13-extract-encryption-to-sync-core.md refactor(sync): remove encryption test mocks 2026-05-13 17:26:55 +02:00
2026-05-14-super-sync-server-perf.md perf(sync): SuperSync server speed + correctness hardening (#7621) 2026-05-15 17:24:16 +02:00
2026-05-15-generic-concurrently-migration-recovery-design.md docs(supersync): design for generic CONCURRENTLY migration recovery 2026-05-15 21:29:34 +02:00
2026-05-15-super-sync-server-decomposition.md refactor(sync): decompose SuperSync server giants into cohesive modules 2026-05-15 20:06:12 +02:00
2026-05-15-sync-docs-and-contributor-model-design.md docs(sync): consolidate sync docs + enforce the contributor model 2026-05-15 16:51:50 +02:00
2026-05-21-clean-slate-data-loss-prevention.md refactor(sync): address multi-review findings on #7709 (#7712) 2026-05-22 15:25:42 +02:00
2026-05-21-document-mode-tiptap-plugin.md feat(document-mode): add TipTap-based document-mode plugin 2026-05-22 17:33:22 +02:00
2026-05-22-clientid-migrate-to-sup-ops.md Improve on sync (#7736) 2026-05-22 17:49:25 +02:00
2026-05-22-document-mode-sync-data-model.md test: strengthen unit test assertions and revive disabled plugin specs (#7755) 2026-05-23 18:31:53 +02:00
2026-05-22-supops-single-connection-alt.md docs(sync): add SUP_OPS versionchange handler plan (#7735) 2026-05-22 19:18:40 +02:00
2026-05-22-supops-single-connection.md docs(sync): add SUP_OPS versionchange handler plan (#7735) 2026-05-22 19:18:40 +02:00
2026-05-23-persisted-data-update-hook.md feat(plugins): adopt PERSISTED_DATA_CHANGED in document-mode (#7752) (#7812) 2026-05-27 11:46:50 +02:00
2026-05-23-stage-a-keyed-plugin-persistence.md feat(plugins): Stage A keyed persistence with LWW (#7749) (#7763) 2026-05-23 22:23:17 +02:00
2026-05-27-ios-background-time-tracking.md fix(ios): reconcile time tracking and focus mode on app resume (#7837) 2026-05-28 16:56:54 +02:00
2026-05-28-android-keyboard-resize-jank.md Android soft-keyboard: fix dark-theme white flash on resize (#7839) 2026-05-28 16:56:21 +02:00
2026-06-03-fresh-client-first-sync-data-loss-trap.md fix(sync): guard fresh-client first sync against data-loss trap & decrypt-race (#7980) 2026-06-03 16:01:16 +02:00