crabidy/plan/node-editing.md

4.4 KiB

Plan — editable and deletable nodes

Ordered tasks for the implement stage. Inputs: architecture/node-editing.md, stubs across the workspace, gates in quality/node-editing.md. Tests: devenv shell -- cargo test --workspace (10 failing at plan time = the target: 5 in tidaldy, 5 in cbd-tui). Use the session-local CARGO_TARGET_DIR if target/ contains artifacts owned by the repo owner.

1. Term-list mutations (tidaldy)

  • Implement rename_search_term(old, new): under one write lock, if new already exists remove the old slot (merge); else if old exists replace it in place; else push new (stale-client forgiveness). Implement remove_search_term(term): retain everything but term. Both poison-tolerant like register_search_term, no await under the lock. Verify: rename_replaces_in_place_and_merges_duplicates passes; gate "lock discipline".

2. Provider rename/delete (tidaldy)

  • rename_lib_node: trim new title → InvalidInput if empty; parse_path(path)? must be TidalPath::SearchTerm(old) else NotSupported; decode old term, rename_search_term, return get_lib_node(join_path(parent, encode_segment(new))). Verify: rename_rejects_empty_titles_and_foreign_paths passes (validation before any network call).
  • delete_lib_node: parse_path(path)? must be SearchTerm else NotSupported; decode + remove_search_term (idempotent); return get_lib_node(/tidal/search). Verify: delete_rejects_foreign_paths and delete_removes_terms_idempotently_and_returns_the_parent pass.
  • Flag term children in the Search arm of get_lib_node: is_editable: true, is_deletable: true via struct update. Verify: search_node_lists_created_terms passes (extended assertion).

3. Server handlers (crabidy-server)

  • Replace the todo!() bodies of rpc.rs rename_library_node/delete_library_node: record span fields, bounded(1) rendezvous with the matching ProviderCommand, map errors exactly like create_library_node (NotSupportedfailed_precondition with an operation-specific message, InvalidInputinvalid_argument, rest → internal). Verify: gates "error mapping" + "bounded rendezvous"; cargo check. (Orchestrator routing and the command-loop arms were finalized at stub time — re-read them against the gate rather than re-doing them.)

4. TUI library accessors + marker (cbd-tui)

  • Implement Library::selected_editable / selected_deletable from list_state.selected() + the UiItem flags. Verify: edit_opens_the_overlay_prefilled_only_on_editable_selections, delete_sends_only_for_deletable_selections, rename_submit_sends_the_trimmed_new_title, rename_cancels_and_emptied_buffers_send_nothing pass (dispatch and overlay logic already landed with the stubs).
  • Render the [e]/[d]/[ed] suffix for modifiable items in Library::render, COLOR_SECONDARY like [%]. Verify: modifiable_items_are_marked_and_the_rename_overlay_is_labelled passes; gate "marked in UI".

5. TUI ↔ server wiring (cbd-tui)

  • RpcClient::rename_library_node: send the rpc, evict the old path and the parent (crabidy_core::parent_path), insert + return the renamed node. delete_library_node: send the rpc, evict the deleted path and the parent, insert + return the returned parent node. Verify: gate "cache eviction" (read; the cache is private, as with create).
  • main.rs poll: replace the todo!() arms — RenameNode calls rename_library_node, DeleteNode calls delete_library_node; on success ReplaceLibraryNode(node), on failure error! and leave the UI unchanged. Verify: gate "failures never panic".

6. End-to-end + gates sweep

  • Exercise the full path against the live API if the local tidal config is available (create a term, rename it, delete it, confirm the parent listing and that a queued search track still resolves a URL); note the outcome in the summary. If unavailable, rely on the offline tests and say so.
  • Sweep: no todo!() left (workspace grep), fmt + clippy + tests green, every quality/node-editing.md box checked, docs updated where behavior shifted. Append the outcome + deviations to plan/summary.md.