Skip to content

ingest: derive code kinds and class-qualified names from graph structure (#17, step 1) - #88

Merged
gangtao merged 1 commit into
mainfrom
feat/ingest-kinds-and-names
Sep 21, 2026
Merged

gangtao merged 1 commit into
mainfrom
feat/ingest-kinds-and-names

Conversation

@gangtao

@gangtao gangtao commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Step 1 of the plan in #17. Does not close #17.

Problem (measured on proton-enterprise@v3.3.1)

graphify has no class/method kinds; tpk mapped every non-callable code node to kind="file": 102,094 "file" nodes, 13,221 real files. An out-of-class C++ definition — BlockIO InterpreterInsertQuery::execute() {…}, the dominant form — comes out of graphify non-callable, with a bare label, merged into its header declaration. So it was stored as ("execute", kind=file) next to 73 other executes; 7,393 such nodes make calls (their calls edges were always intact). search_entities(kinds=["function"]) missed them, kinds=["class"] matched nothing, and classes / type aliases were stored as function.

Change

parse_graph_json reads the edges first (_Structure) and classifies code nodes from flags and structure:

kind rule
class _callable_class; or known only as one end of an inherits edge
function _callable; or a node a class defines whose body is in the graph (a file contains it, or it makes calls)
member a class defines it, no body known — a field, or a declared-only / pure-virtual method
file the node whose label is its file's basename
symbol the rest — bare type / alias / variable references (String, ContextPtr, …; the graph's biggest hubs)

Methods and members are named Class::name from the owning class (.supportsTransactions() → InterpreterInsertQuery::supportsTransactions). Bonus: two functions sharing file::name (overloads, ~3% on proton) no longer collapse onto one id — the second falls back to …#<graphify id>.

The rules were designed against real raw output (graphify on src/Interpreters, 763 files): only classes emit defines (4,690/4,690), and every out-of-class method is "defined by a class + contained by its .cpp".

Result on src/Interpreters (14,202 nodes, 27,506 edges)

  • kinds: function 4,523 · symbol 4,517 · member 2,911 · class 1,517 · file 734 — 0 id collisions, all edges kept.
  • Every node that makes a call is now a function (1,877; previously a third were file).
  • InterpreterInsertQuery.*: all 11 methods are InterpreterInsertQuery::… functions, 5 fields are members, 4 classes, 2 files.
  • function names: 3,617 class-qualified, 0 still starting with .; most common bare names are now TEST / main, not execute ×74.

Also updated: agent prompt kinds list, Explorer kind filter, the parser's documented vocabulary, docs/FEATURES.md.

Rollout

Takes effect only on re-ingest of each corpus entry (node ids change with the kind; ingest_repo already deletes an entry's stale rows). proton repos need the app at ≥ 8 Gi for ingest. Until then old and new entries just coexist with the old vocabulary.

Testing

  • New parser tests (RED→GREEN) on shapes copied from real output: kinds from structure, out-of-class method keeps its calls and is class-qualified, same method name in two classes, distinct ids after reclassification, overload fallback. One existing test updated (a struct is a symbol, not a file).
  • pytest: 318 passed, 10 skipped, 1 failed — tests/test_ingest.py::test_ingest_repo_passes_extraction_and_backend, environment-dependent, failing on main too. Web build clean.
  • Not yet done: a full re-ingest of the proton repos with this parser (validated on the src/Interpreters extraction instead). Known limit: a declared-only method and a field are both member — graphify gives no signal to tell them apart.

Next: step 2 — make path_between a directed, call-like, hub-skipping traversal (the new symbol kind makes the hub filter trivial).

🤖 Generated with Claude Code

…structure (#17)

graphify has no class/method kinds, and tpk mapped every non-callable code node
to kind=file. On proton-enterprise that is 102k 'file' nodes of which 13k are
files: an out-of-class C++ definition (BlockIO Foo::execute() {...}, the dominant
form) comes out of graphify non-callable with a bare label, merged into its
header declaration -- so InterpreterInsertQuery::execute was stored as
('execute', kind=file), with 73 other 'execute's. Its calls edges were intact.

parse_graph_json now reads the edges first and classifies code nodes as
function / class / member / file / symbol: _callable_class -> class; a node a
class 'defines' with a body in the graph (a file 'contains' it, or it calls) ->
function; defined without a body -> member; label == file basename -> file;
either end of 'inherits' -> class; otherwise symbol (String, ContextPtr, ...).
Methods and members are named Class::name from the owning class. Two functions
sharing file::name (overloads) no longer collide on one id.

On src/Interpreters (14,202 nodes): function 4523 / symbol 4517 / member 2911 /
class 1517 / file 734; every node that makes a call is now a function; 0 id
collisions. Agent prompt, Explorer kind filter and docs list the new vocabulary.
Takes effect on re-ingest (stale rows are deleted per entry).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deep call-path queries (e.g. "trace HTTP insert → nativelog write") return no result — sparse C++ call graph + agent doesn't use path_between

1 participant