Skip to content

fix: stop dashboard refresh loops - #1621

Merged
gsxdsm merged 3 commits into
mainfrom
fix/dashboard-refresh-loop-pr
Jun 21, 2026
Merged

fix: stop dashboard refresh loops#1621
gsxdsm merged 3 commits into
mainfrom
fix/dashboard-refresh-loop-pr

Conversation

@gsxdsm

@gsxdsm gsxdsm commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

Returning to an existing dashboard tab no longer risks a blank page or a repeated refresh loop after a rebuild. Built assets now revalidate against the running server before falling back to service-worker cache, so stale JS/CSS cannot keep an old app shell alive.

The version checker also remembers which remote build it already auto-reloaded for. If focus or visibility events keep seeing the same remote build from an old bundle, Fusion suppresses repeat reloads instead of refreshing every time the browser regains focus.

Verification

  • pnpm --filter @fusion/dashboard exec vitest run --project dashboard-app app/__tests__/versionCheck.test.ts app/__tests__/pwa.test.ts
  • pnpm --filter @fusion/dashboard build

Compound Engineering
Codex

Summary by CodeRabbit

  • New Features

    • Enhanced service-worker asset revalidation to ensure rebuilt applications use fresh bundles instead of stale cached versions, preventing blank page display.
  • Bug Fixes

    • Fixed redundant page reloads when version mismatches are detected during tab reuse.

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@gsxdsm, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 883d3ce1-6eb4-4314-8784-b23ecc5b1b54

📥 Commits

Reviewing files that changed from the base of the PR and between cfc580b and 7ddb3ef.

📒 Files selected for processing (1)
  • .changeset/fn-blank-page-service-worker-assets.md
📝 Walkthrough

Walkthrough

Service worker cache-first strategy replaced with network-first for built assets (under /assets/ with script/style/font destinations) to prevent stale bundles; version-check logic now suppresses redundant reloads by tracking already-reloaded remote versions in sessionStorage across navigation.

Changes

Blank Page Prevention via Asset Revalidation and Reload Suppression

Layer / File(s) Summary
Service worker built-asset revalidation
packages/dashboard/app/public/sw.js, packages/dashboard/app/__tests__/pwa.test.ts
Cache version bumped to fusion-cache-v3. Built assets under /assets/ with script/style/font destinations now use network-first strategy with cache fallback. Test validates the detection and caching behavior.
Version-check reload suppression
packages/dashboard/app/versionCheck.ts, packages/dashboard/app/__tests__/versionCheck.test.ts
SessionStorage flag added to track reloaded remote versions. Helpers manage state reads, writes, and clears. Mismatch handler suppresses reloads for versions already reloaded in the same session; first mismatch sets the flag and stores the remote version. Test verifies suppression blocks redundant reload calls.
Release notes and version bump
.changeset/fn-blank-page-service-worker-assets.md
Patch bump documented with guidance that service-worker assets must revalidate before fallback to prevent blank pages from stale bundles.

🎯 3 (Moderate) | ⏱️ ~22 minutes

A rabbit hops through the cache,
Network-first on assets, never a miss,
Reloads suppressed, no more thrash,
Blank pages gone in PWA bliss! 🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: stop dashboard refresh loops' directly and concisely summarizes the main objective of preventing repeated refresh loops, which is the core problem addressed by the changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dashboard-refresh-loop-pr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/dashboard/app/__tests__/pwa.test.ts (1)

121-124: ⚡ Quick win

Cover the font destination in this built-asset contract test.

The production branch includes request.destination === "font", but this regression guard currently validates only script/style.

Suggested patch
   expect(swSource).toContain('request.destination === "script"');
   expect(swSource).toContain('request.destination === "style"');
+  expect(swSource).toContain('request.destination === "font"');
   expect(swSource).toContain('if (isBuiltAssetRequest) {');
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/dashboard/app/__tests__/pwa.test.ts` around lines 121 - 124, The
test in packages/dashboard/app/__tests__/pwa.test.ts is asserting the
built-asset contract against swSource but only checks for script/style
destinations; update the assertions to also require the font destination by
adding an expectation that swSource contains the string 'request.destination ===
"font"'. Keep the other checks (e.g., 'url.pathname.startsWith("/assets/")' and
'if (isBuiltAssetRequest) {') unchanged so the test still validates the
built-asset request handling logic in the service worker.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/dashboard/app/__tests__/pwa.test.ts`:
- Around line 121-124: The test in packages/dashboard/app/__tests__/pwa.test.ts
is asserting the built-asset contract against swSource but only checks for
script/style destinations; update the assertions to also require the font
destination by adding an expectation that swSource contains the string
'request.destination === "font"'. Keep the other checks (e.g.,
'url.pathname.startsWith("/assets/")' and 'if (isBuiltAssetRequest) {')
unchanged so the test still validates the built-asset request handling logic in
the service worker.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ac41d34d-5589-4b38-ad9f-d79bc0305189

📥 Commits

Reviewing files that changed from the base of the PR and between c27c321 and cfc580b.

📒 Files selected for processing (5)
  • .changeset/fn-blank-page-service-worker-assets.md
  • packages/dashboard/app/__tests__/pwa.test.ts
  • packages/dashboard/app/__tests__/versionCheck.test.ts
  • packages/dashboard/app/public/sw.js
  • packages/dashboard/app/versionCheck.ts

@greptile-apps

