[GH-ISSUE #7329] [Bug] Unavailable WebMCP registration source becomes a definite tool modification #4357

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

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

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 one modified WebMCP tool and overall_status: "changed" when two retained registrations differ only because one registration stack was malformed. Production discovery correctly keeps that registration with registration_source: null and a webmcp_tools/invalid_protocol_value exclusion; comparison still treats the unavailable source as a definite different value. The reverse comparison gives the same false modification.

This was reproduced with built production discovery receiving in-memory protocol replies, schema-validated passive capture files, and the public offline CLI. No browser, socket connection, or target code ran. Identical inspection records and equality of every retained tool field except registration_source were asserted.

Reproduction

Use the already built source baseline below with its locked dependencies and Node.js 24. Set REA_ROOT to the checkout root. Dependency installation and building are prerequisites, not steps rerun during this verification. The following is the executed replay program with portable directory setup. All URLs are inert fixture identities; the connection stub accepts only four expected discovery commands and opens no connection.

WORK="$(mktemp -d)"
mkdir -p "$WORK/home" "$WORK/config" "$WORK/cache" "$WORK/compile-cache" "$WORK/temp"
cat > "$WORK/fixture.mjs" <<'JS'
import assert from 'node:assert/strict';
import {writeFileSync, mkdirSync} from 'node:fs';
import {spawnSync} from 'node:child_process';
import {resolve} from 'node:path';
import {pathToFileURL} from 'node:url';
import {release} from 'node:os';
const root = resolve(process.argv[2]), out = resolve(process.argv[3]);
const load = p => import(pathToFileURL(resolve(root,'dist',p+'.js')).href);
const {discoverWebMcp} = await load('browser/CdpWebMcpDiscovery');
const {discoverWebMcpToolsInputSchema,webMcpDiscoverySchema} = await load('domain/webMcpDiscovery');
const {captureSnapshotSchema} = await load('domain/webCaptureDiffSchemas');
assert(process.env.CAPTURE_AT);
const at = new Date(process.env.CAPTURE_AT).toISOString();
const origin='https://fixture.example.test', url=origin+'/';
const browser={product:'fixture',protocol_version:'1.3',revision:'fixture',user_agent:'fixture',js_version:'fixture'};
const input=discoverWebMcpToolsInputSchema.parse({cdp_endpoint:'http://127.0.0.1:9222',target_id:'page',allowed_origins:[origin],observation_ms:0});
let externalOperations=0;
const make = async (stack,description='Search',line=3) => {
 let listener; const commands=[];
 const tree={frameTree:{frame:{id:'frame',url,loaderId:'loader'}}};
 const connection={
  onEvent(fn){listener=fn;return ()=>{listener=undefined;};},
  async send(method){
   commands.push(method);
   if(method==='Page.enable')return {};
   if(method==='Page.getFrameTree')return tree;
   if(method==='WebMCP.enable'){
    const stackTrace=stack==='known'?{callFrames:[{url:origin+'/app.js',lineNumber:line,columnNumber:1}]}:stack==='malformed'?{callFrames:[null]}:undefined;
    listener({method:'WebMCP.toolsAdded',sessionId:'session',params:{tools:[{frameId:'frame',name:'search',description,annotations:{readOnly:true,untrustedContent:true,consequential:false,autosubmit:false},...(stackTrace===undefined?{}:{stackTrace})}]}});
    return {};
   }
   externalOperations++;throw Error('Unexpected command '+method);
  }
 };
 const webmcp=webMcpDiscoverySchema.parse(await discoverWebMcp({connection,sessionId:'session',discovery:{version:browser},target:{id:'page',url},input}));
 assert.deepEqual(commands,['Page.enable','Page.getFrameTree','WebMCP.enable','Page.getFrameTree']);
 assert.equal(webmcp.tools.items.length,1);
 assert.equal(webmcp.tools.total,1);
 const completeness={status:'complete_within_window',conditions:['complete_within_window'],policy_filtered_sections:[],attach_limited_sections:[],truncated_sections:[],unavailable_sections:[],excluded:[],dropped_events:{scripts:0,network_requests:0,console_events:0,websocket_connections:0,websocket_frames:0,webmcp_tools:0,timeline_events:0,total:0}};
 return captureSnapshotSchema.parse({webmcp,inspection:{browser,target:{target_id:'page',type:'page',title:'Fixture',url,origin,attached:false},capture_window:{started_at:at,ended_at:at,observation_ms:0},completeness,
 frames:[{frame_id:'frame',parent_frame_id:null,url,origin}],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:[],workers:[],network:{requests:[],websocket_connections:[],coverage_started_at:at,prior_activity_available:false},console:{events:[],coverage_started_at:at,prior_activity_available:false},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:['Synthetic inspection; WebMCP from in-memory production discovery.']}});
};
mkdirSync(out,{recursive:true});
const captures={known:await make('known'),malformed:await make('malformed'),changed:await make('malformed','Changed description'),otherSite:await make('known','Search',99),absent:await make('absent')};
assert.deepEqual(captures.known.inspection,captures.malformed.inspection);
const {registration_source: knownSource, ...knownFacts}=captures.known.webmcp.tools.items[0];
const {registration_source: missingSource, ...missingFacts}=captures.malformed.webmcp.tools.items[0];
assert.notEqual(knownSource,null);
assert.equal(missingSource,null);
assert.deepEqual(knownFacts,missingFacts);
assert.equal(captures.malformed.webmcp.tools.items[0].registration_source,null);
assert.deepEqual(captures.malformed.webmcp.completeness.excluded,[{section:'webmcp_tools',reason:'invalid_protocol_value',count:1}]);
assert.deepEqual(captures.malformed.webmcp.completeness.unavailable_sections,['webmcp_tools']);
assert.equal(captures.known.webmcp.completeness.status,'complete_within_window');
assert.equal(captures.absent.webmcp.completeness.status,'complete_within_window');
for(const [name,value] of Object.entries(captures))writeFileSync(resolve(out,name+'.json'),JSON.stringify(value,null,2)+'\n');
const env={PATH:process.env.PATH,HOME:resolve(out,'home'),XDG_CONFIG_HOME:resolve(out,'config'),XDG_CACHE_HOME:resolve(out,'cache'),NODE_COMPILE_CACHE:resolve(out,'compile-cache'),TMPDIR:resolve(out,'temp'),REA_LOG_LEVEL:'silent',NO_COLOR:'1'};
for(const path of [env.HOME,env.XDG_CONFIG_HOME,env.XDG_CACHE_HOME,env.NODE_COMPILE_CACHE,env.TMPDIR])mkdirSync(path,{recursive:true});
const results=[];
for(const [left,right,expected] of [['known','malformed','changed'],['malformed','known','changed'],['known','known','unchanged'],['malformed','malformed','unknown'],['known','changed','changed'],['known','otherSite','changed'],['absent','absent','unchanged']]){
 const r=spawnSync(process.execPath,[resolve(root,'scripts/rea.mjs'),'compare-web-captures',resolve(out,left+'.json'),resolve(out,right+'.json'),'--json'],{env,cwd:out,encoding:'utf8',timeout:30000});
 writeFileSync(resolve(out,left+'-'+right+'.stdout.json'),r.stdout);writeFileSync(resolve(out,left+'-'+right+'.stderr.txt'),r.stderr);
 assert.equal(r.status,0,r.stderr+r.stdout);assert.equal(r.stderr,'');assert.equal(r.signal,null);
 const d=JSON.parse(r.stdout).normalized_result;assert.equal(d.dimensions.webmcp.status,expected);
 const summary={left,right,exit:r.status,overall:d.overall_status,webmcp:d.dimensions.webmcp};results.push(summary);console.log(JSON.stringify(summary));
}
assert.equal(externalOperations,0);
writeFileSync(resolve(out,'results.json'),JSON.stringify({runtime:process.version,platform:process.platform,arch:process.arch,kernel:release(),externalOperations,results},null,2)+'\n');
console.log(JSON.stringify({runtime:process.version,platform:process.platform,arch:process.arch,kernel:release(),externalOperations}));
JS
CAPTURE_AT="$(TZ=America/Los_Angeles date '+%Y-%m-%dT%H:%M:%S%z')" HOME="$WORK/home" XDG_CONFIG_HOME="$WORK/config" XDG_CACHE_HOME="$WORK/cache" NODE_COMPILE_CACHE="$WORK/compile-cache" TMPDIR="$WORK/temp" node "$WORK/fixture.mjs" "$REA_ROOT" "$WORK"

The primary child invocation is node "$REA_ROOT/scripts/rea.mjs" compare-web-captures "$WORK/known.json" "$WORK/malformed.json" --json. Inspect normalized_result.dimensions.webmcp. The program saves all capture files and full CLI outputs. It pins the current observed results, so completing without assertion failure confirms the reported behavior rather than restoration.

Expected behavior

When all jointly observed registration facts agree and only the other capture's malformed stack makes its source unavailable, the WebMCP dimension should remain unknown with no confirmed modification, in either direction. A correctly retained incomplete-evidence marker must survive comparison. Independently observed description changes and differences between two known registration sites must remain reportable as changed.

Impact and scope

P2: the supported offline comparison asserts a false tool modification and promotes its aggregate result to changed. The defect is bounded to this WebMCP evidence comparison; stored captures remain intact. The seven cases ran once in the independent replay and once more by executing the exact reproduction block above, with both omission-only directions failing the expected contract on both runs. Frequency of malformed stacks in real browsers is NOT_ENOUGH_INFO; no live browser was sampled.

Manual interpretation is available for this pair: the saved invalid-value ledger identifies unavailable source evidence, and comparison of the remaining retained tool fields demonstrates agreement. An analyst can decline that unsupported modification without changing the captures. This does not recover the missing source or establish a general automatic workaround. Live recapture was not tested.

REA version

Source revision ace4dbf36e8c8f36a0e192aff9a11484ed428358, using its prepared compiled runtime and locked dependencies.

Runtime and platform

Node.js v24.22.1; macOS, Darwin kernel 27.0.0; arm64. No browser or native analysis provider was started.

Target and artifact context

One synthetic main frame and one WebMCP registration on an inert example origin. Registration records and discovery completeness come from production discovery with an in-memory connection; unrelated inspection inventories are identical synthetic data. An omitted optional input schema is valid and complete in the known-stack control.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

  • Expected source: AGENTS.md requires omission and uncertainty to survive boundaries; docs/browser-observation.md:28,486 retains malformed-stack registrations with incomplete coverage, a null source, and an invalid-protocol exclusion. src/domain/webCaptureDiff.ts:45 defines changed as an observed difference. The historical contribution record in docs/releases/6.8.0.contributions.json:6520 explicitly states that this malformed-stack state should leave comparisons unknown rather than confirm a source change.
  • Failure source: the executed production discovery and public CLI above. src/browser/CdpWebMcpDiscovery.ts:521-546 retains the null source and records the correct exclusion. src/domain/webCaptureDiff.ts:889-905 includes that unavailable source in the tool digest; src/domain/webCaptureDiff.ts:81-105 retains the resulting modification despite incomplete evidence. The supported passive CLI route is src/cli/browserAdvancedCommands.ts:109-163 through src/application/BrowserObservationService.ts:178-195 and src/browser/CdpBrowserProvider.ts:309-319.
  • Evidence provenance: observed for production discovery, normalized-input validation, sole-tool-field comparison, and public CLI results; source-derived for the call-path explanation and contract attribution.
  • Local verification: reproduced in two complete runs of seven sequential CLI calls each, all 14 exiting 0 with empty stderr. The fake connection reported zero unexpected operations.
  • Reproduction completeness: complete for the supported offline retained-capture workflow. Real-browser acquisition and MCP transport were not exercised.

Selected executed results:

Pair WebMCP status WebMCP changes Overall status
known / malformed changed 1 modified changed
malformed / known changed 1 modified changed
known / known unchanged 0 unknown
malformed / malformed unknown 0 unknown
known / malformed plus changed description changed 1 modified changed
known / different known source line changed 1 modified changed
omitted stack / omitted stack unchanged 0 unknown

The malformed capture has registration_source: null, unavailable_sections: ["webmcp_tools"], and excluded: [{"section":"webmcp_tools","reason":"invalid_protocol_value","count":1}]. The known capture has complete WebMCP coverage and a retained source. Both failing comparisons return reason: null. Other incomplete dimensions account for overall unknown in the self-comparison controls.

Additional context

Restoration check, proposed rather than executed against a fix: rerun the same cases. The known/malformed pair in both directions must report WebMCP unknown with zero changes and must not promote the overall result to changed solely from the unavailable source. Preserve known self-comparison as unchanged, malformed self-comparison as unknown, and omitted-stack self-comparison as unchanged. Preserve changed for the independent description-change and different-known-source controls.

Last known good version and regression onset are NOT_ENOUGH_INFO; neither was established. This report describes a remaining current requirement after a historical claimed fix. It does not assert that the current failure was introduced after that change.

References

Historical merged PR #6016 says the malformed-stack exclusion prevents a confirmed WebMCP source change. The current replay includes that exclusion but still produces the confirmed modification. The remaining requirement is for comparison to preserve the source's unavailable state; no shared-cause or regression claim is needed. Open and closed report searches found no exact open canonical issue. The closed thread is left unchanged.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The supported public offline CLI gives a definite false tool modification and aggregate changed result for the bounded omission-only fixture in both directions; source records remain intact and live-browser frequency is unknown.
  • Workaround status: available
  • Workaround basis: The executed equality assertions and saved invalid-protocol ledger let an analyst compare jointly known registration facts and decline the unsupported source modification without mutating or recapturing evidence; this is a manual interpretation for this pair, not a general automated repair.
Originally created by @coygeek on GitHub (Oct 26, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7329 ## 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 one modified WebMCP tool and `overall_status: "changed"` when two retained registrations differ only because one registration stack was malformed. Production discovery correctly keeps that registration with `registration_source: null` and a `webmcp_tools/invalid_protocol_value` exclusion; comparison still treats the unavailable source as a definite different value. The reverse comparison gives the same false modification. This was reproduced with built production discovery receiving in-memory protocol replies, schema-validated passive capture files, and the public offline CLI. No browser, socket connection, or target code ran. Identical inspection records and equality of every retained tool field except `registration_source` were asserted. ## Reproduction Use the already built source baseline below with its locked dependencies and Node.js 24. Set `REA_ROOT` to the checkout root. Dependency installation and building are prerequisites, not steps rerun during this verification. The following is the executed replay program with portable directory setup. All URLs are inert fixture identities; the connection stub accepts only four expected discovery commands and opens no connection. ```sh WORK="$(mktemp -d)" mkdir -p "$WORK/home" "$WORK/config" "$WORK/cache" "$WORK/compile-cache" "$WORK/temp" cat > "$WORK/fixture.mjs" <<'JS' import assert from 'node:assert/strict'; import {writeFileSync, mkdirSync} from 'node:fs'; import {spawnSync} from 'node:child_process'; import {resolve} from 'node:path'; import {pathToFileURL} from 'node:url'; import {release} from 'node:os'; const root = resolve(process.argv[2]), out = resolve(process.argv[3]); const load = p => import(pathToFileURL(resolve(root,'dist',p+'.js')).href); const {discoverWebMcp} = await load('browser/CdpWebMcpDiscovery'); const {discoverWebMcpToolsInputSchema,webMcpDiscoverySchema} = await load('domain/webMcpDiscovery'); const {captureSnapshotSchema} = await load('domain/webCaptureDiffSchemas'); assert(process.env.CAPTURE_AT); const at = new Date(process.env.CAPTURE_AT).toISOString(); const origin='https://fixture.example.test', url=origin+'/'; const browser={product:'fixture',protocol_version:'1.3',revision:'fixture',user_agent:'fixture',js_version:'fixture'}; const input=discoverWebMcpToolsInputSchema.parse({cdp_endpoint:'http://127.0.0.1:9222',target_id:'page',allowed_origins:[origin],observation_ms:0}); let externalOperations=0; const make = async (stack,description='Search',line=3) => { let listener; const commands=[]; const tree={frameTree:{frame:{id:'frame',url,loaderId:'loader'}}}; const connection={ onEvent(fn){listener=fn;return ()=>{listener=undefined;};}, async send(method){ commands.push(method); if(method==='Page.enable')return {}; if(method==='Page.getFrameTree')return tree; if(method==='WebMCP.enable'){ const stackTrace=stack==='known'?{callFrames:[{url:origin+'/app.js',lineNumber:line,columnNumber:1}]}:stack==='malformed'?{callFrames:[null]}:undefined; listener({method:'WebMCP.toolsAdded',sessionId:'session',params:{tools:[{frameId:'frame',name:'search',description,annotations:{readOnly:true,untrustedContent:true,consequential:false,autosubmit:false},...(stackTrace===undefined?{}:{stackTrace})}]}}); return {}; } externalOperations++;throw Error('Unexpected command '+method); } }; const webmcp=webMcpDiscoverySchema.parse(await discoverWebMcp({connection,sessionId:'session',discovery:{version:browser},target:{id:'page',url},input})); assert.deepEqual(commands,['Page.enable','Page.getFrameTree','WebMCP.enable','Page.getFrameTree']); assert.equal(webmcp.tools.items.length,1); assert.equal(webmcp.tools.total,1); const completeness={status:'complete_within_window',conditions:['complete_within_window'],policy_filtered_sections:[],attach_limited_sections:[],truncated_sections:[],unavailable_sections:[],excluded:[],dropped_events:{scripts:0,network_requests:0,console_events:0,websocket_connections:0,websocket_frames:0,webmcp_tools:0,timeline_events:0,total:0}}; return captureSnapshotSchema.parse({webmcp,inspection:{browser,target:{target_id:'page',type:'page',title:'Fixture',url,origin,attached:false},capture_window:{started_at:at,ended_at:at,observation_ms:0},completeness, frames:[{frame_id:'frame',parent_frame_id:null,url,origin}],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:[],workers:[],network:{requests:[],websocket_connections:[],coverage_started_at:at,prior_activity_available:false},console:{events:[],coverage_started_at:at,prior_activity_available:false},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:['Synthetic inspection; WebMCP from in-memory production discovery.']}}); }; mkdirSync(out,{recursive:true}); const captures={known:await make('known'),malformed:await make('malformed'),changed:await make('malformed','Changed description'),otherSite:await make('known','Search',99),absent:await make('absent')}; assert.deepEqual(captures.known.inspection,captures.malformed.inspection); const {registration_source: knownSource, ...knownFacts}=captures.known.webmcp.tools.items[0]; const {registration_source: missingSource, ...missingFacts}=captures.malformed.webmcp.tools.items[0]; assert.notEqual(knownSource,null); assert.equal(missingSource,null); assert.deepEqual(knownFacts,missingFacts); assert.equal(captures.malformed.webmcp.tools.items[0].registration_source,null); assert.deepEqual(captures.malformed.webmcp.completeness.excluded,[{section:'webmcp_tools',reason:'invalid_protocol_value',count:1}]); assert.deepEqual(captures.malformed.webmcp.completeness.unavailable_sections,['webmcp_tools']); assert.equal(captures.known.webmcp.completeness.status,'complete_within_window'); assert.equal(captures.absent.webmcp.completeness.status,'complete_within_window'); for(const [name,value] of Object.entries(captures))writeFileSync(resolve(out,name+'.json'),JSON.stringify(value,null,2)+'\n'); const env={PATH:process.env.PATH,HOME:resolve(out,'home'),XDG_CONFIG_HOME:resolve(out,'config'),XDG_CACHE_HOME:resolve(out,'cache'),NODE_COMPILE_CACHE:resolve(out,'compile-cache'),TMPDIR:resolve(out,'temp'),REA_LOG_LEVEL:'silent',NO_COLOR:'1'}; for(const path of [env.HOME,env.XDG_CONFIG_HOME,env.XDG_CACHE_HOME,env.NODE_COMPILE_CACHE,env.TMPDIR])mkdirSync(path,{recursive:true}); const results=[]; for(const [left,right,expected] of [['known','malformed','changed'],['malformed','known','changed'],['known','known','unchanged'],['malformed','malformed','unknown'],['known','changed','changed'],['known','otherSite','changed'],['absent','absent','unchanged']]){ const r=spawnSync(process.execPath,[resolve(root,'scripts/rea.mjs'),'compare-web-captures',resolve(out,left+'.json'),resolve(out,right+'.json'),'--json'],{env,cwd:out,encoding:'utf8',timeout:30000}); writeFileSync(resolve(out,left+'-'+right+'.stdout.json'),r.stdout);writeFileSync(resolve(out,left+'-'+right+'.stderr.txt'),r.stderr); assert.equal(r.status,0,r.stderr+r.stdout);assert.equal(r.stderr,'');assert.equal(r.signal,null); const d=JSON.parse(r.stdout).normalized_result;assert.equal(d.dimensions.webmcp.status,expected); const summary={left,right,exit:r.status,overall:d.overall_status,webmcp:d.dimensions.webmcp};results.push(summary);console.log(JSON.stringify(summary)); } assert.equal(externalOperations,0); writeFileSync(resolve(out,'results.json'),JSON.stringify({runtime:process.version,platform:process.platform,arch:process.arch,kernel:release(),externalOperations,results},null,2)+'\n'); console.log(JSON.stringify({runtime:process.version,platform:process.platform,arch:process.arch,kernel:release(),externalOperations})); JS CAPTURE_AT="$(TZ=America/Los_Angeles date '+%Y-%m-%dT%H:%M:%S%z')" HOME="$WORK/home" XDG_CONFIG_HOME="$WORK/config" XDG_CACHE_HOME="$WORK/cache" NODE_COMPILE_CACHE="$WORK/compile-cache" TMPDIR="$WORK/temp" node "$WORK/fixture.mjs" "$REA_ROOT" "$WORK" ``` The primary child invocation is `node "$REA_ROOT/scripts/rea.mjs" compare-web-captures "$WORK/known.json" "$WORK/malformed.json" --json`. Inspect `normalized_result.dimensions.webmcp`. The program saves all capture files and full CLI outputs. It pins the current observed results, so completing without assertion failure confirms the reported behavior rather than restoration. ## Expected behavior When all jointly observed registration facts agree and only the other capture's malformed stack makes its source unavailable, the WebMCP dimension should remain `unknown` with no confirmed modification, in either direction. A correctly retained incomplete-evidence marker must survive comparison. Independently observed description changes and differences between two known registration sites must remain reportable as `changed`. ## Impact and scope P2: the supported offline comparison asserts a false tool modification and promotes its aggregate result to changed. The defect is bounded to this WebMCP evidence comparison; stored captures remain intact. The seven cases ran once in the independent replay and once more by executing the exact reproduction block above, with both omission-only directions failing the expected contract on both runs. Frequency of malformed stacks in real browsers is NOT_ENOUGH_INFO; no live browser was sampled. Manual interpretation is available for this pair: the saved invalid-value ledger identifies unavailable source evidence, and comparison of the remaining retained tool fields demonstrates agreement. An analyst can decline that unsupported modification without changing the captures. This does not recover the missing source or establish a general automatic workaround. Live recapture was not tested. ## REA version Source revision `ace4dbf36e8c8f36a0e192aff9a11484ed428358`, using its prepared compiled runtime and locked dependencies. ## Runtime and platform Node.js v24.22.1; macOS, Darwin kernel 27.0.0; arm64. No browser or native analysis provider was started. ## Target and artifact context One synthetic main frame and one WebMCP registration on an inert example origin. Registration records and discovery completeness come from production discovery with an in-memory connection; unrelated inspection inventories are identical synthetic data. An omitted optional input schema is valid and complete in the known-stack control. ## Interface CLI ## Earliest failure stage Transport or result projection ## Relevant output or evidence - Expected source: `AGENTS.md` requires omission and uncertainty to survive boundaries; `docs/browser-observation.md:28,486` retains malformed-stack registrations with incomplete coverage, a null source, and an invalid-protocol exclusion. `src/domain/webCaptureDiff.ts:45` defines changed as an observed difference. The historical contribution record in `docs/releases/6.8.0.contributions.json:6520` explicitly states that this malformed-stack state should leave comparisons unknown rather than confirm a source change. - Failure source: the executed production discovery and public CLI above. `src/browser/CdpWebMcpDiscovery.ts:521-546` retains the null source and records the correct exclusion. `src/domain/webCaptureDiff.ts:889-905` includes that unavailable source in the tool digest; `src/domain/webCaptureDiff.ts:81-105` retains the resulting modification despite incomplete evidence. The supported passive CLI route is `src/cli/browserAdvancedCommands.ts:109-163` through `src/application/BrowserObservationService.ts:178-195` and `src/browser/CdpBrowserProvider.ts:309-319`. - Evidence provenance: observed for production discovery, normalized-input validation, sole-tool-field comparison, and public CLI results; source-derived for the call-path explanation and contract attribution. - Local verification: reproduced in two complete runs of seven sequential CLI calls each, all 14 exiting 0 with empty stderr. The fake connection reported zero unexpected operations. - Reproduction completeness: complete for the supported offline retained-capture workflow. Real-browser acquisition and MCP transport were not exercised. Selected executed results: | Pair | WebMCP status | WebMCP changes | Overall status | | --- | --- | --- | --- | | known / malformed | changed | 1 modified | changed | | malformed / known | changed | 1 modified | changed | | known / known | unchanged | 0 | unknown | | malformed / malformed | unknown | 0 | unknown | | known / malformed plus changed description | changed | 1 modified | changed | | known / different known source line | changed | 1 modified | changed | | omitted stack / omitted stack | unchanged | 0 | unknown | The malformed capture has `registration_source: null`, `unavailable_sections: ["webmcp_tools"]`, and `excluded: [{"section":"webmcp_tools","reason":"invalid_protocol_value","count":1}]`. The known capture has complete WebMCP coverage and a retained source. Both failing comparisons return `reason: null`. Other incomplete dimensions account for overall unknown in the self-comparison controls. ## Additional context Restoration check, proposed rather than executed against a fix: rerun the same cases. The known/malformed pair in both directions must report WebMCP unknown with zero changes and must not promote the overall result to changed solely from the unavailable source. Preserve known self-comparison as unchanged, malformed self-comparison as unknown, and omitted-stack self-comparison as unchanged. Preserve changed for the independent description-change and different-known-source controls. Last known good version and regression onset are NOT_ENOUGH_INFO; neither was established. This report describes a remaining current requirement after a historical claimed fix. It does not assert that the current failure was introduced after that change. ## References [Historical merged PR #6016](https://github.com/morluto/rea/pull/6016) says the malformed-stack exclusion prevents a confirmed WebMCP source change. The current replay includes that exclusion but still produces the confirmed modification. The remaining requirement is for comparison to preserve the source's unavailable state; no shared-cause or regression claim is needed. Open and closed report searches found no exact open canonical issue. The closed thread is left unchanged. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The supported public offline CLI gives a definite false tool modification and aggregate changed result for the bounded omission-only fixture in both directions; source records remain intact and live-browser frequency is unknown. - Workaround status: available - Workaround basis: The executed equality assertions and saved invalid-protocol ledger let an analyst compare jointly known registration facts and decline the unsupported source modification without mutating or recapturing evidence; this is a manual interpretation for this pair, not a general automated repair.
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#4357
No description provided.