crabidy/quality/search.md

4.6 KiB

Quality gates — search via creatable nodes

Checklist for the implement stage. Automatic tests live in crabidy-core/src/lib.rs, tidaldy/src/lib.rs, and cbd-tui/src/app/{bindings,mod}.rs test modules; run with devenv shell -- cargo test --workspace (network-dependent tidaldy tests stay #[ignore]).

Contract & wire

  • Proto changes are additive only: existing field numbers untouched; is_creatable fields 7 (LibraryNode) / 4 (LibraryNodeChild); CreateLibraryNode rpc present. Old clients keep working against the new server.
  • rpc.rs (server) maps errors per the documented contract: NotSupportedfailed_precondition, InvalidInputinvalid_argument, everything else → internal. No color-eyre report or debug formatting of internals leaks into Status messages.
  • ProviderOrchestrator::create_lib_node routes /tidal-prefixed parents to the Tidal client and answers anything else (including the synthetic root /) with NotSupported — same prefix discipline as get_lib_node.
  • The CreateLibraryNode command follows the existing bounded(1)-reply rendezvous pattern; no new unbounded channels anywhere in the feature.

Provider semantics (tidaldy)

  • create_lib_node trims the title; empty/whitespace → InvalidInput; parent other than /tidal/searchNotSupported; idempotent (an existing term returns its node, the term list holds no duplicates).
  • search_terms stores raw terms; the path segment is encode_segment(term); the node title shows the raw term. The lock is never held across an await.
  • get_lib_node(/tidal/search) lists created terms as children (decoded titles) and sets is_creatable: true on the node; an unknown term path under search is implicitly (re)created, not an error — survives server restarts with stale client caches.
  • The term node sets is_queable: false (see architecture/search.md: resolve_tracks would otherwise sweep every artist/album result into the queue). Its tracks are the track results with paths /tidal/search/<enc-term>/<track-id>; artist/album results are children with canonical /tidal/artists/... paths.
  • search_tracks/artists/albums fetch a single page with limit=SEARCH_RESULT_LIMIT — they must NOT use make_paginated_request's fetch-everything loop.
  • get_urls_for_track and get_metadata_for_track work for SearchTrack paths (via track_id_from_path).
  • The response models were verified against the live API (explorer request) before the typed methods were finalized; deviations live in tidaldy::models, not in ad-hoc serde attributes inline.

No panics on user input (hard rule)

  • All todo!() stubs from api-design are gone (grep the workspace).
  • encode_segment/decode_segment are total: any client-supplied segment decodes without panicking (lossy, not unwrap).
  • Search/create failures (network, auth, malformed) surface as ProviderError/Status, never a panic; the TUI's create path handles an error reply without crashing the orchestrate task.

TUI behavior

  • While app.input.is_some(), the event loop routes keys exclusively to handle_input_key — the bindings table (including q/quit and ?) is unreachable; % while the overlay is already open just inserts the character.
  • Library::update applies nodes with no children and no tracks when is_creatable (empty search node is enterable); genuinely empty non-creatable nodes keep the existing skip behavior.
  • Creatable children are visibly marked in the library list and the pane title hints at % when the open node is creatable.
  • The input overlay renders inside the library pane (bottom line) with the typed buffer visible; it disappears on cancel/submit.
  • RpcClient::create_library_node evicts the parent's cache entry and caches the created node (stale /tidal/search listings would otherwise hide new terms).
  • On successful create the library navigates into the returned node (ReplaceLibraryNode); on failure the library stays where it was.

Code quality

  • Public items added in all five crates have doc comments matching final behavior; the % binding appears in the help modal automatically.
  • New dependency: only percent-encoding (workspace-managed version); nothing else added.
  • devenv shell -- cargo fmt --check, cargo clippy --workspace (no new warnings), cargo test --workspace all pass.