flaky test fix: Track card-api and file-api deps directly in the render route#4875
Closed
habdelra wants to merge 5 commits into
Closed
flaky test fix: Track card-api and file-api deps directly in the render route#4875habdelra wants to merge 5 commits into
habdelra wants to merge 5 commits into
Conversation
The base FileDef class is implemented in card-api but consumers import it
from file-api, which is also the URL the indexer references when
invalidating file extracts. Previously file-api only entered the dep set
via the runtime tracker — matrix-service's fileAPIModule importResource
calls loader.import('file-api') during service init, which records the
dep against whatever tracking session is active when the task body runs.
The route lifecycle doesn't guarantee that import lands inside the active
session window: beforeModel triggers lazy realm-service/matrix-service
injection, the import task queues, and then model() opens the tracking
session. On a fresh page right after a BrowserManager.restartBrowser()
the import body usually slips inside the session; on a reused page the
module is already cached, the task never reruns, and file-api silently
drops from the dep set on subsequent renders.
Promote file-api to a deterministic static dep on every file-extract,
and warn (with both the extractor's static deps and the tracker's
snapshot) when the runtime tracker missed it — so a future regression in
the tracking-session lifecycle is debuggable from the prerender log
without re-deriving the race.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Preview deployments |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR stabilizes file-extract dependency reporting by ensuring the base file-api module is always included as a dependency, instead of relying on runtime dependency tracking timing.
Changes:
- Adds
baseRealmand a route-specific logger. - Pins
${baseRealm.url}file-apiinto merged file-extract deps. - Logs a warning when both runtime tracking and extractor deps missed
file-api.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The previous commit pinned file-api as a static dep to mask the race
between matrix-service's `fileAPIModule = importResource(() => 'file-api')`
import and the render route's tracking-session window. That covered the
symptom for the file-extract path but left the underlying problem in
place — and the same race could drop other matrix-service-loaded modules
from any render's dep set.
Address the race directly:
- Stamp `__boxelRenderContext = true` at the start of beforeModel,
BEFORE `restoreSessionsFromStorage()`. RealmResource.tokenRefresher
reads this flag synchronously on its first run; without the stamp it
proceeds to its delayed loginTask, which lazily injects matrix-service
outside any tracking session and lands its loader.import('file-api')
in a no-session void.
- After `beginRuntimeDependencyTrackingSession()` in model(), await
`this.matrixService.ready`. This is both the first access to
matrix-service in the render lifecycle (now guaranteed to be inside
the session window) and the await on its existing public readiness
promise — its loader.import calls inside loadSDK record their deps
against THIS render's session.
Revert the file-extract static-dep workaround; with matrix-service init
deferred to inside the session, the runtime tracker observes file-api
deterministically on every render.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… coupling In RenderRoute.beforeModel, stamp __boxelRenderContext before touching the realm service so RealmResource.tokenRefresher short-circuits and does not lazily inject matrix-service in the prerender Chrome. In RenderRoute.model, immediately after opening the runtime-dep tracking session, await Promise.all of loader.import for card-api and file-api. The loader caches the modules so subsequent renders are a cheap re-track, and the deps are now recorded deterministically against THIS render's session without dragging in matrix-service (and its matrix SDK + client + event-binding setup that the prerender never uses). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the card-api / file-api loader.import await out of model() and into #buildModel so the cached entry in #modelPromises covers the imports. Concurrent same-key model() calls (the render -> child-route transition flow that intentionally relies on #modelPromises for dedup) no longer race past the cache check while the imports are in flight. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
habdelra
commented
May 19, 2026
Restore the original beforeModel ordering. With card-api / file-api tracked deterministically by the loader.import await in #buildModel, the early stamp is no longer needed to keep file-api in the dep set — tokenRefresher's lazy matrix-service injection (if it ever fires) no longer affects render correctness. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
packages/host/app/routes/render.ts:199
- Setting
__boxelRenderContextbeforerestoreSessionsFromStorage()but registering the cleanup only afterward means any exception during session restore (for example, malformed JSON in the sessions localStorage entry) leaves the global render-context flag set after the transition aborts. Register cleanup or use a try/finally before calling code that can throw so later host behavior is not incorrectly treated as prerender context.
this.#authGuard.register();
Contributor
Author
|
this approach seems to have introduced a lot of instability. backing off on it |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Realm Server Tests on PR #4872 failed at:
The file extract's returned
depsis supposed to includehttps://cardstack.com/base/file-api— that's the public URL the indexer references when deciding whether to invalidate a previously-extracted file. The assertion was relying on the runtime-dependency tracker to surface that URL, but the path that actually placed it there was incidental:matrix-service.fileAPIModule = importResource(() => 'file-api')happens to callloader.import('file-api')during matrix-service construction, andtrackRuntimeModuleDependencyruns synchronously inside that call. Whether it landed inside the route's active tracking-session window depended on microtask scheduling — fine on a fresh page, flaky after aBrowserManager.restartBrowser()(prior test 68's recovery render) or any other timing perturbation.The render route doesn't actually need matrix-service for anything; it only needed the dep recorded.
Solution
In
RenderRoute.#buildModel, awaitPromise.allofthis.loaderService.loader.importfor bothcard-apiandfile-api. This records each as a runtime dep of the current render against the active tracking session. The loader caches the modules so subsequent renders re-track without re-evaluating, and putting the await inside#buildModelkeeps the promise stored in#modelPromisescovering the imports so concurrent same-keymodel()calls dedup correctly.Files
packages/host/app/routes/render.tsTest plan
file-apiindepsconsistently