crabidy/plan/progressive-queueing.md

95 lines
5.1 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 — 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`.