Target: canonical schema v2 (epic #69, stage #72). v2 counterpart of #80, which tracks the same defect on v1/main and holds the full audit evidence and the minimal reproduction — one root cause, two lineages, so the fix must land (or be forward-ported) on the feat/v2-stage* chain independently of when/whether a 1.x maintenance release picks it up.
Why v2 is affected
The v2 stage chain shares symbol_table_builder.py with v1, and its L2 stage consumes the defective value directly:
- The wrong anchor is present on the v2 chain at
symbol_table_builder.py:610-613 (_infer_callee / return_type inferred at node.lineno, node.col_offset — the first character of the call expression, i.e. the receiver token for any attribute call).
- L2's
backfill_callees (codeanalyzer/schema/l2_callees.py) copies cs.callee_signature verbatim into each body call node's callee — "the one sanctioned value change" of L2. Every receiver-type binding therefore becomes a wrong callee id in the canonical tree and a wrong call_graph edge at application scope.
Measured impact on v1 exports (same producer code): 52,483/59,523 resolved callsites bind to a differently-named symbol; 99.93% of Odoo env[...] calls bind to the caller's own enclosing class. See #80 for the breakdown, Cypher, and fixture.
Acceptance for v2
Why v2 is affected
The v2 stage chain shares
symbol_table_builder.pywith v1, and its L2 stage consumes the defective value directly:symbol_table_builder.py:610-613(_infer_callee/return_typeinferred atnode.lineno, node.col_offset— the first character of the call expression, i.e. the receiver token for any attribute call).backfill_callees(codeanalyzer/schema/l2_callees.py) copiescs.callee_signatureverbatim into each bodycallnode'scallee— "the one sanctioned value change" of L2. Every receiver-type binding therefore becomes a wrongcalleeid in the canonical tree and a wrongcall_graphedge at application scope.Measured impact on v1 exports (same producer code): 52,483/59,523 resolved callsites bind to a differently-named symbol; 99.93% of Odoo
env[...]calls bind to the caller's own enclosing class. See #80 for the breakdown, Cypher, and fixture.Acceptance for v2
_infer_calleeanchoring) so L2 inherits correct values; no compensating logic inl2_callees.py.