# Changelog All notable changes to JellyTau are documented here. Entries are grouped by the capability they change, not by commit. Requirement IDs in parentheses point at [docs/requirements.md](docs/requirements.md); the generated trace matrix lives in [docs/traceability.md](docs/traceability.md). For how long each fixed defect had been shipping before it was found, see [docs/defect-windows.md](docs/defect-windows.md). ## v0.8.0 A security and correctness release, from an audit of the codebase against its own requirements and against current Android/Tauri practice. Most of it is invisible in use; three things change behaviour you can see, listed first. ### ✨ Changes - **The app no longer backs its data up to your Google account.** It never should have: `allowBackup` was on by default, which sent the library catalogue and watch history off the device — and the credentials went with it in a form that could never be read again, because they are encrypted under an Android Keystore key and Keystore keys are never backed up. Restoring onto a new phone therefore produced ciphertext with no key: an authentication failure with no explanation. Backup is now off, for device-to-device transfer as well as cloud (a separate channel with the identical failure), and an unreadable credential blob is now treated as "logged out" rather than an error, so the next sign-in repairs it. (UR-012 → DR-135) - **The app no longer offers itself as an Android TV app.** (This is about the app icon on a TV device's home screen — your TV shows library is untouched.) It advertised a leanback launcher entry without any of what makes a TV app work — no D-pad focus model, no banner, and a missing touchscreen declaration that fails Play's TV validation. Launching it on a TV would have landed you in a UI you could not navigate. It can be re-declared when TV support is actually built. - **Lockscreen skip scrubs a film instead of leaving it.** While a video's audio plays in the background, the skip buttons jump 30 seconds forward and 10 seconds back, rather than advancing to the next episode. There is no "next track" inside a film, and pressing skip to re-hear a line should not eject you from what you are watching. Music is unchanged: skip still moves through the queue. (UR-040, UR-006 → DR-201) ### 🔒 Security - **The webview now runs under a Content-Security-Policy.** It had none, so any script reaching the web layer inherited the full IPC surface. `script-src` is now `'self'` with no inline or eval, and plugins and frames are refused outright. (UR-071 → DR-198) - **The webview stops undoing the network security config.** It set a blanket cleartext opt-in by hand, along with file and content access it never used — defeating the config that exists to block exactly that, and whose own comment warned against it. (UR-071 → DR-199) - **The asset protocol no longer reaches the database or the credential store.** Its scope was the whole app data directory; it is now the one subdirectory it serves. (UR-012, UR-071 → DR-198) ### 🐛 Fixes - **A credential store that could not be read is now recoverable.** The decrypt failure surfaced as a hard error rather than a logged-out state, so the app got stuck instead of offering the login screen. (UR-012 → DR-135) ### 🔧 Internal - CI now enforces the checks the contributor rules already required — `cargo fmt --check` and clippy — neither of which had ever run there. The traceability gate was also raised from 50% to 82%, a floor low enough that half the matrix could rot before it fired, and a new check fails the build on a requirement ID that no longer exists. - Twelve requirements marked "Done" carried no implementation trace at all; they are now tagged, and stale integration requirements that named a backend never built have been re-scoped to the ones that actually deliver them. Coverage moved 86% → 90%. - The Rust lint backlog is cleared (51 warnings → 0), and a flaky test that intermittently reddened CI is fixed — it was paying a cold module-transform cost inside a test body, not waiting on a timer. ## v0.7.0 ### ✨ Changes - **Native Android video is now the default.** Video decodes on the device's hardware decoder instead of the built-in web player, which is easier on the battery and lets picture-in-picture show the video rather than the app. The default had been held back deliberately since the picture defects were fixed, because returning from background audio left playback dead on that path; both blockers below are fixed and verified on a device, which is the standard this default has been held to since it last shipped early. The Settings toggle remains, now as the fallback to the web player, and an explicit choice still wins in both directions — anyone who turned it off keeps it off. (UR-003, UR-004 → DR-188) ### 🐛 Fixes - **The letterbox bars stop showing things that are no longer there.** With native video on, the padding around the picture kept whatever had last been drawn in it: the previous frame flashing on rotation, a ghost copy of the control bar stranded at the top of the screen, each new clock digit drawn over the one before it, and the sleep-timer and quality menus leaving their imprint after closing. One cause under all of it — nothing painted those bars. The window surface is opaque, and for an opaque surface Android's renderer skips clearing the damaged region and assumes the view hierarchy covers every pixel; the video view covers only the letterboxed rect, so the bars were the window background's alone to paint, and enabling compositing had cleared that background to transparent. Three earlier attempts missed because they aimed at the window's rotation animation and at video-frame retention — which is also why the artefact reproduced standing still, with no rotation involved. (UR-003, UR-066 → DR-194) - **Returning from background audio brings the picture back.** On the native path, coming back from the lockscreen left a black screen: a play overlay pinned at 0:00 and a play button that did nothing. Nothing had crashed — the transition was simply dropped. The two render paths resume by different means, and only one of them was performed: the web player reloads from its stream URL, while the native player owns no element and nothing watches that URL on its behalf, so it has to be handed the item again explicitly. It now is, at the position the audio reached. (UR-040, UR-003 → DR-196) - **Next Up stops repeating what Continue Watching already shows.** The same episode could occupy both home rows at once. (UR-023 → DR-197) ## v0.6.0 ### 🐛 Fixes - **Android native video actually shows a picture.** It shipped once as *audio with no picture* and was reverted with the compositing named as the suspect (DR-172). The compositing was not at fault; five independent defects sat between ExoPlayer and the screen, each able to produce that symptom alone. The app shell painted over the video surface through a CSS rule targeting `[data-app-shell]`, an attribute no component had ever set in any commit (DR-185). The poster/title card had no way to lift on a path that renders no `