gitnexus 1.6.3-rc.9 → 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 (257) 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 +4 -2
  5. package/dist/_shared/index.d.ts.map +1 -1
  6. package/dist/_shared/index.js +2 -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/evidence.js +5 -0
  33. package/dist/_shared/scope-resolution/registries/evidence.js.map +1 -1
  34. package/dist/_shared/scope-resolution/registries/lookup-core.js +21 -5
  35. package/dist/_shared/scope-resolution/registries/lookup-core.js.map +1 -1
  36. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts +1 -10
  37. package/dist/_shared/scope-resolution/resolve-type-ref.d.ts.map +1 -1
  38. package/dist/_shared/scope-resolution/resolve-type-ref.js +6 -0
  39. package/dist/_shared/scope-resolution/resolve-type-ref.js.map +1 -1
  40. package/dist/_shared/scope-resolution/scope-tree.d.ts +4 -4
  41. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  42. package/dist/_shared/scope-resolution/scope-tree.js +3 -2
  43. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  44. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts +6 -2
  45. package/dist/_shared/scope-resolution/shadow/aggregate.d.ts.map +1 -1
  46. package/dist/_shared/scope-resolution/shadow/aggregate.js +5 -0
  47. package/dist/_shared/scope-resolution/shadow/aggregate.js.map +1 -1
  48. package/dist/_shared/scope-resolution/types.d.ts +11 -0
  49. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  50. package/dist/cli/ai-context.js +35 -4
  51. package/dist/cli/analyze.d.ts +27 -0
  52. package/dist/cli/analyze.js +31 -1
  53. package/dist/cli/clean.js +19 -1
  54. package/dist/cli/group.js +73 -0
  55. package/dist/cli/index-repo.js +8 -1
  56. package/dist/cli/index.js +26 -1
  57. package/dist/cli/list.js +11 -1
  58. package/dist/cli/remove.d.ts +30 -0
  59. package/dist/cli/remove.js +99 -0
  60. package/dist/cli/setup.js +185 -57
  61. package/dist/cli/tool.d.ts +5 -0
  62. package/dist/cli/tool.js +42 -0
  63. package/dist/config/ignore-service.d.ts +9 -0
  64. package/dist/config/ignore-service.js +80 -13
  65. package/dist/core/embedding-mode.d.ts +30 -0
  66. package/dist/core/embedding-mode.js +30 -0
  67. package/dist/core/embeddings/ast-utils.js +22 -22
  68. package/dist/core/embeddings/chunker.js +30 -25
  69. package/dist/core/embeddings/embedding-pipeline.d.ts +6 -0
  70. package/dist/core/embeddings/embedding-pipeline.js +15 -6
  71. package/dist/core/embeddings/text-generator.d.ts +1 -1
  72. package/dist/core/embeddings/text-generator.js +33 -24
  73. package/dist/core/embeddings/types.d.ts +43 -1
  74. package/dist/core/embeddings/types.js +101 -29
  75. package/dist/core/git-staleness.d.ts +18 -0
  76. package/dist/core/git-staleness.js +108 -0
  77. package/dist/core/graph/graph.js +115 -20
  78. package/dist/core/graph/types.d.ts +12 -1
  79. package/dist/core/group/config-parser.d.ts +4 -0
  80. package/dist/core/group/config-parser.js +18 -1
  81. package/dist/core/group/cross-impact.d.ts +41 -0
  82. package/dist/core/group/cross-impact.js +441 -0
  83. package/dist/core/group/extractors/http-patterns/php.js +126 -18
  84. package/dist/core/group/group-path-utils.d.ts +17 -0
  85. package/dist/core/group/group-path-utils.js +40 -0
  86. package/dist/core/group/resolve-at-member.d.ts +10 -0
  87. package/dist/core/group/resolve-at-member.js +31 -0
  88. package/dist/core/group/service.d.ts +9 -0
  89. package/dist/core/group/service.js +259 -25
  90. package/dist/core/group/types.d.ts +30 -0
  91. package/dist/core/ingestion/ast-cache.d.ts +16 -1
  92. package/dist/core/ingestion/ast-cache.js +14 -2
  93. package/dist/core/ingestion/call-processor.js +9 -0
  94. package/dist/core/ingestion/emit-references.d.ts +88 -0
  95. package/dist/core/ingestion/emit-references.js +229 -0
  96. package/dist/core/ingestion/filesystem-walker.js +6 -4
  97. package/dist/core/ingestion/finalize-orchestrator.d.ts +63 -0
  98. package/dist/core/ingestion/finalize-orchestrator.js +139 -0
  99. package/dist/core/ingestion/framework-detection.js +6 -2
  100. package/dist/core/ingestion/import-processor.js +4 -0
  101. package/dist/core/ingestion/import-resolvers/python.js +9 -6
  102. package/dist/core/ingestion/import-target-adapter.d.ts +73 -0
  103. package/dist/core/ingestion/import-target-adapter.js +95 -0
  104. package/dist/core/ingestion/language-provider.d.ts +36 -33
  105. package/dist/core/ingestion/languages/csharp/accessor-unwrap.d.ts +21 -0
  106. package/dist/core/ingestion/languages/csharp/accessor-unwrap.js +56 -0
  107. package/dist/core/ingestion/languages/csharp/arity-metadata.d.ts +26 -0
  108. package/dist/core/ingestion/languages/csharp/arity-metadata.js +46 -0
  109. package/dist/core/ingestion/languages/csharp/arity.d.ts +23 -0
  110. package/dist/core/ingestion/languages/csharp/arity.js +37 -0
  111. package/dist/core/ingestion/languages/csharp/cache-stats.d.ts +15 -0
  112. package/dist/core/ingestion/languages/csharp/cache-stats.js +26 -0
  113. package/dist/core/ingestion/languages/csharp/captures.d.ts +19 -0
  114. package/dist/core/ingestion/languages/csharp/captures.js +249 -0
  115. package/dist/core/ingestion/languages/csharp/import-decomposer.d.ts +19 -0
  116. package/dist/core/ingestion/languages/csharp/import-decomposer.js +93 -0
  117. package/dist/core/ingestion/languages/csharp/import-target.d.ts +25 -0
  118. package/dist/core/ingestion/languages/csharp/import-target.js +123 -0
  119. package/dist/core/ingestion/languages/csharp/index.d.ts +82 -0
  120. package/dist/core/ingestion/languages/csharp/index.js +82 -0
  121. package/dist/core/ingestion/languages/csharp/interpret.d.ts +15 -0
  122. package/dist/core/ingestion/languages/csharp/interpret.js +132 -0
  123. package/dist/core/ingestion/languages/csharp/merge-bindings.d.ts +27 -0
  124. package/dist/core/ingestion/languages/csharp/merge-bindings.js +55 -0
  125. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +50 -0
  126. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +374 -0
  127. package/dist/core/ingestion/languages/csharp/query.d.ts +35 -0
  128. package/dist/core/ingestion/languages/csharp/query.js +515 -0
  129. package/dist/core/ingestion/languages/csharp/receiver-binding.d.ts +31 -0
  130. package/dist/core/ingestion/languages/csharp/receiver-binding.js +135 -0
  131. package/dist/core/ingestion/languages/csharp/scope-resolver.d.ts +10 -0
  132. package/dist/core/ingestion/languages/csharp/scope-resolver.js +63 -0
  133. package/dist/core/ingestion/languages/csharp/simple-hooks.d.ts +53 -0
  134. package/dist/core/ingestion/languages/csharp/simple-hooks.js +76 -0
  135. package/dist/core/ingestion/languages/csharp.js +14 -0
  136. package/dist/core/ingestion/languages/python/arity-metadata.d.ts +24 -0
  137. package/dist/core/ingestion/languages/python/arity-metadata.js +45 -0
  138. package/dist/core/ingestion/languages/python/arity.d.ts +22 -0
  139. package/dist/core/ingestion/languages/python/arity.js +38 -0
  140. package/dist/core/ingestion/languages/python/cache-stats.d.ts +17 -0
  141. package/dist/core/ingestion/languages/python/cache-stats.js +28 -0
  142. package/dist/core/ingestion/languages/python/captures.d.ts +19 -0
  143. package/dist/core/ingestion/languages/python/captures.js +106 -0
  144. package/dist/core/ingestion/languages/python/import-decomposer.d.ts +15 -0
  145. package/dist/core/ingestion/languages/python/import-decomposer.js +112 -0
  146. package/dist/core/ingestion/languages/python/import-target.d.ts +21 -0
  147. package/dist/core/ingestion/languages/python/import-target.js +99 -0
  148. package/dist/core/ingestion/languages/python/index.d.ts +80 -0
  149. package/dist/core/ingestion/languages/python/index.js +80 -0
  150. package/dist/core/ingestion/languages/python/interpret.d.ts +15 -0
  151. package/dist/core/ingestion/languages/python/interpret.js +191 -0
  152. package/dist/core/ingestion/languages/python/merge-bindings.d.ts +16 -0
  153. package/dist/core/ingestion/languages/python/merge-bindings.js +44 -0
  154. package/dist/core/ingestion/languages/python/query.d.ts +9 -0
  155. package/dist/core/ingestion/languages/python/query.js +267 -0
  156. package/dist/core/ingestion/languages/python/receiver-binding.d.ts +21 -0
  157. package/dist/core/ingestion/languages/python/receiver-binding.js +116 -0
  158. package/dist/core/ingestion/languages/python/scope-resolver.d.ts +16 -0
  159. package/dist/core/ingestion/languages/python/scope-resolver.js +53 -0
  160. package/dist/core/ingestion/languages/python/simple-hooks.d.ts +23 -0
  161. package/dist/core/ingestion/languages/python/simple-hooks.js +35 -0
  162. package/dist/core/ingestion/languages/python.js +14 -0
  163. package/dist/core/ingestion/model/method-registry.d.ts +9 -0
  164. package/dist/core/ingestion/model/method-registry.js +4 -0
  165. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +59 -0
  166. package/dist/core/ingestion/model/scope-resolution-indexes.js +42 -0
  167. package/dist/core/ingestion/model/semantic-model.d.ts +64 -0
  168. package/dist/core/ingestion/model/semantic-model.js +55 -0
  169. package/dist/core/ingestion/mro-processor.js +38 -22
  170. package/dist/core/ingestion/parsing-processor.d.ts +18 -1
  171. package/dist/core/ingestion/parsing-processor.js +45 -11
  172. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  173. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  174. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +10 -0
  175. package/dist/core/ingestion/pipeline-phases/parse-impl.js +17 -2
  176. package/dist/core/ingestion/pipeline-phases/parse.d.ts +18 -0
  177. package/dist/core/ingestion/pipeline.js +2 -1
  178. package/dist/core/ingestion/registry-primary-flag.d.ts +86 -0
  179. package/dist/core/ingestion/registry-primary-flag.js +111 -0
  180. package/dist/core/ingestion/resolve-references.d.ts +63 -0
  181. package/dist/core/ingestion/resolve-references.js +175 -0
  182. package/dist/core/ingestion/scope-extractor-bridge.d.ts +32 -0
  183. package/dist/core/ingestion/scope-extractor-bridge.js +44 -0
  184. package/dist/core/ingestion/scope-extractor.d.ts +86 -0
  185. package/dist/core/ingestion/scope-extractor.js +758 -0
  186. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +372 -0
  187. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +212 -0
  188. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +43 -0
  189. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +79 -0
  190. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.d.ts +57 -0
  191. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +112 -0
  192. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.d.ts +17 -0
  193. package/dist/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +46 -0
  194. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.d.ts +19 -0
  195. package/dist/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +30 -0
  196. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.d.ts +37 -0
  197. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +113 -0
  198. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +38 -0
  199. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +73 -0
  200. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +42 -0
  201. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +198 -0
  202. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +27 -0
  203. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +131 -0
  204. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.d.ts +48 -0
  205. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +130 -0
  206. package/dist/core/ingestion/scope-resolution/passes/mro.d.ts +42 -0
  207. package/dist/core/ingestion/scope-resolution/passes/mro.js +99 -0
  208. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +26 -0
  209. package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +61 -0
  210. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +46 -0
  211. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +327 -0
  212. package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +47 -0
  213. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +130 -0
  214. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.d.ts +68 -0
  215. package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +125 -0
  216. package/dist/core/ingestion/scope-resolution/pipeline/registry.d.ts +17 -0
  217. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +21 -0
  218. package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +66 -0
  219. package/dist/core/ingestion/scope-resolution/pipeline/run.js +157 -0
  220. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.d.ts +36 -0
  221. package/dist/core/ingestion/scope-resolution/scope/namespace-targets.js +52 -0
  222. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +127 -0
  223. package/dist/core/ingestion/scope-resolution/scope/walkers.js +349 -0
  224. package/dist/core/ingestion/scope-resolution/workspace-index.d.ts +52 -0
  225. package/dist/core/ingestion/scope-resolution/workspace-index.js +61 -0
  226. package/dist/core/ingestion/shadow-harness.d.ts +113 -0
  227. package/dist/core/ingestion/shadow-harness.js +148 -0
  228. package/dist/core/ingestion/utils/ast-helpers.d.ts +19 -1
  229. package/dist/core/ingestion/utils/ast-helpers.js +70 -0
  230. package/dist/core/ingestion/utils/max-file-size.d.ts +20 -0
  231. package/dist/core/ingestion/utils/max-file-size.js +52 -0
  232. package/dist/core/ingestion/workers/parse-worker.d.ts +9 -0
  233. package/dist/core/ingestion/workers/parse-worker.js +57 -21
  234. package/dist/core/lbug/lbug-adapter.d.ts +22 -2
  235. package/dist/core/lbug/lbug-adapter.js +58 -14
  236. package/dist/core/lbug/pool-adapter.d.ts +17 -0
  237. package/dist/core/lbug/pool-adapter.js +24 -14
  238. package/dist/core/run-analyze.d.ts +32 -0
  239. package/dist/core/run-analyze.js +74 -19
  240. package/dist/core/search/bm25-index.d.ts +18 -0
  241. package/dist/core/search/bm25-index.js +125 -12
  242. package/dist/core/tree-sitter/parser-loader.js +6 -1
  243. package/dist/mcp/local/local-backend.d.ts +67 -3
  244. package/dist/mcp/local/local-backend.js +296 -34
  245. package/dist/mcp/resources.d.ts +31 -0
  246. package/dist/mcp/resources.js +100 -17
  247. package/dist/mcp/tools.d.ts +4 -1
  248. package/dist/mcp/tools.js +75 -54
  249. package/dist/server/api.js +6 -2
  250. package/dist/storage/git.d.ts +49 -0
  251. package/dist/storage/git.js +111 -0
  252. package/dist/storage/repo-manager.d.ts +246 -1
  253. package/dist/storage/repo-manager.js +391 -9
  254. package/package.json +7 -6
  255. package/scripts/bench-scope-resolution.ts +134 -0
  256. package/scripts/ci-list-migrated-languages.ts +24 -0
  257. package/skills/gitnexus-cli.md +1 -0
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Phase: scopeResolution
3
+ *
4
+ * Generic registry-primary resolution phase (RFC #909 Ring 3).
5
+ *
6
+ * For every language in `MIGRATED_LANGUAGES` (per-language flag set)
7
+ * whose provider is registered in `SCOPE_RESOLVERS`:
8
+ * 1. Filter scanned files by language extension.
9
+ * 2. Read file contents.
10
+ * 3. Drive the scope-based pipeline end-to-end via the generic
11
+ * `runScopeResolution(input, provider)` orchestrator.
12
+ * 4. Emit IMPORTS / CALLS / ACCESSES / INHERITS / USES edges.
13
+ *
14
+ * Pairs with the per-language gates in `import-processor.ts` and
15
+ * `call-processor.ts` that skip files when their language is registry-
16
+ * primary, so we don't double-emit edges from both code paths.
17
+ *
18
+ * Adding a language is two changes:
19
+ * - Implement `ScopeResolver` in `languages/<lang>/scope-resolver.ts`
20
+ * and register it in `scope-resolution/pipeline/registry.ts`.
21
+ * - Add the language to `MIGRATED_LANGUAGES` in
22
+ * `registry-primary-flag.ts`.
23
+ *
24
+ * @deps parse (needs Symbol nodes already in the graph so emit-references
25
+ * can attach edges to existing Function/Method/Class nodes)
26
+ * @reads scannedFiles
27
+ * @writes graph (IMPORTS, CALLS, ACCESSES, INHERITS, USES)
28
+ */
29
+ import { getPhaseOutput } from '../../pipeline-phases/types.js';
30
+ import { isRegistryPrimary } from '../../registry-primary-flag.js';
31
+ import { getLanguageFromFilename } from '../../../../_shared/index.js';
32
+ import { readFileContents } from '../../filesystem-walker.js';
33
+ import { runScopeResolution } from './run.js';
34
+ import { SCOPE_RESOLVERS } from './registry.js';
35
+ import { isDev } from '../../utils/env.js';
36
+ const NOOP_OUTPUT = Object.freeze({
37
+ ran: false,
38
+ filesProcessed: 0,
39
+ importsEmitted: 0,
40
+ referenceEdgesEmitted: 0,
41
+ perLanguage: new Map(),
42
+ });
43
+ export const scopeResolutionPhase = {
44
+ name: 'scopeResolution',
45
+ // Depends on `parse` because emit-references attaches edges to
46
+ // already-existing Symbol nodes (Function/Method/Class). The legacy
47
+ // `parse` phase still creates those nodes; we only replace the
48
+ // import + call resolution layer.
49
+ //
50
+ // Also depends on `crossFile` — we don't read crossFile's output
51
+ // directly (we have our own cross-file resolution), but crossFile
52
+ // writes EXTENDS edges that `buildMro` consumes via
53
+ // `iterRelationshipsByType('EXTENDS')`. Declaring the dep pins the
54
+ // ordering explicitly: without it, Kahn's runner could schedule
55
+ // scopeResolution before crossFile (both unblock after parse), and
56
+ // the MRO walk would miss heritage edges crossFile later adds.
57
+ deps: ['parse', 'crossFile', 'structure'],
58
+ async execute(ctx, deps) {
59
+ const { scannedFiles } = getPhaseOutput(deps, 'structure');
60
+ // Reach into the parse phase's AST cache so per-file extract can
61
+ // skip a second tree-sitter parse. Cache miss is safe (re-parses).
62
+ // Worker-mode parses leave the cache empty for those files; they
63
+ // also fall back to a fresh parse — no correctness impact.
64
+ const parseOutput = getPhaseOutput(deps, 'parse');
65
+ const { scopeTreeCache, resolutionContext } = parseOutput;
66
+ // SemanticModel populated during `parse`: scope-resolution consumes
67
+ // TypeRegistry / MethodRegistry / SymbolTable lookups instead of
68
+ // rebuilding parallel indexes. See ARCHITECTURE.md § "Semantic-model
69
+ // source of truth".
70
+ const model = resolutionContext.model;
71
+ let totalFiles = 0;
72
+ let totalImports = 0;
73
+ let totalRefs = 0;
74
+ let anyRan = false;
75
+ const perLanguage = new Map();
76
+ for (const [lang, provider] of SCOPE_RESOLVERS) {
77
+ if (!isRegistryPrimary(lang))
78
+ continue;
79
+ const langFiles = scannedFiles.filter((f) => getLanguageFromFilename(f.path) === lang);
80
+ if (langFiles.length === 0)
81
+ continue;
82
+ const filePaths = langFiles.map((f) => f.path);
83
+ const contents = await readFileContents(ctx.repoPath, filePaths);
84
+ const files = [];
85
+ for (const fp of filePaths) {
86
+ const content = contents.get(fp);
87
+ if (content !== undefined)
88
+ files.push({ path: fp, content });
89
+ }
90
+ const stats = runScopeResolution({
91
+ graph: ctx.graph,
92
+ model,
93
+ files,
94
+ treeCache: scopeTreeCache,
95
+ onWarn: (msg) => {
96
+ if (isDev)
97
+ console.warn(`[scope-resolution:${lang}] ${msg}`);
98
+ },
99
+ }, provider);
100
+ anyRan = true;
101
+ totalFiles += stats.filesProcessed;
102
+ totalImports += stats.importsEmitted;
103
+ totalRefs += stats.referenceEdgesEmitted;
104
+ perLanguage.set(lang, {
105
+ filesProcessed: stats.filesProcessed,
106
+ importsEmitted: stats.importsEmitted,
107
+ referenceEdgesEmitted: stats.referenceEdgesEmitted,
108
+ });
109
+ if (isDev) {
110
+ console.log(`[scope-resolution:${lang}] ${stats.filesProcessed} files → ${stats.importsEmitted} IMPORTS + ${stats.referenceEdgesEmitted} reference edges (${stats.resolve.unresolved} unresolved sites, ${stats.referenceSkipped} skipped)`);
111
+ }
112
+ }
113
+ // Dispose the cross-phase Tree cache — scope-resolution is the
114
+ // only consumer. Holding Trees past this point is pure memory
115
+ // pressure: downstream phases (mro, community, csv-generator)
116
+ // never read them, and tree-sitter Trees hold native-heap memory
117
+ // under WASM runtimes. ASTCache.clear() fires the LRU dispose
118
+ // handler which calls tree.delete?.() on each retained Tree.
119
+ scopeTreeCache.clear();
120
+ if (!anyRan)
121
+ return NOOP_OUTPUT;
122
+ return {
123
+ ran: true,
124
+ filesProcessed: totalFiles,
125
+ importsEmitted: totalImports,
126
+ referenceEdgesEmitted: totalRefs,
127
+ perLanguage,
128
+ };
129
+ },
130
+ };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Reconcile scope-resolution's ownership view into the SemanticModel.
3
+ *
4
+ * For migrated languages (Python in particular) the legacy `parse` phase
5
+ * emits class-body callables without `ownerId` because
6
+ * `parsing-processor`'s `resolveEnclosingOwner` is language-dependent and
7
+ * not every extractor carries the enclosing-class info at parse time.
8
+ * Scope-resolution later calls `provider.populateOwners(parsed)`, which
9
+ * stamps the correct `ownerId` onto `parsed.localDefs[i]`. This pass
10
+ * mirrors those corrections into `model.methods` and `model.fields` so
11
+ * downstream passes can consult `SemanticModel` as the single
12
+ * authoritative owner-keyed index — no parallel scope-resolution
13
+ * registry is needed.
14
+ *
15
+ * ## Single-source-of-truth invariant (I9)
16
+ *
17
+ * After this pass runs, every `def in parsed.localDefs` with a non-
18
+ * undefined `ownerId` is reachable via either:
19
+ * - `model.methods.lookupAllByOwner(ownerId, simpleName)` — if the
20
+ * def is a Method / Function / Constructor, OR
21
+ * - `model.fields.lookupFieldByOwner(ownerId, simpleName)` — if the
22
+ * def is a Property / Variable.
23
+ *
24
+ * This invariant is the foundation of Contract Invariant I9
25
+ * (`contract/scope-resolver.ts`): scope-resolution passes MUST read
26
+ * symbol-keyed lookups exclusively from `SemanticModel`.
27
+ *
28
+ * ## Idempotency
29
+ *
30
+ * The pass skips registration when `(ownerId, simpleName)` already
31
+ * contains a def with matching `nodeId`. Safe to call multiple times
32
+ * or after a language whose legacy extractor does populate `ownerId`
33
+ * (C#) — no duplicates are introduced.
34
+ *
35
+ * ## Transitional shim
36
+ *
37
+ * This reconciliation pass is an explicit shim. The architectural end
38
+ * state is for the legacy extractor to emit the correct `ownerId` for
39
+ * every language at parse time, removing the need for a second pass.
40
+ * See ARCHITECTURE.md § "Semantic-model source of truth" for the
41
+ * follow-up plan.
42
+ */
43
+ import type { ParsedFile } from '../../../../_shared/index.js';
44
+ import type { MutableSemanticModel, SemanticModel } from '../../model/semantic-model.js';
45
+ export interface ReconcileStats {
46
+ /** Method/Function/Constructor defs registered into MethodRegistry. */
47
+ readonly methodsRegistered: number;
48
+ /** Property/Variable defs registered into FieldRegistry. */
49
+ readonly fieldsRegistered: number;
50
+ /** Defs already present (idempotent skip). */
51
+ readonly skippedAlreadyPresent: number;
52
+ }
53
+ export declare function reconcileOwnership(parsedFiles: readonly ParsedFile[], model: MutableSemanticModel): ReconcileStats;
54
+ /**
55
+ * Debug-mode parity validator. Runs only when
56
+ * `VALIDATE_SEMANTIC_MODEL !== '0'` AND `NODE_ENV !== 'production'`.
57
+ *
58
+ * Iterates every def in `parsedFiles[i].localDefs` with an `ownerId`
59
+ * and asserts it is reachable via `model.methods.lookupAllByOwner` or
60
+ * `model.fields.lookupFieldByOwner`. On mismatch: emits a warning via
61
+ * `onWarn` — never throws, mirroring the pipeline's soft-fail posture.
62
+ *
63
+ * This is the enforcement of Contract Invariant I9 at runtime. In
64
+ * production it is a no-op; in development it surfaces drift between
65
+ * `parsed.localDefs` and `SemanticModel` that would otherwise silently
66
+ * produce wrong edges.
67
+ */
68
+ export declare function validateOwnershipParity(parsedFiles: readonly ParsedFile[], model: SemanticModel, onWarn: (message: string) => void): number;
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Reconcile scope-resolution's ownership view into the SemanticModel.
3
+ *
4
+ * For migrated languages (Python in particular) the legacy `parse` phase
5
+ * emits class-body callables without `ownerId` because
6
+ * `parsing-processor`'s `resolveEnclosingOwner` is language-dependent and
7
+ * not every extractor carries the enclosing-class info at parse time.
8
+ * Scope-resolution later calls `provider.populateOwners(parsed)`, which
9
+ * stamps the correct `ownerId` onto `parsed.localDefs[i]`. This pass
10
+ * mirrors those corrections into `model.methods` and `model.fields` so
11
+ * downstream passes can consult `SemanticModel` as the single
12
+ * authoritative owner-keyed index — no parallel scope-resolution
13
+ * registry is needed.
14
+ *
15
+ * ## Single-source-of-truth invariant (I9)
16
+ *
17
+ * After this pass runs, every `def in parsed.localDefs` with a non-
18
+ * undefined `ownerId` is reachable via either:
19
+ * - `model.methods.lookupAllByOwner(ownerId, simpleName)` — if the
20
+ * def is a Method / Function / Constructor, OR
21
+ * - `model.fields.lookupFieldByOwner(ownerId, simpleName)` — if the
22
+ * def is a Property / Variable.
23
+ *
24
+ * This invariant is the foundation of Contract Invariant I9
25
+ * (`contract/scope-resolver.ts`): scope-resolution passes MUST read
26
+ * symbol-keyed lookups exclusively from `SemanticModel`.
27
+ *
28
+ * ## Idempotency
29
+ *
30
+ * The pass skips registration when `(ownerId, simpleName)` already
31
+ * contains a def with matching `nodeId`. Safe to call multiple times
32
+ * or after a language whose legacy extractor does populate `ownerId`
33
+ * (C#) — no duplicates are introduced.
34
+ *
35
+ * ## Transitional shim
36
+ *
37
+ * This reconciliation pass is an explicit shim. The architectural end
38
+ * state is for the legacy extractor to emit the correct `ownerId` for
39
+ * every language at parse time, removing the need for a second pass.
40
+ * See ARCHITECTURE.md § "Semantic-model source of truth" for the
41
+ * follow-up plan.
42
+ */
43
+ import { simpleQualifiedName } from '../graph-bridge/ids.js';
44
+ export function reconcileOwnership(parsedFiles, model) {
45
+ let methodsRegistered = 0;
46
+ let fieldsRegistered = 0;
47
+ let skippedAlreadyPresent = 0;
48
+ for (const parsed of parsedFiles) {
49
+ for (const def of parsed.localDefs) {
50
+ const ownerId = def.ownerId;
51
+ if (ownerId === undefined)
52
+ continue;
53
+ const simple = simpleQualifiedName(def);
54
+ if (simple === undefined)
55
+ continue;
56
+ if (def.type === 'Method' || def.type === 'Function' || def.type === 'Constructor') {
57
+ const existing = model.methods.lookupAllByOwner(ownerId, simple);
58
+ if (existing.some((e) => e.nodeId === def.nodeId)) {
59
+ skippedAlreadyPresent++;
60
+ continue;
61
+ }
62
+ model.methods.register(ownerId, simple, def);
63
+ methodsRegistered++;
64
+ }
65
+ else if (def.type === 'Property' || def.type === 'Variable') {
66
+ const existing = model.fields.lookupFieldByOwner(ownerId, simple);
67
+ if (existing !== undefined && existing.nodeId === def.nodeId) {
68
+ skippedAlreadyPresent++;
69
+ continue;
70
+ }
71
+ model.fields.register(ownerId, simple, def);
72
+ fieldsRegistered++;
73
+ }
74
+ }
75
+ }
76
+ return { methodsRegistered, fieldsRegistered, skippedAlreadyPresent };
77
+ }
78
+ /**
79
+ * Debug-mode parity validator. Runs only when
80
+ * `VALIDATE_SEMANTIC_MODEL !== '0'` AND `NODE_ENV !== 'production'`.
81
+ *
82
+ * Iterates every def in `parsedFiles[i].localDefs` with an `ownerId`
83
+ * and asserts it is reachable via `model.methods.lookupAllByOwner` or
84
+ * `model.fields.lookupFieldByOwner`. On mismatch: emits a warning via
85
+ * `onWarn` — never throws, mirroring the pipeline's soft-fail posture.
86
+ *
87
+ * This is the enforcement of Contract Invariant I9 at runtime. In
88
+ * production it is a no-op; in development it surfaces drift between
89
+ * `parsed.localDefs` and `SemanticModel` that would otherwise silently
90
+ * produce wrong edges.
91
+ */
92
+ export function validateOwnershipParity(parsedFiles, model, onWarn) {
93
+ if (process.env.NODE_ENV === 'production')
94
+ return 0;
95
+ if (process.env.VALIDATE_SEMANTIC_MODEL === '0')
96
+ return 0;
97
+ let mismatches = 0;
98
+ for (const parsed of parsedFiles) {
99
+ for (const def of parsed.localDefs) {
100
+ const ownerId = def.ownerId;
101
+ if (ownerId === undefined)
102
+ continue;
103
+ const simple = simpleQualifiedName(def);
104
+ if (simple === undefined)
105
+ continue;
106
+ if (def.type === 'Method' || def.type === 'Function' || def.type === 'Constructor') {
107
+ const found = model.methods.lookupAllByOwner(ownerId, simple);
108
+ if (!found.some((d) => d.nodeId === def.nodeId)) {
109
+ onWarn(`semantic-model parity: ${def.type} ${def.nodeId} (${parsed.filePath}) ` +
110
+ `owned by ${ownerId} as "${simple}" not in MethodRegistry`);
111
+ mismatches++;
112
+ }
113
+ }
114
+ else if (def.type === 'Property' || def.type === 'Variable') {
115
+ const found = model.fields.lookupFieldByOwner(ownerId, simple);
116
+ if (found === undefined || found.nodeId !== def.nodeId) {
117
+ onWarn(`semantic-model parity: ${def.type} ${def.nodeId} (${parsed.filePath}) ` +
118
+ `owned by ${ownerId} as "${simple}" not in FieldRegistry`);
119
+ mismatches++;
120
+ }
121
+ }
122
+ }
123
+ }
124
+ return mismatches;
125
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Per-language `ScopeResolver` registry — the lookup the generic
3
+ * `scopeResolutionPhase` uses to pick the right resolver for each
4
+ * migrated language.
5
+ *
6
+ * Adding a language is two lines: implement a `ScopeResolver` in
7
+ * `languages/<lang>/scope-resolver.ts` and register it here. The
8
+ * phase picks it up automatically — no workflow changes, no
9
+ * per-language pipeline phase file.
10
+ */
11
+ import { SupportedLanguages } from '../../../../_shared/index.js';
12
+ import type { ScopeResolver } from '../contract/scope-resolver.js';
13
+ /** Map of `SupportedLanguages` → `ScopeResolver`. The phase iterates
14
+ * this map intersected with `MIGRATED_LANGUAGES` (the per-language
15
+ * flag set) so adding a resolver here without flipping the flag is
16
+ * safe — the resolver sits idle until the language is migrated. */
17
+ export declare const SCOPE_RESOLVERS: ReadonlyMap<SupportedLanguages, ScopeResolver>;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Per-language `ScopeResolver` registry — the lookup the generic
3
+ * `scopeResolutionPhase` uses to pick the right resolver for each
4
+ * migrated language.
5
+ *
6
+ * Adding a language is two lines: implement a `ScopeResolver` in
7
+ * `languages/<lang>/scope-resolver.ts` and register it here. The
8
+ * phase picks it up automatically — no workflow changes, no
9
+ * per-language pipeline phase file.
10
+ */
11
+ import { SupportedLanguages } from '../../../../_shared/index.js';
12
+ import { pythonScopeResolver } from '../../languages/python/scope-resolver.js';
13
+ import { csharpScopeResolver } from '../../languages/csharp/scope-resolver.js';
14
+ /** Map of `SupportedLanguages` → `ScopeResolver`. The phase iterates
15
+ * this map intersected with `MIGRATED_LANGUAGES` (the per-language
16
+ * flag set) so adding a resolver here without flipping the flag is
17
+ * safe — the resolver sits idle until the language is migrated. */
18
+ export const SCOPE_RESOLVERS = new Map([
19
+ [SupportedLanguages.Python, pythonScopeResolver],
20
+ [SupportedLanguages.CSharp, csharpScopeResolver],
21
+ ]);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * `runScopeResolution` — generic registry-primary resolution
3
+ * orchestrator.
4
+ *
5
+ * ParsedFile[] (one per file via `extractParsedFile`)
6
+ * │ finalizeScopeModel( + provider hooks adapted to FinalizeHooks)
7
+ * ▼
8
+ * ScopeResolutionIndexes
9
+ * │ resolveReferenceSites
10
+ * ▼
11
+ * ReferenceIndex
12
+ * │ emitReceiverBoundCalls (FIRST — see Contract Invariant I1)
13
+ * │ emitFreeCallFallback (THEN)
14
+ * │ emitReferencesViaLookup (LAST — uses handledSites)
15
+ * │ emitImportEdges
16
+ * ▼
17
+ * KnowledgeGraph
18
+ *
19
+ * Per-language entry points (e.g. `runPythonScopeResolution` in
20
+ * `languages/python/scope-resolver.ts`) construct an `ScopeResolver` and
21
+ * delegate here.
22
+ *
23
+ * Plan: `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
24
+ */
25
+ import type { KnowledgeGraph } from '../../../graph/types.js';
26
+ import type { MutableSemanticModel } from '../../model/semantic-model.js';
27
+ import { type ResolveStats } from '../../resolve-references.js';
28
+ import type { ScopeResolver } from '../contract/scope-resolver.js';
29
+ interface RunScopeResolutionInput {
30
+ readonly graph: KnowledgeGraph;
31
+ /**
32
+ * Semantic model populated by the legacy `parse` phase. Scope-
33
+ * resolution consumes its `TypeRegistry` / `MethodRegistry` /
34
+ * `SymbolTable` lookups instead of rebuilding parallel indexes from
35
+ * `ParsedFile[]`. See ARCHITECTURE.md § "Semantic-model source of
36
+ * truth". Tests that invoke `runScopeResolution` in isolation pass a
37
+ * freshly-created `MutableSemanticModel` populated from the same
38
+ * `ParsedFile[]` to mirror the pipeline shape.
39
+ */
40
+ readonly model: MutableSemanticModel;
41
+ readonly files: readonly {
42
+ readonly path: string;
43
+ readonly content: string;
44
+ }[];
45
+ readonly onWarn?: (message: string) => void;
46
+ /**
47
+ * Optional pre-parsed-Tree lookup keyed by file path. When the
48
+ * pipeline's parse phase ran sequentially, it populated an
49
+ * `ASTCache`; passing that here lets the per-file extract step
50
+ * skip a second `tree-sitter parser.parse(...)` call. Cache miss
51
+ * is safe — falls back to a fresh parse inside the provider.
52
+ */
53
+ readonly treeCache?: {
54
+ get(filePath: string): unknown;
55
+ };
56
+ }
57
+ interface RunScopeResolutionStats {
58
+ readonly filesProcessed: number;
59
+ readonly filesSkipped: number;
60
+ readonly importsEmitted: number;
61
+ readonly resolve: ResolveStats;
62
+ readonly referenceEdgesEmitted: number;
63
+ readonly referenceSkipped: number;
64
+ }
65
+ export declare function runScopeResolution(input: RunScopeResolutionInput, provider: ScopeResolver): RunScopeResolutionStats;
66
+ export {};
@@ -0,0 +1,157 @@
1
+ /**
2
+ * `runScopeResolution` — generic registry-primary resolution
3
+ * orchestrator.
4
+ *
5
+ * ParsedFile[] (one per file via `extractParsedFile`)
6
+ * │ finalizeScopeModel( + provider hooks adapted to FinalizeHooks)
7
+ * ▼
8
+ * ScopeResolutionIndexes
9
+ * │ resolveReferenceSites
10
+ * ▼
11
+ * ReferenceIndex
12
+ * │ emitReceiverBoundCalls (FIRST — see Contract Invariant I1)
13
+ * │ emitFreeCallFallback (THEN)
14
+ * │ emitReferencesViaLookup (LAST — uses handledSites)
15
+ * │ emitImportEdges
16
+ * ▼
17
+ * KnowledgeGraph
18
+ *
19
+ * Per-language entry points (e.g. `runPythonScopeResolution` in
20
+ * `languages/python/scope-resolver.ts`) construct an `ScopeResolver` and
21
+ * delegate here.
22
+ *
23
+ * Plan: `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
24
+ */
25
+ import { reconcileOwnership, validateOwnershipParity } from './reconcile-ownership.js';
26
+ import { extractParsedFile } from '../../scope-extractor-bridge.js';
27
+ import { finalizeScopeModel } from '../../finalize-orchestrator.js';
28
+ import { resolveReferenceSites } from '../../resolve-references.js';
29
+ import { buildGraphNodeLookup } from '../graph-bridge/node-lookup.js';
30
+ import { buildPopulatedMethodDispatch } from '../graph-bridge/method-dispatch.js';
31
+ import { propagateImportedReturnTypes } from '../passes/imported-return-types.js';
32
+ import { emitReceiverBoundCalls } from '../passes/receiver-bound-calls.js';
33
+ import { emitFreeCallFallback } from '../passes/free-call-fallback.js';
34
+ import { emitReferencesViaLookup } from '../graph-bridge/references-to-edges.js';
35
+ import { emitImportEdges } from '../graph-bridge/imports-to-edges.js';
36
+ import { buildWorkspaceResolutionIndex } from '../workspace-index.js';
37
+ export function runScopeResolution(input, provider) {
38
+ const { graph, files } = input;
39
+ const onWarn = input.onWarn ?? (() => { });
40
+ const PROF = process.env.PROF_SCOPE_RESOLUTION === '1';
41
+ const tStart = PROF ? process.hrtime.bigint() : 0n;
42
+ // ── Phase 1: extract each file → ParsedFile ────────────────────────────
43
+ const parsedFiles = [];
44
+ let filesSkipped = 0;
45
+ const treeCache = input.treeCache;
46
+ for (const file of files) {
47
+ const cachedTree = treeCache?.get(file.path);
48
+ const parsed = extractParsedFile(provider.languageProvider, file.content, file.path, onWarn, cachedTree);
49
+ if (parsed === undefined) {
50
+ filesSkipped++;
51
+ continue;
52
+ }
53
+ provider.populateOwners(parsed);
54
+ parsedFiles.push(parsed);
55
+ }
56
+ // Reconcile scope-resolution's ownership view into the SemanticModel.
57
+ // See `reconcile-ownership.ts` for the full rationale (Contract
58
+ // Invariant I9). Debug-mode validator runs immediately after to
59
+ // catch drift between `parsed.localDefs` and the registries.
60
+ //
61
+ // PHASE BOUNDARY: `input.model` is `MutableSemanticModel` up to this
62
+ // point (write phase: reconciliation). After this line no further
63
+ // writes are expected — downstream passes consume `readonlyModel`
64
+ // (narrowed to `SemanticModel`) so accidental writes would surface
65
+ // as type errors.
66
+ reconcileOwnership(parsedFiles, input.model);
67
+ validateOwnershipParity(parsedFiles, input.model, onWarn);
68
+ const readonlyModel = input.model;
69
+ if (parsedFiles.length === 0) {
70
+ return {
71
+ filesProcessed: 0,
72
+ filesSkipped,
73
+ importsEmitted: 0,
74
+ resolve: { sitesProcessed: 0, referencesEmitted: 0, unresolved: 0 },
75
+ referenceEdgesEmitted: 0,
76
+ referenceSkipped: 0,
77
+ };
78
+ }
79
+ const tExtract = PROF ? process.hrtime.bigint() : 0n;
80
+ // ── Phase 2: finalize → ScopeResolutionIndexes ─────────────────────────
81
+ const allFilePaths = new Set(parsedFiles.map((f) => f.filePath));
82
+ const nodeLookup = buildGraphNodeLookup(graph);
83
+ const mroByClassDefId = provider.buildMro(graph, parsedFiles, nodeLookup);
84
+ const finalized = finalizeScopeModel(parsedFiles, {
85
+ hooks: {
86
+ resolveImportTarget: (targetRaw, fromFile) => provider.resolveImportTarget(targetRaw, fromFile, allFilePaths),
87
+ mergeBindings: (existing, incoming, scopeId) => provider.mergeBindings(existing, incoming, scopeId),
88
+ },
89
+ });
90
+ // Replace the empty MethodDispatchIndex that finalizeScopeModel
91
+ // builds by design with the populated one derived from the
92
+ // language's MRO. Spread produces a fresh `ScopeResolutionIndexes`
93
+ // instead of mutating the finalized result through an `as` cast —
94
+ // downstream passes get an object whose readonly guarantees match
95
+ // the type system.
96
+ const indexes = {
97
+ ...finalized,
98
+ methodDispatch: buildPopulatedMethodDispatch(mroByClassDefId),
99
+ };
100
+ // Build the workspace resolution index ONCE — scope-valued lookups
101
+ // (`classScopeByDefId`, `moduleScopeByFile`) that `SemanticModel`
102
+ // cannot carry. Must run AFTER `populateOwners` (so owned defs are
103
+ // attributed correctly) and AFTER finalize (so module-scope
104
+ // bindings are available).
105
+ const workspaceIndex = buildWorkspaceResolutionIndex(parsedFiles);
106
+ // Cross-file implicit-namespace visibility (C#). Must run before
107
+ // propagateImportedReturnTypes so the latter pass sees siblings'
108
+ // class bindings when chasing return-type chains across files.
109
+ if (provider.populateNamespaceSiblings !== undefined) {
110
+ const fileContents = new Map();
111
+ for (const f of files)
112
+ fileContents.set(f.path, f.content);
113
+ provider.populateNamespaceSiblings(parsedFiles, indexes, {
114
+ fileContents,
115
+ treeCache,
116
+ });
117
+ }
118
+ // Cross-file return-type propagation (Contract Invariant I3 timing:
119
+ // after finalize, before resolve).
120
+ if (provider.propagatesReturnTypesAcrossImports !== false) {
121
+ propagateImportedReturnTypes(parsedFiles, indexes, workspaceIndex);
122
+ }
123
+ const tFinalize = PROF ? process.hrtime.bigint() : 0n;
124
+ // ── Phase 3: resolve references via Registry.lookup ────────────────────
125
+ const registryProviders = {
126
+ arityCompatibility: provider.arityCompatibility,
127
+ };
128
+ const { referenceIndex, stats: resolveStats } = resolveReferenceSites({
129
+ scopes: indexes,
130
+ providers: registryProviders,
131
+ });
132
+ const tResolve = PROF ? process.hrtime.bigint() : 0n;
133
+ // ── Phase 4: emit graph edges (LOAD-BEARING ORDER — see I1) ────────────
134
+ const handledSites = new Set();
135
+ const receiverExtras = emitReceiverBoundCalls(graph, indexes, parsedFiles, nodeLookup, handledSites, provider, workspaceIndex, readonlyModel);
136
+ const freeCallExtras = emitFreeCallFallback(graph, indexes, parsedFiles, nodeLookup, referenceIndex, handledSites, readonlyModel, workspaceIndex);
137
+ const { emitted, skipped } = emitReferencesViaLookup(graph, indexes, referenceIndex, nodeLookup, handledSites);
138
+ const importsEmitted = emitImportEdges(graph, indexes.imports, indexes.scopeTree, provider.importEdgeReason);
139
+ if (PROF) {
140
+ const tEnd = process.hrtime.bigint();
141
+ const ns = (a, b) => Number(b - a) / 1_000_000;
142
+ console.warn(`[scope-resolution prof] extract=${ns(tStart, tExtract).toFixed(0)}ms` +
143
+ ` finalize+propagate=${ns(tExtract, tFinalize).toFixed(0)}ms` +
144
+ ` resolve=${ns(tFinalize, tResolve).toFixed(0)}ms` +
145
+ ` emit=${ns(tResolve, tEnd).toFixed(0)}ms` +
146
+ ` total=${ns(tStart, tEnd).toFixed(0)}ms` +
147
+ ` (${parsedFiles.length} files)`);
148
+ }
149
+ return {
150
+ filesProcessed: parsedFiles.length,
151
+ filesSkipped,
152
+ importsEmitted,
153
+ resolve: resolveStats,
154
+ referenceEdgesEmitted: emitted + receiverExtras + freeCallExtras,
155
+ referenceSkipped: skipped,
156
+ };
157
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Build a per-file `localName → targetFilePath` map over the file's
3
+ * module-scope namespace-kind import edges.
4
+ *
5
+ * Namespace imports (`import X`, `import X as Y`) bind a name that can
6
+ * appear as a receiver in member calls (`X.foo()`, `Y.foo()`). Named
7
+ * imports (`from X import foo`) bind `foo` directly and are a different
8
+ * resolution path.
9
+ *
10
+ * Why not consult `scope.bindings` directly? For namespace imports
11
+ * where the target module has no self-named def,
12
+ * `finalize-algorithm.ts:540` skips binding creation entirely, so
13
+ * `scope.bindings.get('X')` returns undefined. We iterate
14
+ * `indexes.imports` to recover those targets.
15
+ *
16
+ * Next-consumer contract: any language with namespace-style imports
17
+ * (TypeScript `import * as X`, Java static import, Ruby `require`)
18
+ * uses this directly. `ParsedImport.kind === 'namespace'` is the
19
+ * cross-language hook.
20
+ *
21
+ * Scope-chain concern (verified 2026-04-21): `pythonImportOwningScope`
22
+ * documents that function-local and class-body imports bind to the
23
+ * inner scope, which would make a module-only read incomplete. In
24
+ * practice `finalize-algorithm` places ALL of a file's ImportEdges
25
+ * onto `indexes.imports[moduleScope]` regardless of where the
26
+ * `import` statement appears — the integration fixtures
27
+ * `python-function-local-namespace-import` and
28
+ * `python-class-body-namespace-import` both emit correct CALLS edges
29
+ * with reason "namespace-receiver", demonstrating that the module-
30
+ * scope read is sufficient today. If finalize routing ever changes to
31
+ * honor the hook's per-scope contract, this function must walk the
32
+ * reference-site scope chain (mirror `findExportedDefByName`).
33
+ */
34
+ import type { ParsedFile } from '../../../../_shared/index.js';
35
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
36
+ export declare function collectNamespaceTargets(parsed: ParsedFile, scopes: ScopeResolutionIndexes): Map<string, string>;