MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / buildDynamicBoundaries

Method buildDynamicBoundaries

src/mcp/tools.ts:2161–2204  ·  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

2159 * connected flow never reaches this method.
2160 */
2161 private buildDynamicBoundaries(cg: CodeGraph, scanList: Node[], named: Map<string, Node>): string {
2162 const MAX_NOTES = 4; // boundary bullets per explore
2163 const MAX_SCAN = 8; // bodies scanned
2164 const MAX_TOTAL_CHARS = 200_000;
2165 let projectRoot: string;
2166 try { projectRoot = cg.getProjectRoot(); } catch { return ''; }
2167 const notes: string[] = [];
2168 const seenNode = new Set<string>();
2169 const seenSite = new Set<string>();
2170 let scanned = 0, charsScanned = 0;
2171 for (const node of scanList) {
2172 if (notes.length >= MAX_NOTES || scanned >= MAX_SCAN || charsScanned > MAX_TOTAL_CHARS) break;
2173 if (seenNode.has(node.id) || !node.startLine || !node.endLine) continue;
2174 seenNode.add(node.id);
2175 const absPath = validatePathWithinRoot(projectRoot, node.filePath);
2176 if (!absPath || !existsSync(absPath)) continue;
2177 let content: string;
2178 try { content = readFileSync(absPath, 'utf-8'); } catch { continue; }
2179 const body = content.split('\n').slice(node.startLine - 1, node.endLine).join('\n');
2180 scanned++;
2181 charsScanned += body.length;
2182 for (const m of scanDynamicDispatch(body, node.language || '', node.startLine)) {
2183 if (notes.length >= MAX_NOTES) break;
2184 const siteKey = `${node.filePath}:${m.line}:${m.form}`;
2185 if (seenSite.has(siteKey)) continue;
2186 seenSite.add(siteKey);
2187 const more = m.moreSites ? ` (+${m.moreSites} more such site${m.moreSites > 1 ? 's' : ''} in this body)` : '';
2188 notes.push(`- \`${node.name}\` (${node.filePath}:${m.line}) — ${m.label}: \`${m.snippet}\`${more}`);
2189 if (m.key) {
2190 const cand = this.boundaryCandidates(cg, m.key, !!m.keyIsType, named, node.id);
2191 if (cand) notes.push(` ${cand}`);
2192 }
2193 }
2194 }
2195 if (notes.length === 0) return '';
2196 return [
2197 '**Dynamic boundaries (the static path ends at runtime dispatch)**',
2198 '',
2199 ...notes,
2200 '',
2201 '> 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.',
2202 '',
2203 ].join('\n');
2204 }
2205
2206 /**
2207 * 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
getProjectRootMethod · 0.65
joinMethod · 0.45

Tested by

no test coverage detected