ADR-0147: Established event streams do not hold sign-in verification open¶
- Status: Accepted by the owner, 2026-10-02
- Date: 2026-10-02
- Related: ADR-0128, ADR-0145
- Amends: the device verification request-settling mechanism in
docs/plan/browser-automation.md
Evidence¶
After the headed-browser release, the owner's x.com device handoff still
returned a provider failure at the thirty-second deadline. A controlled retry
used the native client's cookie mapping and a non-persistent WebKit view, then
loaded the confirmed page with and without its session in the production
hosted runtime. The signed-out control left the confirmed path. The signed-in
load reached its document, but one fetch remained open through the deadline:
a successful HTTP 200 response with Content-Type: text/event-stream.
Only timing, request types and fixed verification outcomes were inspected;
no session material or page content is retained in this evidence.
The request tracker waits for
Playwright’s requestfinished,
which for an event stream means the live connection closes. That connection is designed to stay open.
The existing mechanism already excludes native EventSource and WebSocket
connections, but an equivalent stream opened with fetch or XHR blocks it.
Decision¶
A tracked fetch or XHR response with HTTP 200 and media type exactly
text/event-stream is established when its response headers arrive. Remove
that request from the finite-request wait at that point and begin the same
500 ms quiet interval. Match the media type case-insensitively and ignore its
optional parameters, matching the
HTML event-stream connection rules.
A stream without response headers, a non-200 response,
a different media type, or a document, script or stylesheet still waits for
completion. No hostname, URL path or website-specific selector is involved.
All ordinary application requests, including delayed JSON session checks, still finish before positive evidence is collected. Both browsers still load the confirmed page, and readiness still requires the signed-in page to stay there without a challenge while the signed-out control behaves differently. The thirty-second deadline, secret boundary, origin confinement, challenge checks, profile sealing and revocation checks remain in force.
This is an explicit exception to the design's rule that every fetch and XHR must finish. As with an existing WebSocket or EventSource connection, a site may deliver a later authentication change over the established stream. The verification is a check at the time of capture, not a guarantee that a site will never revoke a session afterward. It must still refuse a challenge already visible at capture.
Verification evidence¶
- The real-Chromium regression in
tests/unit/test_device_handoff_real_chromium.pyreproduced the failure: expected HTTP 200 but received HTTP 409 at the verification deadline. With the repair it verifies and seals while the HTTP 200 fetch stream remains open. tests/unit/test_browser_playwright.pychecks fetch and XHR, media-type case and parameters, status and resource-type exclusions, and listener cleanup on success and cancellation. All four new positive cases timed out before the repair and pass afterward.- The real-Chromium delayed-session tests now run alongside an open event stream. The ready, challenge, redirect, timeout and retry outcomes pass; the stream never excuses the separate unfinished JSON check.
- The owner repeated the controlled handoff using the repaired runtime in an isolated diagnostic process on the production host. The signed-in page stayed at the confirmed path without a challenge, the signed-out control left it, and storage capture completed in 17.6 seconds while the stream remained open. This verified the repair without changing the running service or saving the diagnostic session as a profile.
Alternatives¶
Increasing the deadline cannot finish a healthy live stream. Ignoring all fetches, all cross-origin traffic, or a hardcoded x.com endpoint would admit unfinished session checks or make the generic provider depend on one site.