[backport cloud/1.49] fix(workspace): recover from initialization failures - #14802
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>
|
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 |
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1785 passed, 0 failed · 3 flaky📊 Browser Reports
📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-05T13:00:12.915Z",
"gitSha": "68af903a3f0cb4785be799fbf31a3006ff76d5b1",
"branch": "backport-14612-to-cloud-1.49",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2118.081000000018,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.866999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 557.0070000000001,
"heapDeltaBytes": 5074068,
"heapUsedBytes": 73293688,
"domNodes": 16,
"jsHeapTotalBytes": 19779584,
"scriptDurationMs": 19.003,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2053.8609999999835,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.663000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 478.722,
"heapDeltaBytes": 5150036,
"heapUsedBytes": 73424496,
"domNodes": 18,
"jsHeapTotalBytes": 20041728,
"scriptDurationMs": 18.308,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2047.318999999959,
"styleRecalcs": 80,
"styleRecalcDurationMs": 44.216,
"layouts": 12,
"layoutDurationMs": 5.451,
"taskDurationMs": 990.7409999999999,
"heapDeltaBytes": -17716632,
"heapUsedBytes": 50344304,
"domNodes": -280,
"jsHeapTotalBytes": 20692992,
"scriptDurationMs": 137.944,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1853.9799999999786,
"styleRecalcs": 74,
"styleRecalcDurationMs": 35.912,
"layouts": 12,
"layoutDurationMs": 3.378,
"taskDurationMs": 871.59,
"heapDeltaBytes": -18115000,
"heapUsedBytes": 50321296,
"domNodes": -284,
"jsHeapTotalBytes": 19906560,
"scriptDurationMs": 124.96099999999998,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1726.25000000005,
"styleRecalcs": 31,
"styleRecalcDurationMs": 15.508000000000001,
"layouts": 6,
"layoutDurationMs": 0.526,
"taskDurationMs": 377.88999999999993,
"heapDeltaBytes": 7831204,
"heapUsedBytes": 75953656,
"domNodes": 75,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 18.83,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1738.401999999951,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.733999999999998,
"layouts": 6,
"layoutDurationMs": 0.6449999999999999,
"taskDurationMs": 393.82199999999995,
"heapDeltaBytes": 8102132,
"heapUsedBytes": 76416520,
"domNodes": 77,
"jsHeapTotalBytes": 19779584,
"scriptDurationMs": 19.878,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 590.172999999993,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.188,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 385.761,
"heapDeltaBytes": -12609976,
"heapUsedBytes": 55605948,
"domNodes": 18,
"jsHeapTotalBytes": 21614592,
"scriptDurationMs": 60.193000000000005,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 559.6909999999298,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.153999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 365.62300000000005,
"heapDeltaBytes": -12352200,
"heapUsedBytes": 56019580,
"domNodes": 18,
"jsHeapTotalBytes": 21876736,
"scriptDurationMs": 58.12500000000001,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2041.7529999999715,
"styleRecalcs": 7,
"styleRecalcDurationMs": 6.197000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 683.292,
"heapDeltaBytes": 8359552,
"heapUsedBytes": 67724116,
"domNodes": -282,
"jsHeapTotalBytes": 5562368,
"scriptDurationMs": 109.61699999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2045.4760000000078,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.029000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 691.0169999999999,
"heapDeltaBytes": 8187632,
"heapUsedBytes": 67747996,
"domNodes": -285,
"jsHeapTotalBytes": 4775936,
"scriptDurationMs": 108.48100000000001,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2243.415999999968,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.572000000000005,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1301.8690000000001,
"heapDeltaBytes": 7417604,
"heapUsedBytes": 68362088,
"domNodes": -284,
"jsHeapTotalBytes": 4476928,
"scriptDurationMs": 433.72999999999996,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2150.26899999998,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.023000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1316.2450000000001,
"heapDeltaBytes": 5198360,
"heapUsedBytes": 65648544,
"domNodes": -284,
"jsHeapTotalBytes": 5525504,
"scriptDurationMs": 437.147,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3231.4270000000533,
"styleRecalcs": 66,
"styleRecalcDurationMs": 15.368,
"layouts": 60,
"layoutDurationMs": 7.249999999999999,
"taskDurationMs": 1550.8760000000002,
"heapDeltaBytes": -340372,
"heapUsedBytes": 61798412,
"domNodes": -288,
"jsHeapTotalBytes": 8445952,
"scriptDurationMs": 536.497,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3245.990000000006,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.855999999999998,
"layouts": 60,
"layoutDurationMs": 7.225,
"taskDurationMs": 1549.843,
"heapDeltaBytes": 44536,
"heapUsedBytes": 62105992,
"domNodes": -288,
"jsHeapTotalBytes": 8183808,
"scriptDurationMs": 544.119,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2039.0479999999798,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.614,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 678.626,
"heapDeltaBytes": 8533120,
"heapUsedBytes": 69642880,
"domNodes": -283,
"jsHeapTotalBytes": 3465216,
"scriptDurationMs": 107.538,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2061.5680000000793,
"styleRecalcs": 9,
"styleRecalcDurationMs": 14.684999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 705.39,
"heapDeltaBytes": 8405860,
"heapUsedBytes": 69732576,
"domNodes": -283,
"jsHeapTotalBytes": 5038080,
"scriptDurationMs": 111.99699999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 614.8559999999748,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.087,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 412.4840000000001,
"heapDeltaBytes": -10910936,
"heapUsedBytes": 57183728,
"domNodes": 20,
"jsHeapTotalBytes": 21614592,
"scriptDurationMs": 125.18399999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 585.7160000000476,
"styleRecalcs": 47,
"styleRecalcDurationMs": 10.842999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 405.352,
"heapDeltaBytes": -10936284,
"heapUsedBytes": 57222124,
"domNodes": 20,
"jsHeapTotalBytes": 21876736,
"scriptDurationMs": 119.82799999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 1998.469,
"styleRecalcs": 10,
"styleRecalcDurationMs": 8.671999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 458.692,
"heapDeltaBytes": 4948636,
"heapUsedBytes": 73300892,
"domNodes": 20,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 13.673,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2024.1260000000239,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.725,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 463.6650000000001,
"heapDeltaBytes": 4882116,
"heapUsedBytes": 73096588,
"domNodes": 20,
"jsHeapTotalBytes": 20303872,
"scriptDurationMs": 13.885000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1739.3150000000333,
"styleRecalcs": 78,
"styleRecalcDurationMs": 37.681,
"layouts": 16,
"layoutDurationMs": 4.92,
"taskDurationMs": 826.771,
"heapDeltaBytes": -22107300,
"heapUsedBytes": 46237368,
"domNodes": 18,
"jsHeapTotalBytes": 20168704,
"scriptDurationMs": 94.396,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1712.003999999979,
"styleRecalcs": 77,
"styleRecalcDurationMs": 38.246,
"layouts": 16,
"layoutDurationMs": 4.209999999999999,
"taskDurationMs": 795.018,
"heapDeltaBytes": -4346416,
"heapUsedBytes": 63875684,
"domNodes": 66,
"jsHeapTotalBytes": 20566016,
"scriptDurationMs": 91.724,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-transition-enter",
"durationMs": 1417.6009999999906,
"styleRecalcs": 18,
"styleRecalcDurationMs": 30.853000000000005,
"layouts": 14,
"layoutDurationMs": 12.713,
"taskDurationMs": 956.7229999999997,
"heapDeltaBytes": 31034244,
"heapUsedBytes": 99779844,
"domNodes": 13673,
"jsHeapTotalBytes": 16252928,
"scriptDurationMs": 37.006000000000014,
"eventListeners": 2375,
"totalBlockingTimeMs": 136,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8310.482000000036,
"styleRecalcs": 249,
"styleRecalcDurationMs": 35.717,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4547.320999999999,
"heapDeltaBytes": 12597608,
"heapUsedBytes": 72072784,
"domNodes": -284,
"jsHeapTotalBytes": 5754880,
"scriptDurationMs": 1401.824,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8431.618999999955,
"styleRecalcs": 249,
"styleRecalcDurationMs": 37.361,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4746.4,
"heapDeltaBytes": 12459396,
"heapUsedBytes": 72380308,
"domNodes": -284,
"jsHeapTotalBytes": 7327744,
"scriptDurationMs": 1520.047,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.669999999999952,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17314.152000000035,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17287.703999999998,
"heapDeltaBytes": -58582472,
"heapUsedBytes": 167174832,
"domNodes": -8312,
"jsHeapTotalBytes": -8069120,
"scriptDurationMs": 590.047,
"eventListeners": -16385,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 17377.297999999995,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17361.323,
"heapDeltaBytes": -44492004,
"heapUsedBytes": 167749540,
"domNodes": -8312,
"jsHeapTotalBytes": -9641984,
"scriptDurationMs": 590.219,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.77333333333336,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21286.684000000037,
"styleRecalcs": 146,
"styleRecalcDurationMs": 17.706999999999972,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21212.172,
"heapDeltaBytes": -54912116,
"heapUsedBytes": 169644392,
"domNodes": -8312,
"jsHeapTotalBytes": -10166272,
"scriptDurationMs": 953.927,
"eventListeners": -16383,
"totalBlockingTimeMs": 186,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 20724.638000000028,
"styleRecalcs": 142,
"styleRecalcDurationMs": 17.08599999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20697.891,
"heapDeltaBytes": -53918452,
"heapUsedBytes": 170657044,
"domNodes": -8316,
"jsHeapTotalBytes": -8069120,
"scriptDurationMs": 892.987,
"eventListeners": -16383,
"totalBlockingTimeMs": 250,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 444.074999999998,
"styleRecalcs": 13,
"styleRecalcDurationMs": 20.315000000000005,
"layouts": 3,
"layoutDurationMs": 0.576,
"taskDurationMs": 124.399,
"heapDeltaBytes": -15427748,
"heapUsedBytes": 51827476,
"domNodes": 132,
"jsHeapTotalBytes": 7458816,
"scriptDurationMs": 10.353000000000002,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 478.69600000001356,
"styleRecalcs": 12,
"styleRecalcDurationMs": 20.249000000000002,
"layouts": 2,
"layoutDurationMs": 0.39800000000000013,
"taskDurationMs": 123.294,
"heapDeltaBytes": -15671500,
"heapUsedBytes": 51598800,
"domNodes": 121,
"jsHeapTotalBytes": 6934528,
"scriptDurationMs": 9.447000000000001,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
}
]
} |
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## cloud/1.49 #14802 +/- ##
=============================================
Coverage ? 78.77%
=============================================
Files ? 1791
Lines ? 104625
Branches ? 32093
=============================================
Hits ? 82419
Misses ? 21776
Partials ? 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
cloud/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