[PR #3125] [CLOSED] fix(browser): preserve DOM capture uncertainty and frame ancestry #5632

Closed
opened 2026-10-31 01:08:08 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/3125
Author: @ShuaixinHuang
Created: 10/14/2026
Status: ❌ Closed

Base: main ← Head: codex/fix-passive-dom-comparison


📝 Commits (2)

  • 28a5474 fix(browser): preserve DOM capture uncertainty and frame ancestry
  • 04e053a fix(browser): mark unresolved child frame parents incomplete

📊 Changes

6 files changed (+501 additions, -31 deletions)

View changed files

📝 docs/browser-observation.md (+11 -0)
📝 src/browser/CdpCaptureDocuments.ts (+49 -10)
📝 src/domain/webCaptureDiff.ts (+64 -21)
➕ src/domain/webCaptureFrameComparison.test.ts (+62 -0)
➕ src/domain/webCaptureFrameComparison.ts (+49 -0)
➕ tests/boundary/browser/cdpDomComparisonCompleteness.test.ts (+266 -0)

📄 Description

Summary

Passive page comparison could report a website change when CDP omitted a DOM node, or report DOM equality when a document's required nodes object was absent. Complete frame captures also lost their parent/child relationships during comparison. This change preserves capture uncertainty and compares complete frame ancestry without relying on volatile frame IDs.

No new issue is opened: the independent reproductions are included in the regression tests below. Related to #2553, which concerns position loss inside malformed aligned node arrays; this change addresses missing snapshot containers and downstream comparison of partial collections.

Problem and expected behavior

Production-boundary input Before After
Same two-node document, but one node's attributes are unavailable dom_structure: changed, including a false modification unknown; retained facts and omissions remain visible
Snapshot document lacks nodes; compare two such captures Empty DOM with no omission, then unchanged DOM and derived metadata incomplete, then unknown
Complete frames change from main -> {a, b} to main -> a -> b Frame relationship ignored, unchanged DOM dimension changed with a frames modification
Complete frame ancestry changes while DOM nodes are incomplete Collection differences conflate evidence and omissions Complete frame difference still proves changed; metadata stays unknown

Change and scope

  • captureDom owns malformed snapshot accounting. Missing/invalid document collections, invalid document entries, missing string tables and missing node objects use the existing completeness ledger. Skipped DOM facts also make their derived URL and agent-hint inventory incomplete; valid facts from other documents remain retained.
  • DOM comparison evaluates complete node and frame inventories independently. Whole partial DOM or metadata collection digests cannot establish modification by themselves. Node-count omissions also prevent DOM equality even if a historical completeness ledger does not list them.
  • Frame forests are fingerprinted from URL, origin and unordered child fingerprints. IDs only resolve references within one capture. Duplicate IDs, missing predecessors and cyclic ancestry remain unknown. Iterative processing avoids recursive serialization of deep ancestry and retains repeated sibling counts.
  • Remove the prior frame URL-only projection; document the resulting semantics in the browser observation guide.

Contribution scope: routine correction within the existing passive capture and offline comparison boundaries. A new-provider or execution-authority decision is not applicable. No target execution, mutation, provider installation or protection-recovery capability is introduced. This PR makes no measured performance improvement claim.

Contract and boundary impact

  • Semantic owner: CDP snapshot normalization and domain capture comparison.
  • CLI/MCP: existing compare-web-captures / compare_web_captures inputs and result schema remain applicable; a frame-only observed difference uses the existing generic change identity frames.
  • Evidence: retain observed partial facts, explicitly record omissions, and prevent incomplete aggregate inventories from proving equality or modification. Independently complete frame/node evidence can still prove change.
  • Provider/packaging: same CDP commands and authority; no package, catalog or generated artifact changes are committed.
  • Effects: read supplied capture data and normalize the existing passive CDP response. Tests use a local simulated CDP endpoint, compiled CLI subprocesses and an in-memory MCP transport.

Evidence and regression coverage

cdpDomComparisonCompleteness.test.ts uses the production provider over actual loopback HTTP/WebSocket transport, the production input schema and comparator. It covers missing/null node objects, missing/invalid/empty document collections, invalid entries mixed with valid documents, missing strings, node/link omissions in both directions, valid node changes, ID renaming, frame ancestry changes, and independently complete frame changes. Its public CLI case reads normalized capture JSON files and checks the returned Evidence for all three principal behaviors.

webCaptureFrameComparison.test.ts covers duplicate IDs, missing parents, self/indirect cycles, sibling multiplicity/order, renamed IDs, and a 5,000-frame chain.

Executed baseline: f3534749e564421036ac445f536fc766b038208f. The initial 11-case production-boundary suite had 10 failures on untouched production code and one valid-change control passed. After correction, all 13 boundary cases and all 6 graph cases pass. The final branch is based on 5830088 (the intervening upstream change only updates release documentation).

Separate public MCP calls through the named tool contract, SDK in-memory client/server transport, production service and result delivery returned the expected DOM status for all three cases: partial DOM unknown, changed complete ancestry changed, missing nodes unknown. These calls do not claim real-browser verification.

Validation performed

Windows x64, Node 24.18.0 and npm 11.16.0, locked dependencies:

  • npm run test:focused -- tests/boundary/browser/cdpDomComparisonCompleteness.test.ts src/domain/webCaptureFrameComparison.test.ts: 19 passed, including the compiled public CLI workflow.
  • Expanded npm run test:focused selection of 16 relevant browser/domain/MCP files: 234 passed, 3 skipped, 1 failed. The skipped SVG tests require an installed Chrome-family executable. The existing browserComparisonInput.test.ts:155 fails advertised JSON-schema validation (expected true to be false). Restoring both changed production files byte-for-byte from f3534749 and running that exact MCP test reproduces the same failure at the same assertion. The test was not weakened or skipped.
  • npm run check: all six tasks and architecture guards passed, zero lint errors.
  • npm run docs:check: passed, including 5 README consistency tests.
  • npm run verify:test-discovery: all 1,244 files discovered exactly once.
  • git diff --check: passed.

Verification limits: no real Chrome capture, Linux/macOS execution, or complete repository-wide test suite is claimed. Frame reparenting uses a simplified simulated DOM; real browser scenarios may also expose changes in other dimensions. Required current-head CI remains a maintainer merge gate; the known baseline MCP failure and skipped real-browser coverage are reported above.

Before merge

  • Concrete scoped problem, Conventional Commit title, and owning documentation.
  • Production-boundary regressions, meaningful negative paths, and actual CLI/MCP output.
  • Partial facts, provenance and explicit uncertainty remain distinguishable.
  • Diff checked for unrelated files, generated artifacts and credentials.
  • Required current-head CI and applicable real-browser verification accepted by the maintainer.

Review follow-up

Addressed the child-frame parent finding at the producing boundary. captureFrames now distinguishes the enclosing tree's root from child records: only a root may omit parent evidence. Missing, null, non-string or empty child parentId values mark frame coverage incomplete instead of making a child appear to be an independent root. Existing root behavior and other retained frame facts are preserved; the domain comparator consumes that ledger without guessing ancestry. Four new production-boundary cases fail on the previous head and pass after this correction, including comparison against complete evidence and self-comparison. Five focused files passed (70 tests, including the public CLI regressions); npm run check passed all six tasks and architecture guards. The previously documented baseline MCP-schema failure and real-browser verification limits still apply. No further cleanup is needed for this producing path.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/3125 **Author:** [@ShuaixinHuang](https://github.com/ShuaixinHuang) **Created:** 10/14/2026 **Status:** ❌ Closed **Base:** `main` ← **Head:** `codex/fix-passive-dom-comparison` --- ### 📝 Commits (2) - [`28a5474`](https://github.com/morluto/rea/commit/28a5474459058556a9f69471b0194e7436ba3386) fix(browser): preserve DOM capture uncertainty and frame ancestry - [`04e053a`](https://github.com/morluto/rea/commit/04e053ae29c4f23b5ad16bb18585a70c25d3df90) fix(browser): mark unresolved child frame parents incomplete ### 📊 Changes **6 files changed** (+501 additions, -31 deletions) <details> <summary>View changed files</summary> 📝 `docs/browser-observation.md` (+11 -0) 📝 `src/browser/CdpCaptureDocuments.ts` (+49 -10) 📝 `src/domain/webCaptureDiff.ts` (+64 -21) ➕ `src/domain/webCaptureFrameComparison.test.ts` (+62 -0) ➕ `src/domain/webCaptureFrameComparison.ts` (+49 -0) ➕ `tests/boundary/browser/cdpDomComparisonCompleteness.test.ts` (+266 -0) </details> ### 📄 Description ## Summary Passive page comparison could report a website change when CDP omitted a DOM node, or report DOM equality when a document's required `nodes` object was absent. Complete frame captures also lost their parent/child relationships during comparison. This change preserves capture uncertainty and compares complete frame ancestry without relying on volatile frame IDs. No new issue is opened: the independent reproductions are included in the regression tests below. Related to #2553, which concerns position loss inside malformed aligned node arrays; this change addresses missing snapshot containers and downstream comparison of partial collections. ## Problem and expected behavior | Production-boundary input | Before | After | | --- | --- | --- | | Same two-node document, but one node's attributes are unavailable | `dom_structure: changed`, including a false modification | `unknown`; retained facts and omissions remain visible | | Snapshot document lacks `nodes`; compare two such captures | Empty DOM with no omission, then `unchanged` | DOM and derived metadata incomplete, then `unknown` | | Complete frames change from `main -> {a, b}` to `main -> a -> b` | Frame relationship ignored, `unchanged` DOM dimension | `changed` with a `frames` modification | | Complete frame ancestry changes while DOM nodes are incomplete | Collection differences conflate evidence and omissions | Complete frame difference still proves `changed`; metadata stays `unknown` | ## Change and scope - `captureDom` owns malformed snapshot accounting. Missing/invalid document collections, invalid document entries, missing string tables and missing node objects use the existing completeness ledger. Skipped DOM facts also make their derived URL and agent-hint inventory incomplete; valid facts from other documents remain retained. - DOM comparison evaluates complete node and frame inventories independently. Whole partial DOM or metadata collection digests cannot establish modification by themselves. Node-count omissions also prevent DOM equality even if a historical completeness ledger does not list them. - Frame forests are fingerprinted from URL, origin and unordered child fingerprints. IDs only resolve references within one capture. Duplicate IDs, missing predecessors and cyclic ancestry remain unknown. Iterative processing avoids recursive serialization of deep ancestry and retains repeated sibling counts. - Remove the prior frame URL-only projection; document the resulting semantics in the browser observation guide. Contribution scope: routine correction within the existing passive capture and offline comparison boundaries. A new-provider or execution-authority decision is not applicable. No target execution, mutation, provider installation or protection-recovery capability is introduced. This PR makes no measured performance improvement claim. ## Contract and boundary impact - Semantic owner: CDP snapshot normalization and domain capture comparison. - CLI/MCP: existing `compare-web-captures` / `compare_web_captures` inputs and result schema remain applicable; a frame-only observed difference uses the existing generic change identity `frames`. - Evidence: retain observed partial facts, explicitly record omissions, and prevent incomplete aggregate inventories from proving equality or modification. Independently complete frame/node evidence can still prove change. - Provider/packaging: same CDP commands and authority; no package, catalog or generated artifact changes are committed. - Effects: read supplied capture data and normalize the existing passive CDP response. Tests use a local simulated CDP endpoint, compiled CLI subprocesses and an in-memory MCP transport. ## Evidence and regression coverage `cdpDomComparisonCompleteness.test.ts` uses the production provider over actual loopback HTTP/WebSocket transport, the production input schema and comparator. It covers missing/null node objects, missing/invalid/empty document collections, invalid entries mixed with valid documents, missing strings, node/link omissions in both directions, valid node changes, ID renaming, frame ancestry changes, and independently complete frame changes. Its public CLI case reads normalized capture JSON files and checks the returned Evidence for all three principal behaviors. `webCaptureFrameComparison.test.ts` covers duplicate IDs, missing parents, self/indirect cycles, sibling multiplicity/order, renamed IDs, and a 5,000-frame chain. Executed baseline: `f3534749e564421036ac445f536fc766b038208f`. The initial 11-case production-boundary suite had 10 failures on untouched production code and one valid-change control passed. After correction, all 13 boundary cases and all 6 graph cases pass. The final branch is based on `5830088` (the intervening upstream change only updates release documentation). Separate public MCP calls through the named tool contract, SDK in-memory client/server transport, production service and result delivery returned the expected DOM status for all three cases: partial DOM `unknown`, changed complete ancestry `changed`, missing nodes `unknown`. These calls do not claim real-browser verification. ## Validation performed Windows x64, Node 24.18.0 and npm 11.16.0, locked dependencies: - `npm run test:focused -- tests/boundary/browser/cdpDomComparisonCompleteness.test.ts src/domain/webCaptureFrameComparison.test.ts`: 19 passed, including the compiled public CLI workflow. - Expanded `npm run test:focused` selection of 16 relevant browser/domain/MCP files: **234 passed, 3 skipped, 1 failed**. The skipped SVG tests require an installed Chrome-family executable. The existing `browserComparisonInput.test.ts:155` fails advertised JSON-schema validation (`expected true to be false`). Restoring both changed production files byte-for-byte from `f3534749` and running that exact MCP test reproduces the same failure at the same assertion. The test was not weakened or skipped. - `npm run check`: all six tasks and architecture guards passed, zero lint errors. - `npm run docs:check`: passed, including 5 README consistency tests. - `npm run verify:test-discovery`: all 1,244 files discovered exactly once. - `git diff --check`: passed. Verification limits: no real Chrome capture, Linux/macOS execution, or complete repository-wide test suite is claimed. Frame reparenting uses a simplified simulated DOM; real browser scenarios may also expose changes in other dimensions. Required current-head CI remains a maintainer merge gate; the known baseline MCP failure and skipped real-browser coverage are reported above. ## Before merge - [x] Concrete scoped problem, Conventional Commit title, and owning documentation. - [x] Production-boundary regressions, meaningful negative paths, and actual CLI/MCP output. - [x] Partial facts, provenance and explicit uncertainty remain distinguishable. - [x] Diff checked for unrelated files, generated artifacts and credentials. - [ ] Required current-head CI and applicable real-browser verification accepted by the maintainer. ## Review follow-up Addressed the child-frame parent finding at the producing boundary. `captureFrames` now distinguishes the enclosing tree's root from child records: only a root may omit parent evidence. Missing, null, non-string or empty child `parentId` values mark frame coverage incomplete instead of making a child appear to be an independent root. Existing root behavior and other retained frame facts are preserved; the domain comparator consumes that ledger without guessing ancestry. Four new production-boundary cases fail on the previous head and pass after this correction, including comparison against complete evidence and self-comparison. Five focused files passed (70 tests, including the public CLI regressions); `npm run check` passed all six tasks and architecture guards. The previously documented baseline MCP-schema failure and real-browser verification limits still apply. No further cleanup is needed for this producing path. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:08:08 +00:00
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#5632
No description provided.