gitnexus 1.6.4-rc.3 → 1.6.4-rc.4

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 (30) hide show
  1. package/dist/_shared/scope-resolution/types.d.ts +10 -2
  2. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  3. package/dist/_shared/scope-resolution/types.js +10 -2
  4. package/dist/_shared/scope-resolution/types.js.map +1 -1
  5. package/dist/core/ingestion/call-processor.js +2 -2
  6. package/dist/core/ingestion/constants.d.ts +4 -3
  7. package/dist/core/ingestion/constants.js +8 -3
  8. package/dist/core/ingestion/finalize-orchestrator.js +5 -0
  9. package/dist/core/ingestion/heritage-processor.js +2 -2
  10. package/dist/core/ingestion/import-processor.js +1 -1
  11. package/dist/core/ingestion/languages/csharp/captures.js +4 -1
  12. package/dist/core/ingestion/languages/csharp/namespace-siblings.d.ts +14 -13
  13. package/dist/core/ingestion/languages/csharp/namespace-siblings.js +62 -50
  14. package/dist/core/ingestion/languages/python/captures.js +4 -1
  15. package/dist/core/ingestion/languages/typescript/captures.js +4 -1
  16. package/dist/core/ingestion/model/scope-resolution-indexes.d.ts +14 -1
  17. package/dist/core/ingestion/parsing-processor.js +3 -3
  18. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +39 -8
  19. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.js +39 -8
  20. package/dist/core/ingestion/scope-resolution/passes/imported-return-types.js +9 -4
  21. package/dist/core/ingestion/scope-resolution/pipeline/phase.js +3 -2
  22. package/dist/core/ingestion/scope-resolution/pipeline/run.js +10 -0
  23. package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.d.ts +39 -0
  24. package/dist/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +65 -0
  25. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +54 -11
  26. package/dist/core/ingestion/scope-resolution/scope/walkers.js +105 -30
  27. package/dist/core/ingestion/utils/env.d.ts +10 -0
  28. package/dist/core/ingestion/utils/env.js +14 -0
  29. package/dist/core/ingestion/workers/parse-worker.js +3 -3
  30. package/package.json +1 -1
@@ -10,8 +10,16 @@
10
10
  * Lifecycle contract (RFC §2.8): scopes are **constructed during extraction,
11
11
  * linked during finalize, immutable after finalize**. All fields are
12
12
  * `readonly` at the type level; `Object.freeze` is applied at runtime in dev
13
- * builds. `ReferenceIndex` is the sole structure populated after freeze — by
14
- * resolution, before emission.
13
+ * builds.
14
+ *
15
+ * Two structures are populated after freeze:
16
+ * 1. `ReferenceIndex` — by resolution, before emission.
17
+ * 2. `ScopeResolutionIndexes.bindingAugmentations` — the dedicated
18
+ * append-only post-finalize binding channel (e.g. C# same-namespace
19
+ * cross-file fanout). The companion `indexes.bindings` is the
20
+ * finalize-output channel and is deep-frozen by `materializeBindings`;
21
+ * walkers consult both via `lookupBindingsAt`. See `ScopeResolver`
22
+ * Invariant I8 for the full lifecycle contract.
15
23
  */
16
24
  import type { NodeLabel } from '../graph/types.js';
17
25
  import type { SymbolDefinition } from './symbol-definition.js';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"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"}
@@ -10,8 +10,16 @@
10
10
  * Lifecycle contract (RFC §2.8): scopes are **constructed during extraction,
11
11
  * linked during finalize, immutable after finalize**. All fields are
12
12
  * `readonly` at the type level; `Object.freeze` is applied at runtime in dev
13
- * builds. `ReferenceIndex` is the sole structure populated after freeze — by
14
- * resolution, before emission.
13
+ * builds.
14
+ *
15
+ * Two structures are populated after freeze:
16
+ * 1. `ReferenceIndex` — by resolution, before emission.
17
+ * 2. `ScopeResolutionIndexes.bindingAugmentations` — the dedicated
18
+ * append-only post-finalize binding channel (e.g. C# same-namespace
19
+ * cross-file fanout). The companion `indexes.bindings` is the
20
+ * finalize-output channel and is deep-frozen by `materializeBindings`;
21
+ * walkers consult both via `lookupBindingsAt`. See `ScopeResolver`
22
+ * Invariant I8 for the full lifecycle contract.
15
23
  */
16
24
  export {};
17
25
  //# sourceMappingURL=types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/scope-resolution/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
