From 96e2207cf32ca8dce24bab778ac8eb3181627416 Mon Sep 17 00:00:00 2001 From: KoalaDev <6156589+Shik3i@users.noreply.github.com> Date: Fri, 26 Jun 2026 15:52:49 +0200 Subject: [PATCH] fix(host-control-mode): retry host-sync on dialog "Stay in sync" too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Resync badge already retried REQUEST_HOST_SYNC when the host's state wasn't known yet, but the dialog's "Stay in sync" path did a single no-retry request — so a stay tap right after the host paused (before a heartbeat propagated) could silently leave the guest paused/out of sync. Extracted the retry loop into a shared hcmRequestHostSyncWithRetry() and used it in both paths. Co-Authored-By: Claude Opus 4.8 --- extension/content.js | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/extension/content.js b/extension/content.js index 66f97d3..081daf4 100644 --- a/extension/content.js +++ b/extension/content.js @@ -181,6 +181,25 @@ reportLog('Host-only: snapped back to host position', 'info'); } + // Resync to the host's current position, retrying briefly if the host's state + // isn't known yet (e.g. they just paused and no heartbeat has propagated) — + // otherwise the request is a silent no-op and the user thinks they're synced + // when they aren't. Shared by the dialog's "Stay in sync" and the Resync badge. + function hcmRequestHostSyncWithRetry() { + let attempts = 0; + const tryOnce = () => { + chrome.runtime.sendMessage({ type: 'REQUEST_HOST_SYNC' }, (res) => { + if (chrome.runtime.lastError || !res || !res.target) { + if (++attempts < 5) setTimeout(tryOnce, 250); + else reportLog('Host-only: resync requested but host state unavailable', 'warn'); + return; + } + hcmSnapBackToHost(res.target); + }); + }; + tryOnce(); + } + // Entry point: background told us our local action was blocked in host-only. function hcmHandleBlocked(action, target) { // HOST_BLOCKED is only ever sent to a gated guest (background verifies @@ -254,10 +273,7 @@ // potentially stale target captured at HOST_BLOCKED time (M-1). const stay = () => { if (settled) return; settled = true; hcmRemoveDialog(); - chrome.runtime.sendMessage({ type: 'REQUEST_HOST_SYNC' }, (res) => { - if (chrome.runtime.lastError) return; - if (res && res.target) hcmSnapBackToHost(res.target); - }); + hcmRequestHostSyncWithRetry(); }; const solo = () => { if (settled) return; settled = true; hcmRemoveDialog(); hcmEnterDesync(); }; stayBtn.addEventListener('click', stay); @@ -283,22 +299,8 @@ if (wasDesynced) { chrome.runtime.sendMessage({ type: 'HCM_DESYNC_STATE', desynced: false }).catch(() => {}); } - // Resync: ask background for the host's current position and snap to it. - // Retry briefly if the host's state isn't known yet (e.g. they just paused - // and no heartbeat has propagated) — otherwise the user's "Resync" tap is - // a silent no-op and they think they're synced when they aren't (L-3). - let attempts = 0; - const tryResync = () => { - chrome.runtime.sendMessage({ type: 'REQUEST_HOST_SYNC' }, (res) => { - if (chrome.runtime.lastError || !res || !res.target) { - if (++attempts < 5) setTimeout(tryResync, 250); - else reportLog('Host-only: resync requested but host state unavailable', 'warn'); - return; - } - hcmSnapBackToHost(res.target); - }); - }; - tryResync(); + // Resync to the host's current position (retries if host state not yet known). + hcmRequestHostSyncWithRetry(); reportLog('Host-only: resynced with the host', 'info'); }