5.8 KiB
Implementation summaries
node-editing (2026-07-20)
Built per plan/node-editing.md: search-term nodes (created via %) are now
modifiable — e opens the input overlay prefilled with the current title and
renames (re-running the search; merge on title collision), d deletes
without confirmation (documented decision, architecture/node-editing.md D4).
Capabilities travel as LibraryNodeChild.is_editable/is_deletable (fields
5/6, child-only — no consumer for node-level copies), surfaced as a [ed]
marker; two new rpcs RenameLibraryNode (returns the renamed node, TUI
navigates into it) and DeleteLibraryNode (returns the refreshed parent).
All 59 workspace tests green; every gate in quality/node-editing.md
checked. Verified against the live Tidal API with a temporary ignored probe
(removed after passing): create beatles → rename to rolling stones
(in-place, 20 tracks / 40 children) → a track queued under the old term
still resolved a stream URL → delete emptied the listing.
The whole feature ran autonomously per standing instruction; decisions are
recorded in architecture/node-editing.md (options + rationale per topic).
Deviations from plan / architecture (node-editing)
- Self-rename bug caught by the gate tests: the first
rename_search_termimplementation deleted a term renamed to itself (the merge branch removed the "old" slot). Fixed with an explicitold != newguard; the architecture text ("merge on collision") now implicitly means distinct titles. pane_bindings_only_match_their_own_pane(help-modal suite) updated: it asserted plaindis unbound in the library — now it isLibraryDeleteNodeby design; the test's queue-only example key moved toc.delete_library_nodealso mapsInvalidInput→invalid_argumentalthough no provider raises it for delete today — keeps the error contract uniform across the three node-mutation rpcs.- End-to-end check ran at the provider layer (as with search): no interactive terminal/audio device in this environment; the gRPC handler and TUI layers above it are covered by unit tests and review.
- Environment note: builds/tests again ran with a session-local
CARGO_TARGET_DIR(owner-built artifacts intarget/); no repo change.
search (2026-07-20)
Built per plan/search.md: % inside /tidal/search opens a one-line input;
the term becomes a persistent (per-process) tree node holding Tidal search
results — 20 tracks queueable in place, plus artist/album results as canonical
/tidal/artists/... children. Creatable nodes carry an is_creatable flag
end-to-end (proto → provider → TUI marker [%] + pane hint). All 48 workspace
tests green; every gate in quality/search.md checked. Verified against the
live Tidal API: payload shapes match the existing models (probe kept as the
ignored probe_search_shapes test), and a full create→list→resolve-URL round
trip succeeded (beatles → 20 tracks / 40 children, idempotent, playable
stream URL from a search-track path).
The whole feature ran autonomously on user instruction; decisions were taken
without mid-stage confirmation and recorded in architecture/search.md
(options + decision per topic).
Deviations from plan / architecture (search)
Library::updatenow concatenates tracks and children (tracks first). The old code showed tracks instead of children, which would have hidden the artist/album results on term nodes — architecture assumed both would render. Existing nodes are unaffected (they only ever carry one kind).- Search categories degrade independently: a failing category logs and
contributes nothing; only all three failing is a
FetchError. The plan did not specify partial-failure behavior. get_lib_nodeno longer requires a user id up front — the gate moved into the favorites arms (planned), which also meanscreate_lib_nodevalidation works fully offline (used by the new unit tests).- End-to-end check ran at the provider layer (temporary ignored test, removed after passing) rather than driving the full TUI + server — no interactive terminal/audio device in this environment. The gRPC handler and TUI layers above it are covered by unit tests and review.
- Environment note:
target/contains owner-built artifacts not writable by this agent's user; builds/tests ran with a session-localCARGO_TARGET_DIR. No repo change involved.
help-modal (2026-07-20)
Built per plan/help-modal.md: app/bindings.rs (declarative
BINDINGS table + lookup + key_label), app/help.rs (overlay), the
App::dispatch/DispatchResult seam, and the rewired event loop in
main.rs. All 20 tests pass; every gate in quality/help-modal.md checked.
Deviations from plan / architecture (help-modal)
- Two-column modal layout. The architecture assumed a single-column list;
the full table is ~50 rows and would not fit even a 100×40 frame. The modal
renders Global in the left column and Library + Queue stacked in the right
column, with the close keys as a footer line (
Close help: ?, Esc, q) derived from theScope::Helpbindings instead of a fourth listed group. The open question "scroll vs truncate" stays resolved as truncate — but after the column split the content fits ~34×94, so truncation only kicks in on genuinely small terminals. ScopederivesHash(not in the stub) so the chord-uniqueness test can use aHashSet.QueueInsertHeredescription reworded to "Insert library selection after this track":crabidy-server'sinsert_trackssplices atposition + 1. Same check confirmed the planned "Queue selection after current track" wording forLibraryQueueNext.main.rspassestxtoApp::newwithout the now-unneeded clone; theKeyCode/KeyModifiers/UiFocus/StatefulListimports moved out with the old match.