# Quality gates: captures Criteria the implementation must satisfy beyond the automatic tests (`crabidy-server/src/capture.rs`, `crabidy-server/src/capture_store.rs`, `fsdy/src/lib.rs`, plus the TUI tests added during implementation). Each gate is pass/fail by reading the code. ## Shared walk (refactor) - [x] Bookmarks and download captures run through **one** walk (`capture::capture_into`/`write_tree`); `bookmark_store` keeps no copy of the worklist, caps, tmp-and-swap, or cleanup logic. - [x] The refactor is behavior-preserving for bookmarks: every existing `bookmark_store` test passes unchanged (module path of `CaptureError` aside). - [x] The walk stays iterative (worklist), all-or-nothing, and removes the temp folder on every failure path — including failed downloads and the byte budget. ## Download sink - [x] Every external call is bounded: connect timeout on the shared HTTP client and one per-track deadline covering URL fetch, request, and the whole body stream. No retries. - [x] Bodies are **streamed** to disk (never buffered whole) and counted against the capture's byte budget while streaming; exceeding it is `TooLarge`, not partial data left behind. - [x] Non-2xx responses and transport errors are typed (`CaptureError::Download`) — no panic on any network condition. - [x] Download log lines carry paths, names, and counts — never stream URLs (they embed tokens) and never file contents. - [x] The toml is written only after its audio file succeeded, with a relative `file` playable naming the sibling (`TrackFile::from_track_with_file`); the audio name shares the toml's order prefix and sanitizer (`capture::audio_file_name`). - [x] Downloads run sequentially inside the one spawned capture task; the orchestrator loop keeps serving commands during a capture. ## Opt-in (is_downloadable) - [x] Tidal sets the flag centrally: nodes are downloadable when queueable or when they list tracks; children mirror `is_queable`. No per-arm copies to drift. - [x] Every other provider (fs, queues, bookmarks, captures, orchestrator root) leaves the flag `false` — a capture can never be built from another capture's or bookmark's links masquerading as sources. - [x] The server enforces the blessing at the capture root (directory source: its own node; track source: its parent node) with `CaptureError::Unsupported`; an unreadable root is `BadSource`. ## RPC and orchestrator - [x] `CaptureLibraryNodeRequest.download` is additive: old clients (no flag) keep getting bookmarks, byte-for-byte. - [x] Error mapping: `InvalidName`/`BadSource` → `invalid_argument`; `TooLarge`/`Disabled`/`Unsupported` → `failed_precondition`; download/walk/write failures → `internal`. - [x] The orchestrator routes `/captures` in every `ProviderClient` method (same completeness as `/bookmarks`), and `get_lib_root` lists the `captures` child only when the instance exists. - [x] Captures init is non-fatal: no config dir or an unopenable store disables download captures and the `/captures` mount, never the server (and never bookmarks). - [x] `/captures` mounts with an editable top level (no reserved names): captures are renamable (`e`) and deletable (`d`) like bookmarks. ## TUI - [x] `W` (shift) is bound in `Scope::Library` with a help description and passes the bindings-table invariant tests unchanged; `w` behavior is untouched. - [x] The capture overlay opens for `W` only when the bare selection is queueable **and** downloadable; tracks inherit their node's flag; marks are ignored. Label: `capture` (vs `bookmark`). - [x] `MessageFromUi::CaptureNode` carries `download`; a failed capture is logged and never tears down the poll loop. ## Hygiene - [x] New public items are documented; docs state error/edge behavior. - [x] `clippy -D warnings`, `fmt`, `taplo`, `markdownlint` clean on the whole workspace; all tests green. - [x] `architecture/captures.md` reconciled where the implementation diverged (e.g. the exact tidal flag rule).