crabidy/quality/build-features.md

157 lines
8.8 KiB
Markdown

# 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 `<root>` and `<root>/…` 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<dyn ProviderClient>`. *(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<dyn Error>` 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.