8.8 KiB
8.8 KiB
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 indefault, so the default dependency graph is identical:cargo tree -p crabidy-serverbefore and after differ in nothing but theoptional/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
.protochange, no feature-conditional RPC, no client-side feature knowledge.cbd-tuiandcbd-webbuild and run against any server build. (gate:git diffshows nocrabidy-core/crabidy/v1/*.protochange.)
Feature hygiene
- G4 — Every gated dependency is
optional = trueand reachable only through its feature. Nodep:alias is enabled by a path other than its own feature; no gated crate is named in a non-optional dependency line. Applies totidaldy,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
providersentries:tidal,youtube,fyyd,abs,soundcloud,jamendo,fs(D3). - G6 —
--no-default-featurescompiles, links, and runs forcrabidy-server,cbd,cbd-tui, andaudio-player. The provider-less server starts, answersInit, 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;fsalone; defaults minusopus; minusspectrum; minusweb-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.nixnames 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 shipsweb-ui(D10). (gate: readflake.nix;nix build .#crabidyyields a binary whosefeaturesoutput lists the providers.)
The mount registry
- G9 — A provider is named in exactly two places. Its
Cargo.tomlfeature line and its registration inProviderOrchestrator::build. No#[cfg(feature = …)]for a provider anywhere else incrabidy-server(thefsfeature is the documented exception — it also gates the store, capture, orphans, persistence, andscan, 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, andresolve_tracks_intoreach the same client for the same paths as the old chain: a mount owns<root>and<root>/…and nothing else (/fsxis 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:
MalformedPathfor lookups/track paths,NotSupportedfor create/rename/delete,falseforis_track_path, with the samewarn!. (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_intostill dispatches dynamically. Providers that override the trait default (paged resolves) keep their override throughArc<dyn ProviderClient>. (test: a fake provider whose override is observed.)
fs (local files and persistent state)
- G14 — Without
fs, nothing referencesfsdy, the store, captures, or orphans.crabidy_store,capture, andorphansare not compiled;Playbackholds no store;restore_currentand the persister are absent or inert. - G15 — Store-dependent RPCs degrade cleanly.
CaptureLibraryNodeandSaveQueueanswerUnimplemented(never a panic, never a hang) with a message naming the missingfsfeature. Existing clients treat it as any other RPC error. - G16 —
scanwithoutfsfails 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
opusoff,symphonia,symphonia-adapter-libopus, and the bundled libopus C build are gone.cargo tree -p audio-player --no-default-featuresshows neither crate; the build needs nocmake/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_sourcereturns anErrwhose message names the missing feature; playback logs it and moves on, exactly as for a corrupt file (hard rule: no panic on input). - G20 —
scanindexes.opusif and only if the feature is on.AUDIO_EXTENSIONScontains"opus"underopusand 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,realfftis gone and no FFT task is spawned; the update stream simply carries noSpectrumFrame. Clients (TUIf, web) still run; their bars stay dark.Player::spectrum_tap()keeps its signature (D8). - G22 — Without
web-ui, notonic-web, noinclude_dir, no embedded bundle, and the gRPC route still serves native HTTP/2 clients. (Existing behaviour; only re-verified.) - G23 — Without
notifications,cbd-tuilinks nonotify-rustand 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 —
featuresprints the truth.crabidy-server featuresandcbd featureslist 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; aprovidersentry this binary cannot mount produces onewarn!naming it. Neither aborts startup (fail-open library layer). (test:unavailable_providers_reports_built_out_and_unknown_names.) - G26 — The default
crabidy-server.tomllists 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 (inSUMMARY.md) with the feature table, the "what you lose" column, and two worked examples (local-files appliance; tidal + web UI).READMEpoints at it.
Hard rules (always apply)
- G28 — No panics on input or environment. No
unwrap/expectadded 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.
ProviderErrorfor provider paths,CaptureError/Statusfor RPCs,Box<dyn Error>only in CLI entry points. Nocolor-eyrereport reaches a client. - G30 — No secrets in the new paths. The
featuresoutput and the startup lines carry feature names only — no config values, no credentials.