Commit graph

85 commits

Author SHA1 Message Date
Johannes Millan
cbeecfd39a fix(sync): multi-tab vector clock staleness + shared code extraction
Bug fix:
- Fix vector clock cache staleness in multi-tab scenarios by clearing
  cache when acquiring operation write lock. Each browser tab has its
  own in-memory cache, so Tab B's cache could be stale if Tab A wrote
  while Tab B was waiting for the lock.

Shared code extraction (client/server consistency):
- Extract vector clock comparison to @sp/shared-schema
  - Client wraps shared impl with null handling
  - Server imports directly from shared
- Extract entity types to @sp/shared-schema
  - Single source of truth for ENTITY_TYPES array
  - Removes duplicated "must match" comments

Files:
- packages/shared-schema/src/vector-clock.ts (new)
- packages/shared-schema/src/entity-types.ts (new)
- src/app/op-log/store/operation-log-store.service.ts (cache clear)
- src/app/op-log/capture/operation-log.effects.ts (call cache clear)
2026-01-03 18:05:11 +01:00
Johannes Millan
0f8d2cbc42 chore: code review improvements for operation-logs branch
Phase 1 - Code Quality:
- Add clarifying comment in lww-update.meta-reducer.ts explaining
  Date.now() choice for LWW conflict resolution
- Document multi-instance deployment limitations in README.md
  (passkey challenges, snapshot locks)

Phase 2 - Testing:
- Add TODAY_TAG selector edge case tests (deleted/archived tasks)

Phase 3 - Minor Polish:
- Add createValidationErrorResponse() helper to hide Zod validation
  details in production (sync.routes.ts)
- Add database index @@index([userId, opType]) for faster restore
  point queries
2026-01-03 13:17:42 +01:00
Johannes Millan
3a179df3cc fix(sync): prevent data loss when multiple clients join SuperSync
Fixes critical bug where multiple clients with existing data could each
create a SYNC_IMPORT when enabling SuperSync, causing data loss.

Root cause: When clients had local data (from legacy migration), each
would detect "server needs migration" and create competing SYNC_IMPORTs.
The last one uploaded would invalidate all prior data.

Fixes:
- Server rejects duplicate SYNC_IMPORT with SYNC_IMPORT_EXISTS (409)
- Client double-checks server is empty before creating SYNC_IMPORT
- Client merges all local op clocks into SYNC_IMPORT's vector clock
- hasSyncedOps() excludes MIGRATION/RECOVERY ops from check
- Upload service gracefully handles SYNC_IMPORT_EXISTS rejection

Tests added:
- ServerMigrationService: double-check and clock merging (5 tests)
- OperationLogStoreService: hasSyncedOps() MIGRATION exclusion (7 tests)
- OperationLogUploadService: SYNC_IMPORT_EXISTS handling (5 tests)
- E2E: Multiple clients with existing data merge correctly
2026-01-02 17:51:12 +01:00
Johannes Millan
187cbdafe3 fix(sync-server): reject empty/whitespace-only entityId in validation
The server was accepting empty string entityId values because the validation
only checked type and length, not content. This could allow corrupt operations
to be synced to other clients.

Changes:
- Add check for empty/whitespace-only entityId (returns INVALID_ENTITY_ID)
- Add 3 unit tests for empty, whitespace-only, and tab/newline entityId
- Update existing empty string test to expect INVALID_ENTITY_ID (format error)
  instead of MISSING_ENTITY_ID (null/undefined check)
2026-01-02 13:58:07 +01:00
Johannes Millan
c087ffee8a fix(sync): prevent infinite rejection loop from corrupt operations
Root cause: Operations with `entityId: undefined` (corrupt ops) caused
an infinite rejection loop during sync:
1. Upload ops → Rejected with CONFLICT_CONCURRENT
2. Rejection handler creates merged ops (still with invalid entityId)
3. Upload merged ops → Rejected again
4. Loop continues forever

This prevented the sync status double checkmark from ever appearing
because hasPendingOps never became false.

Three-layer defense implemented:

1. Client: Guard time tracking dispatch (task.service.ts)
   - Changed `if (currentTask &&` to `if (currentTask?.id &&`
   - Prevents time tracking dispatch with undefined taskId

2. Client: Strengthen entityId validation (operation-log.effects.ts)
   - Check that entityId is truthy AND a string type
   - Log error with actual entityId value for debugging

3. Client: Cleanup corrupt ops on startup (operation-log-recovery.service.ts)
   - New cleanupCorruptOps() method marks corrupt ops as rejected
   - Runs during hydration to break existing infinite loops
   - Excludes bulk 'ALL' operations which legitimately lack entityId

4. Server: Validate entityId for all regular operations (validation.service.ts)
   - Reject ops with missing entityId (except full-state and bulk ops)
   - Full-state: SYNC_IMPORT, BACKUP_IMPORT, REPAIR
   - Bulk entity types: ALL, RECOVERY
   - Returns MISSING_ENTITY_ID error code (permanent rejection)
2025-12-31 17:29:25 +01:00
Johannes Millan
a86df2c356 fix(sync): prevent duplicate operation from aborting batch upload
When a duplicate operation was uploaded, PostgreSQL's unique constraint
violation (P2002) would abort the entire transaction, causing all
subsequent operations in the batch to fail with INTERNAL_ERROR.

Server-side fix:
- Add pre-check via findUnique before attempting insert
- Return DUPLICATE_OPERATION error code without aborting transaction
- Other ops in batch continue processing normally

Client-side fix:
- Handle DUPLICATE_OPERATION error code in rejected-ops-handler
- Mark duplicate as synced (not rejected) since server already has it
- Prevents infinite retry loop for legitimately uploaded ops

Adds tests for both server and client handling.
2025-12-31 16:39:55 +01:00
Johannes Millan
c9a25a988d perf(sync): skip download when all ops fit in piggyback response
Optimize sync to reduce round-trips when remote changes are small:

- Increase PIGGYBACK_LIMIT from 100 to 500 ops on server
- Skip download call when server confirms hasMorePiggyback === false
- CRITICAL: Only skip when hasMorePiggyback is explicitly false, not undefined
  (undefined means no upload happened, so we must still download)

This reduces sync from 2 round-trips to 1 when ≤500 ops are pending,
improving perceived sync performance.

Add 5 unit tests for the skip-download optimization logic.
2025-12-29 21:52:03 +01:00
Johannes Millan
61b8d82ad6 fix(sync): address security and reliability issues from code review
- Add payload complexity validation for full-state ops (SYNC_IMPORT,
  BACKUP_IMPORT, REPAIR) with higher thresholds (50 depth, 100k keys)
  to prevent DoS attacks via deeply nested payloads
- Make persistence failure notification sticky (duration: 0) so users
  don't miss critical errors that require page reload
- Fix misleading comment in gap detection (effectiveSinceSeq, not sinceSeq)
- Add comprehensive unit tests for StaleOperationResolverService (16 tests)
  covering vector clock merging, timestamp preservation, and edge cases
2025-12-29 12:03:49 +01:00
Johannes Millan
1da70487f9 fix: address code review findings from Dec 27-29 changes
ESLint rule improvements:
- Fix require-hydration-guard to detect guards on combineLatest/forkJoin/zip
  results (eliminates 20+ false positive warnings)

Server improvements:
- Add OperationDownloadService unit tests (21 tests covering vector clock
  aggregation, gap detection, excludeClient parameter, snapshot optimization)
- Split ZIP bomb size limits: 10MB for /ops, 30MB for /snapshot
- Document storage quota update as intentionally non-atomic

E2E test improvements:
- Add waitForUISettle() helper using Angular stability instead of fixed timeouts
- Update supersync-cross-entity tests to use dynamic waits

Unit test improvements:
- Add HydrationStateService edge case tests (concurrent calls, cooldown cycles,
  timer cleanup, interleaved operations)
2025-12-29 10:37:12 +01:00
Johannes Millan
f780099ef8 fix(sync): address code review findings from 2025-12-28 changes
- Add unit tests for extracted hydrator services (60 new tests):
  - operation-log-recovery.service.spec.ts (17 tests)
  - operation-log-snapshot.service.spec.ts (20 tests)
  - sync-hydration.service.spec.ts (23 tests)

- Add compressed size pre-check in sync.routes.ts:
  - New MAX_COMPRESSED_SIZE (10MB) to prevent memory exhaustion
  - Two-stage protection: pre-check compressed, post-check decompressed
  - Applied to both /ops and /snapshot upload endpoints

- Remove tombstone references from sync-server-architecture-diagrams.md:
  - Database schema, ER diagram, cleanup tasks

- Replace console.warn with Logger.warn in sync.types.ts
2025-12-28 16:56:56 +01:00
Johannes Millan
ed061f90fb fix(sync): clamp future timestamps instead of rejecting
Operations with timestamps beyond maxClockDriftMs (60s) were being
permanently rejected, causing silent data loss when a client's clock
was slightly ahead.

Now these timestamps are clamped to now + maxClockDriftMs and accepted.
A TIMESTAMP_CLAMPED audit log is recorded for debugging.

- Add clamping logic in SyncService.processOperation()
- Remove future timestamp rejection from ValidationService
- Add comprehensive tests verifying clamped values and boundaries
- Update integration test to expect acceptance
2025-12-28 12:23:36 +01:00
Johannes Millan
17b94f0644 docs: add cross-reference comments for vector clock comparison
Add SYNC NOTE comments to both client and server compareVectorClocks
implementations pointing to each other. This prevents accidental drift
between the two implementations which must use identical algorithms.
2025-12-28 12:17:30 +01:00
Johannes Millan
a52b7716aa refactor(sync): remove tombstone system
Tombstones were used for tracking deleted entities but are no longer
needed with the operation log architecture. This removes:

- Tombstone table from Prisma schema
- Tombstone-related methods from SyncService
- Tombstone mocks and tests from all test files
- Database migration to drop tombstones table

The operation log now handles deletions through DEL operations,
making the separate tombstone tracking redundant.
2025-12-28 12:08:28 +01:00
Johannes Millan
805327731b refactor(sync): unify retention config to single value
- Replace opRetentionMs, maxOpAgeMs, and STALE_DEVICE_THRESHOLD_MS
  with single retentionMs (45 days)
- Consolidate 5 cleanup jobs into 1 daily job
- Remove tombstone-related code and tests (tombstones were removed
  in a previous migration)
- Simplifies configuration and removes contradictory values
  (maxOpAgeMs was 30 days while opRetentionMs was 45 days)
2025-12-28 12:05:44 +01:00
Johannes Millan
9e8d05f430 test(sync): add error handling tests for SuperSync
Add comprehensive tests for error code handling at unit and E2E levels:

Unit tests (super-sync.spec.ts):
- 429 Rate Limited response handling
- 413 Storage Quota Exceeded response handling
- Parse CONFLICT_CONCURRENT/STALE/DUPLICATE_OPERATION from response
- Mixed accept/reject batch categorization
- Extract piggybacked ops from conflict response

Unit tests (operation-log-sync.service.spec.ts):
- STORAGE_QUOTA_EXCEEDED shows alert but doesn't mark op rejected
- INTERNAL_ERROR marks op as rejected
- Verify subsequent operations behavior after errors

E2E tests (supersync-error-handling.spec.ts):
- Concurrent modification triggers LWW conflict resolution
- Sync recovers after initial connection
- Duplicate sync attempts handled gracefully
- Three clients converge to same state

Also adds SnapshotService.clearForUser() helper for test cleanup.
2025-12-28 11:49:21 +01:00
Johannes Millan
77a4ca34cb refactor(sync): remove unused parentOpId field
Remove the parentOpId field from the operation log system. It was
designed for conflict resolution chain tracking but was never
implemented - the field was always undefined/null.

Client changes:
- Remove from Operation interface
- Remove from compact codec encoding/decoding
- Update tests

Server changes:
- Remove from Operation type and Zod validation
- Remove from persistence and download mapping
- Add database migration to drop the column
2025-12-28 11:48:38 +01:00
Johannes Millan
fc05a00514 refactor(sync): remove redundant 5-minute snapshot TTL cache
The snapshot route had a time-based TTL (5 minutes) that could return
stale snapshots even when fresher data was available. This was redundant
because generateSnapshot() already handles caching via sequence-based
freshness - it returns the cached snapshot if up-to-date and only
replays ops when new ones exist.

Remove snapshotCacheTtlMs from SyncConfig and simplify the GET /snapshot
route to just call generateSnapshot() directly.
2025-12-28 10:48:00 +01:00
Johannes Millan
578faafb0b fix(sync): handle base64-encoded gzip from Android clients
Android WebView's fetch() corrupts binary Uint8Array bodies, causing
"incorrect header check" errors when decompressing gzip data on the
server. This fix uses CapacitorHttp with base64-encoded gzip on Android.

Client changes:
- Add isAndroidWebView getter for testability
- Add _fetchApiCompressedAndroid() method using CapacitorHttp
- Modify uploadOps() and uploadSnapshot() to use Android path
- Send Content-Transfer-Encoding: base64 header to signal encoding

Server changes:
- Add decompressBody() helper to handle base64+gzip
- Check Content-Transfer-Encoding header in /ops and /snapshot routes
- Decode base64 before gunzip when header is present

Net bandwidth savings ~50% (gzip saves 70%, base64 adds 33% overhead).

Includes comprehensive tests for both client and server paths.
2025-12-28 10:37:45 +01:00
Johannes Millan
18f03c1ec7 refactor(sync): extract SnapshotService from SyncService
Extract snapshot-related functionality into dedicated SnapshotService:
- getCachedSnapshot, cacheSnapshot, generateSnapshot
- getRestorePoints, generateSnapshotAtSeq, replayOpsToState

Key changes:
- Preserve FIX 1.7 concurrent snapshot lock behavior
- Fix SYNC_IMPORT/BACKUP_IMPORT/REPAIR handling to run before
  entity type check (these are full-state operations)
- Add comprehensive unit tests for SnapshotService

SyncService reduced from ~1988 to 918 lines (54% reduction).
2025-12-27 20:59:31 +01:00
Johannes Millan
4f3f778da5 refactor(sync): extract StorageQuotaService from SyncService
Extract storage quota calculation and checking methods:
- calculateStorageUsage(): Calculate total storage from ops + snapshot
- checkStorageQuota(): Check if upload is within quota
- updateStorageUsage(): Update cached storage counter
- getStorageInfo(): Get storage stats for status endpoint

Complex cleanup operations (freeStorageForUpload, deleteOldestRestorePointAndOps)
remain in SyncService as they orchestrate multiple operations.

All 246 tests passing.
2025-12-27 20:48:08 +01:00
Johannes Millan
07589dd67f refactor(sync): extract DeviceService and OperationDownloadService
Continue SyncService extraction with two more services:

- DeviceService: device ownership and online status queries
  (isDeviceOwner, getAllUserIds, getOnlineDeviceCount)
- OperationDownloadService: operation retrieval with gap detection
  and snapshot optimization (getOpsSince, getOpsSinceWithSeq, getLatestSeq)

SyncService now delegates to 6 extracted services:
- ValidationService
- RateLimitService
- RequestDeduplicationService
- DeviceService
- OperationDownloadService

All 236 tests passing.
2025-12-27 20:43:25 +01:00
Johannes Millan
2a9e3154bc refactor(sync): extract services from SyncService and migrate tests to Prisma
Extract three services from the monolithic SyncService for better
separation of concerns and testability:

