[backport core/1.49] fix(workspace): recover from initialization failures - #14801
Conversation
Part 1/4 of the workspace/billing rollout retirement stack. Makes Cloud workspace initialization failures recoverable without changing either rollout flag or flag-off behavior. Predecessor: none (base: main). Successor: [#14613](#14613). `WorkspaceAuthGate` failed closed on config, unified-auth, workspace initialization, and timeout failures but exposed no recovery action. The team workspace store also treated `error` as terminal, and the Cloud account popover disappeared while workspace initialization was loading or failed, preventing account actions. - Render a fail-closed initialization error with a retry action for config, timeout, unified-auth, and workspace failures. - Permit `teamWorkspaceStore.initialize()` to retry from `error`. - Keep the account menu usable in flag-on loading/error states via account-only workspace popover content. - Preserve legacy popover and gate behavior when `team_workspaces_enabled` is off. - Filter empty logged-out Workspace settings groups. - Add only the Cloud boot session/workspace fixture coverage needed by this layer; both rollout keys remain supported. Initialization failures permanently gate the app and loading/error workspace state removes Cloud account actions. Initialization failures remain fail-closed but can be retried, and Cloud account actions remain available during loading/error. This is the only intended visual change in the stack; it is a state-dependent recovery UI, so no static screenshot is required. - WorkspaceAuthGate failure, timeout, and retry paths with asynchronous settling. - Team workspace store `error -> loading -> ready` retry. - Current user button/popover flag-off, flag-on loading/error, and ready behavior. - Logged-out settings group filtering. - Cloud boot fixture session/workspace bootstrap. - `pnpm test:unit src/platform/workspace/auth/WorkspaceAuthGate.test.ts src/platform/workspace/stores/teamWorkspaceStore.test.ts src/components/topbar/CurrentUserButton.test.ts src/platform/workspace/components/CurrentUserPopoverWorkspace.test.ts src/platform/settings/composables/useSettingUI.test.ts` (125 passed) - `pnpm typecheck` (passed via commit hook after final fix) - `pnpm typecheck:browser` (passed) - targeted `oxfmt --check` (passed) - `git diff --check public/main...HEAD` (passed) - `pnpm knip --cache` (passed via push hook) Review only recoverability, retry state transitions, and preservation of both rollout modes. Flag retirement belongs to later PRs. 1. **This PR** — recovery foundation 2. [#14613](#14613) — retire team workspace rollout flag 3. [#14614](#14614) — canonical billing status and legacy rail preservation 4. [#14615](#14615) — retire consolidated billing rollout flag Deploy the frontend stack before the backend removes `/api/features` compatibility keys. --------- Co-authored-by: Dante <6510430+dante01yoon@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Christian Byrne <cbyrne@comfy.org> Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf
|
Important Review skippedIgnore keyword(s) in the title. ⛔ Ignored keywords (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
🎭 Playwright: ✅ 1787 passed, 0 failed · 2 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle Size
⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-05T12:54:14.873Z",
"gitSha": "ab5d6eb030d34a3d8bb1b7db6a9ffc4d50dc972d",
"branch": "backport-14612-to-core-1.49",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2037.8729999999905,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.935,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 466.292,
"heapDeltaBytes": 5140428,
"heapUsedBytes": 73318212,
"domNodes": 18,
"jsHeapTotalBytes": 19779584,
"scriptDurationMs": 16.325,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-idle",
"durationMs": 2015.2310000000284,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.977,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 471.20099999999996,
"heapDeltaBytes": 5217100,
"heapUsedBytes": 73322232,
"domNodes": 20,
"jsHeapTotalBytes": 20041728,
"scriptDurationMs": 17.146,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1857.2389999999928,
"styleRecalcs": 75,
"styleRecalcDurationMs": 35.354000000000006,
"layouts": 12,
"layoutDurationMs": 3.277,
"taskDurationMs": 893.491,
"heapDeltaBytes": -17253980,
"heapUsedBytes": 50901996,
"domNodes": -280,
"jsHeapTotalBytes": 21217280,
"scriptDurationMs": 122.90899999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1866.9479999999794,
"styleRecalcs": 74,
"styleRecalcDurationMs": 40.17900000000001,
"layouts": 12,
"layoutDurationMs": 3.7520000000000002,
"taskDurationMs": 930.2719999999999,
"heapDeltaBytes": -18994640,
"heapUsedBytes": 49174308,
"domNodes": -281,
"jsHeapTotalBytes": 21217280,
"scriptDurationMs": 129.506,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1721.9949999999926,
"styleRecalcs": 32,
"styleRecalcDurationMs": 16.455000000000002,
"layouts": 6,
"layoutDurationMs": 0.521,
"taskDurationMs": 363.303,
"heapDeltaBytes": 8132576,
"heapUsedBytes": 76264288,
"domNodes": 78,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 18.215999999999998,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.0209999999797,
"styleRecalcs": 31,
"styleRecalcDurationMs": 16.869,
"layouts": 6,
"layoutDurationMs": 0.616,
"taskDurationMs": 372.126,
"heapDeltaBytes": 7971752,
"heapUsedBytes": 76293232,
"domNodes": 77,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 18.796000000000003,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 562.419000000034,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.24,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 373.26400000000007,
"heapDeltaBytes": -11989312,
"heapUsedBytes": 56131464,
"domNodes": 18,
"jsHeapTotalBytes": 21090304,
"scriptDurationMs": 58.991,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 590.118000000075,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.465999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 376.41,
"heapDeltaBytes": -12060340,
"heapUsedBytes": 56077240,
"domNodes": 18,
"jsHeapTotalBytes": 21090304,
"scriptDurationMs": 58.25699999999999,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2030.0090000000068,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.112,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 680.442,
"heapDeltaBytes": 6708744,
"heapUsedBytes": 65919712,
"domNodes": -283,
"jsHeapTotalBytes": 5300224,
"scriptDurationMs": 105.093,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2025.9399999999914,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.0859999999999985,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 688.83,
"heapDeltaBytes": 8081840,
"heapUsedBytes": 67632904,
"domNodes": -283,
"jsHeapTotalBytes": 5562368,
"scriptDurationMs": 109.92200000000001,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2186.9199999999864,
"styleRecalcs": 68,
"styleRecalcDurationMs": 12.581999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1263.736,
"heapDeltaBytes": -4203712,
"heapUsedBytes": 56116812,
"domNodes": -286,
"jsHeapTotalBytes": 5300224,
"scriptDurationMs": 428.031,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2196.0619999999835,
"styleRecalcs": 69,
"styleRecalcDurationMs": 13.532000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1259.425,
"heapDeltaBytes": 5829664,
"heapUsedBytes": 65973812,
"domNodes": -284,
"jsHeapTotalBytes": 5787648,
"scriptDurationMs": 435.43,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3128.0340000000137,
"styleRecalcs": 64,
"styleRecalcDurationMs": 13.627999999999998,
"layouts": 60,
"layoutDurationMs": 7.144,
"taskDurationMs": 1443.516,
"heapDeltaBytes": 22590912,
"heapUsedBytes": 84812948,
"domNodes": 10,
"jsHeapTotalBytes": 8388608,
"scriptDurationMs": 520.235,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3244.854999999916,
"styleRecalcs": 64,
"styleRecalcDurationMs": 12.928000000000003,
"layouts": 60,
"layoutDurationMs": 7.2330000000000005,
"taskDurationMs": 1516.9139999999998,
"heapDeltaBytes": 14360,
"heapUsedBytes": 61913148,
"domNodes": -287,
"jsHeapTotalBytes": 9232384,
"scriptDurationMs": 530.524,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2054.7710000000166,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.731999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 698.683,
"heapDeltaBytes": 8753468,
"heapUsedBytes": 69594668,
"domNodes": -283,
"jsHeapTotalBytes": 3727360,
"scriptDurationMs": 113.833,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2033.6730000000216,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.256999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 688.5179999999999,
"heapDeltaBytes": 8338284,
"heapUsedBytes": 69467064,
"domNodes": -284,
"jsHeapTotalBytes": 5038080,
"scriptDurationMs": 111.13299999999998,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 606.9269999999847,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.539000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 405.582,
"heapDeltaBytes": -10994876,
"heapUsedBytes": 57223464,
"domNodes": 20,
"jsHeapTotalBytes": 21090304,
"scriptDurationMs": 117.44099999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 647.0319999999674,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.59,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 407.641,
"heapDeltaBytes": -10991736,
"heapUsedBytes": 57328988,
"domNodes": 22,
"jsHeapTotalBytes": 21614592,
"scriptDurationMs": 122.83099999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 1993.4769999999844,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.674999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 445.14099999999996,
"heapDeltaBytes": 4923272,
"heapUsedBytes": 73114964,
"domNodes": 22,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 12.589999999999998,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2022.139999999922,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.775,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 446.40500000000003,
"heapDeltaBytes": 4687156,
"heapUsedBytes": 73167080,
"domNodes": 18,
"jsHeapTotalBytes": 19779584,
"scriptDurationMs": 14.084000000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1695.0449999999933,
"styleRecalcs": 76,
"styleRecalcDurationMs": 35.545,
"layouts": 16,
"layoutDurationMs": 4.23,
"taskDurationMs": 752.836,
"heapDeltaBytes": -4670232,
"heapUsedBytes": 63673660,
"domNodes": 62,
"jsHeapTotalBytes": 21090304,
"scriptDurationMs": 89.62100000000001,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1711.426000000074,
"styleRecalcs": 76,
"styleRecalcDurationMs": 38.819,
"layouts": 16,
"layoutDurationMs": 3.9590000000000005,
"taskDurationMs": 768.2280000000001,
"heapDeltaBytes": -4336244,
"heapUsedBytes": 63902068,
"domNodes": 65,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 91.999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-transition-enter",
"durationMs": 1401.6300000000115,
"styleRecalcs": 17,
"styleRecalcDurationMs": 29.778000000000006,
"layouts": 13,
"layoutDurationMs": 12.355999999999998,
"taskDurationMs": 926.2219999999999,
"heapDeltaBytes": 30957688,
"heapUsedBytes": 99446028,
"domNodes": 13673,
"jsHeapTotalBytes": 15728640,
"scriptDurationMs": 35.747,
"eventListeners": 2375,
"totalBlockingTimeMs": 138,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8270.657999999969,
"styleRecalcs": 250,
"styleRecalcDurationMs": 36.621,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4479.034000000001,
"heapDeltaBytes": 12972600,
"heapUsedBytes": 72083352,
"domNodes": -285,
"jsHeapTotalBytes": 7098368,
"scriptDurationMs": 1413.699,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8288.243999999962,
"styleRecalcs": 249,
"styleRecalcDurationMs": 35.908,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4481.3,
"heapDeltaBytes": 5578044,
"heapUsedBytes": 64952920,
"domNodes": -284,
"jsHeapTotalBytes": 5230592,
"scriptDurationMs": 1390.275,
"eventListeners": -165,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17163.666000000034,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17145.441,
"heapDeltaBytes": -56681012,
"heapUsedBytes": 155882028,
"domNodes": -8312,
"jsHeapTotalBytes": -10952704,
"scriptDurationMs": 530.4839999999999,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17228.749999999993,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17210.665,
"heapDeltaBytes": -54868256,
"heapUsedBytes": 171416264,
"domNodes": -8319,
"jsHeapTotalBytes": -3350528,
"scriptDurationMs": 599.723,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.773333333333238,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 20817.41400000004,
"styleRecalcs": 144,
"styleRecalcDurationMs": 17.330000000000013,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20791.961,
"heapDeltaBytes": -32451284,
"heapUsedBytes": 177799352,
"domNodes": -8312,
"jsHeapTotalBytes": -10760192,
"scriptDurationMs": 883.061,
"eventListeners": -16383,
"totalBlockingTimeMs": 255,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 20188.28999999994,
"styleRecalcs": 131,
"styleRecalcDurationMs": 15.599000000000029,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20152.490999999998,
"heapDeltaBytes": -42910524,
"heapUsedBytes": 166749736,
"domNodes": -8312,
"jsHeapTotalBytes": -13836288,
"scriptDurationMs": 884.2420000000001,
"eventListeners": -16387,
"totalBlockingTimeMs": 104,
"frameDurationMs": 17.77333333333336,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 461.75199999998995,
"styleRecalcs": 13,
"styleRecalcDurationMs": 22.924000000000003,
"layouts": 4,
"layoutDurationMs": 1.361,
"taskDurationMs": 129.062,
"heapDeltaBytes": -15485044,
"heapUsedBytes": 51623728,
"domNodes": 132,
"jsHeapTotalBytes": 7720960,
"scriptDurationMs": 11.031,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 477.0529999999553,
"styleRecalcs": 18,
"styleRecalcDurationMs": 23.651,
"layouts": 3,
"layoutDurationMs": 1.47,
"taskDurationMs": 128.677,
"heapDeltaBytes": -15626072,
"heapUsedBytes": 51852432,
"domNodes": 147,
"jsHeapTotalBytes": 7720960,
"scriptDurationMs": 9.680000000000001,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## core/1.49 #14801 +/- ##
=============================================
+ Coverage 78.75% 78.77% +0.01%
=============================================
Files 1791 1791
Lines 104546 104625 +79
Branches 32923 32094 -829
=============================================
+ Hits 82338 82416 +78
- Misses 21778 21779 +1
Partials 430 430
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Backport of #14612 to
core/1.49.Summary
Restores the workspace initialization recovery behavior from the merged PR, including retryable fail-closed errors, account actions during Cloud loading/error states, retryable workspace-store initialization, and supporting test fixtures.
Conflict resolution
Resolved
browser_tests/fixtures/utils/workspaceMocks.tsby retaining the merged PR behavior: route helpers acceptPage | BrowserContextand workspace routes use the suppliedrouteTarget. Kept the workspace-list helper local because this release branch does not yet contain its later stack consumers.Validation
pnpm typecheckpnpm typecheck:browserpnpm knip --cachegit diff --checkOriginal PR: #14612