-- Remove UNIQUE(library_id, file_path, generation_type) constraint to allow -- multiple job rows per file. This enables proper cancel/regenerate semantics: -- a new job is always inserted on regenerate, and the old job is cancelled -- independently. The application layer prevents concurrent running jobs. CREATE TABLE insight_generation_jobs_new ( id INTEGER PRIMARY KEY AUTOINCREMENT, library_id INTEGER NOT NULL DEFAULT 1, file_path TEXT NOT NULL, generation_type TEXT NOT NULL, status TEXT NOT NULL DEFAULT 'running', started_at INTEGER NOT NULL, completed_at INTEGER, result_insight_id INTEGER, error_message TEXT ); INSERT INTO insight_generation_jobs_new SELECT id, library_id, file_path, generation_type, status, started_at, completed_at, result_insight_id, error_message FROM insight_generation_jobs; DROP TABLE insight_generation_jobs; ALTER TABLE insight_generation_jobs_new RENAME TO insight_generation_jobs; CREATE INDEX idx_insight_gen_jobs_file ON insight_generation_jobs(library_id, file_path); CREATE INDEX idx_insight_gen_jobs_status_cleanup ON insight_generation_jobs(status, started_at);