diff --git a/gitnexus/src/core/ingestion/languages/python/simple-hooks.ts b/gitnexus/src/core/ingestion/languages/python/simple-hooks.ts index 399d7c891..914ae235c 100644 --- a/gitnexus/src/core/ingestion/languages/python/simple-hooks.ts +++ b/gitnexus/src/core/ingestion/languages/python/simple-hooks.ts @@ -63,10 +63,7 @@ export function pythonImportOwningScope( * * Implemented as an explicit pass-through so reviewers don't have to * re-derive the analysis from absence. */ -export function pythonShouldShadow( - _scope: Scope, - _bindings: readonly BindingRef[], -): boolean { +export function pythonShouldShadow(_scope: Scope, _bindings: readonly BindingRef[]): boolean { return true; } @@ -77,7 +74,5 @@ export function pythonShouldShadow( * non-Function scopes. */ export function pythonReceiverBinding(functionScope: Scope): TypeRef | null { if (functionScope.kind !== 'Function') return null; - return ( - functionScope.typeBindings.get('self') ?? functionScope.typeBindings.get('cls') ?? null - ); + return functionScope.typeBindings.get('self') ?? functionScope.typeBindings.get('cls') ?? null; } diff --git a/gitnexus/src/core/ingestion/resolve-references.ts b/gitnexus/src/core/ingestion/resolve-references.ts index 0c375961a..be95df7ba 100644 --- a/gitnexus/src/core/ingestion/resolve-references.ts +++ b/gitnexus/src/core/ingestion/resolve-references.ts @@ -179,9 +179,7 @@ function lookupForSite( case 'call': { const opts: Parameters[2] = { ...(site.arity !== undefined ? { callsite: { arity: site.arity } } : {}), - ...(site.explicitReceiver !== undefined - ? { explicitReceiver: site.explicitReceiver } - : {}), + ...(site.explicitReceiver !== undefined ? { explicitReceiver: site.explicitReceiver } : {}), }; return methodRegistry.lookup(site.name, site.inScope, opts); } diff --git a/gitnexus/test/unit/call-processor.test.ts b/gitnexus/test/unit/call-processor.test.ts index 4bbc5d4b0..f8c72b28e 100644 --- a/gitnexus/test/unit/call-processor.test.ts +++ b/gitnexus/test/unit/call-processor.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { processCalls, processCallsFromExtracted, @@ -2227,10 +2227,22 @@ describe('processCallsFromExtracted — interface dispatch', () => { describe('processCalls — D0 MRO fast path (SM-10)', () => { let graph: ReturnType; let ctx: ResolutionContext; + let prevRegistryPython: string | undefined; beforeEach(() => { graph = createKnowledgeGraph(); ctx = createResolutionContext(); + // These tests exercise the LEGACY call-resolution DAG directly + // using .py fixtures. Python defaults to registry-primary now + // (MIGRATED_LANGUAGES), which gates call-processor out for + // Python files. Force the flag off so the legacy DAG runs. + prevRegistryPython = process.env['REGISTRY_PRIMARY_PYTHON']; + process.env['REGISTRY_PRIMARY_PYTHON'] = 'false'; + }); + + afterEach(() => { + if (prevRegistryPython === undefined) delete process.env['REGISTRY_PRIMARY_PYTHON']; + else process.env['REGISTRY_PRIMARY_PYTHON'] = prevRegistryPython; }); const setupChildParent = () => { @@ -2974,10 +2986,20 @@ describe('processAssignmentsFromExtracted', () => { describe('D2 widen path: lookupCallableByName via module alias', () => { let graph: ReturnType; let ctx: ResolutionContext; + let prevRegistryPython: string | undefined; beforeEach(() => { graph = createKnowledgeGraph(); ctx = createResolutionContext(); + // Force legacy DAG for .py fixtures — Python is registry-primary + // by default (MIGRATED_LANGUAGES) which would gate processCalls out. + prevRegistryPython = process.env['REGISTRY_PRIMARY_PYTHON']; + process.env['REGISTRY_PRIMARY_PYTHON'] = 'false'; + }); + + afterEach(() => { + if (prevRegistryPython === undefined) delete process.env['REGISTRY_PRIMARY_PYTHON']; + else process.env['REGISTRY_PRIMARY_PYTHON'] = prevRegistryPython; }); it('resolves method via module alias widen using lookupCallableByName', async () => { diff --git a/gitnexus/test/unit/registry-primary-flag.test.ts b/gitnexus/test/unit/registry-primary-flag.test.ts index 20a547e24..759114af5 100644 --- a/gitnexus/test/unit/registry-primary-flag.test.ts +++ b/gitnexus/test/unit/registry-primary-flag.test.ts @@ -12,6 +12,7 @@ import { envVarNameFor, isRegistryPrimary, primaryLanguages, + MIGRATED_LANGUAGES, } from '../../src/core/ingestion/registry-primary-flag.js'; // ─── Test isolation ───────────────────────────────────────────────────────── @@ -58,9 +59,12 @@ describe('envVarNameFor', () => { // ─── isRegistryPrimary ───────────────────────────────────────────────────── describe('isRegistryPrimary', () => { - it('returns false by default (no env var set)', () => { + it('returns MIGRATED_LANGUAGES membership by default (no env var set)', () => { + // Ring 3: languages in MIGRATED_LANGUAGES are registry-primary by + // default — operators don't need to set an env var for the rolled-out + // migration to take effect. Unmigrated languages default to false. for (const lang of Object.values(SupportedLanguages)) { - expect(isRegistryPrimary(lang)).toBe(false); + expect(isRegistryPrimary(lang)).toBe(MIGRATED_LANGUAGES.has(lang)); } }); @@ -104,16 +108,21 @@ describe('isRegistryPrimary', () => { it('isolates flags per-language (one on does not affect others)', () => { process.env['REGISTRY_PRIMARY_PYTHON'] = 'true'; expect(isRegistryPrimary(SupportedLanguages.Python)).toBe(true); + // Java and Go are not in MIGRATED_LANGUAGES — default false stays + // false regardless of Python's flag. expect(isRegistryPrimary(SupportedLanguages.Java)).toBe(false); expect(isRegistryPrimary(SupportedLanguages.Go)).toBe(false); }); it('respects a mid-process env-var mutation (no stale cache)', () => { - expect(isRegistryPrimary(SupportedLanguages.Python)).toBe(false); - process.env['REGISTRY_PRIMARY_PYTHON'] = 'true'; - expect(isRegistryPrimary(SupportedLanguages.Python)).toBe(true); - delete process.env['REGISTRY_PRIMARY_PYTHON']; - expect(isRegistryPrimary(SupportedLanguages.Python)).toBe(false); + // Use Java — not in MIGRATED_LANGUAGES — so the unset default is + // deterministically `false`, independent of which languages have + // been flipped to registry-primary. + expect(isRegistryPrimary(SupportedLanguages.Java)).toBe(false); + process.env['REGISTRY_PRIMARY_JAVA'] = 'true'; + expect(isRegistryPrimary(SupportedLanguages.Java)).toBe(true); + delete process.env['REGISTRY_PRIMARY_JAVA']; + expect(isRegistryPrimary(SupportedLanguages.Java)).toBe(false); }); it('handles the CPlusPlus → REGISTRY_PRIMARY_CPP mapping correctly', () => { @@ -129,19 +138,26 @@ describe('isRegistryPrimary', () => { // ─── primaryLanguages ────────────────────────────────────────────────────── describe('primaryLanguages', () => { - it('returns an empty set when no flags are set', () => { - expect(primaryLanguages().size).toBe(0); + it('returns MIGRATED_LANGUAGES when no flags are set', () => { + // Default-on for migrated languages (Ring 3); unmigrated stay off. + const enabled = primaryLanguages(); + expect(enabled.size).toBe(MIGRATED_LANGUAGES.size); + for (const lang of MIGRATED_LANGUAGES) { + expect(enabled.has(lang)).toBe(true); + } }); - it('returns exactly the flipped languages', () => { - process.env['REGISTRY_PRIMARY_PYTHON'] = 'true'; + it('returns exactly the flipped languages (env opts in unmigrated, opts out migrated)', () => { + // Python is migrated (default-on), explicitly off via env var. + // Go and Java are unmigrated (default-off); Go opted in, Java left off. + process.env['REGISTRY_PRIMARY_PYTHON'] = 'false'; process.env['REGISTRY_PRIMARY_GO'] = '1'; - process.env['REGISTRY_PRIMARY_JAVA'] = 'false'; // explicitly off const enabled = primaryLanguages(); - expect(enabled.has(SupportedLanguages.Python)).toBe(true); + expect(enabled.has(SupportedLanguages.Python)).toBe(false); expect(enabled.has(SupportedLanguages.Go)).toBe(true); expect(enabled.has(SupportedLanguages.Java)).toBe(false); - expect(enabled.size).toBe(2); + // Only Go is on: migrated-default-Python overridden off, Go explicitly on. + expect(enabled.size).toBe(1); }); it('returns a plain Set (not a frozen proxy) — consistent shape', () => { diff --git a/gitnexus/test/unit/scope-resolution/python/python-hooks.test.ts b/gitnexus/test/unit/scope-resolution/python/python-hooks.test.ts index 2b425fe78..c729abf5d 100644 --- a/gitnexus/test/unit/scope-resolution/python/python-hooks.test.ts +++ b/gitnexus/test/unit/scope-resolution/python/python-hooks.test.ts @@ -69,37 +69,25 @@ describe('pythonArityCompatibility', () => { it('compatible when argCount sits inside [required, total]', () => { expect( - pythonArityCompatibility( - def({ parameterCount: 3, requiredParameterCount: 1 }), - callsite(2), - ), + pythonArityCompatibility(def({ parameterCount: 3, requiredParameterCount: 1 }), callsite(2)), ).toBe('compatible'); }); it('compatible at the lower bound', () => { expect( - pythonArityCompatibility( - def({ parameterCount: 3, requiredParameterCount: 1 }), - callsite(1), - ), + pythonArityCompatibility(def({ parameterCount: 3, requiredParameterCount: 1 }), callsite(1)), ).toBe('compatible'); }); it('incompatible when argCount is below required', () => { expect( - pythonArityCompatibility( - def({ parameterCount: 3, requiredParameterCount: 2 }), - callsite(1), - ), + pythonArityCompatibility(def({ parameterCount: 3, requiredParameterCount: 2 }), callsite(1)), ).toBe('incompatible'); }); it('incompatible when argCount exceeds total and no varargs are declared', () => { expect( - pythonArityCompatibility( - def({ parameterCount: 2, requiredParameterCount: 0 }), - callsite(5), - ), + pythonArityCompatibility(def({ parameterCount: 2, requiredParameterCount: 0 }), callsite(5)), ).toBe('incompatible'); }); @@ -123,10 +111,7 @@ describe('pythonArityCompatibility', () => { it('"unknown" for negative or non-finite arities (defensive)', () => { expect( - pythonArityCompatibility( - def({ parameterCount: 3, requiredParameterCount: 1 }), - callsite(-1), - ), + pythonArityCompatibility(def({ parameterCount: 3, requiredParameterCount: 1 }), callsite(-1)), ).toBe('unknown'); }); });