[GH-ISSUE #5892] [Bug] Completed browser requests gain phantom unfinished-event omissions under budget pressure #3571

Closed
opened 2026-10-31 00:41:59 +00:00 by bsakaguchi · 0 comments
Owner

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

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 completed browser request gains a phantom request-unfinished omission when the event budget rejects its request-finished metadata.

With one completed HTTP-200 document request and capture.limits.max_events: 2, the public CLI retains the request and response, then reports two dropped events: one request-finished and one request-unfinished. The per-transaction omission row and limitations also include the nonexistent unfinished phase.

This is an omission-accounting error. No retained request-unfinished event or response-unfinished body state appeared in the execution.

Reproduction

Executed the built capture-browser-scenario CLI against a local HTTP fixture and real Chrome. The page contains only a title, text, and a data-URL favicon, so it generates one document request.

  1. Start a fixture server:

    node --input-type=module -e 'import http from "node:http"; http.createServer((req,res)=>{res.setHeader("Content-Type","text/html");res.end(`<!doctype html><title>Complete</title><link rel="icon" href="data:,">done`);}).listen(3000,"127.0.0.1")'
    
  2. Save the following as scenario.json, setting browser.executable_path to an installed Chrome-family executable:

    {
      "browser": {
        "mode": "launch",
        "executable_path": "BROWSER_EXECUTABLE"
      },
      "start_url": { "url": "http://127.0.0.1:3000/" },
      "actions": [
        {
          "step_id": "settle",
          "action": "wait_for_timeout",
          "duration_ms": 100
        }
      ],
      "capture": {
        "events": ["network"],
        "network": { "response_body": true },
        "limits": { "max_events": 2 }
      }
    }
    
  3. From the prepared source checkout, run:

    node scripts/rea.mjs capture-browser-scenario scenario.json --json
    
  4. Inspect normalized_result.events. Both request-finished and request-unfinished appear in the omission records for the single completed transaction.

  5. Remove the limits entry and repeat as a control. The control retains request, response, request-finished, and captured response content, with no omissions.

  6. Stop the fixture server.

The executed fixture selected a dynamically allocated loopback port; 3000 above is a reusable equivalent. One run of each case was performed. A third run with max_total_event_allocation_bytes: 6000 also recorded the phantom unfinished omission, alongside omitted response and completion metadata.

Expected behavior

Budget omission must preserve the observed request lifecycle. A completion event may be omitted, but finalization should not synthesize an unfinished phase for that completed transaction.

The capture contract describes omissions as counts of omitted events and identifies the corresponding per-transaction kinds. It reserves request-unfinished for requests still in flight.

Impact and scope

P2: bounded errors in retained Evidence omission accounting and diagnostics under budget pressure. In the event-count reproduction, events.dropped is 2 although only one applicable terminal metadata event was rejected; the second entry claims an omitted unfinished phase for a completed request.

The capture already reports incomplete coverage, and the execution did not establish false equality, body corruption, or a retained false unfinished event. Removing the small budget restored correct output for the control fixture. Live-provider frequency and other platforms were not measured.

REA version

Source revision 52415975942a4b9c53a817915b313ccc404f280e; package manifest version 6.7.0. Executed the prepared source checkout's built CLI, rather than an independently installed published package.

Runtime and platform

macOS 27.0.1 arm64; Node.js v26.12.0; Google Chrome 156.0.8078.47. Real browser execution; no simulated provider.

Target and artifact context

A local HTTP server returning one 73-byte HTML document and HTTP 200. The page uses a data-URL favicon and has no subresources. Network events and response-body capture are selected.

Interface

CLI

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

All three public CLI calls exited 0 with empty stderr. Relevant results:

case                     retained  dropped  retained kinds
default budgets          4         0        request,response,request-finished,network-content
max_events=2             2         2        request,response
aggregate byte limit     1         3        request

For max_events=2:

{
  "by_kind": [
    {
      "kind": "request-finished",
      "count": 1,
      "reasons": ["max-events"]
    },
    {
      "kind": "request-unfinished",
      "count": 1,
      "reasons": ["max-events"]
    }
  ],
  "network_transactions": [
    {
      "transaction_id": "request-1",
      "omitted_kinds": ["request-finished", "request-unfinished"],
      "reasons": ["max-events"]
    }
  ],
  "network_transaction_range": null
}

The limitations include:

Network request-finished metadata was omitted for request-1 because max-events was reached; network coverage is incomplete.
Network request-unfinished metadata was omitted for request-1 because max-events was reached; network coverage is incomplete.

The rejected request-finished record establishes that the completion callback reached the collector. The control retained the completed response body. The fixture server received exactly three document requests across the three captures.

Typed outcome or recovery expected

Retain the actual request-finished omission and the incomplete-coverage state. Do not add a request-unfinished omission, dropped-event count, or unfinished-phase limitation solely because the completed phase could not be retained.

Analysis or suspected cause

Source-derived: finished() returns on rejected metadata admission before setting transaction.terminal = "finished". finish() consequently treats the transaction as unfinished.

The real producer admission records another omission while rejecting that synthetic phase. Its budget is monotonic and the metadata inputs are the same, which explains why the observed result is a phantom omission rather than a retained false event or unavailable-body record.

The adjacent failed() path records terminal state before metadata admission. Recording completion independently of event retention appears to address this lifecycle mismatch; no fix was applied or tested.

Additional context

Current main c324d31b45fcab77e4ed7320ed60f0bf5a4ba188 was fetched and inspected. The network collector, event admission, CLI adapter, and cited capture contract were unchanged from the tested baseline. Execution on that newer revision was not repeated.

CLI wiring and the MCP tool share the scenario provider by source inspection; MCP transport was not exercised.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5892 ### 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 completed browser request gains a phantom `request-unfinished` omission when the event budget rejects its `request-finished` metadata. With one completed HTTP-200 document request and `capture.limits.max_events: 2`, the public CLI retains the request and response, then reports two dropped events: one `request-finished` and one `request-unfinished`. The per-transaction omission row and limitations also include the nonexistent unfinished phase. This is an omission-accounting error. No retained `request-unfinished` event or `response-unfinished` body state appeared in the execution. ### Reproduction Executed the built `capture-browser-scenario` CLI against a local HTTP fixture and real Chrome. The page contains only a title, text, and a data-URL favicon, so it generates one document request. 1. Start a fixture server: ```sh node --input-type=module -e 'import http from "node:http"; http.createServer((req,res)=>{res.setHeader("Content-Type","text/html");res.end(`<!doctype html><title>Complete</title><link rel="icon" href="data:,">done`);}).listen(3000,"127.0.0.1")' ``` 2. Save the following as `scenario.json`, setting `browser.executable_path` to an installed Chrome-family executable: ```json { "browser": { "mode": "launch", "executable_path": "BROWSER_EXECUTABLE" }, "start_url": { "url": "http://127.0.0.1:3000/" }, "actions": [ { "step_id": "settle", "action": "wait_for_timeout", "duration_ms": 100 } ], "capture": { "events": ["network"], "network": { "response_body": true }, "limits": { "max_events": 2 } } } ``` 3. From the prepared source checkout, run: ```sh node scripts/rea.mjs capture-browser-scenario scenario.json --json ``` 4. Inspect `normalized_result.events`. Both `request-finished` and `request-unfinished` appear in the omission records for the single completed transaction. 5. Remove the `limits` entry and repeat as a control. The control retains request, response, request-finished, and captured response content, with no omissions. 6. Stop the fixture server. The executed fixture selected a dynamically allocated loopback port; 3000 above is a reusable equivalent. One run of each case was performed. A third run with `max_total_event_allocation_bytes: 6000` also recorded the phantom unfinished omission, alongside omitted response and completion metadata. ### Expected behavior Budget omission must preserve the observed request lifecycle. A completion event may be omitted, but finalization should not synthesize an unfinished phase for that completed transaction. The [capture contract](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/browser-scenario-contract.md#L215-L230) describes omissions as counts of omitted events and identifies the corresponding per-transaction kinds. It [reserves request-unfinished for requests still in flight](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/browser-scenario-contract.md#L356-L360). ### Impact and scope P2: bounded errors in retained Evidence omission accounting and diagnostics under budget pressure. In the event-count reproduction, `events.dropped` is 2 although only one applicable terminal metadata event was rejected; the second entry claims an omitted unfinished phase for a completed request. The capture already reports incomplete coverage, and the execution did not establish false equality, body corruption, or a retained false unfinished event. Removing the small budget restored correct output for the control fixture. Live-provider frequency and other platforms were not measured. ### REA version Source revision `52415975942a4b9c53a817915b313ccc404f280e`; package manifest version `6.7.0`. Executed the prepared source checkout's built CLI, rather than an independently installed published package. ### Runtime and platform macOS 27.0.1 arm64; Node.js v26.12.0; Google Chrome 156.0.8078.47. Real browser execution; no simulated provider. ### Target and artifact context A local HTTP server returning one 73-byte HTML document and HTTP 200. The page uses a data-URL favicon and has no subresources. Network events and response-body capture are selected. ### Interface CLI ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence All three public CLI calls exited 0 with empty stderr. Relevant results: ```text case retained dropped retained kinds default budgets 4 0 request,response,request-finished,network-content max_events=2 2 2 request,response aggregate byte limit 1 3 request ``` For `max_events=2`: ```json { "by_kind": [ { "kind": "request-finished", "count": 1, "reasons": ["max-events"] }, { "kind": "request-unfinished", "count": 1, "reasons": ["max-events"] } ], "network_transactions": [ { "transaction_id": "request-1", "omitted_kinds": ["request-finished", "request-unfinished"], "reasons": ["max-events"] } ], "network_transaction_range": null } ``` The limitations include: ```text Network request-finished metadata was omitted for request-1 because max-events was reached; network coverage is incomplete. Network request-unfinished metadata was omitted for request-1 because max-events was reached; network coverage is incomplete. ``` The rejected `request-finished` record establishes that the completion callback reached the collector. The control retained the completed response body. The fixture server received exactly three document requests across the three captures. ### Typed outcome or recovery expected Retain the actual `request-finished` omission and the incomplete-coverage state. Do not add a `request-unfinished` omission, dropped-event count, or unfinished-phase limitation solely because the completed phase could not be retained. ### Analysis or suspected cause Source-derived: [finished()](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/browser/network/PlaywrightScenarioNetwork.ts#L240-L273) returns on rejected metadata admission before setting `transaction.terminal = "finished"`. [finish()](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/browser/network/PlaywrightScenarioNetwork.ts#L315-L357) consequently treats the transaction as unfinished. The real [producer admission](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/browser/PlaywrightScenarioEvents.ts#L286-L318) records another omission while rejecting that synthetic phase. Its budget is monotonic and the metadata inputs are the same, which explains why the observed result is a phantom omission rather than a retained false event or unavailable-body record. The adjacent [failed()](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/browser/network/PlaywrightScenarioNetwork.ts#L276-L290) path records terminal state before metadata admission. Recording completion independently of event retention appears to address this lifecycle mismatch; no fix was applied or tested. ### Additional context Current main `c324d31b45fcab77e4ed7320ed60f0bf5a4ba188` was fetched and inspected. The network collector, event admission, CLI adapter, and cited capture contract were unchanged from the tested baseline. Execution on that newer revision was not repeated. CLI wiring and the MCP tool share the scenario provider by source inspection; MCP transport was not exercised.
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#3571
No description provided.