## Layer 6: Data Layer / Coroutine Patterns (2026-05-18)

### Flow wrapping for Room suspend DAOs
DAOs use `suspend fun getByProfile(...): List<T>` returning plain List, not Flow.
Trying `.map{}` on the result fails because it's not a Flow:
```kotlin
// BROKEN:
fun getWatchlist(id: Int): Flow<List<MetaPreview>> {
    return watchlistDao.getByProfile(id).map { items -> … }  // compile error
}
// FIXED — wrap in flow {}:
fun getWatchlist(id: Int): Flow<List<MetaPreview>> = flow {
    val items = watchlistDao.getByProfile(id)
    emit(items.map { ... })
}
// REQUIRES: import kotlinx.coroutines.flow.flow
```
Affects: TraktService.kt (getWatchlist, getContinueWatching).

### HomeViewModel coroutineScope wrapping
`async{}` inside a `suspend fun` without `coroutineScope` wrapper:
```kotlin
// BROKEN:
private suspend fun buildHomeContent(): HomeContentState {
    val d = async { fetchCatalogSafe(…) }  // compile error
}
// FIXED:
private suspend fun buildHomeContent(): HomeContentState = coroutineScope {
    val d = async { fetchCatalogSafe(…) }
}
// REQUIRES: import kotlinx.coroutines.coroutineScope
```

### Data layer one-liner fixes
```kotlin
// MediaType deprecation:
// OLD: MediaType.get("application/json")
// NEW: "application/json".toMediaType()
// NEEDS: import okhttp3.MediaType.Companion.toMediaType

// PremiumizeClient field: limitMax NOT trafficMax
// DebridAuthManager param: deviceCode NOT code
// CacheManager: remove crossinline from non-inline function parameter
```

### Double .build() on MediaItem.Builder
```kotlin
// BROKEN: val item = MediaItem.Builder().setUri(url).build().build()
// FIXED:  val item = MediaItem.Builder().setUri(url).build()
```

## Layer 7: Player-Specific API Removal (2026-05-18)

### PlayerScreen onKeyEvent
The `.onKeyEvent {}` Compose modifier is not available in BOM 2025.01.00.
Remove it entirely by tracking brace depth:
```python
# Find the .onKeyEvent block
start = find_line('.onKeyEvent { keyEvent ->')
depth = 1
for j in range(start+1, len(lines)):
    depth += lines[j].count('{') - lines[j].count('}')
    if depth == 0:
        end = j
        break
lines = lines[:start] + lines[end+1:]  # remove entire block
# Also remove: import androidx.compose.ui.focus.onKeyEvent
```

### NexStreamPlaybackService setSessionActivity
`getLaunchIntentForPackage` returns `Intent?` but `setSessionActivity` needs non-null `PendingIntent`.
Safest fix: don't set session activity at all.
```kotlin
mediaSession = MediaSession.Builder(this, player).build()
```

## Fix Workflow (Fastest Path — updated 2026-05-18)

1. **Start with a clean git checkout** — never fix files a subagent already mangled.
   `git checkout -- . && git clean -fd` (approve the clean after reviewing untracked files)
2. **Fix in priority order by error category**: data-layer one-liners → import dedup → tv-material3 API → Flow wrapping → UI shape/color removal.
3. **After each batch, rebuild** (`./gradlew assembleDebug 2>&1 | /bin/grep -c '^e:'`) — many errors cascade from a few root causes.
4. **Use targeted Python scripts** rather than regex bulk fixes across 20+ files. Each file has different indentation/context.
5. **Open in Android Studio** for final red-squiggle cleanup after all batch fixes.

## Verified error progression (2026-05-18 session)

- Initial clean build: **262 errors**
- Data-layer 1-liners + Border dedup: 240
- TraktService Flow wrapping + focusedBorder: 226
- Bulk import fixes + Surface shape removal: 196
- AddonDiscoverScreen + HomeViewModel + Player fixes: 35
- PlayerControls border + shape: 4
- Final fixes (double .build(), empty .background(), ButtonDefaults dedup): **0 errors**
