Skip to content

[backport cloud/1.51] feat(workspace): enable local credit workspace switching FE-1584 - #15557

Merged
comfy-pr-bot merged 1 commit into
cloud/1.51from
dante01yoon/backport-15164-to-cloud-1.51
Aug 21, 2026
Merged

[backport cloud/1.51] feat(workspace): enable local credit workspace switching FE-1584#15557
comfy-pr-bot merged 1 commit into
cloud/1.51from
dante01yoon/backport-15164-to-cloud-1.51

Conversation

@dante01yoon

Copy link
Copy Markdown
Collaborator

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.

)

## 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:

- workspace token exchange, workspace listing, and `/billing/*` used
same-origin URLs
- generic auth helpers mixed Firebase user identity with
workspace-scoped identity
- Cloud workspace switching reloads the app and changes
workflow-persistence namespaces, which is incorrect for Local/Desktop
where workspaces are credit wallets only

## Change

- Expose the existing workspace switcher in Local/Desktop after Firebase
auth hydrates; keep workspace creation Cloud-only.
- Route workspace auth, listing, and billing requests to the build's
Firebase-paired Cloud gateway (`cloud.comfy.org` in production,
`testcloud.comfy.org` in non-production) while Cloud remains
same-origin.
- Separate Firebase-authenticated `/customers/*` calls from
workspace-authenticated billing and partner-node execution.
- Switch the active local credit context without reloading or moving
local workflow persistence; persist the selected wallet per device.
- Keep Add credits permission-aware and return completed/pending top-ups
to Settings → Credits on Local/Desktop.

### Implementation scope and rationale

| Area | Change | Why it is required |
| --- | --- | --- |
| Workspace API routing | Resolve workspace and billing requests through
the Firebase-paired Cloud gateway outside the Cloud distribution. |
Local `/api` targets the local ComfyUI server, while workspace and
billing state is owned by Cloud ingest. |
| Authentication boundary | Separate Firebase user authentication from
workspace-scoped authentication, including partner-node queue execution.
| A Firebase token identifies the user; the workspace token
deterministically identifies the wallet that owns credits and receives
usage charges. |
| Workspace bootstrap | Share and await the in-flight Local workspace
initialization, and fail closed when no active workspace can be
established. | Prompt submission during bootstrap must not run without
credentials or silently fall back to the personal workspace. |
| Local switch lifecycle | Switch the active credit workspace without
reloading the document. | Cloud reload semantics are unnecessary for
Local and would interrupt the current local workflow. |
| Workflow persistence | Keep Local workflow storage in the personal
namespace when the credit workspace changes. | Local workspace selection
changes the billing wallet only; it must not move, clear, or split
locally stored workflows. |
| Billing routing | Use workspace billing after Local workspace
hydration. | Balances, plans, billing events, and top-ups must follow
the selected credit workspace rather than the personal account. |
| Billing permissions | Keep Credits visible while hiding Add credits
for members who cannot top up. | Backend top-up operations are
owner-only, so the UI must not offer an action the selected workspace
role cannot perform. |
| Top-up completion | Return completed or pending Local top-ups to
Settings → Credits. | Local does not expose the Cloud
workspace-management destination used by the existing Cloud flow. |
| Cloud customer endpoints | Explicitly retain Firebase authentication
for user-scoped customer and subscription calls. | Changing generic auth
precedence must not send a workspace token to endpoints whose contract
is user-scoped. |
| Feature-flag isolation | Disable `unified_cloud_auth` outside the
Cloud distribution. | Unified Cloud JWT minting is not available in
Local/Desktop; accepting that flag there would break workspace discovery
and execution authentication. |
| Regression coverage | Add focused unit tests and a Local Playwright
workspace-switching flow. | The tests protect workspace attribution,
Cloud-gateway routing, no-reload switching, workflow retention,
permissions, and bootstrap race handling. |

