93 lines
4.2 KiB
Markdown
93 lines
4.2 KiB
Markdown
# 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
|
|
|
|
- [x] Every occurrence of the hardcoded `"/fs"`/`"/fs/"` prefix inside
|
|
`fsdy::Client` methods now derives from the instance's `provider_root`;
|
|
`PROVIDER_ROOT` remains only as the default instance's constant
|
|
(`init`) and for external callers.
|
|
- [x] Path traversal validation still happens in exactly one place
|
|
(`disk_path`) and applies to every instance.
|
|
- [x] `TrackFile::from_track` is the single Track→file conversion site,
|
|
and it always emits a `link` playable (no special cases per provider).
|
|
- [x] Serialization never panics: `to_toml` returns
|
|
`TrackFileError::Serialize`, and every `Option` field is
|
|
skip-serialized (TOML cannot represent `None`).
|
|
- [x] The one-hop link argument holds in code: `get_urls_for_track` still
|
|
returns `MalformedPath` for a link playable, so removing the
|
|
`LinkIntoFs` rejection cannot introduce recursion anywhere.
|
|
|
|
## Queue store
|
|
|
|
- [x] `QueueStore` is the only writer of the queues directory; the
|
|
`/queues` provider instance only reads.
|
|
- [x] 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.
|
|
- [x] `save` validates the name first and never touches disk for an
|
|
invalid name or empty snapshot.
|
|
- [x] `load_current` never 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.
|
|
- [x] File names come from `fsdy::track_file_name` — no second naming
|
|
scheme.
|
|
- [x] No file contents in logs anywhere in the store (paths and names
|
|
only).
|
|
|
|
## Playback wiring
|
|
|
|
- [x] The playback loop never blocks on disk: auto-persist goes through
|
|
the `watch` channel (latest wins), `SaveQueue` writes on a spawned
|
|
task that reports back through the command's result channel.
|
|
- [x] Every queue-state change reaches the persist channel: queue
|
|
content changes (the `broadcast_queue` funnel), current-track changes
|
|
(`play`), and the shuffle/repeat toggles.
|
|
- [x] The persister task debounces and skips snapshots equal to the last
|
|
one written (pure `resolving`-flag broadcasts must not rewrite the
|
|
folder).
|
|
- [x] Persist failures are warnings; no persist error can stop playback
|
|
or crash the loop.
|
|
- [x] The startup restore runs before the playback loop serves commands,
|
|
restores tracks + position + repeat/shuffle, and never starts
|
|
playback (`PlayState::Stopped`).
|
|
- [x] Restore tolerates a corrupt position (out of range → clamped or
|
|
reset, never a panic).
|
|
- [x] A server without a usable queues directory (no config dir, mkdir
|
|
fails) runs without persistence after a warning — never dies.
|
|
|
|
## RPC and orchestrator
|
|
|
|
- [x] `save_queue` maps errors: invalid name → `invalid_argument`, empty
|
|
queue → `failed_precondition`, I/O → `internal`; no `color-eyre`/debug
|
|
reports leak to clients.
|
|
- [x] The orchestrator routes `/queues` in **every** `ProviderClient`
|
|
method (same completeness as `/fs`), and `get_lib_root` lists the
|
|
`queues` child only when the instance exists.
|
|
- [x] `/queues` mutations via the library stay `NotSupported`
|
|
(create/rename/delete unchanged).
|
|
|
|
## TUI
|
|
|
|
- [x] `w` is bound in `Scope::Queue` only, has a help description, and
|
|
passes the existing bindings-table invariant tests unchanged.
|
|
- [x] The save overlay reuses `InputState` (Esc cancels, Enter submits
|
|
trimmed, empty submit closes silently) and is a no-op while the queue
|
|
is empty.
|
|
- [x] `MessageFromUi::SaveQueue` reaches the `SaveQueue` RPC; a failed
|
|
save must not crash the TUI.
|
|
|
|
## 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/fs-provider.md` reconciled: the "no links into /fs"
|
|
rule replaced by the one-hop semantics, D2's "chains structurally
|
|
impossible" wording updated.
|