diff --git a/CHANGELOG.md b/CHANGELOG.md index 2514dd1..c2145e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,14 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - nightly -## [1.7.16] - 2026-06-02 +## [1.8.0] - 2026-06-04 -A playback/reliability patch: an iOS audio-session fix, a Vibe search Redis fix, and the first phase of the Discover Weekly correctness rework. +iOS playback reliability rebuilt from a stable baseline, Vibe search hardened against Redis pressure, the first phase of the Discover Weekly correctness rework, and a dependency pin that unbreaks the build. + +### Added + +- **iOS audio diagnostics**: the installed iOS PWA keeps a small rolling log of audio events on your own server to make playback bug reports actionable. Bounded and on-device; groundwork for a forthcoming general support-bundle export. ### Fixed -- **iOS earbud / lock-screen resume produced no audio and lost the audio session to another app**: the MediaSession "play" action called `controller.play()`, which `await`ed the AudioContext bridge **before** `audio.play()`. That await forfeited the iOS user-activation token from the earbud click, so an interrupted/suspended AudioContext never resumed -- and `play()` then *returned* (not threw) on a non-running context, so the handler's `reloadAndPlay()` fallback never ran. The result: earbud-click resume did nothing, no native `playing` event fired, `playbackState` never updated, and after a few clicks iOS reassigned the session to the next app. Added `resumeFromGesture()`, which fires the context resume **without awaiting it**, calls `audio.play()` synchronously in the gesture (mirroring the proven track-advance path), and reloads the source on any failure instead of a silent prompt. Wired only into the explicit "play" action, so it cannot auto-resume on an ambiguous pause/route-change (the v1.7.12 earbud-unplug-through-speaker regression stays fixed). Installed iOS PWA only. -- **Vibe text search crashed the request with "Stream isn't writeable" (#197)**: the text-embedding Redis subscriber duplicated the shared client, inheriting `enableOfflineQueue:false` + `maxRetriesPerRequest:0`, so `psubscribe` threw when the subscriber socket wasn't connected yet -- and the rejected promise was cached, breaking Vibe search permanently until restart (worse on large libraries). The subscriber now gets its own offline queue + retries, clears the cached promise on rejection, and reconnects on disconnect. +- **iOS earbud / lock-screen resume produced no audio and lost the session to another app**: three successive patches on the iOS AudioContext backgrounding bridge had compounded into a regression -- the worst awaited the context resume before `audio.play()` and returned early on a non-running context, so an earbud/lock-screen resume did nothing and, after a few attempts, iOS handed the audio session to the next app (a sleep-sounds app would start playing). The three patches were reverted to the proven bridge baseline (resume the context in parallel, always attempt `audio.play()`, gesture preserved), and the real gaps were fixed on top: the "playback" audio-session category is re-claimed on every explicit resume (not just the first), so iOS can't leave it with an app that grabbed it during an interruption, and an AudioContext `statechange` listener re-claims it when the OS ends an interruption. No background auto-resume was added, so the v1.7.12 earbud-unplug-through-speaker regression stays fixed. Installed iOS PWA only. +- **Vibe text search crashed with "Stream isn't writeable" under load (#197)**: the text-embedding Redis bridge inherited the shared client's fail-fast options (`enableOfflineQueue:false`), so it threw whenever a connection was mid-reconnect, and a rejected promise was cached -- breaking Vibe search until restart. The first fix hardened only the subscriber; this hardens the whole path: the publisher uses its own buffered connection, dead connections are cleaned up instead of leaking or silently dropping responses, subscribe and publish are time-bounded so an unreachable Redis fails fast instead of hanging, analyzer failures are rejected cleanly instead of 500ing, and the analyzer reports internal errors immediately. At very large libraries this can also be driven by Redis memory pressure, which the hardening tolerates but does not itself resolve. +- **Release build broke on a `transformers` update**: `transformers` was unpinned and resolved to a version that needs a newer `torch` than the pinned `torch==2.5.1` (it references `torch.float8_e8m0fnu`, added in torch 2.7), failing the analyzer image build. Pinned to `transformers==5.8.1`, the version proven against torch 2.5.1. - **Discover Weekly never showed (and silently auto-deleted) the generated playlist -- Phase 1 of the rework**: the Sunday cron tagged records with the *ending* week, so `GET /current` (which looked for the current week with exact equality) found nothing, and the next run's cleanup then deleted the invisible records. This phase fixes the core correctness path (no schema migration): generation now tags the upcoming week via a centralized week helper; the cron moves to Monday 05:00 with a dedup key aligned to the batch week; `/current` resolves from the latest completed batch (bounded, with a `stale` flag) so drifted records are visible; `buildFinalPlaylist` marks the batch failed on a transaction error instead of hanging in `scanning`; retry-unavailable routes through the shared completion flow so retried albums actually enter the playlist; cancelling a batch marks it `failed` (not a false "successful empty week"); album deletes are wrapped in transactions with an atomic claim guard so a concurrent "like" can't lose its album (and `/like` is symmetric); a same-`rgMbid` `LIBRARY` album is never deleted; and the BullMQ job hash is cleared before re-enqueue so a retry after a failure isn't silently dropped. `TZ=UTC` is pinned for deterministic week math. ## [1.7.15] - 2026-06-01 diff --git a/backend/package.json b/backend/package.json index d4b5e78..ad4ac41 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "kima-backend", - "version": "1.7.16", + "version": "1.8.0", "description": "Kima backend API server", "license": "GPL-3.0", "repository": { diff --git a/frontend/package.json b/frontend/package.json index 46dda55..4aa173d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "kima-frontend", - "version": "1.7.16", + "version": "1.8.0", "description": "Kima web frontend", "license": "GPL-3.0", "repository": {