4.2 KiB
4.2 KiB
Quality gates: queue-persistence
Criteria the implementation must satisfy beyond the automatic tests
(fsdy/src/lib.rs, crabidy-server/src/queue_store.rs, plus the playback
and TUI tests added during implementation). Each gate is pass/fail by
reading the code.
fsdy instances and serialization
- Every occurrence of the hardcoded
"/fs"/"/fs/"prefix insidefsdy::Clientmethods now derives from the instance'sprovider_root;PROVIDER_ROOTremains only as the default instance's constant (init) and for external callers. - Path traversal validation still happens in exactly one place
(
disk_path) and applies to every instance. TrackFile::from_trackis the single Track→file conversion site, and it always emits alinkplayable (no special cases per provider).- Serialization never panics:
to_tomlreturnsTrackFileError::Serialize, and everyOptionfield is skip-serialized (TOML cannot representNone). - The one-hop link argument holds in code:
get_urls_for_trackstill returnsMalformedPathfor a link playable, so removing theLinkIntoFsrejection cannot introduce recursion anywhere.
Queue store
QueueStoreis the only writer of the queues directory; the/queuesprovider instance only reads.- Writes are tmp-and-swap: entries are written to a hidden (dot-prefixed) temp sibling, then the old folder is removed and the temp renamed. No code path writes entries into the live folder directly.
savevalidates the name first and never touches disk for an invalid name or empty snapshot.load_currentnever fails the server: missing folder →None, broken entry → skip with a warning naming the file (never its contents), broken sidecar → default state with a warning.- File names come from
fsdy::track_file_name— no second naming scheme. - No file contents in logs anywhere in the store (paths and names only).
Playback wiring
- The playback loop never blocks on disk: auto-persist goes through
the
watchchannel (latest wins),SaveQueuewrites on a spawned task that reports back through the command's result channel. - Every queue-state change reaches the persist channel: queue
content changes (the
broadcast_queuefunnel), current-track changes (play), and the shuffle/repeat toggles. - The persister task debounces and skips snapshots equal to the last
one written (pure
resolving-flag broadcasts must not rewrite the folder). - Persist failures are warnings; no persist error can stop playback or crash the loop.
- The startup restore runs before the playback loop serves commands,
restores tracks + position + repeat/shuffle, and never starts
playback (
PlayState::Stopped). - Restore tolerates a corrupt position (out of range → clamped or reset, never a panic).
- A server without a usable queues directory (no config dir, mkdir fails) runs without persistence after a warning — never dies.
RPC and orchestrator
save_queuemaps errors: invalid name →invalid_argument, empty queue →failed_precondition, I/O →internal; nocolor-eyre/debug reports leak to clients.- The orchestrator routes
/queuesin everyProviderClientmethod (same completeness as/fs), andget_lib_rootlists thequeueschild only when the instance exists. /queuesmutations via the library stayNotSupported(create/rename/delete unchanged).
TUI
wis bound inScope::Queueonly, has a help description, and passes the existing bindings-table invariant tests unchanged.- The save overlay reuses
InputState(Esc cancels, Enter submits trimmed, empty submit closes silently) and is a no-op while the queue is empty. MessageFromUi::SaveQueuereaches theSaveQueueRPC; a failed save must not crash the TUI.
Hygiene
- New public items are documented; docs state error/edge behavior.
clippy -D warnings,fmt,taplo,markdownlintclean on the whole workspace; all tests green.architecture/fs-provider.mdreconciled: the "no links into /fs" rule replaced by the one-hop semantics, D2's "chains structurally impossible" wording updated.