resolve_interface_dispatch (shared by the C# and Swift gates) bailed out
entirely whenever an interface/protocol had more than one implementer,
on the reasoning that guessing which one a runtime call reaches would be
wrong. For a static structural graph the question isn't which one is
reached at runtime, it's which ones *can* be reached, and the answer with
N implementers is all N, not none -- so every chain through an injected
dependency with more than one implementation was left permanently cut at
the interface.
Each implementer is still subject to the existing single-owner guard
independently: an implementer that itself declares more than one method
of the same name is skipped on its own, without suppressing its siblings.
Confidence drops as the candidate set grows, using the extraction
rubric's own discrete INFERRED tiers (0.85 / 0.75 / 0.65 floor) instead of
inventing continuous precision.
Fixes#3800 for the dispatches_to half (interface->implementation calls).
The property-access half of that issue (a method edge for `c.Count`
through an interface-typed receiver) is a separate mechanism and is not
addressed here, to keep this PR to one concern.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
(cherry picked from commit f340494e07)
Snap the off-rubric INFERRED confidence scores emitted by the cross-repo
call/type resolvers and interface dispatch (0.8 and 0.9) to the nearest
canonical rubric tier (0.85), so every INFERRED edge carries one of the
documented discrete scores. Label-only change: no edge flips
EXTRACTED<->INFERRED and no edge is produced or dropped. Replaces the
substring-matching guard test with an AST-based one that scans every
module for off-rubric scores.
Squashed from #4045 (authorship preserved).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A call through an injected `Store` resolves to `Store.fetch()` — the
protocol requirement (#3673) — and the conforming `RemoteStore.fetch()`
is a separate node with nothing joining the two, so every directed walk
through an injected dependency stops at the protocol. Link the two with
the member-level `dispatches_to` edge C# already gets (#3003), under the
same single-conformer / single-owner guard: a protocol with two
conformers, a conformer with two same-named methods, or a default
implementation nobody overrides emits nothing.
The mechanism lives in graphify.interface_dispatch, lifted out of the
C# pass in the preceding refactor; swift_dispatch.py is the `.swift`
gate over it, registered right after the C# pass. Both ends of a pair
must be declared in a Swift file, so a Kotlin class naming the same
supertype never receives a Swift edge.
Known limit, pinned by test: a `class C: P` whose protocol is declared
in another file is classified `inherits` by the per-file classifier and
is not dispatched; struct/enum/actor conformers and `extension C: P`
are.
AI disclosure: Claude Code assisted with this change.
(cherry picked from commit 0ca5c71b1c)