-- Additive utility-local schema. Migration content is fingerprinted at application time. CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, filename TEXT NOT NULL UNIQUE, checksum TEXT NOT NULL, applied_at TEXT NOT NULL ); CREATE TABLE blobs ( sha256 TEXT PRIMARY KEY CHECK(length(sha256) = 64), byte_size INTEGER NOT NULL CHECK(byte_size > 0), media_type TEXT NOT NULL CHECK(length(media_type) <= 100), created_at TEXT NOT NULL ); CREATE TABLE captures ( capture_id TEXT PRIMARY KEY, blob_sha256 TEXT NOT NULL REFERENCES blobs(sha256) ON DELETE RESTRICT, state TEXT NOT NULL, received_at TEXT NOT NULL, receipt_token TEXT NOT NULL UNIQUE, failure_reason TEXT, revision INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE jobs ( job_id TEXT PRIMARY KEY, capture_id TEXT REFERENCES captures(capture_id) ON DELETE RESTRICT, kind TEXT NOT NULL CHECK(length(kind) <= 100), state TEXT NOT NULL, attempt INTEGER NOT NULL DEFAULT 0 CHECK(attempt >= 0 AND attempt <= 20), max_attempts INTEGER NOT NULL CHECK(max_attempts BETWEEN 1 AND 20), lease_owner TEXT, lease_expires_at TEXT, last_error_id TEXT, revision INTEGER NOT NULL DEFAULT 0 ); CREATE INDEX jobs_available ON jobs(state, lease_expires_at); CREATE TABLE ocr_runs ( run_id TEXT PRIMARY KEY, capture_id TEXT NOT NULL REFERENCES captures(capture_id), engine_id TEXT NOT NULL, engine_version TEXT NOT NULL, created_at TEXT NOT NULL, state TEXT NOT NULL, payload_json TEXT NOT NULL CHECK(length(payload_json) <= 1000000) ); CREATE TABLE regions ( region_id TEXT PRIMARY KEY, run_id TEXT NOT NULL REFERENCES ocr_runs(run_id), region_json TEXT NOT NULL CHECK(length(region_json) <= 10000), created_at TEXT NOT NULL ); CREATE TABLE classifications ( classification_id TEXT PRIMARY KEY, run_id TEXT NOT NULL REFERENCES ocr_runs(run_id), classification_json TEXT NOT NULL CHECK(length(classification_json) <= 10000), created_at TEXT NOT NULL ); CREATE TABLE extraction_candidates ( candidate_id TEXT PRIMARY KEY, run_id TEXT NOT NULL REFERENCES ocr_runs(run_id), candidate_json TEXT NOT NULL CHECK(length(candidate_json) <= 1000000), created_at TEXT NOT NULL ); CREATE TABLE routing_decisions ( decision_id TEXT PRIMARY KEY, candidate_id TEXT REFERENCES extraction_candidates(candidate_id), decision_json TEXT NOT NULL CHECK(length(decision_json) <= 10000), created_at TEXT NOT NULL ); CREATE TABLE review_events ( event_id TEXT PRIMARY KEY, candidate_id TEXT REFERENCES extraction_candidates(candidate_id), event_json TEXT NOT NULL CHECK(length(event_json) <= 10000), created_at TEXT NOT NULL ); CREATE TABLE lineage ( lineage_id TEXT PRIMARY KEY, child_id TEXT NOT NULL, parent_id TEXT NOT NULL, relation TEXT NOT NULL CHECK(length(relation) <= 100), created_at TEXT NOT NULL ); CREATE TABLE exports ( export_id TEXT PRIMARY KEY, state TEXT NOT NULL, manifest_json TEXT NOT NULL CHECK(length(manifest_json) <= 1000000), created_at TEXT NOT NULL, revision INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE runtime_failures ( failure_id TEXT PRIMARY KEY, subject_type TEXT NOT NULL, subject_id TEXT NOT NULL, code TEXT NOT NULL, message TEXT NOT NULL CHECK(length(message) <= 512), created_at TEXT NOT NULL );