PRAGMA foreign_keys = OFF; BEGIN; DROP INDEX IF EXISTS idx_processed_tickets_tracker_artifact_unique; CREATE TABLE processed_tickets_new ( id TEXT PRIMARY KEY, tracker_id TEXT REFERENCES watched_trackers(id) ON DELETE CASCADE, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE, source TEXT NOT NULL DEFAULT 'tuleap', source_ref TEXT, artifact_id INTEGER NOT NULL, artifact_title TEXT NOT NULL, artifact_data TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'Pending', analyst_report TEXT, developer_report TEXT, worktree_path TEXT, branch_name TEXT, detected_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), processed_at TEXT ); INSERT INTO processed_tickets_new ( id, tracker_id, project_id, source, source_ref, artifact_id, artifact_title, artifact_data, status, analyst_report, developer_report, worktree_path, branch_name, detected_at, processed_at ) SELECT pt.id, pt.tracker_id, wt.project_id, 'tuleap', NULL, pt.artifact_id, pt.artifact_title, pt.artifact_data, pt.status, pt.analyst_report, pt.developer_report, pt.worktree_path, pt.branch_name, pt.detected_at, pt.processed_at FROM processed_tickets pt LEFT JOIN watched_trackers wt ON wt.id = pt.tracker_id; DROP TABLE processed_tickets; ALTER TABLE processed_tickets_new RENAME TO processed_tickets; CREATE UNIQUE INDEX idx_processed_tickets_tracker_artifact_unique ON processed_tickets(tracker_id, artifact_id) WHERE tracker_id IS NOT NULL; CREATE INDEX idx_processed_tickets_project_detected ON processed_tickets(project_id, detected_at DESC); CREATE INDEX idx_processed_tickets_source_ref ON processed_tickets(source, source_ref); CREATE TABLE graylog_credentials ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE, base_url TEXT NOT NULL, api_token_encrypted TEXT NOT NULL, analyst_agent_id TEXT NOT NULL REFERENCES agents(id), developer_agent_id TEXT NOT NULL REFERENCES agents(id), stream_id TEXT, query_filter TEXT NOT NULL DEFAULT '', polling_interval_minutes INTEGER NOT NULL DEFAULT 10, lookback_minutes INTEGER NOT NULL DEFAULT 30, score_threshold INTEGER NOT NULL DEFAULT 70, created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); CREATE TABLE graylog_subjects ( id TEXT PRIMARY KEY, project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE, subject_key TEXT NOT NULL, source TEXT NOT NULL, normalized_message TEXT NOT NULL, first_seen_at TEXT NOT NULL, last_seen_at TEXT NOT NULL, last_score INTEGER NOT NULL DEFAULT 0, active_ticket_id TEXT REFERENCES processed_tickets(id), status TEXT NOT NULL DEFAULT 'idle', created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), UNIQUE(project_id, subject_key) ); CREATE TABLE graylog_detections ( id TEXT PRIMARY KEY, subject_id TEXT NOT NULL REFERENCES graylog_subjects(id) ON DELETE CASCADE, window_start TEXT NOT NULL, window_end TEXT NOT NULL, critical_count INTEGER NOT NULL DEFAULT 0, error_count INTEGER NOT NULL DEFAULT 0, warning_count INTEGER NOT NULL DEFAULT 0, total_count INTEGER NOT NULL DEFAULT 0, last_seen_at TEXT NOT NULL, score INTEGER NOT NULL, triggered INTEGER NOT NULL DEFAULT 0, triggered_ticket_id TEXT REFERENCES processed_tickets(id), created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) ); CREATE INDEX idx_graylog_detections_subject_created ON graylog_detections(subject_id, created_at DESC); COMMIT; PRAGMA foreign_keys = ON;