chore(release): v0.11.6
🏗️ Build and Test JellyTau / Run Tests (push) Skipped
🏗️ Build and Test JellyTau / Android Compile Check (push) Skipped
Build & Release / Create Release (push) Blocked by required conditions
🏗️ Build and Test JellyTau / Supply Chain (push) Successful in 4m4s
📱 Test APK / Build test APK (push) Successful in 48m6s
Publish Documentation / Build & publish docs to gitea-pages (push) Successful in 9m17s
Traceability Validation / Check Requirement Traces (push) Successful in 20s
Build & Release / Run Tests (push) Successful in 22m27s
Build & Release / Build Android (push) Waiting to run
Build & Release / Build Linux (push) Successful in 31m18s
Build & Release / Build Windows (push) In progress

Seven fixes from an audit of the stack's most fragile seams, each with a
test that fails without it. Two could take the app out entirely: an
interrupted database migration left it unable to launch at all, and an
unguarded panic at the Android JNI boundary aborted the process outright.

Frontend gates (bun run test/check/lint/format:check) were not run for
this release — node on the release machine is missing libada.so.3 and
exits 127. All changes are under src-tauri/; CI runs those gates.
This commit is contained in:
2026-09-07 22:29:40 +02:00
parent 65d3d912f7
commit 4bce81a800
5 changed files with 82 additions and 4 deletions
+78
View File
@@ -9,6 +9,84 @@ 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.11.6
Found by an audit of the stack's most fragile seams rather than by hitting them,
so most of these are faults that had not yet been reported — several could only
be reached on a bad day, and the worst of them only once.
### 🐛 Fixes
- **An interrupted update can no longer stop the app from ever opening again.**
Changes to the local database were applied one statement at a time with no way
to undo a half-finished one. If an update was interrupted partway — a full
disk, the phone reclaiming memory, the app being killed mid-launch — the
earlier statements stuck while nothing recorded that the change had happened.
On the next launch it started again from the beginning, immediately hit the
part that was already done, and gave up; and since the app treats a database
it cannot prepare as fatal, it stopped opening at all, on every launch, with
the only way out being to clear its data and lose downloads and sign-ins. Each
change is now all-or-nothing, so an interrupted one leaves no trace and the
next launch simply tries again. (UR-002 → DR-012)
- **The app no longer vanishes without trace when the player hits trouble.**
The parts of the Android player that report back into the app — position,
state changes, errors, the end of a track — had no protection around them, and
a failure inside one killed the whole app instantly: no error, no message, not
even a crash report worth sending. One such failure was reachable in ordinary
use, on the position report that fires four times a second: under memory
pressure the app could fail to build the small worker it needs to send that
report, and that alone was enough to take everything down. A dropped position
report is now just a dropped position report. (UR-005 → DR-052)
- **One internal failure no longer disables the whole app until it is restarted.**
Every part of the app that reads or writes local data shares a single gate to
it. If anything failed while holding that gate, the gate stayed jammed: from
then on every library page, download, setting and sign-in returned an error for
the rest of the session, and only quitting and reopening cleared it. The gate
now recovers instead of jamming. (UR-002 → DR-012)
- **Browsing offline no longer reports a network error over content already on
the device.** A read of local data was given a tenth of a second to answer and
otherwise abandoned and treated as "nothing stored". That is easily exceeded
on phone storage whenever something else is writing — a sync catching up, a
batch of artwork being saved — and offline, where there is no server to fall
back to, the result was a network error shown over a library that was sitting
on disk. Worse, the abandoned read kept running and kept the storage busy,
making the next one slower still. A slow read is now waited for rather than
thrown away, and a fast one still answers immediately as before. (UR-002 →
DR-013)
- **A download that arrived empty is no longer presented as ready to play.** If
the server answered a download with nothing at all — an error page, a
conversion that produced no output — the empty file was moved into place and
the item was marked available offline. Opening it then hung: the app's own
media server promised one byte of it and sent none, so the player waited
forever with nothing on screen to say why. An empty download is now treated as
the failure it is, keeping the partial file so it can resume, and a request for
an empty file gets an honest refusal instead of a promise. (UR-019, UR-071 →
DR-168, DR-137)
- **Renaming your computer no longer signs you out.** On systems without a
password manager, sign-in tokens are kept in a file whose key was rebuilt from
the machine's name and the current username each time the app started. Rename
the machine, or launch it from somewhere the username is not set, and the key
came out different, the file could no longer be read, and the app treated that
as never having been signed in — with nothing shown to explain it. The key is
now made once, kept, and unaffected by what the machine is called. Existing
saved sign-ins are carried over automatically. This file has never been a
substitute for a real password manager, and the app now says so plainly rather
than implying otherwise. (UR-012 → IR-014)
- **The player's internal locking is now checked rather than merely careful.**
The playback controller coordinates seventeen separate pieces of shared state
across the audio engine, the lock screen, timers and every screen in the app.
Nothing stopped two of them being taken in opposite orders by different parts
of the code, which freezes playback outright with no error anywhere — a fault
this part of the app has produced before. The correct order is now written down
and enforced automatically, so a future change cannot quietly reintroduce it.
No such fault existed; this keeps it that way. (UR-005 → DR-052)
## v0.11.5
### 🐛 Fixes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jellytau",
"version": "0.11.5",
"version": "0.11.6",
"description": "A cross-platform Jellyfin client built with Tauri, SvelteKit and Rust.",
"author": "Duncan Tourolle <duncan@tourolle.paris>",
"license": "MIT",
+1 -1
View File
@@ -2209,7 +2209,7 @@ dependencies = [
[[package]]
name = "jellytau"
version = "0.11.5"
version = "0.11.6"
dependencies = [
"aes-gcm",
"argon2",
+1 -1
View File
@@ -4,7 +4,7 @@ name = "jellytau"
# `player-conformance`, and a second binary makes a bare `cargo run` —
# which `tauri dev` issues — ambiguous.
default-run = "jellytau"
version = "0.11.5"
version = "0.11.6"
description = "A cross-platform Jellyfin client"
authors = ["Duncan Tourolle <duncan@tourolle.paris>"]
license = "MIT"
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "JellyTau",
"version": "0.11.5",
"version": "0.11.6",
"identifier": "com.dtourolle.jellytau",
"build": {
"beforeDevCommand": "bun run dev",