`refreshDetail` did an un-transacted `deleteByPlaylist` + `upsertAll` — the
same shape as the Home index write that #2327 just fixed. Room's
InvalidationTracker fires after the DELETE, so an observer of
`observeByPlaylist` would see `emptyList()` before the new rows land, which is
exactly what made every Home row visibly collapse to empty and refill.
Nothing consumes `observeByPlaylist` today, so this is not a live defect — it's
a landmine. Making playlist detail cache-first later would have silently
reintroduced the flicker, and the reason would have been three layers away from
the symptom. One `@Transaction` now costs nothing and removes that.
`deleteByPlaylist` is left in place as the building block but is no longer
called from outside the DAO.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sheet still described the pre-M8 world: "two CI images: ci-go +
ci-flutter", a ci-flutter dep list, and cross-workflow release polling
against flutter.yml. None of that is true now — flutter.yml is gone,
android.yml and release.yml both pull ci-android:36, and image-release
gates on `needs: [android-release]` instead of polling.
Family rule 39 makes this sheet CI-Runner's decision input for "add a dep
to an image vs. fork a variant", so a stale sheet quietly misinforms that
call: CI-Runner was still carrying ci-flutter for a consumer that no
longer exists, and had no record of ci-android's real consumer.
- Runtime images: ci-flutter:3.44 -> ci-android:36, with a note on why
ci-flutter is now unconsumed and what would have to change to revive it.
- Image deps: replace the Flutter/Dart/NDK list with the actual
ci-android surface (JDK 25 + Gradle 9.1 floor, SDK/build-tools 36, no
NDK, ktlint + detekt).
- Label/image split: record that Android jobs still schedule on the
flutter-ci label on purpose — it's a scheduling handle, not a toolchain
assertion.
- Update channel: `needs:` gating, plus the non-tag rebundle path.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>