fix(workspace): recover from initialization failures - #14612
Conversation
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎭 Playwright: ✅ 1788 passed, 0 failed · 2 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 8.26 MB gzip 🔴 +1.13 kBDetailsSummary
Category Glance App Entry Points — 3.64 kB (baseline 3.64 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.35 MB (baseline 1.35 MB) • 🟢 -67 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed / 1 unchanged Views & Navigation — 114 kB (baseline 114 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 13 added / 13 removed / 4 unchanged Panels & Settings — 552 kB (baseline 552 kB) • 🔴 +720 BConfiguration panels, inspectors, and settings screens
Status: 12 added / 12 removed / 15 unchanged User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 6 added / 6 removed / 4 unchanged Editors & Dialogs — 124 kB (baseline 124 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 6 added / 6 removed / 1 unchanged UI Components — 72.1 kB (baseline 72.1 kB) • ⚪ 0 BReusable component library chunks
Status: 6 added / 6 removed / 9 unchanged Data & Services — 3.48 MB (baseline 3.48 MB) • 🟢 -266 BStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 387 kB (baseline 387 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 19 added / 19 removed / 17 unchanged Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 12.8 MB (baseline 12.8 MB) • 🔴 +4.74 kBBundles that do not match a named category
Status: 79 added / 79 removed / 205 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-05T12:02:36.308Z",
"gitSha": "0eb7d4c68d89dcbd9cd4931434786d703f0b35d2",
"branch": "dante/workspace-init-recovery",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2095.6259999999816,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.883000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 585.471,
"heapDeltaBytes": 6024936,
"heapUsedBytes": 71738396,
"domNodes": 16,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 25.833,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2032.0299999999634,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.543000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 446.601,
"heapDeltaBytes": 5983196,
"heapUsedBytes": 71592412,
"domNodes": 18,
"jsHeapTotalBytes": 22806528,
"scriptDurationMs": 14.469,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2053.9660000000026,
"styleRecalcs": 80,
"styleRecalcDurationMs": 51.559999999999995,
"layouts": 12,
"layoutDurationMs": 4.869999999999999,
"taskDurationMs": 1088.7450000000001,
"heapDeltaBytes": 1032732,
"heapUsedBytes": 66539396,
"domNodes": 60,
"jsHeapTotalBytes": 24907776,
"scriptDurationMs": 150.02599999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1807.425999999964,
"styleRecalcs": 74,
"styleRecalcDurationMs": 36.76,
"layouts": 12,
"layoutDurationMs": 3.5380000000000003,
"taskDurationMs": 875.5169999999999,
"heapDeltaBytes": -11576156,
"heapUsedBytes": 53874284,
"domNodes": -280,
"jsHeapTotalBytes": 22654976,
"scriptDurationMs": 120.71600000000001,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1753.861000000029,
"styleRecalcs": 31,
"styleRecalcDurationMs": 18.735000000000003,
"layouts": 6,
"layoutDurationMs": 0.691,
"taskDurationMs": 422.489,
"heapDeltaBytes": 9101456,
"heapUsedBytes": 74567080,
"domNodes": 77,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 23.736,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1707.5060000000803,
"styleRecalcs": 30,
"styleRecalcDurationMs": 15.68,
"layouts": 6,
"layoutDurationMs": 0.565,
"taskDurationMs": 353.29699999999997,
"heapDeltaBytes": 8851172,
"heapUsedBytes": 74590284,
"domNodes": 76,
"jsHeapTotalBytes": 23068672,
"scriptDurationMs": 17.89,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 594.2530000000374,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.9719999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 397.304,
"heapDeltaBytes": -11118016,
"heapUsedBytes": 54645892,
"domNodes": 18,
"jsHeapTotalBytes": 23855104,
"scriptDurationMs": 62.375,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 590.327000000002,
"styleRecalcs": 12,
"styleRecalcDurationMs": 8.310000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 379.563,
"heapDeltaBytes": -10937216,
"heapUsedBytes": 54561820,
"domNodes": 20,
"jsHeapTotalBytes": 23855104,
"scriptDurationMs": 58.835,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2051.6379999999685,
"styleRecalcs": 7,
"styleRecalcDurationMs": 7.820999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 805.498,
"heapDeltaBytes": 9719232,
"heapUsedBytes": 69793860,
"domNodes": -283,
"jsHeapTotalBytes": 2465792,
"scriptDurationMs": 128.473,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-idle",
"durationMs": 2034.104999999954,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.805999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 656.9820000000001,
"heapDeltaBytes": 6750332,
"heapUsedBytes": 66150172,
"domNodes": -283,
"jsHeapTotalBytes": 2990080,
"scriptDurationMs": 100.06899999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2172.032999999999,
"styleRecalcs": 68,
"styleRecalcDurationMs": 13.673000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1341.034,
"heapDeltaBytes": 1199516,
"heapUsedBytes": 62478140,
"domNodes": -286,
"jsHeapTotalBytes": 4231168,
"scriptDurationMs": 443.021,
"eventListeners": -177,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2151.523999999995,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.317000000000004,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1261.2069999999999,
"heapDeltaBytes": 4961848,
"heapUsedBytes": 65562452,
"domNodes": -286,
"jsHeapTotalBytes": 4231168,
"scriptDurationMs": 428.327,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3247.2339999999917,
"styleRecalcs": 64,
"styleRecalcDurationMs": 15.056999999999997,
"layouts": 60,
"layoutDurationMs": 7.617000000000001,
"taskDurationMs": 1542.056,
"heapDeltaBytes": 23856184,
"heapUsedBytes": 85887608,
"domNodes": 10,
"jsHeapTotalBytes": 7602176,
"scriptDurationMs": 544.0070000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-zoom",
"durationMs": 3183.00899999997,
"styleRecalcs": 66,
"styleRecalcDurationMs": 14.614999999999997,
"layouts": 60,
"layoutDurationMs": 7.149000000000001,
"taskDurationMs": 1491.276,
"heapDeltaBytes": -3031564,
"heapUsedBytes": 59128600,
"domNodes": -287,
"jsHeapTotalBytes": 6660096,
"scriptDurationMs": 516.652,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2010.6509999999957,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.914999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 714.8030000000001,
"heapDeltaBytes": 6653176,
"heapUsedBytes": 67676524,
"domNodes": -286,
"jsHeapTotalBytes": 3514368,
"scriptDurationMs": 113.749,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2016.7380000000321,
"styleRecalcs": 8,
"styleRecalcDurationMs": 6.994999999999997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 649.192,
"heapDeltaBytes": 6664152,
"heapUsedBytes": 69831984,
"domNodes": -282,
"jsHeapTotalBytes": 3776512,
"scriptDurationMs": 97.14500000000001,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 612.6979999999662,
"styleRecalcs": 48,
"styleRecalcDurationMs": 12.622,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 426.21100000000007,
"heapDeltaBytes": -10143048,
"heapUsedBytes": 55680860,
"domNodes": 22,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 125.418,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 560.7339999999112,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.955,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 394.99899999999997,
"heapDeltaBytes": -10529308,
"heapUsedBytes": 55343248,
"domNodes": 20,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 117.95199999999998,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-idle",
"durationMs": 2026.7110000000343,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.870000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 542.704,
"heapDeltaBytes": 5364392,
"heapUsedBytes": 71177180,
"domNodes": 18,
"jsHeapTotalBytes": 23592960,
"scriptDurationMs": 21.342,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2011.3289999999324,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.480000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 430.915,
"heapDeltaBytes": 5609308,
"heapUsedBytes": 71244016,
"domNodes": 22,
"jsHeapTotalBytes": 23592960,
"scriptDurationMs": 12.25,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1762.7940000000422,
"styleRecalcs": 77,
"styleRecalcDurationMs": 38.425000000000004,
"layouts": 16,
"layoutDurationMs": 4.765999999999999,
"taskDurationMs": 903.606,
"heapDeltaBytes": -20112692,
"heapUsedBytes": 45720628,
"domNodes": 60,
"jsHeapTotalBytes": 21999616,
"scriptDurationMs": 107.638,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1696.6059999999743,
"styleRecalcs": 77,
"styleRecalcDurationMs": 35.092999999999996,
"layouts": 16,
"layoutDurationMs": 3.763,
"taskDurationMs": 735.012,
"heapDeltaBytes": -3801900,
"heapUsedBytes": 61837396,
"domNodes": 65,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 89.17699999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1383.719000000042,
"styleRecalcs": 17,
"styleRecalcDurationMs": 29.669,
"layouts": 13,
"layoutDurationMs": 12.705999999999998,
"taskDurationMs": 908.7280000000002,
"heapDeltaBytes": 8847084,
"heapUsedBytes": 100580284,
"domNodes": 13673,
"jsHeapTotalBytes": 11796480,
"scriptDurationMs": 34.58,
"eventListeners": 2375,
"totalBlockingTimeMs": 133,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8657.237000000008,
"styleRecalcs": 250,
"styleRecalcDurationMs": 38.543,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4873.54,
"heapDeltaBytes": 16172400,
"heapUsedBytes": 75665044,
"domNodes": -279,
"jsHeapTotalBytes": 6852608,
"scriptDurationMs": 1521.412,
"eventListeners": -161,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "viewport-pan-sweep",
"durationMs": 8214.018000000011,
"styleRecalcs": 250,
"styleRecalcDurationMs": 36.18599999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4340.319,
"heapDeltaBytes": 12090228,
"heapUsedBytes": 71431428,
"domNodes": -281,
"jsHeapTotalBytes": 5804032,
"scriptDurationMs": 1373.736,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17511.29400000002,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17489.703999999998,
"heapDeltaBytes": -43320940,
"heapUsedBytes": 171280116,
"domNodes": -8312,
"jsHeapTotalBytes": -6230016,
"scriptDurationMs": 597.09,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 16627.317999999945,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16610.659,
"heapDeltaBytes": -46368568,
"heapUsedBytes": 166229836,
"domNodes": -8312,
"jsHeapTotalBytes": -10162176,
"scriptDurationMs": 555.844,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 20919.72199999998,
"styleRecalcs": 146,
"styleRecalcDurationMs": 17.61299999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20874.086000000003,
"heapDeltaBytes": -40839660,
"heapUsedBytes": 169111116,
"domNodes": -8312,
"jsHeapTotalBytes": -11210752,
"scriptDurationMs": 890.5569999999999,
"eventListeners": -16385,
"totalBlockingTimeMs": 292,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 20756.703000000016,
"styleRecalcs": 142,
"styleRecalcDurationMs": 17.079999999999984,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20719.641000000003,
"heapDeltaBytes": -54467232,
"heapUsedBytes": 170819468,
"domNodes": -8312,
"jsHeapTotalBytes": -11739136,
"scriptDurationMs": 886.1229999999999,
"eventListeners": -16383,
"totalBlockingTimeMs": 142,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 446.93499999993946,
"styleRecalcs": 13,
"styleRecalcDurationMs": 18.162000000000003,
"layouts": 2,
"layoutDurationMs": 0.37699999999999995,
"taskDurationMs": 109.35400000000001,
"heapDeltaBytes": 5303472,
"heapUsedBytes": 69984028,
"domNodes": 134,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 9.440000000000001,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 469.3699999999126,
"styleRecalcs": 18,
"styleRecalcDurationMs": 20.488,
"layouts": 3,
"layoutDurationMs": 1.2460000000000002,
"taskDurationMs": 112.84700000000002,
"heapDeltaBytes": 5199632,
"heapUsedBytes": 70164848,
"domNodes": 147,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 9.322000000000001,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
}
]
} |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughCloud workspace initialization now uses explicit states and localized recovery controls. Remote configuration refreshes support cancellation and stale-request protection. The workspace popover remains available with account actions during non-ready states, and browser fixtures support workspace mocking. ChangesWorkspace recovery flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant CloudUser
participant CurrentUserButton
participant WorkspaceAuthGate
participant refreshRemoteConfig
participant teamWorkspaceStore
CloudUser->>CurrentUserButton: Open user menu
CurrentUserButton->>WorkspaceAuthGate: Read initialization state
WorkspaceAuthGate->>refreshRemoteConfig: Refresh configuration
refreshRemoteConfig-->>WorkspaceAuthGate: Return ready or error status
WorkspaceAuthGate->>teamWorkspaceStore: Initialize workspace
teamWorkspaceStore-->>WorkspaceAuthGate: Return ready or error state
WorkspaceAuthGate-->>CurrentUserButton: Provide ready or account-only view
CurrentUserButton-->>CloudUser: Show account actions or workspace controls
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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/platform/settings/composables/useSettingUI.test.ts`:
- Around line 202-209: Update the “hides the empty Workspace navigation group
for logged-out Cloud users” test to set env.state.teamWorkspacesEnabled to true
alongside the Cloud and logged-out state, ensuring useSettingUI exercises the
workspace navigation filter rather than the legacy menu path.
🪄 Autofix (Beta)
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: 6a71c9ee-20eb-40ec-9cd8-a888abfe9a21
📒 Files selected for processing (13)
browser_tests/fixtures/ComfyPage.tsbrowser_tests/fixtures/utils/workspaceMocks.tssrc/components/topbar/CurrentUserButton.test.tssrc/components/topbar/CurrentUserButton.vuesrc/locales/en/main.jsonsrc/platform/settings/composables/useSettingUI.test.tssrc/platform/settings/composables/useSettingUI.tssrc/platform/workspace/auth/WorkspaceAuthGate.test.tssrc/platform/workspace/auth/WorkspaceAuthGate.vuesrc/platform/workspace/components/CurrentUserPopoverWorkspace.test.tssrc/platform/workspace/components/CurrentUserPopoverWorkspace.vuesrc/platform/workspace/stores/teamWorkspaceStore.test.tssrc/platform/workspace/stores/teamWorkspaceStore.ts
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #14612 +/- ##
=========================================
Coverage 78.69% 78.70%
=========================================
Files 1797 1796 -1
Lines 106766 109054 +2288
Branches 29719 33434 +3715
=========================================
+ Hits 84020 85826 +1806
- Misses 22286 22732 +446
- Partials 460 496 +36
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 214 files with indirect coverage changes 🚀 New features to boost your workflow:
|
christian-byrne
left a comment
There was a problem hiding this comment.
Reviewed the whole stack (#14612-#14615) together; the cross-service coordination question lives on #14613 and is the one that gates the stack.
On this PR specifically: the recovery affordance is a clear improvement over the pre-state, where a bootstrap failure left an invisible permanent splash. Four comments inline, none of them regressions against main. The first is the one I would most want addressed before merge.
One pre-existing item that this PR does not introduce but that bounds what it can achieve. until(isInitialized).toBe(true, { timeout: FIREBASE_INIT_TIMEOUT_MS }) at line 77 is unchanged context here, and VueUse's promiseTimeout(ms, throwOnTimeout = false) (@vueuse/shared 14.3.0, dist/index.js:434, with throwOnTimeout destructured with no default at :1165) means that timeout resolves rather than rejects. A hung Firebase init therefore falls through to the !currentUser branch and renders the app, so the most likely boot hang is the one failure class that can never reach the new panel and never produces a Sentry event. { timeout: FIREBASE_INIT_TIMEOUT_MS, throwOnTimeout: true } is the one-word change if you want the panel to cover it; happy for that to be a follow-up.
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/platform/remoteConfig/refreshRemoteConfig.ts`:
- Around line 49-59: Update the refresh flow around the AbortController and
catch handling so caller-triggered cancellation returns without clearing
configuration or changing remoteConfigState, while internal timeout aborts still
transition to error. Distinguish signal-caused aborts from the timeout in the
relevant refresh function, and add a test that aborts the caller signal without
starting a newer refresh and verifies shared state remains unchanged.
In `@src/platform/workspace/auth/WorkspaceAuthGate.vue`:
- Around line 93-96: Update the initialization lifecycle around
initializationGeneration and initializationController by adding one cleanup
function that increments the generation, aborts the active controller, and
clears the controller; invoke it before logout() and from onUnmounted. After
each awaited workspace operation, recheck the generation before continuing to
later side effects so canceled initialization cannot resume pricing flow or
setup after logout or unmount.
🪄 Autofix (Beta)
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: 827c5d97-ce25-45ec-98c4-1fa357c7889b
📒 Files selected for processing (7)
src/locales/en/main.jsonsrc/platform/remoteConfig/refreshRemoteConfig.test.tssrc/platform/remoteConfig/refreshRemoteConfig.tssrc/platform/remoteConfig/remoteConfig.tssrc/platform/workspace/auth/WorkspaceAuthGate.test.tssrc/platform/workspace/auth/WorkspaceAuthGate.vuesrc/platform/workspace/components/CurrentUserPopoverWorkspace.test.ts
|
@christian-byrne Thanks for the detailed review. I also included the Firebase timeout correction you called out: the VueUse wait now sets |
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/platform/remoteConfig/refreshRemoteConfig.ts (1)
92-93: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winSet the error state for every non-success response.
If an earlier refresh set
remoteConfigStateto'authenticated', a 5xx response only logs and returns.WorkspaceAuthGatethen accepts the stale state and continues workspace initialization with stale feature flags.Use the same failure transition as the
catchpath for all non-success responses. PreserveremoteConfigErrorStatusonly for 401 and 403. Add a regression that starts from authenticated state, returns 500, and verifies that workspace initialization does not continue.As per coding guidelines, “Implement proper error handling.”
🤖 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/platform/remoteConfig/refreshRemoteConfig.ts` around lines 92 - 93, Update the non-success response handling in the remote-config refresh flow to transition remoteConfigState to 'error' for every failed status, matching the catch path. Keep remoteConfigErrorStatus updates limited to 401 and 403, and add a regression covering an authenticated state followed by a 500 response to verify workspace initialization is blocked.Source: Coding guidelines
🤖 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/platform/remoteConfig/refreshRemoteConfig.ts`:
- Around line 92-93: Update the non-success response handling in the
remote-config refresh flow to transition remoteConfigState to 'error' for every
failed status, matching the catch path. Keep remoteConfigErrorStatus updates
limited to 401 and 403, and add a regression covering an authenticated state
followed by a 500 response to verify workspace initialization is blocked.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: ddd970cd-8549-42c1-98af-38b3a352ff6c
📒 Files selected for processing (4)
src/platform/remoteConfig/refreshRemoteConfig.test.tssrc/platform/remoteConfig/refreshRemoteConfig.tssrc/platform/workspace/auth/WorkspaceAuthGate.test.tssrc/platform/workspace/auth/WorkspaceAuthGate.vue
|
Addressed the latest outside-diff review finding in All non-success Validation: focused unit tests 35/35, |
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf
Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fc8cd-b288-7161-a590-cd76d51212bf Co-authored-by: Amp <amp@ampcode.com>
christian-byrne
left a comment
There was a problem hiding this comment.
Approving. Every finding from my review is fixed and I verified each against the head rather than the thread state.
- The retry no longer races itself:
refreshRemoteConfignow carries a generation token that guards both the success and the error path, and the authenticated branch takes an abort signal. - The sign-out escape actually escapes.
handleSignOutcallscancelInitialization()thenuseAuthActions().logout(), so the full teardown chain runs includingdeleteSession(), rather than the bare FirebasesignOutthat left the user on the same dead panel. - The error panel is now announced and reachable:
role="alert",aria-hiddenon the decorative icon, focus moved on entering the error state, andaria-busyin place of:loadingso the retry button keeps its accessible name. - The popover test asserts something load-bearing, and splash-loader removal is covered.
The recovery affordance is a clear improvement over the pre-state, where a bootstrap failure left an invisible permanent splash with no way forward.
One pre-existing item I raised in my first review and am not holding this on: until(isInitialized).toBe(true, { timeout }) resolves rather than rejects, because VueUse defaults throwOnTimeout to false. That is unchanged context from main, so a hung Firebase init still falls through to the unauthenticated branch and never reaches this panel. Worth a follow-up, not a blocker here.
|
@coderabbitai approve |
✅ Action performedComments resolved and changes approved. |
|
|
) ## Summary Part 2/4 of the workspace/billing rollout retirement stack. Removes `team_workspaces_enabled` atomically after the recovery foundation in [Comfy-Org#14612](Comfy-Org#14612). Predecessor: [Comfy-Org#14612](Comfy-Org#14612). Successor: [Comfy-Org#14614](Comfy-Org#14614). ## Root cause Workspace mode was split across a long-lived rollout key in session cookies, token selection, auth restoration, workspace boot, account UI/settings, URL actions, and billing routing. Once Cloud workspace support is the default, keeping both branches creates divergent authentication and billing behavior and makes backend feature-key removal unsafe. ## Changes - Remove the team workspace key from remote-config types, enum/getters/cache/refresh, local override, Storybook, browser payloads, and obsolete flag tests. - Make Cloud workspace auth/session restoration/switch/recovery and workspace UI/loaders unconditional after the authenticated config checkpoint. - Preserve OSS and unloaded-workspace legacy bootstrap behavior. - Preserve `consolidated_billing_enabled` and the intermediate billing matrix, including explicit `legacy_stripe` account operations with unified pricing. - Update Cloud fixtures and affected account, settings, subscription, pricing, and token-flow coverage. ## AS IS Cloud workspace/auth/UI/billing paths can diverge depending on `team_workspaces_enabled`. ## TO BE Cloud workspace mode is the single path after authenticated config loads; OSS and unloaded workspace bootstrap remain legacy. No intended visual change; this is rollout retirement, so no screenshot is required. ## Regression coverage - Auth session cookie, API/WebSocket token priority, workspace restoration/switch/recovery. - Workspace gate, account UI, settings/dialog layout, create/invite loaders, pricing, and subscription success behavior. - Intermediate billing routing for OSS, unloaded Cloud, team, personal consolidated off/on, and `legacy_stripe`. - Cloud browser fixtures omit the retired key. - Exact source/browser scan confirms no `team_workspaces_enabled` or `teamWorkspacesEnabled` references. ## Validation - 14 focused Vitest files: 453 tests passed. - `pnpm typecheck` and `pnpm typecheck:browser` passed via commit hook. - targeted formatting/lint passed via commit hook. - `git diff --check dante/workspace-init-recovery...HEAD` passed. - `pnpm knip --cache` passed via push hook. - Focused Cloud Playwright: billing facade 2 passed; pricing owner flow 1 passed. The member-plan flow timed out during local app boot before assertions; the corrected full Cloud Playwright CI job passed. ## Review focus Review only the atomic team-flag removal and intermediate routing matrix. Canonical billing status and consolidated-flag retirement are intentionally deferred. ## Stack/deployment order 1. [Comfy-Org#14612](Comfy-Org#14612) — recovery foundation 2. **This PR** — retire team workspace rollout flag 3. [Comfy-Org#14614](Comfy-Org#14614) — canonical billing status and legacy rail preservation 4. [Comfy-Org#14615](Comfy-Org#14615) — retire consolidated billing rollout flag Deploy the frontend stack before the backend removes `/api/features` compatibility keys. This is also a drain requirement: age out deployed frontend versions and open tabs that still read either retired key before removing backend compatibility keys. Confirm `team_workspaces_enabled` is at 100% and update `Comfy-Org/cloud#6040` to depend on the selected stack rather than competing PR Comfy-Org#14530 before merge. After backend key removal, reverting this PR is not a behavioral restore because a reinstated read of the missing key resolves to the off branch. --------- Co-authored-by: Dante <6510430+dante01yoon@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com>
…lures (#14802) 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.ts` by retaining the merged PR behavior: route helpers accept `Page | BrowserContext` and workspace routes use the supplied `routeTarget`. Kept the workspace-list helper local because this release branch does not yet contain its later stack consumers. ## Validation - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm knip --cache` - Focused Vitest: 6 files, 143 tests passed - lint/format pre-commit hooks - `git diff --check` Original PR: #14612 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>
…ures (#14801) 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.ts` by retaining the merged PR behavior: route helpers accept `Page | BrowserContext` and workspace routes use the supplied `routeTarget`. Kept the workspace-list helper local because this release branch does not yet contain its later stack consumers. ## Validation - `pnpm typecheck` - `pnpm typecheck:browser` - `pnpm knip --cache` - Focused Vitest: 6 files, 143 tests passed - lint/format pre-commit hooks - `git diff --check` Original PR: #14612 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>
|
|
Part 4/4 of the workspace and billing rollout retirement stack. Predecessor: [#14614](#14614) Successor: none (final stack PR) ## Summary Retires `consolidated_billing_enabled` after canonical billing discovery and explicit rail preservation landed in [#14614](#14614). Loaded Cloud workspaces now use unified pricing, while personal workspaces on `legacy_stripe` retain legacy account operations. ## Root cause The final billing route still depended on a compatibility feature key even after the workspace billing contract and canonical rail status were available. Removing the backend key first would therefore change personal-workspace routing during bootstrap or strand legacy Stripe balance/top-up/management behavior. The frontend needs a deterministic route based on distribution, workspace readiness/type, and the canonical billing rail before backend compatibility keys disappear. ## Changes - Removes the consolidated-billing key from remote-config types, enum/getter, authenticated cache/refresh, local override path, Storybook mocks, and browser payloads. - Routes OSS and unloaded Cloud workspaces through legacy bootstrap. - Routes loaded Cloud team workspaces and personal `unknown`/`stripe` rails through workspace billing. - Preserves legacy account operations for personal `legacy_stripe` while keeping unified pricing for every loaded Cloud workspace. - Keeps the legacy adapter, balance, top-up, management behavior, and meaningful regression coverage. - Re-runs the canonical-status rail regression with both retired feature keys absent from `/api/features`. ## AS IS A loaded Cloud personal workspace still consults `consolidated_billing_enabled` to decide its billing adapter and pricing route. No intended visual change. This is rollout cleanup, so no screenshot is required. ## TO BE Loaded Cloud workspaces use unified pricing without a rollout key. Canonical `billing_rail` determines whether personal account operations remain on `legacy_stripe`; team and migrated/unknown personal workspaces use workspace billing. No intended visual change. The existing legacy rail UI remains available where the canonical status selects it. ## Regression coverage - OSS and unloaded-workspace legacy bootstrap. - Cloud team workspace billing. - Cloud personal unknown/Stripe workspace billing. - Cloud personal `legacy_stripe` legacy account operations plus unified pricing. - Billing-context adapter switching and preserved legacy adapter behavior. - Playwright avatar-popover rail selection with `/api/features` omitting both retired keys, canonical status discovery, legacy balance/credits, and no stale workspace zero balance. - Repository search confirms no consolidated-flag symbol/string remains in `src` or `browser_tests`. ## Validation - `pnpm test:unit src/composables/useFeatureFlags.test.ts src/composables/billing/useBillingRouting.test.ts src/composables/billing/useBillingContext.test.ts src/platform/remoteConfig/refreshRemoteConfig.test.ts` — 83 passed. - `pnpm typecheck` — passed. - `pnpm typecheck:browser` — passed. - Targeted oxfmt/oxlint/ESLint commit checks — passed. - `pnpm knip --cache` pre-push check — passed. - `PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5174 pnpm test:browser browser_tests/tests/billingFacadeConsumers.spec.ts -g 'avatar popover'` — 1 passed. - `git diff --check dante/canonical-billing-status...HEAD` — passed. ## Review focus Please review only this PR's removal of the consolidated flag and the final routing matrix. Canonical endpoint migration and pending-checkout stabilization belong to [#14614](#14614); workspace-flag retirement belongs to [#14613](#14613). ## Stack/deployment order 1. [#14612](#14612) — workspace initialization recovery 2. [#14613](#14613) — retire team workspace rollout flag 3. [#14614](#14614) — canonical billing status and legacy rail preservation 4. [#14615](#14615) — this PR; retire consolidated billing rollout flag Deploy the frontend stack in this order before the backend removes the `/api/features` compatibility keys. Each branch remains independently deployable; do not deploy a later PR without its predecessor. This is also a drain requirement: age out deployed frontend versions and open tabs that still read either retired key before removing backend compatibility keys. Confirm `consolidated_billing_enabled` is at 100% and update `Comfy-Org/cloud#6040` to depend on this selected stack rather than competing PR #14530 before merge. After backend key removal, reverting this PR is not a behavioral restore because a reinstated read of the missing key resolves to the off branch. Co-authored-by: Connor Byrne <c.byrne@comfy.org>
…fy-Org#14614) ## Summary Part 3/4 of the workspace/billing rollout retirement stack. Standardizes Cloud subscription discovery on canonical workspace billing status while preserving explicit `legacy_stripe` account operations. Predecessor: [Comfy-Org#14613](Comfy-Org#14613). Successor: [Comfy-Org#14615](Comfy-Org#14615). ## Root cause Cloud subscription state was read from both `/customers/cloud-subscription-status` and `/api/billing/status` with different response types and cancellation fields (`end_date` versus `cancel_at`). That allowed stale rail selection and could mix workspace status/balance with legacy top-up and management behavior. ## Changes - Read Cloud subscription status only from `/api/billing/status` using the canonical type and `cancel_at`. - Avoid ingress status requests for non-Cloud builds. - Preserve `legacy_stripe` balance, top-up, and management while unified checkout remains workspace-backed. - Keep `consolidated_billing_enabled` routing intact. - Dispatch the production pending-checkout event in the Playwright helper. - Strengthen the rail regression to prove canonical discovery, no old-status request, legacy balance/credits visibility, and rejection of stale workspace zero balance. ## AS IS Two status contracts can compete, and cancellation/rail state can be stale or translated inconsistently. ## TO BE Canonical status selects the rail; explicit legacy Stripe continues legacy account operations and unified checkout. No intended visual change, so no screenshot is required. ## Regression coverage - Canonical Cloud status and non-Cloud no-ingress behavior. - Canonical cancellation in watcher and legacy adapter. - Adapter switching and stale-rail failure. - Playwright regression serves `consolidated_billing_enabled: true` + `legacy_stripe`, observes canonical status and legacy balance, proves old status absent, and verifies credits/top-up UI without stale zero balance. ## Validation - 4 focused Vitest files: 79 tests passed. - `pnpm typecheck` and `pnpm typecheck:browser` passed via commit hooks. - targeted format/lint passed via commit hooks. - `git diff --check dante/retire-team-workspace-flag...HEAD` passed. - `pnpm knip --cache` passed via push hook. - Focused Playwright legacy-rail regression passed against local Cloud dev server (1 passed). The full two-test file had one transient initial boot timeout on its first run; its second pricing-flow test passed, and the focused failed case passed on rerun. ## Review focus Review only canonical status/cancellation semantics and the legacy-account/unified-checkout boundary. Consolidated flag retirement belongs to part 4. ## Stack/deployment order 1. [Comfy-Org#14612](Comfy-Org#14612) — recovery foundation 2. [Comfy-Org#14613](Comfy-Org#14613) — retire team workspace rollout flag 3. **This PR** — canonical status and legacy rail preservation 4. [Comfy-Org#14615](Comfy-Org#14615) — retire consolidated billing rollout flag Deploy the frontend stack before the backend removes `/api/features` compatibility keys. --------- Co-authored-by: Connor Byrne <c.byrne@comfy.org> Co-authored-by: Amp <amp@ampcode.com>
Summary
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.
Root cause
WorkspaceAuthGatefailed closed on config, unified-auth, workspace initialization, and timeout failures but exposed no recovery action. The team workspace store also treatederroras terminal, and the Cloud account popover disappeared while workspace initialization was loading or failed, preventing account actions.Changes
teamWorkspaceStore.initialize()to retry fromerror.team_workspaces_enabledis off.AS IS
Initialization failures permanently gate the app and loading/error workspace state removes Cloud account actions.
TO BE
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.
Regression coverage
error -> loading -> readyretry.Validation
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)oxfmt --check(passed)git diff --check public/main...HEAD(passed)pnpm knip --cache(passed via push hook)Review focus
Review only recoverability, retry state transitions, and preservation of both rollout modes. Flag retirement belongs to later PRs.
Stack/deployment order
Deploy the frontend stack before the backend removes
/api/featurescompatibility keys.