From fb47438b25610ea3183b9fb575888d5e506e707e Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Thu, 4 Jun 2026 20:51:04 -0400 Subject: [PATCH 1/2] Skip module pre-warming on incremental indexing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Incremental indexing no longer pre-warms the definition cache for the modules in the change set. The module cache is already warm from the prior from-scratch, and any module a render needs that isn't cached resolves through the on-demand `lookupDefinition` read-through during the visit (PagePool-safe: the sub-prerender materializes its own tab). From-scratch indexing is unchanged — it still runs the realm-wide `.gts`/`.gjs` sweep plus the per-invalidation deps warm, where the module cache is cold by definition. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/runtime-common/index-runner.ts | 58 ++++++------------------- 1 file changed, 14 insertions(+), 44 deletions(-) diff --git a/packages/runtime-common/index-runner.ts b/packages/runtime-common/index-runner.ts index 865ce260a1..c293962417 100644 --- a/packages/runtime-common/index-runner.ts +++ b/packages/runtime-common/index-runner.ts @@ -400,8 +400,8 @@ export class IndexRunner { current.#jobInfo, current.#virtualNetwork, ); - // Announce the job at kickoff — before invalidation and pre-warm — so - // the dashboard shows it immediately. The total starts at 0 and the + // Announce the job at kickoff — before invalidation — so the + // dashboard shows it immediately. The total starts at 0 and the // first `file-visited` fills it in once the counts are known. current.#onProgress?.({ type: 'indexing-started', @@ -434,46 +434,15 @@ export class IndexRunner { } current.#scheduleClearCacheForNextRender(); } - // Still pre-warm, but only the modules this batch will actually - // render. For each invalidation `preWarmModulesTable` primes the - // definition cache for the invalidated module file itself (when - // executable) plus the per-row `boxel_index` deps of the invalidated - // cards (and the `adoptsFrom` module of a novel `.json`). Front- - // loading those before the visit phase lets a dependent card's render - // hit the cache instead of firing a same-affinity sub-`prerenderModule` - // mid-render — the per-invalidation warming, bounded by invalidation - // size rather than realm size. - // - // The empty base set drops only the realm-wide `.gts`/`.gjs` sweep. - // That sweep exists to prime sibling modules a card references by - // string (which never appear in any instance's runtime deps) and is - // worth its O(realm) cost only on from-scratch, where the cache is - // cold by definition. On an incremental the cache is already warm from - // the prior from-scratch, and any miss resolves through the on-demand + // Incremental indexing does no module pre-warming. The module cache + // is already warm from the prior from-scratch, and any module a + // render needs that isn't cached resolves through the on-demand // `lookupDefinition` read-through during the visit (PagePool-safe: the - // sub-prerender materializes its own tab). Skipping it also avoids the - // filesystem-mtimes walk this path would otherwise run only to build - // the sweep. - // Pre-warm reports each warmed module as a `file-visited`; modules and - // the files visited below share one `totalFiles` so the dashboard bar - // spans both phases. + // sub-prerender materializes its own tab). Leaning on the read-through + // keeps this path's cost bounded by the cards it actually visits + // instead of fanning out a separate module-warming pass. let filesCompleted = 0; - let preWarmedCount = await current.preWarmModulesTable( - invalidations, - [], - ({ moduleUrl, filesCompleted: completed, totalFiles }) => { - filesCompleted = completed; - current.#onProgress?.({ - type: 'file-visited', - realmURL: current.realmURL.href, - jobId: current.#jobInfo.jobId, - url: moduleUrl, - filesCompleted, - totalFiles, - }); - }, - ); - let totalFiles = preWarmedCount + invalidations.length; + let totalFiles = invalidations.length; let hrefs = urls.map((u) => u.href); let resumedRows = current.batch.resumedRows; @@ -716,10 +685,11 @@ export class IndexRunner { // this layer the search fires a same-affinity `prerenderModule` // mid-card-render at lookup time, which is the wait-shape the // PagePool's tab-materialization for module/command callers is - // meant to relieve. Populated only on from-scratch indexing, where - // the module cache is cold; incrementals pass an empty base set and - // rely on the cache the last from-scratch left warm (the cost of - // this sweep is O(realm module count), not O(files changed)). + // meant to relieve. This realm-wide sweep runs only on from-scratch + // indexing, where the module cache is cold; incremental indexing does + // no pre-warming and relies on the cache the last from-scratch left + // warm (the cost of this sweep is O(realm module count), not O(files + // changed)). // // `.gts` / `.gjs` only is an optimization, not a correctness gate: // `.ts` / `.js` files CAN host `CardDef` (e.g. command-input From 40c5a7ae41b9e4a67d010186519eb809f6718c01 Mon Sep 17 00:00:00 2001 From: Hassan Abdel-Rahman Date: Thu, 4 Jun 2026 21:01:09 -0400 Subject: [PATCH 2/2] Tighten incremental no-pre-warm comments to the actual mechanism MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Describe how query-backed field expansion stays correct without pre-warming: a prerender `_search` reads the `queryFieldDefs` pre-extracted onto each result instance's stored meta (`populateQueryFieldsFromMeta`), so it needs no `modules`-table row for the queried type, and the prerender-search definition path is cache-only by design. Drops the imprecise "cache is already warm from the prior from-scratch" claim — the realm-wide sweep is from-scratch-only because that is when the cache is cold, not because incremental assumes a warm one. Co-Authored-By: Claude Opus 4.8 (1M context) --- packages/runtime-common/index-runner.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/packages/runtime-common/index-runner.ts b/packages/runtime-common/index-runner.ts index c293962417..11ebc65302 100644 --- a/packages/runtime-common/index-runner.ts +++ b/packages/runtime-common/index-runner.ts @@ -434,13 +434,16 @@ export class IndexRunner { } current.#scheduleClearCacheForNextRender(); } - // Incremental indexing does no module pre-warming. The module cache - // is already warm from the prior from-scratch, and any module a - // render needs that isn't cached resolves through the on-demand - // `lookupDefinition` read-through during the visit (PagePool-safe: the - // sub-prerender materializes its own tab). Leaning on the read-through - // keeps this path's cost bounded by the cards it actually visits - // instead of fanning out a separate module-warming pass. + // Incremental indexing does no module pre-warming. Query-backed field + // expansion during a prerender `_search` reads the `queryFieldDefs` + // pre-extracted onto each result instance's stored meta + // (`populateQueryFieldsFromMeta`), so it needs no `modules`-table row + // for the queried type. The prerender-search definition path is + // cache-only by design — a read-through there would re-enter the same + // affinity tab mid-render and deadlock the pool — while definition + // needs outside it resolve through the on-demand `lookupDefinition` + // read-through. There is nothing left for a pre-warm pass to + // front-load here. let filesCompleted = 0; let totalFiles = invalidations.length; @@ -686,10 +689,9 @@ export class IndexRunner { // mid-card-render at lookup time, which is the wait-shape the // PagePool's tab-materialization for module/command callers is // meant to relieve. This realm-wide sweep runs only on from-scratch - // indexing, where the module cache is cold; incremental indexing does - // no pre-warming and relies on the cache the last from-scratch left - // warm (the cost of this sweep is O(realm module count), not O(files - // changed)). + // indexing, where the module cache is cold by definition; incremental + // indexing does no pre-warming (the cost of this sweep is O(realm + // module count), not O(files changed)). // // `.gts` / `.gjs` only is an optimization, not a correctness gate: // `.ts` / `.js` files CAN host `CardDef` (e.g. command-input