gitnexus 1.6.3-rc.8 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (285) hide show
  1. package/README.md +21 -5
  2. package/dist/_shared/graph/types.d.ts +16 -0
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/index.d.ts +20 -2
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +11 -0
  7. package/dist/_shared/index.js.map +1 -1
  8. package/dist/_shared/scope-resolution/def-index.js +2 -2
  9. package/dist/_shared/scope-resolution/def-index.js.map +1 -1
  10. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts +8 -0
  11. package/dist/_shared/scope-resolution/method-dispatch-index.d.ts.map +1 -1
  12. package/dist/_shared/scope-resolution/method-dispatch-index.js +2 -2
  13. package/dist/_shared/scope-resolution/method-dispatch-index.js.map +1 -1
  14. package/dist/_shared/scope-resolution/module-scope-index.d.ts +8 -0
  15. package/dist/_shared/scope-resolution/module-scope-index.d.ts.map +1 -1
  16. package/dist/_shared/scope-resolution/module-scope-index.js +10 -2
  17. package/dist/_shared/scope-resolution/module-scope-index.js.map +1 -1
  18. package/dist/_shared/scope-resolution/parsed-file.d.ts +76 -0
  19. package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -0
  20. package/dist/_shared/scope-resolution/parsed-file.js +54 -0
  21. package/dist/_shared/scope-resolution/parsed-file.js.map +1 -0
  22. package/dist/_shared/scope-resolution/position-index.d.ts +12 -0
  23. package/dist/_shared/scope-resolution/position-index.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/position-index.js +2 -2
  25. package/dist/_shared/scope-resolution/position-index.js.map +1 -1
  26. package/dist/_shared/scope-resolution/qualified-name-index.js +2 -2
  27. package/dist/_shared/scope-resolution/qualified-name-index.js.map +1 -1
  28. package/dist/_shared/scope-resolution/reference-site.d.ts +75 -0
  29. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -0
  30. package/dist/_shared/scope-resolution/reference-site.js +24 -0
  31. package/dist/_shared/scope-resolution/reference-site.js.map +1 -0
  32. package/dist/_shared/scope-resolution/registries/class-registry.d.ts +27 -0
  33. package/dist/_shared/scope-resolution/registries/class-registry.d.ts.map +1 -0
  34. package/dist/_shared/scope-resolution/registries/class-registry.js +30 -0
  35. package/dist/_shared/scope-resolution/registries/class-registry.js.map +1 -0
  36. package/dist/_shared/scope-resolution/registries/context.d.ts +69 -0
  37. package/dist/_shared/scope-resolution/registries/context.d.ts.map +1 -0
  38. package/dist/_shared/scope-resolution/registries/context.js +44 -0
  39. package/dist/_shared/scope-resolution/registries/context.js.map +1 -0
  40. package/dist/_shared/scope-resolution/registries/evidence.d.ts +56 -0
  41. package/dist/_shared/scope-resolution/registries/evidence.d.ts.map +1 -0
  42. package/dist/_shared/scope-resolution/registries/evidence.js +150 -0
  43. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -0
  44. package/dist/_shared/scope-resolution/registries/field-registry.d.ts +26 -0
  45. package/dist/_shared/scope-resolution/registries/field-registry.d.ts.map +1 -0
  46. package/dist/_shared/scope-resolution/registries/field-registry.js +31 -0
  47. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -0
  48. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts +81 -0
  49. package/dist/_shared/scope-resolution/registries/lookup-core.d.ts.map +1 -0
  50. package/dist/_shared/scope-resolution/registries/lookup-core.js +332 -0
  51. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -0
  52. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts +33 -0
  53. package/dist/_shared/scope-resolution/registries/lookup-qualified.d.ts.map +1 -0
  54. package/dist/_shared/scope-resolution/registries/lookup-qualified.js +56 -0
  55. package/dist/_shared/scope-resolution/registries/lookup-qualified.js.map +1 -0
  56. package/dist/_shared/scope-resolution/registries/method-registry.d.ts +36 -0
  57. package/dist/_shared/scope-resolution/registries/method-registry.d.ts.map +1 -0
  58. package/dist/_shared/scope-resolution/registries/method-registry.js +32 -0
  59. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -0
  60. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts +43 -0
  61. package/dist/_shared/scope-resolution/registries/tie-breaks.d.ts.map +1 -0
  62. package/dist/_shared/scope-resolution/registries/tie-breaks.js +60 -0
  63. package/dist/_shared/scope-resolution/registries/tie-breaks.js.map +1 -0
  64. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
  65. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  66. package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
  67. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  68. package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
  69. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  70. package/dist/_shared/scope-resolution/scope-tree.js +3 -2
  71. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  72. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
  73. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
  74. package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
  75. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
  76. package/dist/_shared/scope-resolution/types.d.ts +11 -0
  77. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  78. package/dist/cli/ai-context.js +35 -4
  79. package/dist/cli/analyze.d.ts +27 -0
  80. package/dist/cli/analyze.js +31 -1
  81. package/dist/cli/clean.js +19 -1
  82. package/dist/cli/group.js +73 -0
  83. package/dist/cli/index-repo.js +8 -1
  84. package/dist/cli/index.js +26 -1
  85. package/dist/cli/list.js +11 -1
  86. package/dist/cli/remove.d.ts +30 -0
  87. package/dist/cli/remove.js +99 -0
  88. package/dist/cli/setup.js +185 -57
  89. package/dist/cli/tool.d.ts +5 -0
  90. package/dist/cli/tool.js +42 -0
  91. package/dist/config/ignore-service.d.ts +9 -0
  92. package/dist/config/ignore-service.js +80 -13
  93. package/dist/core/embedding-mode.d.ts +30 -0
  94. package/dist/core/embedding-mode.js +30 -0
  95. package/dist/core/embeddings/ast-utils.js +22 -22
  96. package/dist/core/embeddings/chunker.js +30 -25
  97. package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
  98. package/dist/core/embeddings/embedding-pipeline.js +15 -6
  99. package/dist/core/embeddings/text-generator.d.ts +1 -1
  100. package/dist/core/embeddings/text-generator.js +33 -24
  101. package/dist/core/embeddings/types.d.ts +43 -1
  102. package/dist/core/embeddings/types.js +101 -29
  103. package/dist/core/git-staleness.d.ts +18 -0
  104. package/dist/core/git-staleness.js +108 -0
  105. package/dist/core/graph/graph.js +115 -20
  106. package/dist/core/graph/types.d.ts +12 -1
  107. package/dist/core/group/config-parser.d.ts +4 -0
  108. package/dist/core/group/config-parser.js +18 -1
  109. package/dist/core/group/cross-impact.d.ts +41 -0
  110. package/dist/core/group/cross-impact.js +441 -0
  111. package/dist/core/group/extractors/http-patterns/php.js +126 -18
  112. package/dist/core/group/group-path-utils.d.ts +17 -0
  113. package/dist/core/group/group-path-utils.js +40 -0
  114. package/dist/core/group/resolve-at-member.d.ts +10 -0
  115. package/dist/core/group/resolve-at-member.js +31 -0
  116. package/dist/core/group/service.d.ts +9 -0
  117. package/dist/core/group/service.js +259 -25
  118. package/dist/core/group/types.d.ts +30 -0
  119. package/dist/core/ingestion/ast-cache.d.ts +16 -1
  120. package/dist/core/ingestion/ast-cache.js +14 -2
  121. package/dist/core/ingestion/call-processor.js +9 -0
  122. package/dist/core/ingestion/emit-references.d.ts +88 -0
  123. package/dist/core/ingestion/emit-references.js +229 -0
  124. package/dist/core/ingestion/filesystem-walker.js +6 -4
  125. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  126. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  127. package/dist/core/ingestion/framework-detection.js +6 -2
  128. package/dist/core/ingestion/import-processor.js +4 -0
  129. package/dist/core/ingestion/import-resolvers/python.js +9 -6
  130. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  131. package/dist/core/ingestion/import-target-adapter.js +95 -0
  132. package/dist/core/ingestion/language-provider.d.ts +36 -33
  133. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
  134. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
  135. package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
  136. package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
  137. package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
  138. package/dist/core/ingestion/languages/csharp/arity.js +37 -0
  139. package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
  140. package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
  141. package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
  142. package/dist/core/ingestion/languages/csharp/captures.js +249 -0
  143. package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
  144. package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
  145. package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
  146. package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
  147. package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
  148. package/dist/core/ingestion/languages/csharp/index.js +82 -0
  149. package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
  150. package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
  151. package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
  152. package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
  153. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
  154. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
  155. package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
  156. package/dist/core/ingestion/languages/csharp/query.js +515 -0
  157. package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
  158. package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
  159. package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
  160. package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
  161. package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
  162. package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
  163. package/dist/core/ingestion/languages/csharp.js +14 -0
  164. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
  165. package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
  166. package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
  167. package/dist/core/ingestion/languages/python/arity.js +38 -0
  168. package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
  169. package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
  170. package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
  171. package/dist/core/ingestion/languages/python/captures.js +106 -0
  172. package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
  173. package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
  174. package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
  175. package/dist/core/ingestion/languages/python/import-target.js +99 -0
  176. package/dist/core/ingestion/languages/python/index.d.ts +80 -0
  177. package/dist/core/ingestion/languages/python/index.js +80 -0
  178. package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
  179. package/dist/core/ingestion/languages/python/interpret.js +191 -0
  180. package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
  181. package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
  182. package/dist/core/ingestion/languages/python/query.d.ts +9 -0
  183. package/dist/core/ingestion/languages/python/query.js +267 -0
  184. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
  185. package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
  186. package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
  187. package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
  188. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
  189. package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
  190. package/dist/core/ingestion/languages/python.js +14 -0
  191. package/dist/core/ingestion/model/method-registry.d.ts +9 -0
  192. package/dist/core/ingestion/model/method-registry.js +4 -0
  193. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  194. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  195. package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
  196. package/dist/core/ingestion/model/semantic-model.js +55 -0
  197. package/dist/core/ingestion/mro-processor.js +38 -22
  198. package/dist/core/ingestion/parsing-processor.d.ts +18 -1
  199. package/dist/core/ingestion/parsing-processor.js +45 -11
  200. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  201. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  202. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
  203. package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
  204. package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
  205. package/dist/core/ingestion/pipeline.js +2 -1
  206. package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
  207. package/dist/core/ingestion/registry-primary-flag.js +111 -0
  208. package/dist/core/ingestion/resolve-references.d.ts +63 -0
  209. package/dist/core/ingestion/resolve-references.js +175 -0
  210. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  211. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  212. package/dist/core/ingestion/scope-extractor.d.ts +86 -0
  213. package/dist/core/ingestion/scope-extractor.js +758 -0
  214. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
  215. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
  216. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
  217. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
  218. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
  219. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
  220. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
  221. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
  222. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
  223. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
  224. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
  225. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
  226. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
  227. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
  228. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
  229. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
  230. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
  231. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
  232. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
  233. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
  234. package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
  235. package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
  236. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
  237. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
  238. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
  239. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
  240. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
  241. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
  242. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
  243. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
  244. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
  245. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
  246. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
  247. package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
  248. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
  249. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
  250. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
  251. package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
  252. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
  253. package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
  254. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  255. package/dist/core/ingestion/shadow-harness.js +148 -0
  256. package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
  257. package/dist/core/ingestion/utils/ast-helpers.js +70 -0
  258. package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
  259. package/dist/core/ingestion/utils/max-file-size.js +52 -0
  260. package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
  261. package/dist/core/ingestion/workers/parse-worker.js +57 -21
  262. package/dist/core/lbug/lbug-adapter.d.ts +22 -2
  263. package/dist/core/lbug/lbug-adapter.js +58 -14
  264. package/dist/core/lbug/pool-adapter.d.ts +17 -0
  265. package/dist/core/lbug/pool-adapter.js +24 -14
  266. package/dist/core/run-analyze.d.ts +32 -0
  267. package/dist/core/run-analyze.js +74 -19
  268. package/dist/core/search/bm25-index.d.ts +18 -0
  269. package/dist/core/search/bm25-index.js +125 -12
  270. package/dist/core/tree-sitter/parser-loader.js +6 -1
  271. package/dist/mcp/local/local-backend.d.ts +67 -3
  272. package/dist/mcp/local/local-backend.js +296 -34
  273. package/dist/mcp/resources.d.ts +31 -0
  274. package/dist/mcp/resources.js +100 -17
  275. package/dist/mcp/tools.d.ts +4 -1
  276. package/dist/mcp/tools.js +75 -54
  277. package/dist/server/api.js +6 -2
  278. package/dist/storage/git.d.ts +49 -0
  279. package/dist/storage/git.js +111 -0
  280. package/dist/storage/repo-manager.d.ts +246 -1
  281. package/dist/storage/repo-manager.js +391 -9
  282. package/package.json +7 -6
  283. package/scripts/bench-scope-resolution.ts +134 -0
  284. package/scripts/ci-list-migrated-languages.ts +24 -0
  285. package/skills/gitnexus-cli.md +1 -0
