Commit graph

206 commits

Author SHA1 Message Date
Johannes Millan
5ffc109943 refactor(sync): unify archive operation handling
Consolidate all archive-affecting operations into ArchiveOperationHandler
to eliminate duplicate code and create a single source of truth.

Changes:
- Add ArchiveOperationHandlerEffects for local operations
- Rename handleRemoteOperation to handleOperation
- Add isArchiveAffectingAction helper for action filtering
- Make isIgnoreDBLock conditional on action.meta.isRemote
- Remove archive logic from individual effect files
- Update feature-stores.module.ts to use new effect
- Add archive write points documentation

This ensures archive operations (moveToArchive, restoreTask,
flushYoungToOld, deleteProject, deleteTag, deleteTaskRepeatCfg,
deleteIssueProvider) all go through one handler for both local
and remote operations.
2025-12-12 20:48:12 +01:00
Johannes Millan
320e57e622 refactor(tasks): use satisfies instead of as for PersistentActionMeta
Improves type safety by using satisfies which validates the object
matches the type at compile time, rather than as which is just an
assertion.
2025-12-12 20:48:12 +01:00
Johannes Millan
69f3602927 docs(sync): update review with completed fixes
Mark as fixed:
- Bounds checking in planner-shared.reducer.ts
- Error handling in tag.effects.ts (async IndexedDB operations)
2025-12-12 20:47:48 +01:00
Johannes Millan
8187313e0c docs(sync): mark TaskDueEffects race conditions as false positive
Analysis shows the alleged race conditions are intentional defensive patterns:
- Double sync wait ensures stability during date changes
- Effects are complementary with staggered timing, not competing
- Infinite loop scenario explicitly handled in code

Note: Tests exist but are disabled due to Dropbox SDK mocking issues.
2025-12-12 20:47:48 +01:00
Johannes Millan
89066aca51 docs(sync): mark subtask orphan detection as false positive
Re-evaluated the "missing subtask cascade in tag deletion" issue.
The current behavior is correct:
- A subtask with a surviving parent is NOT orphaned - it's accessible
  through the parent in the UI
- If a parent is orphaned, its subtasks ARE correctly deleted
- Subtasks always have parentId, so they're never directly orphaned

This is intentional behavior, not a bug.
2025-12-12 20:47:48 +01:00
Johannes Millan
e6025b4178 fix(sync): validate state before saving snapshot in hydrator
The snapshot was being saved before validation ran. If validation found
corruption and repaired it, the snapshot was stale/wrong. This fix
moves validation (CHECKPOINT C) BEFORE saving snapshot in both:
- Tail replay path (after replaying ops following a snapshot)
- Full replay path (when no snapshot exists)

Now the snapshot contains validated/repaired state.

Includes regression tests that verify validation happens before snapshot
save in both code paths.
2025-12-12 20:47:48 +01:00
Johannes Millan
bbd50c223a fix(sync): persist compaction counter when no cache exists
The incrementCompactionCounter() function returned 1 without persisting
when no state cache existed. This caused the compaction threshold to
never be reached, leading to unbounded operation log growth.

Also updated review docs:
- Marked compaction counter bug as fixed
- Identified memory leak in TaskElectronEffects as false positive
  (take(1) ensures automatic cleanup)
- Identified TODAY_TAG "board-style" claim as false positive
  (code correctly REMOVES TODAY_TAG from task.tagIds)
- Identified cache invalidation claim as false positive
  (sequence-based invalidation works correctly)
- Identified repair mutex race condition as false positive
  (JavaScript single-threaded, no await between assignment)
2025-12-12 20:47:48 +01:00
Johannes Millan
78c65acf4d test(e2e): add sd:today to tasks for TODAY view visibility
Tasks created without explicit dueDay don't appear in TODAY view
due to recent selector changes. Add sd:today short syntax to ensure
tasks have dueDay set and appear correctly in tests.
2025-12-12 20:47:48 +01:00
Johannes Millan
60bc0e78d7 docs(sync): replace PFAPI with operation log sync for all providers 2025-12-12 20:47:47 +01:00
Johannes Millan
e62f2f86fa feat(e2e): add SuperSync E2E test infrastructure for multi-client sync
- Add test mode to super-sync-server (TEST_MODE=true env var)
- Add /api/test/create-user endpoint for auto-verified test users
- Add /api/test/cleanup endpoint for wiping test data
- Create SuperSyncPage page object for sync configuration
- Create supersync-helpers.ts with test utilities
- Add 6 E2E sync scenarios:
  - 2.1: Client A creates task, Client B downloads
  - 2.2: Both clients create different tasks
  - 1.3: Update propagates between clients
  - 1.4: Delete propagates between clients
  - 3.1: Concurrent edits handled gracefully
  - 2.3: Parent/subtask relationships sync correctly
