-- SRS schema. D1 owns SRS state (stage, next_review, attempts, gates, -- boosts); GitHub issues own the catalog columns, reconciled nightly. CREATE TABLE problems ( lc_number INTEGER PRIMARY KEY, issue INTEGER NOT NULL, topic_issue INTEGER NOT NULL, title TEXT NOT NULL, difficulty TEXT NOT NULL, -- easy|medium|hard set_label TEXT NOT NULL, -- core|optional|deferred stage TEXT NOT NULL DEFAULT 'new', -- new|+2|+5|+10|retired next_review TEXT, -- YYYY-MM-DD ET, NULL when retired/unsolved defer_until TEXT -- deferred Hards: 2026-09-28+ ); CREATE TABLE attempts ( id INTEGER PRIMARY KEY AUTOINCREMENT, lc_number INTEGER NOT NULL REFERENCES problems(lc_number), date TEXT NOT NULL, kind TEXT NOT NULL, -- first|review|drill|gate result TEXT NOT NULL, -- pass|fail source TEXT NOT NULL -- email|webhook|import ); -- One-tap email links must be idempotent (same link twice = no-op), but -- /done webhook corrections (pass then fail, same day) must stay legal — -- so uniqueness applies to email-sourced attempts only. CREATE UNIQUE INDEX attempts_email_once ON attempts (lc_number, date, kind) WHERE source = 'email'; CREATE INDEX attempts_by_date ON attempts (date); CREATE TABLE topics ( issue INTEGER PRIMARY KEY, name TEXT NOT NULL, misses INTEGER NOT NULL DEFAULT 0, boost INTEGER NOT NULL DEFAULT 0, milestone INTEGER -- phase; from the topic issue ); CREATE TABLE gates ( week INTEGER PRIMARY KEY, -- ISO week issue INTEGER, problems TEXT NOT NULL, -- JSON array of lc_numbers pass_rate REAL, closed_on TEXT ); CREATE TABLE drill_pool_used (lc_number INTEGER PRIMARY KEY); -- Digest idempotency: one email per ET date. CREATE TABLE email_log ( date TEXT PRIMARY KEY, -- YYYY-MM-DD ET sent_at TEXT NOT NULL );