@@ -0,0 +1,372 @@
1
+ /**
2
+ * `ScopeResolver` — the per-language contract consumed by the generic
3
+ * scope-resolution orchestrator (`runScopeResolution`).
4
+ *
5
+ * ## Migration cookbook (next language)
6
+ *
7
+ * To add a language to the registry-primary path:
8
+ *
9
+ * 1. Implement `ScopeResolver` in
10
+ * `gitnexus/src/core/ingestion/languages/<lang>/scope-resolver.ts`.
11
+ * Nine required fields (language, languageProvider,
12
+ * importEdgeReason, resolveImportTarget, mergeBindings,
13
+ * arityCompatibility, buildMro, populateOwners, isSuperReceiver)
14
+ * plus optional toggles / hooks:
15
+ * - propagatesReturnTypesAcrossImports (default true)
16
+ * - fieldFallbackOnMethodLookup (default true — turn OFF for
17
+ * statically-typed languages; the heuristic over-connects)
18
+ * - unwrapCollectionAccessor — property-style collection views
19
+ * - collapseMemberCallsByCallerTarget — one edge per caller/target
20
+ * - populateNamespaceSiblings — cross-file implicit visibility
21
+ * - hoistTypeBindingsToModule — enable ONLY when method return
22
+ * types are stored on the enclosing Module scope; most
23
+ * languages attach them to the class scope and leave this off
24
+ * 2. Export a thin entry point:
25
+ * `runYourLangScopeResolution(input) = runScopeResolution(input, yourScopeResolver)`.
26
+ * 3. Register the provider in
27
+ * `gitnexus/src/core/ingestion/scope-resolution/pipeline/registry.ts`
28
+ * (the `SCOPE_RESOLVERS` map).
29
+ * 4. Add `SupportedLanguages.YourLang` to `MIGRATED_LANGUAGES` in
30
+ * `registry-primary-flag.ts`.
31
+ * 5. Verify the resolver integration test at
32
+ * `gitnexus/test/integration/resolvers/<lang>.test.ts` passes
33
+ * under both `REGISTRY_PRIMARY_<LANG>=0` (legacy) and `=1`
34
+ * (registry-primary). The CI parity gate enforces this.
35
+ *
36
+ * No new pipeline phase, no orchestrator copy-paste, no workflow
37
+ * change. The generic `scopeResolutionPhase` and the CI parity
38
+ * workflow auto-discover everything via `MIGRATED_LANGUAGES`.
39
+ *
40
+ * ## ScopeResolver vs LanguageProvider
41
+ *
42
+ * The codebase has two provider contracts. Their lifecycles differ:
43
+ *
44
+ * - `LanguageProvider` (`language-provider.ts`) is the
45
+ * **parsing-side** contract — how to emit captures, classify
46
+ * scopes, interpret imports / typeBindings. ~40 fields covering
47
+ * both legacy and new pipelines. Consumed by `ScopeExtractor`,
48
+ * once per file at extract time.
49
+ * - `ScopeResolver` (this file) is the **emit-side** contract — how
50
+ * the resolution pipeline dispatches references to graph edges.
51
+ * 8 fields total. Consumed by `runScopeResolution`, once per
52
+ * workspace at resolve time.
53
+ *
54
+ * They share three concept names (`arityCompatibility`, `mergeBindings`,
55
+ * `resolveImportTarget`) because the emit pipeline reuses a few
56
+ * finalize hooks. Per-language wiring passes the SAME function
57
+ * reference through both interfaces — no second copy of the logic.
58
+ * Rationale for not collapsing: lifecycle separation, and merging
59
+ * would create a god-interface complicating future migrations.
60
+ *
61
+ * ## Reference implementation
62
+ *
63
+ * `gitnexus/src/core/ingestion/languages/python/scope-resolver.ts` —
64
+ * `pythonScopeResolver` is the canonical example. Read that file when
65
+ * migrating a new language; this interface lists the fields that
66
+ * implementation populates.
67
+ *
68
+ * ## Contract Invariants the orchestrator depends on
69
+ *
70
+ * These are non-obvious behaviors that the orchestrator and the
71
+ * existing Python + C# resolvers depend on. Future implementers will
72
+ * break them silently if not documented.
73
+ *
74
+ * - **I1 — Phase 4 emission order is load-bearing.** `emitReceiverBoundCalls`
75
+ * runs FIRST (populates `handledSites`), then `emitFreeCallFallback`,
76
+ * then `emitReferencesViaLookup` (consumes `handledSites` as a skip
77
+ * set), then `emitImportEdges`. Reordering breaks same-name collision
78
+ * resolution: the shared lookup can mis-resolve `app_metrics.get_metrics()`
79
+ * to a same-named local function, and only the precise per-receiver
80
+ * pass running first prevents the wrong edge.
81
+ *
82
+ * - **I2 — `handledSites` semantics.** A site is added to
83
+ * `handledSites` IFF a `tryEmitEdge` call returned `true` for it.
84
+ * Sites a pass touched but couldn't resolve do NOT get marked —
85
+ * they still get a chance from the shared resolver. Exception:
86
+ * the free-call fallback marks the site unconditionally after
87
+ * attempting emission (even on dedup-collapse), because the
88
+ * per-(caller, target) collapse semantics require multiple call
89
+ * sites in the same caller body not produce multiple edges.
90
+ *
91
+ * - **I3 — `propagateImportedReturnTypes` mutation timing.** The
92
+ * pass mutates `Scope.typeBindings` (a plain `new Map(...)` from
93
+ * `draftToScope`, NOT frozen). It MUST run AFTER `finalizeScopeModel`
94
+ * (so `indexes.bindings` is populated) and BEFORE
95
+ * `resolveReferenceSites` (so resolution sees the propagated types).
96
+ * The pass also re-runs `followChainPostFinalize` on every scope's
97
+ * typeBindings because scope-extractor's pass-4 already ran and
98
+ * missed any chain whose terminal lives in a foreign file.
99
+ *
100
+ * - **I4 — `emitReceiverBoundCalls` case order.** Cases are evaluated
101
+ * in this order; the FIRST that emits an edge wins:
102
+ * 1. super branch (`provider.isSuperReceiver(receiverName)`)
103
+ * 2. Case 0 compound (`receiverName` has `.` or `(`)
104
+ * 3. Case 1 namespace-receiver
105
+ * 4. Case 2 class-name receiver
106
+ * 5. Case 3 dotted typeBinding for namespace prefix
107
+ * 6. Case 3b chain-typebinding (compound resolver)
108
+ * 7. Case 4 simple typeBinding (MRO walk + findOwnedMember)
109
+ * Reordering or merging cases changes resolution semantics. The
110
+ * numbering is part of the contract — keep the comments.
111
+ *
112
+ * - **I5 — Pre-seeding `seen` from `referenceIndex` is forbidden.**
113
+ * Earlier versions of the receiver-bound pass pre-populated `seen`
114
+ * to avoid double-emit. After Phase 4 was reordered, pre-seeding
115
+ * became actively harmful: it suppresses correct emissions for
116
+ * sites the shared resolver happened to resolve to a wrong target.
117
+ * The orchestrator MUST NOT pre-seed.
118
+ *
119
+ * - **I6 — `Scope.typeBindings` is mutable post-finalize.** `draftToScope`
120
+ * (in `scope-extractor.ts`) builds `typeBindings` as a plain
121
+ * `new Map(...)` — not frozen, intentionally. Passes below rely on
122
+ * this. Do NOT freeze `typeBindings` in any downstream refactor.
123
+ *
124
+ * - **I7 — `ScopeResolver` and `LanguageProvider` are distinct contracts.**
125
+ * Python and C# pass the SAME function reference through both
126
+ * interfaces where they share a hook name — no second copy of the
127
+ * logic. Rationale for not collapsing them: lifecycles differ
128
+ * (parsing-side runs once per file at extract time, emit-side runs
129
+ * once per workspace at resolve time), and merging would create a
130
+ * god-interface that complicates future migrations.
131
+ *
132
+ * - **I8 — Post-finalize hooks may mutate `Scope.typeBindings` and
133
+ * `indexes.bindings`.** `propagateImportedReturnTypes` and
134
+ * `populateNamespaceSiblings` both write to these structures via
135
+ * `as Map<...>` casts through `ReadonlyMap` facades. Downstream
136
+ * consumers MUST NOT freeze or snapshot these maps before all
137
+ * post-finalize hooks have run. The `ReadonlyMap<...>` type on
138
+ * `ScopeResolutionIndexes` is a read-guidance surface for
139
+ * consumers, NOT an immutability promise during the resolve phase.
140
+ *
141
+ * - **I9 — `SemanticModel` is the single authoritative symbol store.**
142
+ * Every symbol-indexed lookup (key = `nodeId | simpleName |
143
+ * qualifiedName | filePath`) resolves through
144
+ * `SemanticModel.{symbols,types,methods,fields}`. Scope-resolution
145
+ * passes MUST NOT maintain parallel owner-keyed or name-keyed
146
+ * symbol indexes — `WorkspaceResolutionIndex` is reserved for
147
+ * `Scope`-valued lookups that `SemanticModel` structurally cannot
148
+ * carry.
149
+ *
150
+ * The `runScopeResolution` orchestrator guarantees this invariant
151
+ * in two steps:
152
+ * 1. The legacy `parse` phase populates `SemanticModel` via
153
+ * `symbolTable.add(...)`. For languages whose extractor
154
+ * resolves `enclosingClassId` at parse time, class-body defs
155
+ * are correctly owner-keyed there.
156
+ * 2. The `reconcileOwnership` pass runs after
157
+ * `provider.populateOwners(parsed)` and registers any def in
158
+ * `parsed.localDefs[i]` with a corrected `ownerId` that the
159
+ * legacy pass missed (primarily Python class-body methods).
160
+ * Idempotent — duplicates are skipped by `nodeId`.
161
+ *
162
+ * Contract for consumers: `model` is `MutableSemanticModel` only
163
+ * during those two write phases. Downstream passes receive a
164
+ * narrowed `SemanticModel` (read-only) handle. This is enforced by
165
+ * `runScopeResolution`'s type-level narrowing at the phase
166
+ * boundary.
167
+ *
168
+ * The dev-mode runtime validator (`validateOwnershipParity`)
169
+ * surfaces any drift between `parsed.localDefs` ownership and the
170
+ * registries via `onWarn` when
171
+ * `NODE_ENV !== 'production' && VALIDATE_SEMANTIC_MODEL !== '0'`.
172
+ *
173
+ * This invariant is a **transitional shim**: the architectural
174
+ * end state is for every language's parse-time extractor to emit
175
+ * the correct `ownerId` directly, removing the need for
176
+ * reconciliation. Tracked as a follow-up; see ARCHITECTURE.md §
177
+ * "Semantic-model source of truth".
178
+ *
179
+ * ## Semantic-model source of truth
180
+ *
181
+ * `ParsedFile` (from `gitnexus-shared/src/scope-resolution/parsed-file.ts`)
182
+ * is the single semantic model consumed by both the legacy DAG and the
183
+ * scope-resolution pipeline. Scope-resolution passes MUST NOT build a
184
+ * parallel parse representation; if a pass needs AST-level facts that
185
+ * `ParsedFile` doesn't expose, it should reuse the orchestrator's
186
+ * `treeCache` (see `RunScopeResolutionInput.treeCache`) rather than
187
+ * re-invoke `parser.parse(...)` on its own.
188
+ *
189
+ * ## Same-graph guarantee
190
+ *
191
+ * Edges emitted by `runScopeResolution` and edges emitted by the legacy
192
+ * DAG are indistinguishable to downstream consumers:
193
+ * - Node identity: same `generateId(...)` helper, same qualified-name
194
+ * keyspace, same File/Folder/Method/Class node labels.
195
+ * - Edge vocabulary: `'import-resolved' | 'global' | 'local-call' |
196
+ * 'same-file' | 'interface-dispatch' | 'read' | 'write'` — both
197
+ * paths emit the same reasons (see
198
+ * `gitnexus/src/core/ingestion/call-processor.ts` for the legacy
199
+ * emitter and `passes/receiver-bound-calls.ts` /
200
+ * `passes/free-call-fallback.ts` for the scope-resolution emitters).
201
+ * - Overload disambiguation: both paths use
202
+ * `generateId('Method', ...)` suffixed with `parameterTypes` when a
203
+ * method has overloads — see `graph-bridge/ids.ts`.
204
+ *
205
+ * The CI parity workflow (`.github/workflows/ci-scope-parity.yml`)
206
+ * runs both paths on every migrated language's fixture corpus and
207
+ * fails if the graph outputs diverge.
208
+ *
209
+ * Plan that introduced most of these invariants:
210
+ * `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
211
+ */
212
+ import type { BindingRef, Callsite, ParsedFile, ScopeId, SupportedLanguages, SymbolDefinition } from '../../../../_shared/index.js';
213
+ import type { KnowledgeGraph } from '../../../graph/types.js';
214
+ import type { GraphNodeLookup } from '../graph-bridge/node-lookup.js';
215
+ import { LanguageProvider } from '../../language-provider.js';
216
+ import { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
217
+ /** A LinearizeStrategy receives the full ancestor map so C3-style
218
+ * algorithms (which need to merge each parent's MRO) can implement
219
+ * themselves. Python's depth-first first-seen only consumes
220
+ * `directParents` and `parentsByDefId`. */
221
+ export type LinearizeStrategy = (classDefId: string, directParents: readonly string[], parentsByDefId: ReadonlyMap<string, readonly string[]>) => string[];
222
+ /** Result of `ScopeResolver.arityCompatibility` — mirrors `RegistryProviders.arityCompatibility`. */
223
+ export type ArityVerdict = 'compatible' | 'unknown' | 'incompatible';
224
+ export interface ScopeResolver {
225
+ /** Identity for telemetry + per-language flag check. */
226
+ readonly language: SupportedLanguages;
227
+ /** Parsing-side hook bag consumed by `extractParsedFile`. The
228
+ * same `LanguageProvider` reference flows through both interfaces
229
+ * to keep parsing and emit semantics in sync. */
230
+ readonly languageProvider: LanguageProvider;
231
+ /** Reason text on emitted IMPORTS edges. Mirrors the legacy DAG's
232
+ * per-language convention so consumers asserting on reason keep
233
+ * working. */
234
+ readonly importEdgeReason: string;
235
+ /**
236
+ * Resolve an import statement's `targetRaw` (e.g. `models.user`,
237
+ * `./helpers`) into an absolute repo-relative file path, or `null`
238
+ * for unresolvable / external modules.
239
+ *
240
+ * Called once per `ParsedImport` during `finalizeScopeModel`. The
241
+ * Python implementation wraps `resolvePythonImportTarget`.
242
+ *
243
+ * `allFilePaths` is the workspace's file set — needed by per-language
244
+ * resolvers that must distinguish "this module exists in the repo"
245
+ * from "this module is external" (Python's fallback resolver, for
246
+ * example).
247
+ */
248
+ resolveImportTarget(targetRaw: string, fromFile: string, allFilePaths: ReadonlySet<string>): string | null;
249
+ /**
250
+ * Per-scope binding-merge precedence. The shared finalize pass
251
+ * collects bindings from multiple sources (local declarations,
252
+ * imports, namespace, wildcard, reexport) and asks the language
253
+ * how to order them.
254
+ *
255
+ * Python uses LEGB: local > import / namespace / reexport > wildcard.
256
+ */
257
+ mergeBindings(existing: readonly BindingRef[], incoming: readonly BindingRef[], scopeId: ScopeId): BindingRef[];
258
+ /**
259
+ * Per-language arity compatibility between a callsite and a
260
+ * candidate def. The shared `MethodRegistry.lookup` consults this
261
+ * to penalize incompatible candidates without disqualifying them
262
+ * outright. Note arg order — `(callsite, def)` matches the
263
+ * `RegistryProviders` contract; some legacy provider impls use
264
+ * `(def, callsite)` and need an adapter at the wiring site.
265
+ */
266
+ arityCompatibility(callsite: Callsite, def: SymbolDefinition): ArityVerdict;
267
+ /**
268
+ * Compute the method-dispatch order for every Class def in the
269
+ * workspace. Python uses depth-first first-seen via
270
+ * `pythonLinearize`; future languages may use C3 (Ruby, Python's
271
+ * real MRO when we go beyond the simplified walk), single-
272
+ * inheritance only (Java), or empty-map (languages without
273
+ * inheritance).
274
+ */
275
+ buildMro(graph: KnowledgeGraph, parsedFiles: readonly ParsedFile[], nodeLookup: GraphNodeLookup): Map<string, string[]>;
276
+ /**
277
+ * Mutate `parsed.localDefs[i].ownerId` to point at the structural
278
+ * owner. Python's rule: methods (Function defs whose parent scope
279
+ * is Class) AND class-body fields (defs in Class scopes) are owned
280
+ * by the enclosing class. Other languages may have richer rules
281
+ * (e.g., Java inner-class qualification).
282
+ */
283
+ populateOwners(parsed: ParsedFile): void;
284
+ /**
285
+ * Recognize a `super(...)`-style receiver text. Python returns
286
+ * `/^super\s*\(/.test(t)`. Java returns `t === 'super'`. C++ may
287
+ * also need `this` capture. Languages without inheritance return
288
+ * constant `false`.
289
+ */
290
+ isSuperReceiver(receiverText: string): boolean;
291
+ /**
292
+ * Whether the orchestrator should run `propagateImportedReturnTypes`
293
+ * after finalize. Default `true`. TypeScript with explicit type
294
+ * exports may want a different propagation strategy and opt out.
295
+ */
296
+ readonly propagatesReturnTypesAcrossImports?: boolean;
297
+ /**
298
+ * Whether the compound-receiver resolver should fall back to
299
+ * walking field types when method lookup on the receiver's class
300
+ * fails (the "Phase-9C unified fixpoint" heuristic). Default
301
+ * `true`. Strictly-typed languages should set `false` because the
302
+ * heuristic can produce edges that wouldn't survive a real type
303
+ * check.
304
+ */
305
+ readonly fieldFallbackOnMethodLookup?: boolean;
306
+ /**
307
+ * Unwrap a property-style collection accessor on a typed receiver
308
+ * to its element type. Called by `resolveCompoundReceiverClass`
309
+ * when walking dotted member-access chains of the form
310
+ * `receiver.Accessor`. The provider returns the element type's
311
+ * simple name, or `undefined` when the accessor doesn't unwrap —
312
+ * in which case the regular field-walk resumes.
313
+ *
314
+ * Use this only for languages that expose collection views as
315
+ * properties rather than method calls; languages whose collection
316
+ * views are `.values()` / `.keys()` method calls leave this
317
+ * undefined and let the normal call-expression branch handle them.
318
+ */
319
+ readonly unwrapCollectionAccessor?: (receiverType: string, accessor: string) => string | undefined;
320
+ /**
321
+ * Collapse member-call CALLS edges by `(caller, target)` rather
322
+ * than per-site. Default `false` — scope-resolution's contract
323
+ * invariant is per-site dedup.
324
+ *
325
+ * Enable this when the language's graph convention is one edge per
326
+ * caller/target pair regardless of how many syntactic sites exist,
327
+ * e.g. to match a legacy graph's edge count so downstream
328
+ * consumers don't see a migration-induced inflation.
329
+ */
330
+ readonly collapseMemberCallsByCallerTarget?: boolean;
331
+ /**
332
+ * Optional post-finalize hook to inject cross-file bindings that
333
+ * aren't modeled via explicit imports. Runs after
334
+ * `buildWorkspaceResolutionIndex` and before
335
+ * `propagateImportedReturnTypes`.
336
+ *
337
+ * Use this for languages where a compiler-implicit visibility rule
338
+ * makes names visible across files without a syntactic import —
339
+ * for example a shared-namespace convention where types declared
340
+ * in the same namespace see each other without a `using` / `import`
341
+ * statement. Languages that require explicit imports for cross-file
342
+ * visibility leave this undefined.
343
+ */
344
+ readonly populateNamespaceSiblings?: (parsedFiles: readonly ParsedFile[], indexes: ScopeResolutionIndexes, ctx: {
345
+ readonly fileContents: ReadonlyMap<string, string>;
346
+ /** Pre-parsed tree-sitter trees keyed by file path. Same cache
347
+ * the orchestrator hands to `extractParsedFile`; passing it
348
+ * through here lets per-language hooks read the AST without
349
+ * triggering a second parse. Cache miss = the hook re-parses
350
+ * itself; the cache is opt-in for hooks that need AST-level
351
+ * facts beyond what `ParsedFile` exposes. */
352
+ readonly treeCache?: {
353
+ get(filePath: string): unknown;
354
+ };
355
+ }) => void;
356
+ /**
357
+ * Whether the compound-receiver resolver should walk up from a
358
+ * class scope to ancestor (Module) scopes when looking up a
359
+ * method's return-type typeBinding. Default `false`.
360
+ *
361
+ * Set `true` only when the provider stores method return-type
362
+ * bindings on the enclosing Module scope rather than on the class
363
+ * scope. Without this walk-up, chain resolution fails for methods
364
+ * whose return types were hoisted to module scope.
365
+ *
366
+ * Providers that attach return-type bindings directly to the class
367
+ * scope leave this undefined — enabling the walk-up for them would
368
+ * add an unnecessary branch and risk picking up unrelated module-
369
+ * level bindings.
370
+ */
371
+ readonly hoistTypeBindingsToModule?: boolean;
372
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * `ScopeResolver` — the per-language contract consumed by the generic
3
+ * scope-resolution orchestrator (`runScopeResolution`).
4
+ *
5
+ * ## Migration cookbook (next language)
6
+ *
7
+ * To add a language to the registry-primary path:
8
+ *
9
+ * 1. Implement `ScopeResolver` in
10
+ * `gitnexus/src/core/ingestion/languages/<lang>/scope-resolver.ts`.
11
+ * Nine required fields (language, languageProvider,
12
+ * importEdgeReason, resolveImportTarget, mergeBindings,
13
+ * arityCompatibility, buildMro, populateOwners, isSuperReceiver)
14
+ * plus optional toggles / hooks:
15
+ * - propagatesReturnTypesAcrossImports (default true)
16
+ * - fieldFallbackOnMethodLookup (default true — turn OFF for
17
+ * statically-typed languages; the heuristic over-connects)
18
+ * - unwrapCollectionAccessor — property-style collection views
19
+ * - collapseMemberCallsByCallerTarget — one edge per caller/target
20
+ * - populateNamespaceSiblings — cross-file implicit visibility
21
+ * - hoistTypeBindingsToModule — enable ONLY when method return
22
+ * types are stored on the enclosing Module scope; most
23
+ * languages attach them to the class scope and leave this off
24
+ * 2. Export a thin entry point:
25
+ * `runYourLangScopeResolution(input) = runScopeResolution(input, yourScopeResolver)`.
26
+ * 3. Register the provider in
27
+ * `gitnexus/src/core/ingestion/scope-resolution/pipeline/registry.ts`
28
+ * (the `SCOPE_RESOLVERS` map).
29
+ * 4. Add `SupportedLanguages.YourLang` to `MIGRATED_LANGUAGES` in
30
+ * `registry-primary-flag.ts`.
31
+ * 5. Verify the resolver integration test at
32
+ * `gitnexus/test/integration/resolvers/<lang>.test.ts` passes
33
+ * under both `REGISTRY_PRIMARY_<LANG>=0` (legacy) and `=1`
34
+ * (registry-primary). The CI parity gate enforces this.
35
+ *
36
+ * No new pipeline phase, no orchestrator copy-paste, no workflow
37
+ * change. The generic `scopeResolutionPhase` and the CI parity
38
+ * workflow auto-discover everything via `MIGRATED_LANGUAGES`.
39
+ *
40
+ * ## ScopeResolver vs LanguageProvider
41
+ *
42
+ * The codebase has two provider contracts. Their lifecycles differ:
43
+ *
44
+ * - `LanguageProvider` (`language-provider.ts`) is the
45
+ * **parsing-side** contract — how to emit captures, classify
46
+ * scopes, interpret imports / typeBindings. ~40 fields covering
47
+ * both legacy and new pipelines. Consumed by `ScopeExtractor`,
48
+ * once per file at extract time.
49
+ * - `ScopeResolver` (this file) is the **emit-side** contract — how
50
+ * the resolution pipeline dispatches references to graph edges.
51
+ * 8 fields total. Consumed by `runScopeResolution`, once per
52
+ * workspace at resolve time.
53
+ *
54
+ * They share three concept names (`arityCompatibility`, `mergeBindings`,
55
+ * `resolveImportTarget`) because the emit pipeline reuses a few
56
+ * finalize hooks. Per-language wiring passes the SAME function
57
+ * reference through both interfaces — no second copy of the logic.
58
+ * Rationale for not collapsing: lifecycle separation, and merging
59
+ * would create a god-interface complicating future migrations.
60
+ *
61
+ * ## Reference implementation
62
+ *
63
+ * `gitnexus/src/core/ingestion/languages/python/scope-resolver.ts` —
64
+ * `pythonScopeResolver` is the canonical example. Read that file when
65
+ * migrating a new language; this interface lists the fields that
66
+ * implementation populates.
67
+ *
68
+ * ## Contract Invariants the orchestrator depends on
69
+ *
70
+ * These are non-obvious behaviors that the orchestrator and the
71
+ * existing Python + C# resolvers depend on. Future implementers will
72
+ * break them silently if not documented.
73
+ *
74
+ * - **I1 — Phase 4 emission order is load-bearing.** `emitReceiverBoundCalls`
75
+ * runs FIRST (populates `handledSites`), then `emitFreeCallFallback`,
76
+ * then `emitReferencesViaLookup` (consumes `handledSites` as a skip
77
+ * set), then `emitImportEdges`. Reordering breaks same-name collision
78
+ * resolution: the shared lookup can mis-resolve `app_metrics.get_metrics()`
79
+ * to a same-named local function, and only the precise per-receiver
80
+ * pass running first prevents the wrong edge.
81
+ *
82
+ * - **I2 — `handledSites` semantics.** A site is added to
83
+ * `handledSites` IFF a `tryEmitEdge` call returned `true` for it.
84
+ * Sites a pass touched but couldn't resolve do NOT get marked —
85
+ * they still get a chance from the shared resolver. Exception:
86
+ * the free-call fallback marks the site unconditionally after
87
+ * attempting emission (even on dedup-collapse), because the
88
+ * per-(caller, target) collapse semantics require multiple call
89
+ * sites in the same caller body not produce multiple edges.
90
+ *
91
+ * - **I3 — `propagateImportedReturnTypes` mutation timing.** The
92
+ * pass mutates `Scope.typeBindings` (a plain `new Map(...)` from
93
+ * `draftToScope`, NOT frozen). It MUST run AFTER `finalizeScopeModel`
94
+ * (so `indexes.bindings` is populated) and BEFORE
95
+ * `resolveReferenceSites` (so resolution sees the propagated types).
96
+ * The pass also re-runs `followChainPostFinalize` on every scope's
97
+ * typeBindings because scope-extractor's pass-4 already ran and
98
+ * missed any chain whose terminal lives in a foreign file.
99
+ *
100
+ * - **I4 — `emitReceiverBoundCalls` case order.** Cases are evaluated
101
+ * in this order; the FIRST that emits an edge wins:
102
+ * 1. super branch (`provider.isSuperReceiver(receiverName)`)
103
+ * 2. Case 0 compound (`receiverName` has `.` or `(`)
104
+ * 3. Case 1 namespace-receiver
105
+ * 4. Case 2 class-name receiver
106
+ * 5. Case 3 dotted typeBinding for namespace prefix
107
+ * 6. Case 3b chain-typebinding (compound resolver)
108
+ * 7. Case 4 simple typeBinding (MRO walk + findOwnedMember)
109
+ * Reordering or merging cases changes resolution semantics. The
110
+ * numbering is part of the contract — keep the comments.
111
+ *
112
+ * - **I5 — Pre-seeding `seen` from `referenceIndex` is forbidden.**
113
+ * Earlier versions of the receiver-bound pass pre-populated `seen`
114
+ * to avoid double-emit. After Phase 4 was reordered, pre-seeding
115
+ * became actively harmful: it suppresses correct emissions for
116
+ * sites the shared resolver happened to resolve to a wrong target.
117
+ * The orchestrator MUST NOT pre-seed.
118
+ *
119
+ * - **I6 — `Scope.typeBindings` is mutable post-finalize.** `draftToScope`
120
+ * (in `scope-extractor.ts`) builds `typeBindings` as a plain
121
+ * `new Map(...)` — not frozen, intentionally. Passes below rely on
122
+ * this. Do NOT freeze `typeBindings` in any downstream refactor.
123
+ *
124
+ * - **I7 — `ScopeResolver` and `LanguageProvider` are distinct contracts.**
125
+ * Python and C# pass the SAME function reference through both
126
+ * interfaces where they share a hook name — no second copy of the
127
+ * logic. Rationale for not collapsing them: lifecycles differ
128
+ * (parsing-side runs once per file at extract time, emit-side runs
129
+ * once per workspace at resolve time), and merging would create a
130
+ * god-interface that complicates future migrations.
131
+ *
132
+ * - **I8 — Post-finalize hooks may mutate `Scope.typeBindings` and
133
+ * `indexes.bindings`.** `propagateImportedReturnTypes` and
134
+ * `populateNamespaceSiblings` both write to these structures via
135
+ * `as Map<...>` casts through `ReadonlyMap` facades. Downstream
136
+ * consumers MUST NOT freeze or snapshot these maps before all
137
+ * post-finalize hooks have run. The `ReadonlyMap<...>` type on
138
+ * `ScopeResolutionIndexes` is a read-guidance surface for
139
+ * consumers, NOT an immutability promise during the resolve phase.
140
+ *
141
+ * - **I9 — `SemanticModel` is the single authoritative symbol store.**
142
+ * Every symbol-indexed lookup (key = `nodeId | simpleName |
143
+ * qualifiedName | filePath`) resolves through
144
+ * `SemanticModel.{symbols,types,methods,fields}`. Scope-resolution
145
+ * passes MUST NOT maintain parallel owner-keyed or name-keyed
146
+ * symbol indexes — `WorkspaceResolutionIndex` is reserved for
147
+ * `Scope`-valued lookups that `SemanticModel` structurally cannot
148
+ * carry.
149
+ *
150
+ * The `runScopeResolution` orchestrator guarantees this invariant
151
+ * in two steps:
152
+ * 1. The legacy `parse` phase populates `SemanticModel` via
153
+ * `symbolTable.add(...)`. For languages whose extractor
154
+ * resolves `enclosingClassId` at parse time, class-body defs
155
+ * are correctly owner-keyed there.
156
+ * 2. The `reconcileOwnership` pass runs after
157
+ * `provider.populateOwners(parsed)` and registers any def in
158
+ * `parsed.localDefs[i]` with a corrected `ownerId` that the
159
+ * legacy pass missed (primarily Python class-body methods).
160
+ * Idempotent — duplicates are skipped by `nodeId`.
161
+ *
162
+ * Contract for consumers: `model` is `MutableSemanticModel` only
163
+ * during those two write phases. Downstream passes receive a
164
+ * narrowed `SemanticModel` (read-only) handle. This is enforced by
165
+ * `runScopeResolution`'s type-level narrowing at the phase
166
+ * boundary.
167
+ *
168
+ * The dev-mode runtime validator (`validateOwnershipParity`)
169
+ * surfaces any drift between `parsed.localDefs` ownership and the
170
+ * registries via `onWarn` when
171
+ * `NODE_ENV !== 'production' && VALIDATE_SEMANTIC_MODEL !== '0'`.
172
+ *
173
+ * This invariant is a **transitional shim**: the architectural
174
+ * end state is for every language's parse-time extractor to emit
175
+ * the correct `ownerId` directly, removing the need for
176
+ * reconciliation. Tracked as a follow-up; see ARCHITECTURE.md §
177
+ * "Semantic-model source of truth".
178
+ *
179
+ * ## Semantic-model source of truth
180
+ *
181
+ * `ParsedFile` (from `gitnexus-shared/src/scope-resolution/parsed-file.ts`)
182
+ * is the single semantic model consumed by both the legacy DAG and the
183
+ * scope-resolution pipeline. Scope-resolution passes MUST NOT build a
184
+ * parallel parse representation; if a pass needs AST-level facts that
185
+ * `ParsedFile` doesn't expose, it should reuse the orchestrator's
186
+ * `treeCache` (see `RunScopeResolutionInput.treeCache`) rather than
187
+ * re-invoke `parser.parse(...)` on its own.
188
+ *
189
+ * ## Same-graph guarantee
190
+ *
191
+ * Edges emitted by `runScopeResolution` and edges emitted by the legacy
192
+ * DAG are indistinguishable to downstream consumers:
193
+ * - Node identity: same `generateId(...)` helper, same qualified-name
194
+ * keyspace, same File/Folder/Method/Class node labels.
195
+ * - Edge vocabulary: `'import-resolved' | 'global' | 'local-call' |
196
+ * 'same-file' | 'interface-dispatch' | 'read' | 'write'` — both
197
+ * paths emit the same reasons (see
198
+ * `gitnexus/src/core/ingestion/call-processor.ts` for the legacy
199
+ * emitter and `passes/receiver-bound-calls.ts` /
200
+ * `passes/free-call-fallback.ts` for the scope-resolution emitters).
201
+ * - Overload disambiguation: both paths use
202
+ * `generateId('Method', ...)` suffixed with `parameterTypes` when a
203
+ * method has overloads — see `graph-bridge/ids.ts`.
204
+ *
205
+ * The CI parity workflow (`.github/workflows/ci-scope-parity.yml`)
206
+ * runs both paths on every migrated language's fixture corpus and
207
+ * fails if the graph outputs diverge.
208
+ *
209
+ * Plan that introduced most of these invariants:
210
+ * `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
211
+ */
212
+ export {};
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Graph edge emission primitives.
3
+ *
4
+ * Two functions:
5
+ * - `mapReferenceKindToEdgeType` — translate a scope-resolution
6
+ * `Reference.kind` into the corresponding graph edge type.
7
+ * - `tryEmitEdge` — given a reference site + target def, resolve
8
+ * caller + target to graph ids and emit the edge with
9
+ * language-provided reason text, dedup-keyed by
10
+ * `(edgeType, callerId, targetId, line, col)`.
11
+ *
12
+ * Next-consumer contract: any language provider can call `tryEmitEdge`
13
+ * from its own post-pass to emit edges it resolves Python-specific
14
+ * (or TypeScript-specific, etc.) logic. The dedup key is
15
+ * language-agnostic — no language needs to change it.
16
+ */
17
+ import type { Reference, ScopeId, SymbolDefinition } from '../../../../_shared/index.js';
18
+ import type { KnowledgeGraph } from '../../../graph/types.js';
19
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
20
+ import type { GraphNodeLookup } from '../graph-bridge/node-lookup.js';
21
+ /**
22
+ * Map a `Reference.kind` to a graph edge type. `import-use` is dropped
23
+ * (no edge type today — provenance lives on the IMPORTS edge emitted
24
+ * by `emitImportEdges`).
25
+ */
26
+ export declare function mapReferenceKindToEdgeType(kind: Reference['kind']): 'CALLS' | 'ACCESSES' | 'EXTENDS' | 'USES' | undefined;
27
+ /**
28
+ * Resolve caller + target to graph ids and emit the edge. Returns true
29
+ * if the edge was emitted (not deduped, not skipped).
30
+ *
31
+ * `seen` is a language-shared dedup set keyed by
32
+ * `${edgeType}:${callerGraphId}->${targetGraphId}:${line}:${col}` so
33
+ * multiple language-specific post-passes can share it and never
34
+ * double-emit a resolution one of them already produced.
35
+ */
36
+ export declare function tryEmitEdge(graph: KnowledgeGraph, scopes: ScopeResolutionIndexes, nodeLookup: GraphNodeLookup, site: {
37
+ readonly inScope: ScopeId;
38
+ readonly atRange: {
39
+ startLine: number;
40
+ startCol: number;
41
+ };
42
+ readonly kind: string;
43
+ }, targetDef: SymbolDefinition, reason: string, seen: Set<string>, confidence?: number, collapseByCallerTarget?: boolean): boolean;