InterferencePattern
## What to build There are two partial indexes on `media_items` sharing the same `WHERE NOT (media_filepath IS NULL)` predicate and leading column: - `media_items_cullable_index` on `(source_id, media_downloaded_at)` - `idx_media_items_downloaded_agg` on `(source_id, media_size_bytes)` This overlap is what confused the planner and forced the manual `ANALYZE` band-aid. Replace both with a single covering index `(source_id, media_downloaded_at, media_size_bytes) WHERE NOT (media_filepath IS NULL)`, which serves the retention range scan (on the `(source_id, media_downloaded_at)` prefix) and the downloaded count/size aggregation (index-only, all columns present). Use the EXPLAIN output from #9 to confirm both queries still get index-only plans before dropping the old indexes. ## Acceptance criteria - [ ] Migration creates the consolidated covering index and drops the two old ones - [ ] `EXPLAIN QUERY PLAN` confirms the retention query and the downloaded aggregation both use the new index (no full scan) - [ ] Write path no longer maintains the two redundant indexes - [ ] Migration is reversible (down restores prior indexes) ## Blocked by - #9 (use its EXPLAIN output to confirm index-only plans)