[GH-ISSUE #5609] [Bug] observe-web-session returns pre-window lifecycle milestones as observed activity #3401

Closed
opened 2026-10-31 00:39:57 +00:00 by bsakaguchi · 1 comment
Owner

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

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

On an already-loaded, untouched page, observe-web-session returns lifecycle milestones from before the command started as ordinary timeline entries, with no historical-state or pre-window marker. An independently reproduced two-second capture exited 0 with empty stderr and window.end_reason: "window_elapsed", but included old commit, domcontentloaded, load, networkalmostidle, and networkidle events. The local fixture received zero requests during capture; no navigation event appeared and the independently sampled navigation-start value stayed unchanged.

Reproduction

An independent verifier ran this through the public CLI, including a repeat under the repository-pinned Node runtime:

  1. Serve <!doctype html><title>Fixture</title><h1>Idle</h1> over HTTP on 127.0.0.1. Launch an installed Chromium with a disposable profile and a loopback CDP endpoint, using playwright-core; allow page requests only to the fixture origin.

  2. Navigate to the fixture with waitUntil: "networkidle", then wait another 2000 ms. Through an independent CDP session, call Performance.enable, sample Performance.getMetrics's Timestamp, and obtain the page target ID with Target.getTargetInfo.

  3. From the built source checkout, run the following command against that endpoint and target, leaving the page untouched. CDP_PORT and TARGET_ID below stand for the values obtained from this browser:

    node scripts/rea.mjs observe-web-session "http://127.0.0.1:${CDP_PORT}" "${TARGET_ID}" --observation-ms 2000 --json
    
  4. Inspect normalized_result.timeline: its lifecycle timestamps precede even the independent pre-command Timestamp. Count fixture requests during capture to confirm there were none. Close the CDP session, browser context and HTTP server after capture; the verifier confirmed cleanup.

The verifier also ran a 6000 ms capture with an independent reload during the window, and a 3000 ms capture with an independent history push. Both retained real in-window events alongside historical lifecycle entries. The idle symptom reproduced in the pinned-runtime three-case harness and the portable inline reproduction; the exact historical event count varied.

Expected behavior

Lifecycle milestones from a load completed before observation should either be absent from observed activity or explicitly identified as historical state. Genuine lifecycle events during the window must remain available.

The documented contract in docs/browser-observation.md:20 and src/contracts/browserToolContracts.ts:197 arms a requested observation window for external user action. The returned limitation in src/browser/CdpObservationSession.ts:117-126 says: "The timeline begins after REA attaches; earlier navigation and network activity is unavailable."

Impact and scope

Old load milestones can be mistaken for activity during the requested window, compromising interpretation of the returned timeline. The independently tested reload and same-document navigation remained observable; the defect concerns timeline content, not an actual reload or failed cleanup. This is a bounded evidence-integrity defect (P2 in the independent assessment).

NOT_ENOUGH_INFO: frequency outside the tested local Chromium/macOS setup, and a practical REA-only workaround preserving all valid lifecycle events, were not established. Dropping every event for the initial loader is not proven safe because that loader can still emit legitimate activity during observation.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

macOS arm64; repository-pinned Node.js 24.18.0. Driver: playwright-core 1.58.2. Browser: separately installed Playwright Chromium build 1248, Chrome 156.0.8078.4, explicitly selected rather than the driver's default browser revision.

Target and artifact context

A static HTML fixture served only on 127.0.0.1, already loaded in a disposable Chromium page. No external site or API was used.

Interface

CLI

Earliest failure stage

Capture or observation

Relevant output or evidence

The independent pinned-runtime run sampled the browser's monotonic Timestamp before spawning REA:

Pre-command Timestamp:       406333.696746
First five lifecycle times: 406331.166126 through 406331.197718
Requests during capture:    0
CLI exit:                   0
stderr:                     empty
window.end_reason:          window_elapsed

Those lifecycle entries predate even CLI launch by more than two seconds. That run also included an old interactivetime milestone at 406331.183902.

Discriminating controls: the reload capture returned old milestones first, followed by navigation/lifecycle events for the new loader. Its new load timestamp 406338.069337 followed the independent pre-reload clock sample 406338.060173. The history-push control returned a real same_document_navigation event plus prior-load lifecycle entries. These comparisons use the same browser monotonic clock, not ISO wall time.

Typed outcome or recovery expected

A successful bounded observation should distinguish any retained pre-window lifecycle state from newly observed activity. In-window reload lifecycle events and same-document navigation should remain present.

Analysis or suspected cause

Source-derived reasoning: in src/browser/CdpObservationSession.ts:61-87, the listener is installed before Page.setLifecycleEventsEnabled and before armed_at is recorded. The main-frame lifecycle handler at :317-325 admits events without a historical distinction; :339-348 retains protocol timestamps without a window cutoff or historical-state marker. This supports the runtime observation, rather than establishing a proposed fix.

Additional context

Prepared with AI assistance and independently reproduced through the public CLI. Freshly fetched origin/main at c3d7d0aafc4943c82a5a8513cd3da7f4a7cfe643 includes the long-window timer fix #5605. Source inspection of its diff shows changes to timer/cancellation handling, with the listener setup and lifecycle admission path unchanged; it does not distinguish historical lifecycle entries. No fresh runtime reproduction on that revision is claimed.

Issue #5440 concerns contradictory cleanup reporting in other browser operations and is a different defect. This reproduction completed successfully and demonstrates incorrect timeline content.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5609 ### 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 On an already-loaded, untouched page, `observe-web-session` returns lifecycle milestones from before the command started as ordinary timeline entries, with no historical-state or pre-window marker. An independently reproduced two-second capture exited 0 with empty stderr and `window.end_reason: "window_elapsed"`, but included old `commit`, `domcontentloaded`, `load`, `networkalmostidle`, and `networkidle` events. The local fixture received zero requests during capture; no navigation event appeared and the independently sampled navigation-start value stayed unchanged. ### Reproduction An independent verifier ran this through the public CLI, including a repeat under the repository-pinned Node runtime: 1. Serve `<!doctype html><title>Fixture</title><h1>Idle</h1>` over HTTP on `127.0.0.1`. Launch an installed Chromium with a disposable profile and a loopback CDP endpoint, using `playwright-core`; allow page requests only to the fixture origin. 2. Navigate to the fixture with `waitUntil: "networkidle"`, then wait another 2000 ms. Through an independent CDP session, call `Performance.enable`, sample `Performance.getMetrics`'s `Timestamp`, and obtain the page target ID with `Target.getTargetInfo`. 3. From the built source checkout, run the following command against that endpoint and target, leaving the page untouched. `CDP_PORT` and `TARGET_ID` below stand for the values obtained from this browser: ```sh node scripts/rea.mjs observe-web-session "http://127.0.0.1:${CDP_PORT}" "${TARGET_ID}" --observation-ms 2000 --json ``` 4. Inspect `normalized_result.timeline`: its lifecycle timestamps precede even the independent pre-command `Timestamp`. Count fixture requests during capture to confirm there were none. Close the CDP session, browser context and HTTP server after capture; the verifier confirmed cleanup. The verifier also ran a 6000 ms capture with an independent reload during the window, and a 3000 ms capture with an independent history push. Both retained real in-window events alongside historical lifecycle entries. The idle symptom reproduced in the pinned-runtime three-case harness and the portable inline reproduction; the exact historical event count varied. ### Expected behavior Lifecycle milestones from a load completed before observation should either be absent from observed activity or explicitly identified as historical state. Genuine lifecycle events during the window must remain available. The documented contract in `docs/browser-observation.md:20` and `src/contracts/browserToolContracts.ts:197` arms a requested observation window for external user action. The returned limitation in `src/browser/CdpObservationSession.ts:117-126` says: "The timeline begins after REA attaches; earlier navigation and network activity is unavailable." ### Impact and scope Old load milestones can be mistaken for activity during the requested window, compromising interpretation of the returned timeline. The independently tested reload and same-document navigation remained observable; the defect concerns timeline content, not an actual reload or failed cleanup. This is a bounded evidence-integrity defect (P2 in the independent assessment). NOT_ENOUGH_INFO: frequency outside the tested local Chromium/macOS setup, and a practical REA-only workaround preserving all valid lifecycle events, were not established. Dropping every event for the initial loader is not proven safe because that loader can still emit legitimate activity during observation. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform macOS arm64; repository-pinned Node.js 24.18.0. Driver: playwright-core 1.58.2. Browser: separately installed Playwright Chromium build 1248, Chrome 156.0.8078.4, explicitly selected rather than the driver's default browser revision. ### Target and artifact context A static HTML fixture served only on `127.0.0.1`, already loaded in a disposable Chromium page. No external site or API was used. ### Interface CLI ### Earliest failure stage Capture or observation ### Relevant output or evidence The independent pinned-runtime run sampled the browser's monotonic `Timestamp` before spawning REA: ```text Pre-command Timestamp: 406333.696746 First five lifecycle times: 406331.166126 through 406331.197718 Requests during capture: 0 CLI exit: 0 stderr: empty window.end_reason: window_elapsed ``` Those lifecycle entries predate even CLI launch by more than two seconds. That run also included an old `interactivetime` milestone at `406331.183902`. Discriminating controls: the reload capture returned old milestones first, followed by navigation/lifecycle events for the new loader. Its new `load` timestamp `406338.069337` followed the independent pre-reload clock sample `406338.060173`. The history-push control returned a real `same_document_navigation` event plus prior-load lifecycle entries. These comparisons use the same browser monotonic clock, not ISO wall time. ### Typed outcome or recovery expected A successful bounded observation should distinguish any retained pre-window lifecycle state from newly observed activity. In-window reload lifecycle events and same-document navigation should remain present. ### Analysis or suspected cause Source-derived reasoning: in `src/browser/CdpObservationSession.ts:61-87`, the listener is installed before `Page.setLifecycleEventsEnabled` and before `armed_at` is recorded. The main-frame lifecycle handler at `:317-325` admits events without a historical distinction; `:339-348` retains protocol timestamps without a window cutoff or historical-state marker. This supports the runtime observation, rather than establishing a proposed fix. ### Additional context Prepared with AI assistance and independently reproduced through the public CLI. Freshly fetched `origin/main` at `c3d7d0aafc4943c82a5a8513cd3da7f4a7cfe643` includes the long-window timer fix #5605. Source inspection of its diff shows changes to timer/cancellation handling, with the listener setup and lifecycle admission path unchanged; it does not distinguish historical lifecycle entries. No fresh runtime reproduction on that revision is claimed. Issue #5440 concerns contradictory cleanup reporting in other browser operations and is a different defect. This reproduction completed successfully and demonstrates incorrect timeline content.
Author
Owner

@morluto commented on GitHub (Oct 21, 2026):

Closing as fixed on main by github.com/morluto/rea@2a0410f171, retained at 67a6477f01.

The observation owner samples the browser monotonic timestamp after subscription and filters pre-window lifecycle facts, retaining incomparable timestamps as context. Boundary and public CLI/stdio MCP regressions cover replayed old milestones and genuine current-window events. No new real-Chrome run is claimed.

This closure follows current-main producer and regression source inspection and the existing issue evidence. Tests and runtime reproductions were not rerun in this audit; no additional platform/provider coverage is implied.

<!-- gh-comment-id:6324272522 --> @morluto commented on GitHub (Oct 21, 2026): Closing as fixed on main by https://github.com/morluto/rea/commit/2a0410f1717b3126e8a95063d77dea66a597010a, retained at 67a6477f011ae358fc333e5a6e26c86d932654ed. The observation owner samples the browser monotonic timestamp after subscription and filters pre-window lifecycle facts, retaining incomparable timestamps as context. Boundary and public CLI/stdio MCP regressions cover replayed old milestones and genuine current-window events. No new real-Chrome run is claimed. This closure follows current-main producer and regression source inspection and the existing issue evidence. Tests and runtime reproductions were not rerun in this audit; no additional platform/provider coverage is implied.
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#3401
No description provided.