# Plan — progressive queueing Ordered tasks for the `implement` stage. Inputs: `architecture/progressive-queueing.md`, the workspace stubs, gates in `quality/progressive-queueing.md`. Tests: `devenv shell -- cargo test --workspace` (13 failing at plan time = the target: 5 in crabidy-core, 5 in crabidy-server, 2 in cbd-tui, 1 in tidaldy). Use the session-local `CARGO_TARGET_DIR` if `target/` contains artifacts owned by the repo owner. ## 1. Default chunked resolve (crabidy-core) - [x] Implement the `resolve_tracks_into` default body: track path → single chunk from `get_metadata_for_track` (failure → warn, `Ok`, no chunk — matches today's skip); node path → depth-first **pre-order** walk (stack of paths, children pushed in reverse so siblings pop in listing order), per node: skip unreadable with a warning (root failure → `Err`), skip non-queueable, send `tracks` as one chunk when non-empty; a failed send (receiver gone) → return `Ok` immediately. **Verify**: all 5 `default_resolve_*` tests pass. ## 2. PendingResolve state machine (crabidy-server lib) - [x] Implement `new`/`cancel_flag`/`cancel`/`applied`/`apply_chunk`. `apply_chunk`: `Replace` → `replace_with_tracks` for the first chunk (returns current track), then mutate kind to `Append`; `Append` → `append_tracks`; `InsertAfter(pos)` → `insert_tracks(pos)` then advance the cursor by `tracks.len()` (clamping is `insert_tracks`' job); count `applied`. **Verify**: the 5 op tests pass (`replace_op_*`, `insert_after_op_*` ×2, `append_op_*`, `cancel_flag_*`). ## 3. Playback loop wiring (crabidy-server) - [x] Implement `broadcast_queue` (snapshot → `Queue`, set `resolving` from the pending map, broadcast) and route the remaining direct `StreamUpdate::Queue(...)` broadcasts in `handle_command` through it. **Verify**: gate "resolving is true iff at least one op is pending". - [x] Implement `start_resolve`: allocate op id, insert `PendingResolve`, immediate `broadcast_queue`, spawn the forwarder (`tokio::spawn` + `in_current_span`): per path in order — check the cancel flag (set → stop), open `flume::bounded(4)` chunk channel, send `ProviderCommand::ResolveTracks`, forward each chunk as `ApplyResolvedChunk` (checking the cancel flag between chunks, drop the receiver on cancel); after all paths (or on any exit path) send `ResolveFinished`. Track the op's paths for the zero-track warning. **Verify**: gates "immediate feedback", "cancellation propagates", "no panics"; `cargo check`. - [x] Implement `apply_resolved_chunk` (look up op — unknown id: drop silently; lock queue, `apply_chunk`, `broadcast_queue`, then `play_if_some` outside the locks), `finish_resolve` (remove op, warn when `applied() == 0` with the op's paths, `broadcast_queue`), and `cancel_pending_resolves` (cancel + clear map; no broadcast needed — the caller mutates and broadcasts next). **Verify**: gates "playback starts with the first chunk", "late chunks dropped"; existing playback tests still green. ## 4. tidaldy page-streamed resolve - [x] Implement `make_paginated_request_into` (same loop as `make_paginated_request`, `sink(page.items)` per page, stop on `false`). **Verify**: gate "token/refresh path reused"; `cargo check`. - [x] Implement the `resolve_tracks_into` override: `parse_path` first (foreign → `MalformedPath` before any I/O); track paths → one metadata chunk; pre-order worklist walk where `Playlist` and `Album` nodes stream `tracks/items` pages as chunks (`Track::to_proto` per page, no playlist-metadata fetch) and all other node kinds fall back to `get_lib_node` (tracks as one chunk when queueable, children onto the worklist in listing order). Failed sends → stop, `Ok`. **Verify**: `resolve_rejects_foreign_paths_before_any_network_call` passes; gate "order preserved end-to-end". ## 5. TUI indicator (cbd-tui) - [x] Implement `Queue::loading_dots` (1 + (elapsed_ms / 400) % 3 dots) and render the pseudo-item: when `resolving`, push one extra `ListItem` (dots, `COLOR_SECONDARY`) after the track rows, computed from a monotonic clock at render time; `self.list` stays untouched. **Verify**: `loading_dots_cycle_one_to_three`, `resolving_queue_renders_trailing_dots_item` pass; the three guard tests stay green. ## 6. End-to-end + gates sweep - [x] Exercise the full path against the live API if the local tidal config is available (temporary ignored probe: resolve a multi-album artist through the orchestrator, assert multiple chunks arrive, in listing order, first chunk before the walk completes; drop the receiver mid-stream and confirm fetching stops). Note the outcome in the summary; if unavailable, rely on the offline tests and say so. - [x] Sweep: no `todo!()` left (workspace grep), fmt + clippy + tests green, every `quality/progressive-queueing.md` box checked, docs updated where behavior shifted. Append the outcome + deviations to `plan/summary.md`.