MCPcopy
hub / github.com/colbymchenry/codegraph / buildDynamicBoundaries

Method buildDynamicBoundaries

src/mcp/tools.ts:2119–2162  ·  view source on GitHub ↗

* Dynamic-boundary surfacing (#687): when the flow among the agent's named * symbols does not fully connect, scan the disconnected symbols' bodies for * dynamic-dispatch sites (computed member calls, getattr, reflection, typed * message buses, runtime-keyed emits) and ANNOUNCE the boundary

(cg: CodeGraph, scanList: Node[], named: Map<string, Node>)

Source from the content-addressed store, hash-verified

2117 * connected flow never reaches this method.
2118 */
2119 private buildDynamicBoundaries(cg: CodeGraph, scanList: Node[], named: Map<string, Node>): string {
2120 const MAX_NOTES = 4; // boundary bullets per explore
2121 const MAX_SCAN = 8; // bodies scanned
2122 const MAX_TOTAL_CHARS = 200_000;
2123 let projectRoot: string;
2124 try { projectRoot = cg.getProjectRoot(); } catch { return ''; }
2125 const notes: string[] = [];
2126 const seenNode = new Set<string>();
2127 const seenSite = new Set<string>();
2128 let scanned = 0, charsScanned = 0;
2129 for (const node of scanList) {
2130 if (notes.length >= MAX_NOTES || scanned >= MAX_SCAN || charsScanned > MAX_TOTAL_CHARS) break;
2131 if (seenNode.has(node.id) || !node.startLine || !node.endLine) continue;
2132 seenNode.add(node.id);
2133 const absPath = validatePathWithinRoot(projectRoot, node.filePath);
2134 if (!absPath || !existsSync(absPath)) continue;
2135 let content: string;
2136 try { content = readFileSync(absPath, 'utf-8'); } catch { continue; }
2137 const body = content.split('\n').slice(node.startLine - 1, node.endLine).join('\n');
2138 scanned++;
2139 charsScanned += body.length;
2140 for (const m of scanDynamicDispatch(body, node.language || '', node.startLine)) {
2141 if (notes.length >= MAX_NOTES) break;
2142 const siteKey = `${node.filePath}:${m.line}:${m.form}`;
2143 if (seenSite.has(siteKey)) continue;
2144 seenSite.add(siteKey);
2145 const more = m.moreSites ? ` (+${m.moreSites} more such site${m.moreSites > 1 ? 's' : ''} in this body)` : '';
2146 notes.push(`- \`${node.name}\` (${node.filePath}:${m.line}) — ${m.label}: \`${m.snippet}\`${more}`);
2147 if (m.key) {
2148 const cand = this.boundaryCandidates(cg, m.key, !!m.keyIsType, named, node.id);
2149 if (cand) notes.push(` ${cand}`);
2150 }
2151 }
2152 }
2153 if (notes.length === 0) return '';
2154 return [
2155 '**Dynamic boundaries (the static path ends at runtime dispatch)**',
2156 '',
2157 ...notes,
2158 '',
2159 '> These sites choose their call target at runtime (registry / bus / reflection) — the site shown IS where the flow continues. To follow it, run codegraph_explore or codegraph_node on a candidate; source for the sites above is included below.',
2160 '',
2161 ].join('\n');
2162 }
2163
2164 /**
2165 * Interface/registry-dispatch announcement — #687 extended to GRAPH-visible

Callers 1

Calls 6

boundaryCandidatesMethod · 0.95
validatePathWithinRootFunction · 0.90
scanDynamicDispatchFunction · 0.90
hasMethod · 0.80
joinMethod · 0.80
getProjectRootMethod · 0.65

Tested by

no test coverage detected