[GH-ISSUE #6044] [Bug] HAR grouping views discard nested parent schema diagnostics #3689

Closed
opened 2026-10-31 00:43:05 +00:00 by bsakaguchi · 1 comment
Owner

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

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

For the same authenticated inspect_web_network_capture parent with artifact.bytes set to a string, MCP inspect_analysis_view reports different schema diagnostics by view kind. Summary identifies source.normalized_result.artifact.bytes with invalid_type and expected number; api-surface, environment-manifest and environment-state each return one generic invalid_value issue at source.

The direct production projector also retains a ZodError cause for summary and no cause for the three grouping views. Correcting the byte count succeeds through MCP for all four kinds.

Reproduction

With dependencies installed at this revision, save this as tests/boundary/mcp/issuePathReproduction.test.ts and run:

node node_modules/vitest/vitest.mjs run tests/boundary/mcp/issuePathReproduction.test.ts --maxWorkers 1

This executed the real registered MCP tool through the SDK in-memory transport, using the repository's synthetic normalized capture fixture. The Evidence factory recomputes its digest. A direct projector comparison checks the internal cause, which is not a property of the public MCP error. No capture decoder, browser, target or external provider is started. The assertions record the current defect.

import { Client, InMemoryTransport } from '@modelcontextprotocol/client';
import { expect, it } from 'vitest';
import { createServer } from '../../../src/server/createServer.js';
import { createTestBinarySession } from '../../fixtures/binarySession.js';
import { parseMcpToolError } from '../../fixtures/mcpToolError.js';
import { analysisViewCaptureFixture, analysisViewCaptureEvidence } from '../../fixtures/analysisView.js';
import { projectAnalysisView } from '../../../src/domain/analysisView/analysisView.js';

const withMcp = async (run: (client: Client) => Promise<void>) => {
  let providerLaunches = 0;
  const session = createTestBinarySession(() => { providerLaunches++; throw new Error('No provider expected'); });
  const server = createServer({kind:'session', session});
  const client = new Client({name:'issue-path-test', version:'1'});
  const [ct, st] = InMemoryTransport.createLinkedPair();
  try {
    await server.connect(st); await client.connect(ct);
    await run(client);
    expect(providerLaunches).toBe(0);
    await client.ping();
  } finally { await client.close();await server.close();await session.close(); }
};


it('drops HAR parent diagnostics only for grouping views through MCP', async () => {
  await withMcp(async (client) => {
    const validCapture = analysisViewCaptureFixture();
    const malformedCapture = {...validCapture, artifact:{...validCapture.artifact,bytes:'bad'}};
    const parent = analysisViewCaptureEvidence(malformedCapture as never);
    for(const kind of ['summary','api-surface','environment-manifest','environment-state'] as const) {
      const request = {source:{kind:'inline' as const,evidence:parent},view:kind === 'api-surface' ? {kind,offset:0,limit:64} : {kind}};
      const response = await client.callTool({name:'inspect_analysis_view',arguments:request});
      const diagnostic = parseMcpToolError(response);
      console.log(JSON.stringify({case:'har-'+kind,diagnostic}));
      expect(diagnostic.error.details).toMatchObject({issues:[{path:kind === 'summary' ? ['source','normalized_result','artifact','bytes'] : ['source'],reason:kind === 'summary' ? 'invalid_type' : 'invalid_value'}]});
      const direct = projectAnalysisView({evidenceId:parent.evidence_id,operation:parent.operation,normalizedResult:parent.normalized_result,parameters:parent.parameters,limitations:[],subject:parent.subject},request.view as never);
      if(direct.ok) throw new Error('Expected invalid parent');
      console.log(JSON.stringify({case:'direct-'+kind,cause:direct.error.cause?.constructor.name ?? null}));
      expect(direct.error.cause !== undefined).toBe(kind === 'summary');
      const corrected = await client.callTool({name:'inspect_analysis_view',arguments:{...request,source:{kind:'inline',evidence:analysisViewCaptureEvidence(validCapture)}}});
      expect(corrected.isError).not.toBe(true);
      console.log(JSON.stringify({case:kind+'-valid',success:true}));
    }
  });
});

The focused run passed: four malformed-parent rejections, four successful corrected-parent controls, and zero provider launches.

Expected behavior

Preserve the owning-schema issue beneath source.normalized_result for each view, including the safe field/type reason and internal cause. The shared parent parser explicitly owns that convention. The sibling parser for this same capture operation already preserves it, and native parent regression coverage asserts nested-field attribution.

Impact and scope

P3: correction detail is lost for already-rejected malformed parent data. All three grouping kinds reproduced; valid controls succeeded. The diagnostic cannot name the field to correct, but callers can select summary for the same parent to obtain its precise rejection. No invalid success, valid-input failure, data loss, actual downstream recovery failure or normal producer emitting malformed data was observed. Production frequency is unknown.

REA version

rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1 arm64; locked Vitest 5.0.3. The repository pins Node 24.18.0, which was not separately tested.

Target and artifact context

Digest-valid synthetic normalized HAR capture Evidence, differing from a valid fixture only at artifact.bytes. This is a malformed owned capture result, rather than a raw HAR file. The parent is small enough to pass representation admission.

Interface

MCP

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Actual summary issue:

{"path":["source","normalized_result","artifact","bytes"],"reason":"invalid_type","expected":"number"}

Actual issue for each of the three grouping kinds:

{"path":["source"],"reason":"invalid_value","message":"Parent Evidence result does not match inspect_web_network_capture."}

Every response has code: invalid_request, category: invalid_input, retryable: false and inspect_input_contract recovery for inspect_analysis_view. Direct projection reports cause constructor ZodError for summary; the grouping errors have no cause. Corrected parents return successful Evidence in all four views.

Typed outcome or recovery expected

Keep the refusal, classification, non-retryable status and existing recovery. Return an issue at source.normalized_result.artifact.bytes with invalid_type and expected number for this input, and retain the parse cause internally. Preserve admission budgeting before parsing.

Analysis or suspected cause

Source-derived: admitHarCaptureParent calls the owned capture parser but substitutes a hand-written generic issue on failure. Each grouping projector uses this shared admission. The sibling summary parser maps the original issues instead. This loses information before MCP serialization.

Additional context

Closed PR #3122 was checked by body and complete diff. It corrects pcap parent prefixes and refactors the already-correct sibling web-network parser; it does not change the shared grouping admission in WebNetworkProjectionAccumulator.ts. Applying that diff leaves this reproduction unchanged. Related but separate: #6003 concerns a separate binary-layout dispatcher branch, and #6034 concerns artifactInventoryView.sourceFailure; neither changes this HAR grouping admission. #6001 concerns unsupported artifact view selection. #5950, #5971 and #5980 concern separate validation producers. PR #2708 concerns original HAR file decoding, and #4954 adds manifest grouping rather than repairing this diagnostic.

Fetched upstream main 6d356b84344659085f2b912857b5e27fe0160c9b leaves the relevant admission, owning capture schema, sibling parser and shared helper unchanged. That later revision was inspected, not executed. The CLI and browser-scenario parent branch were not separately exercised.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6044 ### 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 For the same authenticated `inspect_web_network_capture` parent with `artifact.bytes` set to a string, MCP `inspect_analysis_view` reports different schema diagnostics by view kind. Summary identifies `source.normalized_result.artifact.bytes` with `invalid_type` and expected `number`; `api-surface`, `environment-manifest` and `environment-state` each return one generic `invalid_value` issue at `source`. The direct production projector also retains a `ZodError` cause for summary and no cause for the three grouping views. Correcting the byte count succeeds through MCP for all four kinds. ### Reproduction With dependencies installed at this revision, save this as `tests/boundary/mcp/issuePathReproduction.test.ts` and run: ```sh node node_modules/vitest/vitest.mjs run tests/boundary/mcp/issuePathReproduction.test.ts --maxWorkers 1 ``` This executed the real registered MCP tool through the SDK in-memory transport, using the repository's synthetic normalized capture fixture. The Evidence factory recomputes its digest. A direct projector comparison checks the internal cause, which is not a property of the public MCP error. No capture decoder, browser, target or external provider is started. The assertions record the current defect. ```ts import { Client, InMemoryTransport } from '@modelcontextprotocol/client'; import { expect, it } from 'vitest'; import { createServer } from '../../../src/server/createServer.js'; import { createTestBinarySession } from '../../fixtures/binarySession.js'; import { parseMcpToolError } from '../../fixtures/mcpToolError.js'; import { analysisViewCaptureFixture, analysisViewCaptureEvidence } from '../../fixtures/analysisView.js'; import { projectAnalysisView } from '../../../src/domain/analysisView/analysisView.js'; const withMcp = async (run: (client: Client) => Promise<void>) => { let providerLaunches = 0; const session = createTestBinarySession(() => { providerLaunches++; throw new Error('No provider expected'); }); const server = createServer({kind:'session', session}); const client = new Client({name:'issue-path-test', version:'1'}); const [ct, st] = InMemoryTransport.createLinkedPair(); try { await server.connect(st); await client.connect(ct); await run(client); expect(providerLaunches).toBe(0); await client.ping(); } finally { await client.close();await server.close();await session.close(); } }; it('drops HAR parent diagnostics only for grouping views through MCP', async () => { await withMcp(async (client) => { const validCapture = analysisViewCaptureFixture(); const malformedCapture = {...validCapture, artifact:{...validCapture.artifact,bytes:'bad'}}; const parent = analysisViewCaptureEvidence(malformedCapture as never); for(const kind of ['summary','api-surface','environment-manifest','environment-state'] as const) { const request = {source:{kind:'inline' as const,evidence:parent},view:kind === 'api-surface' ? {kind,offset:0,limit:64} : {kind}}; const response = await client.callTool({name:'inspect_analysis_view',arguments:request}); const diagnostic = parseMcpToolError(response); console.log(JSON.stringify({case:'har-'+kind,diagnostic})); expect(diagnostic.error.details).toMatchObject({issues:[{path:kind === 'summary' ? ['source','normalized_result','artifact','bytes'] : ['source'],reason:kind === 'summary' ? 'invalid_type' : 'invalid_value'}]}); const direct = projectAnalysisView({evidenceId:parent.evidence_id,operation:parent.operation,normalizedResult:parent.normalized_result,parameters:parent.parameters,limitations:[],subject:parent.subject},request.view as never); if(direct.ok) throw new Error('Expected invalid parent'); console.log(JSON.stringify({case:'direct-'+kind,cause:direct.error.cause?.constructor.name ?? null})); expect(direct.error.cause !== undefined).toBe(kind === 'summary'); const corrected = await client.callTool({name:'inspect_analysis_view',arguments:{...request,source:{kind:'inline',evidence:analysisViewCaptureEvidence(validCapture)}}}); expect(corrected.isError).not.toBe(true); console.log(JSON.stringify({case:kind+'-valid',success:true})); } }); }); ``` The focused run passed: four malformed-parent rejections, four successful corrected-parent controls, and zero provider launches. ### Expected behavior Preserve the owning-schema issue beneath `source.normalized_result` for each view, including the safe field/type reason and internal cause. The [shared parent parser](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisView/analysisViewParent.ts#L7-L25) explicitly owns that convention. [The sibling parser for this same capture operation](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisView/webNetworkCaptureView.ts#L417-L433) already preserves it, and [native parent regression coverage](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisView/analysisView.test.ts#L265-L287) asserts nested-field attribution. ### Impact and scope P3: correction detail is lost for already-rejected malformed parent data. All three grouping kinds reproduced; valid controls succeeded. The diagnostic cannot name the field to correct, but callers can select summary for the same parent to obtain its precise rejection. No invalid success, valid-input failure, data loss, actual downstream recovery failure or normal producer emitting malformed data was observed. Production frequency is unknown. ### REA version rea-agents 6.7.0; source revision `52415975942a4b9c53a817915b313ccc404f280e`. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1 arm64; locked Vitest 5.0.3. The repository pins Node 24.18.0, which was not separately tested. ### Target and artifact context Digest-valid synthetic normalized HAR capture Evidence, differing from a valid fixture only at `artifact.bytes`. This is a malformed owned capture result, rather than a raw HAR file. The parent is small enough to pass representation admission. ### Interface MCP ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Actual summary issue: ```json {"path":["source","normalized_result","artifact","bytes"],"reason":"invalid_type","expected":"number"} ``` Actual issue for each of the three grouping kinds: ```json {"path":["source"],"reason":"invalid_value","message":"Parent Evidence result does not match inspect_web_network_capture."} ``` Every response has `code: invalid_request`, `category: invalid_input`, `retryable: false` and `inspect_input_contract` recovery for `inspect_analysis_view`. Direct projection reports cause constructor `ZodError` for summary; the grouping errors have no cause. Corrected parents return successful Evidence in all four views. ### Typed outcome or recovery expected Keep the refusal, classification, non-retryable status and existing recovery. Return an issue at `source.normalized_result.artifact.bytes` with `invalid_type` and expected `number` for this input, and retain the parse cause internally. Preserve admission budgeting before parsing. ### Analysis or suspected cause Source-derived: [admitHarCaptureParent](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisView/WebNetworkProjectionAccumulator.ts#L103-L135) calls the owned capture parser but substitutes a hand-written generic issue on failure. Each grouping projector uses this shared admission. The sibling summary parser maps the original issues instead. This loses information before MCP serialization. ### Additional context Closed PR #3122 was checked by body and complete diff. It corrects pcap parent prefixes and refactors the already-correct sibling web-network parser; it does not change the shared grouping admission in `WebNetworkProjectionAccumulator.ts`. Applying that diff leaves this reproduction unchanged. Related but separate: [#6003](https://github.com/morluto/rea/issues/6003) concerns a separate binary-layout dispatcher branch, and [#6034](https://github.com/morluto/rea/issues/6034) concerns `artifactInventoryView.sourceFailure`; neither changes this HAR grouping admission. #6001 concerns unsupported artifact view selection. #5950, #5971 and #5980 concern separate validation producers. PR #2708 concerns original HAR file decoding, and #4954 adds manifest grouping rather than repairing this diagnostic. Fetched upstream main `6d356b84344659085f2b912857b5e27fe0160c9b` leaves the relevant admission, owning capture schema, sibling parser and shared helper unchanged. That later revision was inspected, not executed. The CLI and browser-scenario parent branch were not separately exercised.
Author
Owner

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

Checked the reported fix on main at d87e73f2db.

HAR grouping admission now calls analysisViewResultInputError with the owning parser error after budgeting. The three-kind regression changes artifact.bytes to a string, asserts a ZodError cause, and checks the projected source.normalized_result.artifact.bytes invalid_type path. This covers the original malformed-parent trigger, rather than merely testing valid HAR grouping.

Implementation: WebNetworkProjectionAccumulator.ts, webNetworkApiSurfaceView.test.ts.

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

<!-- gh-comment-id:6380716743 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. HAR grouping admission now calls analysisViewResultInputError with the owning parser error after budgeting. The three-kind regression changes artifact.bytes to a string, asserts a ZodError cause, and checks the projected source.normalized_result.artifact.bytes invalid_type path. This covers the original malformed-parent trigger, rather than merely testing valid HAR grouping. Implementation: [WebNetworkProjectionAccumulator.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/analysisView/WebNetworkProjectionAccumulator.ts#L128), [webNetworkApiSurfaceView.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/analysisView/webNetworkApiSurfaceView.test.ts#L216). The relevant regression checks in [webNetworkApiSurfaceView.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/analysisView/webNetworkApiSurfaceView.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3689
No description provided.