5.0 KiB
5.0 KiB
Quality gates — seek
Criteria an implementation of architecture/seek.md must satisfy.
Automated coverage lives in audio-player/src/player_engine.rs (the
clamping arithmetic, which is where the interesting behaviour is),
crabidy-server/src/playback.rs, and the two clients' binding tests.
Hard rules (highest priority)
- G1 — No panic on any delta, from any state. The clamp is
saturating arithmetic with no
assert/clamp(min, max)wheremin > maxis reachable. Specifically: an unknown duration (reported as 0), a track shorter than the step, a delta larger than the track,i64::MIN/i64::MAXas the delta, and a stopped or empty sink must all be handled without panicking. This closes the existing bug inseek_to(D5). (tests:seek_target_never_panics_on_extremes,seek_target_saturates_at_the_start,an_unknown_duration_does_not_clamp_forward.) - G2 — A failed seek stays server-side.
Seeknever returns acolor-eyre/anyhowreport to a client; an unseekable source produces awarn!and an unchanged position, not an RPC error (D7). - G3 — No new unbounded channel and no new blocking call on an
async task. The seek reply rides the existing bounded reply pattern;
try_seekis called only from the engine thread.
Behaviour
- G4 — Deltas compose. Two successive backward seeks from the
same starting position land at
start - 2 × step, notstart - step— the engine reads the live position each time (D1). (test:successive_seeks_compose.) - G5 — Backward saturates at zero, never below, and never into the previous track (A1).
- G6 — Forward past the end lands one second before the end
when the duration is known, so the track finishes through the normal
end-of-stream path and the queue advances — the engine never seeks to
the exact end (D4). (test:
seek_target_saturates_near_the_end.) - G7 — An unknown duration does not clamp forward. A length-less stream reports duration 0; that must not turn every forward seek into "seek to 0" (the naive clamp) — it must pass the target through and let the decoder refuse it.
- G8 — The new position is broadcast immediately, including
while paused —
tick()returns early for a paused sink, so the engine emitsElapsedfrom the seek path itself (D6). - G9 — Seeking while stopped does nothing and does not start playback (A4).
Wire and plumbing
- G10 — One RPC, relative only:
Seek(SeekRequest{sint32 delta_millis}) -> SeekResponse, fire-and-forget like the other playback RPCs (D2, A3). No absolute field, no oneof — the deferred extension stays compatible. - G11 — The delta is never re-derived client-side. No client
computes a target position from a broadcast
TrackPosition; grep for arithmetic onpositionin the clients and find none (D1, A2). (test:seek_command_forwards_the_delta_unchanged.) - G12 — The playback loop holds no seek state.
PlaybackCommand::Seekforwards to the player and does nothing else — no position cache to go stale.
Clients
- G13 —
,/.seek and</>skip tracks, global in both clients, colliding with nothing in any scope, with the TUI's binding-table uniqueness invariant intact and the shifted forms resolving whether or not the terminal reportsSHIFT. Being plain printable characters, none of them can be swallowed by a browser — whichCtrl-nis, and which is why</>exist. (tests: thebindings/keymaptables and their uniqueness tests, extended.) - G18 — A progress-bar click becomes an offset inside the track.
The mapping is a pure function tested on the native target: a click
behind the playhead seeks back, the edges are exactly the track's ends, a
fraction outside
[0, 1]is clamped rather than extrapolated, and a duration of 0 (or a non-finite fraction, meaning a zero-width element) declines instead of seeking somewhere arbitrary. (tests:a_progress_bar_click_becomes_an_offset,a_click_without_a_scale_is_declined.) - G14 — Every client reaches it: TUI binding →
MessageFromUi→ RPC; web binding +⏪/⏩buttons → RPC;cbd global seek <SECONDS>accepting a negative value (D8). - G15 — Both help surfaces list it — the TUI help modal (from
BINDINGS) and the web help overlay (HELP) — so the two tables stay in lockstep with dispatch, which their tests enforce.
Documentation
- G16 — Documented where a user looks: the key tables in
docs/src/clients/tui.mdand the web page, the CLI page'sgloballist, and a sentence indocs/src/queue.mdsaying seek is within-track, that a forward seek past the end advances, and that SoundCloud's HLS streams cannot seek. - G17 — Clippy clean under
-D warningsacross the feature matrix (check-features), fmt clean, and the wasm target builds — the web client'smod appis#[cfg(target_arch = "wasm32")]and native clippy does not see it.