* feat: cloud prefs sync polish + encryption key rotation (#2906)
1. Add sync status indicator in Preferences panel — shows current state
(synced/pending/error/offline) with colored dot, last-sync timestamp,
and manual "Sync now" button for conflict/error recovery.
2. Support AES key rotation in scripts/lib/crypto.cjs — encrypt() always
uses the latest ENCRYPTION_KEY_V{n} env var, decrypt() reads the version
prefix to select the matching key. Fully backwards-compatible with the
existing NOTIFICATION_ENCRYPTION_KEY single-key setup.
3. Add tests exercising the applyMigrations() schema-version plumbing and
the new multi-version key rotation (12 tests, all passing).
Closes#2906
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address Greptile review feedback
- Raise key version ceiling from 9 to 99 to avoid silent footgun
- Extract SYNC_STATE_LABELS/COLORS to shared constants (no drift risk)
- Gate Cloud Sync UI on isCloudSyncEnabled() so it doesn't render
when VITE_CLOUD_PREFS_ENABLED is off
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>