2025-12-12 20:47:41 +01:00
Johannes Millan
c977ad3edd test(oplog): add integration tests for multi-client sync and state consistency
Add Karma-based integration tests for the operation log system using real
IndexedDB. Tests cover multi-client synchronization scenarios and state
consistency verification.

New test files:
- multi-client-sync.integration.spec.ts (16 tests)
- state-consistency.integration.spec.ts (17 tests)

Test utilities:
- TestClient helper for simulating multiple clients with independent vector clocks
- Operation factory helpers for creating test operations
2025-12-12 20:46:27 +01:00
Johannes Millan
3129c1dbca test(oplog): add persistent-action tests and fix compilation/lint errors
- Add unit tests for isPersistentAction type guard.

- Fix compilation errors in task scheduling components caused by removed reminderId/removeReminderFromTask.

- Fix type error in create-sorted-blocker-blocks.spec.ts.

- Fix lint errors in various files.
2025-12-12 20:46:27 +01:00
Johannes Millan
f661de10de feat(sync): implement time tracking sync and deterministic archive flush
- Add syncTimeSpent persistent action for batched time tracking sync
  - Accumulates duration per task, syncs every 5 minutes or on task stop
  - Local dispatch: no-op (state already updated by second-by-second ticks)
  - Remote dispatch: applies batched duration to task.timeSpentOnDay

- Fix remote archive handling to include time tracking data
  - writeTasksToArchiveForRemoteSync now moves historical time tracking
    data to archiveYoung alongside tasks

- Add flushYoungToOld persistent action for deterministic archive state
  - Replaces inline flush logic with action-based approach
  - Remote clients replay the flush operation to maintain consistent
    archiveOld state without syncing large files
  - Vector clock ordering ensures flush happens after preceding operations
2025-12-12 20:46:26 +01:00
Johannes Millan
fd44db3852 docs(sync): update planning doc 2025-12-12 20:46:26 +01:00
Johannes Millan
410bcde114 feat(sync): implement receiver-side migration shield and fix tests
- Implement processRemoteOps migration logic in OperationLogSyncService using SchemaMigrationService.
- Add MAX_VERSION_SKIP check and user warning via SnackService.
- Fix Log class to support dynamic level switching (fixes PFAPI tests).
- Fix operation-log-stress imports.
- Skip disabled MultiTabCoordinatorService tests.
- Add unit tests for migration logic.
2025-12-12 20:46:26 +01:00
Johannes Millan
4e018a8064 docs: update operation log architecture paths 2025-12-12 20:46:26 +01:00
Johannes Millan
d79dc153fe docs: add architectural introduction to operation log documentation 2025-12-12 20:46:26 +01:00
Johannes Millan
04728267e2 docs: update operation log architecture docs to reflect recent changes
- Update ValidateStateService documentation to reflect better error handling for isRelatedModelDataValid.
- Update Part C status to Complete as Server Sync is implemented.
- List implemented components for Part C.
- Fix lint error in startup.service.ts
2025-12-12 20:46:26 +01:00
Johannes Millan
a70273597f refactor(oplog): split sync service and extract vector clock utilities
- Split OperationLogSyncService (678→289 lines) into focused services:
  - OperationLogManifestService: manifest file operations
  - OperationLogUploadService: upload pending operations
  - OperationLogDownloadService: download remote operations
  - operation-sync.util.ts: shared type guards and converters

- Extract VectorClockService with clear documentation:
  - getCurrentVectorClock(): merged clock from snapshot + ops
  - getEntityFrontier(): per-entity vector clocks
  - getSnapshotVectorClock(): baseline for conflict detection

- Remove dead code:
  - Remove markApplied() no-op from store service
  - Move stress tests to benchmarks/ folder

- Fix type safety:
  - Replace 'as any' casts with proper types
  - Add TaskOperationPayload interface
2025-12-12 20:46:26 +01:00
Johannes Millan
377800caf5 docs(oplog): add refactoring plan for operation log system 2025-12-12 20:46:26 +01:00
Johannes Millan
6818ae8d9f feat(oplog): add persistent compaction counter and syncedAt index
- Store compaction counter in state_cache to share across tabs/restarts
- Add bySyncedAt index for faster getUnsynced() queries
- Remove in-memory opsSinceCompaction counter from effects
- Reset counter in compaction service after successful compaction
2025-12-12 20:46:26 +01:00
Johannes Millan
b73769aa15 feat(oplog): implement migration safety and tail ops migration
Implements A.7.12, A.7.13, and A.7.15 from the operation log architecture:

- A.7.12 Migration Safety: Backup state_cache before migration, restore
  on failure, detect interrupted migrations on startup
- A.7.13 Tail Ops Migration: Migrate operations to current schema before
  replay during hydration
- A.7.15 Unified Interface: Extend SchemaMigration with migrateOperation
  and requiresOperationMigration for explicit migration decisions

