# Data Layer Audit Fixes — May 20, 2026

Session: Principal Android Engineer review of Room/OkHttp data layer based on Kimi K2.6 audit findings.

## Files Changed

| File | Change |
|------|--------|
| `data/local/entity/WatchProgress.kt` | Added composite index `(profileId, isWatched, lastPlayedAt)` |
| `data/local/entity/WatchlistItem.kt` | Added composite index `(profileId, addedAt)` |
| `data/local/NexStreamDatabase.kt` | Bumped v2→v3, added MIGRATION_2_3 (2 x CREATE INDEX IF NOT EXISTS) |
| `data/local/LocalDataModule.kt` | Registered MIGRATION_2_3 in `.addMigrations()` |
| `data/addon/CacheManager.kt` | Added `indices = [Index(value = ["timestamp"])]` on CachedResponseEntity. Rewrote `pruneExpired(now: Long)` to `pruneExpired(cutoff: Long)` with `WHERE timestamp < :cutoff`. Bumped AddonCacheDatabase v2→v3 with MIGRATION_2_3. Added `import androidx.room.Index`. Updated doc comments. |
| `data/tmdb/TmdbRateLimitInterceptor.kt` | **New file.** Sliding-window OkHttp interceptor. 40 req/10s default. `ConcurrentLinkedDeque<Long>` for timestamps. Sleeps when at capacity. Honors `Retry-After` on 429. |
| `data/tmdb/TmdbModule.kt` | Wired `TmdbRateLimitInterceptor()` as first application interceptor in `provideTmdbOkHttpClient()` — placed before logging. |
| `data/addon/StremioAddonClientImpl.kt` | `runCatchingWithTimeout()`: changed `catch (e: Exception)` to three narrow arms: `IOException`, `HttpException`, `SerializationException`. Updated doc comment. |

## Audit Findings Addressed

1. **DB index missing on `getContinueWatching`** — full table scan on `watch_progress` with `WHERE profileId=? AND isWatched=0 ORDER BY lastPlayedAt DESC`. Fix: composite index `(profileId, isWatched, lastPlayedAt)`.

2. **DB index missing on `getByProfile`** — full table scan on `watchlist` with `ORDER BY addedAt DESC`. Fix: composite index `(profileId, addedAt)`.

3. **Arithmetic query prevents index usage** — `DELETE FROM addon_cache WHERE (:now - timestamp) > ttl` cannot use index on `timestamp`. Fix: pre-compute `cutoff = now - 24h` in Kotlin, use `WHERE timestamp < :cutoff`.

4. **No rate limiting on TMDB API** — risks 429 bans. Fix: `TmdbRateLimitInterceptor` with sliding-window tracking and Retry-After handling.

5. **Broad `catch (e: Exception)` swallows programming errors** — `StremioAddonClientImpl.runCatchingWithTimeout` caught everything including NPE, ClassCastException. Fix: narrow catch to `IOException`, `HttpException`, `SerializationException`.

## Build Result

`compileDebugKotlin`: **0 errors, 0 warnings.**

## Boundary Compliance

All changes are strictly data layer: Room entities/DAOs/migrations, OkHttp interceptors, Retrofit client exception handling. No ViewModels, Composables, or UI code touched.
