fix(orbit): dedupe guest outbox entries per host sweep

Guest outboxes are append-only from the host's POV — every sweep reads
the same playlist. `applyOutboxSnapshotsToState` was unconditionally
pushing every trackId in every snapshot into `state.queue`, so the
pending-approval list grew by one duplicate per tick for every unhandled
suggestion (visible as 4+ identical rows after ~10 s).

Dedupe against `(user, trackId)` already present in `state.queue` or
`state.currentTrack` before appending. A guest re-suggesting the same
track after it lands/plays is a rare enough case to live with for now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Psychotoxical
2026-04-25 00:10:40 +02:00
parent 0d18d5dfa9
commit 2b1124d14a
+15
View File
@@ -870,9 +870,24 @@ export function applyOutboxSnapshotsToState(
nowMs: number = Date.now(),
): OrbitState {
// ── Queue additions ──
// Guest outboxes are append-only from the host's POV — the host reads the
// same playlist every sweep, so we must dedupe against anything already in
// `state.queue` (or currently playing) by (user, trackId). Without this,
// every host tick re-adds every outbox entry and the pending-approval list
// balloons indefinitely. A user re-suggesting the same track after it
// lands/plays is a rare enough case to live with for now.
const existingKeys = new Set<string>(
state.queue.map(q => `${q.addedBy}${q.trackId}`),
);
if (state.currentTrack) {
existingKeys.add(`${state.currentTrack.addedBy}${state.currentTrack.trackId}`);
}
const newItems: OrbitQueueItem[] = [];
for (const snap of snapshots) {
for (const trackId of snap.trackIds) {
const key = `${snap.user}${trackId}`;
if (existingKeys.has(key)) continue;
existingKeys.add(key);
newItems.push({ trackId, addedBy: snap.user, addedAt: nowMs });
}
}