- ValidationService: validates operations (validateOp, validatePayloadComplexity)
- RateLimitService: handles rate limiting with in-memory counters
- RequestDeduplicationService: handles request deduplication cache

Also migrate legacy tests from synchronous SQLite patterns to async
Prisma mocks:
- Create shared test state module (sync.service.test-state.ts)
- Migrate conflict-detection.spec.ts, gap-detection.spec.ts,
  sync-operations.spec.ts to Prisma mocks
- Add comprehensive unit tests for extracted services

Test count: 227 passing (up from 164 before service extraction)
2025-12-27 20:38:26 +01:00
Johannes Millan
9ee15b9f08 fix(sync): iteratively cleanup storage until quota satisfied
When storage quota is exceeded, the server now iteratively deletes
old restore points and operations until enough space is freed,
keeping at least one restore point as minimum valid sync state.

Changes:
- Add freeStorageForUpload() method that loops until quota satisfied
- Update deleteOldestRestorePointAndOps() to clear stale snapshot cache
- Update /ops and /snapshot routes to use iterative cleanup
- Add comprehensive tests for new cleanup behavior

The error response now includes cleanupStats showing what was freed
even when cleanup ultimately fails due to minimum data requirements.
2025-12-25 10:18:35 +01:00
Johannes Millan
8f277adc60 fix(sync): prevent concurrent uploads causing transaction rollback
Root cause: On Android, LockService was skipping locks entirely, allowing
ImmediateUploadService and main sync to upload the same operations
concurrently. This caused PostgreSQL serialization failures under
REPEATABLE_READ isolation.

Fixes:
1. LockService: Use fallback mutex on Electron/Android instead of no
   locking. These are single-instance but still need in-process locking
   for concurrent code paths.

2. Server: Add detection for serialization failures (Prisma P2034,
   PostgreSQL 40001/40P01). These are logged as warnings with clearer
   error messages for retry.

Combined with the previous commit that treats INTERNAL_ERROR as
transient (not permanent rejection), these errors will now be
automatically retried on next sync.
2025-12-24 16:43:03 +01:00
Johannes Millan
0b2a394f2e feat(sync): add storage quota auto-cleanup and user alerts
When server storage quota is exceeded:

1. Server-side auto-cleanup:
   - Recalculate actual storage before failing (fixes stale cache issue)
   - Delete oldest restore point and all ops before it to free space
   - If only one restore point, keep it but delete ops before it
   - Retry quota check after cleanup before returning error

2. Client-side strong warnings:
   - Show window.alert when STORAGE_QUOTA_EXCEEDED error received
   - Alert on both ops upload and snapshot upload failures
   - Makes sync failure immediately visible to users

3. Improved error messaging in translations

Includes comprehensive unit tests for all cleanup scenarios.
2025-12-24 16:17:48 +01:00
Johannes Millan
72608a4aaa feat(migration): implement pre-migration dialog and backup functionality 2025-12-24 13:49:37 +01:00
Johannes Millan
1bd96a9182 fix(sync): refresh storage cache after cleanup and add serverTime to response
Address two issues identified in code review:

1. Storage cache not refreshed after cleanup:
   - Modified deleteOldSyncedOpsForAllUsers() to return { totalDeleted, affectedUserIds }
   - Cleanup job now calls updateStorageUsage() for each affected user
   - Prevents stale quota checks after nightly cleanup

2. serverTime missing from download response:
   - Added serverTime: Date.now() to DownloadOpsResponse
   - Enables client-side clock drift detection

Tests:
- Added serverTime response test in sync-fixes.spec.ts
- Added deleteOldSyncedOpsForAllUsers return structure tests
- Updated legacy SQLite tests for new return type (excluded from CI)
2025-12-23 18:31:57 +01:00
Johannes Millan
a405b6ee6d fix: address code review issues from sync and UX changes
Critical fixes:
- fix(sync): handle empty newOps with hasMorePiggyback flag
- fix(sync): handle skippedOps (stale operations) in sync service
- fix(tasks): replace requestAnimationFrame with RxJS delay(0)

High priority fixes:
- fix(sync): add rollback recovery for password change failures
- fix(e2e): improve password change verification robustness
- fix(sync): add try-catch to WebDAV test connection
- fix(sync): improve encrypted restore error message with guidance
- fix(android): add startWith() to fix pairwise() initial state

Medium priority fixes:
- fix(sync): add diagnostics to flush timeout error
- docs: document focus delay constants and rationale
- docs: add TODO for Android reminder native action handlers
2025-12-22 21:09:18 +01:00
Johannes Millan
064c2452ca fix(sync): handle piggyback limit in high-volume sync
When syncing 100+ operations, the server's piggyback limit (100 ops)
was causing Client B to miss operations. The server returned 100 piggybacked
ops but latestSeq was set to the actual server sequence (e.g., 199).
Client B then updated lastServerSeq to 199, so subsequent download got 0 ops.

Changes:
- Server: Add hasMorePiggyback flag to UploadOpsResponse when piggyback limit
  is reached and more ops exist
- Client: When hasMorePiggyback is true, store lastServerSeq as the max
  piggybacked op's serverSeq instead of latestSeq, ensuring subsequent
  download fetches remaining ops
- Effects: Change switchMap to mergeMap in autoAddTodayTagOnMarkAsDone to
  ensure ALL mark-as-done actions trigger planTasksForToday
- Flush service: Implement two-phase wait strategy (poll queue + acquire lock)
  to ensure all pending writes complete before upload
- Add diagnostic logging for operation counts at key stages

Test: High volume sync with 50 tasks + 49 mark-as-done (197 ops total)
now correctly syncs all done states via piggyback (100) + download (97).
2025-12-22 20:31:35 +01:00
Johannes Millan
c4cc32da29 feat(sync): add encryption password change feature for SuperSync
Implements the ability to change the encryption password by deleting
all server data and uploading a fresh snapshot with the new password.

Server changes:
- Add DELETE /api/sync/data endpoint to delete all user sync data
- Add deleteAllUserData() method to SyncService

Client changes:
- Add deleteAllData() to OperationSyncCapable interface
- Implement deleteAllData() in SuperSync provider
- Add EncryptionPasswordChangeService to orchestrate password change
- Add DialogChangeEncryptionPasswordComponent with validation
- Add "Change Encryption Password" button to sync settings (visible
  when encryption is enabled)
- Add translations for all new UI strings

Testing:
- Add 10 unit tests for EncryptionPasswordChangeService
- Add 14 unit tests for DialogChangeEncryptionPasswordComponent
- Add 5 E2E tests for complete password change flow
- Add changeEncryptionPassword() helper to SuperSyncPage

Also fixes:
- Add missing deleteAllData() to MockOperationSyncProvider
- Fix typo S_FINISH_DAY_SYNC_ERROR -> FINISH_DAY_SYNC_ERROR
2025-12-22 13:31:19 +01:00
Johannes Millan
291c424210 fix(sync): address 5 critical sync system issues
1. CRITICAL: Encrypted ops blocking snapshot/restore
   - Server now rejects restore requests when encrypted ops exist
   - Added ENCRYPTED_OPS_NOT_SUPPORTED error code
   - replayOpsToState defensively skips encrypted ops

2. HIGH: Dedup before quota check
   - Moved request deduplication BEFORE quota check
   - Ensures retries after successful uploads don't fail with 413

3. MEDIUM: Chunked uploads reusing lastKnownServerSeq
   - Now updates lastKnownServerSeq between chunks
   - Prevents duplicate piggybacked ops on subsequent chunks

