fix(ci): prettier formatting + Python-migration test adjustments

CI run 24666612657 failed on three jobs. Fixes:

quality/format:
- Prettier --check flagged 3 files after the accumulated branch work.
  Ran prettier --write from repo root (CI's invocation cwd) to apply:
  simple-hooks.ts, resolve-references.ts, python-hooks.test.ts.

tests/{ubuntu,macos,windows} — 9 assertion failures, all traceable to
Python landing in MIGRATED_LANGUAGES (default-on registry-primary):

  - registry-primary-flag.test.ts (3 tests): the 'returns false by
    default' / 'primaryLanguages empty' / 'Python mid-process
    mutation' assertions were written in Ring 2 when MIGRATED_LANGUAGES
    was empty. Rewrote to assert MIGRATED_LANGUAGES membership is the
    default, use Java (unmigrated) for the no-stale-cache test, and
    verify env overrides work in both directions (migrated-off,
    unmigrated-on).
  - call-processor.test.ts (6 tests in SM-10 + D2-widen blocks):
    these exercise the LEGACY call-resolution DAG on .py fixtures.
    processCalls now gates Python out (isRegistryPrimary === true by
    default), returning 0 edges. Added REGISTRY_PRIMARY_PYTHON=false
    override in the relevant beforeEach + restore in afterEach, so
    the legacy DAG runs for these test-local fixtures without
    affecting the production-default behavior.

Local verification: 4126/4126 unit tests pass, prettier clean.
This commit is contained in:
Gergo Magyar 2026-04-20 17:42:27 +01:00
parent 4cb46ef16a
commit df2d3a8a81
5 changed files with 61 additions and 45 deletions

View file

@ -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;
}

View file

@ -179,9 +179,7 @@ function lookupForSite(
case 'call': {
const opts: Parameters<MethodRegistry['lookup']>[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);
}

View file

@ -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<typeof createKnowledgeGraph>;
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<typeof createKnowledgeGraph>;
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 () => {

View file

@ -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', () => {

View file

@ -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');
});
});