From 83e8cf7c532338cf9baa2baa6b5ce1a0134454ee Mon Sep 17 00:00:00 2001 From: Gergo Magyar Date: Mon, 3 Aug 2026 12:45:19 +0000 Subject: [PATCH] test(mcp): guard MCP startup against the group extractor closure returning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sibling forbidden-pattern case in the #2802 startup guard, reusing the concurrent probes it already collects — no new spawn, no new harness. Asserts that none of `dist/mcp/server.js`, `dist/cli/mcp.js`, or `dist/mcp/local/local-backend.js` loads a `core/group/extractors/` module or the native `tree-sitter` package. The parser is matched by package prefix rather than a bare substring, so a source file that merely mentions the word can neither satisfy nor trip it. Verified load-bearing rather than assumed: restoring the static `import { syncGroup }` in `core/group/service.ts` and rebuilding turns `dist/mcp/server.js` red and names all seven offenders — http-route, grpc, thrift, topic, include, manifest and workspace extractors. Reverted and re-confirmed green. Refs #2802 Co-Authored-By: Claude Opus 5 (1M context) --- .../mcp/startup-language-closure.test.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/gitnexus/test/integration/mcp/startup-language-closure.test.ts b/gitnexus/test/integration/mcp/startup-language-closure.test.ts index 93e373b7f..d8aa33b1e 100644 --- a/gitnexus/test/integration/mcp/startup-language-closure.test.ts +++ b/gitnexus/test/integration/mcp/startup-language-closure.test.ts @@ -61,6 +61,22 @@ import { /** Modules under this directory are the analyze-only provider registry. */ const FORBIDDEN_RE = /(^|\/)core\/ingestion\/languages\//; +/** + * The group contract extractors, and the native parser binding they reach. + * + * Same defect class as #2802, found immediately after it: `core/group/service.ts` + * statically imported `./sync.js`, which pulls all six contract extractors, five + * of which statically import `tree-sitter`. Only `group_sync` ever needs them — + * the other seven group tools do not — so a static import put the whole parser + * stack on every MCP server start. Measured cost of that one edge on a native + * filesystem: `dist/mcp/server.js` 521 ms -> 133 ms, `local-backend.js` 453 ms + * -> 66 ms. + * + * Matching the parser by its package prefix rather than a bare substring so a + * source file that merely mentions the word cannot satisfy or trip this. + */ +const FORBIDDEN_GROUP_RE = /(^|\/)core\/group\/extractors\/|(^|\/)node_modules\/tree-sitter/; + // Observed on Node 22.18 against a clean build: server.js loads 489 distinct // modules, local-backend.js 265, cli/mcp.js 4. The floors sit well below those // so normal dependency churn doesn't trip them, while a probe that silently @@ -108,4 +124,21 @@ describe('MCP startup module-load closure (#2802)', () => { ).toEqual([]); }, ); + + it.each(ENTRIES.map((request) => request.entry))( + 'importing dist/%s loads no group contract extractor or native parser', + (entry) => { + const probe = probes.get(entry); + const offenders = probe.matching(FORBIDDEN_GROUP_RE); + + expect( + offenders, + `${probe.label} eagerly loads the group contract extractors and/or the ` + + `native tree-sitter binding. Only \`group_sync\` needs them, and MCP ` + + `startup never syncs — keep \`core/group/sync.js\` behind the lazy ` + + `\`await import(...)\` in \`GroupService.groupSync\`. ` + + `Offending modules:\n${offenders.join('\n')}`, + ).toEqual([]); + }, + ); });