| Property | Value |
|---|---|
| Type | refactor |
| Status | active |
| Date | 2026-04-06 |
| Parent RFC | #568 |
| Prerequisite | #665 (lookupMethodByOwner — merged) |
Today, GitNexus call resolution works like this: tree-sitter queries produce raw captures, per-language extractors scatter type information across TypeEnv, and resolveCallTarget glues it together with fuzzy name-based lookups and cascading heuristics.
The target architecture inverts this. Language providers feed a language-independent semantic model. All resolution flows through the model, not through name-matching.
CURRENT TARGET
═══════ ══════
tree-sitter queries Language Providers (languages/)
→ raw captures → LanguageProvider interface
→ per-language extractors → feed SemanticModel
→ TypeEnv (per-file, discarded) (symbols, types, ownership, heritage)
→ call-processor.ts → SemanticModel drives resolution
→ resolveCallTarget (O(1) owner-scoped lookups)
→ lookupFuzzy (global scan) → knowledge graph
→ heuristic filtering
→ knowledge graph
The language providers already exist (languages/ directory with LanguageProvider interface). The extractors already exist (type-extractors, method-extractors, field-extractors, import-resolvers, named-bindings — all per-language). What's missing is the model they should be feeding and the resolution layer that consumes it.
lookupFuzzy(name) returns every symbol named name across the entire project. For save in a 5000-file repo, this might return 40 candidates from 40 unrelated classes. The caller then spends 5 stages filtering:
typeFiles and typeNodeIdslookupFuzzy (D2)This is a name → filter → filter → filter pipeline. A semantic model makes it owner + name → direct hit.
TypeEnv is built per-file, used during call processing, then discarded. It cannot: