You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Refiled from #183, whose items 2 and 4 describe the same heuristic in two places. Item 2's original wording did not match the code — corrected below — but there is a real defect underneath both.
The heuristic
Two sites resolve a name by suffix match and return the result at full confidence.
indices.py — map_to_node_fqn strip-loop. Strips leading segments off the imported FQN and takes the first suffix that exists as a node:
symbols.py:49 — resolve_class_ref validator. Looks the bare class name up in the global index, then accepts the result if its tail matches:
resolved.endswith(f".{name}")
What is actually wrong (and what is not)
#183 said the strip-loop "returns the FIRST candidate … on a prefix collision it resolves to an arbitrary symbol". That part is incorrect. The loop strips fewest segments first, so it returns the most specific suffix, and candidate in self.nodes is an exact key lookup — there is no candidate set to disambiguate, so the len(candidates) == 1 guard from the suffix_map branch above does not carry over. Measured:
nodes {engine.X, X} map_to_node_fqn('cgis.resolver.engine.X') -> engine.X # most specific wins
The real defect is different: a suffix match cannot tell "the import had an extra package prefix" from "this is a different package entirely", and it reports the guess at confidence 1.0.
nodes {utils.helper} map_to_node_fqn('pkg_a.utils.helper') -> utils.helper # unrelated package
nodes {thing} map_to_node_fqn('a.b.c.thing') -> thing # collapses to a bare leaf
The second shape is the sharp one — a four-segment import matching a bare top-level name is close to unconstrained. resolve_class_ref has the same hole: for a dotted ref util.Base it requires only that the resolved FQN end in .util.Base, which any util module in the graph satisfies.
Absorbing a prefix mismatch is the function's stated purpose, so the heuristic is not simply a bug to delete — it needs a confidence signal or a tighter acceptance rule.
Why this cannot be validated here
Instrumented map_to_node_fqn over a full ingest of src/:
The strip-loop never fires on cgis's own graph. It is a compatibility path for layouts that do not match source_roots, so a self-ingest can neither exercise a change nor measure its blast radius.
The constraint that makes this land carefully
Tightening resolution raises unresolved_ratio, which feeds drift_tolerance — and tolerances are a committed ratchet since #151, with docs/ontology/tolerances.lock failing the suite on any increase. A change that lowers the resolution rate on a repo where this path is live can trip that gate. That is the intended behaviour of the ratchet, but it means this work needs measurement before it needs a patch.
Refiled from #183, whose items 2 and 4 describe the same heuristic in two places. Item 2's original wording did not match the code — corrected below — but there is a real defect underneath both.
The heuristic
Two sites resolve a name by suffix match and return the result at full confidence.
indices.py—map_to_node_fqnstrip-loop. Strips leading segments off the imported FQN and takes the first suffix that exists as a node:symbols.py:49—resolve_class_refvalidator. Looks the bare class name up in the global index, then accepts the result if its tail matches:What is actually wrong (and what is not)
#183 said the strip-loop "returns the FIRST candidate … on a prefix collision it resolves to an arbitrary symbol". That part is incorrect. The loop strips fewest segments first, so it returns the most specific suffix, and
candidate in self.nodesis an exact key lookup — there is no candidate set to disambiguate, so thelen(candidates) == 1guard from thesuffix_mapbranch above does not carry over. Measured:The real defect is different: a suffix match cannot tell "the import had an extra package prefix" from "this is a different package entirely", and it reports the guess at confidence 1.0.
The second shape is the sharp one — a four-segment import matching a bare top-level name is close to unconstrained.
resolve_class_refhas the same hole: for a dotted refutil.Baseit requires only that the resolved FQN end in.util.Base, which anyutilmodule in the graph satisfies.Absorbing a prefix mismatch is the function's stated purpose, so the heuristic is not simply a bug to delete — it needs a confidence signal or a tighter acceptance rule.
Why this cannot be validated here
Instrumented
map_to_node_fqnover a full ingest ofsrc/:The strip-loop never fires on cgis's own graph. It is a compatibility path for layouts that do not match
source_roots, so a self-ingest can neither exercise a change nor measure its blast radius.The constraint that makes this land carefully
Tightening resolution raises
unresolved_ratio, which feedsdrift_tolerance— and tolerances are a committed ratchet since #151, withdocs/ontology/tolerances.lockfailing the suite on any increase. A change that lowers the resolution rate on a repo where this path is live can trip that gate. That is the intended behaviour of the ratchet, but it means this work needs measurement before it needs a patch.Suggested approach
source_rootsso the prefix mismatch is real.unresolved_ratioper domain.--min-confidencealready exists);Acceptance
pkg_a.utils.helpermust not resolve to an unrelatedutils.helper.Supersedes items 2 and 4 of #183.