# Quality gates — build features Criteria an implementation of `architecture/build-features.md` must satisfy. Each is pass/fail by reading/reasoning or by running one command; automated coverage lives in `crabidy-server/src/settings.rs` (built-in vs configured providers), `crabidy-server/src/cli.rs` (`features`, `scan` extensions), and `crabidy-server/src/provider.rs` (the mount registry: dispatch, ordering, absent providers). ## The default build is unchanged (highest priority) - [ ] **G1 — `cargo build` (no flags) links exactly what it links today.** Every feature is in `default`, so the default dependency graph is identical: `cargo tree -p crabidy-server` before and after differ in nothing but the `optional`/feature annotations. *(gate: diff the tree.)* - [ ] **G2 — No behaviour change at default features.** The full build mounts the same providers in the same order (crabidy first, orphans last, rest alphabetical), persists the queue, captures, serves the web UI, decodes Opus, and streams spectrum frames. *(tests: the existing suites all pass unchanged.)* - [ ] **G3 — The proto is untouched.** No `.proto` change, no feature-conditional RPC, no client-side feature knowledge. `cbd-tui` and `cbd-web` build and run against any server build. *(gate: `git diff` shows no `crabidy-core/crabidy/v1/*.proto` change.)* ## Feature hygiene - [ ] **G4 — Every gated dependency is `optional = true` and reachable only through its feature.** No `dep:` alias is enabled by a path other than its own feature; no gated crate is named in a non-optional dependency line. Applies to `tidaldy`, `ytdy`, `fyyd`, `absdy`, `soundclouddy`, `jamendody`, `fsdy`, `blake3`, `reqwest`, `realfft`, `tonic-web`, `include_dir` (crabidy-server), `symphonia` + `symphonia-adapter-libopus` (audio-player), `notify-rust` (cbd-tui). - [ ] **G5 — Feature names match the runtime vocabulary.** The provider features are spelled exactly as the `providers` entries: `tidal`, `youtube`, `fyyd`, `abs`, `soundcloud`, `jamendo`, `fs` (D3). - [ ] **G6 — `--no-default-features` compiles, links, and runs** for `crabidy-server`, `cbd`, `cbd-tui`, and `audio-player`. The provider-less server starts, answers `Init`, serves an empty library root, and shuts down cleanly (D2). - [ ] **G7 — Every curated matrix entry builds warning-free** under `cargo clippy -- -D warnings`: defaults; `--no-default-features`; each provider alone; `fs` alone; defaults minus `opus`; minus `spectrum`; minus `web-ui`; `cbd-tui --no-default-features`. Exposed as one devenv script (D11). No `#[allow(dead_code)]`/`#[allow(unused)]` added to silence a combination — unused code under a feature combination is a signal to move the `#[cfg]`, not to allow it. - [ ] **G8 — `flake.nix` names its features explicitly.** The native package no longer passes a bare `--no-default-features` (which after D1 would mean "no providers at all"); it lists what it wants. The aarch64 cross build still ships `web-ui` (D10). *(gate: read `flake.nix`; `nix build .#crabidy` yields a binary whose `features` output lists the providers.)* ## The mount registry - [ ] **G9 — A provider is named in exactly two places.** Its `Cargo.toml` feature line and its registration in `ProviderOrchestrator::build`. No `#[cfg(feature = …)]` for a provider anywhere else in `crabidy-server` (the `fs` feature is the documented exception — it also gates the store, capture, orphans, persistence, and `scan`, per D5). - [ ] **G10 — Dispatch is preserved exactly.** For every mounted provider, `is_track_path`, `get_urls_for_track`, `get_metadata_for_track`, `get_lib_node`, `create_lib_node`, `rename_lib_node`, `delete_lib_node`, and `resolve_tracks_into` reach the same client for the same paths as the old chain: a mount owns `` and `/…` and nothing else (`/fsx` is not `/fs`). *(tests: `mount_owns_its_root_and_children_only`, `dispatch_reaches_the_owning_mount`.)* - [ ] **G11 — An unowned path degrades, never panics.** No mount → the same errors as today: `MalformedPath` for lookups/track paths, `NotSupported` for create/rename/delete, `false` for `is_track_path`, with the same `warn!`. *(tests: `unowned_paths_are_typed_errors`.)* - [ ] **G12 — The root listing offers only mounted providers, ordered crabidy → alphabetical → orphans.** A provider built out is simply absent — clients need no signal. *(test: `root_lists_mounted_providers_in_order`.)* - [ ] **G13 — `resolve_tracks_into` still dispatches dynamically.** Providers that override the trait default (paged resolves) keep their override through `Arc`. *(test: a fake provider whose override is observed.)* ## `fs` (local files and persistent state) - [ ] **G14 — Without `fs`, nothing references `fsdy`, the store, captures, or orphans.** `crabidy_store`, `capture`, and `orphans` are not compiled; `Playback` holds no store; `restore_current` and the persister are absent or inert. - [ ] **G15 — Store-dependent RPCs degrade cleanly.** `CaptureLibraryNode` and `SaveQueue` answer `Unimplemented` (never a panic, never a hang) with a message naming the missing `fs` feature. Existing clients treat it as any other RPC error. - [ ] **G16 — `scan` without `fs` fails with a clear message** naming the feature, exit code non-zero, no partial writes. The clap surface (and so completions and the man page) is unchanged (D9). - [ ] **G17 — A queue still works without persistence.** Queue, play, next, shuffle, repeat behave normally; only survival across restarts is lost, and the startup log says so once. ## `opus` - [ ] **G18 — With `opus` off, `symphonia`, `symphonia-adapter-libopus`, and the bundled libopus C build are gone.** `cargo tree -p audio-player --no-default-features` shows neither crate; the build needs no `cmake`/ `ninja`. *(gate: build in an environment without cmake.)* - [ ] **G19 — An Opus file in an `opus`-less build fails as an undecodable file, not a panic.** `build_source` returns an `Err` whose message names the missing feature; playback logs it and moves on, exactly as for a corrupt file (hard rule: no panic on input). - [ ] **G20 — `scan` indexes `.opus` if and only if the feature is on.** `AUDIO_EXTENSIONS` contains `"opus"` under `opus` and not otherwise; the other extensions are unaffected. *(tests: `scan_indexes_opus_with_the_feature` / `scan_ignores_opus_without_the_feature`.)* ## `spectrum`, `web-ui`, `notifications` - [ ] **G21 — Without `spectrum`, `realfft` is gone** and no FFT task is spawned; the update stream simply carries no `SpectrumFrame`. Clients (TUI `f`, web) still run; their bars stay dark. `Player::spectrum_tap()` keeps its signature (D8). - [ ] **G22 — Without `web-ui`, no `tonic-web`, no `include_dir`, no embedded bundle**, and the gRPC route still serves native HTTP/2 clients. (Existing behaviour; only re-verified.) - [ ] **G23 — Without `notifications`, `cbd-tui` links no `notify-rust`** and the now-playing path compiles to nothing; every other TUI behaviour is unchanged, including the config key (an ignored key must not break the config parse). ## Discoverability - [ ] **G24 — `features` prints the truth.** `crabidy-server features` and `cbd features` list exactly the compiled providers plus the compiled extras, one per line. *(test: `build_features_lists_providers_then_extras`.)* - [ ] **G25 — The startup log names the build.** One `info!` line lists the compiled features; a `providers` entry this binary cannot mount produces one `warn!` naming it. Neither aborts startup (fail-open library layer). *(test: `unavailable_providers_reports_built_out_and_unknown_names`.)* - [ ] **G26 — The default `crabidy-server.toml` lists only built-in providers**, so a pruned-by-build binary never writes a config full of names it will warn about. *(test: `ensure_default_lists_only_built_in_providers`.)* - [ ] **G27 — The docs describe the flags.** `docs/src/` gains a build-features page (in `SUMMARY.md`) with the feature table, the "what you lose" column, and two worked examples (local-files appliance; tidal + web UI). `README` points at it. ## Hard rules (always apply) - [ ] **G28 — No panics on input or environment.** No `unwrap`/`expect` added on config, path, or stream data in any gated code; a missing feature is an error value or an absent mount, never a panic. `todo!()`/`unimplemented!()` from the stub stage are all gone. - [ ] **G29 — Errors stay typed at the boundaries.** `ProviderError` for provider paths, `CaptureError`/`Status` for RPCs, `Box` only in CLI entry points. No `color-eyre` report reaches a client. - [ ] **G30 — No secrets in the new paths.** The `features` output and the startup lines carry feature names only — no config values, no credentials.