[GH-ISSUE #3215] [Bug] Managed artifact delivery choices create duplicate retained source Evidence IDs #1151

Closed
opened 2026-10-15 18:31:32 +00:00 by bsakaguchi · 1 comment
Owner

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

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

In one retained MCP session, calling inspect_managed_artifact on the same unchanged managed PE/CLI artifact with detail: "summary", omitted detail and detail: "complete" retains three complete source observations with different evidence_id values. Their normalized results agree. The summary's parent_evidence_id differs from the IDs returned by both complete-delivery variants, and filtered get_evidence_bundle discovery reports three matching complete inspection records.

Independent verification exercised production MCP registration through the SDK client and built-in managed static parser. Every tool call succeeded and passed Evidence authentication. A repeated omitted-detail call reused its previous source ID, and the summary linked correctly to its own retained parent.

Reproduction

  1. Start from a clean checkout at the verified baseline and prepare the repository-locked dependencies and runtime artifacts. Verification used an already-prepared checkout; it did not rerun these setup commands.
git clone https://github.com/morluto/rea.git rea-repro
cd rea-repro
git checkout 58300885207133422af235e0f2b10658cf958b03
npm ci
npm run build:cached
  1. Isolate the reproduction's configuration, cache, data and inline-created fixture beneath the checkout.
mkdir -p .repro/home/config .repro/home/cache .repro/home/data .repro/fixtures
export HOME="$PWD/.repro/home"
export XDG_CONFIG_HOME="$HOME/config"
export XDG_CACHE_HOME="$HOME/cache"
export XDG_DATA_HOME="$HOME/data"
export REPRO_DIR="$PWD/.repro/fixtures"
  1. Create and run the following temporary test. It creates the managed PE fixture inline, makes the MCP calls through the production server registration, checks equal metadata and repeated-call identity, and then asserts equality between complete delivery and the summary's retained source ID.
cat > src/server/repro.test.ts <<'EOF'
import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it, onTestFinished } from "vitest";
import { composeBinarySession } from "../application/binary/BinarySessionComposition.js";
import { AnalysisProviderRegistry } from "../application/binary/AnalysisProviderRegistry.js";
import { ManagedStaticProvider } from "../dotnet/ManagedStaticProvider.js";
import { buildManagedPeFixture } from "../dotnet/ManagedPe.fixture.js";
import { parseEvidence } from "../domain/evidence.js";
import { createApplicationMcpHarness } from "../../tests/fixtures/applicationMcpHarness.js";

it("keeps managed source identity independent of delivery", async () => {
  const path = join(process.env.REPRO_DIR!, "sample.dll");
  await writeFile(path, buildManagedPeFixture({}));
  const session = composeBinarySession(new AnalysisProviderRegistry([]), [new ManagedStaticProvider()]);
  const harness = await createApplicationMcpHarness({ session });
  onTestFinished(harness.close);
  const call = async (detail?: string) => {
    const reply = await harness.client.callTool({ name: "inspect_managed_artifact", arguments: { path, ...(detail ? { detail } : {}) } });
    expect(reply.isError).not.toBe(true);
    return parseEvidence(reply.structuredContent);
  };
  const summary = await call("summary");
  const parent = session.evidenceById(summary.normalized_result.parent_evidence_id as string)!;
  expect(parent).toBeDefined();
  const complete = await call();
  const explicit = await call("complete");
  const repeated = await call();
  expect(complete.evidence_id).toBe(repeated.evidence_id);
  expect(complete.normalized_result).toEqual(parent.normalized_result);
  expect(explicit.normalized_result).toEqual(parent.normalized_result);
  const discovery = await harness.client.callTool({ name: "get_evidence_bundle", arguments: { detail: "summary", filters: { operation: "inspect_managed_artifact" } } });
  expect(discovery.isError).not.toBe(true);
  const bundle = discovery.structuredContent as { result: { matching_records: number } };
  console.log(JSON.stringify({
    sameNormalizedResult: JSON.stringify(complete.normalized_result) === JSON.stringify(parent.normalized_result),
    parentMatchesOmitted: parent.evidence_id === complete.evidence_id,
    parentMatchesExplicit: parent.evidence_id === explicit.evidence_id,
    omittedMatchesRepeated: complete.evidence_id === repeated.evidence_id,
    matchingRecords: bundle.result.matching_records,
  }, null, 2));
  const view = await harness.client.callTool({ name: "inspect_analysis_view", arguments: { source: { kind: "retained-evidence", evidence_id: complete.evidence_id }, view: { kind: "summary" } } });
  expect(view.isError).not.toBe(true);
  expect(parseEvidence(view.structuredContent).normalized_result.parent_evidence_id).toBe(complete.evidence_id);
  expect(complete.evidence_id).toBe(parent.evidence_id);
});
EOF
npm run test:local -- src/server/repro.test.ts
  1. Observe one failed test at the final source-ID equality assertion. The preceding controls and complete-first workaround check succeed, and discovery reports three complete inspection records. Remove the reproduction files afterward.
