PROBLEM
get_method delegates to get_all_methods_in_class(class_name) and therefore only searches class scope — module-level functions (most Python code; exactly the Odoo functions in #65) are unreachable. get_all_callers/get_all_callees call get_method internally, so for module-level functions they return the silent false-empty {"caller_details": []} even when the call graph knows the truth (G.predecessors("pkg.mod.helper") == ["pkg.mod.entry"]).
The backend demonstrably knows about module functions: get_all_methods_in_application (codeanalyzer.py:448-454) indexes module.functions under the module name, and _iter_callables (:559-563) enumerates them — the symmetric single-entity lookup is simply missing.
AFFECTED CODE (audit class A)
- cldk/analysis/python/codeanalyzer/codeanalyzer.py:470 —
get_method (class-only) - cldk/analysis/python/codeanalyzer/codeanalyzer.py:621, :660 —
get_all_callers/get_all_callees false-empty for module funcs - cldk/analysis/python/codeanalyzer/codeanalyzer.py:494, :692, :703 —
get_method_parameters, get_class_call_graph(method_sig), get_comments_in_a_method inherit the blindness - cldk/analysis/python/neo4j/neo4j_backend.py:439, :346, :354, :448 — identical bug on the Neo4j backend (and no bare-signature escape hatch)
FIX CONTRACT
get_method(scope, name) resolves module-level functions when scope is a module name (mirroring get_all_methods_in_application's outer keys), with the same short-name fallback the class path has. Nested inner_callables reachable the same way _iter_callables sees them is in scope if cheap; document if deferred.get_all_callers("pkg.mod", "helper") returns the true predecessors from the call graph; same for callees.- Both backends fixed identically — backend parity is part of the contract (assert both in tests).
- No public signature changes; miss-shape semantics unchanged (that is the 2.0.0 batch issue).
DEFINITION OF DONE
- Failing tests first (test-driven): module-level function lookup + callers/callees on a fixture with a
pkg.mod.entry -> pkg.mod.helper edge, exercised on BOTH backends. - Existing tests green; no facade signature changes.
- Branch fix/issue-NNN; one PR closes this.
Found in the 2026-07-09 lookup audit (see #238 discussion; F1 oracle note on #239).
PROBLEM
get_methoddelegates toget_all_methods_in_class(class_name)and therefore only searches class scope — module-level functions (most Python code; exactly the Odoo functions in #65) are unreachable.get_all_callers/get_all_calleescallget_methodinternally, so for module-level functions they return the silent false-empty{"caller_details": []}even when the call graph knows the truth (G.predecessors("pkg.mod.helper") == ["pkg.mod.entry"]).The backend demonstrably knows about module functions:
get_all_methods_in_application(codeanalyzer.py:448-454) indexesmodule.functionsunder the module name, and_iter_callables(:559-563) enumerates them — the symmetric single-entity lookup is simply missing.AFFECTED CODE (audit class A)
get_method(class-only)get_all_callers/get_all_calleesfalse-empty for module funcsget_method_parameters,get_class_call_graph(method_sig),get_comments_in_a_methodinherit the blindnessFIX CONTRACT
get_method(scope, name)resolves module-level functions whenscopeis a module name (mirroringget_all_methods_in_application's outer keys), with the same short-name fallback the class path has. Nestedinner_callablesreachable the same way_iter_callablessees them is in scope if cheap; document if deferred.get_all_callers("pkg.mod", "helper")returns the true predecessors from the call graph; same for callees.DEFINITION OF DONE
pkg.mod.entry -> pkg.mod.helperedge, exercised on BOTH backends.Found in the 2026-07-09 lookup audit (see #238 discussion; F1 oracle note on #239).