On this page

The codebase as a walkable graph๏ƒ

๐Ÿ”ต Expert ยท Lesson 19

THE GRAPH DOCUMENTS. Expert 18 addressed prose by section. The two graph documents carry the same prose surface AND a real graph: nodes, edges, traversal, and blast radius - answered at import, with nothing conjured.

md.graph_network the graph md.graph_details the prose about what the graph names

THE ONE THAT CHANGES HOW YOU WORK graph.node_at(source_path, line) You have a traceback. You have file.py:412. That call turns a line number into the NODE that encloses it, and from there impact() tells you what else moves if you change it. Stack trace to blast radius without leaving the process.

EVERY EDGE EXPLAINS ITSELF Edge(source, relation, target, cardinality, phase, origin, why) Two fields there are unusual and both are the point: why - the justification for the edge, carried WITH it origin - authored or derived: did a human assert this, or did a tool infer it? origin is a TRUST FILTER you can pass to walk() and impact(). Walk only authored when you need what someone meant; walk only derived when you need what the machine can prove. A graph that cannot tell you which is which forces you to trust all of it equally, which in practice means trusting none of it.

AND GUESSES DO NOT SHIP. edge_count excludes extractor candidates - the leads over-generate roughly eightfold and never reach the adjacency table. What you walk is evidence.

walk() YIELDS, AND THAT IS A CONTEXT BUDGET DECISION It is a generator "so an agent can stop at the first useful hop instead of materialising a subgraph it will discard" - expert 18's law again, one grain up. Breadth-first, so shallow relationships arrive first and stopping early stops at the RIGHT things.

TWO GUARANTEES THAT MAKE A WALK SAFE - CYCLES ARE HANDLED. borrows and used_by run both ways, so the graph has cycles and every node is expanded at most once. An unguarded walk would not terminate. - AN EDGE TO AN UNKNOWN NODE IS STILL YIELDED, then not expanded. "The relationship is real even where the target is not described here." A graph that hid those edges would quietly understate what touches what.

IMPACT IS MEASURED IN FILES impact(node_id) -> Impact(source, hops, nodes, edges) Ranked by PROXIMITY, nearest first. Not "here are 400 symbols" - here are the files, in the order you should look at them, in the unit you actually open and edit.

AND THE TWO DOCUMENTS JOIN details_key(node_id) -> the section key in the details document describe(node_id) -> that section's text A node in the network document addresses prose in the other one. That is why they ship as a pair.

Before you run๏ƒ

Use the Expert guide for prerequisite concepts. Run from a checkout with Melder installed and Python 3.14 free-threading selected. The collection download includes the level's local helper modules.

Run the saved script๏ƒ

python UX_and_AIX_experiences/04_expert/19_the_codebase_as_a_walkable_graph.py
py -3.14t UX_and_AIX_experiences/04_expert/19_the_codebase_as_a_walkable_graph.py

Download this collection ยท Source on GitHub

Public surface๏ƒ

md.graph_network / graph_details - node_count, edge_count, relations, node_ids, node, find_nodes, nodes_in, node_at, edges_from, edges_to, neighbors, walk, impact, details_key, describe

Code๏ƒ

  1"""
  2TIER: expert (19)
  3GOAL: THE GRAPH DOCUMENTS. Expert 18 addressed prose by section. The two
  4      graph documents carry the same prose surface AND a real graph:
  5      nodes, edges, traversal, and blast radius - answered at import,
  6      with nothing conjured.
  7
  8        md.__graph_network__    the graph
  9        md.__graph_details__    the prose about what the graph names
 10
 11      THE ONE THAT CHANGES HOW YOU WORK
 12        graph.node_at(source_path, line)
 13      You have a traceback. You have `file.py:412`. That call turns a
 14      line number into the NODE that encloses it, and from there
 15      `impact()` tells you what else moves if you change it. Stack trace
 16      to blast radius without leaving the process.
 17
 18      EVERY EDGE EXPLAINS ITSELF
 19        Edge(source, relation, target, cardinality, phase, origin, why)
 20      Two fields there are unusual and both are the point:
 21        `why`    - the justification for the edge, carried WITH it
 22        `origin` - `authored` or `derived`: did a human assert this, or
 23                   did a tool infer it?
 24      `origin` is a TRUST FILTER you can pass to `walk()` and `impact()`.
 25      Walk only `authored` when you need what someone meant; walk only
 26      `derived` when you need what the machine can prove. A graph that
 27      cannot tell you which is which forces you to trust all of it
 28      equally, which in practice means trusting none of it.
 29
 30      AND GUESSES DO NOT SHIP. `edge_count` excludes extractor
 31      candidates - the leads over-generate roughly eightfold and never
 32      reach the adjacency table. What you walk is evidence.
 33
 34      `walk()` YIELDS, AND THAT IS A CONTEXT BUDGET DECISION
 35      It is a generator "so an agent can stop at the first useful hop
 36      instead of materialising a subgraph it will discard" - expert 18's
 37      law again, one grain up. Breadth-first, so shallow relationships
 38      arrive first and stopping early stops at the RIGHT things.
 39
 40      TWO GUARANTEES THAT MAKE A WALK SAFE
 41        - CYCLES ARE HANDLED. `borrows` and `used_by` run both ways, so
 42          the graph has cycles and every node is expanded at most once.
 43          An unguarded walk would not terminate.
 44        - AN EDGE TO AN UNKNOWN NODE IS STILL YIELDED, then not expanded.
 45          "The relationship is real even where the target is not
 46          described here." A graph that hid those edges would quietly
 47          understate what touches what.
 48
 49      IMPACT IS MEASURED IN FILES
 50        impact(node_id) -> Impact(source, hops, nodes, edges)
 51      Ranked by PROXIMITY, nearest first. Not "here are 400 symbols" -
 52      here are the files, in the order you should look at them, in the
 53      unit you actually open and edit.
 54
 55      AND THE TWO DOCUMENTS JOIN
 56        details_key(node_id)  -> the section key in the details document
 57        describe(node_id)     -> that section's text
 58      A node in the network document addresses prose in the other one.
 59      That is why they ship as a pair.
 60SURFACE EXERCISED: md.__graph_network__ / __graph_details__ -
 61                   node_count, edge_count, relations, node_ids, node,
 62                   find_nodes, nodes_in, node_at, edges_from, edges_to,
 63                   neighbors, walk, impact, details_key, describe
 64VERIFY: rides the owner's 3.14t harness; asserts are the contract.
 65"""
 66import melder as md
 67
 68
 69def main() -> None:
 70    graph = md.__graph_network__
 71    print("__graph_network__ ->", type(graph).__name__,
 72          " available =", graph.available)
 73    if not graph.available:
 74        print("   did not ship:", graph.reason)
 75        print("   it still ANSWERS - refused is not missing (expert 18)")
 76        return
 77
 78    # THE SHAPE, BEFORE ANY TRAVERSAL. Note edge_count excludes extractor
 79    # candidates: what you can walk is evidence, not leads.
 80    print()
 81    print("nodes:", graph.node_count, " edges:", graph.edge_count,
 82          " (candidates excluded - guesses do not ship)")
 83    print("relations:", ", ".join(graph.relations))
 84
 85    node_ids = graph.node_ids()
 86    assert len(node_ids) == graph.node_count
 87    print("node_ids():", len(node_ids), "sorted ids")
 88
 89    # PICK A REAL NODE and read its record.
 90    start = node_ids[0]
 91    node = graph.node(start)
 92    print()
 93    print("node:", node.node_id)
 94    print(f"   {node.kind} {node.name!r} at {node.source}:{node.line}"
 95          f"  unsemantic={node.unsemantic}")
 96
 97    # TRACEBACK -> NODE. This is the move worth remembering: a file and a
 98    # line become the thing that encloses them.
 99    enclosing = graph.node_at(node.source, node.line)
