Skip to content

feat(extension): self-heal — MV3 SW keep-alive + chrome:// uniform handling#5

Merged
LeonTing1010 merged 2 commits intomainfrom
feat/2026-05-08-extension-self-heal
May 8, 2026
Merged

feat(extension): self-heal — MV3 SW keep-alive + chrome:// uniform handling#5
LeonTing1010 merged 2 commits intomainfrom
feat/2026-05-08-extension-self-heal

Conversation

@LeonTing1010
Copy link
Copy Markdown
Owner

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 routes peer_unreachablereconnect_extension on rare SW-failure cases; this PR removes the IDLE-CAUSED firing pattern which was 99% of dogfood instances.

Changes

(i) MV3 SW keep-alive

chrome.alarms.create('tap-keepalive', { periodInMinutes: 0.4 })
chrome.alarms.onAlarm.addListener((alarm) => {
  if (alarm.name === 'tap-keepalive') {
    chrome.runtime.getPlatformInfo(() => {})
  }
})

24s period < 30s MV3 idle window. getPlatformInfo is the cheapest no-op chrome API; calling any chrome API in the listener resets the idle timer. Manifest already declares alarms permission.

(ii) chrome:// guard uniform across daemon/popup

background.js:334 had if (isInternal && !fromDaemon) — daemon-driven navs were exempted from the chrome:// safeguard. When user reloaded extension (active tab = chrome://extensions), daemon nav threw tab_closed: Cannot access a chrome:// URL.

Drop the !fromDaemon clause: 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:

# What
(i)/1 chrome.alarms.create('tap-keepalive', ...) present
(i)/2 onAlarm.addListener wired to that alarm name
(i)/3 periodInMinutes < 0.5 (under MV3 idle window)
(ii)/1 if (isInternal && !fromDaemon) deleted
(ii)/2 if (isInternal) branch opens new tab via chrome.tabs.create

CDD verification

  • 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 + wire_codes pass; no regressions

Test plan

  • node extension/test/self-heal.test.mjs (5/5)
  • All other extension tests still pass
  • Real-world dogfood: SW should remain alive across 30s idle gaps; chrome:// active tab should no longer break daemon nav

Why 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

LeonTing1010 and others added 2 commits May 9, 2026 00:04
…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>
@LeonTing1010 LeonTing1010 merged commit cd50ae3 into main May 8, 2026
2 checks passed
@LeonTing1010 LeonTing1010 deleted the feat/2026-05-08-extension-self-heal branch May 8, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant