Skip to content

[backport cloud/1.47] fix(workspace): gate billing controls by role#13834

Merged
comfy-pr-bot merged 3 commits into
cloud/1.47from
backport-13812-to-cloud-1.47
Jul 19, 2026
Merged

[backport cloud/1.47] fix(workspace): gate billing controls by role#13834
comfy-pr-bot merged 3 commits into
cloud/1.47from
backport-13812-to-cloud-1.47

Conversation

@dante01yoon

@dante01yoon dante01yoon commented Jul 19, 2026

Copy link
Copy Markdown
Collaborator

Summary

Backports #13812 to cloud/1.47 after the automatic cherry-pick conflicted. This branch is stacked on the current #13814 head.

Changes

Review Focus

Validation

  • Focused unit suite: 18 files, 397 tests passed.
  • Main and browser typechecks passed; focused formatting passed.
  • Cloud memberRoleChange.spec.ts: 7 passed.

Screenshots

Personal/default workspace creator Additional workspace creator
Personal workspace creator menu with billing actions but no Leave or Delete action Additional workspace creator menu with Leave and subscription-locked Delete actions
Another owner can change the additional workspace creator role or remove the creator

## Summary

Make Members eligibility follow the active billing plan instead of the
personal/team workspace type. A personal workspace on a Team plan now
gets the same Members experience as a team workspace on the same plan.

## Root cause

The Members surface had three independent workspace-type gates:

- `useTeamPlan` treated “team workspace + subscribed” as the Team-plan
signal, so a personal workspace was always classified as not being on
Team even when billing reported a Team plan.
- `useMembersPanel` inherited the personal-workspace permission and
layout defaults, which forced the single-user row, disabled Invite, and
rendered the Team upgrade banner.
- Member and pending-invite loading short-circuited for personal
workspaces, so valid Team-plan members could not be fetched even if the
UI gate was corrected.

## How this fixes it

- Derive `hasTeamPlan`, active Team-plan, cancelled/lapsed, and loading
states from billing plan identity plus subscription state.
- Overlay Members permissions and table layout from the plan and
workspace role. Team-plan owners can invite and manage members
regardless of workspace type; personal plans keep the single-user
experience.
- Wait for billing initialization before deciding whether to fetch
members/invites or show an upgrade state.
- Remove the store-level personal-workspace short-circuits for member
and invite APIs.
- Keep workspace-container actions such as delete/leave governed by
workspace type; only the Members entitlement moves to plan-based logic.

## Before / After

### Before

A personal workspace with a Team plan was treated as a personal plan:
Invite was disabled and the UI asked the user to upgrade to Team.

<img width="1400" alt="Before: Personal Workspace on Team plan
incorrectly shows disabled Invite and Upgrade to Team"
src="https://github.com/user-attachments/assets/f0d7a99c-6d7b-4cac-b3c0-9d3c9266830f"
/>

### After

The same personal-workspace + Team-plan scenario loads the Team member
list and enables owner member-management actions.

<img width="1400" alt="After: Personal Workspace on Team plan shows
member list, Invite, roles, and member actions"
src="https://github.com/user-attachments/assets/7e2fbcd7-d8e3-4ae8-a9df-ad59974ecf52"
/>

## Review Focus

- Team-plan owners receive member management regardless of workspace
type.
- Personal plans remain single-user.
- Loading, cancelled, and lapsed states do not briefly expose an
incorrect upgrade or invite state.
- Workspace delete/leave behavior remains workspace-type based.

## Validation

- 168 focused Vitest tests passed.
- `pnpm typecheck` passed.
- `pnpm lint` passed.
- `pnpm knip` passed.
- Cloud Playwright: 7/7 tests passed in `memberRoleChange.spec.ts`,
including personal workspace + Team plan regression coverage.
- Pre-commit style, format, lint, and type checks passed.
Gate workspace billing mutations by the active role and scope
creator-only protections to the Personal/default workspace, matching the
backend policy.

