From 676854c9622d32b74f9bd784c3093552276ecca0 Mon Sep 17 00:00:00 2001 From: dhgoal <153369624+dhgoal@users.noreply.github.com> Date: Sun, 19 Jul 2026 13:33:04 +0200 Subject: [PATCH] fix(engine): don't charge per-file-skipped files against repo-map's aggregate budget MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #7247 buildRepoMap added each file's byte count to totalSourceBytes before the check that skips a file for exceeding the per-file maxSourceBytes cap. A single oversized file (a vendored/minified asset or generated bundle) that was itself skipped without being parsed still consumed its full byte count against maxTotalSourceBytes, exhausting the aggregate budget so every subsequent small, legitimate file was also marked skipped: resource_limit — a silent, order-dependent near-empty repo map. Move the aggregate accrual to after the per-file cap check so only files that actually pass the per-file cap (and are parsed) count against the aggregate. In-cap files accrue exactly as before. --- .../loopover-engine/src/miner/repo-map.ts | 19 ++++++++++++---- test/unit/repo-map.test.ts | 22 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/packages/loopover-engine/src/miner/repo-map.ts b/packages/loopover-engine/src/miner/repo-map.ts index 8e103ae1fa..40d229dc01 100644 --- a/packages/loopover-engine/src/miner/repo-map.ts +++ b/packages/loopover-engine/src/miner/repo-map.ts @@ -268,11 +268,22 @@ export async function buildRepoMap( } const languageName = resolveRepoMapLanguage(file.path); const sourceBytes = Buffer.byteLength(file.sourceText, "utf8"); + // A file exceeding the per-file cap is skipped without being parsed, so it must NOT consume the + // aggregate parsed-work budget. Counting it before this check let one oversized file (a vendored/ + // minified asset or generated bundle) exhaust maxTotalSourceBytes and force every subsequent small, + // legitimate file to skip too — a silent, order-dependent near-empty map (#7247). Only files that pass + // the per-file cap accrue against the aggregate, exactly as before for in-cap files. + if (sourceBytes > maxSourceBytes) { + entries.push({ + path: file.path, + language: languageName, + symbols: [], + skipped: "resource_limit", + }); + continue; + } totalSourceBytes += sourceBytes; - if ( - sourceBytes > maxSourceBytes || - totalSourceBytes > maxTotalSourceBytes - ) { + if (totalSourceBytes > maxTotalSourceBytes) { entries.push({ path: file.path, language: languageName, diff --git a/test/unit/repo-map.test.ts b/test/unit/repo-map.test.ts index 5108c8041d..90cadc6271 100644 --- a/test/unit/repo-map.test.ts +++ b/test/unit/repo-map.test.ts @@ -229,6 +229,28 @@ describe("buildRepoMap + extractRepoMapSymbols (#4280)", () => { }); }); + it("does not charge a file skipped for the per-file cap against the aggregate budget (#7247)", async () => { + // "function reallyLongName() {}" (28 bytes) exceeds the per-file cap and is skipped WITHOUT being + // parsed; pre-#7247 its bytes were still charged to the aggregate budget, exhausting it and skipping + // the small, legitimate file after it. The aggregate must only account for files actually parsed. + const entries = await buildRepoMap( + [ + { path: "src/huge.ts", sourceText: "function reallyLongName() {}" }, + { path: "src/small.ts", sourceText: "function s() {}" }, + ], + { maxSourceBytes: 20, maxTotalSourceBytes: 20 }, + ); + expect(entries[0]).toEqual({ + path: "src/huge.ts", + language: "typescript", + symbols: [], + skipped: "resource_limit", + }); + // The small file after the skipped-oversized one is still parsed, not starved of aggregate budget. + expect(entries[1]!.skipped).toBeUndefined(); + expect(entries[1]!.symbols.map((symbol) => symbol.name)).toEqual(["s"]); + }); + it("keeps one entry per input file but resource-limits files beyond the file-count budget", async () => { const entries = await buildRepoMap( [