4. MEDIUM: Vector-clock EQUAL from different client
   - EQUAL clocks from different clients now treated as conflict
   - Added explicit handling for all comparison cases
   - Removed unsafe catch-all that returned hasConflict:false

5. LOW: SuperSync status localStorage scoping
   - Added setScope/clearScope methods to SuperSyncStatusService
   - Storage key now includes scope ID (hash of server+token)
   - Prevents stale status when switching accounts/servers

Tests: Added unit tests for vector clock comparison and updated
all relevant test files with comprehensive coverage.
2025-12-22 10:32:57 +01:00
Johannes Millan
5f9d73c37c fix(sync): address 5 critical sync system issues
Fixes identified issues in the operation log sync system:

1. [High] Upload deduplication now returns fresh piggybacked ops on retry
   - Deduplicated responses now use the retry request's lastKnownServerSeq
   - Ensures clients don't miss operations from other clients

2. [High] Server conflict detection now checks all entityIds in batch ops
   - Multi-entity operations (entityIds array) now properly checked
   - Each entity ID is verified for concurrent modifications

3. [High] Encrypted snapshot uploads now properly flagged
   - Added isPayloadEncrypted parameter to uploadSnapshot interface
   - Server stores and returns the flag with operations
   - Enables proper decryption on download

4. [Medium] clientId validation added to server
   - Rejects operations where op.clientId doesn't match request clientId
   - Returns INVALID_CLIENT_ID error code

5. [Low] Error classification now uses structured errorCode
   - Client checks errorCode instead of string matching error messages
   - More reliable conflict detection

Also adds test infrastructure for Prisma-based server tests:
- New vitest.config.ts with proper setup
- Test setup file with Prisma mocks
- sync-fixes.spec.ts with tests for issues 1 and 3

Note: Legacy SQLite-based tests are excluded pending migration to
async Prisma patterns. Client tests all pass (133/133).
2025-12-21 21:56:01 +01:00
Johannes Millan
8a449fb944 fix(sync-server): compile scripts for production Docker image
ts-node is a devDependency and not available in production containers.
Updated tsconfig to compile scripts/ alongside src/, and changed npm
scripts to use compiled JS. Added monitor:dev for local development.
2025-12-19 15:58:21 +01:00
Johannes Millan
b7012e0248 feat(sync): add per-user storage quota enforcement (100MB)
Add storage quota system to prevent single users from consuming
excessive disk space on the sync server.

Changes:
- Add storageQuotaBytes and storageUsedBytes fields to User model
- Add STORAGE_QUOTA_EXCEEDED error code for quota violations
- Add quota calculation and enforcement methods to SyncService
- Block uploads (POST /ops, POST /snapshot) when quota exceeded
- Allow downloads to continue when over quota (so users can delete data)
- Return storage usage/quota in GET /status response

Default quota: 100MB per user
2025-12-18 17:17:52 +01:00
Johannes Millan
0bacefa58a perf(sync-server): skip pre-import operations when downloading
When a fresh client downloads operations (sinceSeq=0), the server now
finds the latest full-state operation (SYNC_IMPORT, BACKUP_IMPORT, REPAIR)
and starts from there instead of from the beginning.

This optimization:
- Reduces bandwidth by not sending superseded operations
- Reduces server and client processing time
- Works with the client-side filtering fix (pre-import ops are filtered anyway)

The response now includes latestSnapshotSeq to indicate where the
effective state starts, which clients can use for optimization decisions.
2025-12-17 11:31:04 +01:00
Johannes Millan
9a18a8194c fix(sync): respect action opType for archive operations and improve sync robustness
Fix moveToArchive operations being rejected by server:
- deriveOpType() now uses action's declared opType instead of deriving from entity changes
- moveToArchive declares opType: Update but was being changed to Delete based on entity changes
- Server rejected because DEL operations require singular entityId, but bulk ops use entityIds

Additional sync improvements:
- Use OpLog.warn instead of devError for old snapshot format (expected transitional state)
- Add lock-based synchronization for conflict detection (replaces flushPendingWrites)
- Handle TODAY_TAG correctly when checking if client state is empty
- Add partial apply failure handling with validation and user notification
- Server: Add race condition prevention with REPEATABLE_READ isolation and re-check after seq allocation
- Server: Add concurrent snapshot generation lock to prevent duplicate work

Update tests to use LockService instead of OperationWriteFlushService
2025-12-15 17:10:25 +01:00
Johannes Millan
082aca26e8 fix(sync-server): reduce operation and tombstone retention to 45 days
Reduces storage per user by ~50% at steady state. Devices offline
longer than 45 days will need a full resync via gap detection.
2025-12-15 13:28:58 +01:00
Johannes Millan
ef92a12667 feat(sync): add restore from history for Super Sync
- Add server endpoints for restore points (GET /api/sync/restore-points, GET /api/sync/restore/:serverSeq)
- Add RestorePoint types and RestoreCapable interface
- Add getRestorePoints() and generateSnapshotAtSeq() to SyncService
- Add SuperSyncRestoreService for client-side restore orchestration
- Add DialogRestorePointComponent for restore point selection UI
- Add "Restore from History" button to sync settings (visible when Super Sync is active)
- Fix circular dependency in SyncSafetyBackupService using lazy injection
- Update docker-compose.yml to support local development (configurable NODE_ENV, PUBLIC_URL)
2025-12-15 13:18:15 +01:00
Johannes Millan
bf7db5515e fix(sync): handle large payload transaction timeouts
Problem: Importing backups (~8.5MB) failed with "Transaction rolled back
due to internal error". The Prisma transaction had no explicit timeout,
defaulting to 5 seconds which was insufficient for large JSON payloads.
The client then marked the operation as permanently rejected instead of
retrying.

Server-side fix:
- Add 60s timeout to uploadOps transaction (matching generateSnapshot)
- Improve error message to indicate timeout vs other errors
- Include actual error details for debugging

Client-side fix:
- Extend transient error detection to include server errors:
  - "transaction rolled back"
  - "internal server error"
  - "server busy" / "please retry"
  - HTTP 500/502/503/504 errors
- Transient errors are NOT marked as rejected, allowing retry

Tests:
- Add 6 tests for transient vs permanent error handling
- Verify network errors, timeouts, and server errors are retriable
- Verify validation errors remain permanently rejected
2025-12-12 20:48:40 +01:00
Johannes Millan
c03d00b0ff fix(sync): enable E2E encryption for SuperSync operations
- Add isPayloadEncrypted field to Zod OperationSchema (root cause fix)
- Add isPayloadEncrypted column to Prisma schema with migration
- Store and return isPayloadEncrypted in server sync service
- Remove debug logging from encryption services
- Remove potential key exposure from DecryptNoPasswordError
- Remove unnecessary console.log in fallback decryption
- Hide duplicate Advanced encryption fields when SuperSync selected
- Enable previously skipped encryption E2E test
- Add data/ folder to gitignore
2025-12-12 20:48:40 +01:00
Johannes Millan
dfc6df7f13 fix: harden supersync entity allowlist and verify-email route 2025-12-12 20:48:40 +01:00
Johannes Millan
49faa5da92 fix(supersync): Resolve e2e test failures
Fixes:
- : Corrected a TypeScript type casting error () when handling gzipped request bodies by casting  to  before .
- : Added  and  dependencies to the Alpine-based Docker image. This resolves Prisma engine startup failures and ensures database tables are correctly pushed during container initialization, which was causing 'table does not exist' errors in server logs.
- Added  to  to prevent generated Prisma client files from being tracked.

These changes collectively fix the
> superProductivity@16.5.5 e2e:supersync
> docker compose up -d --build supersync && echo 'Waiting for SuperSync server...' && until curl -s http://localhost:1900/health > /dev/null 2>&1; do sleep 1; done && echo 'Server ready!' && npm run e2e -- --grep @supersync; docker compose down supersync

