Skip to content

fix(cdn): defer artifact refresh UI until app is foregrounded#2260

Closed
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-remediation-c136
Closed

fix(cdn): defer artifact refresh UI until app is foregrounded#2260
cursor[bot] wants to merge 1 commit into
mainfrom
cursor/critical-bug-remediation-c136

Conversation

@cursor
Copy link
Copy Markdown

@cursor cursor Bot commented Jun 5, 2026

Bug and impact

Trigger: A CDN-backed app with ENABLE_ARTIFACT_REFRESH=true is backgrounded while _refreshIfStale() fetches a newer manifest. The fetch completes before the user returns to the app.

Impact: Screens can rebuild with updated resources but stale translations. Users see outdated i18n strings until a full app restart. This is significant user-facing breakage for localized CDN apps.

Root cause

PR #2252 correctly ordered notifyAppBundleChanges() → translation refresh → ResourceRefreshEvent in _handlePendingUpdate(), but _refreshIfStale() started calling _handlePendingUpdate() whenever Ensemble().getConfig() != null—including when invoked from onAppLifecycleStateChanged(paused|inactive).

That contradicts the existing background contract ("fetch and update cache but DON'T fire events"). In background, _refreshTranslationsAtRuntime() silently skips when no BuildContext exists, yet _fireManifestRefreshEvent() still runs and clears _hasPendingUpdate. On resume, the pending handler does not run again, so translations never refresh.

Fix

  • Gate immediate _handlePendingUpdate() delivery on app lifecycle: only when WidgetsBinding.instance.lifecycleState is null (cold start) or resumed.
  • Otherwise set _hasPendingUpdate = true so the existing resume path runs the full ordered update with a valid context.
  • Extracted cdnShouldDeliverArtifactRefreshImmediately() for testability.

Validation

  • Added unit tests in modules/ensemble/test/cdn_provider_test.dart for lifecycle gating.
  • Run from modules/ensemble: flutter test test/cdn_provider_test.dart

Duplicate check

Open in Web View Automation 

PR #2252 fixed the appBundle/translation ordering race on resume, but
_refreshIfStale() began calling _handlePendingUpdate() whenever config
was set—even when the refresh was triggered from a paused/inactive
lifecycle callback. That contradicts the background fetch contract
("update cache but DON'T fire events") and can leave translations
stale: _refreshTranslationsAtRuntime() skips when no BuildContext exists,
yet _fireManifestRefreshEvent() still runs and clears _hasPendingUpdate,
so resume never retries i18n refresh.

Only deliver refresh events immediately when lifecycle is resumed (or
unknown during cold start). Otherwise set _hasPendingUpdate for the
existing resume handler.

Co-authored-by: Sharjeel Yunus <sharjeelyunus@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants