test(mcp): guard MCP startup against the group extractor closure returning

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) <noreply@anthropic.com>
This commit is contained in:
Gergo Magyar 2026-08-03 12:45:19 +00:00
parent 9ea9676dc1
commit 83e8cf7c53

View file

@ -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([]);
},
);
});