-- Email Agent schema (AGENTS.md §7). -- -- Every statement is idempotent (CREATE ... IF NOT EXISTS) so this file can be -- executed on every start. Retention/capping jobs belong to later phases. -- -- Attribution: created for the Email Agent Phase 1 skeleton. PRAGMA foreign_keys = ON; -- Records the applied schema version for future migrations. CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, applied_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- FR-1, FR-2: mail metadata and body, plus the forwarding address it arrived at. CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, gmail_id TEXT NOT NULL UNIQUE, thread_id TEXT, from_addr TEXT, to_addr TEXT, subject TEXT, body_text TEXT, source_address TEXT, date_sent TEXT, -- IMAP bookkeeping (AGENTS.md §9): the mailbox UID and the UIDVALIDITY it -- belongs to. UIDs are only meaningful while UIDVALIDITY is unchanged. imap_uid INTEGER, imap_uidvalidity INTEGER, received_at TEXT NOT NULL DEFAULT (datetime('now')), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_messages_received_at ON messages (received_at); CREATE INDEX IF NOT EXISTS idx_messages_thread_id ON messages (thread_id); CREATE INDEX IF NOT EXISTS idx_messages_source ON messages (source_address); -- FR-14: the editable category list. `name` is the stable key stored in -- classifications.category and sent to JEV as the Choice option name. Renaming a -- category cascades to existing classifications (see store.RenameCategory). CREATE TABLE IF NOT EXISTS categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL COLLATE NOCASE UNIQUE, description TEXT NOT NULL DEFAULT '', position INTEGER NOT NULL DEFAULT 0, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_categories_position ON categories (position, name); -- FR-3, FR-14: classifier output per message. CREATE TABLE IF NOT EXISTS classifications ( id INTEGER PRIMARY KEY AUTOINCREMENT, message_id INTEGER NOT NULL REFERENCES messages (id) ON DELETE CASCADE, category TEXT NOT NULL, importance TEXT, action TEXT, confidence REAL, model TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_classifications_message ON classifications (message_id); -- FR-4, FR-11: versioned rules text. CREATE TABLE IF NOT EXISTS rules ( id INTEGER PRIMARY KEY AUTOINCREMENT, text TEXT NOT NULL, version INTEGER NOT NULL, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- FR-13: append-only action log. CREATE TABLE IF NOT EXISTS actions_log ( id INTEGER PRIMARY KEY AUTOINCREMENT, message_id INTEGER REFERENCES messages (id) ON DELETE SET NULL, action TEXT NOT NULL, actor TEXT, rule_id INTEGER REFERENCES rules (id) ON DELETE SET NULL, detail TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_actions_log_message ON actions_log (message_id); CREATE INDEX IF NOT EXISTS idx_actions_log_created ON actions_log (created_at); -- FR-7: unsubscribe tracker and ignore list. CREATE TABLE IF NOT EXISTS unsubscribe ( id INTEGER PRIMARY KEY AUTOINCREMENT, domain TEXT NOT NULL UNIQUE, status TEXT NOT NULL DEFAULT 'done' CHECK (status IN ('done', 'ignored')), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); -- FR-8: reply drafts. CREATE TABLE IF NOT EXISTS drafts ( id INTEGER PRIMARY KEY AUTOINCREMENT, message_id INTEGER NOT NULL REFERENCES messages (id) ON DELETE CASCADE, body TEXT NOT NULL, template_id TEXT, status TEXT NOT NULL DEFAULT 'draft', created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_drafts_message ON drafts (message_id); -- FR-10: daily summary and its delivery target. CREATE TABLE IF NOT EXISTS summaries ( id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL UNIQUE, text TEXT NOT NULL, delivered_to TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')) ); INSERT OR IGNORE INTO schema_migrations (version) VALUES (1);