Skip to content

Commit c573a3f

Browse files
cdeustclaude
andcommitted
feat(viz): surface AP all-file edges — doc References + File→File imports
AP 0.2.0 indexes every file (File nodes for .js/.md/.json/.pdf/...) and adds direct File→File edges: Imports_File_File (.js import/require) and the new References_File_File (Markdown [text](path) links). Query both in _impact_for_graph and fold them into the file-level direction rollups so the impact panel shows real edges for non-AST and frontend files: - references / referenced_by (doc links) - imports folded into depends_on / depended_on_by Richest-graph selection + empty-state check updated to count them. Frontend impact diagram renders the References groups. Verified against the freshly all-file-indexed Cortex graph (1160 files): References_File_File resolves (CONTRIBUTING.md→README.md, README.md→ docs/papers/science.md). py compile + ruff clean; trace.js parses. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c07e179 commit c573a3f

2 files changed

Lines changed: 63 additions & 3 deletions

File tree

mcp_server/server/http_standalone_trace.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,30 @@ async def q(cypher):
344344
"p.depth AS depth, p.symbol_count AS n "
345345
"ORDER BY p.symbol_count DESC LIMIT 40" % esc
346346
)
347+
# All-file indexing (AP >= 0.2.0): direct File→File edges for files
348+
# the AST parsers don't cover. Imports_File_File = .js import/require;
349+
# References_File_File = Markdown/doc links. These make non-code and
350+
# frontend files show real direction in the impact panel.
351+
file_imports = await q(
352+
"MATCH (f:File)-[r:Imports_File_File]->(d:File) "
353+
"WHERE f.id = '%s' "
354+
"RETURN DISTINCT d.id AS name, r.confidence AS conf LIMIT 200" % esc
355+
)
356+
file_imported_by = await q(
357+
"MATCH (s:File)-[r:Imports_File_File]->(f:File) "
358+
"WHERE f.id = '%s' "
359+
"RETURN DISTINCT s.id AS name, r.confidence AS conf LIMIT 200" % esc
360+
)
361+
doc_refs = await q(
362+
"MATCH (f:File)-[r:References_File_File]->(d:File) "
363+
"WHERE f.id = '%s' "
364+
"RETURN DISTINCT d.id AS name, r.confidence AS conf LIMIT 200" % esc
365+
)
366+
doc_referenced_by = await q(
367+
"MATCH (s:File)-[r:References_File_File]->(f:File) "
368+
"WHERE f.id = '%s' "
369+
"RETURN DISTINCT s.id AS name, r.confidence AS conf LIMIT 200" % esc
370+
)
347371

348372
def _file_of(qn):
349373
return str(qn or "").partition("::")[0]
@@ -437,13 +461,40 @@ def _rollup(items):
437461
out.sort(key=lambda x: x["edges"], reverse=True)
438462
return out
439463

464+
def _file_edges(rows, kind):
465+
out = []
466+
for r in rows:
467+
nm = r.get("name")
468+
if not nm or nm == rel_path:
469+
continue
470+
out.append(
471+
{
472+
"file": nm,
473+
"label": _basename(nm),
474+
"kind": kind,
475+
"confidence": _conf(r),
476+
}
477+
)
478+
return out
479+
480+
# Direct File→File edges (AP all-file indexing): code imports for
481+
# non-AST files (.js) and doc references (Markdown). Folded into the
482+
# file-level direction so the panel shows them even when a file has
483+
# no AST symbols at all.
484+
imports_files = _file_edges(file_imports, "imports")
485+
imported_by_files = _file_edges(file_imported_by, "imports")
486+
references = _file_edges(doc_refs, "references")
487+
referenced_by = _file_edges(doc_referenced_by, "references")
488+
440489
return {
441490
"downstream": downstream,
442491
"upstream": upstream,
443492
"members": members,
444493
"processes": processes,
445-
"depends_on": _rollup(downstream),
446-
"depended_on_by": _rollup(upstream),
494+
"references": references,
495+
"referenced_by": referenced_by,
496+
"depends_on": _rollup(downstream + imports_files),
497+
"depended_on_by": _rollup(upstream + imported_by_files),
447498
}
448499

449500
return loop_run(_run())
@@ -491,6 +542,10 @@ def serve_trace_impact(handler) -> None:
491542
+ len(r.get("upstream", []))
492543
+ len(r.get("members", []))
493544
+ len(r.get("processes", []))
545+
+ len(r.get("references", []))
546+
+ len(r.get("referenced_by", []))
547+
+ len(r.get("depends_on", []))
548+
+ len(r.get("depended_on_by", []))
494549
)
495550
if n > best_edges:
496551
best_edges = n

ui/unified/js/trace.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@
268268
// Lead with the file-level direction (developer blast-radius at a glance).
269269
h += _impactFiles('Depends on (files)', d.depends_on, 'down');
270270
h += _impactFiles('Depended on by (files)', d.depended_on_by, 'up');
271+
// Doc references (Markdown links → files) — all-file indexing.
272+
h += _impactGroup('References (docs → files)', d.references, 'down');
273+
h += _impactGroup('Referenced by (docs)', d.referenced_by, 'up');
271274
// Causal chains this file launches.
272275
h += _impactProcesses(d.processes);
273276
// Then the detailed symbol-level edges.
@@ -276,7 +279,9 @@
276279
h += _impactGroup('Defines', d.members, 'flat');
277280
h += _impactVersions(d.versions);
278281
if (!(d.downstream || []).length && !(d.upstream || []).length
279-
&& !(d.members || []).length && !(d.processes || []).length) {
282+
&& !(d.members || []).length && !(d.processes || []).length
283+
&& !(d.references || []).length && !(d.referenced_by || []).length
284+
&& !(d.depends_on || []).length && !(d.depended_on_by || []).length) {
280285
h += '<div class="impact-loading">No dependencies found in the code-graph.</div>';
281286
}
282287
return h;

0 commit comments

Comments
 (0)