feat(workspace): enable local credit workspace switching FE-1584 - #15164
Conversation
Amp-Thread-ID: https://ampcode.com/threads/T-019ff85b-da53-7107-8aaf-c383f7153d85 Co-authored-by: Amp <amp@ampcode.com>
|
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:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
Included review availability: Your plan includes up to 4 reviews per rolling hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe PR adds workspace-specific authentication and API routing, enables background workspace initialization outside cloud deployments, supports local workspace switching without reloads, and updates billing and top-bar workspace controls for cloud and non-cloud distributions. ChangesWorkspace-aware platform behavior
Estimated code review effort: 5 (Critical) | ~120 minutes Merge Risk: 🟡 Moderate · up to This PR enables Local workspace-based credit switching without reloading workflows, but unresolved state-management and account-lifecycle issues could leave workspace data inconsistent or allow stale initialization to affect a later session; merge should wait for these risks to be fixed or explicitly accepted. Sequence Diagram(s)sequenceDiagram
participant User
participant WorkspaceAuthGate
participant teamWorkspaceStore
participant workspaceAuthStore
participant workspaceApi
User->>WorkspaceAuthGate: sign in
WorkspaceAuthGate->>teamWorkspaceStore: initialize workspace context
teamWorkspaceStore->>workspaceAuthStore: authenticate active workspace
workspaceAuthStore->>workspaceApi: request workspace token
workspaceApi-->>workspaceAuthStore: return workspace credentials
workspaceAuthStore-->>teamWorkspaceStore: confirm workspace
teamWorkspaceStore-->>User: update workspace UI without reload
🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✨ Finishing Touches 💡 1⚔️ Resolve merge conflicts 💡
📝 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 |
…orkspace-switcher # Conflicts: # src/platform/workspace/stores/billingOperationStore.test.ts # src/platform/workspace/stores/teamWorkspaceStore.test.ts
🎭 Playwright: ✅ 1841 passed, 0 failed📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 9.13 MB gzip 🔴 +2.9 kBDetailsSummary
Category Glance App Entry Points — 3.71 kB (baseline 3.71 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.37 MB (baseline 1.37 MB) • 🔴 +3.43 kBGraph 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 — 566 kB (baseline 566 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 16 unchanged User & Accounts — 27.5 kB (baseline 27.5 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 6 added / 6 removed / 5 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: 6 added / 6 removed / 8 unchanged Data & Services — 3.53 MB (baseline 3.53 MB) • 🔴 +3.3 kBStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 549 kB (baseline 549 kB) • 🟢 -74 BHelpers, composables, and utility bundles
Status: 21 added / 21 removed / 16 unchanged Vendor & Third-Party — 18.1 MB (baseline 18.1 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 18 unchanged Other — 14.2 MB (baseline 14.2 MB) • 🔴 +4.23 kBBundles that do not match a named category
Status: 77 added / 76 removed / 211 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-20T17:42:35.749Z",
"gitSha": "42b3ecbb17159bd9449f082abdc38c55ebc3b72e",
"branch": "dante01yoon/fe-1584-local-workspace-switcher",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2058.4240000000023,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.496000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 540.653,
"heapDeltaBytes": -1072440,
"heapUsedBytes": 60951752,
"domNodes": -281,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 8.683,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2090.787999999975,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.528,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 590.409,
"heapDeltaBytes": 16690652,
"heapUsedBytes": 78092720,
"domNodes": -282,
"jsHeapTotalBytes": 3661824,
"scriptDurationMs": 13.499999999999998,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2144.672000000014,
"styleRecalcs": 79,
"styleRecalcDurationMs": 54.039,
"layouts": 12,
"layoutDurationMs": 4.3309999999999995,
"taskDurationMs": 1101.875,
"heapDeltaBytes": 6694228,
"heapUsedBytes": 68776048,
"domNodes": -281,
"jsHeapTotalBytes": 6545408,
"scriptDurationMs": 127.53799999999998,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1945.1340000000528,
"styleRecalcs": 75,
"styleRecalcDurationMs": 44.612,
"layouts": 12,
"layoutDurationMs": 4.138999999999999,
"taskDurationMs": 1000.4660000000001,
"heapDeltaBytes": 7337040,
"heapUsedBytes": 68032848,
"domNodes": -280,
"jsHeapTotalBytes": 6283264,
"scriptDurationMs": 115.74099999999999,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1726.9039999999904,
"styleRecalcs": 32,
"styleRecalcDurationMs": 18.317999999999998,
"layouts": 6,
"layoutDurationMs": 0.753,
"taskDurationMs": 373.328,
"heapDeltaBytes": 2463904,
"heapUsedBytes": 63799608,
"domNodes": 76,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 11.406,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1740.9860000000208,
"styleRecalcs": 33,
"styleRecalcDurationMs": 19.569000000000003,
"layouts": 6,
"layoutDurationMs": 0.8210000000000001,
"taskDurationMs": 427.29200000000003,
"heapDeltaBytes": 3143196,
"heapUsedBytes": 64245288,
"domNodes": 77,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 13.804,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 588.5820000000308,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.085,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 381.14699999999993,
"heapDeltaBytes": 10386360,
"heapUsedBytes": 72087296,
"domNodes": 18,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 57.074,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 610.6969999999592,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.069000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 391.19200000000006,
"heapDeltaBytes": 10778500,
"heapUsedBytes": 72139640,
"domNodes": 16,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 58.555,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2037.8429999999526,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.293,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 652.899,
"heapDeltaBytes": -8024832,
"heapUsedBytes": 67758720,
"domNodes": -282,
"jsHeapTotalBytes": -528384,
"scriptDurationMs": 16.637,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 2058.854999999994,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.418,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 794.66,
"heapDeltaBytes": -7335292,
"heapUsedBytes": 68497628,
"domNodes": -281,
"jsHeapTotalBytes": -1839104,
"scriptDurationMs": 19.496,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2150.3809999999817,
"styleRecalcs": 70,
"styleRecalcDurationMs": 17.751999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1210.8,
"heapDeltaBytes": -13027676,
"heapUsedBytes": 63451496,
"domNodes": -281,
"jsHeapTotalBytes": 2093056,
"scriptDurationMs": 320.69499999999994,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2537.992000000031,
"styleRecalcs": 69,
"styleRecalcDurationMs": 17.461000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1425.367,
"heapDeltaBytes": -7124820,
"heapUsedBytes": 69519132,
"domNodes": -278,
"jsHeapTotalBytes": -303104,
"scriptDurationMs": 367.348,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3180.2959999999985,
"styleRecalcs": 66,
"styleRecalcDurationMs": 17.137999999999998,
"layouts": 60,
"layoutDurationMs": 8.886000000000001,
"taskDurationMs": 1418.779,
"heapDeltaBytes": -11924128,
"heapUsedBytes": 66105392,
"domNodes": -287,
"jsHeapTotalBytes": 5500928,
"scriptDurationMs": 402.804,
"eventListeners": -185,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3719.600000000014,
"styleRecalcs": 64,
"styleRecalcDurationMs": 17.146,
"layouts": 60,
"layoutDurationMs": 9.269,
"taskDurationMs": 1695.8489999999997,
"heapDeltaBytes": 10616832,
"heapUsedBytes": 88687640,
"domNodes": -288,
"jsHeapTotalBytes": 4976640,
"scriptDurationMs": 446.099,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333338,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 2189.9740000000065,
"styleRecalcs": 45,
"styleRecalcDurationMs": 9.850999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1433.403,
"heapDeltaBytes": 11357372,
"heapUsedBytes": 91032124,
"domNodes": 10,
"jsHeapTotalBytes": 7532544,
"scriptDurationMs": 460.71599999999995,
"eventListeners": 184,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "legacy-node-drag",
"durationMs": 2809.6719999999777,
"styleRecalcs": 45,
"styleRecalcDurationMs": 11.242000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1653.601,
"heapDeltaBytes": 13351316,
"heapUsedBytes": 92105716,
"domNodes": 10,
"jsHeapTotalBytes": 7270400,
"scriptDurationMs": 520.432,
"eventListeners": 186,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2037.17800000004,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.758000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 619.6189999999999,
"heapDeltaBytes": -12910020,
"heapUsedBytes": 68919132,
"domNodes": -278,
"jsHeapTotalBytes": 4190208,
"scriptDurationMs": 15.53,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2039.4619999999577,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.805,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 802.816,
"heapDeltaBytes": -13446352,
"heapUsedBytes": 63279212,
"domNodes": -280,
"jsHeapTotalBytes": 3403776,
"scriptDurationMs": 20.005,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 607.3450000000093,
"styleRecalcs": 45,
"styleRecalcDurationMs": 9.644,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 408.6360000000001,
"heapDeltaBytes": 10802980,
"heapUsedBytes": 72298468,
"domNodes": 16,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 123.399,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 646.3380000000143,
"styleRecalcs": 46,
"styleRecalcDurationMs": 11.523000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 440.341,
"heapDeltaBytes": 11709208,
"heapUsedBytes": 73091252,
"domNodes": 18,
"jsHeapTotalBytes": 6029312,
"scriptDurationMs": 128.96,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2045.4110000000014,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.055,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 542.7850000000001,
"heapDeltaBytes": 5293424,
"heapUsedBytes": 66812100,
"domNodes": -282,
"jsHeapTotalBytes": 4186112,
"scriptDurationMs": 8.245000000000003,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2036.6510000000062,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.054,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 639.744,
"heapDeltaBytes": -5222308,
"heapUsedBytes": 56111052,
"domNodes": -282,
"jsHeapTotalBytes": 4186112,
"scriptDurationMs": 11.899,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1728.1639999999925,
"styleRecalcs": 75,
"styleRecalcDurationMs": 37.962999999999994,
"layouts": 16,
"layoutDurationMs": 4.887,
"taskDurationMs": 814.5769999999999,
"heapDeltaBytes": 19770300,
"heapUsedBytes": 80860212,
"domNodes": -281,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 87.727,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1810.0470000000541,
"styleRecalcs": 76,
"styleRecalcDurationMs": 43.189,
"layouts": 16,
"layoutDurationMs": 5.180000000000001,
"taskDurationMs": 936.5159999999998,
"heapDeltaBytes": 14220860,
"heapUsedBytes": 75843308,
"domNodes": -282,
"jsHeapTotalBytes": 5496832,
"scriptDurationMs": 98.552,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 1466.6220000000294,
"styleRecalcs": 17,
"styleRecalcDurationMs": 33.11300000000001,
"layouts": 12,
"layoutDurationMs": 14.804999999999998,
"taskDurationMs": 1062.8950000000002,
"heapDeltaBytes": 22139092,
"heapUsedBytes": 105780260,
"domNodes": 13673,
"jsHeapTotalBytes": 11534336,
"scriptDurationMs": 23.25399999999999,
"eventListeners": 2373,
"totalBlockingTimeMs": 150,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8274.954999999976,
"styleRecalcs": 249,
"styleRecalcDurationMs": 44.403999999999996,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4275.621,
"heapDeltaBytes": 70184,
"heapUsedBytes": 75763636,
"domNodes": -280,
"jsHeapTotalBytes": -827392,
"scriptDurationMs": 996.9820000000001,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 9093.507000000045,
"styleRecalcs": 248,
"styleRecalcDurationMs": 47.86399999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4941.05,
"heapDeltaBytes": 6466504,
"heapUsedBytes": 82344404,
"domNodes": -247,
"jsHeapTotalBytes": 188416,
"scriptDurationMs": 1126.1889999999999,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 18716.218000000026,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17785.894,
"heapDeltaBytes": -30985792,
"heapUsedBytes": 178540060,
"domNodes": -8312,
"jsHeapTotalBytes": -17240064,
"scriptDurationMs": 129.386,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.779999999999927,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 19051.71100000007,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 18510.806000000004,
"heapDeltaBytes": -34786364,
"heapUsedBytes": 174561584,
"domNodes": -8312,
"jsHeapTotalBytes": -22220800,
"scriptDurationMs": 143.36100000000002,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.77333333333336,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 23548.78500000001,
"styleRecalcs": 180,
"styleRecalcDurationMs": 24.638000000000048,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 23058.184,
"heapDeltaBytes": -14783352,
"heapUsedBytes": 193641496,
"domNodes": -8316,
"jsHeapTotalBytes": -15179776,
"scriptDurationMs": 479.34299999999996,
"eventListeners": -16381,
"totalBlockingTimeMs": 358,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 24228.174999999967,
"styleRecalcs": 182,
"styleRecalcDurationMs": 28.577999999999992,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 23703.579999999998,
"heapDeltaBytes": -42965380,
"heapUsedBytes": 193021148,
"domNodes": -8312,
"jsHeapTotalBytes": -12558336,
"scriptDurationMs": 507.15600000000006,
"eventListeners": -16381,
"totalBlockingTimeMs": 253,
"frameDurationMs": 17.77666666666652,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 487.83800000001065,
"styleRecalcs": 15,
"styleRecalcDurationMs": 23.806,
"layouts": 4,
"layoutDurationMs": 1.7089999999999999,
"taskDurationMs": 123.77500000000002,
"heapDeltaBytes": 5135972,
"heapUsedBytes": 66233812,
"domNodes": 146,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 9.829,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 479.07999999995354,
"styleRecalcs": 12,
"styleRecalcDurationMs": 19.548,
"layouts": 3,
"layoutDurationMs": 0.637,
"taskDurationMs": 106.57900000000001,
"heapDeltaBytes": 4932668,
"heapUsedBytes": 66140436,
"domNodes": 121,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 7.488999999999999,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
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/workspace/api/workspaceApi.ts`:
- Line 181: Ensure unified cloud authentication completes token minting before
workspace discovery initializes. Update the login flow around mintAtLogin() to
await the mint before WorkspaceAuthGate initializes the workspace store, or make
getWorkspaceAuthHeaderOrThrow() wait for the in-flight mint before returning a
header; preserve the legacy Firebase path for Local/Desktop users.
In `@src/platform/workspace/stores/teamWorkspaceStore.ts`:
- Around line 418-428: Replace the direct workspace removal, active-workspace
assignment, and last-workspace persistence in the local fallback and switch
paths of the workspace store with serializable, deterministic, idempotent
command batches. Validate and execute those batches through the existing command
executor so entity updates and authentication/persistence effects are
replayable, undoable, and CRDT-transmittable, while preserving the current
personal-workspace fallback behavior.
🪄 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: b5972cb4-dd27-49a9-b156-d0d048e2dcc4
📒 Files selected for processing (39)
src/components/topbar/CurrentUserButton.test.tssrc/components/topbar/CurrentUserButton.vuesrc/components/topbar/CurrentUserPopoverLegacy.test.tssrc/components/topbar/CurrentUserPopoverLegacy.vuesrc/composables/billing/useBillingRouting.test.tssrc/composables/billing/useBillingRouting.tssrc/config/comfyApi.test.tssrc/config/comfyApi.tssrc/platform/cloud/subscription/components/PricingTable.test.tssrc/platform/cloud/subscription/composables/useSubscription.test.tssrc/platform/cloud/subscription/composables/useSubscription.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.tssrc/platform/workflow/persistence/base/storageKeys.test.tssrc/platform/workflow/persistence/base/storageKeys.tssrc/platform/workspace/api/workspaceApi.test.tssrc/platform/workspace/api/workspaceApi.tssrc/platform/workspace/api/workspaceApiUrl.test.tssrc/platform/workspace/api/workspaceApiUrl.tssrc/platform/workspace/auth/WorkspaceAuthGate.test.tssrc/platform/workspace/auth/WorkspaceAuthGate.vuesrc/platform/workspace/components/TopUpCreditsDialogContentWorkspace.test.tssrc/platform/workspace/components/TopUpCreditsDialogContentWorkspace.vuesrc/platform/workspace/components/WorkspaceSwitcherPopover.test.tssrc/platform/workspace/components/WorkspaceSwitcherPopover.vuesrc/platform/workspace/stores/billingOperationStore.test.tssrc/platform/workspace/stores/billingOperationStore.tssrc/platform/workspace/stores/teamWorkspaceStore.test.tssrc/platform/workspace/stores/teamWorkspaceStore.tssrc/platform/workspace/stores/useWorkspaceAuth.test.tssrc/platform/workspace/stores/workspaceAuthStore.tssrc/scripts/app.test.tssrc/scripts/app.tssrc/services/customerEventsService.test.tssrc/services/customerEventsService.tssrc/stores/__tests__/authTokenPriority.test.tssrc/stores/authStore.test.tssrc/stores/authStore.tssrc/vite-env.d.ts
💤 Files with no reviewable changes (1)
- src/composables/billing/useBillingRouting.ts
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
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/workspace/auth/WorkspaceAuthGate.vue`:
- Around line 103-107: The non-cloud initialization flow in initialize and
initializeWorkspacesInBackground must be cancellable and deduplicated per
authenticated user. Track the single in-flight operation with the existing
initializationGeneration or an abort signal, invalidate it on logout,
currentUser changes, and component unmount, and validate ownership before
initializeWorkspaceMode mutates state. Ensure the mount path and currentUser
watcher reuse the same operation, and add coverage for cancellation during
unmount/logout and auth hydration triggering both entry points.
In `@src/stores/__tests__/authTokenPriority.test.ts`:
- Around line 280-289: Update the “uses Firebase before workspace
initialization” test setup to set mockTeamWorkspaceInitState to “uninitialized”
before calling store.getWorkspaceAuthHeader() and store.getWorkspaceAuthToken().
Keep the existing Firebase-token assertions so the test exercises the
uninitialized workspace behavior rather than the inherited ready state.
🪄 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: d55f66f1-274a-44f2-81c0-82bcb03d520a
📒 Files selected for processing (39)
src/components/topbar/CurrentUserButton.test.tssrc/components/topbar/CurrentUserButton.vuesrc/components/topbar/CurrentUserPopoverLegacy.test.tssrc/components/topbar/CurrentUserPopoverLegacy.vuesrc/composables/billing/useBillingRouting.test.tssrc/composables/billing/useBillingRouting.tssrc/config/comfyApi.test.tssrc/config/comfyApi.tssrc/platform/cloud/subscription/components/PricingTable.test.tssrc/platform/cloud/subscription/composables/useSubscription.test.tssrc/platform/cloud/subscription/composables/useSubscription.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.tssrc/platform/workflow/persistence/base/storageKeys.test.tssrc/platform/workflow/persistence/base/storageKeys.tssrc/platform/workspace/api/workspaceApi.test.tssrc/platform/workspace/api/workspaceApi.tssrc/platform/workspace/api/workspaceApiUrl.test.tssrc/platform/workspace/api/workspaceApiUrl.tssrc/platform/workspace/auth/WorkspaceAuthGate.test.tssrc/platform/workspace/auth/WorkspaceAuthGate.vuesrc/platform/workspace/components/TopUpCreditsDialogContentWorkspace.test.tssrc/platform/workspace/components/TopUpCreditsDialogContentWorkspace.vuesrc/platform/workspace/components/WorkspaceSwitcherPopover.test.tssrc/platform/workspace/components/WorkspaceSwitcherPopover.vuesrc/platform/workspace/stores/billingOperationStore.test.tssrc/platform/workspace/stores/billingOperationStore.tssrc/platform/workspace/stores/teamWorkspaceStore.test.tssrc/platform/workspace/stores/teamWorkspaceStore.tssrc/platform/workspace/stores/useWorkspaceAuth.test.tssrc/platform/workspace/stores/workspaceAuthStore.tssrc/scripts/app.test.tssrc/scripts/app.tssrc/services/customerEventsService.test.tssrc/services/customerEventsService.tssrc/stores/__tests__/authTokenPriority.test.tssrc/stores/authStore.test.tssrc/stores/authStore.tssrc/vite-env.d.ts
💤 Files with no reviewable changes (1)
- src/composables/billing/useBillingRouting.ts
Amp-Thread-ID: https://ampcode.com/threads/T-019ff85b-da53-7107-8aaf-c383f7153d85 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019ff85b-da53-7107-8aaf-c383f7153d85 Co-authored-by: Amp <amp@ampcode.com>
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/workspace/stores/teamWorkspaceStore.test.ts`:
- Around line 314-330: Update the test around
useTeamWorkspaceStore().initialize() so it waits until mockWorkspaceApi.list has
been called and resolveList has been assigned before invoking the deferred
resolver. Then await both initialization promises and retain the existing
single-call and ready-state assertions.
In `@src/stores/__tests__/authTokenPriority.test.ts`:
- Around line 317-323: Update the test covering workspace initialization errors
to start with mockTeamWorkspaceInitState set to 'uninitialized' and configure
mockInitializeWorkspaces to reject. Assert getWorkspaceAuthToken() resolves to
undefined and neither mockUser.getIdToken nor mockEnsureWorkspaceToken is
called, ensuring the rejected initialize() path and its catch handling are
exercised.
🪄 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: 3ad84c9c-5800-4f89-a12c-c2fce50b32f2
📒 Files selected for processing (5)
src/platform/workspace/stores/teamWorkspaceStore.test.tssrc/platform/workspace/stores/teamWorkspaceStore.tssrc/scripts/app.test.tssrc/stores/__tests__/authTokenPriority.test.tssrc/stores/authStore.ts
christian-byrne
left a comment
There was a problem hiding this comment.
Unit tests are failing
Amp-Thread-ID: https://ampcode.com/threads/T-01a00cd9-76c3-713a-af9f-1fe65b282207 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a0111a-2288-7498-bb61-54e425704aa1 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-01a0111a-2288-7498-bb61-54e425704aa1 Co-authored-by: Amp <amp@ampcode.com>
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #15164 +/- ##
==========================================
+ Coverage 80.01% 81.08% +1.06%
==========================================
Files 2213 1887 -326
Lines 130727 123117 -7610
Branches 42189 37955 -4234
==========================================
- Hits 104606 99833 -4773
+ Misses 25476 22787 -2689
+ Partials 645 497 -148
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 777 files with indirect coverage changes 🚀 New features to boost your workflow:
|
AS IS → TO BE: local workspace switcherThese screenshots were captured from the actual running local ComfyUI app with the same authenticated user and workspace data on both branches. This PR is the functional baseline for local workspace switching. Alex's stacked PR, #15438, keeps this switching behavior unchanged and adds only the local-specific scope explanation from DES-787. AS IS — #15164 running app
TO BE — #15438 running app
Review split: review this PR for the switching implementation; review #15438 separately for the focused design/copy delta. |
## Summary Stacked on #15164. Adds the local-only scope framing to the workspace switcher menu, per [DES-787](https://linear.app/comfyorg/issue/DES-787/local-workspace-switcher-menu-credits-source-rows-one-workspace-state) / [FE-1584](https://linear.app/comfyorg/issue/FE-1584/add-workspace-switcher-to-localdesktop-credits-only-no-settings). Design: [Menu States (6245-41540)](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=6245-41540). ## Behavioral spec 1. On local, the workspace switcher picks whose credits you spend — nothing else (shipped by #15164; unchanged here). 2. Workspace rows are identical to cloud: name, role, check on the active row. 3. The scope explainer, off cloud only: the "Workspaces only affect which credits you use." caption, whose info icon carries the full tooltip ("Runs that use partner nodes spend credits from this workspace. Unlike on Cloud, every workspace saves to your usual output folder."), positioned left so the rows stay visible during hover. 4. No "manage workspaces" entry on local (already gated `isCloud` by #15164; unchanged here). ## Deferred: per-row credit balances Earlier commits on this branch built the designed per-row balances (read-only token mint + balance fetch per workspace, 60s cache) and then removed them — deferred post-V1 so local rows stay in parity with cloud's switcher. Deferring also drops this PR's interim fetch pattern and its two open dependencies: confirming the balance endpoint applies the member min-rule per identity, and the eventual balance field on the workspace list response. The implementation is intact in this branch's history for the follow-up. ## Verification - `pnpm test:unit` — popover suite 10 passed (scope framing off-cloud, rows/roles, cloud unaffected). - Cloud behavior unchanged: caption/tooltip are `!isCloud` only; no balance fetches on any distribution; create-workspace button untouched.
Amp-Thread-ID: https://ampcode.com/threads/T-01a01a57-6259-75bd-bb52-c149e0f7bb9c Co-authored-by: Amp <amp@ampcode.com>
|
@coderabbitai approve |
✅ Action performedComments resolved and changes approved. |
|
|
…witching FE-1584 (#15558) ## Summary Manual backport of #15164 to `core/1.51`, preserving the original local/Desktop credit-workspace switching behavior and regression coverage. ## Changes - **What**: Cherry-picks squash commit `848cd39ed06a2da6fea389305cf7210a0a18c5ee` onto the current `core/1.51` tip. - **Conflict resolution**: The commit applied cleanly; no semantic conflict edits were required. The resulting stable patch ID exactly matches the original commit (`d4910c5a0c7422e75a8e878248822032be6ab222`). ## Verification - `pnpm test:unit <24 changed workspace switcher/auth/billing/persistence test files>` - 24 test files passed - 756 tests passed - Push hook: `knip --cache` passed. ## Review Focus Confirm local credit-workspace switching, workspace-auth routing, billing attribution, and local workflow persistence remain equivalent to #15164 on the 1.51 release line. Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: comfydesigner <alextov@comfy.org>
…switching FE-1584 (#15557) ## Summary Manual backport of #15164 to `cloud/1.51`, preserving the original local/Desktop credit-workspace switching behavior and regression coverage. ## Changes - **What**: Cherry-picks squash commit `848cd39ed06a2da6fea389305cf7210a0a18c5ee` onto the current `cloud/1.51` tip. - **Conflict resolution**: The commit applied cleanly; no semantic conflict edits were required. The resulting stable patch ID exactly matches the original commit (`d4910c5a0c7422e75a8e878248822032be6ab222`). ## Verification - `pnpm test:unit <24 changed workspace switcher/auth/billing/persistence test files>` - 24 test files passed - 756 tests passed - Push hook: `knip --cache` passed. ## Review Focus Confirm local credit-workspace switching, workspace-auth routing, billing attribution, and local workflow persistence remain equivalent to #15164 on the 1.51 release line. Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: comfydesigner <alextov@comfy.org>
## Summary Fixes [FE-1739](https://linear.app/comfyorg/issue/FE-1739/): the workspace selector in the profile popover inherited native browser button styling after PR Comfy-Org#15164 changed it from a `<div>` to a semantic `<button>`. ## Changes - **What**: Reset the selector's native appearance with `appearance-none`, `border-0`, `bg-transparent`, and `text-left` while preserving its button semantics and existing interactions. - **Regression coverage**: Add a focused Playwright screenshot for the selector's neutral menu-row appearance. - **Backports**: Required for `core/1.51` and `cloud/1.51`; staging is currently on the affected `cloud/v1.51.9` (`ad9f651`). ## Review Focus Confirm the selector remains a full-width semantic button without Chromium's native gray fill or border. ## Testing - `pnpm test:unit src/platform/workspace/components/CurrentUserPopoverWorkspace.test.ts` (17/17 passed) - Focused Cloud Playwright screenshot test - `pnpm typecheck` - `pnpm typecheck:browser` - Lint, stylelint, oxlint, and oxfmt via the commit hook ## Screenshot  Co-authored-by: Amp <amp@ampcode.com>


Why
Local/Desktop users cannot select the workspace that owns their subscription or credits. The existing workspace and billing clients also resolve through
api.apiURL(), which points at the local ComfyUI server off Cloud, so simply exposing the Cloud switcher leaves workspace hydration dormant and returns 404s.Root cause
The Cloud-only UI guard hid the switcher, but the deeper boundary was also Cloud-only:
/billing/*used same-origin URLsChange
cloud.comfy.orgin production,testcloud.comfy.orgin non-production) while Cloud remains same-origin./customers/*calls from workspace-authenticated billing and partner-node execution.Implementation scope and rationale
/apitargets the local ComfyUI server, while workspace and billing state is owned by Cloud ingest.unified_cloud_authoutside the Cloud distribution.Backend dependency: cloud#6635 is required and merged. Its Local/Desktop CORS scope covers
POST /api/auth/token,GET /api/workspaces, andGET/POST /api/billing/*; Local keeps workspace creation hidden. The backend PR is merged intomain, and its production manifest contains the loopback origins. A live preflight currently passes ontestcloud.comfy.org, whilecloud.comfy.orgstill omitsAccess-Control-Allow-Originfor loopback origins. No additional backend code change is currently identified; the remaining release check is to confirm the merged configuration has rolled out to the production runtime and repeat the preflight. #15151 independently fixes the existing Settings → Credits activity spinner and billing-portal rail.Plan / QA
Regression audit added a focused Local Playwright flow. It caught and fixed two release blockers: Local rendered the legacy profile popover even after workspace hydration, and
unified_cloud_auth=trueincorrectly selected an unavailable Cloud JWT outside the Cloud distribution. The E2E verifies workspace/role rendering, workspace token exchange, Cloud-gateway billing routing, and that switching neither reloads the document nor loses the open workflow tab.A second auth/accounting audit found two queue-time races: a prompt could be submitted without a workspace token while Local workspace bootstrap was in flight, or fall back to the personal Firebase identity after bootstrap failed. Queue authentication now shares and awaits the in-flight workspace initialization and fails closed if no active workspace can be established. Identity-generation guards prevent a previous user's initialization from overwriting the next user's state.
Design of record: MVP before/after · popover and switcher
The implementation references those prototype states: the profile popover gains the workspace row, the selector opens to the left, and the selected workspace is highlighted with a check while roles remain visible. Local-specific adaptations are the compact single-line trigger and the retained Credits entry, because this scope adds wallet switching without exposing Cloud workspace management.
Captured from the actual Local frontend at 1920×1200 with deterministic auth/workspace/billing API fixtures; no Figma image is substituted for the implementation capture.
Validation
pnpm typecheckpnpm knipreports only existing repository-wide unused dependency/export findingsLinear: FE-1584