crabidy/quality/search.md

88 lines
4.6 KiB
Markdown

# 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
- [x] 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.
- [x] `rpc.rs` (server) maps errors per the documented contract:
`NotSupported``failed_precondition`, `InvalidInput`
`invalid_argument`, everything else → `internal`. No `color-eyre`
report or debug formatting of internals leaks into `Status` messages.
- [x] `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`.
- [x] The `CreateLibraryNode` command follows the existing bounded(1)-reply
rendezvous pattern; no new unbounded channels anywhere in the feature.
## Provider semantics (tidaldy)
- [x] `create_lib_node` trims the title; empty/whitespace → `InvalidInput`;
parent other than `/tidal/search``NotSupported`; idempotent (an
existing term returns its node, the term list holds no duplicates).
- [x] `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`.
- [x] `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.
- [x] 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.
- [x] `search_tracks/artists/albums` fetch a single page with
`limit=SEARCH_RESULT_LIMIT` — they must NOT use
`make_paginated_request`'s fetch-everything loop.
- [x] `get_urls_for_track` and `get_metadata_for_track` work for
`SearchTrack` paths (via `track_id_from_path`).
- [x] 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)
- [x] All `todo!()` stubs from api-design are gone (grep the workspace).
- [x] `encode_segment`/`decode_segment` are total: any client-supplied
segment decodes without panicking (lossy, not `unwrap`).
- [x] 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
- [x] 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.
- [x] `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.
- [x] Creatable children are visibly marked in the library list and the pane
title hints at `%` when the open node is creatable.
- [x] The input overlay renders inside the library pane (bottom line) with
the typed buffer visible; it disappears on cancel/submit.
- [x] `RpcClient::create_library_node` evicts the parent's cache entry and
caches the created node (stale `/tidal/search` listings would
otherwise hide new terms).
- [x] On successful create the library navigates into the returned node
(`ReplaceLibraryNode`); on failure the library stays where it was.
## Code quality
- [x] Public items added in all five crates have doc comments matching final
behavior; the `%` binding appears in the help modal automatically.
- [x] New dependency: only `percent-encoding` (workspace-managed version);
nothing else added.
- [x] `devenv shell -- cargo fmt --check`, `cargo clippy --workspace`
(no new warnings), `cargo test --workspace` all pass.