feat(qconnect): queue_hash divergence scaffold + AskForQueueState trigger

Comparison seam joins the existing resync path. Local hash algorithm is a
documented BLOCKING open question; stub returns None so detection stays inert
(no spurious resyncs) until the Qobuz algorithm is verified.

cc
This commit is contained in:
vicrodh committed 2026-06-01 03:17:09 -06:00
1 parent 3ac68e788a
commit 92695c222a
1 file changed
+42 -1
+42 -1
View File
@@ -326,6 +326,14 @@ where
) {
should_trigger_resync = true;
}
// queue_hash divergence seam. Inert today (local hash algorithm
// is a BLOCKING unknown -> compute_local_queue_hash returns None),
// so this never fires spurious resyncs. When the algorithm lands,
// a mismatch will pull the authoritative QueueState.
if queue_hashes_diverge(&state.queue) {
should_trigger_resync = true;
}
}
snapshot = state.queue.clone();
}
@@ -611,6 +619,28 @@ fn infer_buffer_state(playing_state: Option<i32>) -> Option<i32> {
}
}
/// BLOCKING OPEN QUESTION: the Qobuz `queue_hash` (field #100) algorithm is
/// undocumented. Until it is verified, this returns `None`, which keeps
/// `queue_hashes_diverge` inert so it can NOT fire spurious resyncs. Flipping
/// this to a real implementation is the single switch that turns on divergence
/// detection.
fn compute_local_queue_hash(_queue: &QConnectQueueState) -> Option<Vec<u8>> {
None
}
/// Algorithm-agnostic divergence seam. Only reports divergence when BOTH a
/// locally computed hash and a server-reported hash exist and differ. Because
/// `compute_local_queue_hash` returns `None` today, this is always `false`.
fn queue_hashes_diverge(queue: &QConnectQueueState) -> bool {
match (
compute_local_queue_hash(queue),
queue.last_server_queue_hash.as_ref(),
) {
(Some(local), Some(server)) => &local != server,
_ => false,
}
}
fn map_server_event(event: &QueueServerEvent, current: &QConnectQueueState) -> QueueEvent {
let version = event
.queue_version
@@ -968,7 +998,8 @@ mod tests {
use crate::{QconnectAppEvent, QconnectEventSink};
use super::QconnectApp;
use super::{queue_hashes_diverge, QconnectApp};
use qconnect_core::QConnectQueueState;
use qconnect_protocol::{
QueueCommandType, QueueEventType, QueueServerEvent, RendererCommandType,
RendererServerCommand,
@@ -1186,6 +1217,16 @@ mod tests {
);
}
#[test]
fn queue_hashes_diverge_is_inert_without_local_algorithm() {
let mut queue = QConnectQueueState::default();
queue.last_server_queue_hash = Some(vec![9, 9, 9]);
assert!(
!queue_hashes_diverge(&queue),
"divergence detection must stay inert until the local hash algorithm is known"
);
}
#[tokio::test]
async fn queue_state_event_surfaces_server_queue_hash() {
let (app, _sink, _transport, _events_rx) = build_connected_app().await;