6.9 KiB
6.9 KiB
Quality gates: queue de-duplication and sorting
Criteria an implementation of architecture/queue-order.md must satisfy.
Automatic tests cover the behaviour (see "Tests" at the end); the gates here
are the things a reader has to check by reading the code. Each is pass/fail.
Correctness of the operations
- G1 — Dedup never removes the playing entry. In every group of duplicates the survivor is the entry at the current position when it is in the group, otherwise the earliest one (D4). Check the survivor selection directly, not only through a test: a "keep the first" shortcut is a silent playback stop the moment the playing copy is a later one.
- G2 — The default duplicate identity is provider-scoped. The key is
(first path segment, provider_item_id)when the id is non-empty and the whole path otherwise (D3). Artist/title matching exists only under the opt-inby_titleidentity (D3a), never on the default path, and the flag is never defaulted to true anywhere between key and wire. - G2a —
by_titlekeeps the longest take and still yields to the playing entry (D3a), and every client surface that reaches it is distinct from the safe one (its own key, its own flag) — a user cannot get it by mistyping. - G3 — Sort is stable and total.
sort_permutationreturns a permutation of0..lenfor every strategy and direction, equal keys keep their queue order, and blanks/unknown durations sort last in both directions (D7, D8). No comparator may panic (nounwrapon a partial comparison, no float keys). - G4 — Sort keys are computed once per track. Decorate–sort–undecorate, not a comparator that lowercases inside the comparison (A5). A ten-thousand entry queue must not allocate per comparison.
- G5 — Reverse ignores the direction flag (D6), and
QueueSort::UnspecifiedreachingQueueManager::sortis a no-op rather than an arbitrary default. - G6 — The play-order invariant holds after both operations.
play_orderindexes every track exactly once andcurrent_offsetpoints into it (the invariantshuffle_insert_keeps_order_uniqueguards for inserts). Shuffle off ⇒ rebuilt from the new order; shuffle on ⇒ remapped through the permutation, so the remaining play sequence is unchanged (D10). - G7 — Dedup goes through
remove_tracks. There is exactly one place that removes queue entries (D5). A second removal implementation that maintainsplay_orderitself is a fail even if its tests pass.
Server discipline
- G8 — Both operations run on the playback loop and nowhere else. No
queue mutation outside a
PlaybackCommandarm; the queue lock is never held across anawait(the pattern every existing arm follows). - G9 — One broadcast site. Both go out through
broadcast_queue, so the persister and the update stream cannot drift (D12). A dedup that removed nothing broadcasts nothing. - G10 — No panics on client input.
SortQueuewith an unset or unknown enum value answersInvalidArgument;DedupQueuewith a dropped result receiver logs and continues; an empty queue is a no-op for both. Nounwrap/expect/indexing that a client can reach. - G11 — Both RPCs are in the rights matrix as
QueueOwner(D1), and the pinned method-list test inauth.rsincludes them. A new RPC must not be able to reach the wire unmapped. - G12 — The dedup count crosses the boundary as a number. No
server-formatted human string on the wire or in a
MessageToUi(D2, D15); wording belongs to each client. - G13 — Nothing is logged that could carry a credential. Sort logs the
strategy, dedup logs a count. No track paths, no URLs — a queue entry's path
can be a
/rssitem whose URL is a per-subscriber token.
Clients
- G14 — No client computes an order. No comparators, no
duplicate-finding, and no
Remove-derived dedup incbd-tui,cbd-weborcbd-cli(A1, Option A rejected). Clients send the verb and render what comes back. - G15 — The sort menu is strictly modal. While it is open the bindings
table is unreachable and an unclaimed key leaves it open rather than falling
through — a stray
cmust not clear the queue while the user thinks they are picking a sort (D14). - G16 — Both operations are reachable without a keyboard in the web
client (D16): toolbar buttons dispatching the same actions as
uandS, and the strategy list exists once (SORT_CHOICES), driving both the overlay and the button. - G17 — The TUI and web keymaps agree:
u= dedup,S= sort menu, and the same five menu letters with the same uppercase-is-descending rule. Where they differ, the difference is deliberate and documented. - G18 — Feedback exists for both outcomes of a dedup.
0renders as plainly as7(D2/D15) — a dedup that found nothing must not look like a dropped keypress. - G19 — The CLI strategy is a
ValueEnum(D17), so a typo is a parse error, the shell completes it, and the printed confirmation names the strategy in the same spelling the user typed. - G20 — Every new binding is documented where the others are: the TUI
help modal (derived from
BINDINGS, so automatic), the web help overlay table,docs/src/clients/tui.md,docs/src/clients/cli.md, anddocs/src/queue.md's queue-operation list.
Documentation and honesty
- G21 — The proto documents the semantics at the wire. What a
duplicate is, which copy survives, that unknowns sort last, that
Reverseignoresdescending, and thatUNSPECIFIEDis refused. - G22 — The known gaps are written down, not left to be discovered: a captured copy and its streaming source are not duplicates (D3), a sort with shuffle on does not change what plays next (D10), and tracks still resolving land unsorted after the fact (D11).
- G23 — Public items have doc comments stating intent and edge behaviour, in the register the surrounding code uses (why, not what).
Tests
Behaviour is pinned by these, all of which must pass:
| Area | Test home | Pins |
|---|---|---|
| Identity, survivors, permutations | queue_order.rs |
D3, D4, D6–D9 |
| Play-order invariants | lib.rs (QueueManager) |
D5, D10, empty queues |
| Wiring, broadcast, persistence | playback.rs |
D2, D9, D12 |
| Argument validation | rpc.rs |
D6, the count |
| Rights matrix | auth.rs (existing pinned test) |
D1 |
| Keys and modality | cbd-tui/src/app/{bindings,sort,mod}.rs |
D14 |
| Pane feedback | cbd-tui/src/app/queue.rs |
D15 |
| Command parsing | cbd-cli/src/lib.rs |
D17 |
| Keymap parity | cbd-web/src/keymap.rs |
D16, D17 |
The server rows live under crabidy-server/src/.
Plus the repository's standing gates: cargo clippy clean, cargo fmt,
no new unwrap on client-reachable paths, and the book building.