[PR #2861] [MERGED] fix(browser): report complete storage key inventory changes #5446

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2861
Author: @zhangguiping-xydt
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/browser-storage-key-differences


📝 Commits (2)

  • 9aebc4e fix(browser): report complete storage key inventory changes
  • e84f52f fix(browser): preserve unavailable storage inventory coverage

📊 Changes

3 files changed (+184 additions, -1 deletions)

View changed files

📝 docs/browser-observation.md (+1 -0)
📝 src/domain/webCaptureDiff.ts (+4 -1)
📝 tests/boundary/browser/webCaptureDiff.test.ts (+179 -0)

📄 Description

Summary

Fixes #2608. Report proven storage-key additions/removals without requiring content fingerprints, and preserve unknown inventory coverage when a storage read fails or is malformed.

Problem and expected behavior

What Problem This Solves

Capturing complete local/session-storage key inventories before and after adding a key, without opting into content fingerprints, produces unknown with zero storage changes. Missing content evidence suppresses an independently established inventory difference.

User Impact

compare-web-captures and compare_web_captures retain key additions/removals in keys-only workflows. Comparing identical keys-only captures remains unknown: this does not establish equal values.

Change and scope

Why This Change Was Made

Separate the proof required for inventory membership changes from the stronger proof required for dimension-wide equivalence. Storage supplies its key-inventory completeness to the existing comparator. Content identities are already restricted to mutually comparable fingerprints, so unobserved value changes are not invented. Other dimensions retain the existing default gate.

Storage capture now records unavailable or malformed local/session/IndexedDB/cache inventories in the existing storage_keys completeness ledger. Membership changes require complete inventories on both sides; independently confirmed content modifications remain comparable when an inventory is incomplete. Successful partial observations are retained.

This reuses the necessary key-coverage propagation and independent fingerprint mapping proposed by chAng-L19 in #2491, with producer-to-comparison regressions. #2491 alone does not fix the original complete-inventory, unselected-fingerprint case. The browser observation documentation now explains recapturing older exports whose unavailable inventories were recorded as complete.

Contract and boundary impact

  • Owners: CDP storage capture and its completeness ledger; offline normalized comparison in webCaptureDiff.ts.
  • Scope/maintainer decision: routine correctness fix; advance decision not applicable.
  • CLI/MCP/catalog: same schemas and status semantics; observed changes remain distinct from incomplete equivalence.
  • Evidence: additions/removals require complete key inventories on both sides. Unavailable fingerprints/quota cannot establish equality.
  • Provider/authority/cleanup: existing CDP calls and capture permissions remain unchanged; unavailable results now carry accurate completeness. Local proof owns its Chrome profile and page.
  • Package, generated metadata, installation, migration: none.

Evidence and regression coverage

Evidence

Executed on Linux x64, Node 24.18.0 / npm 11.16.0, Chrome 138.0.7204.157, using the compiled CLI and an actual stdio MCP client.

  1. Serve and open an owned page on localhost. Set localStorage.alpha = '1' with empty session storage.
  2. Capture using node scripts/rea.mjs inspect-web-page <loopback-cdp-endpoint> <target-id> --observation-ms 0 --include-storage-keys --format json. Do not enable fingerprints. Save { "inspection": <normalized_result> } as before.json.
  3. On the real page, set localStorage.beta = '2' and sessionStorage.draft = 'saved'. Capture again as after.json; an independent browser read confirms the exact keys.
  4. Run node scripts/rea.mjs compare-web-captures before.json after.json --format json, then reverse the inputs and compare after.json with itself.
  5. Repeat the captures with inspect_web_page over stdio MCP (include_storage_keys: true) and comparison with compare_web_captures. The comparison consumes the actual normalized captures; no coverage flags, usage/quota values, or inventories are rewritten.
Storage result in both adapters Before cbe769b c3f9e7d
Before → after unknown, zero changes changed: local beta and session draft added
After → before unknown, zero changes changed: both keys removed
After → identical after unknown, zero changes unknown, zero changes

The comparator is unchanged between live-before cbe769b and branch base 6e25cc3. The new boundary regression fails on the unmodified comparator (1 failed / 14 passed). It additionally covers IndexedDB/cache name inventories, missing quota, and incomplete-inventory negative controls in both directions. Existing fingerprint/value/unknown controls remain covered.

Review regression: unread keys must not appear deleted

Addresses the P1 review. On real isolated Chrome, a local WebSocket forwarding proxy injects CDP -32601 only for DOMStorage.getDOMStorageItems; all other requests reach the browser normally. This is controlled protocol failure injection, not a claim that unmodified Chrome naturally emits this error.

  1. Set local stable-local and session stable-session; capture the successful inventories through the compiled CLI and stdio MCP.
  2. Enable the unsupported-read response and capture again. An independent browser read confirms both keys still exist. Compare in both directions without rewriting captured evidence.
  3. Disable the fault, actually clear both stores, recapture and compare as the real-deletion control.
Both adapters Before dd0ac6c Final e10c8a610
Successful → unavailable read incorrectly changed, 2 removed unknown, 0 changes
Unavailable → successful read incorrectly changed, 2 added unknown, 0 changes
Unavailable capture coverage no storage-key exclusion storage_keys/provider_unavailable
Actual empty-storage control changed, 2 removed changed, 2 removed

The producer-to-comparison regressions first failed 4 tests before coverage propagation. A separate regression failed when incomplete inventories suppressed proven fingerprint modifications. The final suite covers unavailable and malformed reads for all four inventory sources, both comparison directions, retained partial keys, complete fingerprints, and incomplete-fingerprint negative controls.

Validation performed

Current revision e10c8a610a15bae2ae424873532aa43ed50cf159 (Linux, pinned Node.js 24.18.0/npm 11.16.0):

  • npm run check — passed, including repository-wide formatting, typecheck, lint/module boundaries, knip, metadata and architecture guards.
  • Build and focused regression suites — 35 passed across web-capture comparison, CDP storage capture, sensitive data and IndexedDB coverage. The compiled runtime was used for the live proofs.
  • npm run docs:check — passed, including 5 documentation consistency tests.
  • Exact-revision live proof — Real Chrome, built CLI and stdio MCP: key additions and removals each produce two changes; identical key inventories retain unknown content equality. Both adapters agree. The controlled unavailable-read and real empty-storage proofs above were also rerun on this exact revision.

The earlier shared CI failures were repaired by integrating main through f1045001f, including #2895 (static checks) and #2910 (portable DEX regex and isolated Linux process inventory). The remaining complete-facet reconstruction fixture correction is reused from #2853; the pinned formatter was applied to the process inventory code introduced by #2910. No assertions or CI gates were removed, and process-observation permission behavior remains unchanged.

Before these repairs, the three affected suites failed 7 tests on unmodified main 1b6c899b0. The shared repairs passed 120 tests across reconstruction, function comparison, advertised schema patterns and the live Linux process fixture. Each PR's own workflow was then checked separately on its final revision above. The locked duplicate scanner was also run directly using its same-version jscpd-linux-x64-musl 5.0.12 binary because the GNU binary requires newer glibc: all five revisions are below 2.94% duplicated lines, within the unchanged 3.5% threshold. npm run scan:todos also passed. Remote CI continues to use its normal GNU binary.

Remote CI for this revision is being checked separately; passing local verification is not a claim that queued GitHub checks have passed.

Compatibility, safety, and release

No schema, dependency, capture-authority, or release changes. What was not tested: other OS/browser versions, the published package, and the full browser verifier. Real-browser changes cover local/session storage; IndexedDB/cache inventory changes are boundary fixtures. No private application data or credentials are used.

Before merge

  • Concrete linked bug and focused title.
  • Scope resolved; no new authority or schema.
  • Failing regression and real before/after behavior evidence.
  • Unknown value coverage and source provenance remain explicit.
  • Browser documentation updated; no generated metadata changes required.
  • Final diff reviewed for unrelated edits, secrets, and unsupported claims.

🔄 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/2861 **Author:** [@zhangguiping-xydt](https://github.com/zhangguiping-xydt) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/browser-storage-key-differences` --- ### 📝 Commits (2) - [`9aebc4e`](https://github.com/morluto/rea/commit/9aebc4e8dd0a07435a14bf8e8d8f0d5a78276ec9) fix(browser): report complete storage key inventory changes - [`e84f52f`](https://github.com/morluto/rea/commit/e84f52f791b732e09b1445140512ca11dc7e78ae) fix(browser): preserve unavailable storage inventory coverage ### 📊 Changes **3 files changed** (+184 additions, -1 deletions) <details> <summary>View changed files</summary> 📝 `docs/browser-observation.md` (+1 -0) 📝 `src/domain/webCaptureDiff.ts` (+4 -1) 📝 `tests/boundary/browser/webCaptureDiff.test.ts` (+179 -0) </details> ### 📄 Description ## Summary Fixes #2608. Report proven storage-key additions/removals without requiring content fingerprints, and preserve unknown inventory coverage when a storage read fails or is malformed. ## Problem and expected behavior ### What Problem This Solves Capturing complete local/session-storage key inventories before and after adding a key, without opting into content fingerprints, produces `unknown` with zero storage changes. Missing content evidence suppresses an independently established inventory difference. ### User Impact `compare-web-captures` and `compare_web_captures` retain key additions/removals in keys-only workflows. Comparing identical keys-only captures remains `unknown`: this does not establish equal values. ## Change and scope ### Why This Change Was Made Separate the proof required for inventory membership changes from the stronger proof required for dimension-wide equivalence. Storage supplies its key-inventory completeness to the existing comparator. Content identities are already restricted to mutually comparable fingerprints, so unobserved value changes are not invented. Other dimensions retain the existing default gate. Storage capture now records unavailable or malformed local/session/IndexedDB/cache inventories in the existing `storage_keys` completeness ledger. Membership changes require complete inventories on both sides; independently confirmed content modifications remain comparable when an inventory is incomplete. Successful partial observations are retained. This reuses the necessary key-coverage propagation and independent fingerprint mapping proposed by **chAng-L19 in #2491**, with producer-to-comparison regressions. #2491 alone does not fix the original complete-inventory, unselected-fingerprint case. The browser observation documentation now explains recapturing older exports whose unavailable inventories were recorded as complete. ## Contract and boundary impact - Owners: CDP storage capture and its completeness ledger; offline normalized comparison in `webCaptureDiff.ts`. - Scope/maintainer decision: routine correctness fix; advance decision not applicable. - CLI/MCP/catalog: same schemas and status semantics; observed changes remain distinct from incomplete equivalence. - Evidence: additions/removals require complete key inventories on both sides. Unavailable fingerprints/quota cannot establish equality. - Provider/authority/cleanup: existing CDP calls and capture permissions remain unchanged; unavailable results now carry accurate completeness. Local proof owns its Chrome profile and page. - Package, generated metadata, installation, migration: none. ## Evidence and regression coverage ### Evidence Executed on Linux x64, Node 24.18.0 / npm 11.16.0, Chrome 138.0.7204.157, using the compiled CLI and an actual stdio MCP client. 1. Serve and open an owned page on localhost. Set `localStorage.alpha = '1'` with empty session storage. 2. Capture using `node scripts/rea.mjs inspect-web-page <loopback-cdp-endpoint> <target-id> --observation-ms 0 --include-storage-keys --format json`. Do not enable fingerprints. Save `{ "inspection": <normalized_result> }` as `before.json`. 3. On the real page, set `localStorage.beta = '2'` and `sessionStorage.draft = 'saved'`. Capture again as `after.json`; an independent browser read confirms the exact keys. 4. Run `node scripts/rea.mjs compare-web-captures before.json after.json --format json`, then reverse the inputs and compare `after.json` with itself. 5. Repeat the captures with `inspect_web_page` over stdio MCP (`include_storage_keys: true`) and comparison with `compare_web_captures`. The comparison consumes the actual normalized captures; no coverage flags, usage/quota values, or inventories are rewritten. | Storage result in both adapters | Before `cbe769b` | c3f9e7d | | --- | --- | --- | | Before → after | unknown, zero changes | changed: local `beta` and session `draft` added | | After → before | unknown, zero changes | changed: both keys removed | | After → identical after | unknown, zero changes | unknown, zero changes | The comparator is unchanged between live-before `cbe769b` and branch base `6e25cc3`. The new boundary regression fails on the unmodified comparator (1 failed / 14 passed). It additionally covers IndexedDB/cache name inventories, missing quota, and incomplete-inventory negative controls in both directions. Existing fingerprint/value/unknown controls remain covered. ### Review regression: unread keys must not appear deleted Addresses [the P1 review](https://github.com/morluto/rea/pull/2861#discussion_r4261730542). On real isolated Chrome, a local WebSocket forwarding proxy injects CDP `-32601` **only** for `DOMStorage.getDOMStorageItems`; all other requests reach the browser normally. This is controlled protocol failure injection, not a claim that unmodified Chrome naturally emits this error. 1. Set local `stable-local` and session `stable-session`; capture the successful inventories through the compiled CLI and stdio MCP. 2. Enable the unsupported-read response and capture again. An independent browser read confirms both keys still exist. Compare in both directions without rewriting captured evidence. 3. Disable the fault, actually clear both stores, recapture and compare as the real-deletion control. | Both adapters | Before `dd0ac6c` | Final `e10c8a610` | | --- | --- | --- | | Successful → unavailable read | incorrectly changed, 2 removed | unknown, 0 changes | | Unavailable → successful read | incorrectly changed, 2 added | unknown, 0 changes | | Unavailable capture coverage | no storage-key exclusion | `storage_keys/provider_unavailable` | | Actual empty-storage control | changed, 2 removed | changed, 2 removed | The producer-to-comparison regressions first failed **4 tests** before coverage propagation. A separate regression failed when incomplete inventories suppressed proven fingerprint modifications. The final suite covers unavailable and malformed reads for all four inventory sources, both comparison directions, retained partial keys, complete fingerprints, and incomplete-fingerprint negative controls. ## Validation performed Current revision `e10c8a610a15bae2ae424873532aa43ed50cf159` (Linux, pinned Node.js 24.18.0/npm 11.16.0): - `npm run check` — **passed**, including repository-wide formatting, typecheck, lint/module boundaries, knip, metadata and architecture guards. - Build and focused regression suites — **35 passed** across web-capture comparison, CDP storage capture, sensitive data and IndexedDB coverage. The compiled runtime was used for the live proofs. - `npm run docs:check` — **passed**, including 5 documentation consistency tests. - Exact-revision live proof — Real Chrome, built CLI and stdio MCP: key additions and removals each produce two changes; identical key inventories retain unknown content equality. Both adapters agree. The controlled unavailable-read and real empty-storage proofs above were also rerun on this exact revision. The earlier shared CI failures were repaired by integrating main through `f1045001f`, including [#2895](https://github.com/morluto/rea/pull/2895) (static checks) and [#2910](https://github.com/morluto/rea/pull/2910) (portable DEX regex and isolated Linux process inventory). The remaining complete-facet reconstruction fixture correction is reused from [#2853](https://github.com/morluto/rea/pull/2853); the pinned formatter was applied to the process inventory code introduced by #2910. No assertions or CI gates were removed, and process-observation permission behavior remains unchanged. Before these repairs, the three affected suites failed **7 tests** on unmodified main `1b6c899b0`. The shared repairs passed **120 tests** across reconstruction, function comparison, advertised schema patterns and the live Linux process fixture. Each PR's own workflow was then checked separately on its final revision above. The locked duplicate scanner was also run directly using its same-version `jscpd-linux-x64-musl` 5.0.12 binary because the GNU binary requires newer glibc: all five revisions are below 2.94% duplicated lines, within the unchanged 3.5% threshold. `npm run scan:todos` also passed. Remote CI continues to use its normal GNU binary. Remote CI for this revision is being checked separately; passing local verification is not a claim that queued GitHub checks have passed. ## Compatibility, safety, and release No schema, dependency, capture-authority, or release changes. **What was not tested:** other OS/browser versions, the published package, and the full browser verifier. Real-browser changes cover local/session storage; IndexedDB/cache inventory changes are boundary fixtures. No private application data or credentials are used. ## Before merge - [x] Concrete linked bug and focused title. - [x] Scope resolved; no new authority or schema. - [x] Failing regression and real before/after behavior evidence. - [x] Unknown value coverage and source provenance remain explicit. - [x] Browser documentation updated; no generated metadata changes required. - [x] Final diff reviewed for unrelated edits, secrets, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:06:42 +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#5446
No description provided.