feat(extension): self-heal — MV3 SW keep-alive + chrome:// uniform handling#5
Merged
LeonTing1010 merged 2 commits intomainfrom May 8, 2026
Merged
Conversation
…ndling
Per ADR 2026-05-08-failure-detection-phase-2 §2C: substrate-side fixes
for two bugs that caused spurious classifier firings during 2026-05-08
traffic-source dogfood session.
(i) MV3 SW keep-alive
Cause: SW unloads ~30s idle → daemon WS dies → peer_unreachable to
engine → classifyOpFailure correctly routes to reconnect_extension,
but the root cause is preventable here.
Fix: chrome.alarms.create('tap-keepalive', periodInMinutes: 0.4).
onAlarm listener calls chrome.runtime.getPlatformInfo (no-op API
call resets idle timer). 24s period < 30s idle window.
(ii) chrome:// guard uniform across daemon/popup paths
Cause: line 334 had `if (isInternal && !fromDaemon)` — daemon path
exempted from the chrome:// safeguard. When user reloaded extension
(active tab = chrome://extensions), daemon-driven navs threw
tab_closed: Cannot access a chrome:// URL.
Fix: drop the !fromDaemon clause. Daemon and popup paths both open
a managed background tab when active is chrome:// / data://, never
clobber. UX preserved for popup; daemon now correct.
Static guards (ADR §3 D6):
extension/test/self-heal.test.mjs (5 source-text constraints):
Rule (i)/1: chrome.alarms.create('tap-keepalive', ...) present
Rule (i)/2: onAlarm.addListener wired to that alarm name
Rule (i)/3: periodInMinutes < 0.5 (under MV3 idle window)
Rule (ii)/1: `if (isInternal && !fromDaemon)` deleted
Rule (ii)/2: `if (isInternal)` opens new tab via chrome.tabs.create
CDD:
RED: 5/5 constraint tests fail
GREEN: 5/5 pass after edits
Regression: architecture (17/17) + protocol (32/32) + multi-tab (15/15)
+ tap-format (0/0) + wire_codes pass; no regressions.
Companion to tap-core PR #52 (§2A classifyOpFailure). §2A still
correctly routes peer_unreachable → reconnect_extension on the rare
SW-failure cases (network drop, debugger detach); this PR removes
the IDLE-CAUSED firing pattern which was 99% of dogfood instances.
Real-world verification: next user dogfood session — SW should remain
alive across 30s idle gaps; chrome:// active tab should no longer
break daemon nav. Manifest already declares `alarms` permission.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends PR #5 §2C with the third dogfood symptom not covered by §2C(i) SW keep-alive or §2C(ii) chrome:// guard: Cause (2026-05-08 dogfood): Cloudflare nav redirected through CF auth chain → tab on dash.cloudflare.com/two-factor. Subsequent juejin.cn nav called chrome.tabs.update(tabId, {url}) on same tab, but eval ran with cloudflare login page state → silent data corruption. Same root cause for parallel batch calls (multiple plans sharing one tab). Fix: Before nav, parse target URL and compare origin to current tab's origin. If different → chrome.tabs.create new background tab. Same- origin SPA-style navs continue to use cheap tabs.update. Combined guard: `if (isInternal || crossOrigin)` — chrome:// active tab (§2C(ii)) OR cross-origin nav (§2C(iii)) both → new bg tab. Static guards (3 new in self-heal.test.mjs): (iii)/1: nav handler computes target origin via `new URL(params.url)` (iii)/2: nav handler accesses `.origin` ≥ 2× (target + current) (iii)/3: origin comparison result gates a chrome.tabs.create branch (accepts both inline and via-boolean idioms) CDD: RED: 3/3 new tests fail GREEN: 8/8 self-heal tests pass after edit Regression: architecture (17/17) + protocol (32/32) + multi-tab (15/15) + tap-format + wire_codes — no regressions Companion to tap-core PR #52 §2A: classifyOpFailure correctly routes peer_unreachable / tab_closed to typed UserActionRequired, but the RIGHT fix for cross-origin tab pollution is to never let it happen in the first place — substrate-side handling. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Per ADR 2026-05-08-failure-detection-phase-2 §2C (private repo): substrate-side fixes for two bugs that caused spurious classifier firings during 2026-05-08 dogfood.
Companion to tap-core PR #52 (§2A
classifyOpFailure). §2A correctly routespeer_unreachable→reconnect_extensionon rare SW-failure cases; this PR removes the IDLE-CAUSED firing pattern which was 99% of dogfood instances.Changes
(i) MV3 SW keep-alive
24s period < 30s MV3 idle window.
getPlatformInfois the cheapest no-op chrome API; calling any chrome API in the listener resets the idle timer. Manifest already declaresalarmspermission.(ii) chrome:// guard uniform across daemon/popup
background.js:334hadif (isInternal && !fromDaemon)— daemon-driven navs were exempted from the chrome:// safeguard. When user reloaded extension (active tab =chrome://extensions), daemon nav threwtab_closed: Cannot access a chrome:// URL.Drop the
!fromDaemonclause: daemon and popup paths both open a managed background tab when active is chrome:// or data://, never clobber.Static guards
extension/test/self-heal.test.mjs— 5 source-text constraints:chrome.alarms.create('tap-keepalive', ...)presentonAlarm.addListenerwired to that alarm nameperiodInMinutes < 0.5(under MV3 idle window)if (isInternal && !fromDaemon)deletedif (isInternal)branch opens new tab viachrome.tabs.createCDD verification
Test plan
node extension/test/self-heal.test.mjs(5/5)chrome://active tab should no longer break daemon navWhy two bugs in one PR
Both are substrate-side fixes for the same dogfood session, both cite the same ADR §2C, both ship as a single uniform "extension self-heal" patch. Splitting would force a second SW-restart for users on this branch. (per
feedback_v2_extension_protocol_gap_2026-05-04— substrate concerns belong together).🤖 Generated with Claude Code