Backend dependency:
[cloud#6635](Comfy-Org/cloud#6635) is required
and merged. Its Local/Desktop CORS scope covers `POST /api/auth/token`,
`GET /api/workspaces`, and `GET`/`POST /api/billing/*`; Local keeps
workspace creation hidden. The backend PR is merged into `main`, and its
production manifest contains the loopback origins. A live preflight
currently passes on `testcloud.comfy.org`, while `cloud.comfy.org` still
omits `Access-Control-Allow-Origin` for 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](#15151)
independently fixes the existing Settings → Credits activity spinner and
billing-portal rail.

## Plan / QA

1. Review the auth and persistence boundaries in this PR.
2. Land #15151 so activity and Invoice History follow the selected
billing rail.
3. Confirm the merged cloud#6635 configuration has rolled out to the
production runtime and repeat the live preflight.
4. Verify on a Desktop build against the deployed gateway:
switch/relaunch, popover and Settings balances, top-up attribution,
partner-node debit and allowlist, member permissions, and zero-credit
local runs.

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=true`
incorrectly 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](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=5874-73181)
· [popover and
switcher](https://www.figma.com/design/CkFTD4c20PyRGpNVAJgpfV/Team-Plan---Workspaces?node-id=5874-72771)

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.

| AS IS — Local profile menu | TO BE — Local workspace selector |
| --- | --- |
| ![AS IS Local profile
menu](https://ampcode.com/user-content/artifacts/5112da3b9ef62052997070dfef05a45f2febbb34c6c6781c6aebbec951cffc93-file.png)
| ![TO BE Local workspace
selector](https://ampcode.com/user-content/artifacts/74da3cdb517dd10ba82e5abbbe4b5d100c76107defdb0d8b4ac0950a63299fbe-file.png)
|

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

- Focused Local workspace Playwright E2E: 1/1 passed on Chromium before
the final auth-race patch; final local rerun was blocked during fixture
setup because the 8188 ComfyUI test backend was unavailable, before any
product assertion ran
- Final focused workspace/auth/queue unit suites: 316/316 passed when
run in their stable focused groups
- Earlier affected unit/component suites: 650/650 passed before rebase;
594/594 passed after rebase
- `pnpm typecheck`
- targeted ESLint and oxfmt checks
- `pnpm knip` reports only existing repository-wide unused
dependency/export findings
- Real-app screenshot Playwright run: 2/2 passed (AS IS without a
workspace roster; TO BE with the selector open)
- Live CORS preflight: staging passes; production runtime rollout/sync
verification remains the release blocker

Linear:
[FE-1584](https://linear.app/comfyorg/issue/FE-1584/add-workspace-switcher-to-localdesktop-credits-only-no-settings)

---------

Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: comfydesigner <alextov@comfy.org>
@dante01yoon dante01yoon added the backport Backporting a PR onto a release candidate label Aug 21, 2026
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 21, 2026
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Ignore keyword(s) in the title.

⛔ Ignored keywords (2)
  • [release]
  • [backport

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 30722bd9-41fd-4770-b079-0118209da35d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

🎭 Playwright: ✅ 1829 passed, 0 failed

📊 Browser Reports
  • chromium: View Report (✅ 1808 / ❌ 0 / ⚠️ 0 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 18 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • New-test walkthrough (chromium, recorded video): View Report

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 08/21/2026, 01:37:55 PM UTC

Links

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.0 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 44.6 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 69.9 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.7 MB heap
large-graph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 69.7 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 65.6 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.1 MB heap
minimap-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.0 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 52.6 MB heap
subgraph-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.7 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 59.0 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 143ms TBT · 76.7 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 67.3 MB heap
vue-large-graph-idle: · 56.3 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 161.6 MB heap
vue-large-graph-pan: · 56.3 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 132ms TBT · 175.6 MB heap
workflow-execution: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.4 MB heap

ℹ️ No baseline found — significance unavailable.

Absolute values
Metric Value
canvas-idle: avg frame time 17ms
canvas-idle: p95 frame time 17ms
canvas-idle: layout duration 0ms
canvas-idle: style recalc duration 9ms
canvas-idle: layout count 0
canvas-idle: style recalc count 10
canvas-idle: task duration 494ms
canvas-idle: script duration 7ms
canvas-idle: TBT 0ms
canvas-idle: heap used 67.0 MB
canvas-idle: DOM nodes 19
canvas-idle: event listeners 5
canvas-mouse-sweep: avg frame time 17ms
canvas-mouse-sweep: p95 frame time 17ms
canvas-mouse-sweep: layout duration 3ms
canvas-mouse-sweep: style recalc duration 36ms
canvas-mouse-sweep: layout count 12
canvas-mouse-sweep: style recalc count 76
canvas-mouse-sweep: task duration 883ms
canvas-mouse-sweep: script duration 113ms
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: heap used 44.6 MB
canvas-mouse-sweep: DOM nodes 0
canvas-mouse-sweep: event listeners -153
canvas-zoom-sweep: avg frame time 17ms
canvas-zoom-sweep: p95 frame time 17ms
canvas-zoom-sweep: layout duration 1ms
canvas-zoom-sweep: style recalc duration 18ms
canvas-zoom-sweep: layout count 6
canvas-zoom-sweep: style recalc count 31
canvas-zoom-sweep: task duration 375ms
canvas-zoom-sweep: script duration 9ms
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: heap used 69.9 MB
canvas-zoom-sweep: DOM nodes 78
canvas-zoom-sweep: event listeners 19
dom-widget-clipping: avg frame time 17ms
dom-widget-clipping: p95 frame time 17ms
dom-widget-clipping: layout duration 0ms
dom-widget-clipping: style recalc duration 8ms
dom-widget-clipping: layout count 0
dom-widget-clipping: style recalc count 11
dom-widget-clipping: task duration 378ms
dom-widget-clipping: script duration 60ms
dom-widget-clipping: TBT 0ms
dom-widget-clipping: heap used 51.7 MB
dom-widget-clipping: DOM nodes 17
dom-widget-clipping: event listeners 0
large-graph-idle: avg frame time 17ms
large-graph-idle: p95 frame time 17ms
large-graph-idle: layout duration 0ms
large-graph-idle: style recalc duration 8ms
large-graph-idle: layout count 0
large-graph-idle: style recalc count 9
large-graph-idle: task duration 602ms
large-graph-idle: script duration 15ms
large-graph-idle: TBT 0ms
large-graph-idle: heap used 69.7 MB
large-graph-idle: DOM nodes -282
large-graph-idle: event listeners -149
large-graph-pan: avg frame time 17ms
large-graph-pan: p95 frame time 17ms
large-graph-pan: layout duration 0ms
large-graph-pan: style recalc duration 15ms
large-graph-pan: layout count 0
large-graph-pan: style recalc count 69
large-graph-pan: task duration 1207ms
large-graph-pan: script duration 334ms
large-graph-pan: TBT 0ms
large-graph-pan: heap used 65.6 MB
large-graph-pan: DOM nodes -283
large-graph-pan: event listeners -149
large-graph-zoom: avg frame time 17ms
large-graph-zoom: p95 frame time 17ms
large-graph-zoom: layout duration 7ms
large-graph-zoom: style recalc duration 15ms
large-graph-zoom: layout count 60
large-graph-zoom: style recalc count 66
large-graph-zoom: task duration 1364ms
large-graph-zoom: script duration 383ms
large-graph-zoom: TBT 0ms
large-graph-zoom: heap used 67.1 MB
large-graph-zoom: DOM nodes -135
large-graph-zoom: event listeners -73
minimap-idle: avg frame time 17ms
minimap-idle: p95 frame time 17ms
minimap-idle: layout duration 0ms
minimap-idle: style recalc duration 7ms
minimap-idle: layout count 0
minimap-idle: style recalc count 8
minimap-idle: task duration 602ms
minimap-idle: script duration 16ms
minimap-idle: TBT 0ms
minimap-idle: heap used 68.0 MB
minimap-idle: DOM nodes -283
minimap-idle: event listeners -165
subgraph-dom-widget-clipping: avg frame time 17ms
subgraph-dom-widget-clipping: p95 frame time 17ms
subgraph-dom-widget-clipping: layout duration 0ms
subgraph-dom-widget-clipping: style recalc duration 11ms
subgraph-dom-widget-clipping: layout count 0
subgraph-dom-widget-clipping: style recalc count 47
subgraph-dom-widget-clipping: task duration 409ms
subgraph-dom-widget-clipping: script duration 119ms
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: heap used 52.6 MB
subgraph-dom-widget-clipping: DOM nodes 19
subgraph-dom-widget-clipping: event listeners 6
subgraph-idle: avg frame time 17ms
subgraph-idle: p95 frame time 17ms
subgraph-idle: layout duration 0ms
subgraph-idle: style recalc duration 10ms
subgraph-idle: layout count 0
subgraph-idle: style recalc count 11
subgraph-idle: task duration 453ms
subgraph-idle: script duration 6ms
subgraph-idle: TBT 0ms
subgraph-idle: heap used 67.7 MB
subgraph-idle: DOM nodes 21
subgraph-idle: event listeners 4
subgraph-mouse-sweep: avg frame time 17ms
subgraph-mouse-sweep: p95 frame time 17ms
subgraph-mouse-sweep: layout duration 4ms
subgraph-mouse-sweep: style recalc duration 37ms
subgraph-mouse-sweep: layout count 16
subgraph-mouse-sweep: style recalc count 76
subgraph-mouse-sweep: task duration 765ms
subgraph-mouse-sweep: script duration 85ms
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: heap used 59.0 MB
subgraph-mouse-sweep: DOM nodes 63
subgraph-mouse-sweep: event listeners 4
subgraph-transition-enter: avg frame time 17ms
subgraph-transition-enter: p95 frame time 17ms
subgraph-transition-enter: layout duration 13ms
subgraph-transition-enter: style recalc duration 31ms
subgraph-transition-enter: layout count 15
subgraph-transition-enter: style recalc count 20
subgraph-transition-enter: task duration 888ms
subgraph-transition-enter: script duration 17ms
subgraph-transition-enter: TBT 143ms
subgraph-transition-enter: heap used 76.7 MB
subgraph-transition-enter: DOM nodes 13673
subgraph-transition-enter: event listeners 2375
viewport-pan-sweep: avg frame time 17ms
viewport-pan-sweep: p95 frame time 17ms
viewport-pan-sweep: layout duration 0ms
viewport-pan-sweep: style recalc duration 35ms
viewport-pan-sweep: layout count 0
viewport-pan-sweep: style recalc count 248
viewport-pan-sweep: task duration 4222ms
viewport-pan-sweep: script duration 1032ms
viewport-pan-sweep: TBT 0ms
viewport-pan-sweep: heap used 67.3 MB
viewport-pan-sweep: DOM nodes -284
viewport-pan-sweep: event listeners -133
vue-large-graph-idle: avg frame time 18ms
vue-large-graph-idle: p95 frame time 17ms
vue-large-graph-idle: layout duration 0ms
vue-large-graph-idle: style recalc duration 0ms
vue-large-graph-idle: layout count 0
vue-large-graph-idle: style recalc count 0
vue-large-graph-idle: task duration 17127ms
vue-large-graph-idle: script duration 123ms
vue-large-graph-idle: TBT 0ms
vue-large-graph-idle: heap used 161.6 MB
vue-large-graph-idle: DOM nodes -8312
vue-large-graph-idle: event listeners -16388
vue-large-graph-pan: avg frame time 18ms
vue-large-graph-pan: p95 frame time 17ms
vue-large-graph-pan: layout duration 0ms
vue-large-graph-pan: style recalc duration 19ms
vue-large-graph-pan: layout count 0
vue-large-graph-pan: style recalc count 177
vue-large-graph-pan: task duration 21176ms
vue-large-graph-pan: script duration 411ms
vue-large-graph-pan: TBT 132ms
vue-large-graph-pan: heap used 175.6 MB
vue-large-graph-pan: DOM nodes -8312
vue-large-graph-pan: event listeners -16383
workflow-execution: avg frame time 17ms
workflow-execution: p95 frame time 17ms
workflow-execution: layout duration 2ms
workflow-execution: style recalc duration 19ms
workflow-execution: layout count 3
workflow-execution: style recalc count 8
workflow-execution: task duration 93ms
workflow-execution: script duration 7ms
workflow-execution: TBT 0ms
workflow-execution: heap used 64.4 MB
workflow-execution: DOM nodes 134
workflow-execution: event listeners 37
Raw data
{
  "timestamp": "2026-08-21T13:48:32.296Z",
  "gitSha": "37bf58d7f3698456995586987adc274e69be99b2",
  "branch": "dante01yoon/backport-15164-to-cloud-1.51",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2008.4909999999923,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.242999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 517.217,
      "heapDeltaBytes": 4994352,
      "heapUsedBytes": 70223656,
      "domNodes": 18,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 7.387000000000001,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-idle",
      "durationMs": 2015.4589999999644,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.449,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 470.63500000000005,
      "heapDeltaBytes": 4954352,
      "heapUsedBytes": 70322168,
      "domNodes": 20,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 6.357999999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1878.957000000014,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 37.049,
      "layouts": 12,
      "layoutDurationMs": 3.1650000000000005,
      "taskDurationMs": 882.1239999999999,
      "heapDeltaBytes": -18618116,
      "heapUsedBytes": 46831652,
      "domNodes": 0,
      "jsHeapTotalBytes": 23171072,
      "scriptDurationMs": 114.11200000000001,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1873.2129999999643,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 35.516000000000005,
      "layouts": 12,
      "layoutDurationMs": 3.0939999999999994,
      "taskDurationMs": 884.6429999999999,
      "heapDeltaBytes": -18659992,
      "heapUsedBytes": 46805332,
      "domNodes": 0,
      "jsHeapTotalBytes": 23171072,
      "scriptDurationMs": 111.486,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1746.1759999999913,
      "styleRecalcs": 30,
      "styleRecalcDurationMs": 18.401,
      "layouts": 6,
      "layoutDurationMs": 0.6899999999999998,
      "taskDurationMs": 370.95900000000006,
      "heapDeltaBytes": 7988476,
      "heapUsedBytes": 73426216,
      "domNodes": 77,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 9.458000000000002,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1736.8579999999838,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 18.11,
      "layouts": 6,
      "layoutDurationMs": 0.607,
      "taskDurationMs": 378.18,
      "heapDeltaBytes": 7918316,
      "heapUsedBytes": 73153288,
      "domNodes": 78,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 9.181000000000001,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 577.0400000000109,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 7.667999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 379.70899999999995,
      "heapDeltaBytes": -11334776,
      "heapUsedBytes": 54074728,
      "domNodes": 16,
      "jsHeapTotalBytes": 25165824,
      "scriptDurationMs": 61.898,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 576.5380000000278,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 7.377999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 376.049,
      "heapDeltaBytes": -10962044,
      "heapUsedBytes": 54364216,
      "domNodes": 18,
      "jsHeapTotalBytes": 24379392,
      "scriptDurationMs": 57.364000000000004,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2027.6119999999764,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.005999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 590.446,
      "heapDeltaBytes": 12919908,
      "heapUsedBytes": 73177800,
      "domNodes": -282,
      "jsHeapTotalBytes": 3768320,
      "scriptDurationMs": 16.052999999999997,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2037.2280000000274,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.166,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 613.2850000000001,
      "heapDeltaBytes": 12778468,
      "heapUsedBytes": 72911940,
      "domNodes": -282,
      "jsHeapTotalBytes": 2981888,
      "scriptDurationMs": 13.846,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2132.1569999999497,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 14.543000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1175.873,
      "heapDeltaBytes": 7465672,
      "heapUsedBytes": 68484488,
      "domNodes": -283,
      "jsHeapTotalBytes": 3436544,
      "scriptDurationMs": 327.895,
      "eventListeners": -147,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2212.24200000006,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 14.882000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1238.161,
      "heapDeltaBytes": 8253696,
      "heapUsedBytes": 69068772,
      "domNodes": -283,
      "jsHeapTotalBytes": 3960832,
      "scriptDurationMs": 340.318,
      "eventListeners": -151,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3163.5120000000256,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 15.601,
      "layouts": 60,
      "layoutDurationMs": 7.204000000000001,
      "taskDurationMs": 1340.7,
      "heapDeltaBytes": 17702092,
      "heapUsedBytes": 79752696,
      "domNodes": 14,
      "jsHeapTotalBytes": 5505024,
      "scriptDurationMs": 390.80099999999993,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3232.3900000000094,
      "styleRecalcs": 65,
      "styleRecalcDurationMs": 14.050000000000004,
      "layouts": 60,
      "layoutDurationMs": 7.109000000000001,
      "taskDurationMs": 1387.5549999999998,
      "heapDeltaBytes": -1078652,
      "heapUsedBytes": 60979604,
      "domNodes": -284,
      "jsHeapTotalBytes": 3506176,
      "scriptDurationMs": 375.912,
      "eventListeners": -153,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "minimap-idle",
      "durationMs": 2041.8070000000057,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 6.551999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 596.4779999999998,
      "heapDeltaBytes": 11813216,
      "heapUsedBytes": 73277296,
      "domNodes": -283,
      "jsHeapTotalBytes": 2981888,
      "scriptDurationMs": 15.537000000000003,
      "eventListeners": -149,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2011.7779999999357,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.353999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 608.3259999999999,
      "heapDeltaBytes": 8070192,
      "heapUsedBytes": 69364356,
      "domNodes": -283,
      "jsHeapTotalBytes": 3506176,
      "scriptDurationMs": 15.893999999999998,
      "eventListeners": -181,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 589.0640000000076,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 10.517999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 409.74100000000004,
      "heapDeltaBytes": -10081120,
      "heapUsedBytes": 55615196,
      "domNodes": 18,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 120.497,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 583.6140000000114,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 10.776000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 407.79600000000005,
      "heapDeltaBytes": -10754248,
      "heapUsedBytes": 54762116,
      "domNodes": 20,
      "jsHeapTotalBytes": 25952256,
      "scriptDurationMs": 117.338,
      "eventListeners": 6,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1990.2839999999742,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 9.968,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 449.49899999999997,
      "heapDeltaBytes": 5373488,
      "heapUsedBytes": 70994304,
      "domNodes": 20,
      "jsHeapTotalBytes": 23855104,
      "scriptDurationMs": 6.395,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2005.3869999999279,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 10.087,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 456.99,
      "heapDeltaBytes": 5358816,
      "heapUsedBytes": 70944028,
      "domNodes": 22,
      "jsHeapTotalBytes": 24117248,
      "scriptDurationMs": 6.0820000000000025,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1701.3279999999895,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 36.455,
      "layouts": 16,
      "layoutDurationMs": 3.975,
      "taskDurationMs": 745.3789999999999,
      "heapDeltaBytes": -3625796,
      "heapUsedBytes": 61813460,
      "domNodes": 63,
      "jsHeapTotalBytes": 24641536,
      "scriptDurationMs": 84.33399999999999,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1691.500000000019,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 36.979,
      "layouts": 16,
      "layoutDurationMs": 4.36,
      "taskDurationMs": 784.8230000000001,
      "heapDeltaBytes": -3708084,
      "heapUsedBytes": 61832744,
      "domNodes": 62,
      "jsHeapTotalBytes": 24903680,
      "scriptDurationMs": 85.536,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1400.1729999999952,
      "styleRecalcs": 20,
      "styleRecalcDurationMs": 31.471,
      "layouts": 15,
      "layoutDurationMs": 13.089999999999998,
      "taskDurationMs": 888.206,
      "heapDeltaBytes": -3977128,
      "heapUsedBytes": 80448056,
      "domNodes": 13673,
      "jsHeapTotalBytes": 9961472,
      "scriptDurationMs": 16.679000000000006,
      "eventListeners": 2375,
      "totalBlockingTimeMs": 143,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8421.581000000004,
      "styleRecalcs": 248,
      "styleRecalcDurationMs": 34.49,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4159.349,
      "heapDeltaBytes": 13264968,
      "heapUsedBytes": 73957472,
      "domNodes": -285,
      "jsHeapTotalBytes": 3174400,
      "scriptDurationMs": 1018.7610000000001,
      "eventListeners": -131,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8352.54400000008,
      "styleRecalcs": 248,
      "styleRecalcDurationMs": 36.04,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4284.568,
      "heapDeltaBytes": 6777096,
      "heapUsedBytes": 67084588,
      "domNodes": -282,
      "jsHeapTotalBytes": 4222976,
      "scriptDurationMs": 1045.318,
      "eventListeners": -135,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 17459.518000000004,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16678.708,
      "heapDeltaBytes": -25985520,
      "heapUsedBytes": 171370760,
      "domNodes": -8312,
      "jsHeapTotalBytes": -6758400,
      "scriptDurationMs": 124.241,
      "eventListeners": -16391,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 18570.369000000028,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 17574.451,
      "heapDeltaBytes": -29691344,
      "heapUsedBytes": 167589116,
      "domNodes": -8312,
      "jsHeapTotalBytes": -13049856,
      "scriptDurationMs": 121.61400000000002,
      "eventListeners": -16385,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 20986.399000000005,
      "styleRecalcs": 172,
      "styleRecalcDurationMs": 17.94300000000004,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20432.266,
      "heapDeltaBytes": -27626240,
      "heapUsedBytes": 185288028,
      "domNodes": -8312,
      "jsHeapTotalBytes": -14426112,
      "scriptDurationMs": 410.971,
      "eventListeners": -16383,
      "totalBlockingTimeMs": 35,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 22707.875999999942,
      "styleRecalcs": 182,
      "styleRecalcDurationMs": 19.11999999999997,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 21919.752000000004,
      "heapDeltaBytes": -14357156,
      "heapUsedBytes": 183056232,
      "domNodes": -8312,
      "jsHeapTotalBytes": -13119488,
      "scriptDurationMs": 411.32099999999997,
      "eventListeners": -16383,
      "totalBlockingTimeMs": 228,
      "frameDurationMs": 17.776666666666642,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "workflow-execution",
      "durationMs": 157.8810000000317,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 23.153,
      "layouts": 3,
      "layoutDurationMs": 3.6169999999999995,
      "taskDurationMs": 110.78399999999999,
      "heapDeltaBytes": 3180932,
      "heapUsedBytes": 67682812,
      "domNodes": 139,
      "jsHeapTotalBytes": 2883584,
      "scriptDurationMs": 10.105,
      "eventListeners": 49,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "workflow-execution",
      "durationMs": 114.94500000003427,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 15.315999999999999,
      "layouts": 3,
      "layoutDurationMs": 1.1239999999999999,
      "taskDurationMs": 74.25099999999999,
      "heapDeltaBytes": 2941748,
      "heapUsedBytes": 67405384,
      "domNodes": 128,
      "jsHeapTotalBytes": 2883584,
      "scriptDurationMs": 4.559999999999998,
      "eventListeners": 25,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    }
  ]
}

@github-actions github-actions Bot added the risk:R3 PR risk grade (advisory shadow check; grader-owned) label Aug 21, 2026
@github-actions
github-actions Bot requested a review from AustinMroz August 21, 2026 13:36
@codecov

codecov Bot commented Aug 21, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.81633% with 18 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/stores/authStore.ts 83.33% 8 Missing ⚠️
src/platform/workspace/auth/WorkspaceAuthGate.vue 89.47% 4 Missing ⚠️
...rc/platform/workspace/stores/teamWorkspaceStore.ts 91.89% 3 Missing ⚠️
...rc/platform/workspace/stores/workspaceAuthStore.ts 85.71% 2 Missing ⚠️
src/config/comfyApi.ts 85.71% 1 Missing ⚠️
@@              Coverage Diff              @@
##             cloud/1.51   #15557   +/-   ##
=============================================
  Coverage              ?   81.53%           
=============================================
  Files                 ?     1884           
  Lines                 ?   117713           
  Branches              ?    37248           
=============================================
  Hits                  ?    95973           
  Misses                ?    21270           
  Partials              ?      470           
Flag Coverage Δ
unit 73.11% <90.81%> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/components/topbar/CurrentUserButton.vue 90.24% <100.00%> (ø)
src/components/topbar/CurrentUserPopoverLegacy.vue 61.59% <100.00%> (ø)
src/composables/billing/useBillingRouting.ts 95.23% <ø> (ø)
src/composables/useFeatureFlags.ts 80.74% <100.00%> (ø)
.../cloud/subscription/composables/useSubscription.ts 86.94% <100.00%> (ø)
...oud/subscription/utils/subscriptionCheckoutUtil.ts 91.66% <100.00%> (ø)
.../platform/workflow/persistence/base/storageKeys.ts 100.00% <100.00%> (ø)
src/platform/workspace/api/workspaceApi.ts 47.30% <100.00%> (ø)
src/platform/workspace/api/workspaceApiUrl.ts 100.00% <100.00%> (ø)
...rkspace/components/CurrentUserPopoverWorkspace.vue 81.20% <100.00%> (ø)
... and 10 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@comfy-pr-bot
comfy-pr-bot merged commit ad9f651 into cloud/1.51 Aug 21, 2026
89 of 91 checks passed
@comfy-pr-bot
comfy-pr-bot deleted the dante01yoon/backport-15164-to-cloud-1.51 branch August 21, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport Backporting a PR onto a release candidate risk:R3 PR risk grade (advisory shadow check; grader-owned) size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants