[GH-ISSUE #4211] [Bug] Omitted initiator URLs cause unsupported passive network comparison results #1651

Closed
opened 2026-10-16 21:08:29 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

The offline compare-web-captures CLI reports network equality for two captures that explicitly omit the compared initiator URL, and reports a request modification when only one capture omits that URL. The omission is recorded in network_initiators completeness. Request identity, inventory, response facts, and the remaining initiator facts are fixed. These are unsupported equality and omission-only modification results, with no demonstrated hidden real initiator change.

Reproduction

With the source revision under REA version and its locked dependencies prepared, run npm run build:cached using the pinned toolchain. Save the following program as repro.mjs in a disposable work directory outside the checkout. It constructs already-recorded passive capture inputs, validates them with the built production schema, and compares nine pairs. The example.test URLs are inert identities. Each child CLI uses isolated home and configuration directories. The fixture timestamp comes from the local date command, rather than a guessed historical capture time.

import { strict as assert } from "node:assert";
import { writeFileSync, mkdirSync } from "node:fs";
import { spawnSync, execFileSync } from "node:child_process";
import { resolve, join } from "node:path";
import { pathToFileURL } from "node:url";
const repo = resolve(process.argv[2]);
const work = resolve(process.argv[3]);
const { captureSnapshotSchema } = await import(pathToFileURL(join(repo, "dist/domain/webCaptureDiffSchemas.js")));
mkdirSync(work, { recursive: true });
mkdirSync(join(work, "home"), { recursive: true });
mkdirSync(join(work, "config"), { recursive: true });
const stamp = execFileSync("/bin/date", ["+%Y-%m-%dT%H:%M:%S%z"], { encoding: "utf8" }).trim();
const time = new Date(stamp).toISOString();
const origin = "https://capture.example.test";
const dropped = Object.fromEntries(["scripts", "network_requests", "console_events", "websocket_connections", "websocket_frames", "webmcp_tools", "timeline_events", "total"].map(k => [k, 0]));
const retained = { inspection: {
  browser: { product: "normalized-fixture", protocol_version: "1.3", revision: "fixture", user_agent: "fixture", js_version: "fixture" },
  target: { target_id: "page", type: "page", title: "Fixture", url: origin, origin, attached: false },
  capture_window: { started_at: time, ended_at: time, observation_ms: 0 },
  completeness: { status: "complete_within_window", conditions: ["complete_within_window"], policy_filtered_sections: [], attach_limited_sections: [], truncated_sections: [], unavailable_sections: [], excluded: [{ section: "json_body_shapes", reason: "not_approved", count: null }], dropped_events: dropped },
  frames: [], dom: { total_nodes: 0, nodes: [] },
  accessibility: { total_nodes: 0, nodes: [], text_capture: { status: "not_approved", retained_bytes: 0, excluded_fields: 0 } },
  scripts: { total: 0, items: [] }, resources: [],
  network: { requests: [{ request_id: "request-1", url: origin + "/data", origin, method: "GET", resource_type: "Fetch", status: 200, mime_type: "application/json", encoded_data_length: 10, redirects: [], initiator: { type: "script", url: origin + "/main.js", line: 1, column: 0 }, body_shapes: { status: "not_approved", request: null, response: null } }], websocket_connections: [], coverage_started_at: time, prior_activity_available: false },
  console: { events: [], coverage_started_at: time, prior_activity_available: false }, workers: [],
  metadata: { responses: [], dom_urls: [], agent_hints: [], excluded_dom_urls: 0, headers_allowlisted: true },
  storage: { origin, usage_bytes: null, quota_bytes: null, local_storage_keys: [], session_storage_keys: [], indexed_db_names: [], cache_names: [], values_redacted: true }, limitations: []
}, webmcp: null };
const omitted = structuredClone(retained);
omitted.inspection.network.requests[0].initiator.url = null;
Object.assign(omitted.inspection.completeness, { status: "policy_filtered", conditions: ["policy_filtered"], policy_filtered_sections: ["network_initiators"] });
omitted.inspection.completeness.excluded.push({ section: "network_initiators", reason: "disallowed_origin", count: 1 });
const different = structuredClone(retained);
different.inspection.network.requests[0].initiator.url = origin + "/other.js";
const statusChanged = structuredClone(omitted);
statusChanged.inspection.network.requests[0].status = 201;
const unrelated = structuredClone(retained);
Object.assign(unrelated.inspection.completeness, { status: "policy_filtered", conditions: ["policy_filtered"], policy_filtered_sections: ["console_text"] });
const attach = value => {
 const result = structuredClone(value);
 result.inspection.completeness.attach_limited_sections = ["network_requests", "console_events", "websocket_connections", "websocket_frames"];
 result.inspection.completeness.conditions = ["attach_limited", ...result.inspection.completeness.conditions.filter(x => x !== "complete_within_window")];
 if (result.inspection.completeness.status === "complete_within_window") result.inspection.completeness.status = "attach_limited";
 return result;
};
const captures = { retained, omitted, omittedAgain: structuredClone(omitted), different, statusChanged, unrelated, attachRetained: attach(retained), attachOmitted: attach(omitted) };
const fixedRequest = value => { const r = structuredClone(value.inspection.network.requests); delete r[0].initiator.url; return r; };
assert.deepEqual(fixedRequest(retained), fixedRequest(omitted));
assert.deepEqual(fixedRequest(retained), fixedRequest(different));
for (const [name, value] of Object.entries(captures)) writeFileSync(join(work, name + ".json"), JSON.stringify(captureSnapshotSchema.parse(value)));
const cases = [["complete", "retained", "retained"], ["bothOmitted", "omitted", "omittedAgain"], ["oneOmitted", "retained", "omitted"], ["reverseOmitted", "omitted", "retained"], ["knownInitiatorChange", "retained", "different"], ["knownStatusChange", "omitted", "statusChanged"], ["unrelatedOmission", "retained", "unrelated"], ["attachComplete", "attachRetained", "attachRetained"], ["attachOmission", "attachRetained", "attachOmitted"]];
const results = {};
for (const [name, before, after] of cases) {
 const args = [join(repo, "scripts/rea.mjs"), "compare-web-captures", join(work, before + ".json"), join(work, after + ".json"), "--json"];
 const result = spawnSync(process.execPath, args, { encoding: "utf8", timeout: 30000, env: { ...process.env, HOME: join(work, "home"), XDG_CONFIG_HOME: join(work, "config") } });
 assert.equal(result.status, 0, result.stderr + result.stdout);
 writeFileSync(join(work, name + ".stdout.json"), result.stdout);
 const parsed = JSON.parse(result.stdout).normalized_result;
 results[name] = { exitCode: result.status, overall_status: parsed.overall_status, network: parsed.dimensions.network };
 console.log(name, JSON.stringify(results[name]));
}
writeFileSync(join(work, "results.json"), JSON.stringify(results, null, 2));

From the checkout root, run the program with the checkout and its disposable work directory as arguments. For example, when the program is saved in the directory named by comparison_work, run:

node "$comparison_work/repro.mjs" "$PWD" "$comparison_work"

The inspected request has method GET, URL ending in /data, resource type Fetch, response status 200, MIME type application/json, byte count 10, and no redirects. The omitted input differs from retained only by replacing the initiator URL with null and adding the matching completeness/exclusion entries. There is one request on both sides in the same order. The different control instead retains a different known initiator URL. The status control retains omitted URLs on both sides and changes the known response status to 201.

Expected behavior

The network dimension must preserve the explicit lack of initiator URL evidence. With no independently established difference, bothOmitted, oneOmitted, and reverseOmitted should report unknown and no modification inferred from the omitted URL. The attach-limited omission pair should likewise remain unknown unless a retained fact proves a difference. A null value accompanied by a recorded omission cannot establish either equality of the missing URLs or a changed initiator URL.

The complete self-comparison should remain unchanged. Known retained initiator URL differences and known response status differences must remain reportable as changes. An unrelated console omission must not make network comparison unknown. These requirements concern the URL fact that actually enters this comparison, rather than every optional initiator field or every comparison dimension.

Impact and scope

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded passive network metadata comparison gives unsupported equality or a false modification, including an overall changed result for omission-only pairs. The synthetic CLI reproduction is complete, and stored captures remain intact. No severe product-wide consequence, hidden real change, or live-provider frequency was established.
  • Workaround status: unverified
  • Workaround basis: A caller can inspect the retained completeness marker to distrust the result, but that does not restore the comparison's known-difference and unknown-evidence guarantees. No practical contract-preserving workaround was demonstrated.

REA version

Source checkout at 62bf09809ef7bb224028916424f8f07652a20f4f, built with npm run build:cached. The source package reports 6.5.0; this is not a reproduction of the published rea-agents-6.5.0 package. Current main 08a897b2cd7b359fcb0674c82baec3f1a74af632 was inspected: the cited contract, comparator, and public adapter files are byte-identical to the tested source revision. The baseline reproduction was not rerun on that newer main revision, and packaged-release applicability was not established.

Runtime and platform

macOS arm64; pinned Node.js v24.18.0 and npm 11.16.0; repository locked dependencies. No browser or external service ran. NOT_ENOUGH_INFO: the OS release was not recorded for the reproduction.

Target and artifact context

Synthetic, schema-validated normalized passive capture JSON files with one HTTP request and no redirects. The URLs are inert recorded identities and are never fetched. Request count, order, identity and known response facts are fixed except for the named controls. The initiator URL is the compared fact explicitly omitted by the fixture's network_initiators marker. These are already-recorded inputs; no actual filtering, redaction, origin-policy action, or protected value was exercised.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

The inline program execution made nine successful CLI calls. Every child exited 0. It returned the following network and overall statuses:

case                   network      changes             overall
complete               unchanged    none                unknown
bothOmitted            unchanged    none                unknown
oneOmitted             changed      one modified        changed
reverseOmitted         changed      one modified        changed
knownInitiatorChange   changed      one modified        changed
knownStatusChange      changed      one modified        changed
unrelatedOmission      unchanged    none                unknown
attachComplete         unknown      none                unknown
attachOmission         changed      one modified        changed

The incorrect equality result is specific to the network dimension; unrelated dimensions leave overall status unknown. The omission-only modifications also make overall status changed. Each modified entry uses the same request identity, net_7a1b22a9c7101a021881143f45152302fb26cb92504f1f9be0947cc571b67abc, with total_changes 1 and reason null. The known differences in the controls use that identity too. Both omitted fixtures contain null, so no real underlying initiator change was observed. The result persisted in a second equivalent execution; this does not establish real-browser occurrence frequency.

Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. The exact inline reproduction was independently rerun after a fresh source build. All nine public CLI calls exited 0. The unchanged existing network-comparison suite passed all 45 tests. MCP sharing is source-derived only; its transport was not run.

Typed outcome or recovery expected

Rerun the inline program and inspect the network dimension. Require unknown with no omission-only modification for bothOmitted, oneOmitted, reverseOmitted, and attachOmission. Keep complete and unrelatedOmission unchanged, keep attachComplete unknown, and retain one modification for each of knownInitiatorChange and knownStatusChange. Preserve the original capture facts and keep genuine differences reportable rather than dropping all initiator evidence or suppressing every network change on incomplete captures.

Analysis or suspected cause

Source-derived expected contract: the public comparison contract and the passive capture guide say missing or truncated evidence never proves equality. The guide also includes initiator locations among network facts. Passive input schemas, nullable initiator URL, and the completeness section enum admit this explicitly omitted compared fact.

Source-derived failure explanation: network comparison and request fingerprinting include the entire initiator object in the fingerprint while omitting network_initiators from the completeness handling and shared-known metadata projection. This explains why one omitted URL becomes a modification and two omissions permit equality. It does not establish a hidden real initiator change or require suppressing independently known differences.

Public reachability: the CLI parses passive file inputs, the application operation selects its adapter, and the adapter calls the pure comparator without browser access. The MCP registration shares this operation by source inspection only.

Additional context

Related closed #3172 handled unavailable redirect response metadata; this fixture has no redirects, and the tested source still reproduces the separate initiator URL result. Related #3386 concerns omitted duplicate requests and multiplicity; these inputs each retain one request in the same order. Open #3879 concerns script-source identity, and #3989 concerns dropped WebMCP observations. Their closure signals do not cover this compared URL and its explicit omission marker.

The complete-within-window inventory is a schema-valid synthetic input, not a claim that a live attach-only capture emits it. The attach-limited pair independently reproduces the omission-only modification. NOT_ENOUGH_INFO: real-browser occurrence frequency, behavior on other platforms, and a practical result-preserving workaround were not established. Published-package behavior was not tested.

Originally created by @coygeek on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4211 ### 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 Evidence, artifacts, or reconstruction ### Observed behavior The offline `compare-web-captures` CLI reports network equality for two captures that explicitly omit the compared initiator URL, and reports a request modification when only one capture omits that URL. The omission is recorded in `network_initiators` completeness. Request identity, inventory, response facts, and the remaining initiator facts are fixed. These are unsupported equality and omission-only modification results, with no demonstrated hidden real initiator change. ### Reproduction With the source revision under REA version and its locked dependencies prepared, run `npm run build:cached` using the pinned toolchain. Save the following program as `repro.mjs` in a disposable work directory outside the checkout. It constructs already-recorded passive capture inputs, validates them with the built production schema, and compares nine pairs. The `example.test` URLs are inert identities. Each child CLI uses isolated home and configuration directories. The fixture timestamp comes from the local date command, rather than a guessed historical capture time. ```javascript import { strict as assert } from "node:assert"; import { writeFileSync, mkdirSync } from "node:fs"; import { spawnSync, execFileSync } from "node:child_process"; import { resolve, join } from "node:path"; import { pathToFileURL } from "node:url"; const repo = resolve(process.argv[2]); const work = resolve(process.argv[3]); const { captureSnapshotSchema } = await import(pathToFileURL(join(repo, "dist/domain/webCaptureDiffSchemas.js"))); mkdirSync(work, { recursive: true }); mkdirSync(join(work, "home"), { recursive: true }); mkdirSync(join(work, "config"), { recursive: true }); const stamp = execFileSync("/bin/date", ["+%Y-%m-%dT%H:%M:%S%z"], { encoding: "utf8" }).trim(); const time = new Date(stamp).toISOString(); const origin = "https://capture.example.test"; const dropped = Object.fromEntries(["scripts", "network_requests", "console_events", "websocket_connections", "websocket_frames", "webmcp_tools", "timeline_events", "total"].map(k => [k, 0])); const retained = { inspection: { browser: { product: "normalized-fixture", protocol_version: "1.3", revision: "fixture", user_agent: "fixture", js_version: "fixture" }, target: { target_id: "page", type: "page", title: "Fixture", url: origin, origin, attached: false }, capture_window: { started_at: time, ended_at: time, observation_ms: 0 }, completeness: { status: "complete_within_window", conditions: ["complete_within_window"], policy_filtered_sections: [], attach_limited_sections: [], truncated_sections: [], unavailable_sections: [], excluded: [{ section: "json_body_shapes", reason: "not_approved", count: null }], dropped_events: dropped }, frames: [], dom: { total_nodes: 0, nodes: [] }, accessibility: { total_nodes: 0, nodes: [], text_capture: { status: "not_approved", retained_bytes: 0, excluded_fields: 0 } }, scripts: { total: 0, items: [] }, resources: [], network: { requests: [{ request_id: "request-1", url: origin + "/data", origin, method: "GET", resource_type: "Fetch", status: 200, mime_type: "application/json", encoded_data_length: 10, redirects: [], initiator: { type: "script", url: origin + "/main.js", line: 1, column: 0 }, body_shapes: { status: "not_approved", request: null, response: null } }], websocket_connections: [], coverage_started_at: time, prior_activity_available: false }, console: { events: [], coverage_started_at: time, prior_activity_available: false }, workers: [], metadata: { responses: [], dom_urls: [], agent_hints: [], excluded_dom_urls: 0, headers_allowlisted: true }, storage: { origin, usage_bytes: null, quota_bytes: null, local_storage_keys: [], session_storage_keys: [], indexed_db_names: [], cache_names: [], values_redacted: true }, limitations: [] }, webmcp: null }; const omitted = structuredClone(retained); omitted.inspection.network.requests[0].initiator.url = null; Object.assign(omitted.inspection.completeness, { status: "policy_filtered", conditions: ["policy_filtered"], policy_filtered_sections: ["network_initiators"] }); omitted.inspection.completeness.excluded.push({ section: "network_initiators", reason: "disallowed_origin", count: 1 }); const different = structuredClone(retained); different.inspection.network.requests[0].initiator.url = origin + "/other.js"; const statusChanged = structuredClone(omitted); statusChanged.inspection.network.requests[0].status = 201; const unrelated = structuredClone(retained); Object.assign(unrelated.inspection.completeness, { status: "policy_filtered", conditions: ["policy_filtered"], policy_filtered_sections: ["console_text"] }); const attach = value => { const result = structuredClone(value); result.inspection.completeness.attach_limited_sections = ["network_requests", "console_events", "websocket_connections", "websocket_frames"]; result.inspection.completeness.conditions = ["attach_limited", ...result.inspection.completeness.conditions.filter(x => x !== "complete_within_window")]; if (result.inspection.completeness.status === "complete_within_window") result.inspection.completeness.status = "attach_limited"; return result; }; const captures = { retained, omitted, omittedAgain: structuredClone(omitted), different, statusChanged, unrelated, attachRetained: attach(retained), attachOmitted: attach(omitted) }; const fixedRequest = value => { const r = structuredClone(value.inspection.network.requests); delete r[0].initiator.url; return r; }; assert.deepEqual(fixedRequest(retained), fixedRequest(omitted)); assert.deepEqual(fixedRequest(retained), fixedRequest(different)); for (const [name, value] of Object.entries(captures)) writeFileSync(join(work, name + ".json"), JSON.stringify(captureSnapshotSchema.parse(value))); const cases = [["complete", "retained", "retained"], ["bothOmitted", "omitted", "omittedAgain"], ["oneOmitted", "retained", "omitted"], ["reverseOmitted", "omitted", "retained"], ["knownInitiatorChange", "retained", "different"], ["knownStatusChange", "omitted", "statusChanged"], ["unrelatedOmission", "retained", "unrelated"], ["attachComplete", "attachRetained", "attachRetained"], ["attachOmission", "attachRetained", "attachOmitted"]]; const results = {}; for (const [name, before, after] of cases) { const args = [join(repo, "scripts/rea.mjs"), "compare-web-captures", join(work, before + ".json"), join(work, after + ".json"), "--json"]; const result = spawnSync(process.execPath, args, { encoding: "utf8", timeout: 30000, env: { ...process.env, HOME: join(work, "home"), XDG_CONFIG_HOME: join(work, "config") } }); assert.equal(result.status, 0, result.stderr + result.stdout); writeFileSync(join(work, name + ".stdout.json"), result.stdout); const parsed = JSON.parse(result.stdout).normalized_result; results[name] = { exitCode: result.status, overall_status: parsed.overall_status, network: parsed.dimensions.network }; console.log(name, JSON.stringify(results[name])); } writeFileSync(join(work, "results.json"), JSON.stringify(results, null, 2)); ``` From the checkout root, run the program with the checkout and its disposable work directory as arguments. For example, when the program is saved in the directory named by `comparison_work`, run: ```sh node "$comparison_work/repro.mjs" "$PWD" "$comparison_work" ``` The inspected request has method GET, URL ending in `/data`, resource type Fetch, response status 200, MIME type application/json, byte count 10, and no redirects. The `omitted` input differs from `retained` only by replacing the initiator URL with null and adding the matching completeness/exclusion entries. There is one request on both sides in the same order. The `different` control instead retains a different known initiator URL. The status control retains omitted URLs on both sides and changes the known response status to 201. ### Expected behavior The network dimension must preserve the explicit lack of initiator URL evidence. With no independently established difference, `bothOmitted`, `oneOmitted`, and `reverseOmitted` should report unknown and no modification inferred from the omitted URL. The attach-limited omission pair should likewise remain unknown unless a retained fact proves a difference. A null value accompanied by a recorded omission cannot establish either equality of the missing URLs or a changed initiator URL. The complete self-comparison should remain unchanged. Known retained initiator URL differences and known response status differences must remain reportable as changes. An unrelated console omission must not make network comparison unknown. These requirements concern the URL fact that actually enters this comparison, rather than every optional initiator field or every comparison dimension. ### Impact and scope - Impact level: P2 - Assessment status: supported - Impact basis: A bounded passive network metadata comparison gives unsupported equality or a false modification, including an overall changed result for omission-only pairs. The synthetic CLI reproduction is complete, and stored captures remain intact. No severe product-wide consequence, hidden real change, or live-provider frequency was established. - Workaround status: unverified - Workaround basis: A caller can inspect the retained completeness marker to distrust the result, but that does not restore the comparison's known-difference and unknown-evidence guarantees. No practical contract-preserving workaround was demonstrated. ### REA version Source checkout at [62bf09809ef7bb224028916424f8f07652a20f4f](https://github.com/morluto/rea/commit/62bf09809ef7bb224028916424f8f07652a20f4f), built with `npm run build:cached`. The source package reports 6.5.0; this is not a reproduction of the published `rea-agents-6.5.0` package. Current main `08a897b2cd7b359fcb0674c82baec3f1a74af632` was inspected: the cited contract, comparator, and public adapter files are byte-identical to the tested source revision. The baseline reproduction was not rerun on that newer main revision, and packaged-release applicability was not established. ### Runtime and platform macOS arm64; pinned Node.js v24.18.0 and npm 11.16.0; repository locked dependencies. No browser or external service ran. NOT_ENOUGH_INFO: the OS release was not recorded for the reproduction. ### Target and artifact context Synthetic, schema-validated normalized passive capture JSON files with one HTTP request and no redirects. The URLs are inert recorded identities and are never fetched. Request count, order, identity and known response facts are fixed except for the named controls. The initiator URL is the compared fact explicitly omitted by the fixture's `network_initiators` marker. These are already-recorded inputs; no actual filtering, redaction, origin-policy action, or protected value was exercised. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence The inline program execution made nine successful CLI calls. Every child exited 0. It returned the following network and overall statuses: ```text case network changes overall complete unchanged none unknown bothOmitted unchanged none unknown oneOmitted changed one modified changed reverseOmitted changed one modified changed knownInitiatorChange changed one modified changed knownStatusChange changed one modified changed unrelatedOmission unchanged none unknown attachComplete unknown none unknown attachOmission changed one modified changed ``` The incorrect equality result is specific to the network dimension; unrelated dimensions leave overall status unknown. The omission-only modifications also make overall status changed. Each modified entry uses the same request identity, `net_7a1b22a9c7101a021881143f45152302fb26cb92504f1f9be0947cc571b67abc`, with total_changes 1 and reason null. The known differences in the controls use that identity too. Both omitted fixtures contain null, so no real underlying initiator change was observed. The result persisted in a second equivalent execution; this does not establish real-browser occurrence frequency. Evidence provenance: observed. Local verification: reproduced. Reproduction completeness: complete. The exact inline reproduction was independently rerun after a fresh source build. All nine public CLI calls exited 0. The unchanged existing network-comparison suite passed all 45 tests. MCP sharing is source-derived only; its transport was not run. ### Typed outcome or recovery expected Rerun the inline program and inspect the network dimension. Require unknown with no omission-only modification for `bothOmitted`, `oneOmitted`, `reverseOmitted`, and `attachOmission`. Keep `complete` and `unrelatedOmission` unchanged, keep `attachComplete` unknown, and retain one modification for each of `knownInitiatorChange` and `knownStatusChange`. Preserve the original capture facts and keep genuine differences reportable rather than dropping all initiator evidence or suppressing every network change on incomplete captures. ### Analysis or suspected cause Source-derived expected contract: [the public comparison contract](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/contracts/browserToolContracts.ts#L235-L241) and [the passive capture guide](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/docs/browser-observation.md#L17-L22) say missing or truncated evidence never proves equality. The guide also [includes initiator locations among network facts](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/docs/browser-observation.md#L282-L309). [Passive input schemas](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/domain/webCaptureDiffSchemas.ts#L8-L24), [nullable initiator URL](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/domain/browserObservationSchemas.ts#L150-L155), and [the completeness section enum](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/domain/browserCompleteness.ts#L5-L29) admit this explicitly omitted compared fact. Source-derived failure explanation: [network comparison and request fingerprinting](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/domain/webCaptureDiff.ts#L248-L550) include the entire initiator object in the fingerprint while omitting `network_initiators` from the completeness handling and shared-known metadata projection. This explains why one omitted URL becomes a modification and two omissions permit equality. It does not establish a hidden real initiator change or require suppressing independently known differences. Public reachability: [the CLI](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/cli/browserAdvancedCommands.ts#L115-L151) parses passive file inputs, [the application operation](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/application/BrowserObservationService.ts#L138-L158) selects its adapter, and [the adapter](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/browser/CdpBrowserProvider.ts#L225-L234) calls the pure comparator without browser access. [The MCP registration](https://github.com/morluto/rea/blob/62bf09809ef7bb224028916424f8f07652a20f4f/src/server/registerBrowserTools.ts#L133-L144) shares this operation by source inspection only. ### Additional context Related closed [#3172](https://github.com/morluto/rea/issues/3172) handled unavailable redirect response metadata; this fixture has no redirects, and the tested source still reproduces the separate initiator URL result. Related [#3386](https://github.com/morluto/rea/issues/3386) concerns omitted duplicate requests and multiplicity; these inputs each retain one request in the same order. Open [#3879](https://github.com/morluto/rea/issues/3879) concerns script-source identity, and [#3989](https://github.com/morluto/rea/issues/3989) concerns dropped WebMCP observations. Their closure signals do not cover this compared URL and its explicit omission marker. The complete-within-window inventory is a schema-valid synthetic input, not a claim that a live attach-only capture emits it. The attach-limited pair independently reproduces the omission-only modification. NOT_ENOUGH_INFO: real-browser occurrence frequency, behavior on other platforms, and a practical result-preserving workaround were not established. Published-package behavior was not tested.
Author
Owner

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

Checked the reported fix on main at d87e73f2db.

networkDimension now derives joint initiator completeness, excludes initiator facts from both comparison maps when incomplete, and includes initiator completeness in equality eligibility. Thus a one-sided omitted URL cannot generate a modification, while both omitted cannot establish equality. Response facts remain compared.

Implementation: webCaptureDiff.ts, webCaptureDiff.ts.

The relevant regression checks in passiveCaptureCorrespondence.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380743211 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. networkDimension now derives joint initiator completeness, excludes initiator facts from both comparison maps when incomplete, and includes initiator completeness in equality eligibility. Thus a one-sided omitted URL cannot generate a modification, while both omitted cannot establish equality. Response facts remain compared. Implementation: [webCaptureDiff.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/webCaptureDiff.ts#L473), [webCaptureDiff.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/webCaptureDiff.ts#L559). The relevant regression checks in [passiveCaptureCorrespondence.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/acceptance/tools/passiveCaptureCorrespondence.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. 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#1651
No description provided.