- Interval
- Ease Factor (EF)
- Repetition Count
- Due Date
- Fail:
- Interval: 1 Day
- Repetition = 0
- Hard:
- Interval: previous_interval x 1.2
- Good:
- Interval: previous_interval x EF
- Easy:
- Interval: previous_interval x EF x 1.3
- Initial EF = 2.5
EF' = EF + (0.1 - (5 - quality) x (0.08 + (5 - quality) x 0.02))
- Here:
- quality belongs to [0-5]
- constrained such that EF >= 1.3 (min bound)
- Due Date = Current Time + Interval
- Stored as timestamp
- Used to determine which cards are "due"
Each card exists in one of the following states:
- New:
- Never reviewed
- Introduced gradually
- Learning:
- Recently introduced
- Short intervals
- Review:
- Long-term retention phase
- SM-2 scheduling
Each card will store:
iddeck_idstateintervalease_factorrepetition_countdue_timestamplast_reviewed_atupdated_atis_deleted
- The local Drift database is the source of truth during offline usage.
- Every user session works against that user's own local database file.
- Local writes happen first.
- Sync is eventual-consistency based.
- Operations are:
- logged locally
- replayed to the server later
- The app uses a separate local SQLite database per signed-in user.
- Logging into a different account switches the active Drift database file instead of reusing the previous account's local cache.
- Logging out switches the app to a guest-local database, so decks from one user cannot appear inside another user's account on the same device.
- Public deck downloads are imported into the currently signed-in user's local database only.
- Review logs:
- all user interactions
- used for analytics + state reconstruction
- Updated cards:
- edited cards
- scheduling updates
- Deck changes:
- created / updated / deleted decks
- the signed-in user's latest decks
- the signed-in user's latest cards
- deleted records needed for convergence
- Save to the local Drift database first.
- Queue the change locally.
- If network is available, upload pending local changes.
- Download latest server changes.
- Merge them into the current user's local database.
- app launch
- manual sync
- network reconnect / background sync trigger
Due to offline-first design, conflicts may occur when the same data is modified on multiple devices.
A combination of:
- Last-Write-Wins (Primary)
- based on
updated_at - fast and simple
- based on
- Versioning (Secondary Safety)
- each record includes
version - incremented on update
- each record includes
- Server-Assisted Merge (For Critical Data)
- reserved for future collaborative deck flows
| Scenario | Resolution |
|---|---|
| Local newer than server | Overwrite server |
| Server newer than local | Overwrite local |
| Same timestamp | Prefer server |
| Deleted vs updated | Deletion wins |
Instead of removing records immediately from sync state:
"is_deleted": trueEnsures:
- sync consistency
- recovery capability
- multi-device convergence
Each record must include:
idupdated_atversionis_deleted
Local table:
{
"operation_id": "uuid",
"type": "create | update | delete | review",
"payload": {},
"created_at": "timestamp",
"synced": false
}last_sync_atis stored per user, not globally.- Switching accounts on the same device must not reuse another user's sync cursor.
- The client uses an SM-2 style local scheduler for card reviews.
- The default daily review limit is 25 cards, configurable from System Settings.
- Daily card selection is enforced locally before a review session begins.
- The client persists both the JWT and the resolved
user_idafter login. - Splash restoration first re-attaches the correct user-scoped local database using the stored
user_id. - A best-effort
/mevalidation can happen after local restore, but temporary network failure must not force the user back to Login. - The Login page should only be shown after logout or explicit token rejection by the backend.