feat(server): archived and starred studysets (ADR 0004)
All checks were successful
CI / check (push) Successful in 4m57s
CI / deploy (push) Has been skipped

studyset gains archivedAt (archived studysets rest in their own section and are left out
of studying, with everything below them) and starredAt (kept at the top of the
library), both nullable. Columns rather than payload fields, for the same reason as the
parent: starring on one device and renaming on another must not overwrite each other.

The branch's migration wasn't applied anywhere yet (tests build fresh databases, and
the branch isn't merged), so it is recreated with the two columns instead of adding a
second one: 20260930213716558-tree-tags-assets replaces 20260930211154224, and differs
from it only by the two ALTER TABLE lines and its version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-09-30 23:38:43 +02:00
commit b90fe10dd4
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -1,4 +1,4 @@
# ADR 0004: Studyset tree, tags, review modes, settings and assets, in one migration
# ADR 0004: Studyset tree and state, tags, review modes, settings and assets, in one migration
- **Status:** accepted
- **Date:** 2026-09-30
@ -9,7 +9,8 @@ The features decided for the first public release need data the synced tables do
have yet:
- a studyset tree (studysets inside studysets, studied one at a time or as a whole
subtree);
subtree), and studysets that can be archived (paused, in a section of their own) or
starred (kept at the top of the library);
- tags on cards, to study and filter by;
- study modes beyond the schedule: reversed cards (back to front) and cramming;
- study goals on a home dashboard, which should follow a person to every device;
@ -18,7 +19,7 @@ have yet:
Every migration of a synced table locks older app versions out of syncing (ADR 0002,
rule 3). Before the first release that costs nothing; after it, every device has to
update before it syncs again. So all of this comes in one migration now,
`20260930211154224-tree-tags-assets`, and later content fields go into the versioned
`20260930213716558-tree-tags-assets`, and later content fields go into the versioned
payloads instead. New card types (multiple choice, vocabulary, drag-and-drop, map
questions) need no migration at all: they are the next schema version of the card
revision's payload, and the per-side merge of ADR 0003 becomes per field.
@ -32,6 +33,10 @@ revision's payload, and the per-side merge of ADR 0003 becomes per field.
make a cycle, and a studyset can lose its parent to a deletion on another device;
the app shows the studysets caught either way at the top level until someone moves
them. Existing studysets start at the top level with position `a0`; ties sort by id.
`studyset.archivedAt` and `studyset.starredAt` record when a studyset was archived or
starred (null when not). They are columns rather than payload fields for the same
reason as the parent: starring on one device and renaming on another must not
overwrite each other.
2. **Tags are rows, and so are their assignments.** `tag` holds a name in its payload
(content, like a studyset's title); `card_tag` links a card and a tag, one row per
assignment, never updated. Removing a tag deletes one row, and adding it on another
@ -58,9 +63,10 @@ revision's payload, and the per-side merge of ADR 0003 becomes per field.
## Consequences
- The server migration only adds: four tables, three columns with defaults, indexes,
and foreign keys (deferred where sync inserts rows in any order). The running server
keeps working while it is applied, before the new server replaces it.
- The server migration only adds: four tables, three columns with defaults and two
nullable ones, indexes, and foreign keys (deferred where sync inserts rows in any
order). The running server keeps working while it is applied, before the new server
replaces it.
- On devices, SQLite can't add a foreign key to a table, so the migration rebuilds
`studyset` and `review_log` (a new table, the rows copied, the old one dropped).
Dropping them with foreign keys enforced would cascade into cards and reviews or fail;