crabidy/plan/queue-order.md

138 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Plan: queue de-duplication and sorting
Executes `architecture/queue-order.md` against the gates in
`quality/queue-order.md`. The wire and the stubs already exist (api-design);
what follows fills them in, in dependency order. Every task names how it is
verified.
## 1. The wire
- [x] **T1 — Proto: `DedupQueue`, `SortQueue`, `QueueSort`.** Two RPCs, the
strategy enum, `DedupQueueResponse.removed`, with the semantics documented at
the wire. *Verified by:* it compiles through `tonic-prost-build`; G21 by
reading.
- [x] **T2 — Rights matrix.** Both methods as `QueueOwner`, added to the pinned
method list. *Verified by:* `auth.rs::the_method_table_pins_every_rpc_of_the_service`
(G11).
## 2. Pure order logic (`crabidy-server/src/queue_order.rs`)
- [x] **T3 — `track_id`.** Provider-scoped item id, else the whole path.
*Verified by:* `a_provider_item_id_identifies_a_track_across_paths`,
`ids_are_scoped_to_their_provider`, `a_captured_copy_is_not_its_streaming_source`,
`without_an_id_the_path_is_the_identity` (G2).
- [x] **T4 — `duplicate_positions`.** Survivor = current, else earliest;
ascending, unique, never the current position; safe on an empty queue and an
out-of-range current. *Verified by:*
`duplicates_after_the_first_are_removed`, `the_playing_entry_survives_its_group`,
`nothing_to_do_is_an_empty_list`, `positions_come_back_ascending_and_are_unique`
(G1).
- [x] **T5 — `sort_permutation`.** Decoratesortundecorate with keys built
once; compound artist key; case-insensitive text; unknown-last in both
directions; `Reverse` ignores the direction; `Unspecified` is the identity.
*Verified by:* `every_strategy_returns_a_permutation`,
`sorting_by_artist_groups_albums_and_keeps_track_order`, `text_sorts_ignore_case`,
`descending_reverses_the_key_but_not_the_blanks`,
`unknown_durations_sort_last_in_both_directions`,
`reverse_flips_the_current_order_and_ignores_the_direction`,
`an_unspecified_strategy_changes_nothing`, `sorting_nothing_does_not_panic`
(G3, G4, G5).
## 3. Queue state (`crabidy-server/src/lib.rs`)
- [x] **T6 — `QueueManager::dedup`.** Delegates the removal to
`remove_tracks`; returns the count. *Verified by:*
`dedup_removes_later_copies_and_reports_the_count`,
`dedup_keeps_the_playing_track_playing`, `dedup_of_a_clean_queue_changes_nothing`,
`dedup_leaves_the_queue_playable` (G1, G6, G7).
- [x] **T7 — `QueueManager::sort`.** Permute `tracks`; shuffle off ⇒ identity
play order with `current_offset` on the current track, shuffle on ⇒ remap.
*Verified by:* `sorting_reorders_the_queue_and_follows_the_current_track`,
`sorting_without_shuffle_changes_what_plays_next`,
`sorting_with_shuffle_on_preserves_the_shuffled_play_sequence`,
`sorting_with_shuffle_on_still_reorders_the_visible_queue`,
`sorting_an_empty_or_single_queue_does_not_panic`,
`an_unspecified_sort_leaves_the_queue_alone` (G6, G5).
## 4. Server plumbing
- [x] **T8 — Playback loop arms.** Lock, mutate, broadcast; dedup replies with
its count and skips the broadcast when it removed nothing. *Verified by:*
`dedup_command_removes_duplicates_and_replies_with_the_count`,
`dedup_command_answers_zero_without_broadcasting`,
`dedup_command_survives_a_dropped_result_channel`,
`sort_command_reorders_the_queue_and_broadcasts_it`,
`queue_order_commands_reach_the_persist_channel` (G8, G9, G10, G12).
- [x] **T9 — RPC handlers.** `Unspecified`/unknown → `InvalidArgument` before
the loop sees it; the count passes through verbatim. *Verified by:*
`sort_queue_refuses_an_unnamed_strategy`, `sort_queue_forwards_the_strategy_and_direction`,
`dedup_queue_returns_the_loops_count` (G10, G12).
## 5. TUI
- [x] **T10 — Client RPC methods** (`dedup_queue`, `sort_queue`) and the
`MessageFromUi`/`MessageToUi` variants. *Verified by:* compiles; exercised
through the dispatch tests.
- [x] **T11 — Bindings.** `u` dedup, `S` sort menu, queue scope only.
*Verified by:* `queue_order_keys_are_bound_in_the_queue_only`,
`chords_are_unique_within_scope` (G17).
- [x] **T12 — `sort::choose` + the menu table.** Lowercase ascending,
uppercase descending, reverse case-insensitive, unclaimed keys unclaimed.
*Verified by:* `a_lowercase_key_sorts_ascending_and_its_capital_descending`,
`reverse_ignores_the_case`, `keys_the_menu_does_not_offer_are_not_claimed`,
`the_table_is_consistent` (G15).
- [x] **T13 — `sort::render` + `popup_area`.** Centered, `Clear`-backed,
clamped to the frame. *Verified by:*
`the_overlay_lists_every_strategy_with_its_key`, `a_tiny_frame_clamps_the_popup`.
- [x] **T14 — `App::handle_sort_key`.** Modal: sort-and-close, `Esc`/`q`/`S`
close, anything else ignored. *Verified by:*
`a_sort_menu_key_sends_the_strategy_and_closes_the_menu`,
`escape_closes_the_sort_menu_without_sorting`,
`an_unknown_key_leaves_the_sort_menu_open` (G15).
- [x] **T15 — Dispatch + event-loop routing + render hook.** Empty-queue
guards on both actions; the menu is checked before the bindings table.
*Verified by:* `dedup_asks_the_server_only_with_a_non_empty_queue`,
`the_sort_menu_opens_only_with_a_non_empty_queue`.
- [x] **T16 — The pane's dedup notice.** `show_dedup_result` + `notice()`
expiry + title precedence (visual > filter > notice > register).
*Verified by:* `the_dedup_result_appears_in_the_title`,
`the_dedup_result_expires`, `an_active_search_outranks_the_dedup_result`
(G18).
## 6. CLI
- [x] **T17 — `queue dedup` / `queue sort <key> [--desc]`.** `ValueEnum`
strategy. *Verified by:* `queue_dedup_takes_no_arguments`,
`queue_sort_parses_a_strategy_and_an_optional_direction` (G19).
- [x] **T18 — `wire_sort` / `sort_label` and the printed results.** Dedup
prints its count including `0`. *Verified by:* the parse tests plus reading
(G18, G19).
## 7. Web client
- [x] **T19 — RPC methods, `Dialog::Sort`, actions, keymap entries, toolbar
buttons, key routing.** *Verified by:* `queue_order_keys_match_the_tui`,
`the_sort_table_is_consistent` (G16, G17).
- [x] **T20 — `sort_menu_key`.** The web port of `sort::choose`, plus the
close keys. *Verified by:*
`the_sort_menu_maps_keys_to_strategies_and_closes_on_escape` (G15, G17).
- [x] **T21 — `SortMenu` component + `notify`.** Rows clickable and typeable,
dedup count in a toast. *Verified by:* the wasm build (`build-web`) plus
reading (G16, G18).
- [x] **T22 — Web help overlay entries** for `u` and `S`. *Verified by:*
`every_action_reachable_from_help_table` plus reading (G20).
## 8. Documentation
- [x] **T23 — `docs/src/queue.md`**: the two order operations in the queue
model, the duplicate rule, the strategies, and the shuffle interaction.
- [x] **T24 — `docs/src/clients/tui.md`** key table and
**`docs/src/clients/cli.md`** command list. *Verified by:* reading; the book
builds (G20).
## 9. Close-out
- [x] **T25 — Full check.** `cargo test` (workspace), `cargo clippy
--all-targets -D warnings`, `cargo fmt --check`, `cargo test -p cbd-tui
--no-default-features`, and the web build. Then `plan/summary.md`.