fix: bound auth wait in startStoreBootstrap + catch-all for unknown routes (IR-105) - #15063
Conversation
The two new cloud-mode tests prove that the current until(isAuthenticated) calls have no timeout. One times out in 5s (bootstrap hangs forever when auth never resolves). A second test will go red once the retry boundary is enforced. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two fixes for the IR-105 splash-screen hang: 1. bootstrapStore: replace unbounded until(isAuthenticated) with a 16s timeout (matching router.ts and WorkspaceAuthGate.vue), one 3s retry, then Sentry report + continue. A stale Firebase session can no longer hang bootstrap indefinitely. 2. router: add a catch-all route /:pathMatch(.*)* → / so unknown paths (e.g. /woiadawd) redirect to root instead of leaving the SPA with no route match and a silent splash-screen hang. The global auth guard then routes unauthenticated users to /cloud/login. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1800 passed, 0 failed · 4 flaky📊 Browser Reports
📦 Bundle: 8.82 MB gzip 🔴 +126 kBDetailsSummary
Category Glance App Entry Points — 3.71 kB (baseline 3.67 kB) • 🔴 +32 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.36 MB (baseline 1.36 MB) • 🔴 +95 BGraph editor runtime, canvas, workflow orchestration
Status: 2 added / 2 removed / 1 unchanged Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 13 added / 13 removed / 4 unchanged Panels & Settings — 565 kB (baseline 565 kB) • 🟢 -3 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 16 unchanged User & Accounts — 27.2 kB (baseline 27 kB) • 🔴 +178 BAuthentication, profile, and account management bundles
Status: 8 added / 7 removed / 3 unchanged Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 7 added / 7 removed / 1 unchanged UI Components — 67.1 kB (baseline 67.1 kB) • ⚪ 0 BReusable component library chunks
Status: 9 added / 9 removed / 5 unchanged Data & Services — 3.5 MB (baseline 3.5 MB) • 🔴 +268 BStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 550 kB (baseline 550 kB) • 🔴 +81 BHelpers, composables, and utility bundles
Status: 22 added / 22 removed / 16 unchanged Vendor & Third-Party — 16.8 MB (baseline 16.3 MB) • 🔴 +525 kBExternal libraries and shared vendor chunks
Status: 2 added / 1 removed / 16 unchanged Other — 14 MB (baseline 14 MB) • 🔴 +477 BBundles that do not match a named category
Status: 85 added / 84 removed / 201 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-12T00:36:57.737Z",
"gitSha": "f0a020ae7da071e9084ad6545b44c342ca455589",
"branch": "fix/login-route-and-auth-timeout",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2011.3870000000134,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.453000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 448.52200000000005,
"heapDeltaBytes": 5909032,
"heapUsedBytes": 70495692,
"domNodes": 20,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 13.402000000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 1997.4720000000161,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.769000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 438.386,
"heapDeltaBytes": 5856752,
"heapUsedBytes": 70507428,
"domNodes": 18,
"jsHeapTotalBytes": 23855104,
"scriptDurationMs": 13.753000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1946.4810000000057,
"styleRecalcs": 76,
"styleRecalcDurationMs": 36.24099999999999,
"layouts": 12,
"layoutDurationMs": 4.435,
"taskDurationMs": 997.5550000000001,
"heapDeltaBytes": -14225368,
"heapUsedBytes": 50516416,
"domNodes": -283,
"jsHeapTotalBytes": 24354816,
"scriptDurationMs": 127.71300000000002,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1852.378999999985,
"styleRecalcs": 75,
"styleRecalcDurationMs": 35.009,
"layouts": 12,
"layoutDurationMs": 3.3709999999999996,
"taskDurationMs": 870.179,
"heapDeltaBytes": -16597128,
"heapUsedBytes": 48207392,
"domNodes": -282,
"jsHeapTotalBytes": 23044096,
"scriptDurationMs": 123.23400000000001,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1734.4229999999925,
"styleRecalcs": 30,
"styleRecalcDurationMs": 14.552000000000001,
"layouts": 6,
"layoutDurationMs": 0.581,
"taskDurationMs": 364.03200000000004,
"heapDeltaBytes": 8773624,
"heapUsedBytes": 73625224,
"domNodes": 76,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 17.369000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1738.8029999999617,
"styleRecalcs": 32,
"styleRecalcDurationMs": 14.838000000000001,
"layouts": 6,
"layoutDurationMs": 0.544,
"taskDurationMs": 351.308,
"heapDeltaBytes": 8784604,
"heapUsedBytes": 73536736,
"domNodes": 77,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 16.988000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 575.5510000000186,
"styleRecalcs": 12,
"styleRecalcDurationMs": 7.113000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 358.622,
"heapDeltaBytes": -11286992,
"heapUsedBytes": 53423916,
"domNodes": 20,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 55.955000000000005,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "dom-widget-clipping",
"durationMs": 572.339999999997,
"styleRecalcs": 9,
"styleRecalcDurationMs": 5.563000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 366.361,
"heapDeltaBytes": -11218308,
"heapUsedBytes": 53384560,
"domNodes": 14,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 56.79300000000001,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2027.5649999999814,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.040999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 648.2379999999998,
"heapDeltaBytes": 6951956,
"heapUsedBytes": 66641244,
"domNodes": -281,
"jsHeapTotalBytes": 2723840,
"scriptDurationMs": 98.15599999999999,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2024.0110000000868,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.621000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 649.8599999999999,
"heapDeltaBytes": 7108748,
"heapUsedBytes": 67215544,
"domNodes": -281,
"jsHeapTotalBytes": 3248128,
"scriptDurationMs": 100.604,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2147.2440000000006,
"styleRecalcs": 68,
"styleRecalcDurationMs": 11.405999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1205.501,
"heapDeltaBytes": 3707292,
"heapUsedBytes": 65417204,
"domNodes": -284,
"jsHeapTotalBytes": 3702784,
"scriptDurationMs": 411.98699999999997,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2180.9180000000197,
"styleRecalcs": 70,
"styleRecalcDurationMs": 13.927999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1236.653,
"heapDeltaBytes": 4815048,
"heapUsedBytes": 65680188,
"domNodes": -284,
"jsHeapTotalBytes": 3440640,
"scriptDurationMs": 424.685,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3147.207000000037,
"styleRecalcs": 66,
"styleRecalcDurationMs": 14.445999999999996,
"layouts": 60,
"layoutDurationMs": 7.164999999999999,
"taskDurationMs": 1450.577,
"heapDeltaBytes": -5406200,
"heapUsedBytes": 56926332,
"domNodes": -287,
"jsHeapTotalBytes": 6656000,
"scriptDurationMs": 508.101,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3196.6690000000426,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.026,
"layouts": 60,
"layoutDurationMs": 6.8660000000000005,
"taskDurationMs": 1452.521,
"heapDeltaBytes": -2679784,
"heapUsedBytes": 59798832,
"domNodes": -288,
"jsHeapTotalBytes": 6393856,
"scriptDurationMs": 512.4,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2022.4629999999593,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.315000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 651.266,
"heapDeltaBytes": 6559040,
"heapUsedBytes": 67165916,
"domNodes": -284,
"jsHeapTotalBytes": 2985984,
"scriptDurationMs": 100.444,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2025.816999999961,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.415000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 649.9720000000001,
"heapDeltaBytes": 6939732,
"heapUsedBytes": 67575780,
"domNodes": -284,
"jsHeapTotalBytes": 3510272,
"scriptDurationMs": 99.79299999999999,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 597.7689999999711,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.515,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 401.58799999999997,
"heapDeltaBytes": -10285952,
"heapUsedBytes": 54425208,
"domNodes": 22,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 121.172,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 557.2230000000218,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.244000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 387.79200000000003,
"heapDeltaBytes": -10692880,
"heapUsedBytes": 54091004,
"domNodes": 20,
"jsHeapTotalBytes": 25690112,
"scriptDurationMs": 117.04699999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2006.3020000000051,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.307000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 430.04600000000005,
"heapDeltaBytes": 5947500,
"heapUsedBytes": 70638872,
"domNodes": 18,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 12.682999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2007.0690000000013,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.082999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 427.871,
"heapDeltaBytes": 5798676,
"heapUsedBytes": 70581048,
"domNodes": 18,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 12.506000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1689.1940000000432,
"styleRecalcs": 76,
"styleRecalcDurationMs": 32.01,
"layouts": 16,
"layoutDurationMs": 4.051,
"taskDurationMs": 704.2760000000001,
"heapDeltaBytes": -3428488,
"heapUsedBytes": 61461664,
"domNodes": 61,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 82.80999999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1695.1350000000502,
"styleRecalcs": 76,
"styleRecalcDurationMs": 32.289,
"layouts": 16,
"layoutDurationMs": 3.47,
"taskDurationMs": 701.9989999999999,
"heapDeltaBytes": -3338516,
"heapUsedBytes": 61372628,
"domNodes": 64,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 81.829,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1406.5330000000245,
"styleRecalcs": 19,
"styleRecalcDurationMs": 30.38600000000001,
"layouts": 15,
"layoutDurationMs": 12.59,
"taskDurationMs": 899.8690000000003,
"heapDeltaBytes": 3748784,
"heapUsedBytes": 74469068,
"domNodes": 13673,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 31.965999999999994,
"eventListeners": 2375,
"totalBlockingTimeMs": 140,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8219.34299999998,
"styleRecalcs": 249,
"styleRecalcDurationMs": 33.83,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4231.7,
"heapDeltaBytes": 11889708,
"heapUsedBytes": 71769956,
"domNodes": -284,
"jsHeapTotalBytes": 6324224,
"scriptDurationMs": 1331.808,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8180.7620000000725,
"styleRecalcs": 250,
"styleRecalcDurationMs": 34.571,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4243.907,
"heapDeltaBytes": 10958024,
"heapUsedBytes": 70806932,
"domNodes": -281,
"jsHeapTotalBytes": 5013504,
"scriptDurationMs": 1336.812,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "vue-large-graph-idle",
"durationMs": 15716.109000000017,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15659.665,
"heapDeltaBytes": -46457880,
"heapUsedBytes": 171578424,
"domNodes": -8312,
"jsHeapTotalBytes": -4136960,
"scriptDurationMs": 534.4649999999999,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.219999999999953,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 15607.569000000012,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15557.235999999999,
"heapDeltaBytes": -59157780,
"heapUsedBytes": 167035864,
"domNodes": -8316,
"jsHeapTotalBytes": -10690560,
"scriptDurationMs": 508.927,
"eventListeners": -16385,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 19374.284000000047,
"styleRecalcs": 128,
"styleRecalcDurationMs": 13.521999999999979,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 19347.322,
"heapDeltaBytes": -45998216,
"heapUsedBytes": 169833844,
"domNodes": -8312,
"jsHeapTotalBytes": -10166272,
"scriptDurationMs": 851.5029999999999,
"eventListeners": -16383,
"totalBlockingTimeMs": 58,
"frameDurationMs": 17.216666666666665,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 19608.925999999996,
"styleRecalcs": 131,
"styleRecalcDurationMs": 13.466000000000033,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 19563.761,
"heapDeltaBytes": -18542996,
"heapUsedBytes": 180613200,
"domNodes": -8312,
"jsHeapTotalBytes": -12070912,
"scriptDurationMs": 854.902,
"eventListeners": -16385,
"totalBlockingTimeMs": 89,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 451.8150000000105,
"styleRecalcs": 15,
"styleRecalcDurationMs": 24.648000000000003,
"layouts": 4,
"layoutDurationMs": 1.3810000000000002,
"taskDurationMs": 114.758,
"heapDeltaBytes": 5067892,
"heapUsedBytes": 68969304,
"domNodes": 126,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 8.953000000000001,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 457.6270000000022,
"styleRecalcs": 14,
"styleRecalcDurationMs": 18.557000000000002,
"layouts": 3,
"layoutDurationMs": 0.6900000000000001,
"taskDurationMs": 105.011,
"heapDeltaBytes": 5047376,
"heapUsedBytes": 68764296,
"domNodes": 123,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 8.514000000000003,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
📝 WalkthroughWalkthroughThe PR adds bounded cloud authentication retries with Sentry and Datadog reporting, expands bootstrap tests, and adds a catch-all router redirect. ChangesBootstrap resilience
Router fallback
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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.
Inline comments:
In `@src/router.ts`:
- Around line 87-90: Add Vitest behavioral coverage for the catch-all route
defined in the router configuration: navigate to an unmatched path and assert
the final route is “/”. When cloud mode is enabled, also verify unauthenticated
navigation ultimately reaches the existing cloud-login route, asserting final
navigation state rather than internal guard calls.
In `@src/stores/bootstrapStore.ts`:
- Around line 41-49: Update waitForResolution in src/stores/bootstrapStore.ts
(lines 41-49) to wait only for isInitialized, allowing signed-out Firebase
results to continue bootstrap without requiring isAuthenticated. In
src/stores/bootstrapStore.test.ts (lines 153-162), add or update coverage to
verify bootstrap loads after mockIsAuthInitialized becomes true while
mockIsAuthAuthenticated remains false, preserving timeout, retry,
Sentry-reporting, and routing behavior coverage.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: dfaf8fa7-2038-4736-a78e-c359fc24f8b9
📒 Files selected for processing (3)
src/router.tssrc/stores/bootstrapStore.test.tssrc/stores/bootstrapStore.ts
| // Catch-all: unknown paths redirect to root rather than hanging on the | ||
| // splash screen with no route match. The global auth guard then routes | ||
| // unauthenticated users to /cloud/login as normal. | ||
| { path: "/:pathMatch(.*)*", redirect: "/" }, |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Add behavioral coverage for the catch-all route.
Add a Vitest test that navigates to an unmatched path and asserts that the final route is /. In cloud mode, also assert that an unauthenticated navigation reaches cloud-login through the existing guard. Assert final navigation state instead of internal guard calls.
As per coding guidelines: “Write Vitest unit/component tests for behavioral changes, especially bug fixes.”
🤖 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 `@src/router.ts` around lines 87 - 90, Add Vitest behavioral coverage for the
catch-all route defined in the router configuration: navigate to an unmatched
path and assert the final route is “/”. When cloud mode is enabled, also verify
unauthenticated navigation ultimately reaches the existing cloud-login route,
asserting final navigation state rather than internal guard calls.
Source: Coding guidelines
Navigating to an unknown path (e.g. /woiadawd) in the cloud build must redirect to /cloud/login rather than hanging indefinitely on the splash screen. The catch-all route added in the previous commit makes this deterministic; this test locks in the behaviour as a CI regression gate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #15063 +/- ##
==========================================
- Coverage 80.81% 80.74% -0.07%
==========================================
Files 1873 1873
Lines 121954 122109 +155
Branches 37495 37566 +71
==========================================
+ Hits 98558 98599 +41
- Misses 22859 22963 +104
- Partials 537 547 +10
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 11 files with indirect coverage changes 🚀 New features to boost your workflow:
|
The ComfyUI Python dev server serves file downloads for arbitrary paths (e.g. /woiadawd), not the SPA shell. Navigate to root to boot the SPA, then push the unknown path via history.pushState so Vue Router handles the redirect client-side. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ated onAuthStateChanged fires with null for signed-out users, which sets isInitialized = true but leaves isAuthenticated = false. Awaiting isAuthenticated here caused every unauthenticated cloud page load to wait 35s (16s + 3s backoff + 16s) before bootstrap continued and fire a false Sentry timeout on every sign-out. Only isInitialized is needed: Firebase initialization resolves whether the user is signed in or not. The existing auth guard in router.ts handles the login redirect for unauthenticated users separately. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Per team preference (Datadog-first), report the bootstrap auth timeout to datadogRum.addError in addition to Sentry captureException so the error surfaces regardless of which observability platform is active. Also normalises the thrown value to an Error instance before reporting. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/stores/bootstrapStore.ts (1)
68-76: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid duplicate Datadog error events.
Datadog automatically collects
console.errorevents. This timeout is also sent throughdatadogRum.addError, so it produces two RUM error events. Removeconsole.erroror replace it with a non-error diagnostic path.🤖 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 `@src/stores/bootstrapStore.ts` around lines 68 - 76, In the bootstrap auth timeout handling around retryError, remove the console.error call or replace it with a non-error diagnostic path so Datadog does not record a duplicate RUM error alongside datadogRum.addError. Preserve the existing normalized err creation and explicit Datadog reporting.Source: MCP tools
🤖 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.
Outside diff comments:
In `@src/stores/bootstrapStore.ts`:
- Around line 68-76: In the bootstrap auth timeout handling around retryError,
remove the console.error call or replace it with a non-error diagnostic path so
Datadog does not record a duplicate RUM error alongside datadogRum.addError.
Preserve the existing normalized err creation and explicit Datadog reporting.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c322bada-4023-4dd3-95b3-722a0fb5d7ba
📒 Files selected for processing (2)
src/stores/bootstrapStore.test.tssrc/stores/bootstrapStore.ts
| datadogRum.addError(err, { error_type: 'bootstrap_auth_wait_timeout' }) | ||
| captureException(err, { |
There was a problem hiding this comment.
should we encapsulate these calls into a helper so we don't leak datadog RUM calls thoughout the codebase?
There was a problem hiding this comment.
Hm I actually did ask for a single helper that encapsulates all loggers but evidently it was not implemented. Good catch
| // Catch-all: unknown paths redirect to root rather than hanging on the | ||
| // splash screen with no route match. The global auth guard then routes | ||
| // unauthenticated users to /cloud/login as normal. | ||
| { path: '/:pathMatch(.*)*', redirect: '/' } |
There was a problem hiding this comment.
How about emitting a Datadog warning for unmatched routes? We can monitor for spikes in unmatched routes.
|
Doing the suggestions in a follow up so we can ship this fix |
|
|
|
@christian-byrne Successfully backported to #15179 |
|
@christian-byrne Successfully backported to #15180 |
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)anduntil(isAuthenticated).toBe(true)instartStoreBootstraphave 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:router.ts:176andWorkspaceAuthGate.vue:78— existing convention)captureExceptionto Sentry + continue bootstrap rather than blockingThis 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/woiadawdgets a 200 + app shell from the static host, Vue boots, finds no matching route, and the global auth guard'suntil(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/loginas normal.Red-Green Verification
test: add failing tests for unbounded auth wait in startStoreBootstrapfix: bound auth wait in startStoreBootstrap and add catch-all routeRelation to open PRs
fix/bootstrap-auth-wait-timeout) — same fix, same tests. If reviewers prefer to land fix: bound the auth wait in startStoreBootstrap with a timeout #15032 first this PR can drop Fix 1./login→cloud-loginroute, already on main).Test Plan
pnpm vitest run src/stores/bootstrapStore.test.ts— 7/7 pass (including 2 new timeout tests)pnpm vitest run src/platform/cloud/onboarding/onboardingCloudRoutes.test.ts— 11/11 passpnpm typecheck— cleanpnpm exec eslint src/stores/bootstrapStore.ts src/router.ts— clean🤖 Generated with Claude Code