# 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)` where `min > max` is reachable. Specifically: an unknown duration (reported as 0), a track shorter than the step, a delta larger than the track, `i64::MIN`/`i64::MAX` as the delta, and a stopped or empty sink must all be handled without panicking. This closes the existing bug in `seek_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.** `Seek` never returns a `color-eyre`/`anyhow` report to a client; an unseekable source produces a `warn!` 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_seek` is called only from the engine thread. ## Behaviour - [ ] **G4 — Deltas compose.** Two successive backward seeks from the same starting position land at `start - 2 × step`, not `start - 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 emits `Elapsed` from 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 on `position` in the clients and find none (D1, A2). *(test: `seek_command_forwards_the_delta_unchanged`.)* - [ ] **G12 — The playback loop holds no seek state.** `PlaybackCommand::Seek` forwards to the player and does nothing else — no position cache to go stale. ## Clients - [ ] **G13 — `Ctrl-b` and `Ctrl-f` are global in both the TUI and the web client** and collide with nothing in any scope — in particular plain `f` still toggles the spectrum, and the TUI's binding-table uniqueness invariant still holds. In the browser the chord must reach the client rather than the find bar. *(tests: the existing `bindings`/`keymap` tables and their uniqueness tests, extended.)* - [ ] **G14 — Every client reaches it**: TUI binding → `MessageFromUi` → RPC; web binding + `⏪`/`⏩` buttons → RPC; `cbd global seek ` 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.md` and the web page, the CLI page's `global` list, and a sentence in `docs/src/queue.md` saying 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 warnings`** across the feature matrix (`check-features`), fmt clean, and the wasm target builds — the web client's `mod app` is `#[cfg(target_arch = "wasm32")]` and native clippy does not see it.