diff --git a/backend/src/graph/live.ts b/backend/src/graph/live.ts index 0eadcd3..6cbb74d 100644 --- a/backend/src/graph/live.ts +++ b/backend/src/graph/live.ts @@ -53,6 +53,14 @@ export function isFilePath(f: string): boolean { /** Engineer names that are test/verification artifacts, not real teammates. */ const ENGINEER_NOISE = /(^verify\b|^.$|testrepo|-?check\b|\d{4,})/i; +const MAX_FILES = 9; + +/** Short, readable node label — last two path segments (full path goes in summary). */ +function shortLabel(file: string): string { + const parts = file.split('/').filter(Boolean); + return parts.slice(-2).join('/') || file; +} + const STATUS_RANK: Record = { stable: 0, active: 1, @@ -183,7 +191,7 @@ export async function materializePodGraph(podId: string): Promise e.kind === 'collides' && e.target === id)) dropNode(id); } + // Cap file nodes to the most-connected (collision files first). + const fileNodes = [...b.nodes.values()].filter((n) => n.kind === 'file'); + if (fileNodes.length > MAX_FILES) { + const inCollision = (id: string) => + [...b.edges.values()].some((e) => e.kind === 'touches' && e.source === id); + const degree = (id: string) => + [...b.edges.values()].filter((e) => e.source === id || e.target === id).length; + fileNodes.sort( + (a, z) => + Number(inCollision(z.id)) - Number(inCollision(a.id)) || degree(z.id) - degree(a.id), + ); + for (const n of fileNodes.slice(MAX_FILES)) dropNode(n.id); + } + // Files / interventions left with no edges -> drop. for (const [id, n] of [...b.nodes]) { if (n.kind === 'file' || n.kind === 'intervention') {