100    assert enclosing is not None
101    print()
102    print(f"node_at({node.source!r}, {node.line}) ->", enclosing.node_id)
103    print("   a stack-trace line, resolved to the node that owns it")
104
105    # EVERY NODE DEFINED IN THAT FILE, in definition order.
106    same_file = graph.nodes_in(node.source)
107    assert any(n.node_id == node.node_id for n in same_file)
108    print(f"nodes_in({node.source!r}) ->", len(same_file), "nodes")
109
110    # EDGES EXPLAIN THEMSELVES. `why` carries the justification and
111    # `origin` says whether a human asserted it or a tool derived it.
112    outbound = graph.edges_from(start)
113    inbound = graph.edges_to(start)
114    print()
115    print(f"edges_from: {len(outbound)}   edges_to: {len(inbound)}")
116    for edge in outbound[:2]:
117        print(f"   -{edge.relation}-> {edge.target}")
118        print(f"      origin={edge.origin} phase={edge.phase} "
119              f"cardinality={edge.cardinality}")
120        if edge.why:
121            print(f"      why: {edge.why[:60]}")
122
123    # ONE STEP OUT, THEN A BOUNDED WALK. The walk is a GENERATOR - stop
124    # when you have enough instead of building a subgraph you discard.
125    neighbours = graph.neighbors(start, direction="both")
126    print()
127    print("neighbors(both):", len(neighbours))
128
129    seen = 0
130    for _ in graph.walk(start, depth=2, direction="both"):
131        seen += 1
132        if seen >= 5:
133            break
134    print("walk(depth=2): stopped after", seen, "hops - breadth-first,")
135    print("   so the shallow relationships were the ones I got")
136
137    # THE TRUST FILTER. Same walk, restricted to what a human asserted.
138    authored = sum(1 for _ in graph.walk(start, depth=1, origin="authored"))
139    derived = sum(1 for _ in graph.walk(start, depth=1, origin="derived"))
140    print()
141    print(f"depth-1 authored: {authored}   derived: {derived}")
142    print("   a graph that cannot separate asserted from inferred makes")
143    print("   you trust all of it equally, which means trusting none")
144
145    # BLAST RADIUS, IN FILES, NEAREST FIRST.
146    radius = graph.impact(start, depth=2)
147    print()
148    print("impact(depth=2) ->", len(radius), "files affected")
149    for item in radius[:3]:
150        print(f"   {item.hops} hop(s)  {item.source}"
151              f"  ({len(item.nodes)} nodes, {item.edges} edges)")
152    print("   ranked by proximity - the unit you actually open and edit")
153
154    # THE PAIR JOINS. A node here addresses prose in the other document.
155    key = graph.details_key(start)
156    prose = graph.describe(start)
157    print()
158    print("details_key ->", key)
159    print("describe    ->", len(prose), "chars of prose about that file")
160    print("   which is why the two graph documents ship as a pair")
161
162    print()
163    print("a line number becomes a node; a node becomes a blast radius")
164    print("edges carry their own justification and their own provenance")
165    print("and the walk yields, so you can stop when you know enough")
166
167
168if __name__ == "__main__":
169    main()

Check the outcome๏ƒ

The script contains its own assertions or demonstrated refusal paths. Run it to evaluate those checks against your installed version. The code above is taken directly from the saved file; no run output is invented here.

More expert examples ยท Level guide