4.6 KiB
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_creatablefields 7 (LibraryNode) / 4 (LibraryNodeChild);CreateLibraryNoderpc present. Old clients keep working against the new server. rpc.rs(server) maps errors per the documented contract:NotSupported→failed_precondition,InvalidInput→invalid_argument, everything else →internal. Nocolor-eyrereport or debug formatting of internals leaks intoStatusmessages.ProviderOrchestrator::create_lib_noderoutes/tidal-prefixed parents to the Tidal client and answers anything else (including the synthetic root/) withNotSupported— same prefix discipline asget_lib_node.- The
CreateLibraryNodecommand follows the existing bounded(1)-reply rendezvous pattern; no new unbounded channels anywhere in the feature.
Provider semantics (tidaldy)
create_lib_nodetrims the title; empty/whitespace →InvalidInput; parent other than/tidal/search→NotSupported; idempotent (an existing term returns its node, the term list holds no duplicates).search_termsstores raw terms; the path segment isencode_segment(term); the nodetitleshows the raw term. The lock is never held across anawait.get_lib_node(/tidal/search)lists created terms as children (decoded titles) and setsis_creatable: trueon 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_trackswould otherwise sweep every artist/album result into the queue). Itstracksare the track results with paths/tidal/search/<enc-term>/<track-id>; artist/album results are children with canonical/tidal/artists/...paths. search_tracks/artists/albumsfetch a single page withlimit=SEARCH_RESULT_LIMIT— they must NOT usemake_paginated_request's fetch-everything loop.get_urls_for_trackandget_metadata_for_trackwork forSearchTrackpaths (viatrack_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_segmentare total: any client-supplied segment decodes without panicking (lossy, notunwrap).- 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 tohandle_input_key— the bindings table (includingq/quit and?) is unreachable;%while the overlay is already open just inserts the character. Library::updateapplies nodes with no children and no tracks whenis_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_nodeevicts the parent's cache entry and caches the created node (stale/tidal/searchlistings 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 --workspaceall pass.