rm src/server/repro.test.ts
rm -rf .repro

Expected behavior

Changing only delivery detail should preserve the complete source Evidence ID for unchanged artifact bytes and analysis parameters. Summary delivery should retain that complete observation and return a separate view linked to it. Complete delivery should return the same source observation inline. After the delivery variants and repeated calls, filtered discovery should report one complete inspection record.

The applicable contract is src/contracts/managed/managedToolContracts.ts:24-27 and :70, which treats detail as delivery selection and promises complete inventories or a distinct linked retained summary. The shared delivery schema at src/contracts/analysisDetail.ts:3-11 describes delivery of completed analysis Evidence. src/server/registerEvidenceTools.ts:36-40 explicitly excludes adapter-only delivery choices from Evidence identity. docs/managed-code-analysis.md:43-82 documents the managed overview and retained-row workflow.

Impact and scope

This is a P2 defect in stable identity and retained discovery. Callers receive duplicate complete observations and cannot correlate the delivery variants by their source ID. The observed failure is bounded to the affected session records. Complete metadata, coverage and valid retained references remain available; the reproduction does not establish metadata corruption, lost records, target execution or a security boundary crossing.

A demonstrated workaround is to request complete delivery with omitted detail consistently, then call inspect_analysis_view with that retained source and a summary view. Verification confirmed the resulting summary's correct parent link. This requires the larger initial response for artifacts that fit complete delivery. Stable identity does not imply a promise to cache analysis or avoid rerunning the parser on a fresh producer request.

REA version

6.4.0 (main at 5830088520)

Runtime and platform

Node 26.10.0; macOS 27.0 arm64

Target and artifact context

An unchanged managed PE/CLI DLL created inline with buildManagedPeFixture({}), inspected through the built-in managed static parser in one session with working Evidence retention. No native engine or execution of target code is required.

Interface

MCP

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

sameNormalizedResult: true
parentMatchesOmitted: false
parentMatchesExplicit: false
omittedMatchesRepeated: true
matchingRecords: 3

One test failed at the final complete-versus-summary-parent source-ID equality assertion.

Typed outcome or recovery expected

All successful delivery variants should identify one complete retained source observation. The summary should keep its separate view ID and link to that source through normalized_result.parent_evidence_id. Preserve the existing normalized metadata and coverage. A regression check should also ensure different caller-selected artifact paths remain distinct. These are restoration criteria; no repaired behavior was executed during verification.

Analysis or suspected cause

Source-derived reasoning at baseline 58300885207133422af235e0f2b10658cf958b03: src/server/registerManagedTools.ts:28 removes detail before provider execution, but its registration at :72 supplies no parametersForEvidence projection. src/server/registerEvidenceTools.ts:115 therefore retains the original caller parameters. Those parameters participate in the semantic ID at src/domain/evidence.ts:246-276, including :260. The observed complete records have parameters shaped as {path, detail: "summary"}, {path} and {path, detail: "complete"} despite equal normalized results.

