[PR #4424] [MERGED] perf(mcp): defer analysis runtime after catalog discovery #6232

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/4424
Author: @leeguooooo
Created: 10/16/2026
Status: ✅ Merged
Merged: 10/22/2026
Merged by: @gensokyo-dev

Base: main ← Head: perf/mcp-deferred-runtime


📝 Commits (10+)

  • e3d367f perf(mcp): 按需初始化分析运行时以降低空闲内存
  • f692ace fix(mcp): 保留连接身份并修复目录构建清理
  • 57cbe1d fix(mcp): 保留启动失败后的清理入口和原始错误
  • c8d8ba7 Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime
  • e2d44b7 Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime
  • f4e02d3 Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime
  • 62f60ba fix(mcp): 按 HTTP 传输后的 serve 句柄类型等待延迟运行时启动
  • d6de5a0 Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime
  • 1c7f7da Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime
  • 73ab8b9 fix(mcp): 修复延迟启动与关闭重试的资源归属

📊 Changes

32 files changed (+9739 additions, -1095 deletions)

View changed files

📝 docs/testing.md (+12 -0)
📝 experiments/analysis-efficiency/managed-cli-runtime.mjs (+29 -10)
📝 experiments/analysis-efficiency/mcp-input-catalog.md (+5 -0)
📝 experiments/analysis-efficiency/mcp-input-catalog.mjs (+196 -78)
➕ experiments/analysis-efficiency/mcp-startup-costs.py (+118 -0)
➕ experiments/analysis-efficiency/mcp-startup.json (+6172 -0)
➕ experiments/analysis-efficiency/mcp-startup.md (+61 -0)
📝 package.json (+2 -2)
📝 scripts/lib/development-tests.mjs (+1 -0)
➕ scripts/record-mcp-catalog.mjs (+119 -0)
📝 src/config/mcpInputSchemaProfile.ts (+6 -1)
📝 src/main.ts (+20 -46)
➕ src/main/OwnedMcpServers.ts (+26 -0)
➕ src/main/deferredRuntime.ts (+243 -0)
➕ src/main/eagerRuntime.ts (+70 -0)
📝 src/main/shutdown.ts (+18 -9)
📝 src/main/transport.ts (+23 -30)
📝 src/main/types.ts (+3 -0)
➕ src/recordedMcpCatalog.ts (+27 -0)
➕ src/server/DeferredEvidenceMcpServer.test.ts (+343 -0)

...and 12 more files

📄 Description

Summary

Target-free stdio discovery currently constructs analysis validators and provider facades before the client can inspect the directory. Serve build-recorded compact/full input advertisements, then materialize canonical handlers and parsers on that same SDK server at first use. Each artifact has its own SHA-256 and named profile; build inputs include the recorder. Explicit input choices keep identical advertisements before/after materialization. HTTP, output-schema-off, initial-target and unrecorded source runs retain eager composition.

A shared OwnedMcpServers owner seals factory publication before its cleanup snapshot in both eager and deferred paths. Startup still joins servers published before an async serving handle settles. Failed exact close capabilities remain available for retry. The moved transport/delivery code preserves current complete-error retention, escaped-preview fitting, response/string-limit advice, target-content provenance and native Evidence continuation. Analysis workflows and tool contracts stay shared.

Real completed tasks

Official Alpine ripgrep 14.1.1-r0 and Zellij 0.40.0-r0 executables, verified against an independent ELF/section/zlib/JSON oracle: 42/422 packages and 88/1,063 edges respectively. The actual SDK task selects serde, repeats retained reads, tests a canonical refusal, exports/verifies complete Evidence and confirms unchanged advertisements. All 72 paired trials and four priming runs pass with identical answers, complete parent IDs/hashes and directories. Package kind null remains unknown.

Three alternating pairs per condition, pinned main@c465ff5a8; default compact medians below (baseline → candidate):

Task Cache Wall s Family CPU s Family peak MiB
Directory cold 5.661 → 2.236 5.403 → 2.050 559.3 → 248.6
Directory warm 5.056 → 2.236 4.800 → 2.015 572.8 → 251.4
ripgrep cold 8.177 → 8.078 7.945 → 7.899 849.7 → 792.5
zellij cold 8.276 → 7.276 8.041 → 7.105 846.2 → 819.0
ripgrep warm 7.976 → 6.879 7.739 → 6.678 826.8 → 802.6
zellij warm 8.285 → 7.472 8.019 → 7.254 882.1 → 811.4

Discovery improves clearly; completed-task results depend on workload (cold default ripgrep is approximately unchanged). Its first analysis RPC pays deferred initialization: cold 232 → 2,532 ms, warm 227 → 2,118 ms. Each profile's directory remains byte-identical; input-catalog estimates remain 49,670/205,744 tokens for compact/full. Actual model usage is unmeasured.

Serial Linux x64, Node 24.18.0/npm 11.16.0, 1 CPU, 4 GiB hard/3 GiB early stop, no swap. Whole-family costs include pre/post source/all-compiled-JS/JSON binding, SDK validation, extra calls and oracle checking; kernel peak includes file cache. Cold disables server compile cache; warm primes a role/profile-specific cache. OS page cache and hit rate are unmeasured; audits warm compiled-file pages. No OOM, resource stop or leftovers.

Full profiles, ranges, scope and reproduction · All trials and input/source hashes.

Validation

  • 125 focused tests across 14 files pass: legacy/auto negotiation, profile identity, canonical invalid input/output, huge/error results, prompts/completion cancellation, partial startup, exact cleanup retry and real compiled HTTP session/replacement/Host behavior. A repeated 22-case schema/profile subset passes after updating both runtimes to the pinned main.
  • Both eager and compiled deferred late-publication regressions fail with only the shared producing guard removed, then pass restored. Runtime admission rejects a mismatched compiled graph before MCP launch; aggregation rejects a tampered trial binding.
  • Accounting now keeps catalog/producer/guard digests distinct and checks each trial hash against its catalog receipt and profile identity. All 72 reports regenerate with unchanged timing/CPU/memory/context/parent values; reintroducing the wrong digest selection is rejected.
  • Both builds, all six static-check tasks plus architecture guards, six documentation checks and five README tests pass. Final runtime source matches the measured candidate; subsequent changes record evidence/accounting.
  • Integration with main@ce5bc8cb0 builds and passes all six static checks plus architecture guards. Actual Zellij SDK tasks in default/full profiles preserve the independent oracle answer, complete parent identity/hash and byte-identical catalogs; these two checks are not another paired timing cohort.
  • Additional real Rizin 0.9.1 SDK smoke passes the deferred first-use path, ELF/entry oracle, byte-identical catalogs and owned cleanup on Debian /usr/bin/true. The same eager flow passes. These single qualifications include complete resource costs and are not another performance cohort.
  • Real-provider scope is static Rust metadata from these public artifacts plus that small-target Rizin debug lifecycle. Real initial-target providers, other provider workflows, foreign OS runtime behavior, retained V8 heap and actual model sessions remain unverified. Current-head Greptile findings are addressed; no unresolved review threads remain. Codex review could not complete because the repository reached its review usage limit. CI: all eight portable shards and the portable aggregate pass, as do static/docs/contracts/build lanes. Windows attempt 1 times out after correct CLI output in webModuleCli.test.ts (15 s); attempt 2 passes that test in 2.1 s but times out in the CLI-help test (30 s), which passed attempt 1. Both full affected files pass on Linux on clean main and candidate (1 + 28 cases). After normalizing only generated chunk import names, the 138-module CLI graphs differ solely in the equivalent, side-effect-free MCP profile-default helper. No CLI implementation or timeout was changed. Windows lane and its required aggregate are not green; integration uses the user-authorized merge exception for these timing failures, with no claim of a clean Windows run. AI assistance: Codex.

Author readiness

Human owner: @leeguooooo

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

🔄 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/4424 **Author:** [@leeguooooo](https://github.com/leeguooooo) **Created:** 10/16/2026 **Status:** ✅ Merged **Merged:** 10/22/2026 **Merged by:** [@gensokyo-dev](https://github.com/gensokyo-dev) **Base:** `main` ← **Head:** `perf/mcp-deferred-runtime` --- ### 📝 Commits (10+) - [`e3d367f`](https://github.com/morluto/rea/commit/e3d367fd833268e8fadf902db267f1249509d9ff) perf(mcp): 按需初始化分析运行时以降低空闲内存 - [`f692ace`](https://github.com/morluto/rea/commit/f692ace7e173ea3afbcbd081fed0627fceab6808) fix(mcp): 保留连接身份并修复目录构建清理 - [`57cbe1d`](https://github.com/morluto/rea/commit/57cbe1d0d2fc4422c0843d263f0ea7cc6d0383e8) fix(mcp): 保留启动失败后的清理入口和原始错误 - [`c8d8ba7`](https://github.com/morluto/rea/commit/c8d8ba75aade250c054ebe87308bcffd7c44a295) Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime - [`e2d44b7`](https://github.com/morluto/rea/commit/e2d44b769f7a048756666b2398730a93608b6bd4) Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime - [`f4e02d3`](https://github.com/morluto/rea/commit/f4e02d37bf67d33e013631adaee6f0cee56bf055) Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime - [`62f60ba`](https://github.com/morluto/rea/commit/62f60ba4a7848efa32474be68010508a6392760d) fix(mcp): 按 HTTP 传输后的 serve 句柄类型等待延迟运行时启动 - [`d6de5a0`](https://github.com/morluto/rea/commit/d6de5a0b23062c54d22bfcfae0322afd8a13273e) Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime - [`1c7f7da`](https://github.com/morluto/rea/commit/1c7f7daaeac943f8eaafbb20f6c81e4e6e90bab3) Merge remote-tracking branch 'origin/main' into perf/mcp-deferred-runtime - [`73ab8b9`](https://github.com/morluto/rea/commit/73ab8b9b8f38ef62c886e11a140c828976d5d7a2) fix(mcp): 修复延迟启动与关闭重试的资源归属 ### 📊 Changes **32 files changed** (+9739 additions, -1095 deletions) <details> <summary>View changed files</summary> 📝 `docs/testing.md` (+12 -0) 📝 `experiments/analysis-efficiency/managed-cli-runtime.mjs` (+29 -10) 📝 `experiments/analysis-efficiency/mcp-input-catalog.md` (+5 -0) 📝 `experiments/analysis-efficiency/mcp-input-catalog.mjs` (+196 -78) ➕ `experiments/analysis-efficiency/mcp-startup-costs.py` (+118 -0) ➕ `experiments/analysis-efficiency/mcp-startup.json` (+6172 -0) ➕ `experiments/analysis-efficiency/mcp-startup.md` (+61 -0) 📝 `package.json` (+2 -2) 📝 `scripts/lib/development-tests.mjs` (+1 -0) ➕ `scripts/record-mcp-catalog.mjs` (+119 -0) 📝 `src/config/mcpInputSchemaProfile.ts` (+6 -1) 📝 `src/main.ts` (+20 -46) ➕ `src/main/OwnedMcpServers.ts` (+26 -0) ➕ `src/main/deferredRuntime.ts` (+243 -0) ➕ `src/main/eagerRuntime.ts` (+70 -0) 📝 `src/main/shutdown.ts` (+18 -9) 📝 `src/main/transport.ts` (+23 -30) 📝 `src/main/types.ts` (+3 -0) ➕ `src/recordedMcpCatalog.ts` (+27 -0) ➕ `src/server/DeferredEvidenceMcpServer.test.ts` (+343 -0) _...and 12 more files_ </details> ### 📄 Description ## Summary Target-free stdio discovery currently constructs analysis validators and provider facades before the client can inspect the directory. Serve build-recorded compact/full input advertisements, then materialize canonical handlers and parsers on that same SDK server at first use. Each artifact has its own SHA-256 and named profile; build inputs include the recorder. Explicit input choices keep identical advertisements before/after materialization. HTTP, output-schema-off, initial-target and unrecorded source runs retain eager composition. A shared `OwnedMcpServers` owner seals factory publication before its cleanup snapshot in both eager and deferred paths. Startup still joins servers published before an async serving handle settles. Failed exact close capabilities remain available for retry. The moved transport/delivery code preserves current complete-error retention, escaped-preview fitting, response/string-limit advice, target-content provenance and native Evidence continuation. Analysis workflows and tool contracts stay shared. ## Real completed tasks Official Alpine ripgrep 14.1.1-r0 and Zellij 0.40.0-r0 executables, verified against an independent ELF/section/zlib/JSON oracle: 42/422 packages and 88/1,063 edges respectively. The actual SDK task selects serde, repeats retained reads, tests a canonical refusal, exports/verifies complete Evidence and confirms unchanged advertisements. All 72 paired trials and four priming runs pass with identical answers, complete parent IDs/hashes and directories. Package kind `null` remains unknown. Three alternating pairs per condition, pinned `main@c465ff5a8`; default compact medians below (baseline → candidate): | Task | Cache | Wall s | Family CPU s | Family peak MiB | | --- | --- | ---: | ---: | ---: | | Directory | cold | 5.661 → 2.236 | 5.403 → 2.050 | 559.3 → 248.6 | | Directory | warm | 5.056 → 2.236 | 4.800 → 2.015 | 572.8 → 251.4 | | ripgrep | cold | 8.177 → 8.078 | 7.945 → 7.899 | 849.7 → 792.5 | | zellij | cold | 8.276 → 7.276 | 8.041 → 7.105 | 846.2 → 819.0 | | ripgrep | warm | 7.976 → 6.879 | 7.739 → 6.678 | 826.8 → 802.6 | | zellij | warm | 8.285 → 7.472 | 8.019 → 7.254 | 882.1 → 811.4 | Discovery improves clearly; completed-task results depend on workload (cold default ripgrep is approximately unchanged). Its first analysis RPC pays deferred initialization: cold 232 → 2,532 ms, warm 227 → 2,118 ms. Each profile's directory remains byte-identical; input-catalog estimates remain 49,670/205,744 tokens for compact/full. Actual model usage is unmeasured. Serial Linux x64, Node 24.18.0/npm 11.16.0, 1 CPU, 4 GiB hard/3 GiB early stop, no swap. Whole-family costs include pre/post source/all-compiled-JS/JSON binding, SDK validation, extra calls and oracle checking; kernel peak includes file cache. Cold disables server compile cache; warm primes a role/profile-specific cache. OS page cache and hit rate are unmeasured; audits warm compiled-file pages. No OOM, resource stop or leftovers. [Full profiles, ranges, scope and reproduction](https://github.com/leeguooooo/rea/blob/499f55476f810f3ef1cfe127a0673f556ecd5136/experiments/analysis-efficiency/mcp-startup.md) · [All trials and input/source hashes](https://github.com/leeguooooo/rea/blob/499f55476f810f3ef1cfe127a0673f556ecd5136/experiments/analysis-efficiency/mcp-startup.json). ## Validation - 125 focused tests across 14 files pass: legacy/auto negotiation, profile identity, canonical invalid input/output, huge/error results, prompts/completion cancellation, partial startup, exact cleanup retry and real compiled HTTP session/replacement/Host behavior. A repeated 22-case schema/profile subset passes after updating both runtimes to the pinned main. - Both eager and compiled deferred late-publication regressions fail with only the shared producing guard removed, then pass restored. Runtime admission rejects a mismatched compiled graph before MCP launch; aggregation rejects a tampered trial binding. - Accounting now keeps catalog/producer/guard digests distinct and checks each trial hash against its catalog receipt and profile identity. All 72 reports regenerate with unchanged timing/CPU/memory/context/parent values; reintroducing the wrong digest selection is rejected. - Both builds, all six static-check tasks plus architecture guards, six documentation checks and five README tests pass. Final runtime source matches the measured candidate; subsequent changes record evidence/accounting. - Integration with `main@ce5bc8cb0` builds and passes all six static checks plus architecture guards. Actual Zellij SDK tasks in default/full profiles preserve the independent oracle answer, complete parent identity/hash and byte-identical catalogs; these two checks are not another paired timing cohort. - [Additional real Rizin 0.9.1 SDK smoke](https://github.com/morluto/rea/pull/4424#issuecomment-6331614600) passes the deferred first-use path, ELF/entry oracle, byte-identical catalogs and owned cleanup on Debian `/usr/bin/true`. The same eager flow passes. These single qualifications include complete resource costs and are not another performance cohort. - Real-provider scope is static Rust metadata from these public artifacts plus that small-target Rizin debug lifecycle. Real initial-target providers, other provider workflows, foreign OS runtime behavior, retained V8 heap and actual model sessions remain unverified. Current-head Greptile findings are addressed; no unresolved review threads remain. Codex review could not complete because the repository reached its review usage limit. CI: all eight portable shards and the portable aggregate pass, as do static/docs/contracts/build lanes. Windows attempt 1 times out after correct CLI output in `webModuleCli.test.ts` (15 s); attempt 2 passes that test in 2.1 s but times out in the CLI-help test (30 s), which passed attempt 1. Both full affected files pass on Linux on clean main and candidate (1 + 28 cases). After normalizing only generated chunk import names, the 138-module CLI graphs differ solely in the equivalent, side-effect-free MCP profile-default helper. No CLI implementation or timeout was changed. Windows lane and its required aggregate are not green; integration uses the user-authorized merge exception for these timing failures, with no claim of a clean Windows run. AI assistance: Codex. ## Author readiness Human owner: @leeguooooo - [x] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [x] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [x] I declare this PR ready for initial maintainer review. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 01:12:53 +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#6232
No description provided.