docs(downloads): mark UR-055/056 Done; fix colliding UT ids

The browsable Downloaded library + Transfers split + on-disk usage
(f25deba, plus today's grouping/perf fixes) fully implement UR-055 and
UR-056, but the requirements doc still listed them and DR-081..085 as
Planned. Flip to Done.

Also fix UT-id collisions: the downloaded-browse and formatBytes tests
reused UT-046..050 (already assigned to smart-cache/playlist tests in the
matrix). Reassign to UT-071..078 and register them in §4, including the
new music/TV container-rollup and orphan-leaf regression tests.
This commit is contained in:
2026-07-24 22:22:43 +02:00
parent 57b24f8c74
commit e2c9d68311
4 changed files with 99 additions and 61 deletions
+15 -7
View File
@@ -65,8 +65,8 @@ For a narrative overview of the system design, see
| UR-052 | While offline, library pages show only media available on the device by default, with an opt-in toggle that additionally reveals the cached server catalog as greyed-out entries which can be queued for download on the next reconnect | High | Done |
| UR-053 | Restrict media downloads to unmetered networks via a "WiFi Only" setting: when enabled, queued downloads are held while the device is on cellular or a metered connection (including metered WiFi hotspots) and resume automatically once an unmetered network is available | Medium | Done (pending device verification) |
| UR-054 | Reach account actions (Settings, Downloads, Display preferences, Sign out) from every authenticated screen via a single account menu anchored to the user's name, identical on desktop and mobile (see [ux-flows.md §1.2](ux-flows.md)) | High | Done |
| UR-055 | Browse downloaded media as an offline-scoped library — reusing the same library grids, cards, and detail pages as online browsing, showing only libraries/containers with downloaded content — with the transfer-progress list demoted to a secondary "Transfers" view (see [ux-flows.md §7.2](ux-flows.md)) | High | Planned |
| UR-056 | See how much disk each downloaded item/album/series consumes, in familiar rounded units shown on the card and detail page, with a device total on the Downloaded surface and a reclaim amount stated at the point of removal (see [ux-flows.md §7.3.1](ux-flows.md)) | Medium | Planned |
| UR-055 | Browse downloaded media as an offline-scoped library — reusing the same library grids, cards, and detail pages as online browsing, showing only libraries/containers with downloaded content — with the transfer-progress list demoted to a secondary "Transfers" view (see [ux-flows.md §7.2](ux-flows.md)) | High | Done |
| UR-056 | See how much disk each downloaded item/album/series consumes, in familiar rounded units shown on the card and detail page, with a device total on the Downloaded surface and a reclaim amount stated at the point of removal (see [ux-flows.md §7.3.1](ux-flows.md)) | Medium | Done |
| UR-057 | Settings apply the instant a control is changed — no "Save" button and no save/dirty state — so leaving the page never loses a change; sliders show a live readout while dragging but persist on release (see [ux-flows.md §8.1](ux-flows.md)) | Medium | Done |
---
@@ -231,11 +231,11 @@ Internal architecture, components, and application logic.
| DR-079 | `isConnected` derives from backend-reported server reachability alone; `navigator.onLine` is advisory and may only trigger a recheck, never force or clear the offline state (a reachable LAN server while the browser reports offline, and an unreachable server on a live link, must both resolve correctly) | Connectivity | UR-052, UR-043 | Done |
| DR-080 | With the catalog-browse gate off, an empty offline `get_items` result is authoritative "no downloads here" and must be returned as-is; the hybrid repository must not treat it as a cache miss and fall through to the server | Storage | UR-052, UR-013 | Done |
| DR-074 | WiFi-only download gate: `NetworkState`/`NetworkType` transport model reported from the platform via `set_network_state`, checked in `pump_download_queue` before starting any pending row (cellular/metered/unknown fail closed, WiFi and Ethernet require `NOT_METERED`); blocked rows stay `pending` and re-pump on network change, with a `waitingForNetwork` event driving the "Waiting for WiFi" notice. Also wires the previously inert Smart Caching / Queue Pre-caching / WiFi Only settings toggles to `CacheConfig` | Downloads | UR-053 | Done (pending device verification) |
| DR-081 | `/downloads` split into a default **Downloaded** browse view and a secondary **Transfers** activity view, with a view switch and a Transfers badge shown only while transfers are active | UI | UR-055 | Planned |
| DR-082 | Offline-scoped browse entry point in the repository client: browse downloaded content only (offline repository `get_items`/`get_libraries` — downloaded items plus their containers) independent of server reachability, without merging server catalog | Storage | UR-055 | Planned |
| DR-083 | Downloaded browse reuses library grids, cards, and detail pages via the offline-scoped source; omits libraries/containers with no downloaded content; badges partially- vs fully-downloaded containers; play uses the local file; remove available at item/album/season/series level | UI | UR-055 | Planned |
| DR-084 | Transfers view renders only in-flight rows (downloading/queued/paused/failed/waiting-for-WiFi) with Pause/Resume/Cancel/Retry; completed transfers leave the view and appear in Downloaded | UI | UR-055 | Planned |
| DR-085 | Per-item on-disk size: stat downloaded files, aggregate to album/season/series subtotals and a device total, format in consistent rounded human units; surface size on cards and detail pages, the device total on the Downloaded surface, and a reclaim figure in the remove confirmation | Downloads | UR-056 | Planned |
| DR-081 | `/downloads` split into a default **Downloaded** browse view and a secondary **Transfers** activity view, with a view switch and a Transfers badge shown only while transfers are active | UI | UR-055 | Done |
| DR-082 | Offline-scoped browse entry point in the repository client: browse downloaded content only (offline repository `get_items`/`get_libraries` — downloaded items plus their containers) independent of server reachability, without merging server catalog | Storage | UR-055 | Done |
| DR-083 | Downloaded browse reuses library grids, cards, and detail pages via the offline-scoped source; omits libraries/containers with no downloaded content; badges partially- vs fully-downloaded containers; play uses the local file; remove available at item/album/season/series level | UI | UR-055 | Done |
| DR-084 | Transfers view renders only in-flight rows (downloading/queued/paused/failed/waiting-for-WiFi) with Pause/Resume/Cancel/Retry; completed transfers leave the view and appear in Downloaded | UI | UR-055 | Done |
| DR-085 | Per-item on-disk size: stat downloaded files, aggregate to album/season/series subtotals and a device total, format in consistent rounded human units; surface size on cards and detail pages, the device total on the Downloaded surface, and a reclaim figure in the remove confirmation | Downloads | UR-056 | Done |
| DR-086 | Settings page persists each control on change via per-group writers (`playerSetAudioSettings` / `playerSetVideoSettings` / `updateCacheConfig`) rather than a batch Save action; slider controls persist on `change` (pointer release) not each `input` tick; no Save button, `saving`, or `saveMessage` state | Settings | UR-057 | Done |
---
@@ -378,6 +378,14 @@ Internal architecture, components, and application logic.
| UT-069 | `isConnected` follows backend reachability alone: false when the server is unreachable on a live link, true for a reachable server while `navigator.onLine` is false | DR-079 | Done |
| UT-070 | Hybrid `get_items` returns an empty offline result as-is when the catalog-browse gate is off, without querying the server | DR-080 | Done |
| UT-066 | WiFi-only download gate: cellular and metered WiFi blocked, unmetered WiFi/Ethernet allowed, unknown/none fail closed, desktop default ungated; plus the frontend network reporter (transport reporting, change subscription, teardown, fail-open queries) | DR-074 | Done |
| UT-071 | Byte-size formatter: zero/negative/non-finite → "0 B"; decimal unit thresholds; 23 significant-figure banding; trailing-zero trimming; largest-unit cap | DR-085 | Done |
| UT-072 | Downloaded-only browse returns a downloaded leaf and its container, filtered to the requested album parent; a non-downloaded sibling is omitted | DR-082, DR-083 | Done |
| UT-073 | An empty downloaded-only browse is authoritative — no rows, no error — regardless of the catalog-browse flag | DR-082 | Done |
| UT-074 | Only libraries with downloaded content are listed; an empty one is omitted | DR-082 | Done |
| UT-075 | Disk usage reports a leaf's own size, a container's summed descendants, and reconciles the device total with the sum of leaves | DR-085 | Done |
| UT-076 | Downloaded library browse lists album containers, not their individual tracks; drilling into the album returns the tracks | DR-082, DR-083 | Done |
| UT-077 | Downloaded TV library browse lists the series, not seasons/episodes; drilling returns the season then the episode | DR-082, DR-083 | Done |
| UT-078 | A downloaded leaf with no cached container (e.g. a movie) still surfaces at the library level | DR-082, DR-083 | Done |
### Integration Tests
+76 -46
View File
@@ -1,11 +1,11 @@
# Code Traceability Matrix
**Generated:** 7/24/2026, 9:05:27 PM
**Generated:** 7/24/2026, 10:21:18 PM
## Summary
- **Total Files Scanned:** 284
- **Total TRACES Found:** 268
- **Total TRACES Found:** 269
- **Requirements Covered:**
- User Requirements (UR): 53
- Integration Requirements (IR): 14
@@ -908,7 +908,7 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
### DR-039
**Locations:** 8 file(s)
**Locations:** 9 file(s)
- **File:** [`src/routes/library/music/+page.svelte`](src/routes/library/music/+page.svelte#L1)
- **Line:** 1
@@ -919,6 +919,9 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
- **File:** [`src/routes/library/tv/+page.svelte`](src/routes/library/tv/+page.svelte#L1)
- **Line:** 1
- **Context:** `Unknown`
- **File:** [`src/lib/components/library/MediaCard.svelte`](src/lib/components/library/MediaCard.svelte#L37)
- **Line:** 37
- **Context:** `Unknown`
- **File:** [`src/lib/components/home/Carousel.svelte`](src/lib/components/home/Carousel.svelte#L1)
- **Line:** 1
- **Context:** `Unknown`
@@ -2866,7 +2869,7 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
### UR-034
**Locations:** 10 file(s)
**Locations:** 11 file(s)
- **File:** [`src/routes/library/music/+page.svelte`](src/routes/library/music/+page.svelte#L1)
- **Line:** 1
@@ -2877,12 +2880,15 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
- **File:** [`src/routes/library/tv/+page.svelte`](src/routes/library/tv/+page.svelte#L1)
- **Line:** 1
- **Context:** `Unknown`
- **File:** [`src/lib/components/home/Carousel.svelte`](src/lib/components/home/Carousel.svelte#L1)
- **Line:** 1
- **File:** [`src/lib/components/library/MediaCard.svelte`](src/lib/components/library/MediaCard.svelte#L37)
- **Line:** 37
- **Context:** `Unknown`
- **File:** [`src/lib/components/home/HeroBanner.svelte`](src/lib/components/home/HeroBanner.svelte#L1)
- **Line:** 1
- **Context:** `Unknown`
- **File:** [`src/lib/components/home/Carousel.svelte`](src/lib/components/home/Carousel.svelte#L1)
- **Line:** 1
- **Context:** `Unknown`
- **File:** [`src/lib/stores/home.ts`](src/lib/stores/home.ts#L2)
- **Line:** 2
- **Context:** `Unknown`
@@ -3419,14 +3425,6 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
- **Line:** 2
- **Context:** `Unknown`
### UT-050
**Locations:** 1 file(s)
- **File:** [`src/lib/utils/formatBytes.test.ts`](src/lib/utils/formatBytes.test.ts#L4)
- **Line:** 4
- **Context:** `Unknown`
### UT-052
**Locations:** 1 file(s)
@@ -3486,6 +3484,14 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
- **Line:** 4
- **Context:** `Unknown`
### UT-071
**Locations:** 1 file(s)
- **File:** [`src/lib/utils/formatBytes.test.ts`](src/lib/utils/formatBytes.test.ts#L4)
- **Line:** 4
- **Context:** `Unknown`
### UR-016
**Locations:** 2 file(s)
@@ -3838,38 +3844,6 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
- **Line:** 136
- **Context:** `Unknown`
### UT-046
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2686)
- **Line:** 2686
- **Context:** `Unknown`
### UT-047
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2809)
- **Line:** 2809
- **Context:** `Unknown`
### UT-048
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2828)
- **Line:** 2828
- **Context:** `Unknown`
### UT-049
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2850)
- **Line:** 2850
- **Context:** `Unknown`
### UT-051
**Locations:** 1 file(s)
@@ -3900,4 +3874,60 @@ JA-001, JA-002, JA-003, JA-004, JA-005, JA-007, JA-010, JA-011, JA-012, JA-016,
- **Line:** 2385
- **Context:** `Unknown`
### UT-072
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2686)
- **Line:** 2686
- **Context:** `Unknown`
### UT-073
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2809)
- **Line:** 2809
- **Context:** `Unknown`
### UT-074
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2828)
- **Line:** 2828
- **Context:** `Unknown`
### UT-075
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2850)
- **Line:** 2850
- **Context:** `Unknown`
### UT-076
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2708)
- **Line:** 2708
- **Context:** `Unknown`
### UT-077
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2742)
- **Line:** 2742
- **Context:** `Unknown`
### UT-078
**Locations:** 1 file(s)
- **File:** [`src-tauri/src/repository/offline.rs`](src-tauri/src/repository/offline.rs#L2788)
- **Line:** 2788
- **Context:** `Unknown`