Changes:
- operation-log-store: Add backup/restore methods for state_cache
- schema-migration: Add operation migration support and validation
- operation-log-hydrator: Integrate backup safety and tail ops migration
- Update architecture docs to reflect implementation status
2025-12-12 20:46:26 +01:00
Johannes Millan
36dd2ee38b docs(sync): replace hybrid manifest mindmap with detailed flow diagrams
Replace the single mindmap with comprehensive diagrams:
- 6.1 Data Lifecycle: Hot → Cold → Frozen
- 6.2 Manifest File Structure
- 6.3 Write Path: Buffer vs Overflow Decision
- 6.4 Read Path: Reconstructing State
- 6.5 Compaction: Freezing State
- 6.6 Request Count Comparison table
2025-12-12 20:46:26 +01:00
Johannes Millan
4554c33074 docs(sync): add A.7.15 unified state and operation migrations
Document the relationship between state migrations (for snapshots) and
operation migrations (for tail ops/sync). Key points:

- Single SchemaMigration interface with both migrateState and migrateOperation
- requiresOperationMigration flag forces explicit decision
- Startup validation catches missing operation migrations
- Examples for field rename and feature removal migrations
- Explains why auto-derivation doesn't work (UPDATE payloads differ from entities)
- Updated implementation status table with A.7.15
2025-12-12 20:46:26 +01:00
Johannes Millan
05ecf23b69 docs(sync): expand hybrid manifest architecture with detailed workflows
- Add complete type definitions (HybridManifest, SnapshotFile, etc.)
- Add ASCII flow diagrams for upload, download, and snapshot workflows
- Document edge cases and failure modes:
  - Concurrent uploads with ETag/rev handling
  - Manifest corruption recovery
  - Missing snapshot handling
  - Schema version mismatch
  - Large pending operations batching
- Add configuration constants reference
- Add implementation plan with 4 phases
- Add file reference for remote storage layout
2025-12-12 20:46:26 +01:00
Johannes Millan
7362eb13dd docs(sync): improve architecture doc accuracy and add missing considerations
- Fix status claims: Part A "Functional" (not Complete), Part C "Design Only"
- Add Known Gaps callout for critical unimplemented features (A.7.12, A.7.13)
- Fix code bug in C.5 conflict resolution (variable out of scope)
- Fix broken markdown (4 backticks instead of 3)
- Add "Edge Cases & Missing Considerations" section:
  - IndexedDB quota exhaustion handling
  - Compaction trigger coordination between tabs
  - Genesis migration with partial data
- Restructure Implementation Status by Part (A, B, C, D)
- Add note to diagrams doc clarifying planned vs implemented features
- Fix mermaid syntax errors in hybrid manifest diagram
2025-12-12 20:46:26 +01:00
Johannes Millan
6489760466 docs: add hybrid manifest mindmap to architecture diagrams 2025-12-12 20:46:25 +01:00
Johannes Millan
0b5f2c7002 docs(sync): update planning doc 2025-12-12 20:46:25 +01:00
Johannes Millan
386c297ade docs: Document Hybrid Manifest & Snapshot Architecture
Introduces a new documentation file detailing the Hybrid Manifest and Snapshot architecture
designed to improve efficiency for file-based sync providers like WebDAV and Dropbox.
This approach reduces API requests and file proliferation by embedding small operations
directly into the manifest file and periodically consolidating changes into snapshots.

Also updates the architecture diagrams to include a visualization of this new hybrid approach.

Additionally, a "Conflict-Aware Migration Strategy" section was added to the existing
operation-log-architecture.md during initial codebase investigation to provide further
context on managing schema version differences during sync.
2025-12-12 20:46:25 +01:00
Johannes Millan
fb590c7fd6 docs: improve mermaid diagrams with file names 2025-12-12 20:46:25 +01:00
Johannes Millan
4cfd73cbd6 docs: add mermaid diagrams for operation log architecture and sync 2025-12-12 20:46:25 +01:00
Johannes Millan
a31de900db docs: clean up execution plan by removing completed tasks and focusing on future enhancements 2025-12-12 20:46:25 +01:00
Johannes Millan
546fd54573 docs: clarify state model in migration guide to AllSyncModels 2025-12-12 20:46:25 +01:00
Johannes Millan
3d00507fa1 docs: add guide on creating new migrations to operation log architecture 2025-12-12 20:46:25 +01:00
Johannes Millan
b68c6c2497 docs: clarify relation between legacy CROSS_MODEL_MIGRATION and operation log migrations 2025-12-12 20:46:25 +01:00
Johannes Millan
4408fbbbb0 docs(sync): update planning doc 2025-12-12 20:46:25 +01:00
Johannes Millan
03e3c3677d docs(operation-log): expand schema migration documentation
Add comprehensive documentation covering:
- Schema version concepts and migration triggers
- Local snapshot migration during app updates
- Operation replay with mixed versions
- Cross-version client sync scenarios
- Full state import handling (SYNC_IMPORT/BACKUP_IMPORT)
- Migration implementation patterns with examples
- Version handling in operations
- Design principles for writing migrations
- Handling unsupported versions (too old/too new)
- Future considerations for migration enhancements
2025-12-12 20:46:25 +01:00
Johannes Millan
d22fbe28b2 feat(operation-log): add data validation and repair system
Integrate PFAPI's validation/repair with operation logs to prevent
corruption and automatically recover from invalid states.

