Learn Review Log Cloud Sync
Syncs CardMirror's Learn review log (cardId/at/grade/
intervalBefore/intervalAfter — the grading history grade() appends
to on every review) to a signed-in user's account, the same way
Learn Flashcards Cloud Sync
syncs a card's own content and
Learn Decks Cloud Sync syncs
custom decks — the Learn store
(packages/debate-editor/src/editor/learn-store.ts) was device-local
only, and before this the review log had no user-facing surface at all,
not even in the "Manage flashcards" overlay.
- Package:
debate-editor(learnReviewLogSync,LearnReviewLogSync,learn-review-log-client,learn-review-log-ui),apps/debate-ai.com(/api/learn-review-log,saved_learn_review_logD1 table)
What it does
On boot, once the local Learn store has loaded, learnReviewLogSync.init()
merges once against the account: any entry synced from another device but
missing locally is adopted (via LearnStore#adoptLogEntry), and any entry
that exists only on this device is best-effort pushed up — neither side
ever overwrites the other, it only fills gaps. After the merge, every
later graded review mirrors itself to the account the same way, including
an entry's removal when its card is deleted or a file's cards are pruned
via "forget this file" (both prune the local log too). Signed out, or
under the Electron desktop build, the review log keeps working exactly as
before, purely on-device.
A review-log entry has no id field of its own — reviewLogEntryId(entry)
(`${cardId}:${at}`) doubles as its synced record's key, since at is
a millisecond-precision ISO timestamp already unique per card. Unlike
Learn Flashcards Cloud Sync,
there's no optimistic-concurrency conflict to handle: an entry's content
for a given id never changes (grade() only ever appends a new entry,
never edits an existing one), so PUT /api/learn-review-log/[entryId] is
a plain idempotent upsert.
Adopting a remote entry is purely informational — it never replays into
schedules, which stays local-only and per-device exactly as the other
two Learn syncs already leave it. Restoring review history on a new
device is not meant to change that device's own due dates or intervals.
The "Manage flashcards" overlay's new History button
(learn-review-log-ui.ts's openReviewLogHistory) lists every logged
review, newest first, with the card's current front text, the grade, and
when it happened, plus a coarse "Synced to your account" / "Not synced"
status line (not a per-entry badge — this sync has no per-record conflict
state worth surfacing).
What's intentionally excluded
The remaining Learn sub-collections (schedules, anchors, AI threads,
notes, doc registry) stay local-only for the same reason
learn-cards-cloud-sync.mdx documents: they live in one shared blob per
device, not one row per record, so they don't fit this sync's per-record
shape.
Known gaps
- An entry adopted from another device can reference a card that hasn't
reached this device yet (the same soft-reference gap
Learn Decks Cloud Sync already
accepts for a deck's
cardIds) — the History list shows "(deleted card)" for it rather than the card's front text.
