fix: bound the auth wait in startStoreBootstrap with a timeout - #15032
fix: bound the auth wait in startStoreBootstrap with a timeout#15032claude[bot] wants to merge 1 commit into
Conversation
Firebase auth resolution was awaited with no timeout, so a stale session or a broken auth response left bootstrap hanging forever with no feedback. Add a 16s timeout (matching the router and WorkspaceAuthGate conventions), retry once after a short backoff, and report a final timeout via Sentry instead of blocking bootstrap indefinitely.
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1804 passed, 0 failed · 1 flaky📊 Browser Reports
📦 Bundle: 8.69 MB gzip 🔴 +15 BDetailsSummary
Category Glance App Entry Points — 3.67 kB (baseline 3.67 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.36 MB (baseline 1.36 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed / 2 unchanged Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces Status: 17 unchanged Panels & Settings — 565 kB (baseline 565 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens Status: 26 unchanged User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 BAuthentication, profile, and account management bundles Status: 10 unchanged Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors Status: 8 unchanged UI Components — 67.1 kB (baseline 67.1 kB) • ⚪ 0 BReusable component library chunks Status: 14 unchanged Data & Services — 3.5 MB (baseline 3.5 MB) • ⚪ 0 BStores, services, APIs, and repositories Status: 17 unchanged Utilities & Hooks — 550 kB (baseline 550 kB) • 🔴 +38 BHelpers, composables, and utility bundles
Status: 1 added / 1 removed / 37 unchanged Vendor & Third-Party — 16.3 MB (baseline 16.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 17 unchanged Other — 14 MB (baseline 14 MB) • ⚪ 0 BBundles that do not match a named category
Status: 1 added / 1 removed / 285 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-11T03:48:48.558Z",
"gitSha": "fe91ab3264d7b6bed70a7fc6d2abbc05dfbd1bdb",
"branch": "fix/bootstrap-auth-wait-timeout",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2189.236000000051,
"styleRecalcs": 6,
"styleRecalcDurationMs": 5.287,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 577.744,
"heapDeltaBytes": 5908400,
"heapUsedBytes": 70282796,
"domNodes": 12,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 21.554,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 1999.6070000000827,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.786999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 482.39200000000005,
"heapDeltaBytes": 5851960,
"heapUsedBytes": 70246648,
"domNodes": 20,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 15.148,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1836.6479999999683,
"styleRecalcs": 74,
"styleRecalcDurationMs": 33.410999999999994,
"layouts": 12,
"layoutDurationMs": 3.2430000000000003,
"taskDurationMs": 845.236,
"heapDeltaBytes": -13121528,
"heapUsedBytes": 51154660,
"domNodes": -283,
"jsHeapTotalBytes": 24879104,
"scriptDurationMs": 107.551,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1840.5449999999064,
"styleRecalcs": 75,
"styleRecalcDurationMs": 37.373000000000005,
"layouts": 12,
"layoutDurationMs": 3.74,
"taskDurationMs": 858.17,
"heapDeltaBytes": -11957180,
"heapUsedBytes": 52307568,
"domNodes": -280,
"jsHeapTotalBytes": 23830528,
"scriptDurationMs": 109.492,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1737.833999999964,
"styleRecalcs": 32,
"styleRecalcDurationMs": 15.194999999999999,
"layouts": 6,
"layoutDurationMs": 0.598,
"taskDurationMs": 372.581,
"heapDeltaBytes": 9008748,
"heapUsedBytes": 73505700,
"domNodes": 77,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 18.270000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1715.5820000000404,
"styleRecalcs": 32,
"styleRecalcDurationMs": 13.871000000000002,
"layouts": 6,
"layoutDurationMs": 0.6180000000000001,
"taskDurationMs": 364.592,
"heapDeltaBytes": 8527824,
"heapUsedBytes": 72801256,
"domNodes": 76,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 17.507,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 565.825000000018,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.189,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 373.67999999999995,
"heapDeltaBytes": -11248248,
"heapUsedBytes": 53071064,
"domNodes": 20,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 55.882,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66666666666665,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 581.0419999999112,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.144,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 359.86,
"heapDeltaBytes": -11070116,
"heapUsedBytes": 53179692,
"domNodes": 16,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 54.964,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2032.6109999999744,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.114999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 656.725,
"heapDeltaBytes": 7270632,
"heapUsedBytes": 66903204,
"domNodes": -283,
"jsHeapTotalBytes": 3248128,
"scriptDurationMs": 86.272,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2027.0799999999554,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.922000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 652.439,
"heapDeltaBytes": 7923292,
"heapUsedBytes": 67888576,
"domNodes": -283,
"jsHeapTotalBytes": 3772416,
"scriptDurationMs": 87.621,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2179.8350000000255,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.326000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1205.083,
"heapDeltaBytes": 4330512,
"heapUsedBytes": 64773516,
"domNodes": -284,
"jsHeapTotalBytes": 3702784,
"scriptDurationMs": 388.85800000000006,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2154.765999999995,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.575999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1243.512,
"heapDeltaBytes": -4950868,
"heapUsedBytes": 55711136,
"domNodes": -284,
"jsHeapTotalBytes": 5869568,
"scriptDurationMs": 411.096,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-zoom",
"durationMs": 3181.184999999971,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.984999999999998,
"layouts": 60,
"layoutDurationMs": 8.036,
"taskDurationMs": 1498.398,
"heapDeltaBytes": -2575516,
"heapUsedBytes": 59671216,
"domNodes": -287,
"jsHeapTotalBytes": 5869568,
"scriptDurationMs": 528.8459999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3197.375999999963,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.457000000000004,
"layouts": 60,
"layoutDurationMs": 7.88,
"taskDurationMs": 1465.665,
"heapDeltaBytes": -2085848,
"heapUsedBytes": 60035444,
"domNodes": -287,
"jsHeapTotalBytes": 6131712,
"scriptDurationMs": 509.7229999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2033.5660000000075,
"styleRecalcs": 6,
"styleRecalcDurationMs": 5.423000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 672.056,
"heapDeltaBytes": 7453152,
"heapUsedBytes": 68249196,
"domNodes": -286,
"jsHeapTotalBytes": 3772416,
"scriptDurationMs": 100.123,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2037.663000000066,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.815999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 660.09,
"heapDeltaBytes": 6813792,
"heapUsedBytes": 67589576,
"domNodes": -283,
"jsHeapTotalBytes": 2985984,
"scriptDurationMs": 93.494,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 584.4490000000064,
"styleRecalcs": 46,
"styleRecalcDurationMs": 9.748,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 404.8109999999999,
"heapDeltaBytes": -10541628,
"heapUsedBytes": 53785768,
"domNodes": 18,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 120.338,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 560.9080000000404,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.917,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 383.00999999999993,
"heapDeltaBytes": -10929320,
"heapUsedBytes": 53853472,
"domNodes": 20,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 117.08499999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2031.2589999999773,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.413000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 499.298,
"heapDeltaBytes": -17532640,
"heapUsedBytes": 46833188,
"domNodes": -279,
"jsHeapTotalBytes": 23306240,
"scriptDurationMs": 12.815999999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2009.8309999999628,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.711,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 459.67400000000004,
"heapDeltaBytes": 5719620,
"heapUsedBytes": 70441004,
"domNodes": 18,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 13.436,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1709.5529999999712,
"styleRecalcs": 76,
"styleRecalcDurationMs": 32.876999999999995,
"layouts": 16,
"layoutDurationMs": 4.348999999999999,
"taskDurationMs": 763.632,
"heapDeltaBytes": -17485560,
"heapUsedBytes": 46749928,
"domNodes": -280,
"jsHeapTotalBytes": 23830528,
"scriptDurationMs": 82.559,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1689.029000000005,
"styleRecalcs": 76,
"styleRecalcDurationMs": 33.196000000000005,
"layouts": 16,
"layoutDurationMs": 4.2940000000000005,
"taskDurationMs": 708.1039999999999,
"heapDeltaBytes": -3377400,
"heapUsedBytes": 61054220,
"domNodes": 61,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 82.98899999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-transition-enter",
"durationMs": 1383.9559999998983,
"styleRecalcs": 19,
"styleRecalcDurationMs": 32.068999999999996,
"layouts": 15,
"layoutDurationMs": 11.185,
"taskDurationMs": 916.8859999999999,
"heapDeltaBytes": 5368992,
"heapUsedBytes": 76514068,
"domNodes": 13673,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 33.995,
"eventListeners": 2375,
"totalBlockingTimeMs": 127,
"frameDurationMs": 16.666666666666696,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8281.192999999974,
"styleRecalcs": 249,
"styleRecalcDurationMs": 40.841,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4310.946,
"heapDeltaBytes": 11014628,
"heapUsedBytes": 71075528,
"domNodes": -283,
"jsHeapTotalBytes": 6062080,
"scriptDurationMs": 1300.177,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8209.152000000016,
"styleRecalcs": 249,
"styleRecalcDurationMs": 38.695,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4165.008999999999,
"heapDeltaBytes": 10627672,
"heapUsedBytes": 71073696,
"domNodes": -280,
"jsHeapTotalBytes": 6324224,
"scriptDurationMs": 1252.051,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17751.277000000016,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17724.438,
"heapDeltaBytes": -38585612,
"heapUsedBytes": 167222256,
"domNodes": -8312,
"jsHeapTotalBytes": -9117696,
"scriptDurationMs": 547.202,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.33666666666662,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17134.77899999998,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17105.409,
"heapDeltaBytes": -36678484,
"heapUsedBytes": 167275344,
"domNodes": -8312,
"jsHeapTotalBytes": -10166272,
"scriptDurationMs": 574.664,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21360.479,
"styleRecalcs": 150,
"styleRecalcDurationMs": 19.99999999999996,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21324.762,
"heapDeltaBytes": -30097696,
"heapUsedBytes": 176587868,
"domNodes": -8312,
"jsHeapTotalBytes": -8400896,
"scriptDurationMs": 876.994,
"eventListeners": -16383,
"totalBlockingTimeMs": 393,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 20821.200999999972,
"styleRecalcs": 140,
"styleRecalcDurationMs": 17.154999999999976,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20788.647,
"heapDeltaBytes": -38053696,
"heapUsedBytes": 176917560,
"domNodes": -8312,
"jsHeapTotalBytes": -11808768,
"scriptDurationMs": 861.755,
"eventListeners": -16383,
"totalBlockingTimeMs": 213,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 448.0710000000272,
"styleRecalcs": 13,
"styleRecalcDurationMs": 18.727999999999998,
"layouts": 3,
"layoutDurationMs": 0.6180000000000001,
"taskDurationMs": 102.413,
"heapDeltaBytes": 5060808,
"heapUsedBytes": 68527476,
"domNodes": 126,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 8.902,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 462.66800000000785,
"styleRecalcs": 15,
"styleRecalcDurationMs": 21.169,
"layouts": 3,
"layoutDurationMs": 0.689,
"taskDurationMs": 105.924,
"heapDeltaBytes": 5046948,
"heapUsedBytes": 68477528,
"domNodes": 123,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 9.350999999999999,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #15032 +/- ##
==========================================
- Coverage 81.22% 80.88% -0.35%
==========================================
Files 1872 1872
Lines 126246 123275 -2971
Branches 39585 38279 -1306
==========================================
- Hits 102541 99706 -2835
+ Misses 23140 23002 -138
- Partials 565 567 +2
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 53 files with indirect coverage changes 🚀 New features to boost your workflow:
|
|
Superseded by #15063, which covers the same fix with two important corrections:
#15063 also includes the catch-all route fix for unknown paths (e.g. Closing in favour of #15063. |
…outes (IR-105) (#15063) ## Summary Two durable fixes for the IR-105 splash-screen hang incident. ### Fix 1 — Bootstrap auth wait timeout (`bootstrapStore.ts`) **Root cause:** `until(isInitialized).toBe(true)` and `until(isAuthenticated).toBe(true)` in `startStoreBootstrap` have no timeout. A stale Firebase session or broken auth response hangs bootstrap silently and indefinitely — no error, no log, just a forever splash screen. **Fix:** Extract `waitForCloudAuth()` with: - 16 s timeout (matching `router.ts:176` and `WorkspaceAuthGate.vue:78` — existing convention) - One retry after 3 s backoff - On second timeout: `captureException` to Sentry + **continue bootstrap** rather than blocking This means the worst case is now 35 s to a usable (logged-out) state instead of infinity. ### Fix 2 — Catch-all route for unknown paths (`router.ts`) **Root cause:** A request to e.g. `cloud.comfy.org/woiadawd` gets a 200 + app shell from the static host, Vue boots, finds **no matching route**, and the global auth guard's `until(isInitialized)` then hangs (see Fix 1). Even with Fix 1, a no-match route leaves the app on the splash screen. **Fix:** Add `{ path: '/:pathMatch(.*)*', redirect: '/' }` at the end of the route list. Unknown paths redirect to root; the global auth guard then sends unauthenticated users to `/cloud/login` as normal. ## Red-Green Verification | Commit | Purpose | |--------|---------| | `test: add failing tests for unbounded auth wait in startStoreBootstrap` | 🔴 Proves test catches the bug — the "gives up" test times out in 5 s against the unpatched code | | `fix: bound auth wait in startStoreBootstrap and add catch-all route` | 🟢 Both new tests pass, all 18 tests in the two affected files pass | ## Relation to open PRs - Supersedes / closes the scope of #15032 (`fix/bootstrap-auth-wait-timeout`) — same fix, same tests. If reviewers prefer to land #15032 first this PR can drop Fix 1. - Companion to #15022 (marketing-site redirect) and #15026 (`/login` → `cloud-login` route, already on main). ## Test Plan - [x] `pnpm vitest run src/stores/bootstrapStore.test.ts` — 7/7 pass (including 2 new timeout tests) - [x] `pnpm vitest run src/platform/cloud/onboarding/onboardingCloudRoutes.test.ts` — 11/11 pass - [x] `pnpm typecheck` — clean - [x] `pnpm exec eslint src/stores/bootstrapStore.ts src/router.ts` — clean - [ ] CI green (Playwright, Storybook, Codecov) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: GitHub Action <action@github.com>
<!-- ccr-slack-attribution -->
Requested by Christian Byrne · Slack thread
Summary
startStoreBootstrapawaited Firebase auth (isInitialized,isAuthenticated) with no timeout, so a stale session or a broken auth response could hang bootstrap forever with zero feedback, leaving the user stuck on the splash/loading screen. It now bounds that wait, retries once, and falls back to continuing bootstrap unauthenticated instead of hanging indefinitely.Changes
waitForCloudAuthinsrc/stores/bootstrapStore.tswraps theuntil(isInitialized).toBe(true)/until(isAuthenticated).toBe(true)waits with a 16s timeout (throwOnTimeout: true), matching the existing convention inrouter.tsandWorkspaceAuthGate.vue. On timeout it retries once after a 3s backoff; if the retry also times out it reports the failure viacaptureException(Sentry, same pattern asWorkspaceAuthGate.vue) and letsstartStoreBootstrapcontinue rather than block forever, soloadAuthenticatedStoresstill runs and downstream consumers (e.g.settingStore/GraphCanvas.vue) get a real ready/error signal instead of hanging.Review Focus
currentUser(workspace-token/API-key sessions can be valid while Firebase never resolves), so refusing to proceed here would incorrectly block otherwise-valid sessions. See the added tests inbootstrapStore.test.tsfor the retry-succeeds and retry-exhausted-then-continues paths./loginroute (FE-1549), which is tracked separately (Update login redirect destination URL #15021/Fix legacy Cloud login route #15022).Generated by Claude Code