/** * `runScopeResolution` — generic registry-primary resolution * orchestrator. * * ParsedFile[] (one per file via `extractParsedFile`) * │ finalizeScopeModel( + provider hooks adapted to FinalizeHooks) * ▼ * ScopeResolutionIndexes * │ resolveReferenceSites * ▼ * ReferenceIndex * │ emitReceiverBoundCalls (FIRST — see Contract Invariant I1) * │ emitFreeCallFallback (THEN) * │ emitReferencesViaLookup (LAST — uses handledSites) * │ emitImportEdges * ▼ * KnowledgeGraph * * Per-language entry points (e.g. `runPythonScopeResolution` in * `languages/python/scope-resolver.ts`) construct an `ScopeResolver` and * delegate here. * * Plan: `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`. */ import type { ParsedFile } from '../../../../_shared/index.js'; import type { KnowledgeGraph } from '../../../graph/types.js'; import type { MutableSemanticModel } from '../../model/semantic-model.js'; import { type ResolveStats } from '../../resolve-references.js'; import { buildGraphNodeLookup } from '../graph-bridge/node-lookup.js'; import type { ScopeResolver } from '../contract/scope-resolver.js'; import type { ResolutionOutcome, ResolutionOutcomeRecorder } from '../resolution-outcome.js'; export type ScopeResolutionSubPhase = 'extracting' | 'analyzing types' | 'resolving references' | 'linking symbols'; interface RunScopeResolutionInput { readonly graph: KnowledgeGraph; /** * Semantic model populated by the legacy `parse` phase. Scope- * resolution consumes its `TypeRegistry` / `MethodRegistry` / * `SymbolTable` lookups instead of rebuilding parallel indexes from * `ParsedFile[]`. See ARCHITECTURE.md § "Semantic-model source of * truth". Tests that invoke `runScopeResolution` in isolation pass a * freshly-created `MutableSemanticModel` populated from the same * `ParsedFile[]` to mirror the pipeline shape. */ readonly model: MutableSemanticModel; readonly files: readonly { readonly path: string; readonly content: string; }[]; readonly onWarn?: (message: string) => void; /** * Optional pre-parsed-Tree lookup keyed by file path: a cache hit lets the * per-file extract step skip a second `tree-sitter parser.parse(...)` call. * Currently always empty — the only producer was the (removed) sequential * parser, and workers can't return native Trees across the MessageChannel, * so the parse phase no longer threads one. Kept as an extension point; * cache miss is safe (the provider re-parses). */ readonly treeCache?: { get(filePath: string): unknown; }; /** * Optional graph-node lookup built ONCE by the caller and shared across * every language pass. `buildGraphNodeLookup` scans the whole graph and is * language-agnostic, so rebuilding it per language wastes both CPU and ~GBs * of heap (on the kernel it is ~2 GB; a 5-file language would otherwise build * its own full copy that then overlaps the next language's). When omitted * (tests / isolated calls) the lookup is built locally as before. Providers * that add graph nodes mid-pass (e.g. Ruby heritage Property nodes) still * rebuild a fresh post-heritage lookup internally, so sharing the pre-loop * base is safe. */ readonly prebuiltNodeLookup?: ReturnType; /** * Opaque per-language import-resolution config (e.g. tsconfig path * aliases for TypeScript). Loaded once by the caller via * `provider.loadResolutionConfig(repoPath)` and threaded into every * `provider.resolveImportTarget` call. `undefined` when the * provider doesn't supply a config loader. */ readonly resolutionConfig?: unknown; /** * Pre-extracted ParsedFile artifacts keyed by file path. When a * file is present here, the extract loop reuses it directly and * skips `extractParsedFile` (which would re-parse the file with * tree-sitter on the main thread). Only files matching the * provider's language are honored — the loop verifies this * implicitly by language filter at the call-site (scopeResolution * phase). * * Worker-mode parses produce these ParsedFile artifacts as a side * effect of `extractParsedFile` running inside the worker; threading * them here is what lets the warm-cache analyze run skip the ~58s * scope-resolution re-parse loop on a multi-thousand-file repo. * Cache miss is safe — falls back to fresh extract. */ readonly preExtractedParsedFiles?: ReadonlyMap; /** * Out-of-core scope index (disk-backed scope seal). When set AND `GITNEXUS_DISK_SCOPE_INDEX` is * enabled, the per-language `scopeTree` is sealed to a disk-backed store at * this path after resolve (before emit), and the heavy `Scope.bindings` * payload is dropped from heap — lowering the per-language peak (kernel: * ~20→~12 GB) so the analysis fits on smaller-RAM machines. Same storage path * as the ParsedFile store; a sibling `scope-index-store/` dir. */ readonly scopeIndexStorePath?: string; /** * Optional additive diagnostics sink. Resolver passes call this when they * intentionally suppress an edge; the graph remains unchanged. */ readonly recordResolutionOutcome?: ResolutionOutcomeRecorder; /** * Optional progress callback for UI updates during long-running scope * resolution. Called periodically during the extract loop and at each * sub-phase boundary (finalize, resolve, emit). * * @param subPhase Current sub-phase name for display * @param current Files processed so far (during extract) or total files (at phase boundaries) * @param total Total files in this language */ readonly onProgress?: (subPhase: ScopeResolutionSubPhase, current: number, total: number) => void; } interface RunScopeResolutionStats { readonly filesProcessed: number; readonly filesSkipped: number; readonly importsEmitted: number; readonly resolve: ResolveStats; readonly referenceEdgesEmitted: number; readonly referenceSkipped: number; readonly resolutionOutcomes: readonly ResolutionOutcome[]; } export declare function runScopeResolution(input: RunScopeResolutionInput, provider: ScopeResolver): RunScopeResolutionStats; export {};