greptile-apps Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR addresses two dashboard refresh-loop bugs: the service worker now uses network-first for built assets to prevent stale cached JS/CSS from surviving a rebuild, and the version checker no longer poisons the RELOADED_REMOTE_VERSION_FLAG when a reload would have been suppressed by an already-set RELOAD_FLAG.

  • sw.js: Cache name bumped to v5 and a network-first-with-cache-fallback strategy added for all built assets (/assets/*, scripts, styles, fonts), replacing the previous cache-first behaviour that allowed stale bundles to stay alive across rebuilds.
  • versionCheck.ts: An alreadyAttempted guard now prevents setReloadedRemoteVersion from being called when the RELOAD_FLAG is already present in sessionStorage, fixing the case where a version-check firing within the 5-second post-reload window would mark a remote version as "already reloaded" without an actual reload ever happening.

Confidence Score: 5/5

Safe to merge — both changes are narrowly scoped fixes with no correctness regressions in the primary paths.

The service-worker change is a straightforward strategy swap backed by a version-bump and matching test update. The versionCheck fix correctly guards setReloadedRemoteVersion behind the same RELOAD_FLAG check used inside reloadOnce, so all three cases (flag set, auto-reload disabled, normal reload) are handled correctly. No logic errors or broken contracts were found in the changed code.

versionCheck.ts — the new alreadyAttempted guard at line 243 has no dedicated test for the specific race window it closes.

Important Files Changed

Filename Overview
packages/dashboard/app/versionCheck.ts Adds alreadyAttempted guard to prevent RELOADED_REMOTE_VERSION_FLAG from being set when reloadOnce would be suppressed by an existing RELOAD_FLAG; logic is correct but uses a verbose inline IIFE that's inconsistent with the identical pattern in reloadOnce, and the specific fix path has no dedicated test.
packages/dashboard/app/public/sw.js Bumps cache name to v5 and adds a network-first strategy for built assets (/assets/*, scripts, styles, fonts), preventing stale content-hashed bundles from being served from the old named cache after a rebuild.
packages/dashboard/app/tests/pwa.test.ts Updates two cache-name assertions from v4 to v5 to match the bumped constant in sw.js.
.changeset/fn-blank-page-service-worker-assets.md Adds a valid changeset entry with proper --- frontmatter fences for a patch-level release note.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[checkVersion called] --> B{version match?}
    B -- yes --> C[clearReloadedRemoteVersion\nexit]
    B -- no --> D{same mismatch\nas last check?}
    D -- no --> E[record mismatch-pending\nexit]
    D -- yes --> F{getReloadedRemoteVersion\n=== remote?}
    F -- yes --> G[reload-suppressed trace\nexit]
    F -- no --> H{RELOAD_FLAG\nalready set?}
    H -- yes: alreadyAttempted=true --> I[skip setReloadedRemoteVersion]
    H -- no: alreadyAttempted=false --> J{autoReloadEnabled?}
    J -- yes --> K[setReloadedRemoteVersion\nremote]
    J -- no --> L[skip setReloadedRemoteVersion]
    K --> M[reloadOnce]
    I --> M
    L --> M
    M --> N{RELOAD_FLAG set?}
    N -- yes --> O[suppress reload\nwarn log]
    N -- no --> P[set RELOAD_FLAG\ncall location.reload]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[checkVersion called] --> B{version match?}
    B -- yes --> C[clearReloadedRemoteVersion\nexit]
    B -- no --> D{same mismatch\nas last check?}
    D -- no --> E[record mismatch-pending\nexit]
    D -- yes --> F{getReloadedRemoteVersion\n=== remote?}
    F -- yes --> G[reload-suppressed trace\nexit]
    F -- no --> H{RELOAD_FLAG\nalready set?}
    H -- yes: alreadyAttempted=true --> I[skip setReloadedRemoteVersion]
    H -- no: alreadyAttempted=false --> J{autoReloadEnabled?}
    J -- yes --> K[setReloadedRemoteVersion\nremote]
    J -- no --> L[skip setReloadedRemoteVersion]
    K --> M[reloadOnce]
    I --> M
    L --> M
    M --> N{RELOAD_FLAG set?}
    N -- yes --> O[suppress reload\nwarn log]
    N -- no --> P[set RELOAD_FLAG\ncall location.reload]
Loading

Reviews (2): Last reviewed commit: "fix(review): changeset frontmatter, relo..." | Re-trigger Greptile

Comment thread .changeset/fn-blank-page-service-worker-assets.md
Comment thread packages/dashboard/app/versionCheck.ts Outdated
@ghost

ghost commented Jun 21, 2026

Copy link
Copy Markdown

Ready to review this PR? Stage has broken it down into 3 individual chapters for you:

Title
1 Prevent redundant reloads using session storage flag
2 Increment service worker cache version
3 Add changeset for service worker revalidation
Open in Stage

Chapters generated by Stage for commit 7ddb3ef on Jun 21, 2026 7:15am UTC.

gsxdsm and others added 3 commits June 21, 2026 00:12
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
- Add required `---` frontmatter fences to the changeset so the
  changesets CLI picks it up during release (greptile P1).
- Mark the remote version as reloaded only when a reload actually
  proceeds, so a pre-existing RELOAD_FLAG no longer permanently
  suppresses future reloads for that version (greptile P2).
- Bump service-worker cache to v5 after merging main's v4 and update
  the pwa test assertions accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gsxdsm
gsxdsm force-pushed the fix/dashboard-refresh-loop-pr branch from 073eb0f to 7ddb3ef Compare June 21, 2026 07:15
@gsxdsm
gsxdsm merged commit 21f23c6 into main Jun 21, 2026
6 checks passed
@gsxdsm
gsxdsm deleted the fix/dashboard-refresh-loop-pr branch July 24, 2026 06:08
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.

1 participant