4.9 KiB
4.9 KiB
Quality gates — progressive queueing
Criteria for architecture/progressive-queueing.md. Automatic tests live in
crabidy-core/src/lib.rs (default resolve_tracks_into walk, fake
provider), crabidy-server/src/lib.rs (PendingResolve chunk application),
tidaldy/src/lib.rs (offline path validation), and
cbd-tui/src/app/queue.rs (indicator rendering). They fail while the stubs
are unimplemented — that is the target state for implement.
Run: devenv shell -- cargo test --workspace (session-local
CARGO_TARGET_DIR when target/ is owner-built).
Channel and concurrency gates (verify by reading)
- All new channels are bounded. The chunk channel is small (≤ 8
chunks); forwarder → playback reuses the existing
bounded(64). Nounbounded()anywhere in the feature. - No lock is held across an
await. Thependingmap andqueuemutexes are locked, used, and released inside synchronous blocks only — same discipline as the existing playback handlers. - The playback loop stays the single writer of queue state. Neither
the forwarder task nor the provider resolve task touches
QueueManageror broadcasts; they only send commands. - The provider loop never blocks on a resolve. The
ResolveTracksarm spawns;GetTrackUrlsfor the first chunk's track must be servable while a resolve is still streaming (deadlock check: playback awaitingGetTrackUrls+ provider awaiting a chunk send must be impossible). - Channel semantics are documented where they are the contract:
sender-drop = finished, receiver-drop = cancel, on both
ProviderClient::resolve_tracks_intoandProviderCommand::ResolveTracks.
Cancellation gates
ReplaceandClearcancel every pending op before mutating the queue;Queue/Append/Insertcancel nothing.- Cancellation propagates to the network fetch: the forwarder drops the chunk receiver, and both resolve implementations (default walk and tidaldy override) treat a failed chunk send as "stop fetching, return Ok" — verified by test for the default, by reading for tidaldy's page loop.
- Late chunks for finished/cancelled ops are dropped silently (no error, no queue mutation).
Behavior gates
- Immediate feedback: accepting a resolve op broadcasts a
Queuesnapshot withresolving = truebefore the first chunk arrives. resolvingis true iff at least one op is pending, and every queue broadcast from the playback loop goes through the one helper that sets it (grep: no directStreamUpdate::Queue(construction in the op/chunk paths besidesbroadcast_queue).- Playback starts with the first chunk that makes a track current
(replace, or landing in an empty queue) and never restarts for later
chunks of the same op — covered by the
PendingResolvetests, plus reading theapply_resolved_chunk→play_if_somewiring. - Order preserved end-to-end: pages arrive in collection order,
nodes in listing order (pre-order walk — the old LIFO reversal must
not reappear), multi-path requests resolve sequentially in request
order, and
InsertAfterkeeps the whole op contiguous. - An op that finishes with zero tracks logs the existing "resolved to no playable tracks" warning with enough context (paths).
Error and robustness gates
- No panics on provider failure, in any spawned task. Unreadable
nodes are skipped with a warning; a dead provider channel ends the op
(with
ResolveFinishedstill sent, soresolvingclears) instead of leaving a stuck indicator. - Tracing spans survive the spawns: forwarder and provider resolve
tasks attribute their events to the originating request's span
(
ProviderMessage::newcapture +in_current_span/explicit parent). - tidaldy page loop honors the existing token/refresh path (reuses
make_request) and adds no new retry logic.
Wire and UI gates
- Proto change is additive only:
Queue.resolving = 4, no rpc shape changes; field documented in the proto. - The TUI pseudo-item is render-only: never in the list model,
unreachable by selection/removal/navigation (
get_sizeunchanged) — covered by tests. - Indicator style matches the pane (
COLOR_SECONDARY, inside the queue block, after the last track). - Old client / new server and new client / old server both degrade cleanly (flag ignored / never set — reasoning check against the generated proto defaults).
Documentation gates
ProviderClient::resolve_tracks_intodocs state the full channel contract (order, completion, cancellation, error policy).- Deviations from the architecture are recorded in
plan/summary.mdunder this feature.