[GH-ISSUE #6289] [Bug] Distinct localStorage code units receive the same digest and scenarios compare unchanged #3803

Closed
opened 2026-10-31 00:44:13 +00:00 by bsakaguchi · 1 comment
Owner

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

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

capture_browser_scenario gives distinct localStorage code units the same value digest, so compare_web_captures reports complete aligned captures as unchanged. The page reported distinct UTF-16 code-unit arrays [55296], [65533], and [55297] for \ud800, \ufffd, and \ud801. The page URL, HTML, ready condition, key name and capture selection were identical. Each final storage artifact was captured with value_state: "hashed", and every capture was equality-eligible.

All three values received the same value_sha256:

83d544ccc223c057d2bf80d3f2a32982c32c3c0db8e2674820da5064783fb097

The actual registered comparison tool returned:

before                  after       overall_status  artifact_diffs.total
\ud800                  \ufffd      unchanged       0
\ud800                  \ud801      unchanged       0
\ud800                  \ud800      unchanged       0
text-a                  text-b      changed         1 (storage)

All four pairs aligned both steps without failures. The third pair used a separate browser capture of the same value. Comparing the retained normalized captures showed only elapsed-time/duration differences between the colliding values; the ordinary-text controls additionally differed only in the final storage value digest. Thus the failure occurs before comparison can recover the actual distinct stored values.

Reproduction

  1. Use the source checkout identified under REA version, with its locked dependencies installed and a caller-supplied Playwright-compatible Chromium headless shell. No build is needed.
  2. Save the test below as tests/boundary/mcp/storageSurrogates.repro.test.ts. It serves one loopback page, seeds one localStorage entry, waits for the page to POST its readback code units, calls the actual registered MCP tools with advertised output-schema validation, and closes its resources.
  3. Set SCENARIO_BROWSER to the absolute executable path of that headless browser, then run this focused command from the checkout root:
TEST_WORK=$(mktemp -d)
mkdir -p "$TEST_WORK/home" "$TEST_WORK/config" "$TEST_WORK/cache" "$TEST_WORK/temp"
HOME="$TEST_WORK/home" DSH_HOME="$TEST_WORK/home" XDG_CONFIG_HOME="$TEST_WORK/config" XDG_CACHE_HOME="$TEST_WORK/cache" TMPDIR="$TEST_WORK/temp" SCENARIO_BROWSER="$SCENARIO_BROWSER" node node_modules/vitest/vitest.mjs run tests/boundary/mcp/storageSurrogates.repro.test.ts --maxWorkers=1

One focused run of this exact test content, under a temporary test filename, reproduced both false-equivalence pairs. Six browser captures and four comparisons completed. The command exited 1 because the two changed-value expectations received unchanged; the readback, cleanup, completeness and control assertions passed.

import { createServer as httpServer } from "node:http";
import { writeFile } from "node:fs/promises";
import { Client, InMemoryTransport } from "@modelcontextprotocol/client";
import { expect, it } from "vitest";
import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js";
import { createBrowserScenarioProvider } from "../../../src/composition/browserScenario.js";
import { ok } from "../../../src/domain/result.js";
import { createServer } from "../../../src/server/createServer.js";
import { observed } from "../../fixtures/analysisExecution.js";
import { createTestBinarySession } from "../../fixtures/binarySession.js";
import { artifactMcpCall } from "../../../scripts/lib/artifact-e2e.mjs";

it("records localStorage code units and compares real scenario captures", async () => {
  const reports: number[][] = [];
  const html = `<!doctype html><title>storage fixture</title><script>
    const value = localStorage.getItem('value');
    const units = Array.from({length: value.length}, (_, i) => value.charCodeAt(i));
    fetch('/report', {method: 'POST', body: JSON.stringify(units)})
      .then(() => { document.body.textContent = 'ready'; document.body.id = 'ready'; });
  </script><body></body>`;
  const fixture = httpServer((req, res) => {
    if (req.method === 'POST' && req.url === '/report') {
      let body = '';
      req.on('data', chunk => { body += chunk; });
      req.on('end', () => { reports.push(JSON.parse(body)); res.end('ok'); });
    } else { res.setHeader('content-type', 'text/html; charset=utf-8'); res.end(html); }
  });
  await new Promise<void>(resolve => fixture.listen(0, '127.0.0.1', resolve));
  const addr = fixture.address();
  if (!addr || typeof addr === 'string') throw new Error('missing port');
  const origin = `http://127.0.0.1:${addr.port}`;
  const session = createTestBinarySession(() => ({
    execute: () => Promise.resolve(observed(null)), close: () => Promise.resolve(ok(null)),
  }));
  const server = createServer({kind: 'session', session}, {
    environment: {...process.env}, browserObservation: new CdpBrowserProvider(),
    browserScenarioCapture: createBrowserScenarioProvider({...process.env}),
    availabilityPolicy: () => ({processCaptureEnabled: false, investigationInputRoots: 0}),
  });
  const client = new Client({name: 'storage-verifier', version: '1'});
  const [ct, st] = InMemoryTransport.createLinkedPair();
  const capture = async (value: string) => {
    const count = reports.length;
    const result = await artifactMcpCall(client, {name: 'capture_browser_scenario', arguments: {
      browser: {mode: 'launch', executable_path: process.env.SCENARIO_BROWSER, headless: true},
      start_url: {url: origin + '/', query: []}, environment: {},
      actions: [{step_id: 'ready', action: 'wait_for', locator: {kind: 'css', selector: '#ready'}, state: 'visible', timeout_ms: 10000}],
      storage: {local_storage: [{origin, entries: [{name: 'value', value: {source: 'literal', value}}]}]},
      capture: {at_end: ['storage', 'url']}, command_timeout_ms: 60000,
    }});
    expect(result.isError, JSON.stringify(result)).not.toBe(true);
    const resultCapture = (result.structuredContent as any).normalized_result;
    expect(reports.length).toBe(count + 1);
    expect(reports.at(-1)).toEqual(Array.from({length: value.length}, (_, i) => value.charCodeAt(i)));
    expect(resultCapture.browser.cleanup).toBe('terminated-owned-process');
    expect(resultCapture.completeness.equality_eligible).toBe(true);
    return resultCapture;
  };
  const compare = async (before: unknown, after: unknown) => {
    const r = await artifactMcpCall(client, {name: 'compare_web_captures', arguments: {before_scenario: before, after_scenario: after}});
    expect(r.isError, JSON.stringify(r)).not.toBe(true);
    return (r.structuredContent as any).normalized_result;
  };
  try {
    await server.connect(st); await client.connect(ct);
    const values = ['\ud800', '\ufffd', '\ud801', '\ud800', 'text-a', 'text-b'];
    const captures = [];
    for (const value of values) captures.push(await capture(value));
    const comparisons = {
      highVsReplacement: await compare(captures[0], captures[1]),
      highVsOtherHigh: await compare(captures[0], captures[2]),
      unchangedControl: await compare(captures[0], captures[3]),
      ordinaryTextControl: await compare(captures[4], captures[5]),
    };
    const summary = {reports, fingerprints: captures.map(c => c.steps.at(-1).artifacts.storage),
      comparisons: Object.fromEntries(Object.entries(comparisons).map(([name, c]) => [name, {overall_status: c.overall_status, artifact_diffs: c.artifact_diffs, alignment: c.alignment, steps: c.steps}]))};
    console.log(JSON.stringify(summary));
    if (process.env.PROBE_OUTPUT) await writeFile(process.env.PROBE_OUTPUT, JSON.stringify({summary, captures, comparisons}, null, 2));
    expect(comparisons.unchangedControl.overall_status).toBe('unchanged');
    expect(comparisons.ordinaryTextControl.overall_status).toBe('changed');
    expect(comparisons.ordinaryTextControl.artifact_diffs.total).toBe(1);
    // Expected contract: distinct stored code units must not prove equivalence.
    expect.soft(comparisons.highVsReplacement.overall_status).toBe('changed');
    expect.soft(comparisons.highVsOtherHigh.overall_status).toBe('changed');
  } finally {
    await client.close(); await server.close(); await session.close();
    await new Promise<void>((resolve, reject) => fixture.close(e => e ? reject(e) : resolve()));
  }
}, 240000);

Expected behavior

Complete, aligned scenario captures whose observed localStorage value changed must expose the storage difference rather than prove equivalence. The scenario comparison documentation expressly includes storage artifacts and returns changed or unknown artifact records, and the registered tool contract promises every artifact-level difference. These requests use supported literal storage values, matching browser/origin/step context and complete selected coverage, with no declared secrets or caller normalization. If a value cannot be represented faithfully for comparison, its uncertainty must prevent a definite unchanged result. The separately captured identical-value control must remain unchanged, and the ordinary-text control must remain changed.

Impact and scope

P2 (supported): this bounded localStorage input class produces false equality in recorded scenario evidence, concealing an observed state difference. No downstream severe harm or population frequency was measured. Runtime proof covers launch-mode localStorage only; sessionStorage, cookies, external-connect mode and other browser/runtime versions were not exercised. Workaround status is unverified: making a caller-owned page render or POST its code units is a verification aid, not proof of a practical workaround for arbitrary applications. Existing captured digests do not retain the distinction.

REA version

Source commit 4a4bbab3c3 (main)

Runtime and platform

Node.js v26.12.1; macOS 27.0.1 arm64; Chromium headless shell 153.0.8010.12; locked playwright-core 1.58.2 and Vitest 5.0.3. The repository pins Node 24.18.0; the reproduction used the stated different version.

Target and artifact context

One owned loopback HTTP fixture page, a provider-owned headless browser profile per capture, and one literal localStorage value. No external application or service was contacted.

Interface

MCP

Earliest failure stage

Capture or observation

Relevant output or evidence

Typed outcome or recovery expected

Repeat the same registered capture-to-comparison workflow. Both distinct code-unit pairs must cease reporting definite unchanged storage: distinguish the values and report the storage change, or explicitly preserve unsupported/unknown comparability. Keep the separate identical-value pair unchanged and text-a versus text-b changed with a storage difference. Preserve declared-secret redaction semantics; no specific string encoding, passive-capture output change or ordinary valid-string digest change is prescribed. The controls in this report do not establish those unexecuted secret/passive checks.

Analysis or suspected cause

Source-derived reasoning: the active storage producer hashes a JavaScript string through Node's default UTF-8 conversion. Unpaired surrogate code units collapse to replacement-character bytes before hashing. The active scenario comparator then compares the storage artifact that contains the already-collided digest. This is a lossy encoding collision before hashing, not a cryptographic SHA-256 collision. The passive comparison consumer is a different path and was not exercised.

Additional context

Read-only searches covered open/closed issues and PRs with six keyword sets, including localStorage surrogate fingerprint, scenario storage fingerprint collision, compare web scenarios Unicode, surrogate storage, BrowserScenarioSecrets, and storage fingerprint Unicode. No duplicate was found. Related #3879 concerns passive script source comparison; #2554 concerns missing IndexedDB values and false completeness; #1591 and PR #1590 concern secret redaction; PR #1579 concerns initial seeding; #2169 concerns mismatched event-family coverage; #1608 and PR #1611 concern escaped secret redaction. None describes this active localStorage fingerprint collision.

Current-main freshness was checked by fetching and inspecting source drift without changing the tested checkout. Fetched main was 002e1b68c66a243948fdca57ac416cfaec617315; relevant drift only replaces a history length numeric schema in commit 7590b50801553ca443922b1fb48e168334f8cc5d. The affected hashing and active comparison logic are unchanged. Runtime reproduction was performed on the stated baseline, not fetched main.

The browser and loopback page were real. The unrelated binary-session fixture permits server construction but performs no binary operation; browser capture, storage seeding, Playwright, hashing, comparison and MCP transport were not mocked. Both exercised tools used their actual advertised output schemas. All six captures reported terminated-owned-process; awaited resource cleanup completed, the test runner exited, the isolated temporary directory was empty, and the checkout was clean after removing the temporary test. This report was prepared with AI-assisted independent source review and local reproduction.

Originally created by @coygeek on GitHub (Oct 22, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6289 ### 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 `capture_browser_scenario` gives distinct localStorage code units the same value digest, so `compare_web_captures` reports complete aligned captures as unchanged. The page reported distinct UTF-16 code-unit arrays `[55296]`, `[65533]`, and `[55297]` for `\ud800`, `\ufffd`, and `\ud801`. The page URL, HTML, ready condition, key name and capture selection were identical. Each final storage artifact was captured with `value_state: "hashed"`, and every capture was equality-eligible. All three values received the same `value_sha256`: ```text 83d544ccc223c057d2bf80d3f2a32982c32c3c0db8e2674820da5064783fb097 ``` The actual registered comparison tool returned: ```text before after overall_status artifact_diffs.total \ud800 \ufffd unchanged 0 \ud800 \ud801 unchanged 0 \ud800 \ud800 unchanged 0 text-a text-b changed 1 (storage) ``` All four pairs aligned both steps without failures. The third pair used a separate browser capture of the same value. Comparing the retained normalized captures showed only elapsed-time/duration differences between the colliding values; the ordinary-text controls additionally differed only in the final storage value digest. Thus the failure occurs before comparison can recover the actual distinct stored values. ### Reproduction 1. Use the source checkout identified under REA version, with its locked dependencies installed and a caller-supplied Playwright-compatible Chromium headless shell. No build is needed. 2. Save the test below as `tests/boundary/mcp/storageSurrogates.repro.test.ts`. It serves one loopback page, seeds one localStorage entry, waits for the page to POST its readback code units, calls the actual registered MCP tools with advertised output-schema validation, and closes its resources. 3. Set `SCENARIO_BROWSER` to the absolute executable path of that headless browser, then run this focused command from the checkout root: ```sh TEST_WORK=$(mktemp -d) mkdir -p "$TEST_WORK/home" "$TEST_WORK/config" "$TEST_WORK/cache" "$TEST_WORK/temp" HOME="$TEST_WORK/home" DSH_HOME="$TEST_WORK/home" XDG_CONFIG_HOME="$TEST_WORK/config" XDG_CACHE_HOME="$TEST_WORK/cache" TMPDIR="$TEST_WORK/temp" SCENARIO_BROWSER="$SCENARIO_BROWSER" node node_modules/vitest/vitest.mjs run tests/boundary/mcp/storageSurrogates.repro.test.ts --maxWorkers=1 ``` One focused run of this exact test content, under a temporary test filename, reproduced both false-equivalence pairs. Six browser captures and four comparisons completed. The command exited 1 because the two changed-value expectations received `unchanged`; the readback, cleanup, completeness and control assertions passed. ```ts import { createServer as httpServer } from "node:http"; import { writeFile } from "node:fs/promises"; import { Client, InMemoryTransport } from "@modelcontextprotocol/client"; import { expect, it } from "vitest"; import { CdpBrowserProvider } from "../../../src/browser/CdpBrowserProvider.js"; import { createBrowserScenarioProvider } from "../../../src/composition/browserScenario.js"; import { ok } from "../../../src/domain/result.js"; import { createServer } from "../../../src/server/createServer.js"; import { observed } from "../../fixtures/analysisExecution.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; import { artifactMcpCall } from "../../../scripts/lib/artifact-e2e.mjs"; it("records localStorage code units and compares real scenario captures", async () => { const reports: number[][] = []; const html = `<!doctype html><title>storage fixture</title><script> const value = localStorage.getItem('value'); const units = Array.from({length: value.length}, (_, i) => value.charCodeAt(i)); fetch('/report', {method: 'POST', body: JSON.stringify(units)}) .then(() => { document.body.textContent = 'ready'; document.body.id = 'ready'; }); </script><body></body>`; const fixture = httpServer((req, res) => { if (req.method === 'POST' && req.url === '/report') { let body = ''; req.on('data', chunk => { body += chunk; }); req.on('end', () => { reports.push(JSON.parse(body)); res.end('ok'); }); } else { res.setHeader('content-type', 'text/html; charset=utf-8'); res.end(html); } }); await new Promise<void>(resolve => fixture.listen(0, '127.0.0.1', resolve)); const addr = fixture.address(); if (!addr || typeof addr === 'string') throw new Error('missing port'); const origin = `http://127.0.0.1:${addr.port}`; const session = createTestBinarySession(() => ({ execute: () => Promise.resolve(observed(null)), close: () => Promise.resolve(ok(null)), })); const server = createServer({kind: 'session', session}, { environment: {...process.env}, browserObservation: new CdpBrowserProvider(), browserScenarioCapture: createBrowserScenarioProvider({...process.env}), availabilityPolicy: () => ({processCaptureEnabled: false, investigationInputRoots: 0}), }); const client = new Client({name: 'storage-verifier', version: '1'}); const [ct, st] = InMemoryTransport.createLinkedPair(); const capture = async (value: string) => { const count = reports.length; const result = await artifactMcpCall(client, {name: 'capture_browser_scenario', arguments: { browser: {mode: 'launch', executable_path: process.env.SCENARIO_BROWSER, headless: true}, start_url: {url: origin + '/', query: []}, environment: {}, actions: [{step_id: 'ready', action: 'wait_for', locator: {kind: 'css', selector: '#ready'}, state: 'visible', timeout_ms: 10000}], storage: {local_storage: [{origin, entries: [{name: 'value', value: {source: 'literal', value}}]}]}, capture: {at_end: ['storage', 'url']}, command_timeout_ms: 60000, }}); expect(result.isError, JSON.stringify(result)).not.toBe(true); const resultCapture = (result.structuredContent as any).normalized_result; expect(reports.length).toBe(count + 1); expect(reports.at(-1)).toEqual(Array.from({length: value.length}, (_, i) => value.charCodeAt(i))); expect(resultCapture.browser.cleanup).toBe('terminated-owned-process'); expect(resultCapture.completeness.equality_eligible).toBe(true); return resultCapture; }; const compare = async (before: unknown, after: unknown) => { const r = await artifactMcpCall(client, {name: 'compare_web_captures', arguments: {before_scenario: before, after_scenario: after}}); expect(r.isError, JSON.stringify(r)).not.toBe(true); return (r.structuredContent as any).normalized_result; }; try { await server.connect(st); await client.connect(ct); const values = ['\ud800', '\ufffd', '\ud801', '\ud800', 'text-a', 'text-b']; const captures = []; for (const value of values) captures.push(await capture(value)); const comparisons = { highVsReplacement: await compare(captures[0], captures[1]), highVsOtherHigh: await compare(captures[0], captures[2]), unchangedControl: await compare(captures[0], captures[3]), ordinaryTextControl: await compare(captures[4], captures[5]), }; const summary = {reports, fingerprints: captures.map(c => c.steps.at(-1).artifacts.storage), comparisons: Object.fromEntries(Object.entries(comparisons).map(([name, c]) => [name, {overall_status: c.overall_status, artifact_diffs: c.artifact_diffs, alignment: c.alignment, steps: c.steps}]))}; console.log(JSON.stringify(summary)); if (process.env.PROBE_OUTPUT) await writeFile(process.env.PROBE_OUTPUT, JSON.stringify({summary, captures, comparisons}, null, 2)); expect(comparisons.unchangedControl.overall_status).toBe('unchanged'); expect(comparisons.ordinaryTextControl.overall_status).toBe('changed'); expect(comparisons.ordinaryTextControl.artifact_diffs.total).toBe(1); // Expected contract: distinct stored code units must not prove equivalence. expect.soft(comparisons.highVsReplacement.overall_status).toBe('changed'); expect.soft(comparisons.highVsOtherHigh.overall_status).toBe('changed'); } finally { await client.close(); await server.close(); await session.close(); await new Promise<void>((resolve, reject) => fixture.close(e => e ? reject(e) : resolve())); } }, 240000); ``` ### Expected behavior Complete, aligned scenario captures whose observed localStorage value changed must expose the storage difference rather than prove equivalence. The scenario comparison documentation expressly includes storage artifacts and returns changed or unknown artifact records, and the registered tool contract promises every artifact-level difference. These requests use supported literal storage values, matching browser/origin/step context and complete selected coverage, with no declared secrets or caller normalization. If a value cannot be represented faithfully for comparison, its uncertainty must prevent a definite unchanged result. The separately captured identical-value control must remain unchanged, and the ordinary-text control must remain changed. ### Impact and scope P2 (supported): this bounded localStorage input class produces false equality in recorded scenario evidence, concealing an observed state difference. No downstream severe harm or population frequency was measured. Runtime proof covers launch-mode localStorage only; sessionStorage, cookies, external-connect mode and other browser/runtime versions were not exercised. Workaround status is unverified: making a caller-owned page render or POST its code units is a verification aid, not proof of a practical workaround for arbitrary applications. Existing captured digests do not retain the distinction. ### REA version Source commit 4a4bbab3c337701febeb6b532ace49041c4876e0 (main) ### Runtime and platform Node.js v26.12.1; macOS 27.0.1 arm64; Chromium headless shell 153.0.8010.12; locked playwright-core 1.58.2 and Vitest 5.0.3. The repository pins Node 24.18.0; the reproduction used the stated different version. ### Target and artifact context One owned loopback HTTP fixture page, a provider-owned headless browser profile per capture, and one literal localStorage value. No external application or service was contacted. ### Interface MCP ### Earliest failure stage Capture or observation ### Relevant output or evidence - Expected source: Active scenario contract: [scenario comparison](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/docs/browser-scenario-contract.md#L390), including storage records at line 422; [registered compare_web_captures contract](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/contracts/browserToolContracts.ts#L235). The passive storage-fingerprint opt-in documentation is not used as the expected contract for this finding. - Failure source: Observed registered-tool results from the inline test. Source-derived path: [Playwright storage read and fingerprint projection](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/browser/PlaywrightScenarioArtifacts.ts#L150), [storage string hashing](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/browser/BrowserScenarioSecrets.ts#L392), [active scenario storage comparison](https://github.com/morluto/rea/blob/4a4bbab3c337701febeb6b532ace49041c4876e0/src/domain/browserScenarioDiff.ts#L361) and its equal-digest branch at line 592. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete ### Typed outcome or recovery expected Repeat the same registered capture-to-comparison workflow. Both distinct code-unit pairs must cease reporting definite unchanged storage: distinguish the values and report the storage change, or explicitly preserve unsupported/unknown comparability. Keep the separate identical-value pair unchanged and `text-a` versus `text-b` changed with a storage difference. Preserve declared-secret redaction semantics; no specific string encoding, passive-capture output change or ordinary valid-string digest change is prescribed. The controls in this report do not establish those unexecuted secret/passive checks. ### Analysis or suspected cause Source-derived reasoning: the active storage producer hashes a JavaScript string through Node's default UTF-8 conversion. Unpaired surrogate code units collapse to replacement-character bytes before hashing. The active scenario comparator then compares the storage artifact that contains the already-collided digest. This is a lossy encoding collision before hashing, not a cryptographic SHA-256 collision. The passive comparison consumer is a different path and was not exercised. ### Additional context Read-only searches covered open/closed issues and PRs with six keyword sets, including `localStorage surrogate fingerprint`, `scenario storage fingerprint collision`, `compare web scenarios Unicode`, `surrogate storage`, `BrowserScenarioSecrets`, and `storage fingerprint Unicode`. No duplicate was found. Related #3879 concerns passive script source comparison; #2554 concerns missing IndexedDB values and false completeness; #1591 and PR #1590 concern secret redaction; PR #1579 concerns initial seeding; #2169 concerns mismatched event-family coverage; #1608 and PR #1611 concern escaped secret redaction. None describes this active localStorage fingerprint collision. Current-main freshness was checked by fetching and inspecting source drift without changing the tested checkout. Fetched main was `002e1b68c66a243948fdca57ac416cfaec617315`; relevant drift only replaces a history length numeric schema in commit `7590b50801553ca443922b1fb48e168334f8cc5d`. The affected hashing and active comparison logic are unchanged. Runtime reproduction was performed on the stated baseline, not fetched main. The browser and loopback page were real. The unrelated binary-session fixture permits server construction but performs no binary operation; browser capture, storage seeding, Playwright, hashing, comparison and MCP transport were not mocked. Both exercised tools used their actual advertised output schemas. All six captures reported `terminated-owned-process`; awaited resource cleanup completed, the test runner exited, the isolated temporary directory was empty, and the checkout was clean after removing the temporary test. This report was prepared with AI-assisted independent source review and local reproduction.
Author
Owner

@gongfpp commented on GitHub (Oct 22, 2026):

I would like to work on a focused fix for the active-scenario storage fingerprint producer and its comparison boundary, with regression coverage through the registered MCP tools and a real owned loopback Chromium fixture. The current UTF-8 conversion loses unpaired UTF-16 code units before hashing. I plan to preserve ordinary-string digests and declared-secret redaction, distinguish malformed code-unit sequences without cross-encoding aliasing, and explicitly account for legacy captures whose lost information cannot be reconstructed (rather than treating an encoding/version difference as a target change). No new provider, execution authority, passive-capture behavior, or secret-pattern inference is intended. I have not found a directly overlapping public PR; #5035 appears to address ordering, not this producer collision. Is anyone already addressing this, or is there a preferred compatibility policy? This is a coordination note, not a claim of assignment or scope approval; any PR will remain draft pending the human owner’s review.

<!-- gh-comment-id:6344478847 --> @gongfpp commented on GitHub (Oct 22, 2026): I would like to work on a focused fix for the active-scenario storage fingerprint producer and its comparison boundary, with regression coverage through the registered MCP tools and a real owned loopback Chromium fixture. The current UTF-8 conversion loses unpaired UTF-16 code units before hashing. I plan to preserve ordinary-string digests and declared-secret redaction, distinguish malformed code-unit sequences without cross-encoding aliasing, and explicitly account for legacy captures whose lost information cannot be reconstructed (rather than treating an encoding/version difference as a target change). No new provider, execution authority, passive-capture behavior, or secret-pattern inference is intended. I have not found a directly overlapping public PR; #5035 appears to address ordering, not this producer collision. Is anyone already addressing this, or is there a preferred compatibility policy? This is a coordination note, not a claim of assignment or scope approval; any PR will remain draft pending the human owner’s review.
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#3803
No description provided.