-- R720 agent-team durable SQLite schema (design §3.3.1, §6.7). -- -- This file holds the raw DDL statements ONLY. The authoritative copies live -- as string constants in agent_team/db/schema.py; this companion file mirrors -- them verbatim for tooling / direct inspection. SQL DDL lives only in these -- two places. -- -- The LangGraph SqliteSaver checkpointer creates its OWN tables against this -- same database file/connection; those are intentionally NOT declared here. -- pending_questions: the durable human-interaction lifecycle ledger. -- The LangGraph checkpoint holds graph state; this table holds the question -- lifecycle (delivery, duplicate/late answers, expiry) and is what delivery, -- the responder, and restart recovery read. Every race resolves via an atomic -- compare-and-set against the `status` column under BEGIN IMMEDIATE. CREATE TABLE IF NOT EXISTS pending_questions ( question_id TEXT PRIMARY KEY, thread_id TEXT NOT NULL, turn INTEGER NOT NULL, status TEXT NOT NULL CHECK (status IN ('open', 'answered', 'expired', 'superseded')), transport TEXT NOT NULL, channel_ref TEXT, posted_at TEXT, deadline_at TEXT, answer_json TEXT, answered_at TEXT, answered_via TEXT ); CREATE INDEX IF NOT EXISTS idx_pending_questions_thread ON pending_questions (thread_id, turn); CREATE INDEX IF NOT EXISTS idx_pending_questions_status ON pending_questions (status); -- Defense-in-depth: at most one OPEN question may carry a given non-null -- channel_ref, so a thread reply's thread_ts can never map to two open rows -- (a partial unique index; NULL channel_refs and closed rows are excluded). CREATE UNIQUE INDEX IF NOT EXISTS uq_pending_questions_open_channel_ref ON pending_questions (channel_ref) WHERE channel_ref IS NOT NULL AND status = 'open'; -- budget_ledger: the persistent shared Claude budget ledger (§6.1, §6.6). -- One row per accounted spend event; the shared daily cap and the -- interactive-first reserve are computed by summing over a UTC day. Spend is -- recorded across ALL R720 Claude work (pipeline + Plane-1 sweeps). CREATE TABLE IF NOT EXISTS budget_ledger ( entry_id INTEGER PRIMARY KEY AUTOINCREMENT, thread_id TEXT, stage TEXT, model TEXT NOT NULL, billing_mode TEXT NOT NULL, input_tokens INTEGER NOT NULL DEFAULT 0, output_tokens INTEGER NOT NULL DEFAULT 0, usd_cost REAL NOT NULL DEFAULT 0.0, recorded_at TEXT NOT NULL, day_bucket TEXT NOT NULL ); CREATE INDEX IF NOT EXISTS idx_budget_ledger_day ON budget_ledger (day_bucket); CREATE INDEX IF NOT EXISTS idx_budget_ledger_thread ON budget_ledger (thread_id); -- ingested_issues (schema v2): durable de-dup ledger for the GitHub-issue intake -- poller. The in-memory poller set does not survive a restart, so a scheduled -- intake (each run a fresh process) would re-ingest every still-open labeled -- issue and spawn duplicate tasks. This table records, per (source, issue_id), -- the issues already turned into tasks so intake is idempotent across restarts. -- The box is read-only (no write token to remove the intake label), so durable -- de-dup is the only correct guard. `source` namespaces by repo (e.g. -- "github:owner/repo") so per-repo issue numbers from different repos cannot collide. CREATE TABLE IF NOT EXISTS ingested_issues ( source TEXT NOT NULL, issue_id TEXT NOT NULL, ingested_at TEXT NOT NULL, PRIMARY KEY (source, issue_id) ); -- schema_meta: single-row table recording the applied schema version so -- migrate() can detect and step forward. CREATE TABLE IF NOT EXISTS schema_meta ( id INTEGER PRIMARY KEY CHECK (id = 1), schema_version INTEGER NOT NULL );