halkyon/docs/adr/0007-recently-deleted.md
Tizian 「ティツィアーン」 4644125cc4
feat(studyset): deleted studysets wait 14 days in Recently deleted
Deleting a studyset blanked it with everything inside it once a
four-second Undo was over. It now only marks it (`studyset.deletedAt`, a
column as `archivedAt`): it leaves every list, count and session with
what is inside it and waits in the library's Recently deleted for 14
days, saying how many are left, with Restore and Delete now. Deleting no
longer asks first and offers Undo; Delete now asks. Once the 14 days are
over, whichever of the person's devices looks next (as their data opens,
after every sync) deletes it for good, blanking first as before; the
server can't blank a payload and never writes a person's space (ADR
0007). The connector, publishing and the learner count leave deleted
studysets out too.

The migration adds one nullable column: app versions before it stop
syncing until they update.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMm7usAo6q9ZZRoPkWjoxi
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-13 21:21:43 +02:00

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.