| Action | Personal/default workspace | Additional workspace |
| --- | --- | --- |
| Billing lifecycle (subscribe, change plan, cancel, reactivate, top up)
| Any owner | Any owner |
| Team to Personal downgrade | Original creator only | Original creator
only |
| Leave | Eligible collaborators; original creator cannot leave | Any
member or owner, including the creator |
| Demote or remove the creator | Not allowed | Allowed for another
owner, subject to backend last-owner validation |
| Delete | Not available | Any owner after the active-subscription lock
clears |

Members remain read-only for billing mutations. Members access continues
to follow the active Team plan rather than workspace type.

- Protect the Personal/default workspace creator from Leave, demotion,
and removal while allowing entitled collaborators to leave.
- Treat an additional workspace creator as an ordinary owner for Leave
and member management.
- Allow any owner to manage the billing lifecycle; keep Team-to-Personal
downgrade original-creator-only.
- Recheck Leave and Delete permissions when captured menu commands
execute.
- Hide Delete for Personal workspaces and members, and keep it disabled
while an additional workspace has an active subscription.
- Incorporate the workspace-type policy from #13831 without replacing
this PR's billing lifecycle guards. #13810 is merged.

- Personal creator protection versus additional workspace creator
behavior.
- Any-owner billing lifecycle access versus original-creator-only
downgrade.
- Stale menu-command permission and subscription-lock checks.

- 201 focused unit tests passed.
- 7 cloud Playwright tests passed.
- `pnpm typecheck`, `pnpm lint`, `pnpm knip`, and `pnpm format:check`
passed. Lint reports only existing repository warnings.

Captured from the Vite cloud app with mocked workspace API state.

| Personal/default workspace creator | Additional workspace creator |
| --- | --- |
| <img width="720" alt="Personal workspace creator menu with billing
actions but no Leave or Delete action"
src="https://github.com/user-attachments/assets/1fa2e62f-9b93-4d8c-b9bb-d02a2a671531"
/> | <img width="720" alt="Additional workspace creator menu with Leave
and subscription-locked Delete actions"
src="https://github.com/user-attachments/assets/3cb7255d-2e41-44d4-a7f8-feddcb90644d"
/> |

The Personal/default creator has no Leave or Delete action. An
additional workspace creator can leave; Delete remains disabled until
the active-subscription lock clears.

<img width="720" alt="Another owner can change the additional workspace
creator role or remove the creator"
src="https://github.com/user-attachments/assets/8fb7b5a5-63b4-4124-a99a-85121f8fa374"
/>

Another owner can change or remove the creator, subject to backend
last-owner validation.

(cherry picked from commit 51884bc)
@dante01yoon dante01yoon added the backport Backporting a PR onto a release candidate label Jul 19, 2026
@coderabbitai

coderabbitai Bot commented Jul 19, 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: 0a77b014-4ab1-47e5-8b5b-1c99a612fec3

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch backport-13812-to-cloud-1.47

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 Jul 19, 2026

Copy link
Copy Markdown

🎭 Playwright: ✅ 1690 passed, 0 failed · 5 flaky