Existing coverage at tests/boundary/mcp/managedArtifactSummaryMcp.test.ts:82 expects summary detail in the retained parent's parameters. Its later complete-delivery checks compare normalized payloads without asserting source-ID equality, so they do not detect this identity defect.

Additional context

Related but distinct from #3010, which requests retained delivery for managed member inspection, and #3134, which measures large retained-investigation paging; this report concerns delivery-dependent source IDs in the existing managed artifact inspection path.

P2 reflects consequential duplicate discovery and source-ID fragmentation with accessible complete metadata and a demonstrated complete-first workaround.

Related: #3216 reports a separate failure in the same area; the two have independent fixes.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3215 ### 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 In one retained MCP session, calling `inspect_managed_artifact` on the same unchanged managed PE/CLI artifact with `detail: "summary"`, omitted detail and `detail: "complete"` retains three complete source observations with different `evidence_id` values. Their normalized results agree. The summary's `parent_evidence_id` differs from the IDs returned by both complete-delivery variants, and filtered `get_evidence_bundle` discovery reports three matching complete inspection records. Independent verification exercised production MCP registration through the SDK client and built-in managed static parser. Every tool call succeeded and passed Evidence authentication. A repeated omitted-detail call reused its previous source ID, and the summary linked correctly to its own retained parent. ### Reproduction 1. Start from a clean checkout at the verified baseline and prepare the repository-locked dependencies and runtime artifacts. Verification used an already-prepared checkout; it did not rerun these setup commands. ```sh git clone https://github.com/morluto/rea.git rea-repro cd rea-repro git checkout 58300885207133422af235e0f2b10658cf958b03 npm ci npm run build:cached ``` 2. Isolate the reproduction's configuration, cache, data and inline-created fixture beneath the checkout. ```sh mkdir -p .repro/home/config .repro/home/cache .repro/home/data .repro/fixtures export HOME="$PWD/.repro/home" export XDG_CONFIG_HOME="$HOME/config" export XDG_CACHE_HOME="$HOME/cache" export XDG_DATA_HOME="$HOME/data" export REPRO_DIR="$PWD/.repro/fixtures" ``` 3. Create and run the following temporary test. It creates the managed PE fixture inline, makes the MCP calls through the production server registration, checks equal metadata and repeated-call identity, and then asserts equality between complete delivery and the summary's retained source ID. ```sh cat > src/server/repro.test.ts <<'EOF' import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect, it, onTestFinished } from "vitest"; import { composeBinarySession } from "../application/binary/BinarySessionComposition.js"; import { AnalysisProviderRegistry } from "../application/binary/AnalysisProviderRegistry.js"; import { ManagedStaticProvider } from "../dotnet/ManagedStaticProvider.js"; import { buildManagedPeFixture } from "../dotnet/ManagedPe.fixture.js"; import { parseEvidence } from "../domain/evidence.js"; import { createApplicationMcpHarness } from "../../tests/fixtures/applicationMcpHarness.js"; it("keeps managed source identity independent of delivery", async () => { const path = join(process.env.REPRO_DIR!, "sample.dll"); await writeFile(path, buildManagedPeFixture({})); const session = composeBinarySession(new AnalysisProviderRegistry([]), [new ManagedStaticProvider()]); const harness = await createApplicationMcpHarness({ session }); onTestFinished(harness.close); const call = async (detail?: string) => { const reply = await harness.client.callTool({ name: "inspect_managed_artifact", arguments: { path, ...(detail ? { detail } : {}) } }); expect(reply.isError).not.toBe(true); return parseEvidence(reply.structuredContent); }; const summary = await call("summary"); const parent = session.evidenceById(summary.normalized_result.parent_evidence_id as string)!; expect(parent).toBeDefined(); const complete = await call(); const explicit = await call("complete"); const repeated = await call(); expect(complete.evidence_id).toBe(repeated.evidence_id); expect(complete.normalized_result).toEqual(parent.normalized_result); expect(explicit.normalized_result).toEqual(parent.normalized_result); const discovery = await harness.client.callTool({ name: "get_evidence_bundle", arguments: { detail: "summary", filters: { operation: "inspect_managed_artifact" } } }); expect(discovery.isError).not.toBe(true); const bundle = discovery.structuredContent as { result: { matching_records: number } }; console.log(JSON.stringify({ sameNormalizedResult: JSON.stringify(complete.normalized_result) === JSON.stringify(parent.normalized_result), parentMatchesOmitted: parent.evidence_id === complete.evidence_id, parentMatchesExplicit: parent.evidence_id === explicit.evidence_id, omittedMatchesRepeated: complete.evidence_id === repeated.evidence_id, matchingRecords: bundle.result.matching_records, }, null, 2)); const view = await harness.client.callTool({ name: "inspect_analysis_view", arguments: { source: { kind: "retained-evidence", evidence_id: complete.evidence_id }, view: { kind: "summary" } } }); expect(view.isError).not.toBe(true); expect(parseEvidence(view.structuredContent).normalized_result.parent_evidence_id).toBe(complete.evidence_id); expect(complete.evidence_id).toBe(parent.evidence_id); }); EOF npm run test:local -- src/server/repro.test.ts ``` 4. Observe one failed test at the final source-ID equality assertion. The preceding controls and complete-first workaround check succeed, and discovery reports three complete inspection records. Remove the reproduction files afterward. ```sh rm src/server/repro.test.ts rm -rf .repro ``` ### Expected behavior Changing only delivery detail should preserve the complete source Evidence ID for unchanged artifact bytes and analysis parameters. Summary delivery should retain that complete observation and return a separate view linked to it. Complete delivery should return the same source observation inline. After the delivery variants and repeated calls, filtered discovery should report one complete inspection record. The applicable contract is `src/contracts/managed/managedToolContracts.ts:24-27` and `:70`, which treats detail as delivery selection and promises complete inventories or a distinct linked retained summary. The shared delivery schema at `src/contracts/analysisDetail.ts:3-11` describes delivery of completed analysis Evidence. `src/server/registerEvidenceTools.ts:36-40` explicitly excludes adapter-only delivery choices from Evidence identity. `docs/managed-code-analysis.md:43-82` documents the managed overview and retained-row workflow. ### Impact and scope This is a P2 defect in stable identity and retained discovery. Callers receive duplicate complete observations and cannot correlate the delivery variants by their source ID. The observed failure is bounded to the affected session records. Complete metadata, coverage and valid retained references remain available; the reproduction does not establish metadata corruption, lost records, target execution or a security boundary crossing. A demonstrated workaround is to request complete delivery with omitted detail consistently, then call `inspect_analysis_view` with that retained source and a summary view. Verification confirmed the resulting summary's correct parent link. This requires the larger initial response for artifacts that fit complete delivery. Stable identity does not imply a promise to cache analysis or avoid rerunning the parser on a fresh producer request. ### REA version 6.4.0 (main at 58300885207133422af235e0f2b10658cf958b03) ### Runtime and platform Node 26.10.0; macOS 27.0 arm64 ### Target and artifact context An unchanged managed PE/CLI DLL created inline with `buildManagedPeFixture({})`, inspected through the built-in managed static parser in one session with working Evidence retention. No native engine or execution of target code is required. ### Interface MCP ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence ```text sameNormalizedResult: true parentMatchesOmitted: false parentMatchesExplicit: false omittedMatchesRepeated: true matchingRecords: 3 One test failed at the final complete-versus-summary-parent source-ID equality assertion. ``` ### Typed outcome or recovery expected All successful delivery variants should identify one complete retained source observation. The summary should keep its separate view ID and link to that source through `normalized_result.parent_evidence_id`. Preserve the existing normalized metadata and coverage. A regression check should also ensure different caller-selected artifact paths remain distinct. These are restoration criteria; no repaired behavior was executed during verification. ### Analysis or suspected cause Source-derived reasoning at baseline `58300885207133422af235e0f2b10658cf958b03`: `src/server/registerManagedTools.ts:28` removes detail before provider execution, but its registration at `:72` supplies no `parametersForEvidence` projection. `src/server/registerEvidenceTools.ts:115` therefore retains the original caller parameters. Those parameters participate in the semantic ID at `src/domain/evidence.ts:246-276`, including `:260`. The observed complete records have parameters shaped as `{path, detail: "summary"}`, `{path}` and `{path, detail: "complete"}` despite equal normalized results. Existing coverage at `tests/boundary/mcp/managedArtifactSummaryMcp.test.ts:82` expects summary detail in the retained parent's parameters. Its later complete-delivery checks compare normalized payloads without asserting source-ID equality, so they do not detect this identity defect. ### Additional context Related but distinct from #3010, which requests retained delivery for managed member inspection, and #3134, which measures large retained-investigation paging; this report concerns delivery-dependent source IDs in the existing managed artifact inspection path. P2 reflects consequential duplicate discovery and source-ID fragmentation with accessible complete metadata and a demonstrated complete-first workaround. Related: #3216 reports a separate failure in the same area; the two have independent fixes.
bsakaguchi 2026-10-15 18:31:32 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 14, 2026):

