cf7b489f — fix(playlists): make the playlist-track replace atomic. Issue #2365, arising from the #2327 audit.
refreshDetail did deleteByPlaylist + upsertAll un-transacted — the same shape that made every Home row visibly collapse to empty and refill, because Room's InvalidationTracker notifies after the DELETE commits. Not a live defect: nothing consumes observeByPlaylist, so no Flow was watching the gap. It's a landmine — making playlist detail cache-first (a plausible next step, cf. #619) would have reintroduced Home's exact flicker with the cause three layers from the symptom. Now one @Transaction, mirroring CachedHomeIndexDao.replaceSections.
Audited the rest of the cache layer while there: CachedLike, CachedQuarantine and CachedPlaylist already had @Transaction; the others do per-row rather than replace-all writes, so they have no observable gap. Home was the outlier, playlist-tracks was the last latent case.
8483948f — docs(ci): true up ci-requirements.md. Pre-existing on dev, not mine. Docs-only.
Two small commits, no release cut.
**`cf7b489f` — fix(playlists): make the playlist-track replace atomic.** Issue #2365, arising from the #2327 audit.
`refreshDetail` did `deleteByPlaylist` + `upsertAll` un-transacted — the same shape that made every Home row visibly collapse to empty and refill, because Room's InvalidationTracker notifies after the DELETE commits. **Not a live defect:** nothing consumes `observeByPlaylist`, so no Flow was watching the gap. It's a landmine — making playlist detail cache-first (a plausible next step, cf. #619) would have reintroduced Home's exact flicker with the cause three layers from the symptom. Now one `@Transaction`, mirroring `CachedHomeIndexDao.replaceSections`.
Audited the rest of the cache layer while there: `CachedLike`, `CachedQuarantine` and `CachedPlaylist` already had `@Transaction`; the others do per-row rather than replace-all writes, so they have no observable gap. Home was the outlier, playlist-tracks was the last latent case.
**`8483948f` — docs(ci): true up ci-requirements.md.** Pre-existing on `dev`, not mine. Docs-only.
`dev` CI green on `cf7b489f` (run 3210).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
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>
`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>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Two small commits, no release cut.
cf7b489f— fix(playlists): make the playlist-track replace atomic. Issue #2365, arising from the #2327 audit.refreshDetaildiddeleteByPlaylist+upsertAllun-transacted — the same shape that made every Home row visibly collapse to empty and refill, because Room's InvalidationTracker notifies after the DELETE commits. Not a live defect: nothing consumesobserveByPlaylist, so no Flow was watching the gap. It's a landmine — making playlist detail cache-first (a plausible next step, cf. #619) would have reintroduced Home's exact flicker with the cause three layers from the symptom. Now one@Transaction, mirroringCachedHomeIndexDao.replaceSections.Audited the rest of the cache layer while there:
CachedLike,CachedQuarantineandCachedPlaylistalready had@Transaction; the others do per-row rather than replace-all writes, so they have no observable gap. Home was the outlier, playlist-tracks was the last latent case.8483948f— docs(ci): true up ci-requirements.md. Pre-existing ondev, not mine. Docs-only.devCI green oncf7b489f(run 3210).🤖 Generated with Claude Code