📊 Browser Reports
  • chromium: View Report (✅ 1671 / ❌ 0 / ⚠️ 4 / ⏭️ 5)
  • chromium-2x: View Report (✅ 2 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • chromium-0.5x: View Report (✅ 1 / ❌ 0 / ⚠️ 0 / ⏭️ 0)
  • mobile-chrome: View Report (✅ 16 / ❌ 0 / ⚠️ 1 / ⏭️ 0)

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 07/19/2026, 10:07:23 PM UTC

Links

📦 Bundle Size

⏳ Size data collection in progress…

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 51.6 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.6 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.8 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 54.4 MB heap
large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.9 MB heap
large-graph-pan: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 77.0 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.8 MB heap
minimap-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.4 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 57.0 MB heap
subgraph-idle: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 52.4 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.3 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 163ms TBT · 97.8 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 70.8 MB heap
vue-large-graph-idle: · 57.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 173.8 MB heap
vue-large-graph-pan: · 58.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 5ms TBT · 160.9 MB heap
workflow-execution: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 45.0 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 397ms
canvas-idle: script duration 15ms
canvas-idle: TBT 0ms
canvas-idle: heap used 51.6 MB
canvas-idle: DOM nodes -277
canvas-idle: event listeners -199
canvas-mouse-sweep: avg frame time 17ms
canvas-mouse-sweep: p95 frame time 17ms
canvas-mouse-sweep: layout duration 4ms
canvas-mouse-sweep: style recalc duration 38ms
canvas-mouse-sweep: layout count 12
canvas-mouse-sweep: style recalc count 73
canvas-mouse-sweep: task duration 831ms
canvas-mouse-sweep: script duration 121ms
canvas-mouse-sweep: TBT 0ms
canvas-mouse-sweep: heap used 56.6 MB
canvas-mouse-sweep: DOM nodes -274
canvas-mouse-sweep: event listeners -199
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 33
canvas-zoom-sweep: task duration 351ms
canvas-zoom-sweep: script duration 19ms
canvas-zoom-sweep: TBT 0ms
canvas-zoom-sweep: heap used 56.8 MB
canvas-zoom-sweep: DOM nodes -229
canvas-zoom-sweep: event listeners -185
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 9ms
dom-widget-clipping: layout count 0
dom-widget-clipping: style recalc count 13
dom-widget-clipping: task duration 363ms
dom-widget-clipping: script duration 56ms
dom-widget-clipping: TBT 0ms
dom-widget-clipping: heap used 54.4 MB
dom-widget-clipping: DOM nodes -125
dom-widget-clipping: event listeners -102
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 521ms
large-graph-idle: script duration 91ms
large-graph-idle: TBT 0ms
large-graph-idle: heap used 56.9 MB
large-graph-idle: DOM nodes -264
large-graph-idle: event listeners -199
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 19ms
large-graph-pan: layout count 0
large-graph-pan: style recalc count 69
large-graph-pan: task duration 1058ms
large-graph-pan: script duration 382ms
large-graph-pan: TBT 0ms
large-graph-pan: heap used 77.0 MB
large-graph-pan: DOM nodes -272
large-graph-pan: event listeners -197
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 63
large-graph-zoom: task duration 1234ms
large-graph-zoom: script duration 470ms
large-graph-zoom: TBT 0ms
large-graph-zoom: heap used 64.8 MB
large-graph-zoom: DOM nodes 7
large-graph-zoom: event listeners 8
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 533ms
minimap-idle: script duration 92ms
minimap-idle: TBT 0ms
minimap-idle: heap used 56.4 MB
minimap-idle: DOM nodes -271
minimap-idle: event listeners -197
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 417ms
subgraph-dom-widget-clipping: script duration 125ms
subgraph-dom-widget-clipping: TBT 0ms
subgraph-dom-widget-clipping: heap used 57.0 MB
subgraph-dom-widget-clipping: DOM nodes -285
subgraph-dom-widget-clipping: event listeners -197
subgraph-idle: avg frame time 17ms
subgraph-idle: p95 frame time 17ms
subgraph-idle: layout duration 0ms
subgraph-idle: style recalc duration 9ms
subgraph-idle: layout count 0
subgraph-idle: style recalc count 10
subgraph-idle: task duration 362ms
subgraph-idle: script duration 14ms
subgraph-idle: TBT 0ms
subgraph-idle: heap used 52.4 MB
subgraph-idle: DOM nodes -134
subgraph-idle: event listeners -98
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 39ms
subgraph-mouse-sweep: layout count 16
subgraph-mouse-sweep: style recalc count 76
subgraph-mouse-sweep: task duration 699ms
subgraph-mouse-sweep: script duration 93ms
subgraph-mouse-sweep: TBT 0ms
subgraph-mouse-sweep: heap used 61.3 MB
subgraph-mouse-sweep: DOM nodes -85
subgraph-mouse-sweep: event listeners -98
subgraph-transition-enter: avg frame time 17ms
subgraph-transition-enter: p95 frame time 17ms
subgraph-transition-enter: layout duration 16ms
subgraph-transition-enter: style recalc duration 32ms
subgraph-transition-enter: layout count 16
subgraph-transition-enter: style recalc count 20
subgraph-transition-enter: task duration 761ms
subgraph-transition-enter: script duration 28ms
subgraph-transition-enter: TBT 163ms
subgraph-transition-enter: heap used 97.8 MB
subgraph-transition-enter: DOM nodes 13673
subgraph-transition-enter: event listeners 2533
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 54ms
viewport-pan-sweep: layout count 0
viewport-pan-sweep: style recalc count 251
viewport-pan-sweep: task duration 3911ms
viewport-pan-sweep: script duration 1367ms
viewport-pan-sweep: TBT 0ms
viewport-pan-sweep: heap used 70.8 MB
viewport-pan-sweep: DOM nodes -124
viewport-pan-sweep: event listeners -82
vue-large-graph-idle: avg frame time 17ms
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 12426ms
vue-large-graph-idle: script duration 536ms
vue-large-graph-idle: TBT 0ms
vue-large-graph-idle: heap used 173.8 MB
vue-large-graph-idle: DOM nodes -5808
vue-large-graph-idle: event listeners -16373
vue-large-graph-pan: avg frame time 17ms
vue-large-graph-pan: p95 frame time 17ms
vue-large-graph-pan: layout duration 0ms
vue-large-graph-pan: style recalc duration 17ms
vue-large-graph-pan: layout count 0
vue-large-graph-pan: style recalc count 75
vue-large-graph-pan: task duration 15219ms
vue-large-graph-pan: script duration 849ms
vue-large-graph-pan: TBT 5ms
vue-large-graph-pan: heap used 160.9 MB
vue-large-graph-pan: DOM nodes -3300
vue-large-graph-pan: event listeners -16375
workflow-execution: avg frame time 17ms
workflow-execution: p95 frame time 17ms
workflow-execution: layout duration 0ms
workflow-execution: style recalc duration 20ms
workflow-execution: layout count 3
workflow-execution: style recalc count 12
workflow-execution: task duration 151ms
workflow-execution: script duration 11ms
workflow-execution: TBT 0ms
workflow-execution: heap used 45.0 MB
workflow-execution: DOM nodes -210
workflow-execution: event listeners -154
Raw data
{
  "timestamp": "2026-07-19T22:17:19.834Z",
  "gitSha": "aeebbbeeb866f50f6b475484132941213b8e5324",
  "branch": "backport-13812-to-cloud-1.47",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2006.2350000000038,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.969,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 391.44199999999995,
      "heapDeltaBytes": -6356020,
      "heapUsedBytes": 45788536,
      "domNodes": -286,
      "jsHeapTotalBytes": 17690624,
      "scriptDurationMs": 15.305000000000001,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "canvas-idle",
      "durationMs": 2062.498000000005,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.756,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 401.71599999999995,
      "heapDeltaBytes": -7363840,
      "heapUsedBytes": 62489924,
      "domNodes": -268,
      "jsHeapTotalBytes": 19546112,
      "scriptDurationMs": 14.799,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1989.4269999999779,
      "styleRecalcs": 74,
      "styleRecalcDurationMs": 41.693,
      "layouts": 12,
      "layoutDurationMs": 3.6530000000000005,
      "taskDurationMs": 913.91,
      "heapDeltaBytes": 17883452,
      "heapUsedBytes": 70791172,
      "domNodes": -282,
      "jsHeapTotalBytes": 16117760,
      "scriptDurationMs": 127.356,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1788.6550000000625,
      "styleRecalcs": 72,
      "styleRecalcDurationMs": 33.312000000000005,
      "layouts": 12,
      "layoutDurationMs": 3.4850000000000003,
      "taskDurationMs": 748.627,
      "heapDeltaBytes": -4006696,
      "heapUsedBytes": 47834520,
      "domNodes": -265,
      "jsHeapTotalBytes": 17428480,
      "scriptDurationMs": 113.77800000000002,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1774.10900000001,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 19.967000000000002,
      "layouts": 6,
      "layoutDurationMs": 0.768,
      "taskDurationMs": 376.016,
      "heapDeltaBytes": -3695812,
      "heapUsedBytes": 66313368,
      "domNodes": -222,
      "jsHeapTotalBytes": 12206080,
      "scriptDurationMs": 21.496,
      "eventListeners": -184,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1738.9760000000933,
      "styleRecalcs": 33,
      "styleRecalcDurationMs": 16.354,
      "layouts": 6,
      "layoutDurationMs": 0.6079999999999998,
      "taskDurationMs": 325.141,
      "heapDeltaBytes": 292696,
      "heapUsedBytes": 52720860,
      "domNodes": -235,
      "jsHeapTotalBytes": 8253440,
      "scriptDurationMs": 17.241999999999997,
      "eventListeners": -186,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 602.328,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 9.538,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 386.55500000000006,
      "heapDeltaBytes": -22283632,
      "heapUsedBytes": 47513012,
      "domNodes": -269,
      "jsHeapTotalBytes": 6438912,
      "scriptDurationMs": 56.059,
      "eventListeners": -203,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 563.4800000000268,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 8.532,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 338.638,
      "heapDeltaBytes": 7272616,
      "heapUsedBytes": 66490712,
      "domNodes": 20,
      "jsHeapTotalBytes": 19136512,
      "scriptDurationMs": 56.501000000000005,
      "eventListeners": 0,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2043.1240000000344,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.020999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 529.9100000000001,
      "heapDeltaBytes": -1837224,
      "heapUsedBytes": 59252964,
      "domNodes": -268,
      "jsHeapTotalBytes": -1093632,
      "scriptDurationMs": 92.54600000000002,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2038.628000000017,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.223999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 512.0469999999999,
      "heapDeltaBytes": -25320316,
      "heapUsedBytes": 59974788,
      "domNodes": -259,
      "jsHeapTotalBytes": 1122304,
      "scriptDurationMs": 88.61,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2130.258000000026,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 16.976000000000003,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1070.435,
      "heapDeltaBytes": 19557540,
      "heapUsedBytes": 81712352,
      "domNodes": -271,
      "jsHeapTotalBytes": -364544,
      "scriptDurationMs": 388.77,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2105.3060000000414,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 21.436,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1045.772,
      "heapDeltaBytes": 17814364,
      "heapUsedBytes": 79718844,
      "domNodes": -272,
      "jsHeapTotalBytes": 684032,
      "scriptDurationMs": 374.869,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3080.573999999956,
      "styleRecalcs": 62,
      "styleRecalcDurationMs": 15.219999999999997,
      "layouts": 60,
      "layoutDurationMs": 6.962999999999999,
      "taskDurationMs": 1265.1979999999999,
      "heapDeltaBytes": 12495424,
      "heapUsedBytes": 67078856,
      "domNodes": 6,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 478.50800000000004,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3042.5689999999577,
      "styleRecalcs": 63,
      "styleRecalcDurationMs": 15.272,
      "layouts": 60,
      "layoutDurationMs": 6.781000000000001,
      "taskDurationMs": 1202.757,
      "heapDeltaBytes": 14311024,
      "heapUsedBytes": 68807808,
      "domNodes": 8,
      "jsHeapTotalBytes": 0,
      "scriptDurationMs": 461.41099999999994,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "minimap-idle",
      "durationMs": 2034.7120000000132,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 6.752999999999998,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 553.701,
      "heapDeltaBytes": -3644796,
      "heapUsedBytes": 59623680,
      "domNodes": -272,
      "jsHeapTotalBytes": -1617920,
      "scriptDurationMs": 98.881,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333335,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2025.7530000000088,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.224999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 512.478,
      "heapDeltaBytes": -4519144,
      "heapUsedBytes": 58590936,
      "domNodes": -269,
      "jsHeapTotalBytes": -1880064,
      "scriptDurationMs": 84.862,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 621.5540000000033,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 11.972999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 434.58799999999997,
      "heapDeltaBytes": 8032252,
      "heapUsedBytes": 60540460,
      "domNodes": -285,
      "jsHeapTotalBytes": 10350592,
      "scriptDurationMs": 125.67899999999999,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 598.6870000000408,
      "styleRecalcs": 46,
      "styleRecalcDurationMs": 10.108,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 399.838,
      "heapDeltaBytes": 6570924,
      "heapUsedBytes": 59011072,
      "domNodes": -285,
      "jsHeapTotalBytes": 10874880,
      "scriptDurationMs": 123.56500000000001,
      "eventListeners": -197,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66666666666665,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 2009.346999999991,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.933000000000002,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 380.739,
      "heapDeltaBytes": -3479716,
      "heapUsedBytes": 49052532,
      "domNodes": -285,
      "jsHeapTotalBytes": 16904192,
      "scriptDurationMs": 14.033,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1991.5500000000748,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.785,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 343.95300000000003,
      "heapDeltaBytes": -2136944,
      "heapUsedBytes": 60919996,
      "domNodes": 18,
      "jsHeapTotalBytes": 25186304,
      "scriptDurationMs": 13.215000000000002,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1693.4640000000059,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 37.43,
      "layouts": 16,
      "layoutDurationMs": 4.464999999999999,
      "taskDurationMs": 663.745,
      "heapDeltaBytes": 18927116,
      "heapUsedBytes": 75101888,
      "domNodes": 60,
      "jsHeapTotalBytes": 18874368,
      "scriptDurationMs": 92.497,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66999999999998,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1763.1329999999252,
      "styleRecalcs": 77,
      "styleRecalcDurationMs": 41.148,
      "layouts": 16,
      "layoutDurationMs": 4.507000000000001,
      "taskDurationMs": 734.976,
      "heapDeltaBytes": -16648180,
      "heapUsedBytes": 53370708,
      "domNodes": -230,
      "jsHeapTotalBytes": 20070400,
      "scriptDurationMs": 94.426,
      "eventListeners": -199,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.670000000000012,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 982.0160000000442,
      "styleRecalcs": 20,
      "styleRecalcDurationMs": 32.41799999999999,
      "layouts": 16,
      "layoutDurationMs": 16.126,
      "taskDurationMs": 761.0489999999999,
      "heapDeltaBytes": 30066356,
      "heapUsedBytes": 102518844,
      "domNodes": 13673,
      "jsHeapTotalBytes": 16515072,
      "scriptDurationMs": 27.986000000000004,
      "eventListeners": 2533,
      "totalBlockingTimeMs": 163,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8175.506999999981,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 56.092,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3904.5270000000005,
      "heapDeltaBytes": -1315940,
      "heapUsedBytes": 71247684,
      "domNodes": 20,
      "jsHeapTotalBytes": 16048128,
      "scriptDurationMs": 1303.833,
      "eventListeners": 20,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.80000000000109
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8142.606999999998,
      "styleRecalcs": 251,
      "styleRecalcDurationMs": 51.90399999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 3918.36,
      "heapDeltaBytes": 8949460,
      "heapUsedBytes": 77180332,
      "domNodes": -268,
      "jsHeapTotalBytes": 7794688,
      "scriptDurationMs": 1429.294,
      "eventListeners": -183,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12832.358999999997,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12811.750999999997,
      "heapDeltaBytes": -14269964,
      "heapUsedBytes": 186567136,
      "domNodes": -3302,
      "jsHeapTotalBytes": 21204992,
      "scriptDurationMs": 535.461,
      "eventListeners": -16376,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.77333333333336,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 12056.70299999997,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 12041.005000000001,
      "heapDeltaBytes": -41054440,
      "heapUsedBytes": 177917520,
      "domNodes": -8314,
      "jsHeapTotalBytes": 26804224,
      "scriptDurationMs": 535.971,
      "eventListeners": -16370,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 15340.936999999996,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 16.966000000000037,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 15316.447,
      "heapDeltaBytes": -46310356,
      "heapUsedBytes": 169082052,
      "domNodes": -3300,
      "jsHeapTotalBytes": -552960,
      "scriptDurationMs": 850.371,
      "eventListeners": -16375,
      "totalBlockingTimeMs": 7,
      "frameDurationMs": 17.220000000000073,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 15157.118999999966,
      "styleRecalcs": 73,
      "styleRecalcDurationMs": 17.295000000000005,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 15120.686,
      "heapDeltaBytes": -35493868,
      "heapUsedBytes": 168256084,
      "domNodes": -3300,
      "jsHeapTotalBytes": 8884224,
      "scriptDurationMs": 847.6239999999999,
      "eventListeners": -16374,
      "totalBlockingTimeMs": 3,
      "frameDurationMs": 17.223333333333358,
      "p95FrameDurationMs": 16.80000000000291
    },
    {
      "name": "workflow-execution",
      "durationMs": 443.672000000106,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 16.519,
      "layouts": 2,
      "layoutDurationMs": 0.41200000000000003,
      "taskDurationMs": 138.122,
      "heapDeltaBytes": -6763176,
      "heapUsedBytes": 45334576,
      "domNodes": -263,
      "jsHeapTotalBytes": 126976,
      "scriptDurationMs": 8.286999999999999,
      "eventListeners": -175,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "workflow-execution",
      "durationMs": 518.8400000000684,
      "styleRecalcs": 12,
      "styleRecalcDurationMs": 23.897000000000002,
      "layouts": 3,
      "layoutDurationMs": 0.5799999999999998,
      "taskDurationMs": 163.729,
      "heapDeltaBytes": -3611152,
      "heapUsedBytes": 49103836,
      "domNodes": -156,
      "jsHeapTotalBytes": 651264,
      "scriptDurationMs": 12.931999999999999,
      "eventListeners": -132,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    }
  ]
}

