Read the playlist (list/count/current) with no permission, and mutate it
(add/jump/remove/move) under the control permission. Mutations route through
prog.Send and the model Update loop so derived state (cursor, current index,
playback) stays consistent; add() reuses the resolve.Args/Remote pipeline off
the UI thread. Indices are 0-based, matching cliamp.queue.current().
queue.save() is intentionally deferred: it needs a shared M3U encoder plus
write-path allowlisting, which belong in their own change.