[PR #3061] [MERGED] perf(managed): add retained member summary and pages #5584

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/3061
Author: @kaoru0822-kitauji
Created: 10/14/2026
Status: ✅ Merged
Merged: 10/14/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: perf/managed-member-summary


📝 Commits (6)

  • 54e89bd perf(managed): add retained member summary and pages
  • 7c6368f perf(managed): validate a sealed member parent once across pages
  • 1f4ee2d Merge origin/main into perf/managed-member-summary
  • 935b622 fix(managed): keep delivery detail out of retained Evidence identity
  • 4558f2b Merge remote-tracking branch 'origin/main' into perf/managed-member-summary
  • b4849e8 Merge remote-tracking branch 'origin/main' into perf/managed-member-summary

📊 Changes

12 files changed (+672 additions, -63 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md (+8 -0)
📝 docs/managed-code-analysis.md (+34 -0)
📝 src/application/analysisView/AnalysisViewService.ts (+3 -0)
📝 src/contracts/analysisViewToolContracts.ts (+1 -1)
📝 src/contracts/managed/managedToolContracts.ts (+11 -3)
📝 src/contracts/toolOutputSchemaGroups.ts (+7 -1)
📝 src/domain/analysisView/analysisView.ts (+33 -1)
📝 src/domain/analysisView/managedArtifactView.ts (+19 -11)
➕ src/domain/analysisView/managedMemberView.ts (+284 -0)
📝 src/server/registerManagedTools.ts (+17 -29)
📝 tests/boundary/mcp/managedArtifactSummaryMcp.test.ts (+18 -17)
➕ tests/boundary/mcp/managedMemberSummaryMcp.test.ts (+237 -0)

📄 Description

Summary

Closes #3010.

inspect_managed_members always returns the assembly-wide member_refs and exported_types, and type_names doesn't narrow them. So one selected type, or a name that matches nothing, still returns megabytes. Over MCP, an ordinary assembly with no selection fails outright with resource_constraint.

This adds the same opt-in delivery that inspect_managed_artifact got in #2413:

  • detail: "summary" retains the complete Evidence and returns a linked inspect_analysis_view summary. It contains:
    • identity and metadata status
    • the selection, with its unmatched names
    • coverage/issues and the separate user-string coverage
    • limitations
    • per-collection counts
  • kind: "managed-members" pages cover types, exported_types, fields, methods, member_refs, call_edges, field_accesses and user_strings (the literals). They return original rows in order. Each page also carries the source selection, coverage and build-local token scope.
  • Older Evidence that never examined exported types or user strings reports a null count and refuses those pages instead of returning an empty one.
  • Defaults are unchanged: an omitted or complete detail returns the same complete result as before.

Bug found while testing against real data

My first version used z.union over the row array schemas, like the artifact view does. Sealing re-parses the page through that union, and the first schema that matches wins. The member row schemas are non-strict and share keys, so method rows parsed as types rows and silently lost rva, impl_flags and body. Each page now has the shape {collection, rows} in a discriminatedUnion("collection"). The boundary test rebuilds every collection page by page and compares it exactly. (The existing artifact view's reference/resource/attribute rows each have distinct required keys, and its pages are exact on both assemblies below, so it is left unchanged.)

  • The token-scope, missing-subject and unknown-MVID unknowns now have a single owner, managedViewIdentityUnknowns, shared by both managed views.
  • After merging main, the member view parses its parent with main's parseAnalysisViewResult, and managed registration uses main's retainedSummaryDelivery instead of its own preflight and projection hooks. Summary is now refused before admission, like the artifact and native families.
  • tests/boundary/mcp/browserComparisonInput.test.ts fails locally on plain origin/main (bcc141549) as well; it's unrelated and untouched.

Verification

Real data: two .NET assemblies from NuGet, parsed and never loaded or run.

  • ICSharpCode.Decompiler.dll 9.1.0.7988 (netstandard2.0, 2,558,464 bytes)
  • Newtonsoft.Json.dll 13.0.3 (net45 and net6.0)

Calls went over real MCP stdio (rea mcp) on macOS with Node 26.

Correctness: every collection was rebuilt through pages and compared exactly (isDeepStrictEqual) with the complete result. The summary's non-collection fields matched exactly.

Case Counts (types/fields/methods/member_refs/call_edges/field_accesses/user_strings) Pages rebuilt exactly
Decompiler, type_names=[CSharpDecompiler] 1 / 12 / 81 / 4,967 / 1,464 / 341 / 50 all 8 collections
Decompiler, unmatched No.Such.Type 0 / 0 / 0 / 4,967 / 0 / 0 / 0; unmatched_type_names reported all 8
Newtonsoft net45, type_names=[JsonConvert] 1 / 8 / 72 / 2,125 / 195 / 15 / 16 all 8
Newtonsoft net6.0, no selection (compared with CLI complete JSON; MCP complete can't deliver it) 494 / 2,204 / 4,208 / 2,149 / 19,137 / 9,901 / 784 all 8 (38,877 rows)

Size and time: before = merge base 30128a03c; after = this branch. Same machine, three runs each; the runs were identical to within a few ms. Token counts are tokenx estimates of the serialized replies, not model usage.

Analyst question Before After
Is CSharpDecompiler present and which direct calls does it declare? 1 call, 6,750,791 B (~2.30M est. tokens), 230 ms summary + 3 call_edges pages of 500 = 4 calls, 904,558 B (~293k est. tokens), 211 ms
Does No.Such.Type exist? 4,890,827 B 15,441 B summary
What does Newtonsoft.Json (net6.0, no selection) declare? resource_constraint error, no result 13,495 B summary; pages on demand

For the first question, the whole client+server process ran two passes of the task (first call in a fresh server, then a repeat in the same session):

  • Wall time: 1.24 s → 0.90 s
  • User CPU: 1.73 s → 1.26 s
  • Peak RSS of one process: 651 → 396 MiB

The repeat pass is only about 10 ms faster in both builds, since the parser doesn't cache across calls.

Paging cost (after review): a sealed retained parent is validated once and its report is reused for later pages; unsealed inline input is still validated on every call. Paging all 19,137 Newtonsoft (net6.0) call edges over MCP, 39 pages of 500, two runs each:

Page time (sum) Process wall User CPU Peak RSS (one process)
54e89bd (parse every page) 1.74 s 3.16 s 4.18 s 715 MiB
7c6368f (validate once) 0.19 s 1.57 s 2.05 s 421 MiB

Response bytes are identical (10,667,479). In process, one 64-row page drops from ~36 ms to 0.6–2.2 ms.

Costs: the advertised catalog grows by 20,098 B (about 6k estimated tokens). inspect_analysis_view gains 16 KB for the eight row schemas and inspect_managed_members gains 4 KB. Summary-first only pays off when a reply would otherwise be large. I did not run real-model (Codex) trials, so I'm not claiming any model-token saving.

Tests:

  • New tests/boundary/mcp/managedMemberSummaryMcp.test.ts (fixture with an exported type, a #US literal, a field access and a call edge; every collection must be non-empty) covers:
    • Ajv validation of the SDK-converted input/output schemas
    • selected and unmatched names
    • unchanged complete and omitted delivery
    • rebuilding every collection page by page after the file is deleted
    • unchanged retained parent and portable inline view
    • historical Evidence with unexamined collections refusing pages
  • managedArtifactSummaryMcp.test.ts now:
    • accepts detail on members (native boundaries still reject it)
    • checks that a server without retention refuses member summary before file access
  • Passing: npm run check:fast, npm run check:changed (58 files), npm run docs:check, npm run jscpd (3.14% < 3.5%), and npm run test:focused on 66 MCP catalog/schema/analysis-view suites plus the managed boundary/acceptance suites.

Not verified: Windows and the issue's exact ILSpy.dll, real-model trials, and the CLI. The CLI has no MCP response budget, so like #2413 this delivery option is MCP-only; inspect-analysis-view accepts the new view kind through its shared schema.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/3061 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/14/2026 **Status:** ✅ Merged **Merged:** 10/14/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `perf/managed-member-summary` --- ### 📝 Commits (6) - [`54e89bd`](https://github.com/morluto/rea/commit/54e89bd5c842e23f00372aca80b0dde242bbc4e9) perf(managed): add retained member summary and pages - [`7c6368f`](https://github.com/morluto/rea/commit/7c6368f932eed9324296c1cbf799871f7f61e6e8) perf(managed): validate a sealed member parent once across pages - [`1f4ee2d`](https://github.com/morluto/rea/commit/1f4ee2dcda85bea83928921c3d5374a5a34b96c9) Merge origin/main into perf/managed-member-summary - [`935b622`](https://github.com/morluto/rea/commit/935b62249b9268ba380a062a0bf03cd0ab83a437) fix(managed): keep delivery detail out of retained Evidence identity - [`4558f2b`](https://github.com/morluto/rea/commit/4558f2b5f38d5cc6ae01d2b76a571c37d3ea1045) Merge remote-tracking branch 'origin/main' into perf/managed-member-summary - [`b4849e8`](https://github.com/morluto/rea/commit/b4849e839d18cf3cc2aeeea17b44d6539361f97f) Merge remote-tracking branch 'origin/main' into perf/managed-member-summary ### 📊 Changes **12 files changed** (+672 additions, -63 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md` (+8 -0) 📝 `docs/managed-code-analysis.md` (+34 -0) 📝 `src/application/analysisView/AnalysisViewService.ts` (+3 -0) 📝 `src/contracts/analysisViewToolContracts.ts` (+1 -1) 📝 `src/contracts/managed/managedToolContracts.ts` (+11 -3) 📝 `src/contracts/toolOutputSchemaGroups.ts` (+7 -1) 📝 `src/domain/analysisView/analysisView.ts` (+33 -1) 📝 `src/domain/analysisView/managedArtifactView.ts` (+19 -11) ➕ `src/domain/analysisView/managedMemberView.ts` (+284 -0) 📝 `src/server/registerManagedTools.ts` (+17 -29) 📝 `tests/boundary/mcp/managedArtifactSummaryMcp.test.ts` (+18 -17) ➕ `tests/boundary/mcp/managedMemberSummaryMcp.test.ts` (+237 -0) </details> ### 📄 Description ## Summary Closes #3010. `inspect_managed_members` always returns the assembly-wide `member_refs` and `exported_types`, and `type_names` doesn't narrow them. So one selected type, or a name that matches nothing, still returns megabytes. Over MCP, an ordinary assembly with no selection fails outright with `resource_constraint`. This adds the same opt-in delivery that `inspect_managed_artifact` got in #2413: - **`detail: "summary"`** retains the complete Evidence and returns a linked `inspect_analysis_view` summary. It contains: - identity and metadata status - the selection, with its unmatched names - coverage/issues and the separate user-string coverage - limitations - per-collection counts - **`kind: "managed-members"` pages** cover `types`, `exported_types`, `fields`, `methods`, `member_refs`, `call_edges`, `field_accesses` and `user_strings` (the literals). They return original rows in order. Each page also carries the source selection, coverage and build-local token scope. - **Older Evidence** that never examined exported types or user strings reports a `null` count and refuses those pages instead of returning an empty one. - **Defaults are unchanged:** an omitted or `complete` detail returns the same complete result as before. ### Bug found while testing against real data My first version used `z.union` over the row array schemas, like the artifact view does. Sealing re-parses the page through that union, and the first schema that matches wins. The member row schemas are non-strict and share keys, so method rows parsed as `types` rows and silently lost `rva`, `impl_flags` and `body`. Each page now has the shape `{collection, rows}` in a `discriminatedUnion("collection")`. The boundary test rebuilds every collection page by page and compares it exactly. (The existing artifact view's reference/resource/attribute rows each have distinct required keys, and its pages are exact on both assemblies below, so it is left unchanged.) ### Related cleanup - The token-scope, missing-subject and unknown-MVID unknowns now have a single owner, `managedViewIdentityUnknowns`, shared by both managed views. - After merging main, the member view parses its parent with main's `parseAnalysisViewResult`, and managed registration uses main's `retainedSummaryDelivery` instead of its own preflight and projection hooks. Summary is now refused before admission, like the artifact and native families. - `tests/boundary/mcp/browserComparisonInput.test.ts` fails locally on plain `origin/main` (bcc141549) as well; it's unrelated and untouched. ## Verification Real data: two .NET assemblies from NuGet, parsed and never loaded or run. - `ICSharpCode.Decompiler.dll` 9.1.0.7988 (netstandard2.0, 2,558,464 bytes) - `Newtonsoft.Json.dll` 13.0.3 (net45 and net6.0) Calls went over real MCP stdio (`rea mcp`) on macOS with Node 26. **Correctness:** every collection was rebuilt through pages and compared exactly (`isDeepStrictEqual`) with the complete result. The summary's non-collection fields matched exactly. | Case | Counts (types/fields/methods/member_refs/call_edges/field_accesses/user_strings) | Pages rebuilt exactly | | --- | --- | --- | | Decompiler, `type_names=[CSharpDecompiler]` | 1 / 12 / 81 / 4,967 / 1,464 / 341 / 50 | all 8 collections | | Decompiler, unmatched `No.Such.Type` | 0 / 0 / 0 / 4,967 / 0 / 0 / 0; `unmatched_type_names` reported | all 8 | | Newtonsoft net45, `type_names=[JsonConvert]` | 1 / 8 / 72 / 2,125 / 195 / 15 / 16 | all 8 | | Newtonsoft net6.0, no selection (compared with CLI complete JSON; MCP complete can't deliver it) | 494 / 2,204 / 4,208 / 2,149 / 19,137 / 9,901 / 784 | all 8 (38,877 rows) | **Size and time:** before = merge base `30128a03c`; after = this branch. Same machine, three runs each; the runs were identical to within a few ms. Token counts are `tokenx` estimates of the serialized replies, not model usage. | Analyst question | Before | After | | --- | --- | --- | | Is `CSharpDecompiler` present and which direct calls does it declare? | 1 call, 6,750,791 B (~2.30M est. tokens), 230 ms | summary + 3 `call_edges` pages of 500 = 4 calls, 904,558 B (~293k est. tokens), 211 ms | | Does `No.Such.Type` exist? | 4,890,827 B | 15,441 B summary | | What does Newtonsoft.Json (net6.0, no selection) declare? | `resource_constraint` error, no result | 13,495 B summary; pages on demand | For the first question, the whole client+server process ran two passes of the task (first call in a fresh server, then a repeat in the same session): - **Wall time:** 1.24 s → 0.90 s - **User CPU:** 1.73 s → 1.26 s - **Peak RSS of one process:** 651 → 396 MiB The repeat pass is only about 10 ms faster in both builds, since the parser doesn't cache across calls. **Paging cost (after review):** a sealed retained parent is validated once and its report is reused for later pages; unsealed inline input is still validated on every call. Paging all 19,137 Newtonsoft (net6.0) call edges over MCP, 39 pages of 500, two runs each: | | Page time (sum) | Process wall | User CPU | Peak RSS (one process) | | --- | ---: | ---: | ---: | ---: | | 54e89bd (parse every page) | 1.74 s | 3.16 s | 4.18 s | 715 MiB | | 7c6368f (validate once) | 0.19 s | 1.57 s | 2.05 s | 421 MiB | Response bytes are identical (10,667,479). In process, one 64-row page drops from ~36 ms to 0.6–2.2 ms. **Costs:** the advertised catalog grows by 20,098 B (about 6k estimated tokens). `inspect_analysis_view` gains 16 KB for the eight row schemas and `inspect_managed_members` gains 4 KB. Summary-first only pays off when a reply would otherwise be large. I did not run real-model (Codex) trials, so I'm not claiming any model-token saving. **Tests:** - New `tests/boundary/mcp/managedMemberSummaryMcp.test.ts` (fixture with an exported type, a `#US` literal, a field access and a call edge; every collection must be non-empty) covers: - Ajv validation of the SDK-converted input/output schemas - selected and unmatched names - unchanged `complete` and omitted delivery - rebuilding every collection page by page after the file is deleted - unchanged retained parent and portable inline view - historical Evidence with unexamined collections refusing pages - `managedArtifactSummaryMcp.test.ts` now: - accepts `detail` on members (native boundaries still reject it) - checks that a server without retention refuses member `summary` before file access - Passing: `npm run check:fast`, `npm run check:changed` (58 files), `npm run docs:check`, `npm run jscpd` (3.14% < 3.5%), and `npm run test:focused` on 66 MCP catalog/schema/analysis-view suites plus the managed boundary/acceptance suites. **Not verified:** Windows and the issue's exact ILSpy.dll, real-model trials, and the CLI. The CLI has no MCP response budget, so like #2413 this delivery option is MCP-only; `inspect-analysis-view` accepts the new view kind through its shared schema. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:07:45 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#5584
No description provided.