From 2f03b7e26fb121f4192465a9239457602e73ad48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 14 Oct 2026 09:58:59 +0200 Subject: [PATCH 1/2] fix(gates): stop the closure walker following JSON data imports A `with { type: 'json' }` import is data with no edges, and the committed-tree reader only holds walked TypeScript sources. Once #3517 put JSON fixtures in platform-android's closure, the merge-base walk threw and eager-closure-budgets failed for every PR. Co-Authored-By: Claude Opus 5.5 --- .../__tests__/eager-closure-budgets.test.ts | 18 ++++++++++++++++++ src/__tests__/eager-import-closure.fixtures.ts | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/scripts/__tests__/eager-closure-budgets.test.ts b/scripts/__tests__/eager-closure-budgets.test.ts index 50b32056c5..5526714357 100644 --- a/scripts/__tests__/eager-closure-budgets.test.ts +++ b/scripts/__tests__/eager-closure-budgets.test.ts @@ -473,6 +473,24 @@ test('the committed-tree reader walks what was committed, not the working direct expect(eagerClosureGraphOf(entry).size, 'working tree: one edge').toBe(2); }); +test('a JSON data import is not an edge, so the committed-tree walk does not read it', () => { + // The committed reader holds only walked TypeScript sources; following a `with { type: 'json' }` + // import into it threw at the merge-base once a façade closure first reached a JSON fixture. + const repo = mkGitFixtureRepo('eager-closure-json-import-'); + const entry = path.join(repo, 'packages/demo/src/entry.ts'); + fs.writeFileSync(path.join(repo, 'packages/demo/src/capture.raw.json'), '{"nodes":[]}\n'); + fs.writeFileSync( + entry, + "import capture from './capture.raw.json' with { type: 'json' };\nexport const a = capture;\n", + ); + execFileSync('git', ['add', '-A'], { cwd: repo }); + execFileSync('git', ['commit', '-qm', 'json import'], { cwd: repo }); + + const committed = createCommittedSourceTree(repo, 'HEAD'); + expect([...eagerClosureGraphOf(entry, committed).keys()]).toEqual([entry]); + expect([...eagerClosureGraphOf(entry).keys()]).toEqual([entry]); +}); + test('a renamed entry is followed to its path at the base, not treated as first-introduced', () => { const repo = mkGitFixtureRepo('eager-closure-renamed-entry-'); execFileSync('git', ['mv', 'packages/demo/src/entry.ts', 'packages/demo/src/moved.ts'], { diff --git a/src/__tests__/eager-import-closure.fixtures.ts b/src/__tests__/eager-import-closure.fixtures.ts index 384ccb1078..3da301c7a7 100644 --- a/src/__tests__/eager-import-closure.fixtures.ts +++ b/src/__tests__/eager-import-closure.fixtures.ts @@ -208,7 +208,9 @@ const workingTreeReader: SourceTreeReader = { */ function resolveRelative(from: string, specifier: string, tree: SourceTreeReader): string | null { const candidate = path.resolve(path.dirname(from), specifier); - if (tree.isFile(candidate)) return candidate; + // A JSON import (`with { type: 'json' }`) is data with no edges, and the committed tree reader + // only reads walked TypeScript sources, so it is not an edge. + if (tree.isFile(candidate)) return candidate.endsWith('.ts') ? candidate : null; for (const suffix of ['.ts', '/index.ts']) { if (tree.exists(`${candidate}${suffix}`)) return `${candidate}${suffix}`; } From a32cee793292a629effdeacef7a0a3d86b905333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 14 Oct 2026 10:02:21 +0200 Subject: [PATCH 2/2] chore(gates): retire the three #3517 approval rows main now carries Co-Authored-By: Claude Opus 5.5 --- scripts/__tests__/eager-closure-budgets.ts | 34 +--------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/scripts/__tests__/eager-closure-budgets.ts b/scripts/__tests__/eager-closure-budgets.ts index 53f5571dfa..01a719f264 100644 --- a/scripts/__tests__/eager-closure-budgets.ts +++ b/scripts/__tests__/eager-closure-budgets.ts @@ -132,39 +132,7 @@ export const NEW_ENTRY_CEILINGS: Readonly> = Objec */ export const APPROVED_OVER_CEILING: Readonly< Record -> = Object.freeze({ - // The single owner of the client-collected replay source bundles tests carry (#1802). Its - // weight is the real collector it exists to share, not fixture code. - 'packages/replay-port/src/replay-script-source.fixtures.ts': { - issue: '#3517', - reason: - 'Test-only request fixture whose whole contract is running the real client collector ' + - '(script-source-bundle + source-discovery) so the root and daemon test trees cannot ' + - 'drift from it; the weight is the ad-script format parser behind resolveReplayFormat. ' + - 'Making that lazy would fork a second collector, which is the duplication #3517 removes. ' + - 'No platform implementation; never bundled.', - owner: 'replay-port', - }, - // The two Android fixture subpaths are by design the REAL walker on archived device trees: - // their whole contract is running the shipped projection over captures no hand-written node - // could stand in for, so their closure IS the mechanics the subpath exposes. - 'packages/platform-android/src/snapshot-capture.fixtures.ts': { - issue: '#3517', - reason: - 'Test-only Android capture builders over the package mechanics facade, shared by the ' + - 'daemon and root test trees; a fixture that simulated the walk would prove nothing about ' + - 'production inclusion decisions. Test-only subpath; never bundled.', - owner: 'platform-android', - }, - 'packages/platform-android/src/ui-hierarchy.fixtures.ts': { - issue: '#3517', - reason: - 'Test-only archived device trees (IME, QS shade) plus the real non-raw/interactive walks ' + - 'over them, shared by the daemon and root test trees; the closure is the mechanics facade ' + - 'the fixtures must run. Test-only subpath; never bundled.', - owner: 'platform-android', - }, -}); +> = Object.freeze({}); /** The category is a function of the path, never a hand-written column. */ export function entryCategoryOf(entryFile: string): EntryCategory {