#1 [internal] load local bake definitions
#1 reading from stdin 560B done
#1 DONE 0.0s

#2 [internal] load build definition from Dockerfile.test
#2 transferring dockerfile: 1.65kB done
#2 DONE 0.0s

#3 [internal] load metadata for docker.io/library/node:22-alpine
#3 DONE 0.9s

#4 [internal] load .dockerignore
#4 transferring context: 147B done
#4 DONE 0.0s

#5 [ 1/15] FROM docker.io/library/node:22-alpine@sha256:9632533eda8061fc1e9960cfb3f8762781c07a00ee7317f5dc0e13c05e15166f
#5 DONE 0.0s

#6 [internal] load build context
#6 transferring context: 104.47kB 0.0s done
#6 DONE 0.0s

#7 [ 6/15] COPY packages/super-sync-server/package.json ./packages/super-sync-server/
#7 CACHED

#8 [ 3/15] RUN apk add --no-cache openssl libc6-compat
#8 CACHED

#9 [ 4/15] COPY package.json package-lock.json ./
#9 CACHED

#10 [ 5/15] COPY packages/shared-schema/package.json ./packages/shared-schema/
#10 CACHED

#11 [ 7/15] RUN npm ci --workspace=packages/shared-schema --workspace=packages/super-sync-server --ignore-scripts
#11 CACHED

#12 [ 2/15] WORKDIR /app
#12 CACHED

#13 [ 8/15] COPY packages/shared-schema/ ./packages/shared-schema/
#13 CACHED

#14 [ 9/15] COPY packages/super-sync-server/ ./packages/super-sync-server/
#14 DONE 0.4s

#15 [10/15] WORKDIR /app/packages/shared-schema
#15 DONE 0.0s

#16 [11/15] RUN npm run build
#16 0.236
#16 0.236 > @sp/shared-schema@1.0.0 build
#16 0.236 > tsc
#16 0.236
#16 DONE 1.1s

#17 [12/15] WORKDIR /app/packages/super-sync-server
#17 DONE 0.0s

#18 [13/15] RUN npx prisma generate
#18 1.958 Environment variables loaded from .env
#18 1.959 Prisma schema loaded from prisma/schema.prisma
#18 2.232
#18 2.232 ✔ Generated Prisma Client (v5.22.0) to ./node_modules/@prisma/client in 72ms
#18 2.232
#18 2.232 Start by importing your Prisma Client (See: https://pris.ly/d/importing-client)
#18 2.232
#18 2.232 Tip: Want to turn off tips and other hints? https://pris.ly/tip-4-nohints
#18 2.232
#18 DONE 2.5s

#19 [14/15] RUN rm -rf dist && npm run build
#19 0.254
#19 0.254 > @super-productivity/super-sync-server@1.0.0 build
#19 0.254 > tsc
#19 0.254
#19 DONE 1.7s

#20 [15/15] RUN cp -r public dist/
#20 DONE 0.2s

#21 exporting to image
#21 exporting layers
#21 exporting layers 0.4s done
#21 writing image sha256:2c3f259261e139e1fea2689043c83b2f4dbcead0a5e2a59966eb4a922df925eb done
#21 naming to docker.io/library/sup-claude-supersync done
#21 DONE 0.5s

#22 resolving provenance for metadata file
#22 DONE 0.0s
Waiting for SuperSync server...
Server ready!

> superProductivity@16.5.5 pree2e
> npm run plugins:build

> superProductivity@16.5.5 plugins:build
> npm run vite-plugin:build && cd packages/plugin-dev && npm run build:all

> superProductivity@16.5.5 vite-plugin:build
> cd packages/vite-plugin && npm run build

> @super-productivity/vite-plugin@1.0.0 build
> tsc

> @super-productivity/plugin-dev@1.0.0 build:all
> node scripts/build-all.js


🚀 Building all plugins...

📦 Building procrastination-buster...
  Installing dependencies...

📦 Building api-test-plugin...
  Installing dependencies...

📦 Building sync-md...
  Installing dependencies...

📦 Building yesterday-tasks-plugin...
  Installing dependencies...

📦 Building ai-productivity-prompts...
  Installing dependencies...
  Building...
 api-test-plugin - Copied to assets (0.2s)
  Building...
 yesterday-tasks-plugin - Copied to assets (0.2s)
  Building...
  Building...
  Building...
 sync-md - Built and copied to assets (0.9s)
 ai-productivity-prompts - Built and copied to assets (1.3s)
 procrastination-buster - Built and copied to assets (1.3s)

📊 Build Summary:
  Total plugins: 5
  Successful: 5
  Total time: 1.3s

📁 Build outputs:
  • sync-md/dist/
  • ai-productivity-prompts/dist/

> superProductivity@16.5.5 e2e
> npx playwright test --config e2e/playwright.config.ts --reporter=line --grep @supersync

[WebServer] 
[WebServer] > superProductivity@16.5.5 startFrontend:e2e
[WebServer] > npm run env && ng serve --port=4242
[WebServer] 

[WebServer] 
[WebServer] > superProductivity@16.5.5 env
[WebServer] > node ./tools/load-env.js
[WebServer] 

[WebServer]  Found 3 environment variable(s)

[WebServer]  Generated /home/johannes/www/sup-claude/src/app/config/env.generated.ts

[WebServer] ❯ Building...

[WebServer] ✔ Building...

[WebServer] Initial chunk files | Names                         |  Raw size
[WebServer] chunk-B5GBBGFZ.js   | -                             |   1.59 MB | 
[WebServer] main.js             | main                          |   1.50 MB | 
[WebServer] chunk-73SFRLD7.js   | -                             | 405.10 kB | 
[WebServer] chunk-4GQJAWHV.js   | -                             | 368.88 kB | 
[WebServer] chunk-GVSM3YI6.js   | -                             | 315.27 kB | 
[WebServer] styles.css          | styles                        | 296.84 kB | 
[WebServer] chunk-SMTMT6UB.js   | -                             | 227.13 kB | 
[WebServer] chunk-ZQQLCIPY.js   | -                             | 166.64 kB | 
[WebServer] chunk-VO4RJIGM.js   | -                             | 136.58 kB | 
[WebServer] chunk-URLH5Z6W.js   | -                             | 122.59 kB | 
[WebServer] chunk-ONOVC6TO.js   | -                             |  68.76 kB | 
[WebServer] chunk-QRUNPE3P.js   | -                             |  63.69 kB | 
[WebServer] chunk-F7PG5TTX.js   | -                             |  55.01 kB | 
[WebServer] chunk-QGJQW3EB.js   | -                             |  42.06 kB | 
[WebServer] chunk-PYPQ3TRT.js   | -                             |  36.73 kB | 
[WebServer] chunk-HSTZRFNF.js   | -                             |  31.90 kB | 
[WebServer] chunk-D53RABDQ.js   | -                             |  27.86 kB | 
[WebServer] chunk-7D42UK3V.js   | -                             |  23.63 kB | 
[WebServer] chunk-4TYACKT3.js   | -                             |  17.55 kB | 
[WebServer] chunk-ZWN6FFZO.js   | -                             |  16.54 kB | 
[WebServer] chunk-2IQPEDY7.js   | -                             |  15.72 kB | 
[WebServer] chunk-4QJA3367.js   | -                             |  13.70 kB | 
[WebServer] chunk-W7AH2J3I.js   | -                             |  13.51 kB | 
[WebServer] chunk-MCWDR5V7.js   | -                             |  13.18 kB | 
[WebServer] chunk-X5IWF2EC.js   | -                             |  12.16 kB | 
[WebServer] chunk-TAD6ZV7S.js   | -                             |  11.76 kB | 
[WebServer] chunk-EX2CES47.js   | -                             |  11.27 kB | 
[WebServer] chunk-4SOE6J4V.js   | -                             |   8.12 kB | 
[WebServer] chunk-TWC2RC7A.js   | -                             |   8.11 kB | 
[WebServer] chunk-GIPKJ6OE.js   | -                             |   7.65 kB | 
[WebServer] chunk-G3JA22CS.js   | -                             |   7.62 kB | 
[WebServer] chunk-RTM45NAG.js   | -                             |   6.61 kB | 
[WebServer] chunk-IOASK5PE.js   | -                             |   5.91 kB | 
[WebServer] chunk-3GNZKYAW.js   | -                             |   5.89 kB | 
[WebServer] chunk-Z4K6LGBW.js   | -                             |   5.81 kB | 
[WebServer] chunk-BE7CIT5D.js   | -                             |   5.41 kB | 
[WebServer] chunk-T3NZZKCV.js   | -                             |   5.20 kB | 
[WebServer] chunk-6PDYIKD2.js   | -                             |   5.05 kB | 
[WebServer] chunk-UZESUJ4I.js   | -                             |   4.93 kB | 
[WebServer] chunk-DMW3XK7Y.js   | -                             |   4.21 kB | 
[WebServer] chunk-TMH5BTGC.js   | -                             |   4.06 kB | 
[WebServer] chunk-WOQ4O7SL.js   | -                             |   3.67 kB | 
[WebServer] chunk-LODE43G5.js   | -                             |   3.31 kB | 
[WebServer] chunk-PF3SKUEW.js   | -                             |   2.92 kB | 
[WebServer] chunk-H72E5A3L.js   | -                             |   2.90 kB | 
[WebServer] chunk-A7NV3EZJ.js   | -                             |   2.73 kB | 
[WebServer] chunk-R2KZN2E2.js   | -                             |   2.67 kB | 
[WebServer] chunk-APGCNZCS.js   | -                             |   2.50 kB | 
[WebServer] chunk-QKGIPAGN.js   | -                             |   2.36 kB | 
[WebServer] chunk-PCML2QOC.js   | -                             |   2.14 kB | 
[WebServer] chunk-VSZF3HME.js   | -                             |   1.94 kB | 
[WebServer] chunk-F7TMK2PM.js   | -                             |   1.88 kB | 
[WebServer] chunk-REG3UXPA.js   | -                             |   1.70 kB | 
[WebServer] chunk-CVJHXYHS.js   | -                             |   1.54 kB | 
[WebServer] chunk-QFVN6FHD.js   | -                             |   1.40 kB | 
[WebServer] chunk-FYTAYCL2.js   | -                             |   1.38 kB | 
[WebServer] chunk-HVC7CZTJ.js   | -                             |   1.38 kB | 
[WebServer] chunk-7HDUANM3.js   | -                             |   1.36 kB | 
[WebServer] chunk-YULDQP7H.js   | -                             |   1.28 kB | 
[WebServer] polyfills.js        | polyfills                     | 467 bytes | 
[WebServer] chunk-CJ4PWQKZ.js   | -                             | 431 bytes | 
[WebServer] chunk-OIDKS6PW.js   | -                             | 375 bytes | 
[WebServer] 
[WebServer]                     | Initial total                 |   5.73 MB
[WebServer] 
[WebServer] Lazy chunk files    | Names                         |  Raw size
[WebServer] chunk-DJQ6CVQS.js   | daily-summary-component       | 140.80 kB | 
[WebServer] chunk-I77IMFKB.js   | config-page-component         | 123.23 kB | 
[WebServer] chunk-R2GNBCCX.js   | -                             |  54.47 kB | 
[WebServer] chunk-ECMUBYXW.js   | metric-page-component         |  50.72 kB | 
[WebServer] chunk-DZVJMCDS.js   | boards-component              |  49.28 kB | 
[WebServer] chunk-JOTLEBHQ.js   | -                             |  40.69 kB | 
[WebServer] chunk-5MOXPKA7.js   | schedule-component            |  30.21 kB | 
[WebServer] chunk-FCWXEDT7.js   | worklog-component             |  29.03 kB | 
[WebServer] chunk-VSXYGPVR.js   | -                             |  27.23 kB | 
[WebServer] chunk-CNHMH3MS.js   | scheduled-list-page-component |  27.01 kB | 
[WebServer] worker-W2VREO2A.js  | reminder-worker               |  26.35 kB | 
[WebServer] chunk-CIZJR6XT.js   | planner-component             |  17.13 kB | 
[WebServer] chunk-5QAAGCOD.js   | -                             |  15.29 kB | 
[WebServer] chunk-QHLVN5TP.js   | quick-history-component       |  15.16 kB | 
[WebServer] chunk-T6QFUCYX.js   | search-page-component         |  12.10 kB | 
[WebServer] ...and 13 more lazy chunks files. Use "--verbose" to show all the files.
[WebServer] 
[WebServer] Application bundle generation complete. [9.592 seconds] - 2025-12-10T12:10:06.690Z
[WebServer] 

[WebServer] Watch mode enabled. Watching for file changes...

[WebServer] NOTE: Raw file sizes do not reflect development server per-request transformations.

[WebServer]   ➜  Local:   http://localhost:4242/

Running tests with 4 workers

Running 26 tests using 4 workers

