Keep the extension polling across a VoxClaw restart - #13
Merged
Conversation
The poll loop lives in setInterval inside an MV3 service worker, which is torn down after ~30s without extension API activity. The success path calls chrome.action every second and keeps the worker alive, but the catch path — taken whenever VoxClaw is unreachable — made no chrome.* calls at all. So quitting VoxClaw killed the worker, the interval died with it, and nothing ever restarted it: startPolling only ran on install/startup. Ducking stayed dead until the browser itself was restarted. Observed directly: after VoxClaw was restarted, the listener answered 200 while the extension made zero requests over a 15s window, and stayed silent indefinitely. Two independent guards, since the failure is silent: - chrome.action.setTitle in the catch path, so the worker survives an outage rather than needing resurrection. It also surfaces the state in the toolbar. - A 30s alarm that restarts polling if the worker was torn down anyway. startPolling is already idempotent via its pollTimer guard. NOT YET VERIFIED against a running browser: loading new worker code requires clicking Reload on the extensions page, and a browser restart is not sufficient (confirmed by changing POLL_INTERVAL_MS to 2000, restarting Dia, and still measuring a 1s poll interval — the old script kept running). Dia is currently unable to open a window for that click. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
The poll loop is a
setIntervalinside an MV3 service worker, which Chrome tears down after ~30s without extension API activity.The success path calls
chrome.action.setIcon/setTitleevery second, which keeps the worker alive. The catch path — taken whenever VoxClaw is unreachable — made nochrome.*calls at all. So:fetchthrows → catch path, no API activitysetIntervaldies with itstartPollingonly runs on install/startup/storage-changeObserved directly while testing #10: after restarting VoxClaw, the listener answered
200while the extension made zero requests across a 15s window, and stayed silent indefinitely.This is pre-existing — it applies equally to the YouTube pausing that shipped before Spotify ducking — but it is much more visible now that the extension is actually installed and relied on.
The fix
Two independent guards, because the failure mode is completely silent:
chrome.action.setTitlein the catch path — the worker survives the outage instead of needing resurrection, and the toolbar now shows "VoxClaw (not running)". The API call is load-bearing beyond the title it sets; there's a comment saying so, since it looks removable.chrome.alarmswatchdog that restarts polling if the worker was torn down anyway (browser idle, crash, sleep).startPollingis already idempotent via itspollTimerguard. Adds thealarmspermission.Verification
Verified against a running browser (Dia, Chromium 150), reproducing the exact failure:
service_worker_registration_info.versionwent 1.1.0 → 1.1.1, andserviceworkereventsgainedalarms.onAlarm. (This check matters: a browser restart picks up new manifest permissions while still running the old worker script, which invalidated an earlier attempt at this test.)Result: 75 requests in 75 seconds, a steady 1 Hz resuming ~12s after the listener returned. Against the previous code the same sequence produced zero requests, permanently.
🤖 Generated with Claude Code