@@ -613,7 +613,7 @@ importedRawReturnTypesMap, heritageMap, bindingAccumulator) => {
613
613
  if (!tree) {
614
614
  try {
615
615
  tree = parser.parse(file.content, undefined, {
616
- bufferSize: getTreeSitterBufferSize(file.content.length),
616
+ bufferSize: getTreeSitterBufferSize(file.content),
617
617
  });
618
618
  }
619
619
  catch (parseError) {
@@ -2591,7 +2591,7 @@ export const extractFetchCallsFromFiles = async (files, astCache) => {
2591
2591
  if (!tree) {
2592
2592
  try {
2593
2593
  tree = parser.parse(file.content, undefined, {
2594
- bufferSize: getTreeSitterBufferSize(file.content.length),
2594
+ bufferSize: getTreeSitterBufferSize(file.content),
2595
2595
  });
2596
2596
  }
2597
2597
  catch {
@@ -10,7 +10,8 @@ export declare const TREE_SITTER_BUFFER_SIZE: number;
10
10
  export declare const TREE_SITTER_MAX_BUFFER: number;
11
11
  /**
12
12
  * Compute adaptive buffer size for tree-sitter parsing.
13
- * Uses file size, clamped between 512 KB and 32 MB.
14
- * Previous 256 KB fixed limit silently skipped files > ~200 KB (e.g., imgui.h at 411 KB).
13
+ * Uses 2x UTF-8 byte size, clamped between 512 KB and 32 MB.
14
+ * Keeps tree-sitter's byte-sized buffer above large ASCII and multibyte sources.
15
15
  */
16
- export declare const getTreeSitterBufferSize: (contentLength: number) => number;
16
+ export declare const getTreeSitterContentByteLength: (sourceText: string) => number;
17
+ export declare const getTreeSitterBufferSize: (sourceText: string) => number;
@@ -1,3 +1,4 @@
1
+ import { Buffer } from 'node:buffer';
1
2
  /**
2
3
  * Default minimum buffer size for tree-sitter parsing (512 KB).
3
4
  * tree-sitter requires bufferSize >= file size in bytes.
@@ -10,7 +11,11 @@ export const TREE_SITTER_BUFFER_SIZE = 512 * 1024;
10
11
  export const TREE_SITTER_MAX_BUFFER = 32 * 1024 * 1024;
11
12
  /**
12
13
  * Compute adaptive buffer size for tree-sitter parsing.
13
- * Uses file size, clamped between 512 KB and 32 MB.
14
- * Previous 256 KB fixed limit silently skipped files > ~200 KB (e.g., imgui.h at 411 KB).
14
+ * Uses 2x UTF-8 byte size, clamped between 512 KB and 32 MB.
15
+ * Keeps tree-sitter's byte-sized buffer above large ASCII and multibyte sources.
15
16
  */
16
- export const getTreeSitterBufferSize = (contentLength) => Math.min(Math.max(contentLength * 2, TREE_SITTER_BUFFER_SIZE), TREE_SITTER_MAX_BUFFER);
17
+ export const getTreeSitterContentByteLength = (sourceText) => Buffer.byteLength(sourceText, 'utf8');
18
+ export const getTreeSitterBufferSize = (sourceText) => {
19
+ const byteLength = getTreeSitterContentByteLength(sourceText);
20
+ return Math.min(Math.max(byteLength * 2, TREE_SITTER_BUFFER_SIZE), TREE_SITTER_MAX_BUFFER);
21
+ };
@@ -91,6 +91,11 @@ export function finalizeScopeModel(parsedFiles, options = {}) {
91
91
  methodDispatch,
92
92
  imports: finalizeOut.imports,
93
93
  bindings: finalizeOut.bindings,
94
+ // Empty post-finalize augmentation channel. Populated (if at all)
95
+ // by language hooks like `populateCsharpNamespaceSiblings` running
96
+ // AFTER `finalizeScopeModel` returns, before `resolveReferenceSites`
97
+ // consumes the bundle. Most languages leave it empty.
98
+ bindingAugmentations: new Map(),
94
99
  referenceSites: Object.freeze([...allReferenceSites]),
95
100
  sccs: finalizeOut.sccs,
96
101
  stats: finalizeOut.stats,
@@ -148,7 +148,7 @@ export const processHeritage = async (graph, files, astCache, ctx, onProgress) =
148
148
  // Use larger bufferSize for files > 32KB
149
149
  try {
150
150
  tree = parser.parse(file.content, undefined, {
151
- bufferSize: getTreeSitterBufferSize(file.content.length),
151
+ bufferSize: getTreeSitterBufferSize(file.content),
152
152
  });
153
153
  }
154
154
  catch (parseError) {
@@ -295,7 +295,7 @@ export async function extractExtractedHeritageFromFiles(files, astCache) {
295
295
  if (!tree) {
296
296
  try {
297
297
  tree = parser.parse(file.content, undefined, {
298
- bufferSize: getTreeSitterBufferSize(file.content.length),
298
+ bufferSize: getTreeSitterBufferSize(file.content),
299
299
  });
300
300
  }
301
301
  catch {
@@ -242,7 +242,7 @@ export const processImports = async (graph, files, astCache, ctx, onProgress, re
242
242
  if (!tree) {
243
243
  try {
244
244
  tree = parser.parse(file.content, undefined, {
245
- bufferSize: getTreeSitterBufferSize(file.content.length),
245
+ bufferSize: getTreeSitterBufferSize(file.content),
246
246
  });
247
247
  }
248
248
  catch (parseError) {
@@ -21,6 +21,7 @@ import { computeCsharpArityMetadata } from './arity-metadata.js';
21
21
  import { synthesizeCsharpReceiverBinding } from './receiver-binding.js';
22
22
  import { getCsharpParser, getCsharpScopeQuery } from './query.js';
23
23
  import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
24
+ import { getTreeSitterBufferSize } from '../../constants.js';
24
25
  /** Declaration anchors that carry function-like arity metadata. */
25
26
  const FUNCTION_DECL_TAGS = [
26
27
  '@declaration.method',
@@ -43,7 +44,9 @@ export function emitCsharpScopeCaptures(sourceText, _filePath, cachedTree) {
43
44
  // the LanguageProvider contract layer; cast here at the use site.
44
45
  let tree = cachedTree;
45
46
  if (tree === undefined) {
46
- tree = getCsharpParser().parse(sourceText);
47
+ tree = getCsharpParser().parse(sourceText, undefined, {
48
+ bufferSize: getTreeSitterBufferSize(sourceText),
49
+ });
47
50
  recordCacheMiss();
48
51
  }
49
52
  else {
@@ -11,17 +11,18 @@
11
11
  * field-chain resolution fails at `findClassBindingInScope('User')`
12
12
  * in the Service.cs scope chain.
13
13
  *
14
- * Implementation: after the finalize pass populates `indexes.bindings`
15
- * (from explicit `using` directives), walk each file's tree-sitter
16
- * AST for `namespace_declaration` / `file_scoped_namespace_declaration`
17
- * and `using_directive` nodes. The orchestrator hands us its
18
- * `treeCache` so files already parsed by `extractParsedFile` are
19
- * re-used instead of re-parsed — `ParsedFile`'s underlying tree is
20
- * the single source of truth. Group classes by namespace, and inject
21
- * cross-file sibling classes into each Namespace scope's finalized
22
- * bindings with `origin: 'namespace'` — a tier below `local` so a
23
- * local declaration still shadows a cross-file sibling with the same
24
- * name.
14
+ * Implementation: after the finalize pass populates immutable
15
+ * `indexes.bindings` (from explicit `using` directives), walk each
16
+ * file's tree-sitter AST for `namespace_declaration` /
17
+ * `file_scoped_namespace_declaration` and `using_directive` nodes.
18
+ * The orchestrator hands us its `treeCache` so files already parsed
19
+ * by `extractParsedFile` are re-used instead of re-parsed —
20
+ * `ParsedFile`'s underlying tree is the single source of truth.
21
+ * Group classes by namespace, and append cross-file sibling classes
22
+ * into each Namespace scope's `bindingAugmentations` bucket with
23
+ * `origin: 'namespace'`. Finalized bindings remain first in
24
+ * `lookupBindingsAt`, and local lexical `Scope.bindings` remains the
25
+ * first-tier shadowing channel.
25
26
  *
26
27
  * The tree-sitter walk is authoritative: it sees `global using static`,
27
28
  * aliased `using static X = Y.Z;`, attributed namespace declarations,
@@ -42,8 +43,8 @@ export interface CsharpSiblingInputs {
42
43
  };
43
44
  }
44
45
  /**
45
- * Mutate `indexes.bindings` in-place, adding cross-file sibling class
46
- * defs to each Namespace scope. Class-like defs (Class / Interface /
46
+ * Append cross-file sibling class defs to each Namespace scope's
47
+ * `bindingAugmentations` bucket. Class-like defs (Class / Interface /
47
48
  * Struct / Record / Enum) are visible cross-file; method / field
48
49
  * members are not.
49
50
  */
@@ -11,17 +11,18 @@
11
11
  * field-chain resolution fails at `findClassBindingInScope('User')`
12
12
  * in the Service.cs scope chain.
13
13
  *
14
- * Implementation: after the finalize pass populates `indexes.bindings`
15
- * (from explicit `using` directives), walk each file's tree-sitter
16
- * AST for `namespace_declaration` / `file_scoped_namespace_declaration`
17
- * and `using_directive` nodes. The orchestrator hands us its
18
- * `treeCache` so files already parsed by `extractParsedFile` are
19
- * re-used instead of re-parsed — `ParsedFile`'s underlying tree is
20
- * the single source of truth. Group classes by namespace, and inject
21
- * cross-file sibling classes into each Namespace scope's finalized
22
- * bindings with `origin: 'namespace'` — a tier below `local` so a
23
- * local declaration still shadows a cross-file sibling with the same
24
- * name.
14
+ * Implementation: after the finalize pass populates immutable
15
+ * `indexes.bindings` (from explicit `using` directives), walk each
16
+ * file's tree-sitter AST for `namespace_declaration` /
17
+ * `file_scoped_namespace_declaration` and `using_directive` nodes.
18
+ * The orchestrator hands us its `treeCache` so files already parsed
19
+ * by `extractParsedFile` are re-used instead of re-parsed —
20
+ * `ParsedFile`'s underlying tree is the single source of truth.
21
+ * Group classes by namespace, and append cross-file sibling classes
22
+ * into each Namespace scope's `bindingAugmentations` bucket with
23
+ * `origin: 'namespace'`. Finalized bindings remain first in
24
+ * `lookupBindingsAt`, and local lexical `Scope.bindings` remains the
25
+ * first-tier shadowing channel.
25
26
  *
26
27
  * The tree-sitter walk is authoritative: it sees `global using static`,
27
28
  * aliased `using static X = Y.Z;`, attributed namespace declarations,
@@ -30,13 +31,17 @@
30
31
  * coincidences).
31
32
  */
32
33
  import { getCsharpParser } from './query.js';
34
+ import { getTreeSitterBufferSize } from '../../constants.js';
33
35
  /** Build a structural view of a C# file by walking the tree-sitter
34
36
  * AST. Prefers `cachedTree` (handed in via `treeCache`) so we don't
35
37
  * re-parse files the orchestrator already parsed for `extractParsedFile`;
36
38
  * falls back to a fresh parse on cache miss. Parser singleton is
37
39
  * shared across calls. */
38
40
  function extractFileStructure(content, cachedTree) {
39
- const tree = cachedTree ?? getCsharpParser().parse(content);
41
+ const tree = cachedTree ??
42
+ getCsharpParser().parse(content, undefined, {
43
+ bufferSize: getTreeSitterBufferSize(content),
44
+ });
40
45
  const namespaces = [];
41
46
  const usingStaticPaths = [];
42
47
  const visit = (node) => {
@@ -81,8 +86,8 @@ function extractFileStructure(content, cachedTree) {
81
86
  return { namespaces, usingStaticPaths };
82
87
  }
83
88
  /**
84
- * Mutate `indexes.bindings` in-place, adding cross-file sibling class
85
- * defs to each Namespace scope. Class-like defs (Class / Interface /
89
+ * Append cross-file sibling class defs to each Namespace scope's
90
+ * `bindingAugmentations` bucket. Class-like defs (Class / Interface /
86
91
  * Struct / Record / Enum) are visible cross-file; method / field
87
92
  * members are not.
88
93
  */
@@ -161,12 +166,15 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
161
166
  }
162
167
  }
163
168
  }
164
- // Inject cross-file siblings into each namespace scope's finalized
165
- // bindings. `indexes.bindings` is typed `ReadonlyMap<ScopeId, ...>`
166
- // but is a plain Map at runtime; mutating here is the established
167
- // pattern (see `propagateImportedReturnTypes` which does the same
168
- // for module-scope typeBindings).
169
- const finalized = indexes.bindings;
169
+ // Inject cross-file siblings into each namespace scope's
170
+ // post-finalize augmentation channel (per I8). The
171
+ // `indexes.bindingAugmentations` map is the dedicated mutable
172
+ // append-only buffer for post-finalize hooks: inner `BindingRef[]`
173
+ // arrays here are NEVER frozen (unlike `indexes.bindings`, which
174
+ // `materializeBindings` freezes). Walkers consult both channels
175
+ // via `lookupBindingsAt`; we never need to consult or mutate
176
+ // `indexes.bindings`.
177
+ const augmentations = indexes.bindingAugmentations;
170
178
  // Cross-namespace type-binding propagation: for each file, mirror
171
179
  // method return-type bindings from same-namespace sibling files and
172
180
  // from files in namespaces the importer `using`s, into the
@@ -268,18 +276,14 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
268
276
  const simpleName = mq.includes('.') ? mq.slice(mq.lastIndexOf('.') + 1) : mq;
269
277
  if (simpleName === '')
270
278
  continue;
271
- // Add to `indexes.bindings[moduleScope]` so
272
- // `findCallableBindingInScope` picks it up.
273
- let scopeBindings = finalized.get(moduleScope.id);
274
- if (scopeBindings === undefined) {
275
- scopeBindings = new Map();
276
- finalized.set(moduleScope.id, scopeBindings);
277
- }
278
- const existing = scopeBindings.get(simpleName) ?? [];
279
- if (existing.some((b) => b.def.nodeId === memberDef.nodeId))
279
+ // Append to the augmentation bucket for the importer's module
280
+ // scope. `findCallableBindingInScope` reads via
281
+ // `lookupBindingsAt`, which fans out across `bindings` +
282
+ // `bindingAugmentations`.
283
+ const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName);
284
+ if (bucketArr.some((b) => b.def.nodeId === memberDef.nodeId))
280
285
  continue;
281
- existing.push({ def: memberDef, origin: 'import' });
282
- scopeBindings.set(simpleName, existing);
286
+ bucketArr.push({ def: memberDef, origin: 'import' });
283
287
  }
284
288
  }
285
289
  }
@@ -310,16 +314,10 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
310
314
  const simpleName = q.includes('.') ? q.slice(q.lastIndexOf('.') + 1) : q;
311
315
  if (simpleName === '')
312
316
  continue;
313
- let scopeBindings = finalized.get(moduleScope.id);
314
- if (scopeBindings === undefined) {
315
- scopeBindings = new Map();
316
- finalized.set(moduleScope.id, scopeBindings);
317
- }
318
- const existing = scopeBindings.get(simpleName) ?? [];
319
- if (existing.some((b) => b.def.nodeId === def.nodeId))
317
+ const bucketArr = getAugmentationBucket(augmentations, moduleScope.id, simpleName);
318
+ if (bucketArr.some((b) => b.def.nodeId === def.nodeId))
320
319
  continue;
321
- existing.push({ def, origin: 'namespace' });
322
- scopeBindings.set(simpleName, existing);
320
+ bucketArr.push({ def, origin: 'namespace' });
323
321
  }
324
322
  }
325
323
  }
@@ -339,11 +337,6 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
339
337
  defsByName.set(key, arr);
340
338
  }
341
339
  for (const { scopeId, filePath } of bucket.scopes) {
342
- let scopeBindings = finalized.get(scopeId);
343
- if (scopeBindings === undefined) {
344
- scopeBindings = new Map();
345
- finalized.set(scopeId, scopeBindings);
346
- }
347
340
  for (const [name, defs] of defsByName) {
348
341
  // Skip names already present locally — `origin: 'local'` in
349
342
  // scope.bindings would naturally shadow the cross-file
@@ -351,20 +344,39 @@ export function populateCsharpNamespaceSiblings(parsedFiles, indexes, inputs) {
351
344
  const local = bucket.scopes.find((s) => s.filePath === filePath)?.scope.bindings.get(name);
352
345
  if (local !== undefined && local.some((b) => b.origin === 'local'))
353
346
  continue;
354
- const existing = scopeBindings.get(name) ?? [];
347
+ let bucketArr = null;
355
348
  for (const def of defs) {
356
349
  if (def.filePath === filePath)
357
350
  continue; // don't self-reference
358
- if (existing.some((b) => b.def.nodeId === def.nodeId))
351
+ if (bucketArr === null)
352
+ bucketArr = getAugmentationBucket(augmentations, scopeId, name);
353
+ if (bucketArr.some((b) => b.def.nodeId === def.nodeId))
359
354
  continue;
360
- existing.push({ def, origin: 'namespace' });
355
+ bucketArr.push({ def, origin: 'namespace' });
361
356
  }
362
- if (existing.length > 0)
363
- scopeBindings.set(name, existing);
364
357
  }
365
358
  }
366
359
  }
367
360
  }
361
+ /** Get-or-create a mutable inner bucket inside the `bindingAugmentations`
362
+ * channel. The inner arrays here are mutable by contract (see
363
+ * `ScopeResolutionIndexes.bindingAugmentations` doc + scope-resolver I8);
364
+ * callers may `push` directly. Allocating the outer/inner Maps lazily
365
+ * keeps the augmentation footprint zero for files with no cross-file
366
+ * fanout. */
367
+ function getAugmentationBucket(augmentations, scopeId, name) {
368
+ let scopeBindings = augmentations.get(scopeId);
369
+ if (scopeBindings === undefined) {
370
+ scopeBindings = new Map();
371
+ augmentations.set(scopeId, scopeBindings);
372
+ }
373
+ let bucketArr = scopeBindings.get(name);
374
+ if (bucketArr === undefined) {
375
+ bucketArr = [];
376
+ scopeBindings.set(name, bucketArr);
377
+ }
378
+ return bucketArr;
379
+ }
368
380
  function isTypeDef(def) {
369
381
  return (def.type === 'Class' ||
370
382
  def.type === 'Interface' ||
@@ -21,6 +21,7 @@ import { getPythonParser, getPythonScopeQuery } from './query.js';
21
21
  import { synthesizeReceiverTypeBinding } from './receiver-binding.js';
22
22
  import { computePythonArityMetadata } from './arity-metadata.js';
23
23
  import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
24
+ import { getTreeSitterBufferSize } from '../../constants.js';
24
25
  export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
25
26
  // Skip the parse when the caller (parse phase's ASTCache) already
26
27
  // produced a Tree for this source. Cache miss = re-parse, same as
@@ -29,7 +30,9 @@ export function emitPythonScopeCaptures(sourceText, _filePath, cachedTree) {
29
30
  // here at the use site.
30
31
  let tree = cachedTree;
31
32
  if (tree === undefined) {
32
- tree = getPythonParser().parse(sourceText);
33
+ tree = getPythonParser().parse(sourceText, undefined, {
34
+ bufferSize: getTreeSitterBufferSize(sourceText),
35
+ });
33
36
  recordCacheMiss();
34
37
  }
35
38
  else {
@@ -30,6 +30,7 @@ import { getTsParser, getTsScopeQuery, tsCachedTreeMatchesGrammar } from './quer
30
30
  import { recordCacheHit, recordCacheMiss } from './cache-stats.js';
31
31
  import { synthesizeTsReceiverBinding } from './receiver-binding.js';
32
32
  import { computeTsArityMetadata } from './arity-metadata.js';
33
+ import { getTreeSitterBufferSize } from '../../constants.js';
33
34
  /** tree-sitter-typescript node types for function-like scopes that may
34
35
  * carry a synthesized `this` binding. Kept in sync with the
35
36
  * `@scope.function` patterns in `query.ts`. */
@@ -110,7 +111,9 @@ export function emitTsScopeCaptures(sourceText, filePath, cachedTree) {
110
111
  tree = undefined;
111
112
  }
112
113
  if (tree === undefined) {
113
- tree = getTsParser(filePath).parse(sourceText);
114
+ tree = getTsParser(filePath).parse(sourceText, undefined, {
115
+ bufferSize: getTreeSitterBufferSize(sourceText),
116
+ });
114
117
  recordCacheMiss();
115
118
  }
116
119
  else {
@@ -48,8 +48,21 @@ export interface ScopeResolutionIndexes {
48
48
  readonly methodDispatch: MethodDispatchIndex;
49
49
  /** Finalized `ImportEdge[]` per module scope. */
50
50
  readonly imports: ReadonlyMap<ScopeId, readonly ImportEdge[]>;
51
- /** Merged bindings (local + imports + wildcards) per module scope. */
51
+ /** Finalize-output bindings (local + imports + wildcards) per module scope.
52
+ * Inner `BindingRef[]` arrays are frozen by `materializeBindings`;
53
+ * this channel is permanently immutable post-finalize. Consumers
54
+ * MUST read via `lookupBindingsAt` so the augmentation channel is
55
+ * consulted alongside. See I8 in `contract/scope-resolver.ts`. */
52
56
  readonly bindings: ReadonlyMap<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>;
57
+ /** Append-only post-finalize augmentation channel. Populated by
58
+ * language hooks such as `populateNamespaceSiblings` for cross-file
59
+ * bindings synthesized after finalize (e.g. C# same-namespace
60
+ * visibility, `using static` member exposure). Inner arrays are
61
+ * NOT frozen — hooks `push()` directly. Walkers must consult both
62
+ * this map and `bindings` via `lookupBindingsAt`; finalized refs
63
+ * are returned first and win duplicate `def.nodeId` metadata, with
64
+ * unique augmentations appended after. See I8. */
65
+ readonly bindingAugmentations: ReadonlyMap<ScopeId, ReadonlyMap<string, readonly BindingRef[]>>;
53
66
  /** Pre-resolution usage facts; consumed by the resolution phase. */
54
67
  readonly referenceSites: readonly ReferenceSite[];
55
68
  /** SCC condensation of the file-level import graph — callers that want
@@ -10,7 +10,7 @@ import { getDefinitionNodeFromCaptures, findEnclosingClassInfo, getLabelFromCapt
10
10
  import { detectFrameworkFromAST } from './framework-detection.js';
11
11
  import { buildTypeEnv } from './type-env.js';
12
12
  import { buildMethodProps, arityForIdFromInfo, typeTagForId, constTagForId, buildCollisionGroups, } from './utils/method-props.js';
13
- import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from './constants.js';
13
+ import { getTreeSitterBufferSize, getTreeSitterContentByteLength, TREE_SITTER_MAX_BUFFER, } from './constants.js';
14
14
  // ============================================================================
15
15
  // Worker-based parallel parsing
16
16
  // ============================================================================
@@ -247,7 +247,7 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, sco
247
247
  continue;
248
248
  }
249
249
  // Skip files larger than the max tree-sitter buffer (32 MB)
250
- if (file.content.length > TREE_SITTER_MAX_BUFFER)
250
+ if (getTreeSitterContentByteLength(file.content) > TREE_SITTER_MAX_BUFFER)
251
251
  continue;
252
252
  // Vue SFC preprocessing: extract <script> block content
253
253
  let parseContent = file.content;
@@ -270,7 +270,7 @@ const processParsingSequential = async (graph, files, symbolTable, astCache, sco
270
270
  let tree;
271
271
  try {
272
272
  tree = parser.parse(parseContent, undefined, {
273
- bufferSize: getTreeSitterBufferSize(parseContent.length),
273
+ bufferSize: getTreeSitterBufferSize(parseContent),
274
274
  });
275
275
  }
276
276
  catch (parseError) {
@@ -136,14 +136,45 @@
136
136
  * once per workspace at resolve time), and merging would create a
137
137
  * god-interface that complicates future migrations.
138
138
  *
139
- * - **I8 — Post-finalize hooks may mutate `Scope.typeBindings` and
140
- * `indexes.bindings`.** `propagateImportedReturnTypes` and
141
- * `populateNamespaceSiblings` both write to these structures via
142
- * `as Map<...>` casts through `ReadonlyMap` facades. Downstream
143
- * consumers MUST NOT freeze or snapshot these maps before all
144
- * post-finalize hooks have run. The `ReadonlyMap<...>` type on
145
- * `ScopeResolutionIndexes` is a read-guidance surface for
146
- * consumers, NOT an immutability promise during the resolve phase.
139
+ * - **I8 — Two-channel binding lifecycle.**
140
+ * `indexes.bindings` is the **finalize-output channel**. After
141
+ * `finalizeScopeModel` returns, its inner `BindingRef[]` arrays
142
+ * are deep-frozen by `materializeBindings` and MUST NOT be
143
+ * mutated by any post-finalize hook. Treat `indexes.bindings` as
144
+ * immutable from the moment `finalizeScopeModel` returns.
145
+ *
146
+ * `indexes.bindingAugmentations` is the **post-finalize
147
+ * append-only channel**. Hooks like `populateNamespaceSiblings`
148
+ * append cross-file bindings synthesized after finalize (C#
149
+ * same-namespace visibility, `using static` member exposure)
150
+ * into this channel, NOT into `indexes.bindings`. Inner arrays
151
+ * here are NEVER frozen — hooks `push()` directly. Any consumer
152
+ * that reads post-finalize workspace bindings MUST query both
153
+ * index channels via `lookupBindingsAt`
154
+ * (`scope-resolution/scope/walkers.ts`); the helper returns
155
+ * finalized refs first, appends unique augmentation refs after,
156
+ * and dedupes by `def.nodeId` so finalized metadata wins on
157
+ * duplicate defs. Per-`Scope.bindings` local declarations are the
158
+ * lexical extraction channel and remain a separate first-tier
159
+ * lookup for local shadowing.
160
+ *
161
+ * `Scope.typeBindings` remains mutable post-finalize per I6 (it
162
+ * is intentionally not frozen at any point).
163
+ *
164
+ * The `ReadonlyMap<...>` types on `ScopeResolutionIndexes` are
165
+ * compile-time read-guidance for consumers; structural mutation
166
+ * of `bindingAugmentations` is performed via a deliberate
167
+ * `as Map<...>` cast inside the hook implementations and is the
168
+ * ONLY sanctioned channel for post-finalize binding fanout.
169
+ *
170
+ * The dev-mode runtime validator
171
+ * (`validateBindingsImmutability` in
172
+ * `scope-resolution/validate-bindings-immutability.ts`) surfaces
173
+ * any drift — i.e. a hook writing to `indexes.bindings` instead
174
+ * of `bindingAugmentations`, or producing a non-frozen finalized
175
+ * bucket — via `onWarn` when explicitly enabled by
176
+ * `NODE_ENV === 'development' || VALIDATE_SEMANTIC_MODEL === '1'`
177
+ * (`VALIDATE_SEMANTIC_MODEL=0` is an explicit off switch).
147
178
  *
148
179
  * - **I9 — `SemanticModel` is the single authoritative symbol store.**
149
180
  * Every symbol-indexed lookup (key = `nodeId | simpleName |
@@ -136,14 +136,45 @@
136
136
  * once per workspace at resolve time), and merging would create a
137
137
  * god-interface that complicates future migrations.
138
138
  *
139
- * - **I8 — Post-finalize hooks may mutate `Scope.typeBindings` and
140
- * `indexes.bindings`.** `propagateImportedReturnTypes` and
141
- * `populateNamespaceSiblings` both write to these structures via
142
- * `as Map<...>` casts through `ReadonlyMap` facades. Downstream
143
- * consumers MUST NOT freeze or snapshot these maps before all
144
- * post-finalize hooks have run. The `ReadonlyMap<...>` type on
145
- * `ScopeResolutionIndexes` is a read-guidance surface for
146
- * consumers, NOT an immutability promise during the resolve phase.
139
+ * - **I8 — Two-channel binding lifecycle.**
140
+ * `indexes.bindings` is the **finalize-output channel**. After
141
+ * `finalizeScopeModel` returns, its inner `BindingRef[]` arrays
142
+ * are deep-frozen by `materializeBindings` and MUST NOT be
143
+ * mutated by any post-finalize hook. Treat `indexes.bindings` as
144
+ * immutable from the moment `finalizeScopeModel` returns.
145
+ *
146
+ * `indexes.bindingAugmentations` is the **post-finalize
147
+ * append-only channel**. Hooks like `populateNamespaceSiblings`
148
+ * append cross-file bindings synthesized after finalize (C#
149
+ * same-namespace visibility, `using static` member exposure)
150
+ * into this channel, NOT into `indexes.bindings`. Inner arrays
151
+ * here are NEVER frozen — hooks `push()` directly. Any consumer
152
+ * that reads post-finalize workspace bindings MUST query both
153
+ * index channels via `lookupBindingsAt`
154
+ * (`scope-resolution/scope/walkers.ts`); the helper returns
155
+ * finalized refs first, appends unique augmentation refs after,
156
+ * and dedupes by `def.nodeId` so finalized metadata wins on
157
+ * duplicate defs. Per-`Scope.bindings` local declarations are the
158
+ * lexical extraction channel and remain a separate first-tier
159
+ * lookup for local shadowing.
160
+ *
161
+ * `Scope.typeBindings` remains mutable post-finalize per I6 (it
162
+ * is intentionally not frozen at any point).
163
+ *
164
+ * The `ReadonlyMap<...>` types on `ScopeResolutionIndexes` are
165
+ * compile-time read-guidance for consumers; structural mutation
166
+ * of `bindingAugmentations` is performed via a deliberate
167
+ * `as Map<...>` cast inside the hook implementations and is the
168
+ * ONLY sanctioned channel for post-finalize binding fanout.
169
+ *
170
+ * The dev-mode runtime validator
171
+ * (`validateBindingsImmutability` in
172
+ * `scope-resolution/validate-bindings-immutability.ts`) surfaces
173
+ * any drift — i.e. a hook writing to `indexes.bindings` instead
174
+ * of `bindingAugmentations`, or producing a non-frozen finalized
175
+ * bucket — via `onWarn` when explicitly enabled by
176
+ * `NODE_ENV === 'development' || VALIDATE_SEMANTIC_MODEL === '1'`
177
+ * (`VALIDATE_SEMANTIC_MODEL=0` is an explicit off switch).
147
178
  *
148
179
  * - **I9 — `SemanticModel` is the single authoritative symbol store.**
149
180
  * Every symbol-indexed lookup (key = `nodeId | simpleName |
@@ -38,6 +38,7 @@
38
38
  * Generic; promoted from `languages/python/scope-resolver.ts` per the
39
39
  * scope-resolution generalization plan.
40
40
  */
41
+ import { lookupBindingsAt, namesAtScope } from '../scope/walkers.js';
41
42
  /**
42
43
  * Max chain depth for the post-finalize re-follow. Effective end-to-end
43
44
  * depth is roughly 2× this number, because chain-following runs once
@@ -126,14 +127,18 @@ export function propagateImportedReturnTypes(parsedFiles, indexes, index) {
126
127
  const importerModule = moduleScopeByFile.get(filePath);
127
128
  if (importerModule === undefined)
128
129
  continue;
129
- const finalizedBindings = indexes.bindings.get(importerModule.id);
130
- if (finalizedBindings === undefined)
131
- continue;
132
- for (const [localName, refs] of finalizedBindings) {
130
+ // Iterate finalized + augmented binding names at this scope so
131
+ // post-finalize hooks (e.g. `using static` augmentations from
132
+ // `populateCsharpNamespaceSiblings`) are visible to the
133
+ // import-derived typeBinding mirror. Both helpers fast-path when
134
+ // no augmentations exist for the scope, so the common case is
135
+ // allocation-free. See I8.
136
+ for (const localName of namesAtScope(importerModule.id, indexes)) {
133
137
  // Skip if importer already has a typeBinding for this name —
134
138
  // an explicit local annotation must win over import-derived.
135
139
  if (importerModule.typeBindings.has(localName))
136
140
  continue;
141
+ const refs = lookupBindingsAt(importerModule.id, localName, indexes);
137
142
  for (const ref of refs) {
138
143
  if (ref.origin !== 'import' && ref.origin !== 'reexport')
139
144
  continue;
@@ -32,7 +32,7 @@ import { getLanguageFromFilename } from '../../../../_shared/index.js';
32
32
  import { readFileContents } from '../../filesystem-walker.js';
33
33
  import { runScopeResolution } from './run.js';
34
34
  import { SCOPE_RESOLVERS } from './registry.js';
35
- import { isDev } from '../../utils/env.js';
35
+ import { isDev, isSemanticModelValidatorEnabled } from '../../utils/env.js';
36
36
  const NOOP_OUTPUT = Object.freeze({
37
37
  ran: false,
38
38
  filesProcessed: 0,
@@ -101,8 +101,9 @@ export const scopeResolutionPhase = {
101
101
  treeCache: scopeTreeCache,
102
102
  resolutionConfig,
103
103
  onWarn: (msg) => {
104
- if (isDev)
104
+ if (isSemanticModelValidatorEnabled()) {
105
105
  console.warn(`[scope-resolution:${lang}] ${msg}`);
106
+ }
106
107
  },
107
108
  }, provider);
108
109
  anyRan = true;
@@ -23,6 +23,7 @@
23
23
  * Plan: `docs/plans/2026-04-20-001-refactor-emit-pipeline-generalization-plan.md`.
24
24
  */
25
25
  import { reconcileOwnership, validateOwnershipParity } from './reconcile-ownership.js';
26
+ import { validateBindingsImmutability } from './validate-bindings-immutability.js';
26
27
  import { extractParsedFile } from '../../scope-extractor-bridge.js';
27
28
  import { finalizeScopeModel } from '../../finalize-orchestrator.js';
28
29
  import { resolveReferenceSites } from '../../resolve-references.js';
@@ -107,6 +108,8 @@ export function runScopeResolution(input, provider) {
107
108
  // Cross-file implicit-namespace visibility (C#). Must run before
108
109
  // propagateImportedReturnTypes so the latter pass sees siblings'
109
110
  // class bindings when chasing return-type chains across files.
111
+ // The hook writes to `bindingAugmentations` only; finalized
112
+ // `indexes.bindings` remains immutable post-finalize (I8).
110
113
  if (provider.populateNamespaceSiblings !== undefined) {
111
114
  const fileContents = new Map();
112
115
  for (const f of files)
@@ -126,6 +129,13 @@ export function runScopeResolution(input, provider) {
126
129
  propagateImportedReturnTypes(parsedFiles, indexes, workspaceIndex);
127
130
  }
128
131
  const tPropagate = PROF ? process.hrtime.bigint() : 0n;
132
+ // Opt-in I8 invariant guard. Runs once after all post-finalize hooks
133
+ // (`populateNamespaceSiblings`, `propagateImportedReturnTypes`) have
134
+ // had a chance to drift, so a single sweep covers the full
135
+ // post-finalize surface visible to `resolveReferenceSites`. No-op in
136
+ // default CLI runs; enabled by NODE_ENV=development or
137
+ // VALIDATE_SEMANTIC_MODEL=1.
138
+ validateBindingsImmutability(indexes, onWarn);
129
139
  // ── Phase 3: resolve references via Registry.lookup ────────────────────
130
140
  const registryProviders = {
131
141
  arityCompatibility: provider.arityCompatibility,
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Dev-mode runtime validator for the two-channel binding lifecycle
3
+ * (Contract Invariant I8 in `contract/scope-resolver.ts`).
4
+ *
5
+ * The two channels:
6
+ * - `indexes.bindings` — finalize-output channel. After
7
+ * `finalizeScopeModel` returns, every inner `BindingRef[]` array
8
+ * here is deep-frozen by `materializeBindings`. NO post-finalize
9
+ * hook should ever mutate this map's inner arrays — drift here
10
+ * manifests at runtime as the `Cannot add property N, object is
11
+ * not extensible` crash (issue #1066) or, more insidiously, as
12
+ * a hook silently mutating one of the frozen arrays (a no-op in
13
+ * production where freezes can be elided, a `TypeError` in dev).
14
+ *
15
+ * - `indexes.bindingAugmentations` — post-finalize append-only
16
+ * channel. Inner arrays here are NEVER frozen; hooks like
17
+ * `populateNamespaceSiblings` `push()` directly. Walkers consult
18
+ * both channels via `lookupBindingsAt`.
19
+ *
20
+ * This validator runs after every post-finalize hook has executed
21
+ * (so the dev-mode envelope captures the FULL surface area visible
22
+ * to `resolveReferenceSites`) and asserts:
23
+ *
24
+ * 1. Every inner `BindingRef[]` array in `indexes.bindings` is
25
+ * `Object.isFrozen` — i.e. finalize produced a frozen bucket
26
+ * AND no hook accidentally `set()`-back a mutable replacement.
27
+ *
28
+ * 2. Every inner `BindingRef[]` array in
29
+ * `indexes.bindingAugmentations` is NOT frozen — i.e. the
30
+ * hook used the augmentation channel as designed (mutable
31
+ * `push()`) and didn't accidentally freeze its own scratch
32
+ * arrays. Self-documenting; mostly a sanity net.
33
+ *
34
+ * Mirrors `validateOwnershipParity` (#909): warns via `onWarn`,
35
+ * never throws, and is opt-in outside development. Gated by
36
+ * `isSemanticModelValidatorEnabled()` (`utils/env.ts`).
37
+ */
38
+ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
39
+ export declare function validateBindingsImmutability(indexes: ScopeResolutionIndexes, onWarn: (message: string) => void): number;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Dev-mode runtime validator for the two-channel binding lifecycle
3
+ * (Contract Invariant I8 in `contract/scope-resolver.ts`).
4
+ *
5
+ * The two channels:
6
+ * - `indexes.bindings` — finalize-output channel. After
7
+ * `finalizeScopeModel` returns, every inner `BindingRef[]` array
8
+ * here is deep-frozen by `materializeBindings`. NO post-finalize
9
+ * hook should ever mutate this map's inner arrays — drift here
10
+ * manifests at runtime as the `Cannot add property N, object is
11
+ * not extensible` crash (issue #1066) or, more insidiously, as
12
+ * a hook silently mutating one of the frozen arrays (a no-op in
13
+ * production where freezes can be elided, a `TypeError` in dev).
14
+ *
15
+ * - `indexes.bindingAugmentations` — post-finalize append-only
16
+ * channel. Inner arrays here are NEVER frozen; hooks like
17
+ * `populateNamespaceSiblings` `push()` directly. Walkers consult
18
+ * both channels via `lookupBindingsAt`.
19
+ *
20
+ * This validator runs after every post-finalize hook has executed
21
+ * (so the dev-mode envelope captures the FULL surface area visible
22
+ * to `resolveReferenceSites`) and asserts:
23
+ *
24
+ * 1. Every inner `BindingRef[]` array in `indexes.bindings` is
25
+ * `Object.isFrozen` — i.e. finalize produced a frozen bucket
26
+ * AND no hook accidentally `set()`-back a mutable replacement.
27
+ *
28
+ * 2. Every inner `BindingRef[]` array in
29
+ * `indexes.bindingAugmentations` is NOT frozen — i.e. the
30
+ * hook used the augmentation channel as designed (mutable
31
+ * `push()`) and didn't accidentally freeze its own scratch
32
+ * arrays. Self-documenting; mostly a sanity net.
33
+ *
34
+ * Mirrors `validateOwnershipParity` (#909): warns via `onWarn`,
35
+ * never throws, and is opt-in outside development. Gated by
36
+ * `isSemanticModelValidatorEnabled()` (`utils/env.ts`).
37
+ */
38
+ import { isSemanticModelValidatorEnabled } from '../../utils/env.js';
39
+ export function validateBindingsImmutability(indexes, onWarn) {
40
+ if (!isSemanticModelValidatorEnabled())
41
+ return 0;
42
+ let violations = 0;
43
+ for (const [scopeId, bucketMap] of indexes.bindings) {
44
+ for (const [name, bucket] of bucketMap) {
45
+ if (!Object.isFrozen(bucket)) {
46
+ onWarn(`binding-immutability: indexes.bindings[${scopeId}][${name}] is NOT frozen — ` +
47
+ `finalize produced a mutable bucket OR a post-finalize hook replaced a frozen ` +
48
+ `bucket with a mutable one. Hooks must write to indexes.bindingAugmentations ` +
49
+ `instead. See ScopeResolver Invariant I8.`);
50
+ violations++;
51
+ }
52
+ }
53
+ }
54
+ for (const [scopeId, bucketMap] of indexes.bindingAugmentations) {
55
+ for (const [name, bucket] of bucketMap) {
56
+ if (Object.isFrozen(bucket)) {
57
+ onWarn(`binding-immutability: indexes.bindingAugmentations[${scopeId}][${name}] is FROZEN — ` +
58
+ `the augmentation channel is mutable by contract; freezing it defeats the ` +
59
+ `append-only purpose. See ScopeResolver Invariant I8.`);
60
+ violations++;
61
+ }
62
+ }
63
+ }
64
+ return violations;
65
+ }
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Scope-chain lookup primitives shared across language providers.
3
3
  *
4
- * Four functions:
4
+ * Five functions:
5
5
  * - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
6
6
  * for a receiver name.
7
- * - `findClassBindingInScope` — walk scope.bindings + indexes.bindings
8
- * (pre-finalize + post-finalize) for a class-kind binding. Dual-
9
- * source is required because the cross-file finalize pass produces
10
- * a separate bindings map that is not merged back into scope.bindings.
7
+ * - `lookupBindingsAt` — read finalized + augmented binding refs at
8
+ * one scope, deduped by `def.nodeId`. The dual-source-aware
9
+ * primitive every other binding lookup composes with.
10
+ * - `findClassBindingInScope` walk scope.bindings + the indexes via
11
+ * `lookupBindingsAt` for a class-kind binding.
11
12
  * - `findOwnedMember` — find a method/field owned by a class def
12
13
  * across all parsed files by (ownerId, simpleName).
13
14
  * - `findExportedDef` — find a file-level exported def (top-of-module
@@ -18,10 +19,44 @@
18
19
  * "resolve member on owner with MRO" pattern. All four are reusable
19
20
  * as-is for TypeScript, Java, Kotlin, Ruby, etc.
20
21
  */
21
- import type { ParsedFile, ScopeId, SymbolDefinition, TypeRef } from '../../../../_shared/index.js';
22
+ import type { BindingRef, ParsedFile, ScopeId, SymbolDefinition, TypeRef } from '../../../../_shared/index.js';
22
23
  import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexes.js';
23
24
  import type { SemanticModel } from '../../model/semantic-model.js';
24
25
  import type { WorkspaceResolutionIndex } from '../workspace-index.js';
26
+ /**
27
+ * Look up binding refs at `scopeId` for `name`, consulting both the
28
+ * finalize-owned `bindings` channel and the post-finalize
29
+ * `bindingAugmentations` channel (see invariant I8 in
30
+ * `contract/scope-resolver.ts`). Finalized refs come first; augmented
31
+ * refs append, deduped by `def.nodeId` so a sibling that's also
32
+ * explicitly imported doesn't double-emit.
33
+ *
34
+ * Returns a shared frozen empty array when neither channel has the
35
+ * name — callers can compare against `=== EMPTY_BINDINGS` if they
36
+ * want a fast-path miss check. The bucket arrays are returned by
37
+ * reference when only one channel populates them; the merged path
38
+ * allocates a fresh array.
39
+ *
40
+ * Walker primitives (`findClassBindingInScope`,
41
+ * `findCallableBindingInScope`, `findExportedDefByName`) and
42
+ * post-finalize passes that read finalized bindings (e.g.
43
+ * `propagateImportedReturnTypes`, `namespace-targets`) MUST go
44
+ * through this helper instead of `scopes.bindings.get(...)` directly,
45
+ * so the augmentation channel is always visible.
46
+ */
47
+ export declare function lookupBindingsAt(scopeId: ScopeId, name: string, scopes: ScopeResolutionIndexes): readonly BindingRef[];
48
+ /**
49
+ * Return the union of bound names at `scopeId` across both the
50
+ * finalized and augmented channels. Companion to `lookupBindingsAt`
51
+ * for callers that need to iterate every name at a scope (e.g.
52
+ * `propagateImportedReturnTypes`). Order is not guaranteed; callers
53
+ * that need stable iteration should sort externally.
54
+ *
55
+ * Fast paths (zero allocation) when at most one channel is populated:
56
+ * returns the underlying `Map.keys()` iterator directly. Only when both
57
+ * channels carry names do we materialize a `Set` for deduplication.
58
+ */
59
+ export declare function namesAtScope(scopeId: ScopeId, scopes: ScopeResolutionIndexes): Iterable<string>;
25
60
  /**
26
61
  * True when a def's `type` names a class-like declaration — every kind
27
62
  * that collapses to `@scope.class` in the scope-extractor query contract.
@@ -48,8 +83,10 @@ export declare function findReceiverTypeBinding(startScope: ScopeId, receiverNam
48
83
  * Walks the scope chain upward and consults TWO sources at each step:
49
84
  * 1. `scope.bindings` — populated during scope-extraction Pass 2 with
50
85
  * local declarations (`origin: 'local'`).
51
- * 2. `indexes.bindings` populated by the cross-file finalize pass
52
- * with import/namespace/wildcard/reexport origins.
86
+ * 2. The cross-file finalized + augmented bindings, via
87
+ * `lookupBindingsAt` (per I8: finalized = canonical immutable
88
+ * output; augmented = post-finalize hooks like
89
+ * `populateNamespaceSiblings`).
53
90
  *
54
91
  * Without (2) we'd miss every cross-file class-receiver call.
55
92
  */
@@ -57,8 +94,9 @@ export declare function findClassBindingInScope(startScope: ScopeId, receiverNam
57
94
  /**
58
95
  * Look up a callable (Function/Method/Constructor) by name in the
59
96
  * given scope's chain. Uses the dual-source pattern (scope.bindings +
60
- * indexes.bindings) so cross-file imports are visible — without it
61
- * free calls to imported functions never resolve via the post-pass.
97
+ * `lookupBindingsAt` for finalized + augmented) so cross-file
98
+ * imports are visible — without it free calls to imported functions
99
+ * never resolve via the post-pass.
62
100
  *
63
101
  * Mirrors `findClassBindingInScope` exactly; only the accepted
64
102
  * def-type predicate differs.
@@ -122,6 +160,11 @@ export declare function findOwnedMember(ownerDefId: string, memberName: string,
122
160
  * excluded.
123
161
  *
124
162
  * Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
125
- * lookup that doesn't live on `SemanticModel`).
163
+ * lookup that doesn't live on `SemanticModel`). This intentionally
164
+ * does NOT call `lookupBindingsAt`: `findExportedDef` answers "what
165
+ * did the target file declare locally at module scope?", while
166
+ * `bindingAugmentations` models importer-side visibility created by
167
+ * post-finalize hooks. Callers that need importer-visible exports use
168
+ * `findExportedDefByName`, which is dual-channel aware.
126
169
  */
127
170
  export declare function findExportedDef(targetFile: string, memberName: string, index: WorkspaceResolutionIndex): SymbolDefinition | undefined;
@@ -1,13 +1,14 @@
1
1
  /**
2
2
  * Scope-chain lookup primitives shared across language providers.
3
3
  *
4
- * Four functions:
4
+ * Five functions:
5
5
  * - `findReceiverTypeBinding` — walk scope.typeBindings up the chain
6
6
  * for a receiver name.
7
- * - `findClassBindingInScope` — walk scope.bindings + indexes.bindings
8
- * (pre-finalize + post-finalize) for a class-kind binding. Dual-
9
- * source is required because the cross-file finalize pass produces
10
- * a separate bindings map that is not merged back into scope.bindings.
7
+ * - `lookupBindingsAt` — read finalized + augmented binding refs at
8
+ * one scope, deduped by `def.nodeId`. The dual-source-aware
9
+ * primitive every other binding lookup composes with.
10
+ * - `findClassBindingInScope` walk scope.bindings + the indexes via
11
+ * `lookupBindingsAt` for a class-kind binding.
11
12
  * - `findOwnedMember` — find a method/field owned by a class def
12
13
  * across all parsed files by (ownerId, simpleName).
13
14
  * - `findExportedDef` — find a file-level exported def (top-of-module
@@ -18,6 +19,80 @@
18
19
  * "resolve member on owner with MRO" pattern. All four are reusable
19
20
  * as-is for TypeScript, Java, Kotlin, Ruby, etc.
20
21
  */
22
+ const EMPTY_BINDINGS = Object.freeze([]);
23
+ /**
24
+ * Look up binding refs at `scopeId` for `name`, consulting both the
25
+ * finalize-owned `bindings` channel and the post-finalize
26
+ * `bindingAugmentations` channel (see invariant I8 in
27
+ * `contract/scope-resolver.ts`). Finalized refs come first; augmented
28
+ * refs append, deduped by `def.nodeId` so a sibling that's also
29
+ * explicitly imported doesn't double-emit.
30
+ *
31
+ * Returns a shared frozen empty array when neither channel has the
32
+ * name — callers can compare against `=== EMPTY_BINDINGS` if they
33
+ * want a fast-path miss check. The bucket arrays are returned by
34
+ * reference when only one channel populates them; the merged path
35
+ * allocates a fresh array.
36
+ *
37
+ * Walker primitives (`findClassBindingInScope`,
38
+ * `findCallableBindingInScope`, `findExportedDefByName`) and
39
+ * post-finalize passes that read finalized bindings (e.g.
40
+ * `propagateImportedReturnTypes`, `namespace-targets`) MUST go
41
+ * through this helper instead of `scopes.bindings.get(...)` directly,
42
+ * so the augmentation channel is always visible.
43
+ */
44
+ export function lookupBindingsAt(scopeId, name, scopes) {
45
+ const finalized = scopes.bindings.get(scopeId)?.get(name);
46
+ const augmented = scopes.bindingAugmentations.get(scopeId)?.get(name);
47
+ const fLen = finalized?.length ?? 0;
48
+ const aLen = augmented?.length ?? 0;
49
+ if (fLen === 0 && aLen === 0)
50
+ return EMPTY_BINDINGS;
51
+ if (aLen === 0)
52
+ return finalized;
53
+ if (fLen === 0)
54
+ return augmented;
55
+ const seen = new Set();
56
+ const out = [];
57
+ for (const r of finalized) {
58
+ seen.add(r.def.nodeId);
59
+ out.push(r);
60
+ }
61
+ for (const r of augmented) {
62
+ if (seen.has(r.def.nodeId))
63
+ continue;
64
+ out.push(r);
65
+ }
66
+ return out;
67
+ }
68
+ const EMPTY_NAMES = Object.freeze([]);
69
+ /**
70
+ * Return the union of bound names at `scopeId` across both the
71
+ * finalized and augmented channels. Companion to `lookupBindingsAt`
72
+ * for callers that need to iterate every name at a scope (e.g.
73
+ * `propagateImportedReturnTypes`). Order is not guaranteed; callers
74
+ * that need stable iteration should sort externally.
75
+ *
76
+ * Fast paths (zero allocation) when at most one channel is populated:
77
+ * returns the underlying `Map.keys()` iterator directly. Only when both
78
+ * channels carry names do we materialize a `Set` for deduplication.
79
+ */
80
+ export function namesAtScope(scopeId, scopes) {
81
+ const finalized = scopes.bindings.get(scopeId);
82
+ const augmented = scopes.bindingAugmentations.get(scopeId);
83
+ const fSize = finalized?.size ?? 0;
84
+ const aSize = augmented?.size ?? 0;
85
+ if (fSize === 0 && aSize === 0)
86
+ return EMPTY_NAMES;
87
+ if (aSize === 0)
88
+ return finalized.keys();
89
+ if (fSize === 0)
90
+ return augmented.keys();
91
+ const out = new Set(finalized.keys());
92
+ for (const name of augmented.keys())
93
+ out.add(name);
94
+ return out;
95
+ }
21
96
  /**
22
97
  * True when a def's `type` names a class-like declaration — every kind
23
98
  * that collapses to `@scope.class` in the scope-extractor query contract.
@@ -67,8 +142,10 @@ export function findReceiverTypeBinding(startScope, receiverName, scopes) {
67
142
  * Walks the scope chain upward and consults TWO sources at each step:
68
143
  * 1. `scope.bindings` — populated during scope-extraction Pass 2 with
69
144
  * local declarations (`origin: 'local'`).
70
- * 2. `indexes.bindings` populated by the cross-file finalize pass
71
- * with import/namespace/wildcard/reexport origins.
145
+ * 2. The cross-file finalized + augmented bindings, via
146
+ * `lookupBindingsAt` (per I8: finalized = canonical immutable
147
+ * output; augmented = post-finalize hooks like
148
+ * `populateNamespaceSiblings`).
72
149
  *
73
150
  * Without (2) we'd miss every cross-file class-receiver call.
74
151
  */
@@ -89,13 +166,10 @@ export function findClassBindingInScope(startScope, receiverName, scopes) {
89
166
  return b.def;
90
167
  }
91
168
  }
92
- const finalizedScopeBindings = scopes.bindings.get(currentId);
93
- const importedBindings = finalizedScopeBindings?.get(receiverName);
94
- if (importedBindings !== undefined) {
95
- for (const b of importedBindings) {
96
- if (isClassLike(b.def.type))
97
- return b.def;
98
- }
169
+ const importedBindings = lookupBindingsAt(currentId, receiverName, scopes);
170
+ for (const b of importedBindings) {
171
+ if (isClassLike(b.def.type))
172
+ return b.def;
99
173
  }
100
174
  currentId = scope.parent;
101
175
  }
@@ -104,8 +178,9 @@ export function findClassBindingInScope(startScope, receiverName, scopes) {
104
178
  /**
105
179
  * Look up a callable (Function/Method/Constructor) by name in the
106
180
  * given scope's chain. Uses the dual-source pattern (scope.bindings +
107
- * indexes.bindings) so cross-file imports are visible — without it
108
- * free calls to imported functions never resolve via the post-pass.
181
+ * `lookupBindingsAt` for finalized + augmented) so cross-file
182
+ * imports are visible — without it free calls to imported functions
183
+ * never resolve via the post-pass.
109
184
  *
110
185
  * Mirrors `findClassBindingInScope` exactly; only the accepted
111
186
  * def-type predicate differs.
@@ -128,13 +203,10 @@ export function findCallableBindingInScope(startScope, callableName, scopes) {
128
203
  }
129
204
  }
130
205
  }
131
- const finalizedScopeBindings = scopes.bindings.get(currentId);
132
- const importedBindings = finalizedScopeBindings?.get(callableName);
133
- if (importedBindings !== undefined) {
134
- for (const b of importedBindings) {
135
- if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
136
- return b.def;
137
- }
206
+ const importedBindings = lookupBindingsAt(currentId, callableName, scopes);
207
+ for (const b of importedBindings) {
208
+ if (b.def.type === 'Function' || b.def.type === 'Method' || b.def.type === 'Constructor') {
209
+ return b.def;
138
210
  }
139
211
  }
140
212
  currentId = scope.parent;
@@ -272,12 +344,10 @@ export function findExportedDefByName(name, inScope, scopes, index) {
272
344
  return b.def;
273
345
  }
274
346
  }
275
- const finalized = scopes.bindings.get(currentId)?.get(name);
276
- if (finalized !== undefined) {
277
- for (const b of finalized) {
278
- if (b.def.type === 'Function' || b.def.type === 'Method')
279
- return b.def;
280
- }
347
+ const finalized = lookupBindingsAt(currentId, name, scopes);
348
+ for (const b of finalized) {
349
+ if (b.def.type === 'Function' || b.def.type === 'Method')
350
+ return b.def;
281
351
  }
282
352
  currentId = scope.parent;
283
353
  }
@@ -332,7 +402,12 @@ export function findOwnedMember(ownerDefId, memberName, model) {
332
402
  * excluded.
333
403
  *
334
404
  * Reads from `WorkspaceResolutionIndex.moduleScopeByFile` (scope-tied
335
- * lookup that doesn't live on `SemanticModel`).
405
+ * lookup that doesn't live on `SemanticModel`). This intentionally
406
+ * does NOT call `lookupBindingsAt`: `findExportedDef` answers "what
407
+ * did the target file declare locally at module scope?", while
408
+ * `bindingAugmentations` models importer-side visibility created by
409
+ * post-finalize hooks. Callers that need importer-visible exports use
410
+ * `findExportedDefByName`, which is dual-channel aware.
336
411
  */
337
412
  export function findExportedDef(targetFile, memberName, index) {
338
413
  const moduleScope = index.moduleScopeByFile.get(targetFile);
@@ -8,3 +8,13 @@
8
8
  */
9
9
  /** Whether we're running in development mode (enables verbose console logging). */
10
10
  export declare const isDev: boolean;
11
+ /**
12
+ * Whether scope-resolution dev validators (e.g. `validateBindingsImmutability`)
13
+ * should run AND emit warnings. Off by default in CLI runs to avoid silent
14
+ * O(n) scans on large repos; on in `NODE_ENV=development` or when explicitly
15
+ * opted-in via `VALIDATE_SEMANTIC_MODEL=1`. `VALIDATE_SEMANTIC_MODEL=0` is the
16
+ * explicit off switch and wins over both.
17
+ *
18
+ * Read every call (not memoized) so test setups using `vi.stubEnv` work.
19
+ */
20
+ export declare const isSemanticModelValidatorEnabled: () => boolean;
@@ -8,3 +8,17 @@
8
8
  */
9
9
  /** Whether we're running in development mode (enables verbose console logging). */
10
10
  export const isDev = process.env.NODE_ENV === 'development';
11
+ /**
12
+ * Whether scope-resolution dev validators (e.g. `validateBindingsImmutability`)
13
+ * should run AND emit warnings. Off by default in CLI runs to avoid silent
14
+ * O(n) scans on large repos; on in `NODE_ENV=development` or when explicitly
15
+ * opted-in via `VALIDATE_SEMANTIC_MODEL=1`. `VALIDATE_SEMANTIC_MODEL=0` is the
16
+ * explicit off switch and wins over both.
17
+ *
18
+ * Read every call (not memoized) so test setups using `vi.stubEnv` work.
19
+ */
20
+ export const isSemanticModelValidatorEnabled = () => {
21
+ if (process.env.VALIDATE_SEMANTIC_MODEL === '0')
22
+ return false;
23
+ return process.env.NODE_ENV === 'development' || process.env.VALIDATE_SEMANTIC_MODEL === '1';
24
+ };
@@ -15,7 +15,7 @@ import Ruby from 'tree-sitter-ruby';
15
15
  import { createRequire } from 'node:module';
16
16
  import { SupportedLanguages } from '../../../_shared/index.js';
17
17
  import { getProvider } from '../languages/index.js';
18
- import { getTreeSitterBufferSize, TREE_SITTER_MAX_BUFFER } from '../constants.js';
18
+ import { getTreeSitterBufferSize, getTreeSitterContentByteLength, TREE_SITTER_MAX_BUFFER, } from '../constants.js';
19
19
  // tree-sitter-swift is an optionalDependency — may not be installed
20
20
  const _require = createRequire(import.meta.url);
21
21
  let Swift = null;
@@ -1001,7 +1001,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1001
1001
  }
1002
1002
  for (const file of files) {
1003
1003
  // Skip files larger than the max tree-sitter buffer (32 MB)
1004
- if (file.content.length > TREE_SITTER_MAX_BUFFER)
1004
+ if (getTreeSitterContentByteLength(file.content) > TREE_SITTER_MAX_BUFFER)
1005
1005
  continue;
1006
1006
  // Vue SFC preprocessing: extract <script> block content
1007
1007
  let parseContent = file.content;
@@ -1019,7 +1019,7 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1019
1019
  let tree;
1020
1020
  try {
1021
1021
  tree = parser.parse(parseContent, undefined, {
1022
- bufferSize: getTreeSitterBufferSize(parseContent.length),
1022
+ bufferSize: getTreeSitterBufferSize(parseContent),
1023
1023
  });
1024
1024
  }
1025
1025
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.4-rc.3",
3
+ "version": "1.6.4-rc.4",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",