@github-actions

Copy link
Copy Markdown

🎨 Storybook: 🚧 Building...

@dante01yoon
dante01yoon marked this pull request as ready for review July 19, 2026 22:08
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 19, 2026
@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.11650% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...tform/workspace/components/UnifiedPricingTable.vue 72.72% 6 Missing ⚠️
.../subscription/composables/useSubscriptionDialog.ts 94.11% 1 Missing ⚠️
.../platform/workspace/composables/useMembersPanel.ts 95.23% 1 Missing ⚠️
@@              Coverage Diff              @@
##             cloud/1.47   #13834   +/-   ##
=============================================
  Coverage              ?   78.30%           
=============================================
  Files                 ?     1662           
  Lines                 ?    93231           
  Branches              ?    32313           
=============================================
  Hits                  ?    73006           
  Misses                ?    19616           
  Partials              ?      609           
Flag Coverage Δ
unit 66.42% <96.11%> (?)

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

Files with missing lines Coverage Δ
...t/subscription/CancelSubscriptionDialogContent.vue 92.53% <100.00%> (ø)
...ubscription/components/SubscriptionFooterLinks.vue 88.23% <100.00%> (ø)
...bscription/composables/usePricingTableUrlLoader.ts 91.30% <100.00%> (ø)
src/platform/workspace/api/workspaceApi.ts 85.62% <ø> (ø)
...rkspace/components/CurrentUserPopoverWorkspace.vue 77.27% <100.00%> (ø)
...e/components/SubscriptionPanelContentWorkspace.vue 96.12% <100.00%> (ø)
...nts/SubscriptionRequiredDialogContentWorkspace.vue 100.00% <100.00%> (ø)
.../components/TopUpCreditsDialogContentWorkspace.vue 72.97% <100.00%> (ø)
...omponents/dialogs/settings/MembersPanelContent.vue 100.00% <100.00%> (ø)
...omponents/dialogs/settings/WorkspaceMenuButton.vue 92.50% <100.00%> (ø)
... and 11 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.

@deepme987 deepme987 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assuming these were already reviewed, blind approving

@comfy-pr-bot
comfy-pr-bot merged commit aae5802 into cloud/1.47 Jul 19, 2026
67 checks passed
@comfy-pr-bot
comfy-pr-bot deleted the backport-13812-to-cloud-1.47 branch July 19, 2026 22:49
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

backport Backporting a PR onto a release candidate 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