fix(browser): isolate Chrome MCP pending attach aborts#88305
Conversation
|
Codex review: needs maintainer review before merge. Reviewed May 31, 2026, 9:43 PM ET / 01:43 UTC. Summary PR surface: Source +191, Tests +529. Total +720 across 4 files. Reproducibility: yes. Current main source shows the shared pending promise is created with the first caller's AbortSignal, and the linked issue/PR describe a gated factory path where aborting the first caller rejects an unrelated waiter; I did not run tests in this read-only review. Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the waiter-scoped Chrome MCP pending-session lifecycle change after browser/runtime owner acceptance and focused CI, keeping cancellation isolated without adding config or public API surface. Do we have a high-confidence way to reproduce the issue? Yes. Current main source shows the shared pending promise is created with the first caller's AbortSignal, and the linked issue/PR describe a gated factory path where aborting the first caller rejects an unrelated waiter; I did not run tests in this read-only review. Is this the best way to solve the issue? Yes, this appears to be the right owner-boundary fix: it keeps the repair inside the Chrome MCP session cache, uses an internal shared attach controller, and releases per caller. The remaining question is lifecycle-risk acceptance, not a narrower mechanical fix. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against 72bc9ae95231. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +191, Tests +529. Total +720 across 4 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
|
Pushed a narrow repair for the pending cleanup race in What changed:
Verification run after the repair: node scripts/run-vitest.mjs extensions/browser/src/browser/chrome-mcp.test.ts
# 44 tests passed
git diff --check origin/main...HEAD -- extensions/browser/src/browser/chrome-mcp.ts extensions/browser/src/browser/chrome-mcp.test.ts
# passedThe proof remains scoped to the Chrome MCP session cache and controlled session factory; I did not launch a live Chrome/CDP process for this cleanup interleaving. @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Addressed the last-waiter cleanup finding by evicting the matching cached Chrome MCP session before awaiting |
|
🦞👀 Command router queued. I will update this comment with the next step. Re-review progress:
|
83f91cc to
2e8bc90
Compare
|
@clawsweeper re-review Rebased this PR onto latest node scripts/run-vitest.mjs test/scripts/test-projects.test.ts
# 134 tests passed
node scripts/run-vitest.mjs extensions/browser/src/browser/chrome-mcp.test.ts
# 44 tests passed |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
2e8bc90 to
7954ffe
Compare
3c182fd to
8c9dada
Compare
8c9dada to
398de8b
Compare
398de8b to
edcb5d0
Compare
edcb5d0 to
b559439
Compare
|
Land-ready verification for
Local proof:
CI proof:
Known gap: no live Chrome/CDP manual session was run; the regression coverage exercises the Chrome MCP session factory lifecycle races directly. |
* fix(browser): isolate Chrome MCP pending attach aborts * fix(browser): evict closing Chrome MCP sessions * fix(browser): clean chrome mcp pending session lifecycle * fix(browser): handle stale chrome mcp pending sessions * fix(browser): serialize stale chrome mcp replacement * fix(browser): skip cancelled chrome mcp attach * fix(browser): retire timed-out chrome mcp pending sessions * fix(browser): retire stale chrome mcp after readiness * fix(browser): keep shared chrome mcp timeouts isolated * fix(browser): bound stale chrome mcp ready retries * fix(browser): narrow pending session lease release * fix(browser): keep ephemeral probes out of pending attaches * fix(foundry): satisfy provider lint --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(browser): isolate Chrome MCP pending attach aborts * fix(browser): evict closing Chrome MCP sessions * fix(browser): clean chrome mcp pending session lifecycle * fix(browser): handle stale chrome mcp pending sessions * fix(browser): serialize stale chrome mcp replacement * fix(browser): skip cancelled chrome mcp attach * fix(browser): retire timed-out chrome mcp pending sessions * fix(browser): retire stale chrome mcp after readiness * fix(browser): keep shared chrome mcp timeouts isolated * fix(browser): bound stale chrome mcp ready retries * fix(browser): narrow pending session lease release * fix(browser): keep ephemeral probes out of pending attaches * fix(foundry): satisfy provider lint --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(browser): isolate Chrome MCP pending attach aborts * fix(browser): evict closing Chrome MCP sessions * fix(browser): clean chrome mcp pending session lifecycle * fix(browser): handle stale chrome mcp pending sessions * fix(browser): serialize stale chrome mcp replacement * fix(browser): skip cancelled chrome mcp attach * fix(browser): retire timed-out chrome mcp pending sessions * fix(browser): retire stale chrome mcp after readiness * fix(browser): keep shared chrome mcp timeouts isolated * fix(browser): bound stale chrome mcp ready retries * fix(browser): narrow pending session lease release * fix(browser): keep ephemeral probes out of pending attaches * fix(foundry): satisfy provider lint --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary
Fixes #88304.
session.readyMotivation / root cause
getSession()shared one pending Chrome MCP attach per profile/cache key, but created that shared pending promise with the first caller'sAbortSignal. If that first caller cancelled while another caller was also waiting for the same Chrome MCP session, the shared pending attach could reject with the first caller's abort reason and fail the unrelated caller.The fix gives the shared attach its own abort controller and treats each caller as a waiter. Caller aborts release that waiter. The shared attach is only aborted or closed when no waiters remain, and last-waiter close cleanup removes the session from the cache before awaiting close so a fresh caller starts a new attach instead of reusing a handle being torn down.
Real behavior proof
Behavior addressed: one cancelled Chrome MCP caller no longer cancels another caller waiting on the same shared pending attach; all-cancelled attaches are still cleaned up; fresh callers do not reuse a session that is already closing after last-waiter cancellation.
Real environment tested: macOS local checkout with the Chrome MCP runtime module loaded through
node --import tsxand the reallistChromeMcpTabspath using a test session factory.Exact steps or command run after this patch:
Evidence before fix:
{ "first": "rejected", "firstReason": "first caller cancelled", "second": "rejected", "secondReason": "first caller cancelled", "closed": 1 }Evidence after fix:
{ "proof": "mixed-factory-pending", "first": "rejected", "firstReason": "first caller cancelled", "second": "fulfilled", "secondTabs": 1, "closed": 0 }{ "proof": "mixed-ready-pending", "factoryCalls": 1, "first": "rejected", "firstReason": "first caller cancelled", "second": "fulfilled", "thirdTabs": 1, "closed": 0 }{ "proof": "all-aborted-before-ready", "factoryCalls": 1, "first": "rejected", "firstReason": "caller cancelled", "closed": 1 }{ "proof": "fresh-caller-during-close", "factoryCalls": 2, "first": "rejected", "firstReason": "caller cancelled", "secondTabs": 1, "closeCounts": [ 1, 0 ] }Observed result after fix: a remaining waiter can complete and later calls reuse the same session; when every waiter cancels before readiness, the session handle is closed exactly once; when a fresh caller arrives while the old session close is still awaiting, it starts a new attach and does not reuse the closing session.
What was not tested: a live Chrome browser/CDP process was not launched for this proof. The exercised path is the Chrome MCP runtime session cache and cancellation behavior with a controlled session factory.
Regression tests
Added focused coverage in
extensions/browser/src/browser/chrome-mcp.test.tsfor:session.readyis pending;close();Compatibility / risks
This only changes Chrome MCP cached-session cancellation and cleanup behavior. The main risk is around session lifecycle races, so the patch keeps stale-session, profile-key rotation, and transport-error cleanup behavior intact and adds tests for the cancellation cases that can otherwise leak, duplicate, or reuse closing sessions.