Key features:
- REPAIR operation type with full repaired state + summary
- 4 validation checkpoints (A: before write, B: after snapshot load,
  C: after replay, D: after sync)
- Payload validation before IndexedDB write (lenient structural checks)
- Full Typia + cross-model validation at state checkpoints
- User notification when auto-repair occurs
- Infinite loop prevention during repair

New files:
- validate-state.service.ts: wraps PFAPI validation + repair
- validate-operation-payload.ts: checkpoint A payload validation
- repair-operation.service.ts: REPAIR operation creation
2025-12-12 20:46:25 +01:00
Johannes Millan
37b8ec392f docs(sync): update planning doc 2025-12-12 20:46:25 +01:00
Johannes Millan
73ac516608 docs(sync): update 2025-12-12 20:46:21 +01:00
Johannes Millan
c87e7ec218 refactor(sync): use uuidv7 library for UUID generation
Replace custom UUID v7 implementations with the standard uuidv7 library.
The custom implementation had a bug with JavaScript bitwise operators
not handling 48-bit timestamps correctly.
2025-12-12 20:46:19 +01:00
Johannes Millan
c3ef2436dc fix(effects): skip remote actions in task effects
Add filterLocalAction() operator to filter out remote actions from sync.
Effects that handle UI feedback (snackbars, sounds) or derive additional
actions should not run for operations replayed from other clients.
2025-12-12 20:46:17 +01:00
Johannes Millan
94c1123c1f docs(op-log): update architecture doc and fix compaction service
- Update architecture doc to reflect actual implementation state:
  - Replace action blacklist with isPersistent pattern
  - Mark Part C (server sync) as complete (was incorrectly "not started")
  - Update file reference section with all current files
  - Update compaction config to show 7-day retention window
  - Remove obsolete "models requiring migration" section
- Fix compaction service:
  - Use PfapiStoreDelegateService for consistency
  - Add schemaVersion to state cache saves
2025-12-12 20:46:13 +01:00
Johannes Millan
cdaedf4fb8 feat(sync-server): implement operation log sync API
Add server-side operation log synchronization:

- Database schema: operations, user_sync_state, sync_devices, tombstones
- SyncService: upload/download ops, snapshots, rate limiting, validation
- REST API: POST/GET /api/sync/ops, /snapshot, /status, /devices/:id/ack
- Cleanup jobs: daily tombstone/old ops cleanup, hourly stale device cleanup
- Compression: gzip for snapshots
- Security: timestamp validation, payload size limits, idempotency

Endpoints:
- POST /api/sync/ops - Upload operations batch
- GET /api/sync/ops?sinceSeq=N - Download ops since sequence
- GET /api/sync/snapshot - Get full state snapshot
- POST /api/sync/snapshot - Upload full state (migration/recovery)
- GET /api/sync/status - Get sync status
- POST /api/sync/devices/:clientId/ack - Acknowledge sequences
2025-12-12 20:46:12 +01:00
Johannes Millan
5d7bc5d7fb feat(op-log): add disaster recovery and schema migration
A.6: Disaster Recovery
- Add snapshot validation in hydrateStore()
- Implement _attemptRecovery() to restore from legacy 'pf' database
- Add _hasUsableData() and _recoverFromLegacyData() helpers
- Add 'RECOVERY' entity type for recovery operations

A.7: Schema Migration Service
- Create SchemaMigrationService with migration infrastructure
- Define MigratableStateCache interface with schemaVersion
- Integrate migrations into hydration flow
- Update state_cache schema to include schemaVersion

Also updates architecture doc to reflect Part A/B completion.
2025-12-12 20:46:12 +01:00
Johannes Millan
302eec4758 docs: plan operation logs server 1 2025-12-12 20:46:12 +01:00
Johannes Millan
aa05c1638f docs: plan operation logs again 13 2025-12-12 20:46:12 +01:00
Johannes Millan
7f1ee6ea74 docs: plan operation logs again 13 2025-12-12 20:46:12 +01:00
Johannes Millan
73cf144ed4 docs: plan operation logs again 12 2025-12-12 20:46:12 +01:00
Johannes Millan
e53c581621 docs: plan operation logs again 11 2025-12-12 20:46:12 +01:00