Observation
When Jedi fails to resolve a method call, the level-2 defuse linker links the call site to every callable in the application with that method name.
Concrete case on odoo-slim-19 (1.4.1): self.worksheet.write(...) in ExportXlsxWriter.write has no PY_RESOLVES_TO in one analysis run (it had one in another run under a different interpreter — see the companion reproducibility issue). With no resolution, the linker emits PY_CALLS from that one call site to every write in the application — hundreds of targets, prov=defuse. In a consumer's bounded call graph this single site adds 219 edges at depth 1 to the Many2many.write_real hub (27,568 → 27,784) and makes ExportXlsxWriter.write a caller of AccountMove.write, which it is not.
Why it matters
- Precision collapses exactly where the analysis is least sure; a consumer cannot distinguish "this call really fans out" from "we did not know".
prov=defuse is on the edge, so the information exists — but every consumer has to know to discount it, and callers_of("AccountMove.write") returns 332 results of which some are artefacts.
- Combined with interpreter-sensitive resolution, the fan-out makes call-graph shape vary run to run.
Suggestions
- Cap or type-gate the fallback: link only to methods on plausible receivers (in the MRO of any inferred type, or at minimum the same package), or emit a single edge to an unresolved-method ghost (
@external/<unresolved>/write) instead of N concrete edges.
- If the fan-out stays, mark it distinctly (
prov=defuse-fanout, or a candidates count on the edge) so consumers can filter it without discarding well-founded defuse links.
Definition of done
- An unresolved receiver no longer produces one
PY_CALLS edge per same-named method, or such edges are distinguishable from single-target defuse links by an edge property.
Found while verifying python-sdk#327 against two 1.4.1 emits.
Observation
When Jedi fails to resolve a method call, the level-2 defuse linker links the call site to every callable in the application with that method name.
Concrete case on odoo-slim-19 (1.4.1):
self.worksheet.write(...)inExportXlsxWriter.writehas noPY_RESOLVES_TOin one analysis run (it had one in another run under a different interpreter — see the companion reproducibility issue). With no resolution, the linker emitsPY_CALLSfrom that one call site to everywritein the application — hundreds of targets,prov=defuse. In a consumer's bounded call graph this single site adds 219 edges at depth 1 to theMany2many.write_realhub (27,568 → 27,784) and makesExportXlsxWriter.writea caller ofAccountMove.write, which it is not.Why it matters
prov=defuseis on the edge, so the information exists — but every consumer has to know to discount it, andcallers_of("AccountMove.write")returns 332 results of which some are artefacts.Suggestions
@external/<unresolved>/write) instead of N concrete edges.prov=defuse-fanout, or acandidatescount on the edge) so consumers can filter it without discarding well-founded defuse links.Definition of done
PY_CALLSedge per same-named method, or such edges are distinguishable from single-target defuse links by an edge property.Found while verifying python-sdk#327 against two 1.4.1 emits.