[1/26] [chromium] › e2e/tests/sync/supersync-advanced.spec.ts:132:7 › @supersync SuperSync Advanced › Tag Management: Add/Remove tags syncs correctly
[2/26] [chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[3/26] [chromium] › e2e/tests/sync/import-sync.spec.ts:66:7 › @importsync @supersync Import + Sync E2E › Import backup file on Client A, sync to Client B
[4/26] [chromium] › e2e/tests/sync/supersync-advanced.spec.ts:50:7 › @supersync SuperSync Advanced › Large dataset sync (50 tasks)
[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[Merge Test] Phase 1: Creating initial tasks

[chromium] › e2e/tests/sync/import-sync.spec.ts:66:7 › @importsync @supersync Import + Sync E2E › Import backup file on Client A, sync to Client B
[Import Test] Phase 1: Client A importing backup

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:50:7 › @supersync SuperSync Advanced › Large dataset sync (50 tasks)
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/import-sync.spec.ts:66:7 › @importsync @supersync Import + Sync E2E › Import backup file on Client A, sync to Client B
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:132:7 › @supersync SuperSync Advanced › Tag Management: Add/Remove tags syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:50:7 › @supersync SuperSync Advanced › Large dataset sync (50 tasks)
[LargeData] Creating 50 tasks on Client A...

[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[Merge Test] Client A created and synced: ExistingA-1765368607691

[chromium] › e2e/tests/sync/import-sync.spec.ts:66:7 › @importsync @supersync Import + Sync E2E › Import backup file on Client A, sync to Client B
[Import Test] Client A imported backup successfully

[Import Test] Client A has imported tasks visible

[Import Test] Phase 2: Client A syncing to server

[Import Test] Client A sync complete

[Import Test] Phase 3: Client B syncing from server

[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:132:7 › @supersync SuperSync Advanced › Tag Management: Add/Remove tags syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:132:7 › @supersync SuperSync Advanced › Tag Management: Add/Remove tags syncs correctly
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/import-sync.spec.ts:66:7 › @importsync @supersync Import + Sync E2E › Import backup file on Client A, sync to Client B
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[Import Test] Client B sync complete

[Import Test] Phase 4: Verifying active tasks on Client B

[Import Test] Client B has both active tasks

[Import Test] Phase 5: Verifying archived tasks in worklog

[Import Test] Checking for archived tasks in worklog...

[Import Test] Client B worklog has archived task entries

[Import Test] Phase 6: Final state verification

[Import Test] Client A task count: 3

[Import Test] Client B task count: 3

[Import Test] ✓ Import + Sync test passed!

[5/26] [chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[Merge Test] Client B created and synced: ExistingB-1765368607691

[Merge Test] Phase 2: Client A importing backup

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:132:7 › @supersync SuperSync Advanced › Tag Management: Add/Remove tags syncs correctly
[TagTest] ✓ Tag added and removed successfully across clients

[6/26] [chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[Merge Test] Client A imported backup

[Merge Test] Phase 3: Syncing after import

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
Client A manually set time to: 10m

[chromium] › e2e/tests/sync/import-sync.spec.ts:220:7 › @importsync @supersync Import + Sync E2E › Import merges with existing synced data
[Merge Test] Phase 4: Verifying merged data

[Merge Test] Client A has imported tasks

[Merge Test] Client B received imported tasks via sync

[Merge Test] ✓ Import merge test passed!

[7/26] [chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:113:7 › @supersync SuperSync Edge Cases › Move task between projects syncs correctly
[chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
Client A archived tasks.

Client A synced.

[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:113:7 › @supersync SuperSync Edge Cases › Move task between projects syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:113:7 › @supersync SuperSync Edge Cases › Move task between projects syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:244:7 › @supersync SuperSync Advanced › Concurrent Delete vs. Update (Conflict Handling)
[ConflictTest] ✓ Concurrent Delete/Update resolved correctly (deletion won)

[8/26] [chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:226:8 › @supersync SuperSync Edge Cases › Undo propagation syncs correctly
[9/26] [chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:328:8 › @supersync SuperSync Edge Cases › Field-level merging syncs correctly
[10/26] [chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:433:7 › @supersync SuperSync Edge Cases › Offline burst of changes syncs correctly
[chromium] › e2e/tests/sync/supersync-daily-summary.spec.ts:47:7 › @supersync Daily Summary Sync › Archived tasks and time tracking appear on Daily Summary
Client B synced.

Client B on Daily Summary.

Row A Content: A-1765368625691-3-TaskA-17653686256910:10 0:10  - undo check

Row B Content: A-1765368625691-3-TaskB-1765368625691- -  - undo check

✓ Daily Summary verification passed!

[11/26] [chromium] › e2e/tests/sync/supersync-encryption.spec.ts:63:8 › @supersync SuperSync Encryption › Encrypted data syncs correctly with valid password
[12/26] [chromium] › e2e/tests/sync/supersync-encryption.spec.ts:110:8 › @supersync SuperSync Encryption › Encrypted data fails to sync with wrong password
[13/26] [chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:433:7 › @supersync SuperSync Edge Cases › Offline burst of changes syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:433:7 › @supersync SuperSync Edge Cases › Offline burst of changes syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
Client A synced initial tasks

Client B created local tasks

[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:433:7 › @supersync SuperSync Edge Cases › Offline burst of changes syncs correctly
[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:113:7 › @supersync SuperSync Edge Cases › Move task between projects syncs correctly
[MoveTask] ✓ Task moved between projects successfully across clients

[14/26] [chromium] › e2e/tests/sync/supersync-models.spec.ts:165:7 › @supersync SuperSync Models › Projects and their tasks sync correctly
[chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
Client A added more tasks and synced

[chromium] › e2e/tests/sync/supersync-edge-cases.spec.ts:433:7 › @supersync SuperSync Edge Cases › Offline burst of changes syncs correctly
[BurstTest] ✓ Offline burst changes synced successfully

[15/26] [chromium] › e2e/tests/sync/supersync-models.spec.ts:237:7 › @supersync SuperSync Models › Tags and tagged tasks sync correctly
[chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
Client B added more local tasks

Client B enabling sync...

[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync-models.spec.ts:165:7 › @supersync SuperSync Models › Projects and their tasks sync correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-models.spec.ts:237:7 › @supersync SuperSync Models › Tags and tagged tasks sync correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

Context menu item not found, trying shortcut...

[chromium] › e2e/tests/sync/supersync-late-join.spec.ts:37:7 › @supersync SuperSync Late Join › Late joiner merges correctly with existing server data
Verifying all tasks on Client A

Verifying all tasks on Client B

[16/26] [chromium] › e2e/tests/sync/supersync-models.spec.ts:403:7 › @supersync SuperSync Models › Project notes sync correctly
[chromium] › e2e/tests/sync/supersync-models.spec.ts:403:7 › @supersync SuperSync Models › Project notes sync correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-models.spec.ts:165:7 › @supersync SuperSync Models › Projects and their tasks sync correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-models.spec.ts:237:7 › @supersync SuperSync Models › Tags and tagged tasks sync correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[17/26] [chromium] › e2e/tests/sync/supersync.spec.ts:69:7 › @supersync SuperSync E2E › 2.1 Client A creates task, Client B downloads it
[18/26] [chromium] › e2e/tests/sync/supersync.spec.ts:131:7 › @supersync SuperSync E2E › 2.2 Both clients create different tasks
[chromium] › e2e/tests/sync/supersync.spec.ts:69:7 › @supersync SuperSync E2E › 2.1 Client A creates task, Client B downloads it
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:131:7 › @supersync SuperSync E2E › 2.2 Both clients create different tasks
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-models.spec.ts:403:7 › @supersync SuperSync Models › Project notes sync correctly
Attempt 1 to open note dialog failed, retrying...

[chromium] › e2e/tests/sync/supersync.spec.ts:131:7 › @supersync SuperSync E2E › 2.2 Both clients create different tasks
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:50:7 › @supersync SuperSync Advanced › Large dataset sync (50 tasks)
[LargeData] Creation complete. Verifying local count...

[LargeData] Syncing Client A...

[chromium] › e2e/tests/sync/supersync.spec.ts:131:7 › @supersync SuperSync E2E › 2.2 Both clients create different tasks
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:69:7 › @supersync SuperSync E2E › 2.1 Client A creates task, Client B downloads it
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync-models.spec.ts:403:7 › @supersync SuperSync Models › Project notes sync correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[19/26] [chromium] › e2e/tests/sync/supersync.spec.ts:196:7 › @supersync SuperSync E2E › 1.3 Update propagates between clients
[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:50:7 › @supersync SuperSync Advanced › Large dataset sync (50 tasks)
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[LargeData] Syncing Client B...

[20/26] [chromium] › e2e/tests/sync/supersync.spec.ts:258:7 › @supersync SuperSync E2E › 1.4 Delete propagates between clients
[chromium] › e2e/tests/sync/supersync.spec.ts:196:7 › @supersync SuperSync E2E › 1.3 Update propagates between clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync-advanced.spec.ts:50:7 › @supersync SuperSync Advanced › Large dataset sync (50 tasks)
[LargeData] Verifying Client B count...

[21/26] [chromium] › e2e/tests/sync/supersync.spec.ts:340:7 › @supersync SuperSync E2E › 3.1 Concurrent edits handled gracefully
[LargeData] ✓ Success: Synced 50 tasks.

[chromium] › e2e/tests/sync/supersync.spec.ts:258:7 › @supersync SuperSync E2E › 1.4 Delete propagates between clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[22/26] [chromium] › e2e/tests/sync/supersync.spec.ts:415:7 › @supersync SuperSync E2E › 2.3 Client A creates parent, Client B creates subtask
[chromium] › e2e/tests/sync/supersync.spec.ts:340:7 › @supersync SuperSync E2E › 3.1 Concurrent edits handled gracefully
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:415:7 › @supersync SuperSync E2E › 2.3 Client A creates parent, Client B creates subtask
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:196:7 › @supersync SuperSync E2E › 1.3 Update propagates between clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:258:7 › @supersync SuperSync E2E › 1.4 Delete propagates between clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:340:7 › @supersync SuperSync E2E › 3.1 Concurrent edits handled gracefully
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:415:7 › @supersync SuperSync E2E › 2.3 Client A creates parent, Client B creates subtask
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[23/26] [chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[SuperSyncPage] Clicking Apply to apply resolution...

[24/26] [chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:340:7 › @supersync SuperSync E2E › 3.1 Concurrent edits handled gracefully
[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[25/26] [chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[26/26] [chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Phase 1: Client A creates Task-1

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client A created task: Archive-Task1-1765368701284

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client A created task: ProjectX-1765368698503

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client A created task: Archive-Task2-1765368701284

[Archive Test] Client A marked Archive-Task1-1765368701284 as done

[Archive Test] Client A marked Archive-Task2-1765368701284 as done

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client A synced tasks

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[TimeTrack Test] Client A created task: TimeTrack-1765368704669

[TimeTrack Test] Client A started time tracking

[TimeTrack Test] Time tracking active

[TimeTrack Test] Waiting 5 seconds for time to accumulate...

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Client A created: Task1-1765368705251

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client B received initial task

[Chain Test] Client B renamed task to: ProjectX-Planning-1765368698503

[Chain Test] Client B added subtask: Research-1765368698503

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Client A synced

[3-Client Test] Phase 2: Client B syncs and creates Task-2

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client A received rename and subtask

[Chain Test] Client A marked Research-1765368698503 as done

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[TimeTrack Test] Client A stopped time tracking

[TimeTrack Test] Client A recorded time: -

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client A added subtask: Implementation-1765368698503

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client B synced (downloaded tasks)

[Archive Test] Client B received both tasks

[Archive Test] Client B clicked Finish Day

[Archive Test] Client B on daily summary page

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client B clicked Save and go home (archiving)

[Archive Test] Client B back on work view after archiving

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[TimeTrack Test] Client A synced

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client B synced (uploaded archive)

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Client B received Task-1

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client B received done status and new subtask

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client A synced (downloaded archive)

[Archive Test] Client A navigated to worklog

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client B added subtask: Testing-1765368698503

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client B navigated to worklog

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Client B created: Task2-1765368705251

[3-Client Test] Phase 3: Client C syncs, creates Task-3, renames Task-1

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Client B marked Testing-1765368698503 as done

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:724:7 › @supersync SuperSync E2E › 5.1 Archived tasks appear in worklog on both clients
[Archive Test] Client A worklog has both tasks

[Archive Test] Client B worklog has both tasks

[Archive Test] ✓ All verifications passed!

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[SuperSyncPage] Initial sync started automatically, waiting for completion...

[SuperSyncPage] Conflict dialog detected, using remote...

[chromium] › e2e/tests/sync/supersync.spec.ts:512:7 › @supersync SuperSync E2E › 4.1 Complex chain of actions syncs correctly
[Chain Test] Verifying final state...

[Chain Test] ✓ All verifications passed!

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[SuperSyncPage] Clicking Apply to apply resolution...

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[TimeTrack Test] Client B synced

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Client C received Task-1

[chromium] › e2e/tests/sync/supersync.spec.ts:895:7 › @supersync SuperSync E2E › 6.1 Time tracking syncs between clients
[TimeTrack Test] Client B shows time: -

[TimeTrack Test] Time values match on both clients

[TimeTrack Test] ✓ All verifications passed!

[chromium] › e2e/tests/sync/supersync.spec.ts:1033:7 › @supersync SuperSync E2E › 7.1 Three clients achieve eventual consistency
[3-Client Test] Client C created: Task3-1765368705251

[3-Client Test] Client C renamed Task-1 to: Task1-Renamed-1765368705251

[3-Client Test] Phase 4: Client B syncs (uploads Task-2)

[3-Client Test] Client B synced

[3-Client Test] Phase 5: Client C syncs

[3-Client Test] Client C received Task-2

[3-Client Test] Phase 6: Client A syncs

[3-Client Test] Client A received all updates

[3-Client Test] Phase 7: Client B syncs

[3-Client Test] Client B received all updates

[3-Client Test] Phase 8: Verifying eventual consistency

[3-Client Test] Client A task count: 3

[3-Client Test] Client B task count: 3

[3-Client Test] Client C task count: 3

[3-Client Test] Task-1 (renamed) exists on all clients

[3-Client Test] Task-2 exists on all clients

[3-Client Test] Task-3 exists on all clients

[3-Client Test] ✓ All three clients have identical state!

[3-Client Test] ✓ Eventual consistency achieved!

  4 skipped
  22 passed (2.3m) failures.
2025-12-12 20:48:40 +01:00
Johannes Millan
f68fac6b34 fix: support gzip uploads and safe op pruning 2025-12-12 20:48:40 +01:00
Johannes Millan
b6fc65831c feat(superSync): implement prisma and postgres 2025-12-12 20:48:38 +01:00
Johannes Millan
c45a1ca611 Fix super-sync server auth and snapshot safety 2025-12-12 20:48:13 +01:00
Johannes Millan
0fe31613ff fix(sync): support encrypted payloads in server validation and e2e
- Add blur after filling encryption password in e2e to trigger form update
- Allow string payloads in server validation for encrypted operations
2025-12-12 20:48:13 +01:00
Johannes Millan
23c1c49c9b fix(sync): add gap detection for server reset and stale client scenarios
- Server: Detect when client's sinceSeq > latestSeq (server reset)
- Server: Detect when sinceSeq > 0 but latestSeq = 0 (empty server)
- Client: Handle gapDetected response by resetting lastServerSeq to 0
- Client: Re-download all ops from beginning when gap is detected
- Server: Increase max payload keys from 10000 to 20000

This fixes the issue where Client B had a stale lastServerSeq from a
previous session. After server reset or Client A's fresh sync, Client B
would request ops after its stale seq and get nothing, even though new
ops existed at lower sequence numbers.
2025-12-12 20:47:48 +01:00
Johannes Millan
b51684e978 fix(sync-server): skip payload complexity check for full-state ops
SYNC_IMPORT, BACKUP_IMPORT, and REPAIR operations contain the entire
application state which naturally exceeds the normal complexity limits
(max depth 20, max keys 10000). These operations are already protected
by bodyLimit (30MB) so the DoS concern is addressed.

This fixes the error "Payload too complex" when importing data via sync.
2025-12-12 20:47:48 +01:00
Johannes Millan
526afbafef test(operation-log): add comprehensive client-side test coverage
Add 54 new tests covering critical edge cases:

Emergency compaction failure scenarios:
- Nested quota failure handling
- Lock acquisition failures
- Error propagation from dependencies

Quota exceeded handling:
- Firefox and Safari quota error detection
- Circuit breaker for repeated quota errors
- Recovery success messaging

Multi-tab coordination:
- Concurrent lock request serialization
- Lock timeout and error handling
- Nested lock requests

Sync failure recovery:
- Network failure handling
- Partial batch failure
- Mixed accept/reject responses
- Lock acquisition failures

IndexedDB edge cases:
- Concurrent operations
- Large payloads
- Special characters
- Empty array handling
2025-12-12 20:47:48 +01:00
Johannes Millan
fe8cc5e7ac fix(sync): code review fixes and parentOpId support
Code review fixes:
- Fix download memory limit to correctly return success: false
- Add logging when vector clock entries are stripped during sanitization
- Add integration test for partial batch upload recovery (network failure)

parentOpId support for conflict resolution:
- Add parentOpId field to Operation type for conflict chain tracking
- Add parent_op_id column to operations table with migration
- Update sync routes schema to accept parentOpId
- Store and retrieve parentOpId in sync service

Also adds PLUGIN_USER_DATA and PLUGIN_METADATA to allowed entity types.
2025-12-12 20:47:47 +01:00