crabidy/quality/bookmarks.md

74 lines
3.4 KiB
Markdown

# 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).