mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
fix(scope-resolution): rank production anchors above test fixtures
Found by testing R3-4 on the reporting repo instead of on its fixtures. Anchoring
returned literals took `wickRatio` from 6 definitions to 13 — and backend reads
still resolved to nothing, because SEVEN of the new JavaScript anchors compete
and four of them are in `tests/`. A test constructs throwaway shapes carrying
production field names; a read in shipped code cannot mean one of them.
Applied before the declared/return-shape split, because "is this the shipped
program" is the stronger signal — a declaration inside a test fixture is still a
test fixture. Skipped when the READER is itself a test, since a read there
legitimately means the test's own shape.
The first version of this test was vacuous and the mutation check caught it: the
reader sat in the same file as the production anchor, so the same-file tier
resolved it whether or not this tier existed. The reader now lives in a file
that imports neither anchor, which leaves production-vs-test as the only thing
that can decide.
Honest about what this does NOT do: it narrows `wickRatio` from seven candidates
to three, and three functions in different files each returning that field is
GENUINELY ambiguous — refusing is correct, and the ambiguity is now counted and
named rather than silent. The reported question ("who reads wickRatio?") is
answerable only where one producer exists; where several do, the honest answer
is the list of producers, which R3-4 made nameable for the first time.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
af5eec5c05
commit
c764847a90
5 changed files with 60 additions and 0 deletions
|
|
@ -69,6 +69,7 @@ import type { ScopeResolutionIndexes } from '../../model/scope-resolution-indexe
|
|||
import type { GraphNodeLookup } from '../graph-bridge/node-lookup.js';
|
||||
import { resolveCallerGraphId } from '../graph-bridge/ids.js';
|
||||
import { callableFlowSiteKey } from './callable-value-flow.js';
|
||||
import { isTestFile } from '../../entry-point-scoring.js';
|
||||
import { getLanguageFromFilename } from 'gitnexus-shared';
|
||||
|
||||
/** Language a definition lives in, for reporting which anchor a reader cannot reach. */
|
||||
|
|
@ -310,6 +311,21 @@ function narrowToSingleCandidate(
|
|||
// what guarantees R3-4 cannot change an answer that already resolved before
|
||||
// return shapes were indexed at all.
|
||||
let candidates = candidatesIn;
|
||||
|
||||
// PRODUCTION CODE FIRST. A test constructs throwaway shapes with the same
|
||||
// field names as the thing it exercises — measured on the reporting repo,
|
||||
// four of the seven JavaScript anchors for `wickRatio` are in `tests/` — and
|
||||
// a read in production code cannot mean any of them. Applied before the
|
||||
// declared/return-shape split because "is this the shipped program" is the
|
||||
// stronger signal: a declaration in a test fixture is still a test fixture.
|
||||
//
|
||||
// Only when the READER is production. A read inside a test legitimately means
|
||||
// the test's own shape, so this must not fire there.
|
||||
if (!isTestFile(readingFile)) {
|
||||
const production = candidates.filter((c) => !isTestFile(c.filePath));
|
||||
if (production.length > 0) candidates = production;
|
||||
}
|
||||
|
||||
const declared = candidates.filter((c) => !c.fromReturnShape);
|
||||
const ranked = declared.length > 0 ? declared : candidates;
|
||||
|
||||
|
|
|
|||
9
gitnexus/test/fixtures/lang-resolution/javascript-object-properties/fixture-shapes.test.js
vendored
Normal file
9
gitnexus/test/fixtures/lang-resolution/javascript-object-properties/fixture-shapes.test.js
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// A TEST file that constructs a throwaway shape with a production field name.
|
||||
// Measured on the reporting repo: four of seven JavaScript anchors for one
|
||||
// field lived in `tests/`, competing with the three real ones and making every
|
||||
// production read ambiguous. A read in production cannot mean any of these.
|
||||
export function buildTestFixture() {
|
||||
return {
|
||||
productionAndTestField: 'fixture',
|
||||
};
|
||||
}
|
||||
8
gitnexus/test/fixtures/lang-resolution/javascript-object-properties/production-reader.js
vendored
Normal file
8
gitnexus/test/fixtures/lang-resolution/javascript-object-properties/production-reader.js
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// The reader lives in its OWN file and imports neither anchor, so no same-file
|
||||
// or direct-import tier can decide this. What is left is production-vs-test,
|
||||
// which is exactly the tier under test — with a reader beside the production
|
||||
// anchor, the same-file tier resolves it either way and the assertion proves
|
||||
// nothing.
|
||||
export function readsProductionShape(bag) {
|
||||
return bag.productionAndTestField;
|
||||
}
|
||||
|
|
@ -45,3 +45,11 @@ export const anonHolder = [
|
|||
return { anonReturnKey: row.x };
|
||||
},
|
||||
];
|
||||
|
||||
// The production anchor for a name a test fixture also constructs. A read here
|
||||
// must resolve to THIS one, not to the fixture's.
|
||||
export function buildProductionShape(row) {
|
||||
return {
|
||||
productionAndTestField: row.real,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,25 @@ describe('JavaScript plain-object property access (A1/A5)', () => {
|
|||
expect(ids.some((id) => id.includes('formatSummary.summaryOnlyField'))).toBe(true);
|
||||
});
|
||||
|
||||
// Production code outranks test fixtures. Measured on the reporting repo:
|
||||
// four of the seven JavaScript anchors for one field were in `tests/`,
|
||||
// competing with the three real ones and making every production read
|
||||
// ambiguous. A test builds throwaway shapes with production field names; a
|
||||
// read in shipped code cannot mean one.
|
||||
it('does not let a test fixture compete with the production anchor', () => {
|
||||
expect(readersOf('productionAndTestField')).toContain('readsProductionShape');
|
||||
const ids = Array.from(
|
||||
(result as unknown as { graph: { iterNodes(): Iterable<PropNode> } }).graph.iterNodes(),
|
||||
)
|
||||
.filter((n) => n.label === 'Property')
|
||||
.map((n) => String(n.id));
|
||||
// Both anchors exist — it is the RANKING that differs, not the indexing.
|
||||
expect(ids.some((id) => id.includes('buildProductionShape.productionAndTestField'))).toBe(
|
||||
true,
|
||||
);
|
||||
expect(ids.some((id) => id.includes('buildTestFixture.productionAndTestField'))).toBe(true);
|
||||
});
|
||||
|
||||
// THE GUARANTEE that reconciles this with R2-1b. `sharedWithDeclared` is
|
||||
// both a named-object key and a return-shape key; a read must still resolve
|
||||
// to the DECLARED one, or indexing return shapes would silently move
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue