Repository navigation
feat: module graph of one package, as Mermaid or JSON - #743
Conversation
…aph, cgis module-graph) "How is this package built inside?" had no tool: cgis_get_structure gives containment, trace/impact the graph around one symbol, suggest_packages communities and bridges. cgis_module_graph and `cgis module-graph` answer it from graph.db (#741): - Nodes are the package's direct children, each sub-package one node (`expand` for every module below it). - Edges are imports -->, extends ==> and calls -.->, a symbol edge counting for the files holding its ends. Imports read IMPORTS_SYMBOL too, since `from pkg import mod` points IMPORTS at the package. Calls are off by default; type-only imports and test modules are left out. - Each kind is transitively reduced. An edge stays when it is part of a cycle or when nothing else reaches its target, so cycles never vanish from the picture. - Dependencies on code outside the package are a table per module, not edges; a package outside is named whole (lib.deep, not lib). - Mermaid ids carry an m_ prefix, so `end` or `graph` is safe. The json form is {prefix, nodes, subpackages, edges, dropped, outside}. The prefix is resolved by the surfaces through effective_prefixes (#584), and the module keeps no import inside cgis.query: with them, query's self-drift rose from 0.228 to 0.241 against a 0.24 tolerance. Without them it is 0.229. The tolerance is unchanged. Closes #741 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a new module-graph feature, adding a CLI command and an MCP tool to visualize a package's internal module dependencies as a Mermaid flowchart or JSON data. The implementation includes transitive reduction to simplify the graph and lists outbound dependencies. The review feedback suggests several code quality improvements: avoiding mutable default arguments in the MCP server and CLI options, adding defensive checks for empty or whitespace-only prefixes, optimizing the file index construction to minimize iterations, and deduplicating the layers sequence to prevent redundant rendering.
…in stays Review of #743: - Blocker: the edge-by-edge rule let parallel edges into one cycle justify each other, so x -> a1, x -> a2 with a1 <-> a2 lost both, and two cycles joined twice looked unrelated. The reduction now works on the condensation: every edge inside a component stays, and the edges joining two components go together, only when a third component carries the path. - With --expand, `from pkg.sub import mod` no longer draws an edge to the empty pkg/sub/__init__ as well as to pkg.sub.mod. - The MCP diagram carries the freshness note the other diagram tools give. - A layer named twice is one layer; no layer is an error. Test files are left out of the outside table too. The Go behaviour of --expand (files, not packages) is stated in the module docstring. - build_module_graph is split into helpers (C901). - .github/test-count-baseline raised to 4433, as the floor asked. Each fix has a test that failed first. query's self-drift is 0.231 (tolerance 0.24, unchanged). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…one pass over nodes From Gemini's review of #743: the MCP and CLI layer defaults are tuples (the MCP schema is unchanged: an array defaulting to imports, extends); a blank prefix is refused by both surfaces with a usage message, with a test; the file index is built in one pass over the nodes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
/gemini review |
|
/guardian review |
There was a problem hiding this comment.
Summary: Checked the transitive reduction (SCC condensation, implied-edge check, cycle preservation), the file-to-group mapping for collapsed and expanded views, outside-package summarisation, and the ontology edge types used (IMPORTS, IMPORTS_SYMBOL, EXTENDS, CALLS), which all exist in the ontology; the reduction logic and tests for cycles and parallel edges look correct. Flagged the Mermaid node-id collision in expand mode and a broad ValueError handler in the CLI.
🤖 claude-haiku-5-5 · 26,437 prompt + 8,111 completion = 34,548 tokens · graph 4/6 files (67%)
There was a problem hiding this comment.
Code Review
This pull request introduces a new module graph feature to visualize and analyze the internal dependencies of a package. It adds a new cgis_module_graph MCP tool, a module-graph CLI command, and the underlying logic in module_graph.py to extract, transitively reduce, and render the module graph in Mermaid or JSON formats. Feedback on the pull request suggests improving the _outside_package helper function to correctly resolve deeply nested outside dependencies by finding the longest matching package prefix.
…re the graph From guardian's review of #743 (round 2): - Folding a name to [A-Za-z0-9_] is not injective: pkg.a.b and pkg.a_b both gave m_a_b, and Mermaid merged them. Ids are now assigned once per render, a collision getting _2, _3... in sorted order. - The CLI validates --layer (checked_layers, now public) before it opens the graph, so its ValueError handler no longer wraps unrelated code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
* refactor(module-graph): clear the three Sonar issues #743 left - transitive_reduction (S3776, 18): the condensation is built in _condense, and "reachable through a third component" is _implied over a generic _reach, instead of a nested search. - build_module_graph (S3776, 16): the edge loop moves to _collect. - test_module_graph (S3415): the one assertion with its expected side first is turned round. Behaviour is unchanged: the same 25 tests pass, and query's self-drift is 0.230 (tolerance 0.24). Closes #746 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * perf(module-graph): one walk per implied check again Review of #747: _implied ran a separate full _reach from each successor, where the code it replaced did one DFS from all of them, so the "behaviour- preserving" refactor made the reduction up to 2.6x slower on a synthetic 2000-node / 12000-edge graph. _reach now takes several starts, and _implied walks once from all of a's other successors. Measured with the reviewer's bench against the pre-refactor function: same output on 3000 random graphs; ratio now 1.0x / 1.5x / 1.2x (cyclic 0.05, cyclic 0.01, DAG; was 1.0x / 2.6x / 1.9x). The remainder is the old early exit on reaching b, kept out to stay under Sonar's complexity limit; real module graphs have tens of nodes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(module-graph): a type-only import draws no edge From guardian's review of #747: the type_only filter in the edge collection had no test. One now ingests an import under TYPE_CHECKING and expects no edge; it fails with the filter removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>



Closes #741.
What
cgis_module_graph(MCP) andcgis module-graph(CLI) answer "how is this package built inside?" fromgraph.db.[[sub/]]).expandlists every module below the package instead.-->, inheritance==>and calls-.->. A symbol-level edge counts for the files that hold its ends.from pkg import modpoints IMPORTS atpkg, and only IMPORTS_SYMBOL namespkg.mod.exclude_tests,--with-tests), and so are type-only imports, as the cycle tools do.lib.deep, notlib).m_prefix, so a module namedendorgraphis safe.{prefix, nodes, subpackages, edges:[{source,target,kind}], dropped, outside}, so a repository can keep and guard its own snapshot.Two things the self-parsing gates caught
self.*placeholder calls. A small helper class pushed the count from 82±10 to 95. It is now plain functions.cgis.query. Importingfqn,drift._sccandcohesionfrom the new module raised query's self-drift from 0.228 to 0.241, against a 0.24 tolerance.effective_prefixes(mcp: cgis_find_symbol's fqn_prefix does not drop the directories above the graph (#568's rule) #584 still holds).direct_childis inlined.Prefix
Give the prefix as the graph names it (
cgis.query;cgis_find_symbolshows it). A directory above the ingest root, such assrc., is dropped. The issue's exampledomains.reservationwould need the root, e.g.app.domains.reservationon owner-api.Example (
cgis.queryon this repository)Checks
tests/unit/test_module_graph.py: 16 tests. They cover the reduction, including cycles, the default layers, tests excluded,expand, the outside table, the Mermaid styles and keywords, the CLI and the MCP tool.MCP_REFERENCE.mdis regenerated by autodoc on main, so it is not edited here.🤖 Generated with Claude Code