feat(mcp): trace relevance + closure-collection + god-file rendering + cold-start handshake (#580)

Trace endpoint relevance (overloaded names resolve to the real implementation instead of an empty protocol/delegate stub), Swift closure-collection synthesizer, multi-phase god-file explore rendering, and serve --mcp cold-start handshake sped ~811ms→~90ms (proxy answers initialize/tools-list locally). Full suite green (1090 pass).
This commit is contained in:
Colby Mchenry
2026-05-31 18:41:41 -05:00
committed by GitHub
parent b026e64b41
commit 3a1ddf41cd
12 changed files with 756 additions and 92 deletions
+90
View File
@@ -47,6 +47,19 @@ const VUE_HANDLER_RE = /(?:@|v-on:)([a-zA-Z][\w-]*)(?:\.[\w]+)*\s*=\s*"([^"]+)"/
// Captures the destructure body + the called composable; only `use*` calls qualify.
const VUE_DESTRUCTURE_RE = /(?:const|let|var)\s*\{([^}]+)\}\s*=\s*(\w+)\s*\(/g;
// Closure-collection dynamic dispatch (language-agnostic, Swift-first). A method
// appends a closure to a collection property; another method iterates that
// property *invoking each element* (`coll.forEach { $0() }` / `{ it() }`). The
// element-invoke (`$0(` / `it(`) PROVES the collection holds closures, so pairing
// a dispatcher to same-named registrars (`.append`/`.add`/`.push`/`.insert`,
// incl. Swift `prop.write { $0.append }`) is high-precision. Cross-file/class by
// design: Alamofire appends in `DataRequest.validate` but iterates in the base
// `Request.didCompleteTask` — neither same-file nor same-class pairing reaches it.
const CC_DISPATCH_RE = /(\w+)\.forEach\s*\{\s*(?:\$0|it)\s*\(/g;
const CC_APPEND_WRITE_RE = /(\w+)\.write\s*\{\s*\$0(?:\.(\w+))?\.(?:append|add|push|insert)\s*\(/g;
const CC_APPEND_DIRECT_RE = /(\w+)\.(?:append|add|push|insert)\s*\(/g;
const CC_FANOUT_CAP = 8; // skip a field name with more dispatchers/registrars than this (too generic to pair confidently)
function kebabToPascal(s: string): string {
return s.split('-').map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join('');
}
@@ -143,6 +156,81 @@ function fieldChannelEdges(queries: QueryBuilder, ctx: ResolutionContext): Edge[
return edges;
}
/**
* Closure-collection dispatch: dispatcher iterates a closure-collection property
* invoking each element; registrar appends a closure to the same-named property.
* Emits dispatcher → registrar so a flow reaches the registration site (where the
* appended closure's body — and its callers — live). High-precision: the
* dispatcher's element-invoke is the gate (a `.forEach` that does NOT invoke its
* element is ignored), so a repo with no closure-collection dispatch yields zero
* edges regardless of how many `.append`/`.push` sites it has.
*
* Pairs globally by field name (cross-file/class is required — see Alamofire's
* base-class `Request.didCompleteTask` iterating `validators` appended by the
* subclass `DataRequest.validate`), bounded by a fan-out cap so a generic field
* name shared across unrelated classes can't fan out into noise.
*/
function closureCollectionEdges(queries: QueryBuilder, ctx: ResolutionContext): Edge[] {
const candidates = [...queries.getNodesByKind('method'), ...queries.getNodesByKind('function')];
const dispatchers = new Map<string, Array<{ node: Node; line: number }>>(); // field → dispatcher methods + forEach line
const registrars = new Map<string, Array<{ node: Node; line: number }>>(); // field → registrar methods + append line
const addReg = (field: string | undefined, node: Node, absLine: number) => {
if (!field || /^\d+$/.test(field)) return; // `$0.append` mis-captures the `0`; the write-RE owns that field
const arr = registrars.get(field) ?? [];
if (!arr.some((r) => r.node.id === node.id)) arr.push({ node, line: absLine });
registrars.set(field, arr);
};
for (const m of candidates) {
const content = ctx.readFile(m.filePath);
const src = content && sliceLines(content, m.startLine, m.endLine);
if (!src) continue;
const hasForEach = src.includes('.forEach');
const hasAppend = src.includes('.append(') || src.includes('.add(') || src.includes('.push(') || src.includes('.insert(');
if (!hasForEach && !hasAppend) continue;
const lineAt = (idx: number) => (m.startLine ?? 1) + src.slice(0, idx).split('\n').length - 1;
if (hasForEach) {
CC_DISPATCH_RE.lastIndex = 0;
let d: RegExpExecArray | null;
while ((d = CC_DISPATCH_RE.exec(src))) {
const arr = dispatchers.get(d[1]!) ?? [];
if (!arr.some((n) => n.node.id === m.id)) arr.push({ node: m, line: lineAt(d.index) });
dispatchers.set(d[1]!, arr);
}
}
if (hasAppend) {
CC_APPEND_WRITE_RE.lastIndex = 0;
let w: RegExpExecArray | null;
while ((w = CC_APPEND_WRITE_RE.exec(src))) addReg(w[2] || w[1], m, lineAt(w.index)); // nested `$0.streams` else the `.write` receiver
CC_APPEND_DIRECT_RE.lastIndex = 0;
let a: RegExpExecArray | null;
while ((a = CC_APPEND_DIRECT_RE.exec(src))) addReg(a[1], m, lineAt(a.index));
}
}
const edges: Edge[] = [];
const seen = new Set<string>();
for (const [field, disps] of dispatchers) {
const regs = registrars.get(field);
if (!regs || regs.length === 0) continue;
if (disps.length > CC_FANOUT_CAP || regs.length > CC_FANOUT_CAP) continue; // generic field — can't pair confidently
for (const disp of disps) for (const reg of regs) {
if (disp.node.id === reg.node.id) continue;
const key = `${disp.node.id}>${reg.node.id}`;
if (seen.has(key)) continue;
seen.add(key);
edges.push({
source: disp.node.id, target: reg.node.id, kind: 'calls', line: disp.line,
provenance: 'heuristic',
metadata: { synthesizedBy: 'closure-collection', field, registeredAt: `${reg.node.filePath}:${reg.line}` },
});
}
}
return edges;
}
/** Phase 2: string-keyed EventEmitter channels (on('e', fn) ↔ emit('e')). */
function eventEmitterEdges(ctx: ResolutionContext): Edge[] {
const emitsByEvent = new Map<string, Set<string>>(); // event → dispatcher node ids
@@ -1093,6 +1181,7 @@ function ginMiddlewareChainEdges(queries: QueryBuilder, ctx: ResolutionContext):
*/
export function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionContext): number {
const fieldEdges = fieldChannelEdges(queries, ctx);
const closureCollEdges = closureCollectionEdges(queries, ctx);
const emitterEdges = eventEmitterEdges(ctx);
const renderEdges = reactRenderEdges(queries, ctx);
const jsxEdges = reactJsxChildEdges(ctx);
@@ -1110,6 +1199,7 @@ export function synthesizeCallbackEdges(queries: QueryBuilder, ctx: ResolutionCo
const seen = new Set<string>();
for (const e of [
...fieldEdges,
...closureCollEdges,
...emitterEdges,
...renderEdges,
...jsxEdges,