52 lines
3.1 KiB
Markdown
52 lines
3.1 KiB
Markdown
|
|
# ADR 0007: Deleted studysets wait 14 days in Recently deleted
|
||
|
|
|
||
|
|
- **Status:** accepted
|
||
|
|
- **Date:** 2026-10-12
|
||
|
|
|
||
|
|
## Context
|
||
|
|
|
||
|
|
Deleting a studyset blanked and deleted it with everything inside it once a four-second
|
||
|
|
Undo was over (ADR 0002, rule 2: blanking can't be undone, so the Undo comes first). A
|
||
|
|
studyset can hold months of work and thousands of reviews, and a moment's Undo is easy
|
||
|
|
to miss. People expect what they delete to stay restorable for a while, as Vaia keeps
|
||
|
|
deleted studysets for 14 days in a section of their library.
|
||
|
|
|
||
|
|
## Decision
|
||
|
|
|
||
|
|
1. **A deleted studyset is marked, not deleted.** `studyset.deletedAt` records when the
|
||
|
|
person deleted it (null while it isn't), a column like `archivedAt` and for the same
|
||
|
|
reason (ADR 0004): deleting on one device and editing on another must not overwrite
|
||
|
|
each other, which they would inside the payload, whose last writer wins whole. Only
|
||
|
|
the deleted studyset is marked; what is inside it is deleted with it by ancestry, as
|
||
|
|
archiving works.
|
||
|
|
2. **What is deleted counts nowhere.** The app's lists, counts, study sessions, the
|
||
|
|
dashboard, reading deadlines and copies' updates read the studysets without those
|
||
|
|
deleted and what is inside them; so do the server's connector, publishing and the
|
||
|
|
learner count. Restoring brings it all back as it was, progress included.
|
||
|
|
3. **The library's Recently deleted lists them for 14 days** (`Studyset.keptWhenDeleted`),
|
||
|
|
the most recently deleted first, each with the days it has left, Restore, and Delete
|
||
|
|
now. Deleting no longer asks first; it offers Undo, as archiving does. Delete now asks,
|
||
|
|
since it can't be undone. A studyset deleted inside one deleted later is listed only
|
||
|
|
once the outer one is restored, and goes on its own clock.
|
||
|
|
4. **The person's devices delete them for good**, as before, blanking first: whichever
|
||
|
|
device looks next once the 14 days are over (`StudysetDeletions.deleteExpired`, as the
|
||
|
|
person's data opens and after every sync that went through). The server doesn't: it
|
||
|
|
never writes into a person's space (ADR 0005), and the sync engine (checked with
|
||
|
|
v0.0.10) stores a payload the server updates as its JSON text rather than its bytes, so
|
||
|
|
it couldn't blank one. A studyset deleted by someone who never opens the app again
|
||
|
|
therefore stays on the server, marked, like the rest of what they left, until their
|
||
|
|
account goes (pruning, or deleting it).
|
||
|
|
5. **A restore and a deletion for good racing on two devices end deleted**: existence is
|
||
|
|
the sync engine's tombstone, which a field update doesn't lift. That needs one device
|
||
|
|
restoring offline in the last moments of the 14 days while another deletes it, and is
|
||
|
|
accepted.
|
||
|
|
|
||
|
|
## Consequences
|
||
|
|
|
||
|
|
- The migration `20261012162411835-studyset-deleted-at` adds the column. Like every
|
||
|
|
change to a synced table, it locks app versions before it out of syncing until they
|
||
|
|
update (ADR 0002, rule 3); `minimumAppBuild` needn't rise.
|
||
|
|
- Deleted content stays on the server and in backups for up to 14 days longer than
|
||
|
|
before, longer for people who stop using the app. The privacy page should say so.
|
||
|
|
- Deleting a studyset still never withdraws its publication: that stays the Publish
|
||
|
|
sheet's.
|