[GH-ISSUE #6232] [Bug] Page inspection reports network and console coverage before CDP domains are enabled #3794

Closed
opened 2026-10-31 00:44:07 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @coygeek on GitHub (Oct 22, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6232

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Process capture or runtime observation

Observed behavior

A successful inspect_web_page MCP request reports network.coverage_started_at and console.coverage_started_at equal to capture_window.started_at, before setup has finished and the relevant domains can be enabled. Reproduced in 3/3 requests through the registered MCP tool and production CDP provider against the suite's loopback HTTP/WebSocket fake. This is a bounded timestamp-reporting error; no real-browser event loss was tested.

Reproduction

Use the source revision below with its locked dependencies already installed. Isolate HOME, DSH_HOME, XDG_CONFIG_HOME and XDG_CACHE_HOME in a disposable directory. Save the following as tests/boundary/mcp/coverage-start-repro.test.ts, then run:

node node_modules/vitest/vitest.mjs run tests/boundary/mcp/coverage-start-repro.test.ts --maxWorkers=1

The test adds 25 ms of Page.enable response latency and deliberately asserts the incorrect ordering. All three cases passed in one invocation, reproducing the defect. The test filename has been normalized for publication; its directory depth and contents match the executed temporary test.

import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
import { expect, it } from "vitest";
import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js";
import { webPageInspectionSchema } from "../../../src/domain/browserObservationSchemas.js";
import { ok } from "../../../src/domain/result.js";
import { createServer } from "../../../src/server/createServer.js";
import { observed } from "../../fixtures/analysisExecution.js";
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js";

it.each([1, 2, 3])("reports coverage before domain enable, attempt %i", async () => {
  const received = new Map<string, number>();
  let pageEnableReplyEarliest = 0;
  const browser = await startFakeCdpBrowser({
    eventsBeforeReply: ["Network.enable"],
    commandResult(command) {
      if (!received.has(command.method)) received.set(command.method, Date.now());
      if (command.method === "Page.enable") {
        // Simulate 25 ms of CDP setup latency before the domain-enable calls.
        Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25);
        pageEnableReplyEarliest = Date.now();
      }
      return undefined;
    },
  });
  const session = createTestBinarySession(() => ({
    execute: () => Promise.resolve(observed(null)),
    close: () => Promise.resolve(ok(null)),
  }));
  const server = createServer({ kind: "session", session }, {
    environment: process.env,
    browserObservation: new CdpBrowserProvider(),
    availabilityPolicy: () => ({ processCaptureEnabled: false, investigationInputRoots: 0 }),
  });
  const client = new Client({ name: "coverage-repro", version: "1" });
  const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
  try {
    await server.connect(serverTransport);
    await client.connect(clientTransport);
    const result = await client.callTool({ name: "inspect_web_page", arguments: {
      cdp_endpoint: browser.endpoint,
      target_id: "allowed-page",
      allowed_origins: [browser.allowedOrigin],
      observation_ms: 0,
    } });
    expect(result.isError).not.toBe(true);
    const inspection = webPageInspectionSchema.parse(result.structuredContent?.normalized_result);
    const networkStart = Date.parse(inspection.network.coverage_started_at);
    const consoleStart = Date.parse(inspection.console.coverage_started_at);
    expect(inspection.network.coverage_started_at).toBe(inspection.capture_window.started_at);
    expect(inspection.console.coverage_started_at).toBe(inspection.capture_window.started_at);
    expect(inspection.network.prior_activity_available).toBe(false);
    expect(inspection.console.prior_activity_available).toBe(false);
    expect(networkStart).toBeLessThan(pageEnableReplyEarliest);
    expect(consoleStart).toBeLessThan(pageEnableReplyEarliest);
    expect(received.has("Runtime.enable")).toBe(true);
    expect(received.has("Network.enable")).toBe(true);
    console.log(JSON.stringify({
      network_enable_received_after_reported_start_ms: received.get("Network.enable")! - networkStart,
      runtime_enable_received_after_reported_start_ms: received.get("Runtime.enable")! - consoleStart,
      page_enable_reply_earliest_after_reported_start_ms: pageEnableReplyEarliest - networkStart,
    }));
  } finally {
    await client.close();
    await server.close();
    await session.close();
    await browser.close();
  }
}, 20000);

Expected behavior

The documented contract says network and console coverage starts only after REA attaches and enables the relevant CDP domains. Each section's coverage start should reflect when observation becomes available; the overall capture lifecycle may start earlier. See Completeness and limits. Events delivered while enabling a domain should remain represented within the reported coverage boundary.

Impact and scope

The response assigns a pre-enable start to both section coverage windows. In this controlled fake-CDP run, the reported start preceded the earliest possible Page.enable reply by 28/27/30 ms. The existing prior-activity flags remained false, and the documented warning against inferring event absence still applies. No consequential downstream failure or real-browser event loss was observed. Only inspect_web_page was executed; source inspection shows analyze_web_bundle uses the same capture producer, but that tool was not run.

REA version

Source commit c465ff5a81 (main)

Runtime and platform

Node v26.12.0; macOS 27.0.1; arm64. The repository pins Node 24.18.0; this reproduction used the different Node version listed here.

Target and artifact context

Suite fake Chromium CDP endpoint over real loopback HTTP/WebSocket transport; target allowed-page; exact allowed origin; observation_ms=0. No external website or real browser was used.

Interface

MCP

Earliest failure stage

Capture or observation

Relevant output or evidence

Three MCP requests succeeded. Both section coverage starts equaled capture-window start and both prior_activity_available fields were false. Focused test output:

Attempt 1: Network.enable received 30 ms after reported start; Runtime.enable 29 ms; earliest Page.enable reply 28 ms.
Attempt 2: Network.enable received 27 ms after reported start; Runtime.enable 27 ms; earliest Page.enable reply 27 ms.
Attempt 3: Network.enable received 31 ms after reported start; Runtime.enable 31 ms; earliest Page.enable reply 30 ms.
Test Files 1 passed (1); Tests 3 passed (3).

These record command receipt, not dispatch or exact domain activation. The earlier Page.enable reply barrier, combined with the sequential production command order, establishes that the coverage timestamps also precede dispatch of the later enable commands. This is a controlled ordering probe, not real-browser latency measurement.

Typed outcome or recovery expected

A successful inspection with truthful section coverage boundaries, the broader capture lifecycle start, and the existing explicit prior-activity limitations preserved. No error response is expected for this valid request.

Analysis or suspected cause

Source-derived explanation: the capture-entry timestamp is recorded before authorization and domain enabling, then reused for both section coverage fields. This explains the observed ordering without requiring a particular repair.

Additional context

Searched open and closed issues/PRs using coverage_started_at, coverage start, CdpPageCapture, coverage/timestamp, coverage/enable, Network.enable/coverage, and coverage/before/enable. No matching duplicate found. Related PR #2287 clarifies pre-attach script reporting; issue #3423 addresses distinct passive-observation and timeout documentation concerns. Current fetched main had no subsequent changes to the cited implementation or contract. Last known good version: NOT_ENOUGH_INFO; no earlier revision was tested.

Originally created by @coygeek on GitHub (Oct 22, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6232 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Process capture or runtime observation ### Observed behavior A successful `inspect_web_page` MCP request reports `network.coverage_started_at` and `console.coverage_started_at` equal to `capture_window.started_at`, before setup has finished and the relevant domains can be enabled. Reproduced in 3/3 requests through the registered MCP tool and production CDP provider against the suite's loopback HTTP/WebSocket fake. This is a bounded timestamp-reporting error; no real-browser event loss was tested. ### Reproduction Use the source revision below with its locked dependencies already installed. Isolate HOME, DSH_HOME, XDG_CONFIG_HOME and XDG_CACHE_HOME in a disposable directory. Save the following as `tests/boundary/mcp/coverage-start-repro.test.ts`, then run: ```sh node node_modules/vitest/vitest.mjs run tests/boundary/mcp/coverage-start-repro.test.ts --maxWorkers=1 ``` The test adds 25 ms of Page.enable response latency and deliberately asserts the incorrect ordering. All three cases passed in one invocation, reproducing the defect. The test filename has been normalized for publication; its directory depth and contents match the executed temporary test. ```typescript import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { webPageInspectionSchema } from "../../../src/domain/browserObservationSchemas.js"; import { ok } from "../../../src/domain/result.js"; import { createServer } from "../../../src/server/createServer.js"; import { observed } from "../../fixtures/analysisExecution.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; import { startFakeCdpBrowser } from "../../fixtures/fakeCdpBrowser.js"; it.each([1, 2, 3])("reports coverage before domain enable, attempt %i", async () => { const received = new Map<string, number>(); let pageEnableReplyEarliest = 0; const browser = await startFakeCdpBrowser({ eventsBeforeReply: ["Network.enable"], commandResult(command) { if (!received.has(command.method)) received.set(command.method, Date.now()); if (command.method === "Page.enable") { // Simulate 25 ms of CDP setup latency before the domain-enable calls. Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 25); pageEnableReplyEarliest = Date.now(); } return undefined; }, }); const session = createTestBinarySession(() => ({ execute: () => Promise.resolve(observed(null)), close: () => Promise.resolve(ok(null)), })); const server = createServer({ kind: "session", session }, { environment: process.env, browserObservation: new CdpBrowserProvider(), availabilityPolicy: () => ({ processCaptureEnabled: false, investigationInputRoots: 0 }), }); const client = new Client({ name: "coverage-repro", version: "1" }); const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); try { await server.connect(serverTransport); await client.connect(clientTransport); const result = await client.callTool({ name: "inspect_web_page", arguments: { cdp_endpoint: browser.endpoint, target_id: "allowed-page", allowed_origins: [browser.allowedOrigin], observation_ms: 0, } }); expect(result.isError).not.toBe(true); const inspection = webPageInspectionSchema.parse(result.structuredContent?.normalized_result); const networkStart = Date.parse(inspection.network.coverage_started_at); const consoleStart = Date.parse(inspection.console.coverage_started_at); expect(inspection.network.coverage_started_at).toBe(inspection.capture_window.started_at); expect(inspection.console.coverage_started_at).toBe(inspection.capture_window.started_at); expect(inspection.network.prior_activity_available).toBe(false); expect(inspection.console.prior_activity_available).toBe(false); expect(networkStart).toBeLessThan(pageEnableReplyEarliest); expect(consoleStart).toBeLessThan(pageEnableReplyEarliest); expect(received.has("Runtime.enable")).toBe(true); expect(received.has("Network.enable")).toBe(true); console.log(JSON.stringify({ network_enable_received_after_reported_start_ms: received.get("Network.enable")! - networkStart, runtime_enable_received_after_reported_start_ms: received.get("Runtime.enable")! - consoleStart, page_enable_reply_earliest_after_reported_start_ms: pageEnableReplyEarliest - networkStart, })); } finally { await client.close(); await server.close(); await session.close(); await browser.close(); } }, 20000); ``` ### Expected behavior The documented contract says network and console coverage starts only after REA attaches and enables the relevant CDP domains. Each section's coverage start should reflect when observation becomes available; the overall capture lifecycle may start earlier. See [Completeness and limits](https://github.com/morluto/rea/blob/c465ff5a814b8e1a386fc547cbcd94df5c35f086/docs/browser-observation.md#L466). Events delivered while enabling a domain should remain represented within the reported coverage boundary. ### Impact and scope The response assigns a pre-enable start to both section coverage windows. In this controlled fake-CDP run, the reported start preceded the earliest possible Page.enable reply by 28/27/30 ms. The existing prior-activity flags remained false, and the documented warning against inferring event absence still applies. No consequential downstream failure or real-browser event loss was observed. Only inspect_web_page was executed; source inspection shows analyze_web_bundle uses the same capture producer, but that tool was not run. ### REA version Source commit c465ff5a814b8e1a386fc547cbcd94df5c35f086 (main) ### Runtime and platform Node v26.12.0; macOS 27.0.1; arm64. The repository pins Node 24.18.0; this reproduction used the different Node version listed here. ### Target and artifact context Suite fake Chromium CDP endpoint over real loopback HTTP/WebSocket transport; target allowed-page; exact allowed origin; observation_ms=0. No external website or real browser was used. ### Interface MCP ### Earliest failure stage Capture or observation ### Relevant output or evidence Three MCP requests succeeded. Both section coverage starts equaled capture-window start and both prior_activity_available fields were false. Focused test output: ```text Attempt 1: Network.enable received 30 ms after reported start; Runtime.enable 29 ms; earliest Page.enable reply 28 ms. Attempt 2: Network.enable received 27 ms after reported start; Runtime.enable 27 ms; earliest Page.enable reply 27 ms. Attempt 3: Network.enable received 31 ms after reported start; Runtime.enable 31 ms; earliest Page.enable reply 30 ms. Test Files 1 passed (1); Tests 3 passed (3). ``` These record command receipt, not dispatch or exact domain activation. The earlier Page.enable reply barrier, combined with the sequential production command order, establishes that the coverage timestamps also precede dispatch of the later enable commands. This is a controlled ordering probe, not real-browser latency measurement. ### Typed outcome or recovery expected A successful inspection with truthful section coverage boundaries, the broader capture lifecycle start, and the existing explicit prior-activity limitations preserved. No error response is expected for this valid request. ### Analysis or suspected cause Source-derived explanation: the [capture-entry timestamp](https://github.com/morluto/rea/blob/c465ff5a814b8e1a386fc547cbcd94df5c35f086/src/browser/CdpPageCapture.ts#L95) is recorded before [authorization and domain enabling](https://github.com/morluto/rea/blob/c465ff5a814b8e1a386fc547cbcd94df5c35f086/src/browser/CdpPageCapture.ts#L250), then [reused for both section coverage fields](https://github.com/morluto/rea/blob/c465ff5a814b8e1a386fc547cbcd94df5c35f086/src/browser/CdpPageCapture.ts#L364). This explains the observed ordering without requiring a particular repair. ### Additional context Searched open and closed issues/PRs using coverage_started_at, coverage start, CdpPageCapture, coverage/timestamp, coverage/enable, Network.enable/coverage, and coverage/before/enable. No matching duplicate found. Related PR #2287 clarifies pre-attach script reporting; issue #3423 addresses distinct passive-observation and timeout documentation concerns. Current fetched main had no subsequent changes to the cited implementation or contract. Last known good version: NOT_ENOUGH_INFO; no earlier revision was tested.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

确认根因定位合理:capture-entry timestamp 在 authorization 与 domain enabling 之前记录、又复用到两个 section coverage 字段,必然早于 enable 完成时刻,3/3 复现且延迟稳定在 30ms 左右说明不是偶发噪声。建议修复时以 domain enable 完成的实际时刻作为 coverage_started_at(例如以最后一个相关域 enable reply 的时间为准),capture_window.started_at 继续表示整体捕获生命周期起点;同时保持 prior_activity_available 语义不变——coverage 起点推迟不应被解读为"之前无活动"。

<!-- gh-comment-id:6332727562 --> @XinwangTao commented on GitHub (Oct 22, 2026): 确认根因定位合理:capture-entry timestamp 在 authorization 与 domain enabling 之前记录、又复用到两个 section coverage 字段,必然早于 enable 完成时刻,3/3 复现且延迟稳定在 30ms 左右说明不是偶发噪声。建议修复时以 domain enable 完成的实际时刻作为 coverage_started_at(例如以最后一个相关域 enable reply 的时间为准),capture_window.started_at 继续表示整体捕获生命周期起点;同时保持 prior_activity_available 语义不变——coverage 起点推迟不应被解读为"之前无活动"。
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked on main at d87e73f2db50be72d33a4b3c7199af35518b441c: Network and console coverage now use domain-specific first-event or successful enable-acknowledgment times, preserving pre-reply events and keeping prior_activity_available false. A deterministic regression checks both boundaries. CdpPageCapture.ts CdpPageCapture.ts CdpCaptureEvents.ts CdpCaptureEvents.ts CdpCaptureEvents.coverageStart.test.ts

I ran the cited regression file on this revision; all passed as part of the focused 28-file, 603-test run. Provider-facing checks here use controlled fixtures; this does not claim a run with the real analysis engine. Closing as completed on main.

<!-- gh-comment-id:6380636218 --> @coygeek commented on GitHub (Oct 24, 2026): Checked on main at `d87e73f2db50be72d33a4b3c7199af35518b441c`: Network and console coverage now use domain-specific first-event or successful enable-acknowledgment times, preserving pre-reply events and keeping prior_activity_available false. A deterministic regression checks both boundaries. [CdpPageCapture.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/browser/CdpPageCapture.ts#L402) [CdpPageCapture.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/browser/CdpPageCapture.ts#L428) [CdpCaptureEvents.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/browser/CdpCaptureEvents.ts#L96) [CdpCaptureEvents.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/browser/CdpCaptureEvents.ts#L143) [CdpCaptureEvents.coverageStart.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/browser/CdpCaptureEvents.coverageStart.test.ts#L7) I ran the cited regression file on this revision; all passed as part of the focused 28-file, 603-test run. Provider-facing checks here use controlled fixtures; this does not claim a run with the real analysis engine. Closing as completed on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3794
No description provided.