Fixed on main by #3061 (748440160), which merged after this report's baseline (58300885).

Root cause: registerManagedTools removed detail only before calling the provider. createEvidence still recorded it in the retained parent's parameters, which feed the content-addressed evidence_id. So each delivery choice of the same inspection produced its own complete record.

Fix: managed tools now strip delivery detail through the shared withoutDeliveryDetail(SUMMARY_DELIVERY) owner, for both parametersForExecution and parametersForEvidence. That is the same owner inspect_macho uses, and it covers inspect_managed_artifact and inspect_managed_members.

Verification on main e5791229f: tests/boundary/mcp/managedArtifactSummaryMcp.test.ts (production MCP registration via the SDK client) asserts that the summary's parent has parameters { path }, and that omitted and detail: "complete" calls return the same evidence_id as the summary's parent_evidence_id. It passes (3/3). managedMemberSummaryMcp.test.ts asserts the same for inspect_managed_members. With one retained record per inspection, filtered bundle discovery no longer finds three.

Closing as fixed; please reopen if your reproduction still shows distinct IDs on current main.

<!-- gh-comment-id:6162419198 --> @kaoru0822-kitauji commented on GitHub (Oct 14, 2026): Fixed on main by #3061 (748440160), which merged after this report's baseline (`58300885`). **Root cause:** `registerManagedTools` removed `detail` only before calling the provider. `createEvidence` still recorded it in the retained parent's `parameters`, which feed the content-addressed `evidence_id`. So each delivery choice of the same inspection produced its own complete record. **Fix:** managed tools now strip delivery `detail` through the shared `withoutDeliveryDetail(SUMMARY_DELIVERY)` owner, for both `parametersForExecution` and `parametersForEvidence`. That is the same owner `inspect_macho` uses, and it covers `inspect_managed_artifact` and `inspect_managed_members`. **Verification on main `e5791229f`:** `tests/boundary/mcp/managedArtifactSummaryMcp.test.ts` (production MCP registration via the SDK client) asserts that the summary's parent has `parameters` `{ path }`, and that omitted and `detail: "complete"` calls return the **same `evidence_id`** as the summary's `parent_evidence_id`. It passes (3/3). `managedMemberSummaryMcp.test.ts` asserts the same for `inspect_managed_members`. With one retained record per inspection, filtered bundle discovery no longer finds three. Closing as fixed; please reopen if your reproduction still shows distinct IDs on current 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#1151
No description provided.