Lexemes before exercises
The core data model separates a word's lexical identity from its inflected forms and from the tasks generated around it. The current schema includes lexemes, inflections, task_specs, and practice_history, with part-of-speech-specific source data for verbs, nouns, adjectives, adverbs, prepositions, and other categories.
This avoids coupling the content store to one exercise type. A shared registry describes task types, while the client renders practice by taskType and the API serves POS-aware task descriptors.
Content-to-practice flow
Offline practice without a second application model
The web client is installable as a Progressive Web App. Recent task responses are cached for offline reuse, while practice attempts made without a usable network are stored in an IndexedDB queue through Dexie.
When connectivity returns, the sync hook flushes queued attempts to the normal submission endpoint. Each device carries a persistent device ID, so the same practice-history model is used whether an attempt was submitted immediately or replayed later.
Content provenance and enrichment
Language data is not treated as a one-off seed script. The enrichment pipeline can inspect incomplete entries, collect structured material from sources such as Wiktextract/Kaikki, OpenThesaurus, MyMemory and Tatoeba, optionally use an LLM for missing fields, and retain provider snapshots for later replay.
Updates are separated from collection: suggestions can be generated without mutating the canonical database, reviewed, backed up, and then applied. Attribution metadata is stored alongside generated task content where required.
Identity across clients
Supabase Auth is used so the web application and Android client can resolve the same user identity and preserve shared practice history. The web project also includes optional Microsoft OAuth support and server-side verification of web auth tokens.
Testable database and content pipelines
Tests do not require a shared development database. The test harness can run the real Drizzle migrations against an isolated in-memory Postgres implementation, seed controlled data, execute API tests, and tear the schema down again. A real Postgres instance can be substituted when needed.
Generated content also has its own checks: task-pack linting, type checking, unit/integration tests, environment validation, and build-time checks reduce the chance that content or schema changes silently produce invalid practice tasks.