crabidy/quality/queue-order.md

6.9 KiB
Raw Permalink Blame History

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-in by_title identity (D3a), never on the default path, and the flag is never defaulted to true anywhere between key and wire.
  • G2a — by_title keeps 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_permutation returns a permutation of 0..len for 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 (no unwrap on a partial comparison, no float keys).
  • G4 — Sort keys are computed once per track. Decoratesortundecorate, 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::Unspecified reaching QueueManager::sort is a no-op rather than an arbitrary default.
  • G6 — The play-order invariant holds after both operations. play_order indexes every track exactly once and current_offset points into it (the invariant shuffle_insert_keeps_order_unique guards 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 maintains play_order itself 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 PlaybackCommand arm; the queue lock is never held across an await (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. SortQueue with an unset or unknown enum value answers InvalidArgument; DedupQueue with a dropped result receiver logs and continues; an empty queue is a no-op for both. No unwrap/expect/indexing that a client can reach.
  • G11 — Both RPCs are in the rights matrix as QueueOwner (D1), and the pinned method-list test in auth.rs includes 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 /rss item whose URL is a per-subscriber token.

Clients

  • G14 — No client computes an order. No comparators, no duplicate-finding, and no Remove-derived dedup in cbd-tui, cbd-web or cbd-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 c must 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 u and S, 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. 0 renders as plainly as 7 (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, and docs/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 Reverse ignores descending, and that UNSPECIFIED is 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, D6D9
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.