Skip to content

explain/_find_node cannot resolve node IDs containing punctuation (e.g. concept:domain:x) #2467

Description

@sean-soomgo

Summary

_find_node (and therefore graphify explain) cannot resolve any node ID that contains punctuation. In our graph this silently disabled lookup for 2,327 of 2,327 concept/overlay nodes (concept:domain:*, concept:class:*, entity:*, commit:*), while IDs without punctuation resolved 400/400.

The docstring states the intent clearly, so this looks like an oversight rather than a design decision:

Return node IDs whose label or ID matches the search term (diacritic-insensitive).
[...] Node-ID exact matches are grouped with label exact matches.

find_node_ambiguity's user-facing hint also assumes ID lookup works:

Retry with the repo-relative path or the full node id.

Version: graphifyy 0.9.32

Reproduction

import networkx as nx
from graphify.serve import _find_node

G = nx.DiGraph()
G.add_node("concept:domain:gosu", label="Gosu")   # id contains ':'
G.add_node("plain_node_id", label="Plain")        # id has no punctuation

print("colon id  ->", _find_node(G, "concept:domain:gosu"))
print("plain id  ->", _find_node(G, "plain_node_id"))
print("by label  ->", _find_node(G, "Gosu"))

Actual:

colon id  -> []
plain id  -> ['plain_node_id']
by label  -> ['concept:domain:gosu']

Expected: the first line resolves to ['concept:domain:gosu'].

Same through the CLI:

$ graphify explain "concept:domain:gosu"
No node matching 'concept:domain:gosu' found.     # node exists in the graph

Root cause

In graphify/serve.py, _find_node_tiers builds two normalizations of the query:

term       = " ".join(_search_tokens(label))          # \w+ tokens -> punctuation becomes a space
norm_query = _strip_diacritics(str(label)).lower().strip()   # punctuation preserved

The exact-match tier compares the node ID against term only:

elif (
    term == norm_label or term == bare_label or term == label_tokens or term == nid_lower
    or norm_query == norm_label or norm_query == bare_label
):

For concept:domain:gosu:

value
term concept domain gosu
norm_query concept:domain:gosu
nid_lower concept:domain:gosu

term == nid_lower is always False once the ID contains punctuation. norm_query — which already holds the correct form and is even part of the trigram prefilter (_trigram_candidates(G, [term, norm_query])) — is never compared against nid_lower.

norm_query appears to have been introduced for punctuated labels (the comment references #1704); it just wasn't extended to node IDs.

Suggested fix

One additional clause in the exact tier:

elif (
    term == norm_label or term == bare_label or term == label_tokens or term == nid_lower
    or norm_query == norm_label or norm_query == bare_label or norm_query == nid_lower
):

Why this is worse than a plain lookup failure

The natural workaround — calling explain with the node's label instead of its ID — resolves to the wrong node silently. Across our 35 concept nodes, only 4 resolved correctly; 31 landed on a same-named code node:

query resolves to intended
Repository data_repository_feedrepository_ts concept:class:Repository
API api_apitypehelper_ts concept:class:API
Gosu entity:Gosu concept:domain:gosu

So users get a plausible-looking wrong answer rather than an error, which is hard to notice in downstream reasoning.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions