mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-07-18 00:46:45 +00:00
docs(sync): document archive-wins rule, singleton LWW, and stale op handling
Document 6 gaps found during documentation audit: - Archive-wins conflict resolution rule (moveToArchive always wins over field-level updates regardless of timestamps) - Two-level archive resurrection prevention (ConflictResolutionService + bulkOperationsMetaReducer pre-scan filtering) - Singleton entity LWW update handling (adapter vs singleton vs unsupported storage patterns) - Stale moveToArchive operation preservation with merged vector clocks - Fix upload batch size from 100 to 25 in operation-rules.md and operation-log-architecture.md - Update meta-reducer chain from simplified 7-phase to accurate 8-phase, 15-entry structure matching meta-reducer-registry.ts
This commit is contained in:
parent
d618238864
commit
4ab86ba0fe
6 changed files with 241 additions and 31 deletions
|
|
@ -245,18 +245,27 @@ Last-Write-Wins automatically resolves conflicts using timestamps.
|
|||
|
||||
**Key Invariants:**
|
||||
|
||||
| Invariant | Reason |
|
||||
| --------------------------------- | --------------------------------------------- |
|
||||
| Preserve original timestamp | Prevents unfair advantage in future conflicts |
|
||||
| Merge ALL clocks | New op dominates everything known |
|
||||
| Reject ALL pending ops for entity | Prevents stale ops from being uploaded |
|
||||
| Mark rejected BEFORE applying | Crash safety |
|
||||
| Remote wins on tie | Server-authoritative |
|
||||
| Invariant | Reason |
|
||||
| --------------------------------- | -------------------------------------------------------------------------- |
|
||||
| Archive-wins rule | `moveToArchive` always wins over field-level updates, bypassing timestamps |
|
||||
| Preserve original timestamp | Prevents unfair advantage in future conflicts |
|
||||
| Merge ALL clocks | New op dominates everything known |
|
||||
| Reject ALL pending ops for entity | Prevents stale ops from being uploaded |
|
||||
| Mark rejected BEFORE applying | Crash safety |
|
||||
| Remote wins on tie | Server-authoritative |
|
||||
|
||||
**Stale Operation Special Cases:**
|
||||
|
||||
| Operation Type | Stale Handling |
|
||||
| --------------- | --------------------------------------------------------------------------------------------- |
|
||||
| Regular UPDATE | Re-created with current entity state + merged clock |
|
||||
| DELETE | Re-created with original payload + merged clock (entity gone from store) |
|
||||
| `moveToArchive` | Re-created with original payload + merged clock (entity removed from NgRx by archive reducer) |
|
||||
|
||||
**Key Files:**
|
||||
|
||||
- `conflict-resolution.service.ts` - LWW resolution
|
||||
- `stale-operation-resolver.service.ts` - Stale op handling
|
||||
- `conflict-resolution.service.ts` - LWW resolution + archive-wins rule
|
||||
- `stale-operation-resolver.service.ts` - Stale op handling (incl. moveToArchive special case)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -362,51 +371,67 @@ Meta-reducers enable atomic multi-entity changes in a single reducer pass.
|
|||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ META-REDUCER CHAIN │
|
||||
│ META-REDUCER CHAIN (8 Phases, 15 Entries) │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Action Dispatched │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 1: operationCaptureMetaReducer │ │
|
||||
│ │ Captures persistent actions → FIFO queue │ │
|
||||
│ │ Phase 1: operationCaptureMetaReducer [MUST BE FIRST] │ │
|
||||
│ │ Captures original state BEFORE modifications │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 2: bulkOperationsMetaReducer │ │
|
||||
│ │ Applies bulk ops during sync/hydration │ │
|
||||
│ │ Unwraps bulk dispatches for hydration/sync │ │
|
||||
│ │ Pre-scans for archive ops to prevent │ │
|
||||
│ │ resurrection via LWW Update │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 3: lwwUpdateMetaReducer │ │
|
||||
│ │ Handles LWW Update actions (conflict wins) │ │
|
||||
│ │ Phase 3: undoTaskDeleteMetaReducer │ │
|
||||
│ │ Captures task context before deletion │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 4: Tag/Project/IssueProvider Shared Reducers │ │
|
||||
│ │ Cascade deletions across entities │ │
|
||||
│ │ Phase 4: Core CRUD Meta-Reducers (dependency order) │ │
|
||||
│ │ • taskSharedCrudMetaReducer │ │
|
||||
│ │ • taskBatchUpdateMetaReducer │ │
|
||||
│ │ • taskSharedLifecycleMetaReducer │ │
|
||||
│ │ • taskSharedSchedulingMetaReducer │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 5: Planner/ShortSyntax Shared Reducers │ │
|
||||
│ │ Handle task movements and parsing │ │
|
||||
│ │ Phase 5: Entity-Specific Cascades │ │
|
||||
│ │ • projectSharedMetaReducer │ │
|
||||
│ │ • tagSharedMetaReducer │ │
|
||||
│ │ • issueProviderSharedMetaReducer │ │
|
||||
│ │ • taskRepeatCfgSharedMetaReducer │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 6: Feature Reducers (task, project, tag, etc.) │ │
|
||||
│ │ Core state updates │ │
|
||||
│ │ Phase 6: plannerSharedMetaReducer │ │
|
||||
│ │ Syncs with task.dueDay and TODAY_TAG │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 7: validateAndFixDataConsistency │ │
|
||||
│ │ Repair any inconsistencies │ │
|
||||
│ │ Phase 7: Synthetic Multi-Step Operations │ │
|
||||
│ │ • shortSyntaxSharedMetaReducer │ │
|
||||
│ │ • lwwUpdateMetaReducer │ │
|
||||
│ │ (adapter / singleton / unsupported patterns) │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ Phase 8: actionLoggerReducer [MUST BE LAST] │ │
|
||||
│ │ Pure logging only │ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
|
|
@ -415,6 +440,13 @@ Meta-reducers enable atomic multi-entity changes in a single reducer pass.
|
|||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Critical Ordering Rules:**
|
||||
|
||||
- `operationCaptureMetaReducer` must be at index 0 (captures pre-modification state)
|
||||
- `bulkOperationsMetaReducer` must be at index 1 (unwraps bulk before other reducers run)
|
||||
- `actionLoggerReducer` must be last (logs final state)
|
||||
- Development mode validates these constraints at startup
|
||||
|
||||
**Why Meta-Reducers for Multi-Entity Changes?**
|
||||
|
||||
| Approach | Problem |
|
||||
|
|
@ -544,8 +576,15 @@ Bulk application optimizes performance by applying many operations in a single d
|
|||
│ │ │
|
||||
│ ▼ (in bulkOperationsMetaReducer) │
|
||||
│ ┌────────────────────────────────────────┐ │
|
||||
│ │ // Pre-scan: collect archived IDs │ │
|
||||
│ │ archivedIds = findArchiveOps(ops) │ │
|
||||
│ │ │ │
|
||||
│ │ for (op of operations) { │ │
|
||||
│ │ action = convertOpToAction(op) │ │
|
||||
│ │ // Skip LWW Updates for archived │ │
|
||||
│ │ if (isLwwUpdate(action) && │ │
|
||||
│ │ archivedIds.has(entityId)) │ │
|
||||
│ │ continue; │ │
|
||||
│ │ state = reducer(state, action) │ │
|
||||
│ │ } │ │
|
||||
│ │ return state │ │
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue