# Quality gates: bookmarks Criteria the implementation must satisfy beyond the automatic tests (`fsdy/src/lib.rs`, `crabidy-server/src/bookmark_store.rs`, plus the TUI tests added during implementation). Each gate is pass/fail by reading the code. ## Capture - [x] The walk is iterative (worklist), never recursive — a deep provider tree cannot overflow the stack. - [x] The caps bound *total* directories and tracks and abort with a typed error; the temp folder is removed on every failure path (cap, walk error, write error). - [x] Captures write through the same primitives as queues (`TrackFile::from_track`, `track_file_name`, and `dir_name` sharing one sanitizer) — no second serialization or naming scheme. - [x] Whole-bookmark writes are tmp-and-swap; overwriting an existing bookmark never leaves a mix of old and new entries. - [x] A source path that is a track captures as a folder with one file; an unreadable source is `BadSource`, not a panic or an empty bookmark. - [x] The capture runs on a spawned task; the orchestrator loop keeps serving commands during a large walk. - [x] No file contents in logs (paths, names, and counts only). ## Mutable top level (fsdy) - [x] The `is_editable`/`is_deletable` flags appear **only** on direct child folders of an editable instance's root, never on reserved names, nested nodes, tracks, or immutable instances. - [x] `rename_lib_node`/`delete_lib_node` enforce the same gate they advertise: option on + direct child + not reserved; everything else is `NotSupported` (matching the flags a client saw). - [x] Rename validates the new title with the shared `validate_folder_name` (reserved names rejected as targets) and never merges onto an existing sibling (`InvalidInput`). - [x] Delete is idempotent (already gone → success) and returns the refreshed root listing; rename returns the renamed node at its new path. - [x] The `/queues` instance reserves `current`; `/bookmarks` reserves nothing; `/fs` stays fully immutable. - [x] Path traversal validation still happens only in `disk_path`; rename and delete go through it for every path they touch. ## RPC and orchestrator - [x] `capture_library_node` maps errors: invalid name/source → `invalid_argument`, over-cap or disabled → `failed_precondition`, walk/write failures → `internal`. - [x] The orchestrator routes `/bookmarks` in every `ProviderClient` method (same completeness as `/queues`), and `get_lib_root` lists the `bookmarks` child only when the instance exists. - [x] Bookmarks init is non-fatal: no config dir or an unopenable store disables capture and the `/bookmarks` mount, never the server. ## TUI - [x] `w` is bound in `Scope::Library` (queue's `w` untouched), has a help description, and passes the bindings-table invariant tests unchanged. - [x] The capture overlay opens only for a queueable bare selection, prefilled with the selection's title; marks are ignored. - [x] `MessageFromUi::CaptureNode` reaches the new rpc; a failed capture is logged and never tears down the poll loop. - [x] Renaming/deleting bookmark and saved-queue folders works through the existing `e`/`d` flows with no TUI code changes. ## Hygiene - [x] New public items are documented; docs state error/edge behavior. - [x] `clippy -D warnings`, `fmt`, `taplo`, `markdownlint` clean on the whole workspace; all tests green. - [x] `architecture/queue-persistence.md` D8 reconciled (rename/delete of saved queues is no longer out of scope).