-- §5 / §10.3 / §15.6 / §15.7: the freshness cursors and the watch model. -- -- Two cursor families per §15.7: per-event read state lives on notifications -- (added in 006); per-scope freshness lives on pr_seen and branch_chat_seen. -- They serve different jobs and are reconciled by the visit-advances-cursor -- reconciler in §15.7. CREATE TABLE pr_seen ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, rfc_slug TEXT NOT NULL, pr_number INTEGER NOT NULL, last_seen_commit_sha TEXT, last_seen_message_id INTEGER REFERENCES thread_messages(id) ON DELETE SET NULL, seen_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (user_id, rfc_slug, pr_number) ); CREATE TABLE branch_chat_seen ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, rfc_slug TEXT NOT NULL, branch_name TEXT NOT NULL, last_seen_message_id INTEGER REFERENCES thread_messages(id) ON DELETE SET NULL, seen_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE (user_id, rfc_slug, branch_name) ); -- §15.6: the watch model. Three states; auto-rules upgrade but never -- downgrade; explicit settings exempt from the 90-day decay. Per-RFC -- mute lives here as state='muted'. CREATE TABLE watches ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, rfc_slug TEXT NOT NULL, state TEXT NOT NULL CHECK (state IN ('watching', 'following', 'muted')), set_by TEXT NOT NULL CHECK (set_by IN ('auto', 'explicit')), set_at TEXT NOT NULL DEFAULT (datetime('now')), last_participation_at TEXT, -- 90-day decay key per §15.6 UNIQUE (user_id, rfc_slug) ); CREATE INDEX idx_watches_user ON watches (user_id); CREATE INDEX idx_watches_rfc ON watches (rfc_slug); CREATE INDEX idx_watches_decay ON watches (state, last_participation_at);